code:
<?php
/**
 * Класс управления запросами к базе данных:
 */
class PDOQuery
{
	/**
	 * Соединение с базой данных:
	 */
	private $connection = null;

	/**
	 * Вызов экземпляра класса:
	 */
	public static function getInstance()
	{
		static $instance;

		if (!is_object($instance))
			$instance = new PDOQuery();

		return $instance;
	}

	/**
	 * Конструктор (подключение к базе данных):
	 */
	private function __construct()
	{
		$config = Config::getInstance();

		$engine   = $config['database']['engine'];
		$host     = $config['database']['host'];
		$database = $config['database']['name'];
		$user     = $config['database']['user'];
		$pass     = $config['database']['pass'];

		$this -> connection = new PDO(
			$engine.':dbname='.$database.';host='.$host, $user, $pass
		);

		$this -> connection
			-> exec('SET NAMES utf8mb4');
	}

	/**
	 * Фильтрация кавычек:
	 */
	public function q($string)
	{
		return $this -> connection -> quote($string);
	}

	/**
	 * Обработка булевых и прочих подобных POST-запросов:
	 */
	public static function toTinyint($string) {
		if(!isset($string)) return 0;
		switch($string) {
			case 'off':
				return 0;
			case 'on':
				return 1;
			case false:
				return 0;
			case true:
				return 1;
			case '0':
				return 0;
			case '1':
				return 1;
			default:
				return 0;
		}
	}

	/**
	 * Запрос на вставку значений:
	 */
	public function insert($to, $fields)
	{
		$query = $this -> connection
			-> prepare(
				'INSERT INTO '. $to .
				' (`'.implode('`,`', array_keys($fields)).'`)' .
				' VALUES (:'. implode(', :', array_keys($fields)) .')'
			);

		if ($query -> execute($fields))
			return $this -> connection -> lastInsertId();

		return false;
	}

	/**
	 * Запрос на получение значений:
	 */
	public function select($from, $fields = '*', $where = null, $orderBy = null, $limit = null, &$count = null)
	{
		$query = $this -> connection
			-> prepare(
				'SELECT SQL_CALC_FOUND_ROWS '. $fields .' FROM '. $from .
				($where ? '  WHERE '. $where : '') .
				($orderBy ? ' ORDER BY '. $orderBy : '') .
				($limit ? ' LIMIT '. $limit : '')
			);

		if ($query -> execute())
		{
			$found_rows = $this -> connection -> query('SELECT FOUND_ROWS() as count;');
			$count = $found_rows -> fetchColumn();

			return $query -> fetchAll(PDO::FETCH_ASSOC);
		}

 		return false;
	}

	/**
	 * Запрос на обновление значений:
	 */
	public function update($table, $fields = array(), $where = null, $limit = null)
	{
		$set = array();
		foreach ($fields as $key => $value)
		{
			$set[] = $key .' = :'. $key;
		}

		$query = $this -> connection -> prepare(
			'UPDATE '. $table .' SET '.
			 implode(', ', $set) .
			($where ? '  WHERE '. $where : '') .
			($limit ? ' LIMIT '. $limit : '')
		);

		return $query -> execute($fields);
	}

	/**
	 * Запрос на обновление значений:
	 */
	public function update_insecure($table, $fields = array(), $where = null, $limit = null)
	{
		$set = array();
		foreach ($fields as $key => $value)
		{
			$set[] = $key .' = '. $value;
		}

		return $this -> connection -> exec(
			'UPDATE '. $table .' SET '.
			 implode(', ', $set) .
			($where ? '  WHERE '. $where : '') .
			($limit ? ' LIMIT '. $limit : '')
		);
	}

	/**
	 * Запрос на удаление значений:
	 */
	public function delete($table, $where = null, $limit = null)
	{
		return $this -> connection -> exec(
			'DELETE FROM '. $table .
			($where ? '  WHERE '. $where : '') .
			($limit ? ' LIMIT '. $limit : '')
		);
	}
}


code:
<?php
/**
 * Класс работы с профилем (и сессией):
 */
class Session
{
	/**
	 * Время жизни долгой сессии:
	 */
	const LIFETIME = 604800;

	/**
	 * Флаг создания сессии:
	 */
	private $justCreated = false;

	/**
	 * Ключ сессии:
	 */
	private $key = null;

	/**
	 * Параметры временной сессии:
	 */
	private $activeSession = array();

	/**
	 * Параметры постоянной сессии:
	 */
	private $persistenceSession = array();
	private $persistenceUpdated = false;

	/**
	 * Параметры "моментальной" сессии:
	 */
	private $instantSession = array();

	/**
	 * Вызов экземпляра класса:
	 */
	static function getInstance()
	{
		static $instance;

		if (!is_object($instance))
			$instance = new Session();

		return $instance;
	}

	/**
	 * Конструктор:
	 */
	private function __construct()
	{
		session_start();
		if (array_key_exists('session', $_SESSION))
		{
			$cache = KVS::getInstance();

			$this -> key                = @$_SESSION['session']['key'];
			$this -> activeSession      = @$_SESSION['session']['active'];
			$this -> persistenceSession = unserialize($cache -> get('Session', md5($this -> key), 'storage'));
			$this -> instantSession     = @$_SESSION['session']['instant'] ?
										   $_SESSION['session']['instant'] : array();

			return;
		}

		$this -> start();
	}

	/**
	 * Обновление сессии:
	 */
	private function start()
	{
		$cache = KVS::getInstance();

		if (!array_key_exists('key', $_COOKIE) || !$cache -> exists('Session', md5($_COOKIE['key']), 'storage'))
			if ($cache -> exists('Session_ip', $_SERVER['REMOTE_ADDR'], 'key'))
				$this -> key = $_COOKIE['key'] = $cache -> get('Session_ip', $_SERVER['REMOTE_ADDR'], 'key');

		if (array_key_exists('key', $_COOKIE))
		{
			$this -> key = $_COOKIE['key'];

			if ($cache -> exists('Session', md5($this -> key), 'storage'))
			{
				$this -> persistenceSession =
					unserialize($cache -> get('Session', md5($this -> key), 'storage'));
				$cache -> expire('Session', md5($this -> key), 'storage', Session::LIFETIME);
			}
		}
		else
		{
			$this -> key = uniqid(md5(time()));
			$this -> justCreated = true;
		}

		setcookie('key', $this -> key, time() + Session::LIFETIME, '/');
		$_SESSION['open_from'] = $_SERVER['REMOTE_ADDR'];
		$this -> update();
	}

	/**
	 * Обновление сессии:
	 */
	private function update()
	{
		$_SESSION['session']['key']            = $this -> key;
		$_SESSION['session']['active']         = $this -> activeSession;
		$_SESSION['session']['persistence']    = $this -> persistenceSession;

		return true;
	}

	/**
	 * Получить ключ сессии:
	 */
	public function getKey()
	{
		return $this -> key;
	}

	/**
	 * Проверка флага создания сессии:
	 */
	public function isJustCreated()
	{
		return $this -> justCreated;
	}

	/**
	 * Установка параметра активной сессии:
	 */
	public function activeSet($key, $value)
	{
		$this -> activeSession[$key] = $value;
		$_SESSION['session']['active'][$key] = $value;
	}

	/**
	 * Получение параметра активной сессии:
	 */
	public function activeGet($key, $default = false)
	{
		if (array_key_exists($key, $this -> activeSession))
			return $this -> activeSession[$key];

		return $default;
	}

	/**
	 * Очистка активной сессии:
	 */
	public function activeFree()
	{
	    $this -> activeSession = array();
		$this -> update();
	    return true;
	}

	/**
	 * Установка параметра долгой сессии:
	 */
	public function persistenceSet($key, $value)
	{
		$this -> persistenceSession[$key] = $value;
		$this -> persistenceSave();
	}

	/**
	 * Сохранение долгой сессии:
	 */
	private function persistenceSave()
	{
		$cache = KVS::getInstance();
		$cache -> set('Session', md5($this -> key), 'storage', serialize($this -> persistenceSession));
		$cache -> expire('Session', md5($this -> key), 'storage', Session::LIFETIME);
	}

	/**
	 * Получение параметра долгой сессии:
	 */
	public function persistenceGet($key, $default = false)
	{
		if (array_key_exists($key, $this -> persistenceSession))
			return $this -> persistenceSession[$key];

		return $default;
	}

	/**
	 * Установка параметра мгновенной сессии:
	 */
	public function instantSet($key, $value)
	{
		$this -> instantSession[$key] = $value;
		$_SESSION['session']['instant'][$key] = $value;
	}

	/**
	 * Получение параметра мгновенной сессии:
	 */
	public function instantGet($key, $default = false)
	{
		if (array_key_exists($key, $this -> instantSession))
		{
			unset($_SESSION['session']['instant'][$key]);
			return $this -> instantSession[$key];
		}

		return $default;
	}

	/**
	 * Проверка на админство:
	 */
	public function isAdmin($key, $hash)
	{
		$moderators = ControlModel::GetModerators();

		foreach($moderators as $mod)
		{
			if ($mod['key'] == $key .'%'. $hash)
			{
				$_SESSION['auth'] = $mod;
				return true;
			}
		}
		return false;
	}

	/**
	 * Проверка на админскую сессию:
	 */
	public function isAdminSession()
	{
		if (array_key_exists('auth', $_SESSION))
		{
			if ($_SESSION['auth']['class'] == 0)
				return true;
		}
		return false;
	}

	/**
	 * Проверка на модераторство:
	 */
	public function isModerator($key)
	{
		$moderators = ControlModel::GetModerators();
		foreach($moderators as $mod)
		{
			if ($mod['key'] === $key)
			{
				$_SESSION['auth'] = $mod;
				return true;
			}
		}
		return false;
	}

	/**
	 * Проверка на модераторскую сессию:
	 */
	public function isModeratorSession()
	{
		if (array_key_exists('auth', $_SESSION))
		{
			if ($_SESSION['auth']['class'] >= 0)
				return true;
		}
		return false;
	}

	/**
	 * Деструктор (сохраняет данные в бд):
	 */
	public function __destruct()
	{
		$cache = KVS::getInstance();

		$cache -> set('Session_ip', $_SERVER['REMOTE_ADDR'], 'key', $this -> key);
		$cache -> expire('Session_ip', $_SERVER['REMOTE_ADDR'], 'key', 60 * 5);

		$this -> update();
	}
}

repo_name: devarped/pierwszykanal

license: mit


code:
<?php
/**
 *
 */
class Template
{
	/**
	 * Парамтеры вида:
	 */
	private $viewParams = array();

	/**
	 * Флаг изменения заголовка:
	 */
	private $headerChanged = false;

	/**
	 * Метод получения параметра вида:
	 * $name    - имя параметра.
	 * $default - значение по-умолчанию.
	 */
	public function getParameter($name, $default = null) {
		if (array_key_exists($name, $this -> viewParams))
			return $this -> viewParams[$name];

		return $default;
	}

	/**
	 * Метод установки параметра вида:
	 * $name    - имя параметра.
	 * $value   .
	 */
	public function setParameter($name, $value) {
		$this -> viewParams[$name] = $value;
	}

	/**
	 * Заголовок кодировки:
	 */
	public function headerContentEncoding($encoding) {
		header('Content-Encoding: '. $encoding);
	}

	/**
	 * Заголовок типа:
	 */
	public function headerContentType($type, $charset) {
		header('Content-Type: '. $type .';charset='. $charset);
	}

	public function headerContentTypeWOCharset($type) {
		header('Content-Type: '. $type);
	}

	/**
	 * Заголовок размера:
	 */
	public function headerContentLength($bytes) {
		header('Content-Length: '. $bytes);
	}

	/**
	 * Заголовок файла:
	 */
	public function headerContentDisposition($filename) {
		header('Content-Disposition: attachment; filename='. $filename);
	}

	/**
	 * Заголовок не кешируемого контента:
	 */
	public function headerNoCache() {
		header('Cache-Control: no-store, no-cache, must-revalidate');
		header('Cache-Control: post-check=0, pre-check=0', false);
	}

	/**
	 * Заголовок "годен до":
	 */
	public function headerExpires($time) {
		header('Expires: '. gmdate('D, d M Y H:i:s', $time) .' GMT');
	}

	/**
	 * Заголовок "модифицирован в":
	 */
	public function headerLastModified($time) {
		header('Last-Modified: '. gmdate('D, d M Y H:i:s', $time) .' GMT');
	}

	/**
	 * Заголовок ETag:
	 */
	public function headerETag($etag) {
		header('ETag: '. $etag);
	}

	/**
	 * Заголовок 200 OK:
	 */
	public function headerOk() {
		header($_SERVER['SERVER_PROTOCOL'] .' 200 OK');
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 201 Создан:
	 */
	public function headerCreated($redirect) {
		header($_SERVER['SERVER_PROTOCOL'] .' 201 Created');
		header('Location: '.$redirect);
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 301 Перемещен навечно:
	 */
	public function headerMovedPermanently($redirect) {
		header($_SERVER['SERVER_PROTOCOL'] .' 301 Moved Permanently');
		header('Location: '.$redirect);
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 307 Временно перемещен:
	 */
	public function headerMovedTemporarily($redirect) {
		header($_SERVER['SERVER_PROTOCOL'] .' 307 Temporary Redirect');
		header('Location: '.$redirect);
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 303 Смотри в:
	 */
	public function headerSeeOther($redirect) {
		header($_SERVER['SERVER_PROTOCOL'] .' 303 See Other');
		header('Location: '.$redirect);
		$this -> headerChanged = true;
	}

	/**
 	 * Заголовок Location:
	 */
	public function headerLocation($redirect) {
		header('Location: '.$redirect);
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 400 Неправильный запрос:
	 */
	public function headerBadRequest() {
		header($_SERVER['SERVER_PROTOCOL'] .' 400 Bad Request');
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 403 Запрещено:
	 */
	public function headerForbidden() {
		header($_SERVER['SERVER_PROTOCOL'] .' 403 Forbidden');
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 404 Не найдено:
	 */
	public function headerNotFound() {
		header($_SERVER['SERVER_PROTOCOL'] .' 404 Not Found');
		$this -> headerChanged = true;
	}

	/**
	 * Заголовок 410 Пропало:
	 */
	public function headerGone() {
		header($_SERVER['SERVER_PROTOCOL'] .' 410 Gone');
		$this -> headerChanged = true;
	}

	/**
	 * Метод рендеринга вида html:
	 * $data - параметры вида.
	 * $layout - лейаут вида.
	 */
	public function render($data, $layout = 'layout')
	{
		$application = Application::getInstance();

		$controller = $application -> getController();
		$action     = $application -> getAction();

		$content = $this -> renderTemplate(str_replace('_', '/', $controller) .'/'. $action, $data);
		$result  = $this -> renderTemplate($layout, array('content' => $content));

		if (!$this -> headerChanged)
			$this -> headerOk();

		$this -> headerNoCache();
		$this -> headerContentType('text/html', 'UTF-8');
		echo $result;

		return true;
	}

	/**
	 * Метод рендеринга вида для шаблона:
	 * $template - путь к шаблону.
	 * $data     - данные вида.
	 */
	public function renderTemplate($template, $data = array())
	{
		extract($data);

		ob_start();
		include(VIEWS_DIR .'/'. $template .'.php');
		$content = ob_get_contents();
		ob_end_clean();

		return $content;
	}

	/**
	 * Рендеринг JSONP ответа:
	 */
	public function renderJSONP($callback, $data)
	{
		$this -> headerOk();
		$this -> headerNoCache();
		$this -> headerContentType('text/html', 'UTF-8');
		echo '<script type="text/javascript">document.domain = document.domain;</script>';
		echo '<script type="text/javascript">top.'. $callback .'('. json_encode($data) .');</script>';

		return true;
	}
}


code:
<?php
return array(
	'database' => array(
		'engine' => 'mysql',
		'host'   => SQL_HOST,
		'name'   => SQL_NAME,
		'user'   => SQL_USER,
		'pass'   => SQL_PASSWORD
	),
	'routes' => array(
		'/admin' => array(
			'controller' => 'admin',
			'action' => 'posts'
		),
		'/admin/?([^/]+)?' => array(
			'action',
			'controller' => 'admin'
		),
		'/auth' => array(
			'controller' => 'generic_authorize',
			'action' => 'authorize'
		),
		'/captcha' => array(
			'controller' => 'generic_captcha',
			'action' => 'index'
		),
		'/' => array(
		    'url' => '/news/',
		    'controller' => 'generic_redirect',
		    'action' => 'redirect'
		),
		'/b/all' => array(
		    'url' => '/news/all/',
		    'controller' => 'generic_redirect',
		    'action' => 'redirect'
		), /*
		'/(operate|alone)' => array(
			'board',
			'controller' => 'board',
			'action' => 'viewThreads'
		),
		'/(operate|alone)/(\d+)' => array(
			'board', 'page',
			'controller' => 'board',
			'action' => 'viewThreads'
		),
		'/(operate|alone)/res/(\d+)' => array(
			'board', 'thread_id',
			'controller' => 'board',
			'action' => 'viewThread'
		),
		'/(operate|alone)/res/(\d+)/stats' => array(
			'board', 'thread_id',
			'controller' => 'board',
			'action' => 'postStats'
		),
		'/(operate|alone)/(create|createAjaxForm|createPost|createPostAjaxForm|get|remove|changeTitle)' => array(
			'board', 'action',
			'controller' => 'board'
		), */
		'/service/subscribeBoard/(operate|alone)' => array(
			'board',
			'controller' => 'board',
			'action' => 'subscribeBoard'
		),
		'/service/unsubscribeBoard/(operate|alone)' => array(
			'board',
			'controller' => 'board',
			'action' => 'unsubscribeBoard'
		),
		'/service/notifyCheck/(operate|alone)/(\d+)' => array(
			'board', 'id',
			'controller' => 'board',
			'action' => 'notifyCheck'
		),
		'/service/notifyGet' => array(
			'controller' => 'board',
			'action' => 'notifyGet'

		),
		'/service/last_board_posts' => array(
			'controller' => 'board',
			'action'     => 'lastBoardPosts'
		),
		'/service/last_board_posts/(\d+)' => array(
			'page',
			'controller' => 'board',
			'action'     => 'lastBoardPosts'
		),
        '/service/force\-o\-meter' => array(
            'controller' => 'generic_forceometer',
            'action'     => 'index'
        ),
		'/fav' => array(
			'controller' => 'board',
			'action' => 'viewFavorites'
		),
		'/fav/toggle/(operate|alone)/(\d+)' => array(
			'board', 'id',
			'controller' => 'board',
			'action' => 'toggleFavorite'
		),
		'/help/markup'  => array(
			'controller' => 'markup',
			'action' => 'index'
		),
		'/news' => array(
			'controller' => 'blog',
			'action' => 'viewApproved'
		),
		'/help/news' => array(
			'controller' => 'blog',
			'action' => 'viewRules'
		),
		'/news/(\d+)' => array(
			'page',
			'controller' => 'blog',
			'action' => 'viewApproved'
		),
		'/news/all' => array(
			'controller' => 'blog',
			'action' => 'viewAll'
		),
		'/news/all/reset' => array(
			'controller' => 'blog',
			'action' => 'markAsRead'
		),
		'/news/all/new' => array(
			'controller' => 'blog',
			'action' => 'viewNewPost'
		),
		'/news/all/(\d+)' => array(
			'page',
			'controller' => 'blog',
			'action' => 'viewAll'
		),
		'/news/hidden' => array(
			'controller' => 'blog',
			'action' => 'viewHidden'
		),
		'/news/hidden/(\d+)' => array(
			'page',
			'controller' => 'blog',
			'action' => 'viewHidden'
		),
		'/news/cat' => array(
			'controller' => 'blog',
			'action' => 'viewCategories'
		),
		'/news/cat/([^/]+)' => array(
			'category',
			'controller' => 'blog',
			'action' => 'viewCategory'
		),
		'/news/cat/([^/]+)/(\d+)' => array(
			'category', 'page',
			'controller' => 'blog',
			'action' => 'viewCategory'
		),
		'/news/cat/([^/]+)/rss.xml' => array(
			'category',
			'controller' => 'blog',
			'action' => 'viewCategoryRss'
		),
		'/news/sort/created_at/' => array(
			'controller' => 'blog',
			'action'   => 'sort',
			'sortby' => 'created_at'
		),
		'/news/sort/updated_at/' => array(
			'controller' => 'blog',
			'action'   => 'sort',
			'sortby' => 'updated_at'
		),
		'/news/fav' => array(
			'controller' => 'blog',
			'action' => 'viewFavorite'
		),
		'/news/fav/toggle/(\d+)' => array(
			'id',
			'controller' => 'blog',
			'action' => 'toggleFavorite'
		),
		'/news/res/(\d+)' => array(
			'id',
			'controller' => 'blog',
			'action' => 'viewPost'
		),
		'/news/res/(\d+)/rate_post/up' => array(
			'id',
			'vote' => 'up',
			'controller' => 'blog',
			'action' => 'ratePost'
		),
		'/news/res/(\d+)/rate_post/down' => array(
			'id',
			'vote' => 'down',
			'controller' => 'blog',
			'action' => 'ratePost'
		),
		'/news/res/(\d+)/add_comment' => array(
			'post_id',
			'controller' => 'blog',
			'action' => 'addComment'
		),
		'/news/res/(\d+)/stats' => array(
			'post_id',
			'controller' => 'blog',
			'action' => 'postStats'
		),
		'/news/res/(\d+)/getComment/(\d+)' => array(
			'post_id', 'id',
			'controller' => 'blog',
			'action' => 'getPostComment'
		),
		'/news/add' => array(
			'controller' => 'blog',
			'action' => 'addPost',
		),
		'/news/add/validate' => array(
			'controller' => 'blog',
			'action' => 'validatePost',
		),
		'/news/add/preview' => array(
			'controller' => 'blog',
			'action' => 'previewPost',
		),
		'/news/search' => array(
			'controller' => 'blog',
			'action' => 'search'
		),
		'/news/last_comments' => array(
			'controller' => 'mod',
			'action' => 'getLastComments'
		),
		'/news/rss.xml' => array(
			'controller' => 'blog',
			'action' => 'rssApproved'
		),
		'/news/all/rss.xml' => array(
			'controller' => 'blog',
			'action' => 'rssAll'
		),
		'/news/hidden/rss.xml' => array(
			'controller' => 'blog',
			'action' => 'rssHidden'
		),
		'/mod/getPost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'getPost'
		),
		'/mod/categoryPost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'categoryPost'
		),
		'/mod/pinnedPost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'pinnedPost'
		),
		'/mod/ratedPost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'ratedPost'
		),
		'/mod/rateablePost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'rateablePost'
		),
		'/mod/closedPost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'closedPost'
		),
		'/mod/hiddenPost/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'hiddenPost'
		),
		'/mod/removePostComment/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'removePostComment'
		),
		'/mod/removeOnlineLink/(\d+)' => array(
			'id',
			'controller' => 'mod',
			'action' => 'removeOnlineLink'
		),
		'/live'  => array(
			'controller' => 'live',
			'action' => 'index'
		),
		'/live/add'  => array(
			'controller' => 'live',
			'action' => 'add'
		),
		'/live/addXS'  => array(
			'controller' => 'live',
			'action' => 'addCrossSite'
		),
		'/live/redirect/(\d+)'  => array(
			'id',
			'controller' => 'live',
			'action' => 'redirect'
		),
		'/live/set_filter'  => array(
			'controller' => 'live',
			'action' => 'setFilter'
		),
		'/live/linksPanel/' => array(
		    'controller' => 'live',
		    'action' => 'toggleLinksPanel'
		),
		'/service/theme/([^/]+)' => array(
			'theme',
			'controller' => 'generic_theme',
			'action' => 'switch'
		),
		'/service/preview' => array(
			'controller' => 'static',
			'action' => 'preview'
		),
		'/service/getGlobalStats' => array(
		    'controller' => 'blog',
		    'action' => 'getGlobalStats'
		),
		'/service/modlog' => array(
		    'controller' => 'mod',
		    'action' => 'getModActions'
		),
		'/service/modlog/rss.xml' => array(
				'controller' => 'mod',
				'action' => 'getModActionsRss'
		),
		'/service/share' => array(
			'controller' => 'mod',
			'action' => 'shareLink'
		),/*
		'/service/poo-chan' => array(
			'controller' => 'static',
			'action' => 'poo'
		),*/
		'/chat' => array(
			'controller' => 'chat',
			'action' => 'index'
		),
		'/chat/add' => array(
			'controller' => 'chat',
			'action' => 'add'
		),
		'/chat/favorites' => array(
			'controller' => 'chat',
			'action' => 'setFavorites'
		),
		'/chat/common' => array(
			'controller' => 'chat',
			'action' => 'common'
		),
		'/chat/([^/]{45})' => array(
			'id',
			'controller' => 'chat',
			'action' => 'chat'
		),
		'/chat/log/([^/]{45})' => array(
			'id',
			'controller' => 'chat',
			'action' => 'log'

		),
		'/chat/([0-9A-z\-]{4,20})' => array(
			'alias',
			'controller' => 'chat',
			'action' => 'chat'
		),
		'(.*)' => array(
			'page',
			'controller' => 'static',
			'action' => 'index'
		)
	),
	'captcha' => array(
//		'alphabet'         => '0123456789abcdefghijklmnopqrstuvwxyz',
//		'allowed_symbols'  => '23456789abcdeghkmnpqsuvxyz',
		'alphabet'         => '0123456789',
		'allowed_symbols'  => '23456789',
		'fontsdir'         => 'fonts',
		'width'            => 160,
		'height'           => 50,
		'fluctuation_amplitude' => 6,
		'no_spaces'        => false,
		'show_credits'     => false,
		'credits'          => '',
		'foreground_color' => array(mt_rand(0,100), mt_rand(0,100), mt_rand(0,100)),
		'background_color' => array(255, 255, 255),
		'jpeg_quality'     => 0
	)
);


code:
<?php
/**
 *
 */
class AdminController extends Controller
{
	/**
	 * Конструктор (проверка авторизации):
	 */
	public function __construct(Application $application, Template $template)
	{
		$session = Session::getInstance();

		if (!$session -> isAdminSession())
			die($application -> go('errors_error403'));
	}

	/**
	 * Выход:
	 */
	public function logoutAction(Application $application, Template $template)
	{
		unset($_SESSION['auth']);
		$template -> headerSeeOther(
			'/news/'
		);
		return false;
	}

	/**
	 * Главная страница (список всех постов):
	 */
	public function postsAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'post_list');

		$page = @$_GET['page'] ? $_GET['page'] : 0;

		switch(@$_GET['filter'])
		{
			default:
			case 'all':
				$template -> setParameter('filter', 'all');
				$this['posts'] = Blog_BlogPostsModel::GetAllPosts($page, 30, false, $pages);

				break;
			case 'rated':
				$template -> setParameter('filter', 'rated');
				$this['posts'] = Blog_BlogPostsModel::GetRatedPosts($page, 30, false, $pages);

				break;
			case 'hidden':
				$template -> setParameter('filter', 'hidden');
				$this['posts'] = Blog_BlogPostsModel::GetHiddenPosts($page, 30, true, $pages);

				break;
		}
		$template -> setParameter('total_pages',  $pages);
		$template -> setParameter('current_page', $page);
		return true;
	}

	/**
	 * Добавить пост:
	 */
	public function postAddAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'GET')
		{
			$template -> setParameter('menu', 'posts');
			$template -> setParameter('submenu', 'post_add');

			return true;
		}
		else
		{
			Blog_BlogPostsModel::CreatePost($_POST, false);
			$template -> headerSeeOther(
				'/admin/posts/'
			);

			exit;
		}
	}

	/**
	 * Редактировать пост:
	 */
	public function postEditAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'GET')
		{
			$template -> setParameter('menu', 'posts');
			$this['post'] = Blog_BlogPostsModel::GetPost($_GET['id']);
			return true;
		}
		else
		{
			Blog_BlogPostsModel::EditPost($_POST['id'], $_POST, false);
			$template -> headerSeeOther(
				'/admin/postEdit?id='. $_POST['id']
			);

			exit;
		}
	}

	/**
	 * Удаление поста:
	 */
	public function postDeleteAction(Application $application, Template $template)
	{
		Blog_BlogPostsModel::RemovePost($_GET['id']);
		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);

		exit;
	}

	/**
	 * Поиск постов:
	 */
	public function postSearchAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'post_search');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			if (!empty($_POST['id']))
			{
				$post = Blog_BlogPostsModel::GetPost($_POST['id']);

				if ($post)
					$this['posts'] = array($post);
			}
			else
			{
				$search = new SphinxClient();
       			$search -> SetServer(SPHINX_HOST, SPHINX_PORT);
        		$search -> SetSortMode(SPH_SORT_EXTENDED, '@relevance DESC, created_at DESC, @id DESC');
				$search -> SetLimits(0, 60);
				$search -> SetWeights ( array ('title' => 40, 'text' => 20, 'text_full' => 10 ) );

				if (isset($_POST['category']))
					$search -> SetFilter('category', $_POST['category']);

				$result = $search -> Query($_POST['query'], '*');

				if (is_array($result['matches']))
				{
					$ids = array_keys($result['matches']);
					$this['posts'] = Blog_BlogPostsModel::GetPostsByIds($ids);
				}
			}
		}

		return true;
	}

	/**
	 * Управление комментариями:
	 */
	public function postCommentsAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'post_comments');

		return true;
	}

	/**
	 * Редактирование комментария:
	 */
	public function postCommentEditAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'post_comments');

		if ($_SERVER['REQUEST_METHOD'] == 'GET')
		{
			$this['post'] = Blog_BlogCommentsModel::GetComment($_GET['id']);
			return true;
		}
		else
		{
			Blog_BlogCommentsModel::EditComment($_POST['id'], $_POST, false);
			$template -> headerSeeOther(
				'/admin/postCommentEdit?id='. $_POST['id']
			);

			exit;
		}

		return true;
	}

	/**
	 * Удаление поста:
	 */
	public function postCommentDeleteAction(Application $application, Template $template)
	{
		Blog_BlogCommentsModel::RemoveComment($_GET['id']);
		$template -> headerSeeOther(
			'/admin/postComments'
		);

		exit;
	}

	/**
	 * Список категорий:
	 */
	public function postCategoryAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'post_category');

		$this['cats'] = Blog_BlogCategoryModel::GetCategories();

		return true;
	}

	/**
	 * Список категорий:
	 */
	public function postCategoryAddAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'GET')
		{
			$template -> setParameter('menu', 'posts');
			$template -> setParameter('submenu', 'post_category');

			return true;
		}
		else
		{
			Blog_BlogCategoryModel::CreateCategory($_POST, false);
			$template -> headerSeeOther(
				'/admin/postCategory/'
			);

			exit;
		}
	}

	/**
	 * Редактировать категорию:
	 */
	public function postCategoryEditAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'GET')
		{
			$template -> setParameter('menu', 'posts');
			$template -> setParameter('submenu', 'post_category');
			$this['cat'] = Blog_BlogCategoryModel::GetCategoryById($_GET['id']);
			return true;
		}
		else
		{
			Blog_BlogCategoryModel::EditCategory($_POST['id'], $_POST);
			$template -> headerSeeOther(
				'/admin/postCategoryEdit?id='. $_POST['id']
			);

			exit;
		}
	}

	/**
	 * Сортировка категорий:
	 */
	public function postCategoryResortAction(Application $application, Template $template)
	{
		Blog_BlogCategoryModel::Resort($_POST['pos']);
		$template -> headerSeeOther(
			'/admin/postCategory/'
		);
		exit;
	}

	/**
	 * Удаление категория:
	 */
	public function postCategoryDeleteAction(Application $application, Template $template)
	{
		Blog_BlogCategoryModel::RemoveCategory($_GET['id']);
		$template -> headerSeeOther(
			'/admin/postCategory/'
		);

		exit;
	}

	/**
	 * Управление настройками:
	 */
	public function blogSettingsAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'settings');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			ControlModel::SetSettings($_POST);
		}

		$this['settings'] = ControlModel::GetSettings();
		return true;
	}

	/**
	 * Управление вордфильтром:
	 */
	public function blogWordfilterAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'wordfilter');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$words = explode("\r\n", $_POST['wordfilter']);
			array_filter($words);
			ControlModel::SetWordfilter($words);

			$links = explode("\r\n", $_POST['linkfilter']);
			array_filter($links);
			ControlModel::SetLinkfilter($links);

			$links = explode("\r\n", $_POST['spamfilter']);
			array_filter($links);
			ControlModel::SetSpamfilter($links);
		}

		$words = ControlModel::GetWordfilter();
		$links = ControlModel::GetLinkfilter();
		$spam  = ControlModel::GetSpamfilter();

		$this['wordfilter'] = implode("\n", (array)$words);
		$this['linkfilter'] = implode("\n", (array)$links);
		$this['spamfilter'] = implode("\n", (array)$spam);
		return true;
	}

	/**
	 * Управление модераторами:
	 */
	public function blogModeratorsAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'moderators');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$moderators = array();
			$strings    = explode("\r\n", $_POST['moderators']);

			foreach ($strings as $str)
			{
				if (empty($str)) continue;

				list($name, $key, $class, $category)   = explode(' | ', $str);
				$moderators[] = array(
					'name' => $name,   'key' => $key,
					'class' => $class, 'category' => $category
				);
			}

			ControlModel::SetModerators($moderators);
		}

		$moderators = ControlModel::GetModerators();
		$mods       = '';

		foreach ($moderators as $mod)
		{
			$mods .= $mod['name'] ." | ". $mod['key'] ." | ". $mod['class'] ." | ". $mod['category'] ."\n";
		}

		$this['moderators'] = $mods;
		return true;
	}

	/**
	 * Управление ссылками:
	 */
	public function blogLinksAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'links');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$links     = array();
			$imgboards = explode("\n", $_POST['imgboards']);
			$services  = explode("\n", $_POST['services']);

			foreach ($imgboards as $str)
			{
				if (empty($str)) continue;

				list($href, $title)   = explode(' | ', $str);
				$links['imgboards'][] = array('href' => $href, 'title' => $title);
			}

			foreach ($services as $str)
			{
				if (empty($str)) continue;

				list($href, $title)  = explode(' | ', $str);
				$links['services'][] = array('href' => $href, 'title' => $title);
			}

			Blog_BlogLinksModel::SetLinks($links);
		}

		$links = Blog_BlogLinksModel::GetLinks();
		$imgboards = '';
		$services  = '';

		foreach ($links['imgboards'] as $link)
		{
			$imgboards .= $link['href'] ." | ". $link['title'] ."\n";
		}

		foreach ($links['services'] as $link)
		{
			$services .= $link['href'] ." | ". $link['title'] ."\n";
		}

		$this['imgboards'] = $imgboards;
		$this['services']  = $services;
		return true;
	}

	/**
	 * Управление ссылками:
	 */
	public function blogLogAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'log');
		$log = ControlModel::getLogModEvent();
		$this['log'] = '
'. implode("

", array_reverse($log)) .'
';
		return true;
	}

	/**
	 * Управление каналами ротатора:
	 */
	public function onlineChannelAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'posts');
		$template -> setParameter('submenu', 'online_channel');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$cats = array();
			$strings    = explode("\n", $_POST['channels']);

			foreach ($strings as $str)
			{
				if (empty($str)) continue;

				list($title, $regexp, $url)   = explode(' :|: ', $str);
				$cats[] = array(
					'title' => $title, 'regexp' => $regexp, 'url' => $url
				);
			}

			Blog_BlogOnlineModel::SetCategories($cats);
		}

		$cats = Blog_BlogOnlineModel::GetCategories();
		$result       = '';

		foreach ($cats as $cat)
		{
			$result .=  $cat['title'] .' :|: '.  $cat['regexp'] .' :|: '. $cat['url'] ."\n";
		}

		$this['channels'] = $result;
		return true;
	}

	/**
	 * Список чатов:
	 */
	public function chatsAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'chats');
		$template -> setParameter('submenu', 'chats');

		$this['chats'] = Chat_ChatRoomsModel::GetAllRooms();

		return true;
	}

	/**
	 * Добавление чата:
	 */
	public function chatAddAction(Application $application, Template $template)
	{
	    if ($_SERVER['REQUEST_METHOD'] == 'POST')
	    {
			Chat_ChatRoomsModel::CreateRoom($_POST);
			$template -> headerSeeOther(
				'/admin/chats/'
			);

			exit;
	    }

		$template -> setParameter('menu', 'chats');
		$template -> setParameter('submenu', 'chat_add');
	    return true;
	}

	/**
	 * Редактирование чата:
	 */
	public function chatEditAction(Application $application, Template $template)
	{
	    if ($_SERVER['REQUEST_METHOD'] == 'POST')
	    {
	        if (!isset($_POST['public'])) $_POST['public'] = false;

			Chat_ChatRoomsModel::EditRoom($_POST['id'], $_POST);
			$template -> headerSeeOther(
				'/admin/chatEdit?id='. $_POST['id']
			);

			exit;
	    }

		$template -> setParameter('menu', 'chats');
		$template -> setParameter('submenu', 'chat_add');

	    $this['room'] = Chat_ChatRoomsModel::GetRoom($_GET['id']);

	    return true;
	}

	/**
	 * Удаление чата:
	 */
	public function chatDeleteAction(Application $application, Template $template)
	{
		Chat_ChatRoomsModel::RemoveRoom($_GET['id']);
		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);

		exit;
	}

	/**
	 * Просмотр страниц:
	 */
	 public function staticPagesAction(Application $application, Template $template)
	{
		$template -> setParameter('menu', 'static');
		$template -> setParameter('submenu', 'static_pages');

		$pages = StaticModel::GetPages();
		ksort($pages);
		$this['pages'] = $pages;
		return true;
	}

	/**
	 * Добавление страницы:
	 */
	public function staticAddAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'POST') {
			StaticModel::SetPage($_POST['page'], $_POST['title'], $_POST['content'], $_POST['published']);
			$template -> headerSeeOther(
				'/admin/staticPages/'
			);

			exit;
		}

		$template -> setParameter('menu', 'static');
		$template -> setParameter('submenu', 'static_add');

		return true;
	}

	/**
	 * Редактирование страницы:
	 */
	public function staticEditAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'POST') {
			if ($_POST['old_page'] != $_POST['page'])
				StaticModel::RemovePage($_POST['old_page']);

			StaticModel::SetPage($_POST['page'], $_POST['title'], $_POST['content'], $_POST['published']);
			$template -> headerSeeOther(
				'/admin/staticEdit?page='.$_POST['page']
			);

			exit;
		}

		$template -> setParameter('menu', 'static');
		$this['page'] = StaticModel::GetPage($_GET['page']);
		return true;
	}

	/**
	 * Удаление страницы:
	 */
	public function staticDeleteAction(Application $application, Template $template)
	{
		StaticModel::RemovePage($_GET['page']);
		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);

		exit;
	}

	/**
	 * Список загруженных файлов:
	 */
	public function staticFilesAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			if (array_key_exists('upload', $_FILES))
				StaticModel::CreateFile($_FILES['upload']);
		}

		$template -> setParameter('menu', 'static');
		$template -> setParameter('submenu', 'static_files');

		$this['files'] = StaticModel::GetFiles();
		return true;
	}

	/**
	 * Удаление страницы:
	 */
	public function staticFilesDeleteAction(Application $application, Template $template)
	{
		StaticModel::RemoveFile($_GET['name']);
		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);

		exit;
	}

	/**
	 * Выбор лейаута:
	 */
	public function process($template)
	{
		return $template -> render($this -> viewParams, 'admin_layout');
	}
}


code:
<?php
/**
 * Базовый контроллер:
 */
class BaseController extends Controller
{
	/**
	 * Конструктор:
	 */
	public function __construct()
	{
		$session = Session::getInstance();

		if (!$session -> activeGet('last_visit', false))
			$session -> activeSet('last_visit', $session -> persistenceGet('last_visit', time()));

		$session -> persistenceSet('last_visit', time());
	}

	/**
	 * Процессор:
	 */
	public function process($template)
	{
		$session = Session::getInstance();
		$stats   = Blog_BlogStatisticsModel::getGlobalStats();

		$links_on = $session -> persistenceGet('show_links_panel', true);
		if ($links_on) {
		    $filter   = $session -> persistenceGet('live_filter', true);
		    $links    = Blog_BlogOnlineModel::GetLinks($filter);

		    $template -> setParameter('online_links', array_slice($links, 0, 12));
		    $template -> setParameter('right_panel', $links_on);
        }

		$template -> setParameter('global_unique',          $stats['unique']);
		$template -> setParameter('global_online',          $stats['online']);
		$template -> setParameter('global_posts',           $stats['posts']);
		$template -> setParameter('global_unique_posters',  $stats['unique_posters']);
		$template -> setParameter('global_speed',           $stats['speed']);

		if ($session -> persistenceGet('global_theme', false)) {
			return $template -> render($this -> viewParams, $session -> persistenceGet('global_theme'));
		}

		parent::process($template);
	}
}


code:
<?php
/**
 * Контроллер блога:
 */
class BlogController extends BaseController
{
    /**
     * Получение статистики:
     */
    public function getGlobalStatsAjaxAction(Application $application)
    {
        $stats = Blog_BlogStatisticsModel::getGlobalStats();
        return array(
					'global_online' => $stats['online'],
					'global_unique' => $stats['unique'],
					'global_posts'  => $stats['posts'],
					'global_speed'  => $stats['speed']
				);
    }

	/**
	 * Действие просмотра правил сайта:
	 */
	public function viewRulesAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Правила и советы');
		$template -> setParameter('board_id', 'news');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			if (array_key_exists('accept', $_POST) && $_POST['accept'])
			{
				$session = Session::getInstance();
	    			$session -> persistenceSet('rules_accepted', true);

				$template -> headerSeeOther(
					'/news/add/'
				);
			}
		}

		$template -> setParameter('confirm', array_key_exists('confirm', $_GET) ? true : false);

		return true;
	}

	/**
	 * Действие просмотра одобренных новостей:
	 */
	public function viewApprovedAction(Application $application, Template $template)
	{
		$session = Session::getInstance();

		$template -> setParameter('title', 'Одобренные посты');
		$template -> setParameter('board_id', 'news');
		$template -> setParameter('section', 'rated');

		$page = @$_GET['page'] ? $_GET['page'] : 0;
		$sortby = $session -> persistenceGet('posts_sortby', 'created_at');

		$this['posts'] = Blog_BlogPostsModel::GetRatedPosts($page, 30, $sortby == 'updated_at', $pages);

		$template -> setParameter('total_pages',  ceil($pages - 1));
		$template -> setParameter('current_page', $page);
		$template -> setParameter('link_pages', '/news/%d/');
		$template -> setParameter('sortby',       $sortby);

		EventModel::getInstance()
			-> Broadcast('view_approved_post');

		return true;
	}

	/**
	 * Действие просмотра всех новостей:
	 */
	public function viewAllAction(Application $application, Template $template)
	{
		$session = Session::getInstance();

		$template -> setParameter('title', 'Все посты');
		$template -> setParameter('board_id', 'news');
		$template -> setParameter('section', 'all');

		$page = @$_GET['page'] ? $_GET['page'] : 0;
		$sortby = $session -> persistenceGet('posts_sortby', 'created_at');

		$this['posts'] = Blog_BlogPostsModel::GetAllPosts($page, 30, $sortby == 'updated_at', $pages);

		$template -> setParameter('total_pages',  ceil($pages - 1));
		$template -> setParameter('current_page', $page);
		$template -> setParameter('link_pages', '/news/all/%d/');
		$template -> setParameter('sortby',       $sortby);

		EventModel::getInstance()
			-> Broadcast('view_all_post');

		return true;
	}

	/**
	 * Дествие очистки сессии чтений (все прочитано):
	 */
    public function markAsReadAction(Application $application, Template $template)
	{
	    $session = Session::getInstance();
	    $session -> persistenceSet('last_visit', time());
	    $session -> activeFree();

	    $template -> headerSeeOther(
		    $_SERVER['HTTP_REFERER']
		);

		return false;
	}

	/**
	 * Действие просмотра избранных новостей:
	 */
	public function viewFavoriteAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$sortby  = $session -> persistenceGet('posts_sortby', 'created_at');

		$template -> setParameter('title',   'Избранные посты');
		$template -> setParameter('board_id', 'news');
		$template -> setParameter('section', 'favorite');
		$template -> setParameter('sortby',   $sortby);

		$this['posts'] = Blog_BlogPostsModel::GetFavoritePosts($sortby == 'updated_at');

		EventModel::getInstance()
			-> Broadcast('view_favorite_post');

		return true;
	}

	/**
	 * Действие просмотра категорий:
	 */
	public function viewCategoriesAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Категории');
		$template -> setParameter('board_id', 'news');
		$this['categories'] = Blog_BlogCategoryModel::GetCategories();

		EventModel::getInstance()
			-> Broadcast('view_categories_post');

		return true;
	}

	/**
	 * Действие просмотра категорий (ajax):
	 */
	public function viewCategoriesAjaxAction(Application $application)
	{
		$categories = Blog_BlogCategoryModel::GetPublicCategories();
		$result = array();

		foreach($categories as $category)
		{
			$result[] = array(
				'value' =>  $category['code'],
				'label'  =>  $category['title'] .' '. $category['code'] .' '. $category['description'],
				'title'   =>  $category['title'],
				'desc'   => $category['description']
			);
		}

		EventModel::getInstance()
			-> Broadcast('view_categories_post');

		return $result;
	}

	/**
	 * Действие просмотра новостей из категории:
	 */
	public function viewCategoryAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$template -> setParameter('title', 'Посты из категории');
		$template -> setParameter('board_id', 'news');

		$page = @$_GET['page'] ? $_GET['page'] : 0;
		$sortby = $session -> persistenceGet('posts_sortby', 'created_at');

		if (($category = Blog_BlogCategoryModel::GetCategoryByName($_GET['category'])) !== false)
		{
			$template -> setParameter('title', 'Посты из категории «'. $category['title'] .'»');
			$template -> setParameter('section', 'category');
			$this['posts'] = Blog_BlogPostsModel::GetAllPostsFromCategory($category['id'], $page, 30, $sortby == 'updated_at', $pages);

			$template -> setParameter('total_pages',  ceil($pages - 1));
			$template -> setParameter('current_page', $page);
			$template -> setParameter('link_pages', '/news/cat/'.$_GET['category'].'/%d/');
			$template -> setParameter('category_title', $category['title']);
			$template -> setParameter('rss_link', '/news/cat/'.$_GET['category'].'/rss.xml');
			$template -> setParameter('sortby',       $sortby);
		}

		EventModel::getInstance()
			-> Broadcast('view_category_post');

		return true;
	}

	/**
	 * Действие просмотр rss ленты из категори:
	 */
	public function viewCategoryRssAction(Application $application, Template $template)
	{
		if (($category = Blog_BlogCategoryModel::GetCategoryByName($_GET['category'])) !== false)
		{
			$posts = Blog_BlogPostsModel::GetAllPostsFromCategory($category['id'], 0, 30, 'created_at');

			if ($posts)
			{
				$rss = new rss('utf-8');

				$rss->channel('Первый канал', 'http://'. TemplateHelper::getSiteUrl() .'/', 'Новости имиджборд и не только.');
				$rss->language('ru-ru');
				$rss->copyright('Все права пренадлежат вам © 2010');
				$rss->managingEditor('1kun.ebet.sobak@gmail.com');
				$rss->category('Посты из категории «'. $category['title'] .'»');

				$rss->startRSS();
				foreach($posts as $key => $post) {
					$title = $post['category'] ?
						TemplateHelper::BlogCategory($post['category'], 'title') .' — '. $post['title'] :
						$post['title'];

				    $rss->itemTitle($title);
				    $rss->itemLink('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/');
				    $rss->itemDescription(($post['link'] ? ''.$post['link'].'
'.$post['text'] : $post['text']));
				    $rss->itemAuthor('anonymous');
				    $rss->itemGuid('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/', true);
				    $rss->itemPubDate(date('D, d M Y H:i:s O', $post['created_at']));
				    $rss->addItem();
				}

				$result = $rss->RSSdone();
			}

			EventModel::getInstance()
				-> Broadcast('view_rss_approved_post');

			$template -> headerOk();
			$template -> headerContentType('application/rss+xml', 'UTF-8');
			echo $result;
			return false;
		}
	}

	/**
	 * Действие просмотра скрытых новостей:
	 */
	public function viewHiddenAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$template -> setParameter('title', 'Скрытые посты');
		$template -> setParameter('board_id', 'news');
		$template -> setParameter('section', 'hidden');

		$page = @$_GET['page'] ? $_GET['page'] : 0;
		$sortby = $session -> persistenceGet('posts_sortby', 'created_at');

		$this['posts'] = Blog_BlogPostsModel::GetHiddenPosts($page, 30, $sortby == 'updated_at', $pages);

		$template -> setParameter('total_pages',  ceil($pages - 1));
		$template -> setParameter('current_page', $page);
		$template -> setParameter('link_pages', '/news/hidden/%d/');
		$template -> setParameter('sortby',       $sortby);

		EventModel::getInstance()
			-> Broadcast('view_hidden_posts');

		return true;
	}

	/**
	 * Действие поиск поста:
	 */
	public function searchAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$this['form_errors'] = array();
		$this['blog_form']   = array();

		$template -> setParameter('title', 'Поиск постов');
		$template -> setParameter('board_id', 'news');

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$validator = new ValidatorHelper($_POST);
			//$validator -> assertExists('query', 'Введите текст запроса');
			$validator -> assertLength('query', 80, 'Слишком длинный запрос поиска');
			$validator -> assertExists('order', 'Не указан порядок сортировки');
			$validator -> assertExists('sort', 'Не указано направление сортировки');

			$this['form_errors'] = $validator -> getValidationResults();
			$this['blog_form']   = $_POST;

			if ($validator -> isValid())
			{
				$search = new SphinxClient();
       			$search -> SetServer(SPHINX_HOST, SPHINX_PORT);

       			switch($_POST['order'])
       			{
       				default:
       				case 'relevance':
       					$sort = $_POST['sort'] == 'desc' ? '@relevance DESC, created_at DESC, @id DESC' : '@relevance ASC, created_at ASC, @id ASC';
       					$search -> SetSortMode(SPH_SORT_EXTENDED, $sort);
       					break;

					case 'created_at':
						$sort = $_POST['sort'] == 'desc' ? SPH_SORT_ATTR_DESC : SPH_SORT_ATTR_ASC;
       					$search -> SetSortMode($sort, 'created_at');
						break;

					case 'updated_at':
						$sort = $_POST['sort'] == 'desc' ? SPH_SORT_ATTR_DESC : SPH_SORT_ATTR_ASC;
       					$search -> SetSortMode($sort, 'updated_at');
						break;

					case 'rate':
						$sort = $_POST['sort'] == 'desc' ? SPH_SORT_ATTR_DESC : SPH_SORT_ATTR_ASC;
       					$search -> SetSortMode($sort, 'rate');
						break;

					case 'comments':
						$sort = $_POST['sort'] == 'desc' ? SPH_SORT_ATTR_DESC : SPH_SORT_ATTR_ASC;
       					$search -> SetSortMode($sort, 'comments');
						break;
       			}

				$search -> SetWeights (array('link' => 50, 'title' => 40, 'text' => 20, 'text_full' => 10));
				$search -> SetLimits(0, 60);

				if (isset($_POST['category']) && $_POST['category'] != 0)
					$search -> SetFilter('category', array($_POST['category']));

				$result = $search -> Query($_POST['query'], 'posts');

				if ($result && array_key_exists('matches', $result) && is_array($result['matches']))
				{
					$ids   = array_keys($result['matches']);
					$posts = Blog_BlogPostsModel::GetPostsByIds($ids);
					$this['posts'] = $posts;

					$template -> setParameter('total', sizeof($result['matches']));
					$template -> setParameter('total_found', $result['total_found']);
				}
				else
				{
					$this['form_errors'] = array('По данному запросу ничего не найдено');
				}
			}
		}

		EventModel::getInstance()
			-> Broadcast('view_search_post');

		return true;
	}

	/**
	 * Перессылка на первый непрочитанный пост:
	 */
	public function viewNewPostAction(Application $application, Template $template)
	{
	    $session = Session::getInstance();
		$posts = Blog_BlogPostsModel::GetAllPosts(0, 30, 'updated_at', $pages);
        foreach($posts as $post) {
            if (TemplateHelper::isPostUpdated($post, false)) {
                $template -> headerSeeOther(
						'/news/res/'. $post['id'] .'/#new'
				);
			    return false;
            }
        }
		$template -> headerSeeOther(
			'/news/all/'
		);
	    return false;
	}

	/**
	 * Действие просмотра поста:
	 */
	public function viewPostAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$this['form_errors'] = $session -> instantGet('comment_errors', array());
		$this['blog_form']   = $session -> instantGet('comment_form', array());

		$template -> setParameter('title',   'Пост не найден');
		$template -> setParameter('board_id', 'news');
		$template -> setParameter('section', 'entry');

		if (isset($_GET['id']))
		{
			$post = Blog_BlogPostsModel::GetPost($_GET['id']);
			if ($post)
			{
				// Сложная и ебанутая система определения прочитан ли пост:
				if (!$session -> activeGet('last_visit_post_'. $_GET['id'], false)) {
					$session -> instantSet('last_visit_post_'. $_GET['id'], $session -> activeGet('last_visit'));
					$session -> activeSet('last_visit_post_'. $_GET['id'], time());
				}
				else
				{
					$session -> instantSet('last_visit_post_'. $_GET['id'], $session -> activeGet('last_visit_post_'. $_GET['id']));
					$session -> activeSet('last_visit_post_'. $_GET['id'], time());
				}

				$template -> setParameter('title', 'Пост №'. $_GET['id']);
				$this['comments'] = Blog_BlogCommentsModel::GetComments($_GET['id']);
				$this['post']     = $post;

				if (ControlModel::isCommentCaptcha())
				{
					$key     = 'comment';
					$template -> setParameter('captcha_key', $key);
					$session  -> instantSet('captcha_'.$key, true);
				}

				EventModel::getInstance()
					-> Broadcast('view_post', $post['id']);
			}
		}

		$stats = Blog_BlogStatisticsModel::getPostStats($post['id']);

		$template -> setParameter('total_read',   $stats['online']);
		$template -> setParameter('total_write',  $stats['writers']);
		$template -> setParameter('total_unique', $stats['unique']);

		return true;
	}

	/**
	 * Просмотр поста (ajax):
	 */
	public function viewPostAjaxAction(Application $application)
	{
		$session = Session::getInstance();
		$session -> activeSet('last_visit_post_'. $_GET['id'], time());
		return true;
	}

	/**
	 * Получение коммента:
	 */
	public function getPostCommentAjaxAction()
	{
	    $cache  = KVS::getInstance();
		$comment = Blog_BlogCommentsModel::GetComment($_GET['id']);

		$session = Session::getInstance();
		$session -> activeSet('last_visit_post_'. $comment['post_id'], time());

		if ($comment)
		{
		    if ($_GET['title']) {
			    $post = Blog_BlogPostsModel::GetPost($comment['post_id']);
			    $comment['post_title'] = $post['title'];
			}

			$comment['created_at'] = TemplateHelper::date('d M Y @ H:i:s', $comment['created_at']);
			$comment['author']     = array($comment['author'], HomeBoardHelper::getBoard($comment['author']));
			unset($comment['ip']);

			return $comment;
		}

		return false;
	}

	/**
	 * Действие оценки поста:
	 */
	public function ratePostAction(Application $application, Template $template)
	{
		if (!isset($_SERVER['HTTP_REFERER']) || (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) != TemplateHelper::getSiteUrl()))
			return false;

		if (ControlModel::isPostRateCaptcha())
		{
			$settings = ControlModel::GetSettings();
			$session  = Session::getInstance();
			$template -> setParameter('referer', $_SERVER['HTTP_REFERER']);

			if ($_SERVER['REQUEST_METHOD'] == 'POST')
			{
				if ($_POST['captcha'] === $session -> instantGet('captcha_'. $_POST['captcha_key'], false))
				{
					if (@$_GET['vote'] == 'up')
					Blog_BlogPostsModel::RatePost($_GET['id'], true);

					elseif(@$_GET['vote'] == 'down')
						Blog_BlogPostsModel::RatePost($_GET['id'], false);

					$session -> persistenceSet('captcha_mode', false);
					$session -> persistenceSet('captcha_mode_length', @$settings['captcha_length']);

					$template -> headerSeeOther(
						$_POST['referer']
					);
					return false;
				}
				else
				{
					$template -> setParameter('captcha_err', true);
				}

				$template -> setParameter('referer', $_POST['referer']);
			}

			$key = 'rate';
			$template -> setParameter('title',   'Голосование за пост');
			$template -> setParameter('board_id', 'news');
			$template -> setParameter('captcha_key', $key);
			$session  -> instantSet('captcha_'.$key, true);
			return true;
		}

		if (@$_GET['vote'] == 'up')
			Blog_BlogPostsModel::RatePost($_GET['id'], true);

		elseif(@$_GET['vote'] == 'down')
			Blog_BlogPostsModel::RatePost($_GET['id'], false);

		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);
		return false;
	}

	/**
	 * Действие оценки поста (ajax):
	 */
	public function ratePostAjaxAction(Application $application)
	{
		$ip    = $_SERVER['REMOTE_ADDR'];
		$cache = KVS::getInstance();

		$raters = (array)unserialize($cache -> get('Blog_BlogPostsModel', $_GET['id'], 'raters'));

		if (in_array($ip, $raters))
			return array('rate' => false);

		if (ControlModel::isPostRateCaptcha())
			return array('rate' => 'captcha');

		if (@$_GET['vote'] == 'up')
			return array('rate' => Blog_BlogPostsModel::RatePost($_GET['id'], true));

		elseif(@$_GET['vote'] == 'down')
			return array('rate' => Blog_BlogPostsModel::RatePost($_GET['id'], false));
	}

	/**
	 * Действие: добавление/удаление из избранного:
	 */
	public function toggleFavoriteAction(Application $application, Template $template)
	{
		Blog_BlogPostsModel::ToggleFavoritePost($_GET['id']);
		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);
		return false;
	}

	/**
	 * Действие: добавление/удаление из избранного (ajax):
	 */
	public function toggleFavoriteAjaxAction(Application $application)
	{
		return array('favorite' => Blog_BlogPostsModel::ToggleFavoritePost($_GET['id']));
	}

	/**
	 * Действие добавления поста:
	 */
	public function addPostAction(Application $application, Template $template)
	{
		$session  = Session::getInstance();
		$settings = ControlModel::GetSettings();

		if ($session -> isJustCreated())
			return false;

		/* if (!$session -> persistenceGet('rules_accepted'))
			$template -> headerSeeOther(
				'/help/news/?confirm'
			); */

		$this['form_errors'] = array();
		$this['blog_form']   = array();

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$text_test      = ControlModel::checkContent($_POST['text']);
			$text_full_test = ControlModel::checkContent($_POST['text_full']);

			$text_flood      = ControlModel::isFloodFilter() ? ControlModel::checkFlood($_POST['text']) : true;
			$text_full_flood = $_POST['text_full'] && ControlModel::isFloodFilter() ? ControlModel::checkFlood($_POST['text_full']) : true;

			$text_unique      = ControlModel::isSamepostFilter() ? ControlModel::checkPostUnique($_POST['text']) : true;
			$text_full_unique = $_POST['text_full'] && ControlModel::isSamepostFilter() ? ControlModel::checkPostUnique($_POST['text_full']) : true;

			$validator = new ValidatorHelper($_POST);
			if (ControlModel::isPostCaptcha())
			{
				$validator -> assertExists('captcha_key', 'Введите капчу');
				if ($_POST['captcha_key'])
					$validator -> assertEqual(
						'captcha', $session -> instantGet('captcha_'. $_POST['captcha_key'], false),
						'Капча введена неверно'
					);
			}

			$validator -> assertExists('title',           'Не введён заголовок');
			$validator -> assertLength('title',     70,   'Заголовок слишком длинный');
			$validator -> assertExists('text',            'Не введён вводный текст');
			$validator -> assertLength('text',      1024, 'Вводный текст слишком длинный');

			if (!$session -> isModeratorSession())
			    $validator -> assertLength('text_full', 5096, 'Подробный текст слишком длинный');

			$validator -> assertNotExists('email',        'Заполнено лишнее поле');

			if (!isset($_POST['hidepost']) || $_POST['hidepost'] != 'on') {
				if ($validator -> fieldValid('title'))
					$validator -> assertLengthMore('title', 3, 'Заголовок слишком короткий');

				if ($validator -> fieldValid('text'))
					$validator -> assertLengthMore('text', 15, 'Вводный текст слишком короткий');

				if ($validator -> fieldValid('title'))
					// https://stackoverflow.com/questions/16733674/php-remove-symbols-from-string
					$validator -> assertTrue('title', preg_replace('/[^\p{L}\p{N}\s]/u', '', str_replace(' ', '', $_POST['title'])) != '', 'Заголовок не несёт смысловой нагрузки');

				if ($validator -> fieldValid('title'))
					$validator -> assertTrue('title', mb_substr($_POST['title'], -1, 1, 'UTF-8') != '.', 'Точка в конце заголовка');

				if ($validator -> fieldValid('title') && $validator -> fieldValid('text'))
					$validator -> assertTrue('title',
											($_POST['title'] != $_POST['text']) && (TexyHelper::typo($_POST['title']) != $_POST['text']),
											'Заголовок совпадает с вводным текстом');

				if ($validator -> fieldValid('text') && $validator -> fieldValid('text_full'))
					$validator -> assertTrue('text', $_POST['text'] != $_POST['text_full'], 'Вводный текст совпадает с подробным текстом');

				if ($validator -> fieldValid('text_full') && $validator -> fieldValid('title'))
					$validator -> assertTrue('title',
											($_POST['title'] != $_POST['text_full']) && (TexyHelper::typo($_POST['title']) != $_POST['text_full']),
											'Заголовок совпадает с подробным текстом');

				$validator -> assertTrue(
					'text', $text_test && $text_full_test,
					'Запрещённое слово из вордфильтра'
				);

				$validator -> assertTrue(
					'text', $text_flood && $text_full_flood,
					'Обнаружен флуд'
				);

				$validator -> assertTrue(
					'text', $text_unique && $text_full_unique,
					'Новость или комментарий с таким текстом уже существует'
				);
			}

			$validator -> assertTrue(
				'timeout', ControlModel::getPostInterval() == 0,
				'Таймаут '. TemplateHelper::ending(ControlModel::getPostInterval(), 'секунда', 'секунды', 'секунд')
			);

			if ($_POST['link'] != '')
				$validator -> assertRegexp('link', ValidatorHelper::URL_REGEXP, 'Ссылка введена неверно');

			if ($validator -> fieldValid('link'))
				$validator -> assertTrue('link', ControlModel::CheckLinkfilter($_POST['link']) == false, 'Ссылка запрещена');

			if ($_POST['category'] != '')
				$validator -> assertTrue('category', Blog_BlogCategoryModel::CategoryExists($_POST['category']), 'Неверный ключ категории');

			if ($validator -> isValid())
			{
				$id = Blog_BlogPostsModel::CreatePost($_POST, true);
				if (ControlModel::checkModrights(Blog_BlogCategoryModel::GetCategoryIdByCode($_POST['category'])))
				{
					if (isset($_POST['rated']) && $_POST['rated'])
						Blog_BlogPostsModel::RatedPost($id, true);

					if (isset($_POST['pinned']) && $_POST['rated'])
						Blog_BlogPostsModel::PinPost($id, true);

					if (isset($_POST['notrateable']) && $_POST['rated'])
						Blog_BlogPostsModel::RateablePost($id, false);

					if (isset($_POST['closed']) && $_POST['rated'])
						Blog_BlogPostsModel::ClosePost($id, true);
				}

				if (isset($_POST['hidepost']) && $_POST['hidepost'] == 'on')
					Blog_BlogPostsModel::HidePost($id, true);

				if (ControlModel::isPostPremoderation()) {
					Blog_BlogPostsModel::HidePost($id, true);
			        Blog_BlogPostsModel::SetSpecialComment($id, 'Пост ожидает модерации');
			    }

				if (ControlModel::isPostHandApproving())
					Blog_BlogPostsModel::RateablePost($id, false);

				$session -> persistenceSet('captcha_mode', false);
				$session -> persistenceSet('captcha_mode_length', @$settings['captcha_length']);

				$template -> headerSeeOther(
					'/news/res/'. $id .'/'
				);
				return false;
			}

			$this['form_errors'] = $validator -> getValidationResults();
			$this['blog_form']   = $_POST;
		}

		if (ControlModel::isPostCaptcha())
		{
			$key     = 'post';
			$template -> setParameter('captcha_key', $key);
			$session  -> instantSet('captcha_'.$key, true);
		}

		$template -> setParameter('title', 'Добавить пост');
		$template -> setParameter('board_id', 'news');
		$template -> setParameter('section', 'add');

		EventModel::getInstance()
			-> Broadcast('view_add_post');

		return true;
	}

	/**
	 * Действие валидации поста (ajax):
	 */
	public function validatePostAjaxAction(Application $application)
	{
	    $session = Session::getInstance();

		$validator = new ValidatorHelper($_POST);
		$validator -> assertExists('title',           'Не введен заголовок');
		$validator -> assertLength('title',     70,   'Заголовок слишком длинный');
		$validator -> assertExists('text',            'Не введен вводный текст');
		$validator -> assertLength('text',      1024, 'Вводный текст слишком длинный');

		if (!$session -> isModeratorSession())
		    $validator -> assertLength('text_full', 5096, 'Подробный текст слишком длинный');

		if ($_POST['link'] != '')
			$validator -> assertRegexp('link', ValidatorHelper::URL_REGEXP, 'Ссылка введена неверно');

		if ($validator -> fieldValid('link'))
			$validator -> assertTrue('link', ControlModel::CheckLinkfilter($_POST['link']) == false, 'Ссылка запрещена');

		if (!isset($_POST['hidepost']) || $_POST['hidepost'] != 'on') {
			if ($validator -> fieldValid('title'))
				$validator -> assertLengthMore('title', 3, 'Заголовок слишком короткий');

			if ($validator -> fieldValid('text'))
				$validator -> assertLengthMore('text', 15, 'Вводный текст слишком короткий');

			if ($validator -> fieldValid('title'))
				// https://stackoverflow.com/questions/16733674/php-remove-symbols-from-string
				$validator -> assertTrue('title', preg_replace('/[^\p{L}\p{N}\s]/u', '', str_replace(' ', '', $_POST['title'])) != '', 'Заголовок не несёт смысловой нагрузки');

			if ($validator -> fieldValid('title'))
				$validator -> assertTrue('title', mb_substr($_POST['title'], -1, 1, 'UTF-8') != '.', 'Точка в конце заголовка');

			if ($validator -> fieldValid('title') && $validator -> fieldValid('text'))
				$validator -> assertTrue('title',
										($_POST['title'] != $_POST['text']) && (TexyHelper::typo($_POST['title']) != $_POST['text']),
										'Заголовок совпадает с вводным текстом');

			if ($validator -> fieldValid('text') && $validator -> fieldValid('text_full'))
				$validator -> assertTrue('text', $_POST['text'] != $_POST['text_full'], 'Вводный текст совпадает с подробным текстом');

			if ($validator -> fieldValid('text_full') && $validator -> fieldValid('title'))
				$validator -> assertTrue('title',
										($_POST['title'] != $_POST['text_full']) && (TexyHelper::typo($_POST['title']) != $_POST['text_full']),
										'Заголовок совпадает с подробным текстом');
		}

		if ($_POST['category'] != '')
			$validator -> assertTrue('category', Blog_BlogCategoryModel::CategoryExists($_POST['category']), 'Неверный ключ категории');

		return array(
			'isValid'           => $validator -> isValid(),
			'validationResults' => $validator -> getValidationResults()
		);
	}

	/**
	 * Действие предпросмотра поста (ajax):
	 */
	public function previewPostAjaxAction(Application $application)
	{
		$preview = array();
		$preview['title']     = TexyHelper::typo(@$_POST['title']);
		$preview['text']      = TexyHelper::markup(@$_POST['text'], true);
		$preview['text_full'] = TexyHelper::markup(@$_POST['text_full'], true);
		$preview['icon']      = @$_POST['link'] ? TemplateHelper::getIcon(@$_POST['link']) : '/ico/favicons/1chan.pl.png';

		if (array_key_exists('category', $_POST) && !empty($_POST['category']))
		{
			if (Blog_BlogCategoryModel::CategoryExists($_POST['category']))
			{
				$category = Blog_BlogCategoryModel::GetCategoryByCode($_POST['category']);
				$preview['category'] = TemplateHelper::BlogCategory($category['id'], 'title');
			}
		}

		return $preview;
	}

	/**
	 * Действие обновление статистики:
	 */
	public function postStatsAjaxAction(Application $application)
	{
		Blog_BlogStatisticsModel::updatePostStats($_GET['post_id'], $_GET['writing']);
		return Blog_BlogStatisticsModel::getPostStats($_GET['post_id']);
	}

	/**
	 * Действие добавление комментария:
	 */
	public function addCommentAction(Application $application, Template $template)
	{
		$session  = Session::getInstance();
		$settings = ControlModel::GetSettings();

		if ($session -> isJustCreated())
			return false;

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$text_test   = ControlModel::checkContent($_POST['text']);
			$text_flood  = ControlModel::isFloodFilter() ? ControlModel::checkFlood($_POST['text']) : true;
			$text_unique = ControlModel::isSamecommFilter() ? ControlModel::checkCommUnique($_POST['text']) : true;

			$validator = new ValidatorHelper($_POST);
			if (ControlModel::isCommentCaptcha())
			{
				$validator -> assertExists('captcha_key', 'Введите капчу');
				if ($_POST['captcha_key'])
					$validator -> assertEqual(
						'captcha', $session -> instantGet('captcha_'. $_POST['captcha_key'], false),
						'Капча введена неверно'
					);
			}
			$validator -> assertExists('text',       'Не введен текст комментария');
			$validator -> assertExists('post_id',    'Не указан идентификатор поста');
			$validator -> assertLength('text', 2048, 'Текст комментария слишком длинный');
			$validator -> assertNotExists('email',   'Заполнено лишнее поле');

			$post = Blog_BlogPostsModel::GetPost($_POST['post_id']);

			if ($post["hidden"] == 0) {
				$validator -> assertTrue(
					'text', $text_flood,
					'Обнаружен флуд'
				);

				$validator -> assertTrue(
					'text', $text_unique,
					'Новость или комментарий с таким текстом уже существует'
				);

				$validator -> assertTrue(
					'text', $text_test,
					'Запрещенное слово из вордфильтра'
				);
			}

			$validator -> assertTrue(
				'timeout', ControlModel::getPostCommentInterval() == 0,
				'Таймаут '. TemplateHelper::ending(ControlModel::getPostCommentInterval(), 'секунда', 'секунды', 'секунд')
			);

			if ($validator -> isValid())
			{
				$id = Blog_BlogCommentsModel::CreateComment($_POST, true);
				$session -> activeSet('last_visit_post_'. $_POST['post_id'], time());

				$session -> persistenceSet('captcha_mode', false);
				$session -> persistenceSet('captcha_mode_length', @$settings['captcha_length']);

				$template -> headerSeeOther(
					'/news/res/'. $_GET['post_id'] .'/#'. $id
				);
				return false;
			}

			$session -> instantSet('comment_errors', $validator -> getValidationResults());
			$session -> instantSet('comment_form', $_POST);
			$template -> headerSeeOther(
				'/news/res/'. $_GET['post_id'] .'/#comment_form'
			);
			return false;
		}

		$template -> headerSeeOther(
			'/news/'
		);
		return false;
	}

	/**
	 * Действие добавления комментария (ajax):
	 */
	public function addCommentAjaxAction(Application $application)
	{
		$session = Session::getInstance();
		$settings = ControlModel::GetSettings();

		if ($session -> isJustCreated())
			return false;

		$validator = new ValidatorHelper($_POST);
		if (ControlModel::isCommentCaptcha())
		{
			$validator -> assertExists('captcha_key', 'Введите капчу');
			if ($_POST['captcha_key'])
				$validator -> assertEqual(
					'captcha', $session -> instantGet('captcha_'. $_POST['captcha_key'], false),
					'Капча введена неверно'
				);
//			return array('captcha' => true);
		}
		$validator -> assertExists('text',       'Не введен текст комментария');
		$validator -> assertExists('post_id',    'Не указан идентификатор поста');
		$validator -> assertLength('text', 2048, 'Текст комментария слишком длинный');
		$validator -> assertNotExists('email',   'Заполнено лишнее поле');

		$text_test   = ControlModel::checkContent($_POST['text']);
		$text_flood  = ControlModel::isFloodFilter() ? ControlModel::checkFlood($_POST['text']) : true;
		$text_unique = ControlModel::isSamecommFilter() ? ControlModel::checkCommUnique($_POST['text']) : true;

		$post = Blog_BlogPostsModel::GetPost($_POST['post_id']);

		if ($post["hidden"] == 0) {
			$validator -> assertTrue(
				'text', $text_flood,
				'Обнаружен флуд'
			);

			$validator -> assertTrue(
				'text', $text_unique,
				'Новость или комментарий с таким текстом уже существует'
			);

			$validator -> assertTrue(
				'text', $text_test,
				'Запрещенное слово из вордфильтра'
			);
		}

		$validator -> assertTrue(
			'timeout', ControlModel::getPostCommentInterval() == 0,
			'Таймаут '. TemplateHelper::ending(ControlModel::getPostCommentInterval(), 'секунда', 'секунды', 'секунд')
		);

		if ($validator -> isValid()) {
			$id = Blog_BlogCommentsModel::CreateComment($_POST, true);
			$session -> activeSet('last_visit_post_'. $_POST['post_id'], time());

			$session -> persistenceSet('captcha_mode', false);
			$session -> persistenceSet('captcha_mode_length', @$settings['captcha_length']);
		}

		return array(
			'isValid'           => $validator -> isValid(),
			'validationResults' => $validator -> getValidationResults()
		);
	}

	/**
	 * Действие просмотр rss ленты одобренных постов:
	 */
	public function rssApprovedAction(Application $application, Template $template)
	{
		$posts = Blog_BlogPostsModel::GetRatedPosts(0, 20, false);

		if ($posts)
		{
			$rss = new rss('utf-8');

			$rss->channel('Первый канал - Одобренные', 'http://'. TemplateHelper::getSiteUrl() .'/', 'Новости имиджборд и не только.');
			$rss->language('ru-ru');
			$rss->copyright('Все права пренадлежат вам © 2010');
			$rss->managingEditor('1kun.ebet.sobak@gmail.com');
			$rss->category('Одобренные');

			$rss->startRSS();
			foreach($posts as $key => $post) {
				$title = $post['category'] ?
					TemplateHelper::BlogCategory($post['category'], 'title') .' — '. $post['title'] :
					$post['title'];

			    $rss->itemTitle($title);
			    $rss->itemLink('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/');
			    $rss->itemDescription(($post['link'] ? ''.$post['link'].'
'.$post['text'] : $post['text']));
			    $rss->itemAuthor('anonymous');
			    $rss->itemGuid('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/', true);
			    $rss->itemPubDate(date('D, d M Y H:i:s O', $post['created_at']));
			    $rss->addItem();
			}

			$result = $rss->RSSdone();
		}

		EventModel::getInstance()
			-> Broadcast('view_rss_approved_post');

		$template -> headerOk();
		$template -> headerContentType('application/rss+xml', 'UTF-8');
		echo $result;
		return false;
	}

	/**
	 * Действие просмотр rss ленты всех постов:
	 */
	public function rssAllAction(Application $application, Template $template)
	{
		$posts = Blog_BlogPostsModel::GetAllPosts(0, 20, false);

		if ($posts)
		{
			$rss = new rss('utf-8');

			$rss->channel('Первый канал - Все', 'http://'. TemplateHelper::getSiteUrl() .'/', 'Новости имиджборд и не только.');
			$rss->language('ru-ru');
			$rss->copyright('Все права пренадлежат вам © 2010');
			$rss->managingEditor('1kun.ebet.sobak@gmail.com');
			$rss->category('Все');

			$rss->startRSS();
			foreach($posts as $key => $post) {
				$title = $post['category'] ?
					TemplateHelper::BlogCategory($post['category'], 'title') .' — '. $post['title'] :
					$post['title'];

			    $rss->itemTitle($title);
			    $rss->itemLink('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/');
			    $rss->itemDescription(($post['link'] ? ''.$post['link'].'
'.$post['text'] : $post['text']));
			    $rss->itemAuthor('anonymous');
			    $rss->itemGuid('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/', true);
			    $rss->itemPubDate(date('D, d M Y H:i:s O', $post['created_at']));
			    $rss->addItem();
			}

			$result = $rss->RSSdone();
		}

		EventModel::getInstance()
			-> Broadcast('view_rss_all_post');

		$template -> headerOk();
		$template -> headerContentType('application/rss+xml', 'UTF-8');
		echo $result;

		return false;
	}

	/**
	 * Действие просмотр rss ленты скрытых постов:
	 */
	public function rssHiddenAction(Application $application, Template $template)
	{
		$posts = Blog_BlogPostsModel::GetHiddenPosts(0, 20, false);

		if ($posts)
		{
			$rss = new rss('utf-8');

			$rss->channel('Первый канал - Скрытые', 'http://'. TemplateHelper::getSiteUrl() .'/', 'Новости имиджборд и не только.');
			$rss->language('ru-ru');
			$rss->copyright('Все права пренадлежат вам © 2010');
			$rss->managingEditor('1kun.ebet.sobak@gmail.com');
			$rss->category('Все');

			$rss->startRSS();
			foreach($posts as $key => $post) {
				$title = $post['category'] ?
					TemplateHelper::BlogCategory($post['category'], 'title') .' — '. $post['title'] :
					$post['title'];

			    $rss->itemTitle($title);
			    $rss->itemLink('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/');
			    $rss->itemDescription(($post['link'] ? ''.$post['link'].'
'.$post['text'].$post['special_comment'] : $post['text'].$post['special_comment']));
			    $rss->itemAuthor('anonymous');
			    $rss->itemGuid('http://'. TemplateHelper::getSiteUrl() .'/news/res/'. $post['id'] .'/', true);
			    $rss->itemPubDate(date('D, d M Y H:i:s O', $post['created_at']));
			    $rss->addItem();
			}

			$result = $rss->RSSdone();
		}

		EventModel::getInstance()
			-> Broadcast('view_rss_hidden_post');

		$template -> headerOk();
		$template -> headerContentType('application/rss+xml', 'UTF-8');
		echo $result;

		return false;
	}

	/**
	 * Действие выбора сортировки:
	 */
	public function sortAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$session -> persistenceSet('posts_sortby', $_GET['sortby']);

		$template -> headerSeeOther($_SERVER['HTTP_REFERER']);
		return false;
	}
}

/**
 * Обработчики событий:
 */
EventModel::getInstance()
	/**
	 * Статистика просмотра:
	 */
	-> AddEventListener('view_*', function() {
		Blog_BlogStatisticsModel::updateGlobalVisitors();
	})
	/**
	 * Статистика просмотра поста:
	 */
	-> AddEventListener('view_post', function($data) {
		Blog_BlogStatisticsModel::updatePostStats($data, false);
	});


code:
<?php
/**
 * Контроллер разделов на одинчане:
 */

class BoardController extends BaseController
{
	/**
	 * Создание темы:
	 */
	public function createAction(Application $application, Template $template)
	{
		$board   = new Board_BoardModel($_GET['board']);
		$session = Session::getInstance();

		if ($session -> isJustCreated())
			return false;

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$validator = new ValidatorHelper($_POST);

			$validator -> assertLength('title', 60,     $_GET['board'] != 'int' ? 'Заголовок слишком длинный' : 'Title is too long');

			if (!array_key_exists('upload', $_FILES) || $_FILES['upload']['error'] == 4)
				$validator -> assertExists('text',     $_GET['board'] != 'int' ? 'Не введен текст' : 'Please enter the message');

			$validator -> assertLength('text', 2048,  $_GET['board'] != 'int' ? 'Текст слишком длинный' : 'Post\'s text is too long');
			$validator -> assertNotExists('email',    $_GET['board'] != 'int' ? 'Заполнено лишнее поле' : 'Spam omitted');

			$validator -> assertTrue(
				'text', ControlModel::checkSpam($_POST['text']),
				$_GET['board'] != 'int' ? 'Ваше сообщение определено, как спам' : 'Spam message was detected'
			);

			$validator -> assertTrue(
				'upload', Board_UploadModel::checkUpload(),
				$_GET['board'] != 'int' ? 'Изображение имеет неизвестный формат, либо превышает допустимый размер' :
				                          'Unknown image type or file is too heavy to process'
			);

			$validator -> assertTrue(
				'timeout', ControlModel::getBoardPostInterval() == 0,
				$_GET['board'] != 'int' ?
				    'Таймаут '. TemplateHelper::ending(ControlModel::getBoardPostInterval(), 'секунда', 'секунды', 'секунд') :
				    ControlModel::getBoardPostInterval() .' seconds timeout'
			);

			$validator -> assertLengthMore('captcha', 1, $_GET['board'] != 'int' ? 'Не введена капча' : 'Please enter the Captcha code');
			if ($validator -> fieldValid('captcha'))
				$validator -> assertEqual(
					'captcha', $session -> instantGet('captcha_board', false),
					$_GET['board'] != 'int' ?
					    'Капча введена неверно' :
					    'Captcha code is incorrect'
				);

			if ($validator -> isValid())
			{
				$id = $board -> createThread($_POST);

				$template -> headerSeeOther(
					'/'. $board -> getId() .'/res/'. $id .'/'
				);
				return false;
			}

			$session  -> instantSet('captcha_board', true);
			$session -> instantSet('board_errors', $validator -> getValidationResults());
			$session -> instantSet('board_form', $_POST);
		}

		$template -> headerSeeOther(
			'/'. $board -> getId() .'/'
		);
		return false;
	}

	/**
	 * Создание темы (форма ajax):
	 */
	public function createAjaxFormAction(Application $application, Template $template)
	{
		$board   = new Board_BoardModel($_GET['board']);
		$session = Session::getInstance();

		if ($session -> isJustCreated())
			return false;

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$validator = new ValidatorHelper($_POST);

			$validator -> assertLength('title', 60,     $_GET['board'] != 'int' ? 'Заголовок слишком длинный' : 'Title is too long');

			if (!array_key_exists('upload', $_FILES) || $_FILES['upload']['error'] == 4)
				$validator -> assertExists('text',      $_GET['board'] != 'int' ? 'Не введен текст' : 'Please enter the message');

			$validator -> assertLength('text', 2048, $_GET['board'] != 'int' ? 'Текст слишком длинный' : 'Post\'s text is too long');
			$validator -> assertNotExists('email',   $_GET['board'] != 'int' ? 'Заполнено лишнее поле' : 'Spam omitted');

			$validator -> assertTrue(
				'text', ControlModel::checkSpam($_POST['text']),
				$_GET['board'] != 'int' ? 'Ваше сообщение определено, как спам' : 'Spam message was detected'
			);

			$validator -> assertTrue(
				'upload', Board_UploadModel::checkUpload(),
				$_GET['board'] != 'int' ? 'Изображение имеет неизвестный формат, либо превышает допустимый размер' :
				                          'Unknown image type or file is too heavy to process'
			);

			$validator -> assertTrue(
				'timeout', ControlModel::getBoardPostInterval() == 0,
				$_GET['board'] != 'int' ?
				    'Таймаут '. TemplateHelper::ending(ControlModel::getBoardPostInterval(), 'секунда', 'секунды', 'секунд') :
				    ControlModel::getBoardPostInterval() .' seconds timeout'
			);

			$validator -> assertLengthMore('captcha', 1, $_GET['board'] != 'int' ? 'Не введена капча' : 'Please enter the Captcha code');
			if ($validator -> fieldValid('captcha'))
				$validator -> assertEqual(
					'captcha', $session -> instantGet('captcha_board', false),
					$_GET['board'] != 'int' ?
					    'Капча введена неверно' :
					    'Captcha code is incorrect'
				);

			if ($validator -> isValid())
			{
				$id = $board -> createThread($_POST);

				$template -> renderJSONP('board_callback', array('sucess' => true, 'id' => $id));
				return false;
			}

			$session  -> instantSet('captcha_board', true);
			$template -> renderJSONP('board_callback', array('success' => false, 'errors' => $validator -> getValidationResults()));
			return false;
		}

		return false;
	}

	/**
	 * Создание поста:
	 */
	public function createPostAction(Application $application, Template $template)
	{
		$board   = new Board_BoardModel($_GET['board']);
		$session = Session::getInstance();

		if ($session -> isJustCreated())
			return false;

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$validator = new ValidatorHelper($_POST);

			if (!array_key_exists('upload', $_FILES) || $_FILES['upload']['error'] == 4)
				$validator -> assertExists('text', $_GET['board'] != 'int' ? 'Не введен текст' : 'Please enter the message');

			$validator -> assertLength('text', 2048, $_GET['board'] != 'int' ? 'Текст слишком длинный' : 'Post\'s text is too long');
			$validator -> assertNotExists('email',   $_GET['board'] != 'int' ? 'Заполнено лишнее поле' : 'Spam omitted');

			$validator -> assertTrue(
				'timeout', ControlModel::getBoardPostInterval() == 0,
				$_GET['board'] != 'int' ?
				    'Таймаут '. TemplateHelper::ending(ControlModel::getBoardPostInterval(), 'секунда', 'секунды', 'секунд') :
				    ControlModel::getBoardPostInterval() .' seconds timeout'
			);

			if ($validator -> fieldValid('timeout'))
			{
				ControlModel::checkBoardPost($_POST['text']);
			}

			if (ControlModel::isCommentCaptcha()) {
				$validator -> assertLengthMore('captcha', 1, $_GET['board'] != 'int' ? 'Не введена капча' : 'Please enter the Captcha code');

			if ($validator -> fieldValid('captcha'))
				$validator -> assertEqual(
					'captcha', $session -> instantGet('captcha_board_comment', false),
					$_GET['board'] != 'int' ?
					    'Капча введена неверно' :
					    'Captcha code is incorrect'
				);
			}

			$validator -> assertTrue(
				'text', ControlModel::checkSpam($_POST['text']),
				$_GET['board'] != 'int' ? 'Ваше сообщение определено, как спам' : 'Spam message was detected'
			);

			$validator -> assertTrue(
				'upload', Board_UploadModel::checkUpload(),
				$_GET['board'] != 'int' ? 'Ошибка загрузки изображения' :
				                          'File upload error'
			);

			if (ControlModel::isCommentCaptcha()) {
				$validator -> assertLengthMore('captcha', 1, $_GET['board'] != 'int' ? 'Не введена капча' : 'Please enter the Captcha code');
				if ($validator -> fieldValid('captcha'))
					$validator -> assertEqual(
						'captcha', $session -> instantGet('captcha_board_comment', false),
					    $_GET['board'] != 'int' ?
					        'Капча введена неверно' :
					        'Captcha code is incorrect'
					);
			}

			if ($validator -> isValid())
			{
				$id = $board -> createPost($_POST['parent_id'], $_POST);

				$session -> persistenceSet('captcha_mode', false);
				$session -> persistenceSet('captcha_mode_length', @$settings['captcha_length']);

				$template -> headerSeeOther(
					'/'. $board -> getId() .'/res/'. $_POST['parent_id'] .'/#'. $id
				);
				return false;
			}

			$session -> instantSet('board_post_errors', $validator -> getValidationResults());
			$session -> instantSet('board_post_form', $_POST);
		}

		$template -> headerSeeOther(
			'/'. $board -> getId() .'/res/'. $_POST['parent_id'] .'/'
		);
		return false;
	}

	/**
	 * Создание поста (форма ajax):
	 */
	public function createPostAjaxFormAction(Application $application, Template $template)
	{
		$board   = new Board_BoardModel($_GET['board']);
		$session = Session::getInstance();

		if ($session -> isJustCreated())
			return false;

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$validator = new ValidatorHelper($_POST);

			if (!array_key_exists('upload', $_FILES) || $_FILES['upload']['error'] == 4)
				$validator -> assertExists('text', $_GET['board'] != 'int' ? 'Не введен текст' : 'Please enter the message');

			$validator -> assertLength('text', 2048, $_GET['board'] != 'int' ? 'Текст слишком длинный' : 'Post\'s text is too long');
			$validator -> assertNotExists('email',   $_GET['board'] != 'int' ? 'Заполнено лишнее поле' : 'Spam omitted');

			$validator -> assertTrue(
				'timeout', ControlModel::getBoardPostInterval() == 0,
				$_GET['board'] != 'int' ?
				    'Таймаут '. TemplateHelper::ending(ControlModel::getBoardPostInterval(), 'секунда', 'секунды', 'секунд') :
				    ControlModel::getBoardPostInterval() .' seconds timeout'
			);

			if ($validator -> fieldValid('timeout'))
			{
				ControlModel::checkBoardPost($_POST['text']);
			}

			if (ControlModel::isCommentCaptcha()) {
				$validator -> assertLengthMore('captcha', 1, $_GET['board'] != 'int' ? 'Не введена капча' : 'Please enter the Captcha code');

			if ($validator -> fieldValid('captcha'))
				$validator -> assertEqual(
					'captcha', $session -> instantGet('captcha_board_comment', false),
					    $_GET['board'] != 'int' ?
					        'Капча введена неверно' :
					        'Captcha code is incorrect'
					);
			}

			$validator -> assertTrue(
				'text', ControlModel::checkSpam($_POST['text']),
				$_GET['board'] != 'int' ? 'Ваше сообщение определено, как спам' : 'Spam message was detected'
			);

			$validator -> assertTrue(
				'upload', Board_UploadModel::checkUpload(),
				$_GET['board'] != 'int' ? 'Ошибка загрузки изображения' :
				                          'File upload error'
			);

			$session  -> instantSet('captcha_board_comment', true);

			if ($validator -> isValid())
			{
				$id = $board -> createPost($_POST['parent_id'], $_POST);

				$session -> persistenceSet('captcha_mode', false);
				$session -> persistenceSet('captcha_mode_length', @$settings['captcha_length']);

				$template -> renderJSONP('comment_callback', array('sucess' => true, 'id' => $id));
				return false;
			}

			$template -> renderJSONP('comment_callback', array('success' => false, 'errors' => $validator -> getValidationResults()));
		}

		return false;
	}

	/**
	 * Получение поста:
	 */
	public function getAction(Application $application, Template $template)
	{
		$board = new Board_BoardModel($_GET['board']);

		if ($board -> existsPost($_GET['id']))
		{
			$post = $board -> getPost($_GET['id']);

			if ($post['parent_id'] == null)
			{
				$template -> headerSeeOther(
					'/'. $board -> getId() .'/res/'. $post['post_id'] .'/'
				);
				return false;
			}

			$template -> headerSeeOther(
				'/'. $board -> getId() .'/res/'. $post['parent_id'] .'/#'. $post['post_id']
			);
			return false;
		}

		$template -> headerSeeOther(
			'/'. $board -> getId() .'/'
		);
		return false;
	}

	/**
	 * Получение поста(ов) (ajax):
	 */
	public function getAjaxAction(Application $application)
	{
		$board = new Board_BoardModel($_GET['board']);
		$settings = $board -> getSettings();

		if (array_key_exists('id', $_GET))
		{
			if (is_array($_GET['id']))
			{
				$result = array();
				foreach ($_GET['id'] as $id)
				{
					if ($post = $board -> getPost($id))
					{
						$post['created_at']  = TemplateHelper::date($_GET['board'] != 'int' ? 'd M Y @ H:i:s' : 'Y-m-d @ H:i:s', $post['created_at']);
						$post['board_title'] = $settings['title'];
						$post['author']      = array($post['author'] ? $post['author'] : 'anonymous', HomeBoardHelper::getBoard($post['author']));
						unset($post['ip']);

						$result[] = $post;
					}
				}

				return $result;
			}
			else
			{
				if ($post = $board -> getPost($_GET['id']))
				{
					$post['created_at']  = TemplateHelper::date($_GET['board'] != 'int' ? 'd M Y @ H:i:s' : 'Y-m-d @ H:i:s', $post['created_at']);
					$post['board_title'] = $settings['title'];
					$post['author']      = array($post['author'] ? $post['author'] : 'anonymous', HomeBoardHelper::getBoard($post['author']));
					unset($post['ip']);
					return $post;
				}
				return false;
			}
		}

		if (array_key_exists('thread_id', $_GET))
		{
			if ($thread = $board -> getThread($_GET['thread_id'], true))
			{
				$result = array();
				foreach ($thread['posts'] as $id => $post)
				{
					$post['created_at']  = TemplateHelper::date($_GET['board'] != 'int' ? 'd M Y @ H:i:s' : 'Y-m-d @ H:i:s', $post['created_at']);
					$post['board_title'] = $settings['title'];
					$post['author']     = array($post['author'] ? $post['author'] : 'anonymous', HomeBoardHelper::getBoard($post['author']));
					unset($post['ip']);
					$result[] = $post;
				}

				return $result;
			}
		}
		return false;
	}

	/**
	 * Удаление поста:
	 */
	public function removeAction(Application $application, Template $template)
	{
		$session = Session::getInstance();

		if ($session -> isAdminSession() && $_GET['prompt'] == 'yes')
		{
			echo 'Done!';
			fastcgi_finish_request();

			$board = new Board_BoardModel($_GET['board']);
			$board -> removeBoard();

			return false;
		}

		// Не имплементировано:
		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);
		return false;
	}

	/**
	 * Удаление поста (ajax):
	 */
	public function removeAjaxAction(Application $application)
	{
		$board   = new Board_BoardModel($_GET['board']);
		$session = Session::getInstance();

		if ($board -> existsPost($_GET['id']))
		{
			$post = $board -> getPost($_GET['id']);

			if ((!empty($post['password']) && $post['password'] == $_GET['password']))
			{
				if ($board -> existsThread($_GET['id']))
					$board -> removeThread($_GET['id']);
				else
					$board -> removePost($_GET['id']);

				return true;
			}

			if ($session -> isAdminSession())
			{
                if (@$_GET['delall'] == true)
                {
                    $board -> removePostsByAuthor($_GET['id']);
                    return true;
                }
                else
                {
				    if ($board -> existsThread($_GET['id']))
					    $board -> removeThread($_GET['id']);
				    else
					    $board -> removePost($_GET['id']);

				    if ($board -> getId() != 'alone')
					    ControlModel::logModEvent(
						    date("d-m-Y H:i") .' '. $_SESSION['auth']['name'] .'<br /> удалил пост №'. $_GET['id'] .' в разделе '. $_GET['board']
					    );
                }
			}

			return false;
		}
		return true;
	}

	/**
	 * Действие: добавление/удаление из избранного:
	 */
	public function toggleFavoriteAction(Application $application, Template $template)
	{
		$state = Board_FavoritesModel::ToggleFavoritePost($_GET['board'], $_GET['id']);
		$key = Session::getInstance() -> getKey();

		if ($state)
			Board_BoardModel::subscribeThread($key, $_GET['board'] .'_'. $_GET['id']);
		else
			Board_BoardModel::unsubscribeThread($key, $_GET['board'] .'_'. $_GET['id']);

		$template -> headerSeeOther(
			$_SERVER['HTTP_REFERER']
		);
		return false;
	}

	/**
	 * Действие: добавление/удаление из избранного (ajax):
	 */
	public function toggleFavoriteAjaxAction(Application $application)
	{
		$state = Board_FavoritesModel::ToggleFavoritePost($_GET['board'], $_GET['id']);
		$key = Session::getInstance() -> getKey();

		if (Board_FavoritesModel::IsFavoritePost($_GET['board'], $_GET['id']))
			Board_BoardModel::subscribeThread($key, $_GET['board'] .'_'. $_GET['id']);
		else
			Board_BoardModel::unsubscribeThread($key, $_GET['board'] .'_'. $_GET['id']);

		return array('favorite' => $state);
	}

	/**
	 * Действие обновление статистики:
	 */
	public function postStatsAjaxAction(Application $application)
	{
		Board_StatisticsModel::updatePostStats($_GET['board'], $_GET['thread_id'], $_GET['writing']);
		return Board_StatisticsModel::getPostStats($_GET['board'], $_GET['thread_id']);
	}

	/**
	 * Просмотр списка тредов борды:
	 */
	public function viewThreadsAction(Application $application, Template $template)
	{
		$board   = new Board_BoardModel($_GET['board']);

		$session  = Session::getInstance();
		$settings = $board -> getSettings();
		$key = $session -> getKey();

		$template -> setParameter('title',             $settings['title']);
		$template -> setParameter('description', $settings['description']);
		$template -> setParameter('board_id',      $board -> getId());

		$page   = @$_GET['page'] ? $_GET['page'] : 0;

		$this['posts'] = $board -> getThreads($page, $pages);
		$this['form_errors'] = $session -> instantGet('board_errors', array());
		$this['board_form'] = $session -> instantGet('board_form', array());
		$this['subscribed'] = Board_BoardModel::subscribedBoard($key, $_GET['board']);

		$template -> setParameter('total_pages',  ceil($pages - 1));
		$template -> setParameter('current_page', $page);
		$template -> setParameter('link_pages', '/'. $board -> getId() .'/%d/');

		$session  -> instantSet('captcha_board', true);
		$session  -> instantSet('captcha_board_comment', true);

		EventModel::getInstance()
			-> Broadcast('view_board_threads', $_GET['board']);

		return true;
	}

	/**
	 * Просмотр треда:
	 */
	public function viewThreadAction(Application $application, Template $template)
	{
		$board   = new Board_BoardModel($_GET['board']);

		$session  = Session::getInstance();
		$settings = $board -> getSettings();

		$template -> setParameter('title', isset($settings) ? $settings['title'] : '');
		$template -> setParameter('description', isset($settings) ? $settings['description'] : '');
		$template -> setParameter('board_id', $board -> getId());

		$thread = $board -> getThread($_GET['thread_id']);

		if ($thread === false) {
			$template -> headerNotFound();
		}

		$this['form_errors'] = $session -> instantGet('board_post_errors', array());
		$this['board_form'] = $session -> instantGet('board_post_form', array());

		$stats = Board_StatisticsModel::getPostStats($thread['board_id'], $thread['id']);

		$template -> setParameter('total_read',   isset($stats['online']) ? $stats['online'] : 0);
		$template -> setParameter('total_write',  isset($stats['writers']) ? $stats['writers'] : 0);
		$template -> setParameter('total_unique', isset($stats['unique']) ? $stats['unique'] : 0);

		$session  -> instantSet('captcha_board_comment', true);

		$this['post'] = $thread;

		EventModel::getInstance()
			-> Broadcast('view_board_thread', array($_GET['board'], $_GET['thread_id']));

		return true;
	}

	/**
	 * Просмотр избранных тредов:
	 */
	public function viewFavoritesAction(Application $application, Template $template)
	{
		$session  = Session::getInstance();
		$template -> setParameter('title', 'Избранные треды');
		$template -> setParameter('description', 'Список постов, отмеченных вами');
		$template -> setParameter('board_id', 'fav');

		$this['posts'] = Board_FavoritesModel::GetFavoritePosts();
		$session  -> instantSet('captcha_board_comment', true);

		EventModel::getInstance()
			-> Broadcast('view_board_favorites');

		return true;
	}

	/**
	 * Изменение заголовков:
	 */
	public function changeTitleAjaxAction(Application $application)
	{
		$session  = Session::getInstance();
		if ($session -> isAdminSession())
		{
			$board = new Board_BoardModel($_GET['board']);
			$board -> setSettings(array(
				'title' => $_GET['title'],
				'description' => $_GET['description']
			));
		}
		return false;
	}

	/**
	 * Просмотр последних постов:
	 */
	public function lastBoardPostsAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Последние посты разделов');

		$page   = @$_GET['page'] ? $_GET['page'] : 0;
		$this['posts'] = Board_BoardModel::getLastPosts($page, $pages);

		$template -> setParameter('total_pages',  ceil($pages - 1));
		$template -> setParameter('current_page', $page);
		$template -> setParameter('link_pages', '/service/last_board_posts/%d/');

		return true;
	}

	/**
	 * Подписка на раздел:
	 */
	public function subscribeBoardAjaxAction(Application $application, Template $template)
	{
		$key = Session::getInstance() -> getKey();
		return Board_BoardModel::subscribeBoard($key, $_GET['board']);
	}

	/**
	 * Отписка от раздела:
	 */
	public function unsubscribeBoardAjaxAction(Application $application, Template $template)
	{
		$key = Session::getInstance() -> getKey();
		return Board_BoardModel::unsubscribeBoard($key, $_GET['board']);
	}

	/**
	 * Проверка нотифаера:
	 */
	public function notifyCheckAjaxAction(Application $application)
	{
		$kvs = KVS::getInstance();
		$key = Session::getInstance() -> getKey();
		$kvs -> hashRemove('Notify', $key, 'messages', $_GET['board'] .'_'. $_GET['id']);
		return true;
	}

	/**
	 * Получение нотифаеров:
	 */
	public function notifyGetAjaxAction(Application $application)
	{
		$kvs = KVS::getInstance();
		$key = Session::getInstance() -> getKey();

		$messages = $kvs -> hashGetAll('Notify', $key, 'messages');
		$kvs -> remove('Notify', $key, 'messages');

		return $messages;
	}
}

/**
 * Обработчики событий:
 */
EventModel::getInstance()
	/**
	 * Статистика просмотра:
	 */
	-> AddEventListener('view_*', function() {
		Blog_BlogStatisticsModel::updateGlobalVisitors();
	})
	/**
	 * Статистика просмотра поста:
	 */
	-> AddEventListener('view_board_thread', function($data) {
		Board_StatisticsModel::updatePostStats($data[0], $data[1], false);
	});