<?php
/*
* @package LabCMS
* @copyright (c) 2006 Alexander Levin aka Loki (loki_angel@mail.ru)
* @support http://labcms.ru
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/

class lc_comments_main extends ModuleTools
{
	public $validation=3;
	
	public function comment_()
	{
		if (!access('admin')) return LabCMS::i()->error403();
		self::_setTpl('main');
	}
	
	/**
	* Добавление нового комментария
	**/
	public function comment_add()
	{
		if (!access('add')) return LabCMS::i()->error403();
		if (!$_GET['var1']) return self::_error(_m('Запись не найдена'));
		if ($item=$this->_getItemById($_GET['var1']))
			$this->_add($item);
	}
	
	/**
	* Ответ на существующий комментарий
	**/
	public function comment_reply()
	{
		if (!access('add')) return LabCMS::i()->error403();
		if ($comment=$this->_getCommentById($_GET['var1']))
			$this->_add($comment->item, $comment);
	}
	
	
	/**
	* генерация формы, ее проверка и доабвление комментария
	**/
	protected function _add($item, $parent=null)
	{
		$form=$this->_buildCommentForm();
		//валидация должна быть до рендеринга, чтобы можно было отображать ошибки
		$validate=$form->validate();
		$form_html=$this->_renderForm($form);

		//если незарегистрированный пользователь - вводим дополнительные проверки
		if (User::i()->isGuest()) {
			$this->_botObor($form, $form_html);
		}
		
		if ($validate) {
			$data=$form->getValue();
			$data['status']=access('no_premoderation')?1:2;
			if ($item->addComment($data, $parent))
				refresh('', $item->url);
		}
		self::_assignByRef('parent', $parent);
		self::_assign('item', $item);
		self::_assign('form', $form_html);
		self::_setTpl('form');
	}
	
	public function comment_edit()
	{
		if (!$cmt=$this->_getCommentById($_GET['var1'])) return;
		
		if (($cmt->user_id==User::i()->id && !access('edit'))
			|| ($cmt->user_id!=User::i()->id && !access('moderator, admin')))
				return LabCMS::i()->error403();
		
		$form=$this->_buildCommentForm();
		
		if ($form->validate()) {
			if ($cmt->edit($form->getValue()))
				refresh('', $cmt->item->url);
		}
		
		$form->addDataSource(
			Factory::create(
				'HTML_QuickForm2_DataSource_Array', 
				array(
					'login'	=> $cmt->login,
					'email'	=> $cmt->email,
					'text'	=> $cmt->text
				)
			)
		);
		
		self::_assign('form', $this->_renderForm($form));
		self::_assignByRef('item', $cmt->item);
		self::_setTpl('form');
	}
	
	public function comment_delete()
	{
		if (!User::checkToken($_GET['token'], false)) return;
		if (!$cmt=$this->_getCommentById($_GET['var1'])) return;
		
		if (($cmt->user_id==User::i()->id && !access('edit'))
			|| ($cmt->user_id!=User::i()->id && !access('moderator, admin')))
				return LabCMS::i()->error403();
		
		//если сообщение последнее в цепочке и удаляет автор - удаляем полностью
		//если нет - просто помечаем удаленным
		if ($cmt->leaf && $cmt->user_id==User::i()->id)
			$cmt->deleteBranch();
		else
			$cmt->delete();
		
		if (LabCMS::i()->isNoError()) refresh('', $cmt->item->url);
		
	}
	
	public function comment_undelete()
	{
		if (!User::checkToken($_GET['token'], false)) return;
		if (!$cmt=$this->_getCommentById($_GET['var1'])) return;
		
		if (!access('admin')) return LabCMS::i()->error403();
		if ($cmt->undelete())
			refresh('', $cmt->item->url);
	}
	
	public function comment_unhide()
	{
		if (!User::checkToken($_GET['token'], false)) return;
		if (!$cmt=$this->_getCommentById($_GET['var1'])) return;
		
		if (!access('moderator, admin')) return LabCMS::i()->error403();
		if ($cmt->unhide())
			refresh('', $cmt->item->url);
	}
	
	public function comment_delete_branch()
	{
		if (!User::checkToken($_GET['token'], false)) return;
		if (!$cmt=$this->_getCommentById($_GET['var1'])) return;
		
		if (!access('admin')) return LabCMS::i()->error403();
		if ($cmt->deleteBranch())
			refresh('', $cmt->item->url);
	}
	
	public function comment_abuse()
	{
		if (!User::checkToken($_GET['token'], false)) return;
		if (!$cmt=$this->_getCommentById($_GET['var1'])) return;

		if (!User::i()->isGuest() && access('abuse')) {
			$cmt->abuse();
		}
		refresh('', $cmt->item->url);
	}
	
	public function comment_moderate()
	{
		if (!access('moderator, admin'))	return LabCMS::i()->error403();
		$list=$this->_getList();
		if ($_SERVER['REQUEST_METHOD']=='POST') {
			$list->updateAbusedComments($_POST['act']);
			refresh(false, _url(false, 'moderate'));
		}
		
		$comments=$list->getCommentsByAbused();

		self::_assign('comments', $comments);
		self::_setTpl('moderation');
	}

	public function comment_premoderate()
	{
		if (!access('moderator, admin'))	return LabCMS::i()->error403();
		$list=$this->_getList();
		self::_assign('mode', 'pre');
		if ($_SERVER['REQUEST_METHOD']=='POST') {
			$list->updatePremoderatedComments($_POST['act']);
			refresh(false, _url(false, 'premoderate'));
		}
		
		$comments=$list->getCommentsByModeration();
		self::_assign('comments', $comments);
		self::_setTpl('moderation');
	}
	
	public function comment_rss()
	{
		$item=null;
		if ($_GET['var1']) {
			if (!$item=$this->_getItemById($_GET['var1'])) return;
		}
		$list=$this->_getList();
		$comments=$list->getLastComments($item);
		$title=$item?$item->title:config::get('core.site_name');
		$path=$item?$item->url:_url();
		$rss=array('title'=>'Последние комментарии - '.$title,
						'link'=>config::get('core.siteUrl').$path,
						'description'=>"Последние комментарии на сайте ".config::get('core.siteUrl').' '
						.$item?$title:'');

		if ($comments) foreach ($comments as $comment) {
			if (in_array($comment->status, array(1,2))) {
				$text_html=($comment->status==1)?getbb($comment->text):'Ожидает модерации';
				$rss['feed'][]=array('title'=>_m('комментарий #').$comment->id,
								'description'=>'<b>'.htmlspecialchars($comment->user->login).'</b><br />'.$text_html,
								'link'=>config::get('core.siteUrl').$comment->item->url,
								'pubDate'=>$comment->updated);
			}
		}
		LabVars::$break=true;
		feed($rss);
	}
	
	public function comment_repair()
	{
		if (!access('admin'))	return LabCMS::i()->error403();
		$comment=Factory::create('lc_comments.comment', $id);
		$comment->repairComments();
		LabCMS::i()->reset_cache('html');
		LabCMS::i()->reset_cache('db');
		refresh();
	}
	
	public function comment_comments()
	{
		if (!$item=$this->_getItemById($_GET['var1'])) return;

		$comments=$item->getComments();
		$this->_setItemAccess($item);
		$item->setCommentsUserData();
		self::_setTpl('comments');
		self::_assignByRef('comments_item', $item);
		self::_assignByRef('comments', $comments);
	}
	
	public function comment_detele_old()
	{
		if (!access('admin'))	return LabCMS::i()->error403();
		$list=Factory::create('lc_comments.list');
		$list->deleteOldComments();
		refresh();
		
	}
	
	public function getCommentsItem($key, $url, $title, $type=1)
	{
		$item=Factory::create('lc_comments.item');
		$item->loadDataByKey($key, $url, $title, $type);

		$this->_setItemAccess($item);
		
		//для админа и модератора выводим сообщения о комментариях, требущих их внимания		
		if (access('module.lc_comments.moderator, module.lc_comments.admin')) {
			$list=Factory::create('lc_comments.list');
			if ($count=$list->getAbusedCounter()) {
				LabCMS::i()->setMessage(
					sprintf(
						_m('<a href="%s">Жалоб на комментарии: %s</a>'),
						_url(false, 'moderate', false, getModulePath('lc_comments')), $count
					),
					true, true
				);
			}
			if ($count=$list->getModerationCounter()) {
				LabCMS::i()->setMessage(
					sprintf(
						_m('<a href="%s">Комментариев ожидают модерации: %s</a>'),
						_url(false, 'premoderate', false, getModulePath('lc_comments')), $count
					),
					true, true
				);
			}
		}
		
		return $item;
	}
	
	protected function _setItemAccess($item)
	{
		$_SESSION['comments'][$item->id]=true;
	}
	
	protected function _checkItemAccess($item)
	{
		if (access('moderator, admin')) return true;
		//если пользователь пришел не со страницы с записью, то делаем запрос к модулю чтобы узнать есть ли такая запись
		if (!isset($_SESSION['comments'][$item->id])) {
			$module=DB::i()->selectCell("SELECT module FROM ?_modules WHERE module_id=?", $item->module_id);
			$classname=$module.'_api';
			Factory::set($classname, $classname, LAB_PATH_MODULES.'/'.$module.'/class.api.php');
			$api=Factory::create($classname);

			$_SESSION['comments'][$item->id]=$api->checkCommentAccess($item->key);
		}
		//if (LabCMS::i()->isAjax()) $this->_json_error(_m('Недостаточно прав'));
		if (!$_SESSION['comments'][$item->id])	refresh('', _url(false, false, false, $item->url));
	}
	
	public function comment_json()
	{
		Debug_HackerConsole_Main::disable();
		if (!User::checkToken($_POST['token'], false)) $this->_json_error();
		
		$method=(isset($_GET['action']))?'_json_'.$_POST['action']:null;
		if (is_callable(array($this, $method))) {
			$json_response=$this->$method();
		} else {
			$json_response=array('error'=>'Метод не найден');
		}
			
		echo real_json_encode($json_response);
		exit;
	}
	
	function _json_comment_get_new()
	{
		if (!$item=$this->_getItemById($_POST['item_id'])) return;
		$list=$this->_getList();
		$json_response=array();
		if ($comments=$list->getNewComments($item, $_POST['comment_id'])) {
			foreach ($comments as $comment) {
				$comment['text_html']=getbb($comment['text']);
				self::_assign('comment', $comment);
				$json_response[]=array(
					'comment_id'=>$comment['comment_id'],
					'parent_id'=>$comment['parent_id'],
					'html'=>Tpl::i()->fetch(LabCMS::i()->getModuleName().'/comment_ajax.tpl')
				);
			}
		}
		return $json_response;
	}
	
	protected function _json_comment_add()
	{
		if (!access('add')) $this->_json_error(_m('Недостаточно прав для написания комментариев!'));
		if ($_POST['comment_id']) {
			if (!$comment=$this->_getCommentById($_POST['comment_id'])) return $this->_json_error();
			$item=$comment->item;
		} else {
			if (!$item=$this->_getItemById($_POST['item_id'])) return $this->_json_error();
		}
		
		$form=$this->_buildCommentForm();
		if ($form->validate()) {
			$data=$form->getValue();
			$data['status']=access('no_premoderation')?1:2;
			if ($comment=$item->addComment($data, $comment)) {
				self::_assign('comment', $comment);
				$json_response['comment_id']=$comment->id;
				$json_response['parent_id']=intval($comment->parent_id);
				$json_response['html']=Tpl::i()->fetch('lc_comments/comment_ajax.tpl');
				return $json_response;
			}
		} else {
			foreach (new RecursiveIteratorIterator($form->getIterator()) as $elem) {
				if ($elem->getError()) $this->_json_error($elem->getError());
			}
		}
	}
	
	protected function _json_comment_edit()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;
		
		if (($cmt->user_id==User::i()->id && !access('edit'))
			|| ($cmt->user_id!=User::i()->id && !access('moderator, admin')))
				$this->_json_error(_m('Недостаточно прав для редактирования!'));
		
		$form=$this->_buildCommentForm();
		
		if ($form->validate()) {
			$data=$form->getValue();
			if ($cmt->edit($data)) return array('text_html'=>getbb($data['text']), 'comment_id'=>$cmt->id);
		} else {
			foreach (new RecursiveIteratorIterator($form->getIterator()) as $elem) {
				if ($elem->getError()) $this->_json_error($elem->getError());
			}
		}
	}
	
	protected function _json_comment_delete()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;

		if (($cmt->user_id==User::i()->id && !access('edit'))
			|| ($cmt->user_id!=User::i()->id && !access('moderator, admin')))
				$this->_json_error(_m('Недостаточно прав для удаления!'));

		$json_response['comment_id']=$cmt->id;
		
		//если сообщение последнее в цепочке и удаляет автор - удаляем полностью
		//если нет - просто помечаем удаленным
		if ($cmt->leaf && $cmt->user_id==User::i()->id) {
			if (!$cmt->deleteBranch()) $this->_json_error();
			$json_response['branch_id']=$cmt->id;
		} else {
			if (!$cmt->delete()) $this->_json_error();
		}
		return $json_response;
	}
	
	protected function _json_comment_undelete()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;
		
		if (!access('admin')) $this->_json_error(_m('Недостаточно прав для восстановления!'));
		
		$json_response['comment_id']=$cmt->id;
		
		if (!$cmt->undelete()) $this->_json_error();

		return $json_response;
	}
	
	protected function _json_comment_unhide()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;
		
		if (!access('moderator, admin')) {
			$this->_json_error(_m('Недостаточно прав для модерации!'));
		}
		
		$json_response['comment_id']=$cmt->id;
		
		if (!$cmt->unhide()) $this->_json_error();
		$json_response['ok']=true;

		return $json_response;
	}
	
	protected function _json_comment_delete_branch()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;
		
		if (!access('admin')) $this->_json_error(_m('Недостаточно прав для удаления!'));
		if ($cmt->deleteBranch()) return array('comment_id'=>$cmt->id);
		$this->_json_error();
	}
	
	protected function _json_comment_abuse()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;

		if (!User::i()->isGuest() && access('abuse')) {
			if ($cmt->abuse()) return array('comment_id'=>$cmt->id, 'message'=>"Жалоба принята");
			$this->_json_error();
		} else $this->_json_error(_m('Недостаточно прав для подачи жалобы'));
	}
	
	protected function _json_comment_get()
	{
		if (!$cmt=$this->_getCommentById($_POST['comment_id'])) return;

		if ((!access('edit') && $cmt->user_id==User::i()->id)
				&& !access('moderator, admin')) {
				$this->_json_error(_m('Недостаточно прав для редактирования!'));
		}
		
		$json_response['comment_id']=$cmt->login;
		$json_response['text']=$cmt->text;
		return $json_response;
	}
	
	protected function _json_vote()
	{
		//в данном случае $_POST['item_id'] соотвествует comment_id для унификации клиентского кода
		if (!$cmt=$this->_getCommentById($_POST['item_id'])) return;
		if ($json_response=$cmt->vote($_POST['vote']))	return $json_response;
		else $this->_json_error();
	}
	
	protected function _json_error($msg=false)
	{
		if ($msg) echo real_json_encode(array('error'=>$msg));
		else echo real_json_encode(array('error'=>LabCMS::i()->errors[0]));
		exit; 
	}
	
	protected function _buildCommentForm()
	{
		$form=Factory::create('HTML_QuickForm2', 'cmt_form', 'post', array('action'=>'#'), false);
		$token=$form->addElement('hidden', 'token', array('value'=>User::i()->token));

		if (User::i()->isGuest()) {
			$login=$form->addElement(
				'text', 'login', null,
				array('label'=>'Имя')
			);
			$login->addRule('required', _m('Надо ввести имя'), null, $this->validation);
			$form->addElement('text', 'email', null, array('label'=>_m('Email')));
			$this->_addLoginRules($login);
		}
		$msg=$form->addElement(
			'textarea', 'text',
			array(
				'class'=>'bbcode',
				'rows'=>5,
				'cols'=>50
			),
			array('label'=>_m('Комментарий'))
		);
		$form->addElement(
			'submit', null, array('value'=>_m('Сохранить')),
			array('label'=>'&nbsp;')
		);
		$msg->addRule(
			'required', _m('Не введен текст комментария'), null, $this->validation
		);
		return $form;
	}
	
	protected function _addLoginRules(&$login)
	{
		return $login->addRule(
			'required', _m('Надо указать логин'), null, $this->validation
		)
		->and_(
			$login->createRule(
				'maxlength',
				sprintf(
					_m('Логин не может быть длиннее %d символов'),
					$this->max_login_length
				),
				20, $this->validation
			)
		)
		->and_(
			$login->createRule(
				'notCallback', _m('Логин уже занят'),
				array(User::i(), 'isLoginExists')
			)
		);
	}
	
	protected function _getCommentById($id)
	{
		if (!intval($id)) return self::_error(_m('Комментарий не найден'));
		$comment=Factory::create('lc_comments.comment', $id);
		if (!$comment->id) return self::_error(_m('Комментарий не найден'));
		$this->_checkItemAccess($comment->item);
		return $comment;
	}
	
	protected function _getItemById($id)
	{
		if (!intval($id)) return self::_error(_m('Запись не найдена'));
		$item=Factory::create('lc_comments.item', $id);
		if (!$item->id) return self::_error(_m('Запись не найдена'));
		$this->_checkItemAccess($item);
		return $item;
	}
	
	protected function _getList()
	{
		return Factory::create('lc_comments.list');
	}
	
	protected function _botObor($form, $form_html)
	{
		$bform = new Botobor_Form($form_html);
		$bform->addHoneypot('login');
		
		$form_html = $bform->getCode();
		if ($form->isSubmitted()) {
			if (Botobor_Keeper::get()->isRobot()) {
				die('злобный бот!');
			}
			//после восстановления полей снова передаем данные в обработчик формы
			$form->setDatasources(array(Factory::create('HTML_QuickForm2_DataSource_SuperGlobal')));
		}
	}
}
