code:
<?php
/**
 * Контроллер чатов:
 */
class ChatController extends BaseController
{
	/**
	 * Список комнат чата:
	 */
	public function indexAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Анонимные чаты');
		$template -> setParameter('section', 'all');

		$rooms = Chat_ChatRoomsModel::GetRooms();

		$this['favorites'] = $rooms['favorites'];
		$this['public']    = $rooms['public'];

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

		return true;
	}

	/**
	 * Добавление комнаты чата:
	 */
	public function addAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		$this['form_errors'] = array();
		$this['blog_form']   = array();

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$validator = new ValidatorHelper($_POST);
			$validator -> assertExists('captcha_key', 'Введите капчу');

			if ($_POST['captcha_key'])
				$validator -> assertEqual(
					'captcha', $session -> instantGet('captcha_'. $_POST['captcha_key'], false),
					'Капча введена неверно'
				);

			$validator -> assertExists('title',              'Не введен заголовок');
			$validator -> assertLength('title',       25,    'Заголовок слишком длинный');
			$validator -> assertExists('description',        'Не введен вводный текст');
			$validator -> assertLength('description', 75,    'Вводный текст слишком длинный');
			$validator -> assertExists('controlword',        'Не введен контрольный пароль');

			if ($validator -> fieldValid('title'))
				$validator -> assertLengthMore('title', 3, 'Заголовок слишком короткий');

			if ($validator -> fieldValid('description'))
				$validator -> assertLengthMore('description', 5, 'Описание слишком короткое');

			if ($validator -> fieldValid('controlword'))
				$validator -> assertLengthMore('controlword', 3, 'Контрольный пароль слишком короткий');

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

			if ($validator -> isValid())
			{
				$id = Chat_ChatRoomsModel::CreateRoom($_POST);

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

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

		$key     = 'chat';
		$template -> setParameter('captcha_key', $key);
		$session  -> instantSet('captcha_'.$key, true);

		$template -> setParameter('title', 'Добавление комнаты чата');
		$template -> setParameter('section', 'add');
		return true;
	}

	/**
	 * Добавление комнаты чата (ajax):
	 */
	public function addAjaxAction(Application $application)
	{
		$validator = new ValidatorHelper($_POST);
		$validator -> assertExists('title',              'Не введен заголовок');
		$validator -> assertLength('title',       25,    'Заголовок слишком длинный');
		$validator -> assertExists('description',        'Не введен вводный текст');
		$validator -> assertLength('description', 75,   'Вводный текст слишком длинный');
		$validator -> assertExists('controlword',        'Не введен контрольный пароль');

		if ($validator -> fieldValid('controlword'))
			$validator -> assertLengthMore('controlword', 3, 'Контрольный пароль слишком короткий');

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

	/**
	 * Установка избранных комнат:
	 */
	public function setFavoritesAjaxAction(Application $application)
	{
		Chat_ChatRoomsModel::UpdateFavorites(explode(',', $_POST['favorites']));
		return true;
	}

	/**
	 * Общая комната чата:
	 */
	public function commonAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Общий чат');
		$template -> setParameter('section', 'common');

        Chat_ChatRoomsModel::CreateCommonRoom();

        $this['room'] = Chat_ChatRoomsModel::GetRoom(substr(md5('common') . md5('common'), 0, 45));

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

		return true;
	}

	/**
	 * Просмотр лога чата:
	 */
	public function logAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Лог комнаты чата');

	    if ($_SERVER['REQUEST_METHOD'] == 'POST')
	        $channel = Chat_ChatModel::TestChatChannel($_GET['id'], $_POST['password']);
        else
            $channel = Chat_ChatModel::TestChatChannel($_GET['id'], false);

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

        if ($channel != false) {
	        $log = Chat_ChatModel::GetLog($channel);
	        $this['log'] = $log;
	    }

	    return true;
	}

	/**
	 * Комната чата:
	 */
	public function chatAction(Application $application, Template $template)
	{
            if ($_GET['id'])
	         $room = Chat_ChatRoomsModel::GetRoom($_GET['id']);
	    elseif ($_GET['alias'])
                 $room = Chat_ChatRoomsModel::GetRoomByAlias($_GET['alias']);

	    if ($room)
	    {
		    $template -> setParameter('title', '«'. $room['title'] .'» чат');
		    $this['room'] = $room;

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

		    return true;
		}

		$template -> setParameter('title', 'Комната не существует');

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

		return true;
	}

	/**
	 * Операции с чатом (ajax):
	 */
	public function chatAjaxAction(Application $application)
	{
	    $session = Session::getInstance();
	    if ($_SERVER['REQUEST_METHOD'] == 'GET')
	    {
	        switch(@$_GET['command']) {
	            case 'enter':
	                return Chat_ChatModel::TestChatChannel($_GET['id']);

	            case 'ping':

	                EventModel::getInstance()
				        -> Broadcast('view_chat', $_GET['id']);

	                return true;

	            case 'welcome':
	                $room = Chat_ChatRoomsModel::GetRoom($_GET['id']);
	                $info = Chat_ChatRoomsModel::GetInfo($_GET['id']);

	                EventModel::getInstance()
				        -> Broadcast('view_chat', $_GET['id']);

	                return array('title' => $room['title'], 'description' => $room['description'], 'info' => $info);
	        }
	    }
	    else
	    {
	        switch(@$_POST['command']) {
	            case 'checkpassword':
	                return Chat_ChatModel::TestChatChannel($_GET['id'], $_POST['password']);

	            case 'message':
	                if (($room = Chat_ChatRoomsModel::GetRoom($_GET['id'])) == false)
	                    return array('error' => true, 'errors' => 'Комнаты чата больше не существует.');

	                if (strpos($_POST['message'], '/') === 0)
	                {
	                    switch(true) {
	                        case (strpos($_POST['message'], '/help') === 0):
                                return array('error' => true, 'errors' => '

						                Доступные команды:

Изменение названия комнаты:

/change_title пароль_управления новое название

Изменение описания комнаты:

/change_description пароль_управления новое описание

Изменение информационного сообщения:

/set_info пароль_управления сообщение

«Девойс» (лишение права голоса):

/devoice пароль_управления номер сообщения

«Энвойс» (разблокировка):

/envoice пароль_управления хеш (выводится при девойсе)

Очистка блокировок:

/envoice пароль_управления

Блокировка слова:

/blockword пароль_управления слово или фраза

Просмотр блокированых слов:

/ls_blockwords пароль_управления

Разблокировка слова:

/rm_blockword пароль_управления номер_слова (из списка слов)

Статус публичности:

/set_public пароль_управления 0/1(0 - скрытая, 1 - публичная)

Удаление комнаты:

/remove_room пароль_управления

					        ');

					        case (strpos($_POST['message'], '/get_controlword') === 0):
                                if ($session -> isAdminSession())
                                    return array('error' => true, 'errors' => 'Пароль управления: '. $room['controlword']);

                                return array('error' => true, 'errors' => 'Неверная команда.');

                            case (strpos($_POST['message'], '/devoice') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $message_id = preg_replace("/[^0-9]/", '', array_shift($parts));

                                $key = Chat_ChatModel::deVoice($_GET['id'], $message_id);

                                if ($key !== false)
                                    Chat_ChatModel::AddInfoMessage($_GET['id'], 'Автор сообщения >>'. $message_id .' ('.$key.') был лишен голоса.', $room['password']);
                                else
                                    return array('error' => true, 'errors' => 'Сообщение слишком старое.');

                                return array('error' => false, 'id' => -1);

                            case (strpos($_POST['message'], '/envoice') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $key = array_shift($parts);

                                if ($key)
                                    Chat_ChatModel::enVoice($_GET['id'], $key);
                                else
                                    Chat_ChatModel::clearVoice($_GET['id']);

                                return array('error' => false, 'id' => -1);

                           case (strpos($_POST['message'], '/set_alias') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $alias = $parts[0];

                                if (!preg_match('/^[0-9A-z\-]{4,20}$/i', $alias))
                                    return array('error' => true, 'errors' => 'Запрещенные символы в алиасе.');

				                if (Chat_ChatRoomsModel::SetAlias($_GET['id'], $alias)) {
		                                        Chat_ChatRoomsModel::EditRoom($_GET['id'], array('alias' => $alias));
		                                        Chat_ChatModel::AddInfoMessage($_GET['id'], 'Установлен новый алиас, теперь комната доступна по адресу https://'. TemplateHelper::getSiteUrl() .'/chat/'. $alias .'/');
                                                	return array('error' => false, 'id' => -1);
				                }
                                return array('error' => true, 'errors' => 'Не удалось установить алиас (возможно он уже занят?).');

                           case (strpos($_POST['message'], '/remove_alias') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');
				                Chat_ChatRoomsModel::RemoveAlias($room['alias']);
				                Chat_ChatRoomsModel::EditRoom($_GET['id'], array('alias' => null));
                                return array('error' => false, 'id' => -1);

			                case (strpos($_POST['message'], '/blockword') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $blocked = implode(' ', $parts);
                                Chat_ChatModel::blockWord($_GET['id'], $blocked);
                                return array('error' => false, 'id' => -1);

			                case (strpos($_POST['message'], '/ls_blockwords') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $result = array();
				                foreach(Chat_ChatModel::getBlockList($_GET['id']) as $num => $value) {
					                $result[] = $num .': '. $value;
				                }
                                return array('error' => true, 'errors' => sizeof($result) ? 'Запрещенные слова:
'. implode('
', $result) : 'Нет слов в блокировке.');

			                case (strpos($_POST['message'], '/rm_blockword') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $blocked = array_shift($parts);
                                Chat_ChatModel::unblockWord($_GET['id'], $blocked);
                                return array('error' => false, 'id' => -1);

                            case (strpos($_POST['message'], '/change_title') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $title = implode(' ', $parts);

                                if (mb_strlen($title, 'UTF-8') > 25)
                                    return array('error' => true, 'errors' => 'Слишком длинное название.');

                                Chat_ChatRoomsModel::EditRoom($_GET['id'], array('title' => $title));
                                Chat_ChatModel::AddInfoMessage($_GET['id'], 'Название комнаты изменено с «'. $room['title'] .'» на «'. $title .'»', $room['password']);

                                return array('error' => false, 'id' => -1);

                            case (strpos($_POST['message'], '/change_description') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $description = implode(' ', $parts);

                                if (mb_strlen($description, 'UTF-8') > 75)
                                    return array('error' => true, 'errors' => 'Слишком длинное описание.');

                                Chat_ChatRoomsModel::EditRoom($_GET['id'], array('description' => $description));
                                Chat_ChatModel::AddInfoMessage($_GET['id'], 'Описание комнаты изменено с «'. $room['description'] .'» на «'. $description .'»', $room['password']);

                                return array('error' => false, 'id' => -1);

                            case (strpos($_POST['message'], '/a') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                $symbol = array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $message = ''.$symbol.' '. implode(' ', $parts);
                                $id = Chat_ChatModel::AddMessage($_GET['id'], $message, $_POST['password']);

	                            return array('error' => false, 'id' => $id);

                            case (strpos($_POST['message'], '/set_info') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $info = implode(' ', $parts);

                                if (mb_strlen($info, 'UTF-8') > 612)
                                    return array('error' => true, 'errors' => 'Слишком длинное информационное сообщение.');

                                $info = Chat_ChatRoomsModel::SetInfo($_GET['id'], $info);
                                Chat_ChatModel::AddInfoMessage($_GET['id'], 'Новое информационное сообщение:'. $info, $room['password']);
                                return array('error' => false, 'id' => -1);

                            case (strpos($_POST['message'], '/set_public') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                $state = array_shift($parts);
                                Chat_ChatRoomsModel::EditRoom($_GET['id'], array('public' => (bool)$state));
                                Chat_ChatModel::AddInfoMessage($_GET['id'], 'Изменен статус комнаты: '. ((bool)$state ? 'Публичная' : 'Приватная'), $room['password']);

                                return array('error' => false, 'id' => -1);

                            case (strpos($_POST['message'], '/remove_room') === 0):
                                $parts = explode(' ', $_POST['message']);
                                array_shift($parts);

                                if ($room['controlword'] !== array_shift($parts))
                                    return array('error' => true, 'errors' => 'Неверный пароль управления.');

                                Chat_ChatModel::AddErrorMessage($_GET['id'], 'Комната была удалена.', $room['password']);
                                Chat_ChatRoomsModel::RemoveRoom($_GET['id']);

                                return array('error' => false, 'id' => -1);
                            default:
                                return array('error' => true, 'errors' => 'Неверная команда.');
                        }
                    }

	                $validator = new ValidatorHelper($_POST);
	                $validator -> assertExists('message',       'Не введено сообщение');
	                $validator -> assertLength('message', 1024, 'Сообщение слишком длинное');

	                EventModel::getInstance()
				        -> Broadcast('view_chat', $_GET['id']);

				    if ($validator -> isValid())
				    {
				        if (!Chat_ChatModel::hasVoice($_GET['id'], $_SERVER['REMOTE_ADDR']))
				            return array('error' => true, 'errors' => 'Вам запрещено отправлять сообщения в этот чат.');

					$blocked_words = Chat_ChatModel::getBlockList($_GET['id']);
					$messagefilter = htmlspecialchars_decode(strtr($_POST['message'], array('*' => '', '_' => '', '=' => '', '-' => '')));
					foreach($blocked_words as $word) {
						if (preg_match('~'. str_replace('~', '\~', $word) .'~ui', $messagefilter) == true)
							return array('error' => true, 'errors' => 'Запрещенное слово из вордфильтра.');
					}

				        similar_text(trim($session -> persistenceGet('last_chat_text', '')), trim($_POST['message']), $percent);

			            if ($percent < 90) {
				            Chat_ChatRoomsModel::TouchRoom($_GET['id']);
				            $session -> persistenceSet('last_chat_text', $_POST['message']);

				            $id = Chat_ChatModel::AddMessage($_GET['id'], $_POST['message'], $_POST['password']);

	                        return array('error' => false, 'id' => $id);
	                    }

	                    return array('error' => true, 'errors' => 'Флуд контроль, сообщение проигнорировано.');
	                }
	                else
	                    return array('error' => true, 'errors' => implode(', ', $validator -> getValidationResults()));
	        }
	    }
	}
}

/**
 * Обработчики событий:
 */
EventModel::getInstance()
	/**
	 * Статистика просмотра:
	 */
	-> AddEventListener('view_*', function($data) {
		if ($data)
		    Chat_ChatRoomsModel::SetRoomOnline($data);

		Blog_BlogStatisticsModel::updateGlobalVisitors();
	});


code:
<?php
/**
 * Обработчик ошибки 403:
 */
class Errors_Error403Controller extends BaseController
{
	public function indexAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Доступ запрещен');
		return true;
	}
}

repo_name: devarped/pierwszykanal

license: mit


code:
<?php
/**
 * Обработчик ошибки 404:
 */
class Errors_Error404Controller extends BaseController
{
	public function indexAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Ничего не найдено');
		$template -> headerNotFound();
		return true;
	}
}


code:
<?php
/**
 * Обработчик ошибки 500:
 */
class Errors_Error500Controller extends BaseController
{
	public function indexAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Свершилась ошибка');
		return true;
	}
}


code:
<?php
/**
 * Контроллер авторизации:
 */
class Generic_AuthorizeController extends BaseController
{
	/**
	 * Метод авторизации:
	 */
	public function authorizeAjaxAction(Application $application)
	{
		$session = Session::getInstance();

		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			if ($_POST['hash'] == 0)
				return array('authorized' => $session -> isModerator($_POST['key']));
			else
				return array('authorized' => $session -> isAdmin($_POST['key'], $_POST['hash']));
		}
		return array('authorized' => false);
	}
}


code:
<?php
/**
 * Контроллер каптчи:
 */
class Generic_CaptchaController
{
	/**
	 * Вывод каптчи:
	 */
	public function indexAction(Application $application, Template $template)
	{
		$kvs = KVS::getInstance();
/*
		if ($kvs -> exists(__CLASS__, 'captcha_ban', $_SERVER['REMOTE_ADDR']))
			return false;
		$kvs -> set(__CLASS__, 'captcha', $_SERVER['REMOTE_ADDR'], $kvs -> get(__CLASS__, 'captcha', $_SERVER['REMOTE_ADDR']) + 1);
		$kvs -> expire(__CLASS__, 'captcha', $_SERVER['REMOTE_ADDR'], 5);

		if ($kvs -> get(__CLASS__, 'captcha', $_SERVER['REMOTE_ADDR']) > 10) {
			$kvs -> set(__CLASS__, 'captcha_ban', $_SERVER['REMOTE_ADDR'], true);
			$kvs -> expire(__CLASS__, 'captcha_ban', $_SERVER['REMOTE_ADDR'], 60 * 15);
			return false;
		}
*/
		if (!isset($_GET['key']))
			return false;
/*
		if (!preg_match('~^https://1chan\.ru/~i', $_SERVER['HTTP_REFERER']))
		    return false;
*/
		$session = Session::getInstance();

//		if ($session -> instantGet('captcha_'. $_GET['key'], false))
//		{
			$captcha = new KCAPTCHA();
			$session -> instantSet('captcha_'. $_GET['key'], $captcha -> getKeyString());
//		}

//		return false;
	}
}


code:
<?php
/**
 * Контроллер форсометра:
 */
class Generic_ForceometerController extends BaseController
{
	public function indexAction(Application $application, Template $template)
	{
        $template -> setParameter('title', 'Форс-о-метр');

        if (array_key_exists('query', $_GET)) {
            $query = $this['query'] = $_GET['query'];

            $search = new SphinxClient();
       	    $search -> SetServer(SPHINX_HOST, SPHINX_PORT);

            $search -> SetGroupBy('created_at', SPH_GROUPBY_MONTH);

            // Полный поиск (запрос):
		    $search -> ResetFilters();
            $search -> SetMatchMode(SPH_MATCH_PHRASE);
		    $search -> AddQuery($query, 'forceometer');

            // Поиск уникальных в месяц (запрос):
		    $search -> ResetFilters();
            $search -> SetMatchMode(SPH_MATCH_PHRASE);
            $search -> SetFilter('uniq_m', array(1));
		    $search -> AddQuery($query, 'forceometer');

            // Поиск уникальных вообще (запрос):
		    $search -> ResetFilters();
            $search -> SetMatchMode(SPH_MATCH_PHRASE);
            $search -> SetFilter('uniq_f', array(1));
		    $search -> AddQuery($query, 'forceometer');

		    $query_result = $search -> RunQueries();

            // Поиск уникальных постеров месяца (вообще):
		    $search -> ResetFilters();
            $search -> SetMatchMode(SPH_MATCH_FULLSCAN);
            $search -> SetFilter('uniq_m', array(1));
            $bare_query = $search -> Query('', 'forceometer');
/*
		    $search -> ResetFilters();
            $search -> SetMatchMode(SPH_MATCH_FULLSCAN);
            $bare_query2 = $search -> Query('', 'forceometer');
*/
            if (!$query_result || $query_result[0]['total_found'] == 0) {
                $this['not_found'] = 1;
                return true;
            }

            $result = array(
                'posts'   => array(),
                'posters' => array(),
                'uniq_m'  => array(),
                'uniq_f'  => array()
            );

            for ($y = 2017; $y <= date('Y'); $y++) {
                for ($m = 1; $m <= 12; $m++) {
                    // Пропуск несуществующих дат и текущего месяца:
                    if ($y == 2017 && $m < 4) continue;
                    if ($y == date('Y') && $m >= date('m')) break;

                    $stamp = $y .'-'. ($m < 10 ? '0'. $m : $m) .'-01';

                    $result['posts'][$stamp]   = 0;
                    $result['posters'][$stamp] = 0;
                    $result['uniq_m'][$stamp]  = 0;
                    $result['uniq_f'][$stamp]  = 0;
                }
            }

            // Проходим результаты:
            if ($query_result[0]['matches']) {
                foreach($query_result[0]['matches'] as $match) {
                    $date  = $match['attrs']['@groupby'];
                    $stamp = substr($date, 0, 4) .'-'. substr($date, 4, 2) .'-01';

                    if (array_key_exists($stamp, $result['posts']))
                        $result['posts'][$stamp] = $match['attrs']['@count'];
                }
            }

            if ($bare_query['matches']) {
                foreach($bare_query['matches'] as $match) {
                    $date = $match['attrs']['@groupby'];
                    $stamp = substr($date, 0, 4) .'-'. substr($date, 4, 2) .'-01';

                    if (array_key_exists($stamp, $result['posters']))
                        $result['posters'][$stamp] = $match['attrs']['@count'];
                }
            }

            if ($query_result[1]['matches']) {
                foreach($query_result[1]['matches'] as $match) {
                    $date = $match['attrs']['@groupby'];
                    $stamp = substr($date, 0, 4) .'-'. substr($date, 4, 2) .'-01';

                    if (array_key_exists($stamp, $result['uniq_m']))
                        $result['uniq_m'][$stamp] = $match['attrs']['@count'];
                }
            }
/*
            if ($bare_query2['matches']) {
                foreach($bare_query2['matches'] as $match) {
                    $date = $match['attrs']['@groupby'];
                    $stamp = substr($date, 0, 4) .'-'. substr($date, 4, 2) .'-01';

                    if (array_key_exists($stamp, $result['uniq_f']))
                        $result['uniq_f'][$stamp] = $match['attrs']['@count'];
                }
            }*/

            foreach($result['posts'] as $date => $count) {
                if ($count == 0) {
                    unset($result['posts'][$date]);
                    unset($result['posters'][$date]);
                    unset($result['uniq_f'][$date]);
                    unset($result['uniq_m'][$date]);
                }
            }

            $this['results'] = json_encode($result);
            unset($result);
            return true;
        }

		return true;
	}
}


code:
<?php
/**
 * Контроллер каптчи:
 */
class Generic_RedirectController
{
	/**
	 * Вывод каптчи:
	 */
	public function redirectAction(Application $application, Template $template)
	{
		$template -> headerMovedPermanently(
			$_GET['url']
		);
		return false;
	}
}


code:
<?php
/**
 * Контроллер смены тем оформления сайта:
 */
class Generic_ThemeController
{
	/**
	 * Смена темы:
	 */
	public function switchAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		if ($_GET['theme'] == 'normal') {
			$session -> persistenceSet('global_theme', false);
		} else {
			if (is_file(VIEWS_DIR .'/layout_'. str_replace('/', '', $_GET['theme']) .'.php')) {
				$session -> persistenceSet('global_theme', 'layout_'. $_GET['theme']);
			}
		}

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


code:
<?php
/**
 * Контроллер действий раздела "онлайн":
 */
class LiveController extends BaseController
{
	/**
	 * Просмотр главной страницы раздела:
	 */
	public function indexAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		/* $filter = $session -> persistenceGet('live_filter', true); */
		$this['form_errors'] = $session -> instantGet('live_errors', array());
		$this['live_form']   = $session -> instantGet('live_form', array());

		$template -> setParameter('title', '«Онлайн»');

//		$this['links']  = Blog_BlogOnlineModel::GetLinks($filter);
//		$this['filter'] = $filter;

		$session -> persistenceSet('live_last_visit', time());
		return true;
	}

	/**
	 * Просмотр главной страницы раздела (ajax):
	 */
	public function indexAjaxAction(Application $application)
	{
		$session = Session::getInstance();
		/* $filter = $session -> persistenceGet('live_filter', true); */
		$session -> persistenceSet('live_last_visit', time());
		$links = Blog_BlogOnlineModel::GetLinks($filter);

		if (@$_GET['num'])
		    return array_slice($links, 0, (int)$_GET['num']);

		return $links;
	}

	/**
	 * Включение/выключение панели:
	 */
	public function toggleLinksPanelAction(Application $application) {
	    $session = Session::getInstance();

	    if ($_GET['status'] == 'on')
	        $session -> persistenceSet('show_links_panel', true);
	    else
	        $session -> persistenceSet('show_links_panel', false);

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

	public function toggleLinksPanelAjaxAction(Application $application) {
	    $session = Session::getInstance();

	    if ($_GET['status'] == 'on')
	        $session -> persistenceSet('show_links_panel', true);
	    else
	        $session -> persistenceSet('show_links_panel', false);

	    return true;
	}

	/**
	 * Добавление новой ссылки:
	 */
	public function addAction(Application $application, Template $template)
	{
		$session = Session::getInstance();

		if (!isset($_SERVER['HTTP_REFERER']) || (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) != TemplateHelper::getSiteUrl()))
			return false;

		if (ControlModel::isLiveCaptcha())
		{
			$captcha = false;
			$this['live_form'] = $_POST;

			if (@$_POST['captcha'])
			{
				if ($_POST['captcha'] !== $session -> instantGet('captcha_'. $_POST['captcha_key'], false))
				{
					$template -> setParameter('captcha_err', true);
					$captcha = true;
				}
				else
					$session -> persistenceSet('captcha_mode', false);
			}
			else
				$captcha = true;

			$key = 'live';
			$template -> setParameter('title',   'Добавление ссылки');
			$template -> setParameter('captcha_key', $key);
			$session  -> instantSet('captcha_'.$key, true);

			if ($captcha)
				return true;
		}

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

			$validator -> assertExists('link', 'Не введена ссылка');
			$validator -> assertRegexp('link', ValidatorHelper::URL_REGEXP, 'Ссылка введена неверно');

			if ($validator -> fieldValid('link'))
				$validator -> assertTrue(
					'link', Blog_BlogOnlineModel::CheckCategory($_POST['link']) !== false,
					'Данная ссылка не может участвовать в ленте'
				);

			if ($validator -> fieldValid('link'))
				$validator -> assertTrue(
					'link', Blog_BlogOnlineModel::CheckLinkPosted($_POST['link']) != true,
					'Ссылка уже участвует в ленте'
				);

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

			$validator -> assertTrue(
				'link', !ControlModel::CheckLinkfilter($params['link']),
				'Запрещенная ссылка'
			);

			$validator -> assertExists('description', 'Не введено описание');
			$validator -> assertLength('description', 100, 'Описание длиннее 100 символов');

			if ($validator -> isValid())
			{
				Blog_BlogOnlineModel::CreateLink($_POST);
				$template -> headerSeeOther(
					'/live/'
				);
				return false;
			}

			$session -> instantSet('live_errors', $validator -> getValidationResults());
			$session -> instantSet('live_form', $_POST);

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

	/**
	 * Добавление ссылки через букмарклет:
	 */
	public function addCrossSiteAction(Application $application, Template $template)
	{
	    $_GET['link']        = urldecode($_GET['link']);
	    $_GET['description'] = urldecode($_GET['description']);

	    $validator = new ValidatorHelper($_GET);

		$validator -> assertExists('link', 'Не введена ссылка');
		$validator -> assertRegexp('link', ValidatorHelper::URL_REGEXP, 'Ссылка введена неверно');

		if ($validator -> fieldValid('link'))
			$validator -> assertTrue(
				'link', Blog_BlogOnlineModel::CheckCategory($_GET['link']) !== false,
				'Данная ссылка не может участвовать в ленте'
		);

		if ($validator -> fieldValid('link'))
			$validator -> assertTrue(
				'link', Blog_BlogOnlineModel::CheckLinkPosted($_GET['link']) != true,
				'Ссылка уже участвует в ленте'
		);

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

		$validator -> assertTrue(
			'link', !ControlModel::CheckLinkfilter($params['link']),
			'Запрещенная ссылка'
		);

		$validator -> assertExists('description', 'Не введено описание');
		$validator -> assertLength('description', 100, 'Описание длиннее 100 символов');

        header('Content-type: image/png');

		if ($validator -> isValid())
		{
			Blog_BlogOnlineModel::CreateLink($_GET);
			readfile(WEB_DIR .'/ico/live_added.png');
			return false;
		}

		readfile(WEB_DIR .'/ico/live_failed.png');
	    return false;
	}

	/**
	 * Добавление новой ссылки (ajax):
	 */
	public function addAjaxAction(Application $application)
	{
		if (ControlModel::isLiveCaptcha())
			return array('isValid' => 'captcha');

		ControlModel::checkContent($_POST['description']);

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

		if ($validator -> fieldValid('link'))
			$validator -> assertTrue(
				'link', Blog_BlogOnlineModel::CheckCategory($_POST['link']) !== false,
				'Данная ссылка не может участвовать в ленте'
			);

		if ($validator -> fieldValid('link'))
			$validator -> assertTrue(
				'link', Blog_BlogOnlineModel::CheckLinkPosted($_POST['link']) != true,
				'Ссылка уже участвует в ленте'
			);

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

		$validator -> assertTrue(
			'link', !ControlModel::CheckLinkfilter($params['link']),
			'Запрещенная ссылка'
		);

		$validator -> assertExists('description', 'Не введено описание');
		$validator -> assertLength('description', 100, 'Описание длиннее 100 символов');

		if ($validator -> isValid())
		{
			Blog_BlogOnlineModel::CreateLink($_POST);
			return true;
		}

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

	/**
	 * Установка фильтра:
	 */
/*
	public function setFilterAction(Application $application, Template $template)
	{
		$session = Session::getInstance();
		if ($_SERVER['REQUEST_METHOD'] == 'POST')
			$session -> persistenceSet('live_filter', array_keys($_POST['boards']));

		$template -> headerSeeOther(
			'/live/'
		);
		return false;
	}
*/
	/**
	 * Установка фильтра (ajax):
	 */
/*
	public function setFilterAjaxAction(Application $application)
	{
		$session = Session::getInstance();
		if (@$_GET['boards'])
			$session -> persistenceSet('live_filter', array_keys($_GET['boards']));

		return $session -> persistenceGet('live_filter', true);
	}
*/
	/**
	 * Переход по ссылке:
	 */
	public function redirectAction(Application $application, Template $template)
	{
		Blog_BlogOnlineModel::Click($_GET['id']);
		$template -> headerSeeOther($_GET['to']);
	}
}


code:
<?php
/**
 * Контроллер правил разметки:
 */
class MarkupController extends BaseController
{
	/**
	 * Просмотр главной страницы раздела:
	 */
	public function indexAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Правила разметки');

		return true;
	}
}


code:
<?php
/**
 * Контроллер модераторских действий.
 */
class ModController extends BaseController
{
	/**
	 * Действие просмотра последний комментариев:
	 */
	public function getLastCommentsAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Последние комментарии');
		$template -> setParameter('board_id', 'news');
		$this['comments'] = Blog_BlogCommentsModel::GetLastComments();
		return true;
	}

	/**
	 * Действие просмотра лога модераторских действий:
	 */
	public function getModActionsAction(Application $application, Template $template)
	{
		$template -> setParameter('title', 'Последние действия модераторов');
		$template -> setParameter('board_id', 'news');
		$modlog = ControlModel::getLogModEvent();
		$this['comments'] = array_reverse($modlog);
		return true;
	}

	/**
	 * Действие просмотра RSS-ленты лога модераторских действий:
	 */
	public function getModActionsRssAction(Application $application, Template $template)
	{
	 	$modlog = array_reverse(ControlModel::getLogModEvent());
	 	if ($modlog)
	 	{
	 		$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($modlog as $entry)
	 		{
	 			$rss->itemDescription($entry);
	 			$rss->itemAuthor('anonymous');
	 			$rss->addItem();
	 		}

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

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

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

	/**
	 * Действие просмотра поста комментария:
	 */
	public function getLastCommentsAjaxAction(Application $application)
	{
		$cache  = KVS::getInstance();

		if ($cache -> exists(__CLASS__, $_GET['id'], 'preview'))
			return unserialize($cache -> get(__CLASS__, $_GET['id'], 'preview'));

		$comment = Blog_BlogCommentsModel::GetComment($_GET['id']);
		$result  = false;

		if ($comment)
		{
			$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']);

			$result = $comment;
		} elseif($post = Blog_BlogPostsModel::GetPost($_GET['id'])) {
			$result = array(
				'id'           => $post['id'],
				'post_id'      => $post['id'],
				'post_title'   => $post['title'],
				'text'         => $post['text'],
				'author'       => array($post['author'], HomeBoardHelper::getBoard($post['author'])),
				'created_at'   => TemplateHelper::date('d M Y @ H:i:s', $post['created_at']),
				'post_preview' => true
			);
		}

		$cache -> set(__CLASS__, $_GET['id'], 'preview', serialize($result));
		$cache -> expire(__CLASS__, $_GET['id'], 'preview', 60 * 60);
		return $result;
	}

	/**
	 * Действие получения настрооек поста:
	 */
	public function getPostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isModeratorSession())
			return false;

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post)
			return array(
				'pinned'   => $post['pinned'],
				'rated'    => $post['rated'],
				'rateable' => $post['rateable'],
				'closed'   => $post['closed']
			);

		return false;
	}

	/**
	 * Добавление поста в категорию:
	 */
	public function categoryPostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isModeratorSession())
			return false;

		if ($_GET['cat'] == '')
		    $category_id = 0;
		else
		{
		    if (!Blog_BlogCategoryModel::CategoryExists($_GET['cat']))
                return false;

            $category_id = Blog_BlogCategoryModel::GetCategoryIdByCode($_GET['cat']);
		}

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post && ControlModel::checkModrights($post['category']) && ControlModel::checkModrights($category_id)) {
		    $post['category'] = $category_id;
			Blog_BlogPostsModel::CatPost($_GET['id'], $post, date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .' изменил категорию поста.');
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
 изменил категорию поста >>'. $post['id'] .''
			);
		}

		return true;
	}

	/**
	 * Действие прикрепления поста:
	 */
	public function pinnedPostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isModeratorSession())
			return false;

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post && ControlModel::checkModrights($post['category'])) {
			Blog_BlogPostsModel::PinPost($_GET['id'], !$post['pinned'], date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] . (!$post['pinned'] ? ' прикрепил' : ' открепил') .' пост.');
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
'. (!$post['pinned'] ? ' прикрепил' : ' открепил') .' пост >>'. $post['id'] .''
			);
		}

		return true;
	}

	/**
	 * Действие одобрения поста:
	 */
	public function ratedPostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isModeratorSession())
			return false;

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post && ControlModel::checkModrights($post['category'])) {
			Blog_BlogPostsModel::RatedPost($_GET['id'], !$post['rated'], date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] . (!$post['rated'] ? ' одобрил' : ' убрал из одобренного') .' пост.');
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
'. (!$post['rated'] ? ' одобрил' : ' убрал из одобренного') .' пост >>'. $post['id'] .''
			);
		}

		return true;
	}

	/**
	 * Действие оцениваемого поста:
	 */
	public function rateablePostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isModeratorSession())
			return false;

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post && ControlModel::checkModrights($post['category'])) {
			Blog_BlogPostsModel::RateablePost($_GET['id'], !$post['rateable'], date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] . (!$post['rateable'] ? ' сделал оцениваемым' : ' сделал неоцениваемым') .' пост.');
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
'. (!$post['rateable'] ? ' сделал оцениваемым' : ' сделал неоцениваемым') .' пост >>'. $post['id'] .''
			);

		}

		return true;
	}

	/**
	 * Действие закрытого поста:
	 */
	public function closedPostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isModeratorSession())
			return false;

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post && ControlModel::checkModrights($post['category'])) {
			Blog_BlogPostsModel::ClosePost($_GET['id'], !$post['closed'], date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] . (!$post['closed'] ? ' закрыл' : ' открыл') .' пост.');
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
'. (!$post['closed'] ? ' закрыл' : ' открыл') .' пост >>'. $post['id'] .''
			);
		}

		return true;
	}

	/**
	 * Действие скрытого поста:
	 */
	public function hiddenPostAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isAdminSession())
			return false;

		$post = Blog_BlogPostsModel::GetPost($_GET['id']);
		if ($post && ControlModel::checkModrights($post['category'])) {
			Blog_BlogPostsModel::HidePost($_GET['id'], !$post['hidden'],
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] . (!$post['hidden'] ? ' скрыл' : ' показал') .' пост.'.
				'
Причина: '. (!empty($_GET['why']) ? ''. $_GET['why'] .'' : 'не указана'));

			if (!$post['hidden'])
			    Blog_BlogPostsModel::SetSpecialComment($_GET['id'], 'Причина: '. (!empty($_GET['why']) ? ''. $_GET['why'] .'' : 'не указана'));
			else
			    Blog_BlogPostsModel::SetSpecialComment($_GET['id'], '');

			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
'. (!$post['hidden'] ? ' скрыл' : ' показал') .' пост >>'. $post['id'] .''.
				'
Причина: '. (!empty($_GET['why']) ? ''. $_GET['why'] .'' : 'не указана')
			);
		}

		return true;
	}

	/**
	 * Действие удаление комментария:
	 */
	public function removePostCommentAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isAdminSession())
			return false;

		$comment = Blog_BlogCommentsModel::GetComment($_GET['id']);
		$post    = Blog_BlogPostsModel::GetPost(@$comment['post_id']);
		if ($comment && $post && ControlModel::checkModrights($post['category'])) {
			Blog_BlogCommentsModel::RemoveComment($_GET['id']);
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
 удалил комментарий '. $comment['id'] .' в посте >>'. $post['id'] .''.
				'
'.strip_tags($comment['text'],'a').''
			);
			return true;
		}
		return false;
	}

	/**
	 * Действие удаление ссылки из ротатора:
	 */
	public function removeOnlineLinkAjaxAction(Application $application)
	{
		if (!Session::getInstance() -> isAdminSession())
			return false;

		$link = Blog_BlogOnlineModel::GetLink($_GET['id']);
		if ($link) {
			Blog_BlogOnlineModel::RemoveLink($_GET['id']);
			ControlModel::logModEvent(
				date("d-m-Y H:i:s") .' '. $_SESSION['auth']['name'] .'
 удалил ссылку '. $link['link'].
				'
'.strip_tags($link['description']).''
			);
		}

		return true;
	}

	/**
	 * Действие "поделиться ссылкой" через букмарклет:
	 */
	public function shareLinkAction(Application $application, Template $template)
	{
		$kvs   = KVS::getInstance();

		$validator = new ValidatorHelper($_GET);
		$validator -> assertExists('title', '');
		$validator -> assertLength('title', 70, '');
		$validator -> assertExists('link', '');
		$validator -> assertRegexp('link', ValidatorHelper::URL_REGEXP, '');
		$validator -> assertLength('description', 128, '');

		if ($validator -> isValid())
		{
			$key     = md5(strtolower($_GET['link']));
			$ip      = md5($_SERVER['REMOTE_ADDR']);
			$counter = $kvs -> get(__CLASS__, 'shared_links_ip', $ip);

			if (!$kvs -> exists(__CLASS__, 'shared_links', $key) && !$kvs -> exists(__CLASS__, 'shared_links_ban', $ip))
			{
				if ($kvs -> exists(__CLASS__, 'shared_links_ip', $ip))
				{
					if ($counter > 5)
					{
					    $kvs -> set(__CLASS__, 'shared_links_ban', $ip, true);
					    $kvs -> expire(__CLASS__, 'shared_links_ban', $ip, 5 * 60);
					}
					else
					{
						$lifetime = $kvs -> lifetime(__CLASS__, 'shared_links_ip', $ip);
						$kvs -> set(__CLASS__, 'shared_links_ip', $ip, ++$counter);
						$kvs -> expire(__CLASS__, 'shared_links_ip', $ip, $lifetime);
					}
				}
				else
				{
					$kvs -> set(__CLASS__, 'shared_links_ip', $ip, 1);
					$kvs -> expire(__CLASS__, 'shared_links_ip', $ip, 60);
				}

				$kvs -> set(__CLASS__,    'shared_links', $key, true);
				$kvs -> expire(__CLASS__, 'shared_links', $key, 60 * 60);

				$template -> headerOk();
				$template -> headerContentTypeWOCharset('image/png');
				readfile(WEB_DIR .'/ico/tick.png');
				exit;
			}
		}

		$template -> headerBadRequest();
		exit;
	}
}


code:
<?php
/**
 * Контроллер статических страниц:
 */
class StaticController extends BaseController
{
	/**
	 * Показ статической страницы:
	 */
	public function indexAction(Application $application, Template $template)
	{
		if (($page = StaticModel::GetPage($_GET['page'])) != false)
		{
			if ($page['published'] == true) {
				$template -> setParameter('title', $page['title']);
				$this['page'] = $page['name'];
				return true;
			}
		}

		$application -> go('errors_error404');
		return false;
	}

	/**
	 * Предварительный просмотр:
	 */
	public function previewAction(Application $application, Template $template)
	{
		if ($_SERVER['REQUEST_METHOD'] != 'POST')
			die($application -> go('errors_error404'));

		$session = Session::getInstance();
		if (!$session -> isAdminSession())
			die($application -> go('errors_error403'));

		$template -> setParameter('title', $_POST['title']);
		$this['preview_content'] = $_POST['content'];

		return true;
	}

	/**
	 * Poo chan:
	 */
	public function pooAjaxAction(Application $application) {
		$cache = KVS::getInstance();
		if ($cache -> exists(__CLASS__, 'poo', $_SERVER['REMOTE_ADDR'])) {
			return false;
		}

		EventModel::getInstance()
            -> ClientBroadcast('page_'. $_POST['target'], "poo", array('top' => $_POST['top'], 'left' => $_POST['left']));

		$cache -> set(__CLASS__, 'poo', $_SERVER['REMOTE_ADDR'], 1);
		$cache -> expire(__CLASS__, 'poo', $_SERVER['REMOTE_ADDR'], 1);
		return true;
	}
}


code:
<?php
/**
 * Хелпер по домашней борде пользователя:
 */
class HomeBoardHelper {
	static private $boards = array(
		'anonymous'    => array('anonymous.png', 'Аноним'),
	);

	static public function getBoards() {
		return self::$boards;
	}

	static public function existsBoard($id) {
		return array_key_exists($id, self::$boards);
	}

	static public function getBoard($id) {
		if (self::existsBoard($id))
			return self::$boards[$id];
		return self::$boards['anonymous'];
	}
}


code:
<?php
/**
 * Фильтр загрузок (разметка):
 */
class PreviewHelper
{
    /**
     * Загрузка:
     */
    public function upload($url) {
        $kvs = KVS::getInstance();
		if ($kvs -> exists(__CLASS__, $url))
		    return $kvs -> get(__CLASS__, $url);

        $data = @get_headers($url, 1);
        if (in_array($data['Content-Type'], array('image/jpeg', 'image/jpg', 'image/png', 'image/gif'))) {
            $tm  = tempnam('/tmp', 'img');
            $nam = md5(uniqid('', true));
            $dir = substr($nam, 0, 1);

            $im    = fopen($url, 'r');
            $fp    = fopen($tm, 'w');
            $start = time();

            while(!feof($im) && (time() - $start) < 5) {
                fputs($fp, fgets($im, 24));
            }
            fclose($im);
            fclose($fp);

            if ((time() - $start) > 5)
                return false;

            self::createThumbnail($tm, UPLOAD_PATH .'/news/'. $dir .'/'. $nam .'.png');
            $p = '/uploads/news/'. $dir .'/'. $nam .'.png';
            @unlink(tm);
            $kvs -> set(__CLASS__, $url, null, '/uploads/news/'. $dir .'/'. $nam .'.png');
            $kvs -> expire(__CLASS__, $url, null, 60 * 60 * 24 * 30);

            return $p;
        }
        return false;
    }

    /**
	 * Создание тамбнейла:
	 */
	private static function createThumbnail($path, $name)
	{
		$thumb  = new Imagick($path);
		$width  = $thumb -> getImageWidth();
		$height = $thumb -> getImageHeight();
		$s      = self::scaleImage($width, $height, 150, 200);
		$thumb -> scaleImage($s[0], $s[1]);
		$thumb -> writeImage($name);
		$thumb->destroy();
		return true;
	}

    /**
	**/
	private static function scaleImage($x,$y,$cx,$cy) {
		//Set the default NEW values to be the old, in case it doesn't even need scaling
		list($nx,$ny)=array($x,$y);

		//If image is generally smaller, don't even bother
		if ($x>=$cx || $y>=$cx) {

		    //Work out ratios
		    if ($x>0) $rx=$cx/$x;
		    if ($y>0) $ry=$cy/$y;

		    //Use the lowest ratio, to ensure we don't go over the wanted image size
		    if ($rx>$ry) {
		        $r=$ry;
		    } else {
		        $r=$rx;
		    }

		    //Calculate the new size based on the chosen ratio
		    $nx=intval($x*$r);
		    $ny=intval($y*$r);
		}

		//Return the results
		return array($nx,$ny);
	}
}


code:
<?php
/**
 * Хелпер для шаблонов:
 */
class TemplateHelper
{
	/**
	 * Получение урла сайта:
	 */
	public static function getSiteUrl()
	{
		return $_SERVER["HTTP_HOST"];
	}

	/**
	 * Получение иконки:
	 */
	public static function getIcon($site)
	{
		$site = str_replace('www.', '', parse_url($site, PHP_URL_HOST));
		if (is_file(WEB_DIR .'/ico/favicons/'. $site .'.png'))
			return '/ico/favicons/'. $site .'.png';

		return 'https://proxy.duckduckgo.com/ip3/'. $site . '.ico';
	}

	/**
	 * Русская дата:
	 */
	public static function date($pattern, $time = false) {
	    // Не горжусь этим хаком:
	    if ($pattern == 'Y-m-d @ H:i:s') {
	        return date($pattern, $time ? $time : time());
	    }

		if (date('Y') == date('Y', $time))
			$pattern = str_replace(' Y', '', $pattern);

		$date = date($pattern, $time ? $time : time());
		return strtr($date, array(
			'Jan' => 'Января',
			'Feb' => 'Февраля',
			'Mar' => 'Марта',
			'Apr' => 'Апреля',
			'May' => 'Мая',
			'Jun' => 'Июня',
			'Jul' => 'Июля',
			'Aug' => 'Августа',
			'Sep' => 'Сентября',
			'Oct' => 'Октября',
			'Nov' => 'Ноября',
			'Dec' => 'Декабря'
		));
	}

	/**
	 * Русское окончание:
	 */
	public static function ending($chislo, $n1, $n2, $n5){
		$chislo = (int)$chislo;
		$ch = substr($chislo, -1);

		if ($ch==1)
		{
			if (strlen($chislo) > 1)
				$result = substr($chislo,-2,1) == 1 ? $n5 : $n1;
			else
				$result = $n1;
		}
		elseif($ch > 1 && $ch < 5)
		{
			if (strlen($chislo) > 1)
				$result = substr($chislo, -2, 1) == 1 ? $n5 : $n2;
			else
				$result = $n2;
		}
		else
		{
			$result=$n5;
		}
		return $chislo .' '. $result;
	}

	/**
	 * Форматирование размеров файлов:
	 */
	public static function format_bytes($bytes) {
	   if ($bytes < 1024) return $bytes.' B';
	   elseif ($bytes < 1048576) return round($bytes / 1024, 2).' KB';
	   elseif ($bytes < 1073741824) return round($bytes / 1048576, 2).' MB';
	   elseif ($bytes < 1099511627776) return round($bytes / 1073741824, 2).' GB';
	   else return round($bytes / 1099511627776, 2).' TB';
	}

	/**
	 * Получение информации о категории:
	 */
	public static function BlogCategory($id, $field = null)
	{
		static $categories;
		if (!is_array($categories))
		{
			foreach (Blog_BlogCategoryModel::GetCategories() as $row)
				$categories[$row['id']] = $row;
		}

		if (is_null($field))
			return $categories[$id];
		else
			return $categories[$id][$field];
	}

	/**
	 * Функция проверки обновления поста:
	 */
	public static function isPostUpdated(&$post, $comments_only = true)
	{
		if ($post['comments'] == 0 && $comments_only)
			return false;

		$session    = Session::getInstance();
		$last_visit = $session -> activeGet('last_visit');

		if (($last_visit_post = $session -> activeGet('last_visit_post_'. $post['id'], false)) !== false)
		{
			if ($post['updated_at'] > $last_visit_post)
				return true;
		}
		else
		{
			if ($post['updated_at'] > $last_visit)
				return true;
		}

		return false;
	}

	/**
	 * Функция проверки нового поста:
	 */
	public static function isNewComment(&$comment)
	{
		$session    = Session::getInstance();
		$last_visit = $session -> activeGet('last_visit');

		if (($last_visit_post = $session -> instantGet('last_visit_post_'. $comment['post_id'], false)) !== false)
		{
			if ($comment['created_at'] > $last_visit_post)
				return true;
		}
		else
		{
			if ($comment['created_at'] > $last_visit)
				return true;
		}

		return false;
	}

	/**
	 * Функция проверки обновлений в разделе "онлайн":
	 */
	public static function isLiveUpdated()
	{
		$session = Session::getInstance();
		$cache   = KVS::getInstance();
		$last_visit = $session -> persistenceGet('live_last_visit', time());
		return $cache -> get('Blog_BlogOnlineModel', null, 'lastUpdate') > $last_visit;
	}
}


code:
<?php
/**
 * Фильтр сообщений (разметка):
 */
class TexyHelper
{
	/**
	 * Регулярные выражения для видео:
	 */
	const YOUTUBE_REGEXP = 'https:\/\/(?:www\.|)youtube\.com\/watch\?v=([a-zA-Z0-9_\-]+)';

	/**
	 * Метод создания объекта Texy:
	 */
	private static function createTexyObject($post_link = true, $board = null)
	{
		$texy = new Texy();
		$texy -> mergeLines = false;
		$texy -> htmlOutputModule -> baseIndent  = 6;
		$texy -> typographyModule -> locale = 'fr';

		$texy->registerLinePattern(
			array('TexyHelper', 'trollface'),
			'/:trollface:/',
			'TexyHelper_trollface'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'coolface'),
			'/:coolface:/',
			'TexyHelper_coolface'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'sobak'),
			'/:sobak:/',
			'TexyHelper_sobak'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'desu'),
			'/:desu:/',
			'TexyHelper_desu'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'rage'),
			'/:rage:/',
			'TexyHelper_rage'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'ffuu'),
			'/:f+u+:/',
			'TexyHelper_ffuu'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'okay'),
			'/:okay:/',
			'TexyHelper_okay'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'omsk'),
			'/:omsk:/',
			'TexyHelper_omsk'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'awesome'),
			'/:awesome:/',
			'TexyHelper_awesome'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'cancer'),
			'/:cancer:/',
			'TexyHelper_cancer'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'mrgreen'),
			'/:mrgreen:/',
			'TexyHelper_mrgreen'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'nyan'),
			'/:nyan:/',
			'TexyHelper_nyan'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'sheez'),
			'/:sheez:/',
			'TexyHelper_sheez'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'cheez'),
			'/:cheez:/',
			'TexyHelper_cheez'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'popka'),
			'/:popka:/',
			'TexyHelper_popka'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'popkatwo'),
			'/:popka2:/',
			'TexyHelper_popkatwo'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'slon'),
			'/:slon:/',
			'TexyHelper_slon'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'pauk'),
			'/:pauk:/',
			'TexyHelper_pauk'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'sosak'),
			'/:sosak:/',
			'TexyHelper_sosak'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'makak'),
			'/:makak:/',
			'TexyHelper_makak'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'deb'),
			'/:deb:/',
			'TexyHelper_deb'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'projector'),
			'/:projector:/',
			'TexyHelper_projector'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'hazard'),
			'/:hazard:/',
			'TexyHelper_hazard'
		);

		$texy->registerLinePattern(
			array('TexyHelper', 'spoiler'),
			'/%%(([^%]|%[^%])+)%%/',
			'TexyHelper_spoiler'
		);
		/*
		$texy->registerLinePattern(
			array('TexyHelper', 'tts'),
			'/#%(([^%]|%[^%])+)%#/',
			'TexyHelper_tts'
		);
		*/
		$texy->registerLinePattern(
			array('TexyHelper', 'coincidence'),
			'/\(\(\(([^\(\)]+)\)\)\)/',
			'TexyHelper_coincidence'
		);
/*
		$texy->registerLinePattern(
			array('TexyHelper', 'redline'),
			'/\$\$(([^\$]|\$[^\$])+)\$\$/',
			'TexyHelper_redline'
		);
*/
		if ($board == null)
		{
			$texy->registerLinePattern(
				array('TexyHelper', 'imgur'),
				'/\[i:([^\]]+):\]/',
				'TexyHelper_imgur'
			);

			$texy->registerLinePattern(
				array('TexyHelper', 'catbox'),
				'/\[c:(([^\]]+)\.((?i)jpe?g|gif|png|webp)):\]/',
				'TexyHelper_catbox'
			);

			$texy->registerLinePattern(
				array('TexyHelper', 'catboxvid'),
				'/\[c:(([^\]]+)\.((?i)webm|mp4)):\]/',
				'TexyHelper_catboxvid'
			);
/*
			$texy->registerLinePattern(
				array('TexyHelper', 'youtube'),
				'/\[youtube:([^\]]+)\]/',
				'TexyHelper__youtube'
			);
*/
/*
			$texy->registerLinePattern(
				array('TexyHelper', 'images'),
				'/\[([^\]]+)\]/',
				'TexyHelper__images'
			);
*/

			if ($post_link)
			{
			    $texy->registerLinePattern(
				    array('TexyHelper', 'postlink'),
				    '/>>(\d+)/',
				    'TexyHelper_postlink'
			    );

			    $texy->registerLinePattern(
				    array('TexyHelper', 'boardpostlink2'),
				    '/>>([^\/]+)\/(\d+)/',
				    'boardlink'
			    );
			}
		}
		else
		{
				$texy->registerLinePattern(
				    array('TexyHelper', 'boardpostlink'),
				    '/>>(\d+)/',
				    'boardlink_'. $board
			    );

			    $texy->registerLinePattern(
				    array('TexyHelper', 'boardpostlink2'),
				    '/>>([^\/]+)\/(\d+)/',
				    'boardlink'
			    );
		}

		return $texy;
	}

	/**
	 * Обработать строку (только типография):
	 */
	public static function typo($text)
	{
		$texy = self::createTexyObject();
		TexyConfigurator::safeMode($texy);
		TexyConfigurator::disableImages($texy);
		TexyConfigurator::disableLinks($texy);
		return $texy -> processTypo(htmlspecialchars($text));
	}

	/**
	 * Обработать строку (вся разметка):
	 */
	public static function markup($text, $safeMode = true, $links = true, $board = null)
	{
		$texy = self::createTexyObject($links, $board);

		if ($safeMode)
		{
			TexyConfigurator::safeMode($texy);
			TexyConfigurator::disableImages($texy);
		}
		return $texy -> process($text);
	}

    	/**
	 * Вставка картинок:
	 */
/*
	static function images($parser, $matches, $name) {
		list(, $mContent) = $matches;
        if (preg_match(self::URL_REGEXP, $mContent))
        {
            if ($GLOBALS['post_image_count']++) {
		        $parser -> again = false;
                return '['. $mContent .']';
            }
            if (($u = PreviewHelper::upload($mContent)) !== false) {
                $img = TexyHtml::el('img');
		        $img -> attrs['src']    = $u;
		        $img -> attrs['alt']    = '';
		        $link = TexyHtml::el('a');
		        $link -> attrs['target'] = '_blank';
		        $link -> attrs['class']  = 'b-image-link';
		        $link -> href($mContent);
		        $link -> add($img);
		        $parser -> again = false;
		        $GLOBALS['post_image_count'] = true;
		        return $link;
		    }
        }
        return '['. $mContent .']';
	}
*/

	/**
	 * Имгур:
	 */
	static function imgur($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		if (isset($GLOBALS['texy_test']) && $GLOBALS['texy_test']) {
			if ($GLOBALS['post_image_count']++) {
				$parser -> again = false;
				return '[i:'. $mContent .':]';
			}
		}

		$img = TexyHtml::el('img');
		$img -> attrs['src']    = 'https://i.imgur.com/'.$mContent.'.jpg';
		$img -> attrs['alt']    = '';

		$link = TexyHtml::el('a');
		$link -> attrs['target'] = '_blank';
		$link -> attrs['class'] = 'b-image-link';
		$link -> attrs['rel'] = 'nofollow noopener noreferrer';
		$link -> href('https://i.imgur.com/'.$mContent.'.jpg');
		$link -> add($img);

		$parser -> again = false;
		return $link;
	}

	/**
	 * Кятбокс:
	 */
	static function catbox($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		if (isset($GLOBALS['texy_test']) && $GLOBALS['texy_test']) {
			if ($GLOBALS['post_image_count']++) {
				$parser -> again = false;
				return '[i:'. $mContent .':]';
			}
		}

		$img = TexyHtml::el('img');
		$img -> attrs['src']    = 'https://files.catbox.moe/'.$mContent;
		$img -> attrs['alt']    = '';

		$link = TexyHtml::el('a');
		$link -> attrs['target'] = '_blank';
		$link -> attrs['class'] = 'b-image-link';
		$link -> attrs['rel'] = 'nofollow noopener noreferrer';
		$link -> href('https://files.catbox.moe/'.$mContent);
		$link -> add($img);

		$parser -> again = false;
		return $link;
	}

	/**
	 * Кятбокс (видео):
	 */
	static function catboxvid($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		if (isset($GLOBALS['texy_test']) && $GLOBALS['texy_test']) {
			if ($GLOBALS['post_image_count']++) {
				$parser -> again = false;
				return '[i:'. $mContent .':]';
			}
		}

		$vid = TexyHtml::el('video');
		$vid -> attrs['src'] = 'https://files.catbox.moe/'.$mContent;
		$vid -> attrs['autoplay'] = '';
		$vid -> attrs['loop'] = '';
		$vid -> attrs['muted'] = '';
		$vid -> attrs['controls'] = '';

		$link = TexyHtml::el('a');
		$link -> attrs['target'] = '_blank';
		$link -> attrs['class'] = 'b-image-link';
		$link -> attrs['rel'] = 'nofollow noopener noreferrer';
		$link -> href('https://files.catbox.moe/'.$mContent);
		$link -> add($vid);

		$parser -> again = false;
		return $link;
	}

	/**
	 * Спойлер:
	 */
	static function spoiler($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		$spl = TexyHtml::el('span');
		$spl -> attrs['class'] = 'b-spoiler-text';
		$spl -> setText($mContent);

		$parser -> again = true;
		return $spl;
	}

	/**
	 * Детектор совпадений:
	 */
	static function coincidence($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		$spl = TexyHtml::el('span');
		$spl -> attrs['class'] = 'b-coincidence';
		$spl -> setText( '((('.$mContent.')))' );

		$parser -> again = false;
		return $spl;
	}

	/**
	 * Розмовлялка:
	 */
	static function tts($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		$spl = TexyHtml::el('audio');
		$spl -> attrs['controls'] = '';
		$spl -> attrs['src'] = 'https://tts.voicetech.yandex.net/tts?text='.$mContent.'&lang=ru_RU&format=mp3&quality=hi&platform=web&application=translate&chunked=0&mock-ranges=1';

		$parser -> again = true;
		return $spl;
	}

	/**
	 * Реверсивная цитата:
	 */
	static function reverseblockquote($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		$spl = TexyHtml::el('blockquote');
		$spl -> attrs['class'] = 'reverse';

		$parser -> again = true;
		return $spl;
	}

	/**
	 * Красный:
	 */
	static function redline($parser, $matches, $name) {
		list(, $mContent, $mMod) = $matches;

		$spl = TexyHtml::el('span');
		$spl -> attrs['style'] = 'color:red';
		$spl -> setText($mContent);

		$parser -> again = true;
		return $spl;
	}

	/**
	 * Троллфейс:
	 */
	static function trollface($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/trollface.gif';
		$el -> attrs['width']  = '30';
		$el -> attrs['height'] = '25';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * FFUU smile
	 */
	static function ffuu($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['src']    = '/img/ffuu.png';
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['width']  = '43';
		$el -> attrs['height'] = '31';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * okay smile
	 */
	static function okay($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['src']    = '/img/okay.png';
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['width']  = '26';
		$el -> attrs['height'] = '32';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * nyan smile
	 */
	static function nyan($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['src']    = '/img/nyan.gif';
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['width']  = '53';
		$el -> attrs['height'] = '21';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * awesome smile
	 */
	static function awesome($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/awesome.png';
		$el -> attrs['width']  = '20';
		$el -> attrs['height'] = '20';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * rage smile
	 */
	static function rage($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/rage.png';
		$el -> attrs['width']  = '28';
		$el -> attrs['height'] = '30';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * cancer smile
	 */
	static function cancer($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/cancer.gif';
		$el -> attrs['width']  = '31';
		$el -> attrs['height'] = '29';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * mrgreen smile
	 */
	static function mrgreen($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/mrgreen.gif';
		$el -> attrs['width']  = '32';
		$el -> attrs['height'] = '32';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * omsk smile
	 */
	static function omsk($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/omsk.png';
		$el -> attrs['width']  = '17';
		$el -> attrs['height'] = '19';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * Собак:
	 */
	static function sobak($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/sobak.gif';
		$el -> attrs['width']  = '30';
		$el -> attrs['height'] = '24';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * Десу:
	 */
	static function desu($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/desu.gif';
		$el -> attrs['width']  = '30';
		$el -> attrs['height'] = '42';
		$el -> attrs['alt']    = '';

		$parser -> again = false;

		return $el;
	}

	/**
	 * Кулфейс:
	 */
	static function coolface($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/coolface.gif';
		$el -> attrs['width']  = '30';
		$el -> attrs['height'] = '25';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Шизик:
	 */
	static function sheez($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/sheez.png';
		$el -> attrs['width']  = '40';
		$el -> attrs['height'] = '40';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Корка сыра:
	 */
	static function cheez($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/cheez.gif';
		$el -> attrs['width']  = '51';
		$el -> attrs['height'] = '40';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Попка:
	 */
	static function popka($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/popka.gif';
		$el -> attrs['width']  = '45';
		$el -> attrs['height'] = '35';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Попка-2:
	 */
	static function popkatwo($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/popka2.gif';
		$el -> attrs['width']  = '35';
		$el -> attrs['height'] = '25';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Слон:
	 */
	static function slon($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/slon.gif';
		$el -> attrs['width']  = '33';
		$el -> attrs['height'] = '46';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Паук:
	 */
	static function pauk($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/pauk.gif';
		$el -> attrs['width']  = '37';
		$el -> attrs['height'] = '35';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Сосак:
	 */
	static function sosak($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/sosak.gif';
		$el -> attrs['width']  = '51';
		$el -> attrs['height'] = '37';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Макак:
	 */
	static function makak($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/makak.gif';
		$el -> attrs['width']  = '51';
		$el -> attrs['height'] = '37';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Деб:
	 */
	static function deb($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/deb.png';
		$el -> attrs['width']  = '32';
		$el -> attrs['height'] = '32';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Проектор:
	 */
	static function projector($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/projector.png';
		$el -> attrs['width']  = '50';
		$el -> attrs['height'] = '33';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Хазард:
	 */
	static function hazard($parser, $matches, $name) {
		$el = TexyHtml::el('img');
		$el -> attrs['class']  = 'smiley';
		$el -> attrs['src']    = '/img/hazard.png';
		$el -> attrs['width']  = '32';
		$el -> attrs['height'] = '32';
		$el -> attrs['alt']    = '';

		$parser->again = false;

		return $el;
	}

	/**
	 * Ссылка на пост (комментарий):
	 */
	static function postlink($parser, $matches, $name)
	{
		list(, $id) = $matches;
		$parser -> again = false;

		if (Blog_BlogCommentsModel::CommentExists($id))
		{
			$comment = Blog_BlogCommentsModel::GetComment($id);
			$post_id = $comment['post_id'];
		}
		elseif (Blog_BlogPostsModel::PostExists($id))
			$post_id = $id;
		else
			return '>>'. $id;

		$link = TexyHtml::el('a');
		$link -> href('/news/res/'. $post_id .'/#'. $id );
		$link -> attrs['class'] = 'js-cross-link';
		$link -> attrs['name'] = 'news/'. $id;
		$link -> setText('>>'. $id);

		return $link;
	}

	/**
	 * Ссылка на пост борды:
	 */
	static function boardpostlink($parser, $matches, $name)
	{
		list(, $id) = $matches;
		$parser -> again = false;

		// Получаем название текущей борды:
		$board_name = substr($name, 10);
		$board = new Board_BoardModel($board_name);

		if ($board -> existsPost($id))
		{
			$post = $board -> getPost($id);
			$href = '/'. $board_name .'/res/';

			if ($post['parent_id'] == null)
				$href .= $id .'/#top';
			else
				$href .= $post['parent_id'] .'/#'. $id;

			$link = TexyHtml::el('a');
			$link -> href($href);
			$link -> attrs['class']  = 'js-cross-link';
			$link -> attrs['name'] = $board_name .'/'. $id;
			$link -> setText('>>'. $id);

			return $link;
		}

		return '>>'. $id;
	}

	static function boardpostlink2($parser, $matches, $name)
	{
		list(, $board_name, $id) = $matches;
		$parser -> again = false;

		if ($board_name != 'news')
		{
			$board = new Board_BoardModel($board_name);

			if ($board -> existsPost($id))
			{
				$post = $board -> getPost($id);
				$href = '/'. $board_name .'/res/';

				if ($post['parent_id'] == null)
					$href .= $id .'/#top';
				else
					$href .= $post['parent_id'] .'/#'. $id;

				$link = TexyHtml::el('a');
				$link -> href($href);
				$link -> attrs['class']  = 'js-cross-link';
				$link -> attrs['name'] = $board_name .'/'. $id;
				$link -> setText('>>'. $board_name .'/'. $id);

				return $link;
			}

			return '>>'. $board_name .'/'. $id;
		}

		if (Blog_BlogCommentsModel::CommentExists($id))
		{
			$comment = Blog_BlogCommentsModel::GetComment($id);
			$post_id = $comment['post_id'];
		}
		elseif (Blog_BlogPostsModel::PostExists($id))
			$post_id = $id;
		else
			'>>news/'. $id;

		$link = TexyHtml::el('a');
		$link -> href('/news/res/'. $post_id .'/#'. $id );
		$link -> attrs['class'] = 'js-cross-link';
		$link -> attrs['name'] = 'news/'. $id;
		$link -> setText('>>news/'. $id);

		return $link;
	}

	/**
	 * Получить код вставки видео:
	 */
	public static function getVideo($url)
	{
		if (preg_match('/^'.self::YOUTUBE_REGEXP.'/i', $url))
		{
			return preg_replace('/'.self::YOUTUBE_REGEXP.'(.*)/i', '<div class="b-video"><div class="g-hidden"><object width="520" height="400"><param name="wmode" value="opaque"></param><embed src="https://www.youtube.com/v/$1&hl=en" type="application/x-shockwave-flash" wmode="opaque" width="520" height="400"></embed></param></embed></object></div></div>', $url);
		}

		return false;
	}

    const URL_REGEXP = "{
			  ^
			  (
			    (https?)://[-\\w]+(\\.\\w[-\\w]*)+
			  |
			    (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \\. )+
			    (?-i: com\\b
			| edu\\b
			| biz\\b
			| gov\\b
			| in(?:t|fo)\\b # .int or .info
			| mil\\b
			| net\\b
			| org\\b
			| ovh\\b
			| xyz\\b
			| space\\b
			| club\\b
			| onion\\b
			| i2p\\b
			| [a-z][a-z]\\.[a-z][a-z]\\b # two-letter country code
			    )
			  )
			  ( : \\d+ )?
			  (
			    /
			    [^.!,?;\"\\'<>()\[\]\{\}\s\x7F-\\xFF]*
			    (
			      [.!,?]+ [^.!,?;\"\\'<>()\\[\\]\{\\}\s\\x7F-\\xFF]+
			    )*
			  )?
			}ix";
}


code:
<?php
/**
 * Класс валидатора данных с формы:
 */
class ValidatorHelper
{
	/**
	 * Флаг валидности:
	 */
	private $isValid = true;

	/**
	 * Массив результатов валидации (сообщений):
	 */
	private $validationResults = array();

	/**
	 * Массив валидируемых значений:
	 */
	private $validationTarget = array();

	/**
	 * Конструктор:
	 */
	public function __construct($validationTarget)
	{
		$this -> validationTarget = $validationTarget;
	}

	/**
	 * Проверка заполнености значения:
	 */
	public function assertExists($key, $message = '')
	{
		if (!array_key_exists($key, $this -> validationTarget))
		{
			$this -> isValid = false;
			return false;
		}

		if (strlen(trim(html_entity_decode($this -> validationTarget[$key]))) == 0)
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

	/**
	 * Проверка незаполнености значения:
	 */
	public function assertNotExists($key, $message = '')
	{
		if (strlen(trim(@$this -> validationTarget[$key])) != 0)
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

	/**
	 * Проверка длинны значения:
	 */
	public function assertLength($key, $length, $message = '')
	{
		if (mb_strlen(trim(html_entity_decode($this -> validationTarget[$key])), 'UTF-8') > $length)
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

	/**
	 * Проверка длинны значения:
	 */
	public function assertLengthMore($key, $length, $message = '')
	{
		if (mb_strlen(trim($this -> validationTarget[$key]), 'UTF-8') < $length)
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

	/**
	 * Проверка значения на регулярное выражение:
	 */
	public function assertRegexp($key, $regexp, $message = '')
	{
		if (!preg_match($regexp, $this -> validationTarget[$key]))
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

	/**
	 * Проверка на точное совпадение:
	 */
	public function assertEqual($key, $value, $message = '')
	{
		if ($this -> validationTarget[$key] !== $value)
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

	/**
	 * Проверка на истинность:
	 */
	public function assertTrue($key, $expr, $message = '')
	{
		if ($expr !== true)
		{
			$this -> validationResults[$key] = $message;
			$this -> isValid = false;
			return false;
		}

		return true;
	}

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

	/**
	 * Получение массива результатов валидации:
	 */
	public function getValidationResults()
	{
		return $this -> validationResults;
	}

	/**
	 * Проверка валидности определенного поля:
	 */
	public function fieldValid($field)
	{
		return !array_key_exists($field, $this -> validationResults);
	}

	/**
	 * Константы некоторых регулярных выражений:
	 */
	const URL_REGEXP = "{
			  ^
			  (
			    (https?)://[-\\w]+(\\.\\w[-\\w]*)+
			  |
			    (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \\. )+
			    (?-i: com\\b
				| edu\\b
				| biz\\b
				| gov\\b
				| in(?:t|fo)\\b # .int or .info
				| mil\\b
				| net\\b
				| org\\b
				| ovh\\b
				| xyz\\b
				| space\\b
				| club\\b
				| onion\\b
				| i2p\\b
			        | [a-z][a-z]\\.[a-z][a-z]\\b # two-letter country code
			    )
			  )
			  ( : \\d+ )?
			  (
			    /
			    [^.!,?;\"\\'<>()\[\]\{\}\s\x7F-\\xFF]*
			    (
			      [.!,?]+ [^.!,?;\"\\'<>()\\[\\]\{\\}\s\\x7F-\\xFF]+
			    )*
			  )?
			}ix";

	const EMAIL_REGEXP = "/^[_\.0-9a-zA-Z-]+@([0-9a-zA-Z][0-9a-zA-Z-]+\.)+[a-zA-Z]{2,6}$/";
}


code:
<?php
/**
 * Модель постов:
 */
class Blog_BlogCategoryModel
{
	/**
	 * Создание категории:
	 */
	public static function CreateCategory($params)
	{
		$record = array(
			'name'  => $params['name'],
			'title' => $params['title'],
			'description' => $params['description'],
			'code'  => $params['code'],
			'public' => PDOQuery::toTinyint($params['public'])
		);

		$dbh = PDOQuery::getInstance();

		EventModel::getInstance()
			-> Broadcast('add_category', $record);

		return $dbh -> insert('1chan_category', $record);
	}

	/**
	 * Редактирование категории:
	 */
	public static function EditCategory($id, $params)
	{
		$record = array(
			'name'  => $params['name'],
			'title' => $params['title'],
			'description' => $params['description'],
			'code'  => $params['code'],
			'public' => PDOQuery::toTinyint($params['public'])
		);

		$dbh = PDOQuery::getInstance();

		EventModel::getInstance()
			-> Broadcast('edit_category', $record);

		return $dbh -> update('1chan_category', $record, 'id = '. $dbh -> q($id));
	}

	/**
	 * Удаление категории:
	 */
	public static function RemoveCategory($id)
	{
		$dbh = PDOQuery::getInstance();

		EventModel::getInstance()
			-> Broadcast('remove_category', $id);

		return $dbh -> delete('1chan_category', 'id = '. $id, 1);
	}

	/**
	 * Изменение счетчика постов категории:
	 */
	public static function CountCategory($id, $increment = true)
	{
	    if ($id)
        {
		    $dbh = PDOQuery::getInstance();
            $posts = $dbh -> select('1chan_post', 'COUNT(*)', 'hidden = 0 AND category = '. $dbh -> q($id));
		    $count = $posts[0]['COUNT(*)'];

		    if ($increment)
		        $count++;
		    else
		        $count--;

		    return $dbh -> update('1chan_category', array('posts' => $count), 'id = '. $dbh -> q($id), 1, true);
		}
		return false;
	}

	/**
	 * Получение списка категорий:
	 */
	public static function GetCategories()
	{
		$dbh = PDOQuery::getInstance();
		return $dbh -> select('1chan_category', '*', null, 'pos ASC');
	}

	/**
	 * Получение списка публичных категорий:
	 */
	public static function GetPublicCategories()
	{
		$dbh = PDOQuery::getInstance();
		return $dbh -> select('1chan_category', '*', 'public = 1', 'pos ASC');
	}

	/**
	 * Получение категории по ключу:
	 */
	public static function GetCategoryByCode($category_code)
	{
		$dbh = PDOQuery::getInstance();
		$category_code= self::getCodeFromString($category_code);
		$category = $dbh -> select('1chan_category', '*', 'code = '. $dbh -> q($category_code), null, 1);

		if ($category)
			return $category[0];
		else
			return false;
	}

	/**
	 * Получение категории по имени:
	 */
	public static function GetCategoryByName($name)
	{
		$dbh = PDOQuery::getInstance();
		$category = $dbh -> select('1chan_category', '*', 'name = '. $dbh -> q($name), null, 1);

		if ($category)
			return $category[0];
		else
			return false;
	}

	/**
	 * Получение категории по имени:
	 */
	public static function GetCategoryById($id)
	{
		$dbh = PDOQuery::getInstance();
		$category = $dbh -> select('1chan_category', '*', 'id = '. $dbh -> q($id), null, 1);

		if ($category)
			return $category[0];
		else
			return false;
	}

	/**
	 * Получение кода категории по идентификатору:
	 */
	public static function GetCategoryCodeById($id)
	{
		$dbh = PDOQuery::getInstance();
		$category = $dbh -> select('1chan_category', '*', 'id = '. $dbh -> q($id), null, 1);

		if ($category)
			return $category[0]['code'];
		else
			return false;
	}

	/**
	 * Получение id категории по коду:
	 */
	public static function GetCategoryIdByCode($code)
	{
		if (self::CategoryExists($code))
		{
			$dbh = PDOQuery::getInstance();
			$code = self::getCodeFromString($code);
			$category = $dbh -> select('1chan_category', '*', 'code = '. $dbh -> q($code), null, 1);

			if ($category)
				return $category[0]['id'];
			else
				return false;
		}
		return null;
	}

	/**
	 * Проверка существования категории по ключу:
	 */
	public static function CategoryExists($category_code)
	{
		$dbh = PDOQuery::getInstance();
		$category_code = self::getCodeFromString($category_code);
		$category = $dbh -> select('1chan_category', '*', 'code = '. $dbh -> q($category_code), null, 1, $count);
		return (bool)$count;
	}

	/**
	 * Сортировка категорий:
	 */
	public static function Resort($order)
	{
		$dbh = PDOQuery::getInstance();
		foreach ($order as $id => $posi)
		{
			$dbh -> update('1chan_category', array('pos' => $posi), 'id = '. $dbh -> q($id));
		}
		return true;
	}

	/**
	 * Получение кода из строки:
	 */
	public static function getCodeFromString($string)
	{
		if (preg_match_all("/<([^>]+)>/", $string, $matches))
		{
			return $matches[1][0];
		}

		return $string;
	}
}


code:
<?php
/**
 * Модель комментариев:
 */
class Blog_BlogCommentsModel
{
	/**
	 * Добавление комментария:
	 */
	public static function CreateComment($params, $safeMode = true)
	{
		if (Blog_BlogPostsModel::PostExists($params['post_id']))
		{
			$session = Session::getInstance();
			$post = Blog_BlogPostsModel::GetPost($params['post_id']);

			// Запрет комментирования закрытых тредов:
			if ($post['closed'] == true)
				return false;

			$cache = KVS::getInstance();
			$id    = $cache -> incr('Blog_BlogPostsModel', null, 'nextPostId');
			$text_original = $params['text'];

			// Обработка:
			$params['text'] = TexyHelper::markup($params['text'], !$session -> isAdminSession());

			$record = array(
				'id'         => $id,
				'post_id'    => $params['post_id'],
				'ip'         => $_SERVER['REMOTE_ADDR'],
				'created_at' => time(),
				'text'       => $params['text']
			);

			if (HomeBoardHelper::existsBoard($params['homeboard']))
				$record['author'] = $params['homeboard'];

			if (!$safeMode)
			{
				$record = array_merge($record, array(
					'special_comment' => @$params['special_comment']
				));
			}

			$dbh = PDOQuery::getInstance();
			$dbh -> insert('1chan_comment', $record);

			if ($post['bumpable'] && (time() - $post['created_at'] < 60 * 60 * 24 * 30))
				$dbh -> update_insecure('1chan_post', array('updated_at' => time(), 'comments' => '`comments`+1'), 'id = '. $dbh -> q($params['post_id']), 1, true);
			else
				$dbh -> update_insecure('1chan_post', array('comments' => '`comments`+1'), 'id = '. $dbh -> q($params['post_id']), 1, true);

			$record['text_original'] = $text_original;
			EventModel::getInstance()
				-> Broadcast('add_comment', $record);

			/**
			 * Сохраняем в кеше последние комментарии:
			 */
			$record['post_title'] = $post['title'];
			$cache -> listAdd(__CLASS__, null, 'lastComments', $id);
			$cache -> listTrim(__CLASS__, null, 'lastComments', 0, 31);

			return $id;
		}
		return false;
	}

	/**
	 * Получение комментариев новости:
	 */
	public static function GetComments($post_id)
	{
		$cache = KVS::getInstance();
		$dbh = PDOQuery::getInstance();
		if ($cache -> exists(__CLASS__, $post_id, 'comments'))
			return unserialize($cache -> get(__CLASS__, $post_id, 'comments'));

		$comments = $dbh -> select('1chan_comment', '*', 'post_id = '. $dbh -> q($post_id), 'created_at ASC');
		$cache -> set(__CLASS__, $post_id, 'comments', serialize($comments));
		$cache -> expire(__CLASS__, $post_id, 'comments', 60 * 60 * 24);

		return $comments;
	}

	/**
	 * Получение комментариев новости:
	 */
	public static function GetLastComments($num = 30)
	{
		$cache = KVS::getInstance();
		$comments = $cache -> listGet(__CLASS__, null, 'lastComments');
		$result = array();
		foreach ($comments as $id)
		{
			if ($comment = self::GetComment($id))
				$result[] = $comment;
			else
				$cache -> listRemove(__CLASS__, null, 'lastComments', $id);
		}
		return $result;
	}

	/**
	 * Получение одного комментария:
	 */
	public static function GetComment($id)
	{
		$dbh   = PDOQuery::getInstance();
		$posts = $dbh -> select('1chan_comment', '*', 'id = '. $dbh -> q($id), null, 1);

		if (!empty($posts[0]))
			return $posts[0];

		return false;
	}

	/**
	 * Проверка существования комментария:
	 */
	public static function CommentExists($id)
	{
		$dbh   = PDOQuery::getInstance();
		$posts = $dbh -> select('1chan_comment', '*', 'id = '. $dbh -> q($id), null, 1);

		if ($posts && !empty($posts[0]))
			return true;

		return false;
	}

	/**
	 * Проверка существования комментария с текстом:
	 */
	public static function CommentWithTextExists($text)
	{
		$dbh   = PDOQuery::getInstance();
		$posts = $dbh -> select('1chan_comment', '*', 'text = '. $dbh -> q($text), null, 1);

		if ($posts && !empty($posts[0]))
			return true;

		return false;
	}

	/**
	 * Редактирование комментария:
	 */
	public static function EditComment($id, $params, $safeMode = true)
	{
		if (Blog_BlogPostsModel::PostExists($params['post_id']))
		{
			$session = Session::getInstance();
			$cache = KVS::getInstance();

			// Обработка:
			$params['text'] = TexyHelper::markup($params['text'], !$session -> isAdminSession());

			$record = array(
				'text'       => $params['text']
			);

			if (!$safeMode)
			{
				$record = array_merge($record, array(
					'special_comment' => @$params['special_comment']
				));
			}

			$dbh = PDOQuery::getInstance();
			$dbh -> update('1chan_comment', $record, 'id = '. $dbh -> q($id));

			EventModel::getInstance()
				-> Broadcast('edit_comment', $record);

			return $id;
		}
		return false;
	}

	/**
	 * Установка специального комментария:
	 */
	public static function SetSpecialComment($id, $comment = '')
	{
		$dbh = PDOQuery::getInstance();
		$comment = self::GetComment($id);

		EventModel::getInstance()
			-> Broadcast('special_comment_comment', array('id' => $id, 'post_id' => $comment['post_id'], 'special_comment' => $comment));

		return $dbh -> update('1chan_comment', array('special_comment' => $comment), 'id = '. $dbh -> q($id));
	}

	/**
	 * Удаление комментария:
	 */
	public static function RemoveComment($id)
	{
		$comment = self::GetComment($id);
		$kvs     = KVS::getInstance();
		$cache = KVS::getInstance();
		if ($comment)
		{
			if ($kvs -> exists('ControlModel', 'timeblock', $comment['ip']))
			{
				$kvs -> set('ControlModel', 'timeban', $comment['ip'], true);
				$kvs -> expire('ControlModel', 'timeban', $comment['ip'], 60 * 60);
			}

			if ($kvs -> exists('ControlModel', 'timeban', $comment['ip']))
			{
				$life = $kvs -> lifetime('ControlModel', 'timeban', $comment['ip']);
				$kvs -> expire('ControlModel', 'timeban', $comment['ip'], $life + (60 * 60));
			}

			$kvs -> set('ControlModel', 'timeblock', $comment['ip'], true);
			$kvs -> expire('ControlModel', 'timeblock', $comment['ip'], 60 * 60);

			$dbh = PDOQuery::getInstance();
			$dbh -> update_insecure('1chan_post', array('comments' => '`comments`-1'), 'id = '. $dbh -> q($comment['post_id']), 1, true);

			$cache -> listRemove(__CLASS__, null, 'lastComments', $id);
			EventModel::getInstance()
				-> Broadcast('remove_comment', array('id' => $id, 'post_id' => $comment['post_id']));

			return $dbh -> delete('1chan_comment', 'id = '. $dbh -> q($id), 1);
		}
		return false;
	}
}

/**
 * Обработчики событий:
 */
EventModel::getInstance()
	-> AddEventListener('add_comment', function($data) {

		$session = Session::getInstance();
		$session -> persistenceSet('last_comment_date', time());
		$session -> persistenceSet('last_post_text', $data['text_original']);

		/**
		 * Очистка и фильтарция:
		 */
		$data['created_at'] = TemplateHelper::date('d M Y @ H:i:s', $data['created_at']);
		$data['author']     = array($data['author'], HomeBoardHelper::getBoard($data['author']));
		unset($data['ip']);
		unset($data['text_original']);

		$post = Blog_BlogPostsModel::GetPost($data['post_id']);
		EventModel::getInstance()
			-> ClientBroadcast(
				'post_'. $data['post_id'], 'add_post_comment',
				$data
			)
			-> ClientBroadcast(
				'post_last_comments', 'add_post_comment',
				array('post_id' => $post['id'], 'id' => $data['id'])
			)
			-> ClientBroadcast('posts', 'add_post_comment', array('id' => $data['post_id'], 'count' => $post['comments']));

		Blog_BlogStatisticsModel::updateGlobalPosting();
	})
	-> AddEventListener('remove_comment', function($data) {
		$post = Blog_BlogPostsModel::GetPost($data['post_id']);
		EventModel::getInstance()
			-> ClientBroadcast(
				'post_'. $data['post_id'], 'remove_post_comment',
				array('id' => $data['id'])
			)
			-> ClientBroadcast(
				'post_last_comments', 'remove_post_comment',
				array('id' => $data['id'])
			)
			-> ClientBroadcast('posts', 'remove_post_comment', array('id' => $data['post_id'], 'count' => $post['comments']));
	})
	-> AddEventListener('*_comment', function($data) {
		if (array_key_exists('post_id', $data))
		{
			$cache = KVS::getInstance();
			$cache -> remove('Blog_BlogCommentsModel', $data['post_id'], 'comments');
		}
	});


code:
<?php
/**
 * Модель управления ссылками в футере:
 */
class Blog_BlogLinksModel
{
	/**
	 * Получение списка ссылок:
	 */
	public static function GetLinks()
	{
		$cache = KVS::getInstance();
		return unserialize($cache -> get(__CLASS__, null, 'links'));
	}

	/**
	 * Установка списка ссылок:
	 */
	public static function SetLinks($links = array())
	{
		$cache = KVS::getInstance();
		return $cache -> set(__CLASS__, null, 'links', serialize($links));
	}
}