<?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 LabCMS
{
	protected static $instance;
	public $no_errors=true;
	public $errors=array();
	public $title=null;
	public $description=null;
	public $keywords=null;
	public $cache=array();
	public $skins=array();
	public $cached=false;
	
	private $compile_id=1;
	private $group_id=-1;
	private $template=null;
	private $translate=null;
	private $_cache_id=1;

	private function __construct()
	{
		$this->url=$_SERVER['PHP_SELF'];
		$this->group_id=User::i()->group_id;
		$this->_GetNode();
		
		//подключаем языковой файл для ядра
		$this->_GetTranslate('core');

		$this->_GetModules();
		
		$this->setSkin(LabConfig::$cfg['skin']);
	}

	private function __clone() {}
	
	public static function i()
	{
		return self::instance();
	}
	
	public static function instance()
	{
		if (self::$instance === null)
		{
			//если существует кэшированый экземпляр класса LabCMS, то загружаем его
			if (LabConfig::$cfg['cache'] && $data=@file_get_contents(LAB_PATH_DATA.'/objects/'.User::i()->group_id.'-'.rawurlencode($_GET['url']).'.obj'))
				self::$instance=unserialize($data);
			else
				self::$instance = new self;
		}
		return self::$instance;
	}

	private function _GetNode()
	{
		$node=$this->_GetNodeByUrl($this->url);
		if (!empty($node))
		{
			$this->_GetNodeParams($node['params']);

			if ($node['symlink'])
			{
				$node=$this->_GetNodeById($node['symlink']);
				$this->_GetNodeParams($node['params']);
			}
			
			$this->id=$node['tree_id'];
		}
		
		if (empty($this->id) && !$this->error301()) $this->error404();
		
		$this->parent_id=$node['parent_id'];
		$this->title=$node['tree_name'];
		$this->template=$node['template'];
		
		$this->lang['id']=$node['lang_id'];
		$this->lang['key']=$node['key'];
		$this->lang['name']=$node['lang_name'];
		
		$this->parents=$this->GetAllParents($this->id);

		if (!$this->lang['id'] || !$this->template) $this->_GetNodeInfo();
	}

	private function _GetNodeInfo()
	{
		$parent_nodes=Db::i()->select("SELECT template, l.lang_id, lang_name, `key`  FROM ?_tree t LEFT JOIN ?_langs l ON t.lang_id=l.lang_id WHERE tree_id IN (?a) ORDER BY FIELD(tree_id, ?a)", $this->parents, array_reverse($this->parents));
		if ($parent_nodes)
		{
			foreach($parent_nodes as $node)
			{
				if ($this->template && $this->lang['id']) break;
				if (!$this->template && $node['template']) $this->template=$node['template'];
				if (!$this->lang['id'] && $node['lang_id'])
				{
					$this->lang['id']=$node['lang_id'];
					$this->lang['key']=$node['key'];
					$this->lang['name']=$node['lang_name'];
				}
			}
		}
		
		if (!$this->lang['id'])
		{
			$lang=Db::i()->selectRow("SELECT lang_id, lang_name, `key` FROM ?_langs WHERE `key`=?", LabConfig::$cfg['default_lang']);
			$this->lang['id']=$lang['lang_id'];
			$this->lang['name']=$lang['lang_name'];
			$this->lang['key']=$lang['key'];
		}
		
	}
	
	private function _GetModules()
	{
		$static=$component=$system=$common=array();
		$modules=Db::i()->select('SELECT m.module_id, module, component, tree_id, static, runlevel FROM ?_includes i JOIN ?_modules m ON i.module_id=m.module_id WHERE tree_id IN (?a) AND m.disable=0 ORDER BY FIELD(tree_id, ?a), runlevel, component DESC', $this->parents, array_reverse($this->parents));
		if ($modules)
		foreach ($modules as $module)
		{
			if (!count($static) && !count($system) && !count($component) && !count($common)) $current_node=$module['tree_id'];
			if ($module['tree_id']==$current_node || $module['static'])
			{
				if ($module['runlevel']>1000) $common[$module['module_id']]=$module;
				elseif ($module['runlevel']<1000) $system[$module['module_id']]=$module;
				elseif ($module['component']) $component[$module['module_id']]=$module;
			}
		}
		
		//порядок подключения модулей: системные, компоненты, статичные, остальные
		$this->modules=$system+$component+$common;

		if ($this->modules)
		foreach($this->modules as $module)
		{
			$this->_GetModuleInfo($module);
		}
	}
	
	private function _GetNodeByUrl($url)
	{
		return Db::i()->selectRow("SELECT tree_id, parent_id, symlink, params, template, tree_name, l.lang_id, lang_name, `key` FROM ?_tree t LEFT JOIN ?_langs l ON t.lang_id=l.lang_id WHERE path=? AND `show`=1 LIMIT 1", $url);
	}
	
	private function _GetNodeById($id)
	{
		return Db::i()->selectRow("SELECT tree_id, parent_id, symlink, params, template, tree_name, l.lang_id, lang_name, `key` FROM ?_tree t LEFT JOIN ?_langs l ON t.lang_id=l.lang_id WHERE tree_id=?d and `show`=1", $id);
	}
	
	private function _GetNodeParams($params)
	{
		//разбираем параметры полученные из БД
		$var_arr=explode("&", $params);
		$this->params=array();
		foreach ($var_arr as $val)
		{
			$vars=explode("=", $val);
			if (trim($vars[0])) $this->params[trim($vars[0])]=trim($vars[1]);
		}
		
		if(isset($this->params['location'])) $this->_RedirectByLocation(); 
	}
	
	private function _RedirectByLocation()
	{
		// Если ссылка на tree_id, если нет, то просто переходим как по url
		if(is_numeric($this->params['location']))
		{
			$this->params['location'] = Db::i()->selectCell("SELECT path  FROM ?_tree WHERE `tree_id` = ?d", $this->params['location']); // Получаем путь
		}
		
		if(!empty($this->params['location']))
		{
			header("Location: ".$this->params['location']);
			exit();
		}
	}
	
	public function setSkin($skin)
	{
		LabVars::$current_skin=$skin;
		$this->_setTemplateDir($this->getSkin());
		Tpl::i()->compile_id=$this->getSkin();
		$this->_setBase($this->getSkin());

		if (array_key_exists($skin, $this->skins) && is_array($this->skins[$skin])
			&& is_array($this->skins[$skin]['css']) && is_array($this->skins[$skin]['js'])) return;
		
		$this->skins[$this->getSkin()]['css']=$this->skins[$this->getSkin()]['js']=array();

		//подключаем общие для дизайна css и js
		$skin=(is_dir(LAB_PATH_TEMPLATE.'/'.$this->getSkin().'/css'))?$this->getSkin():'default';
		$this->_SetSkinCss(LAB_PATH_TEMPLATE.'/'.$skin.'/css');

		$skin=(is_dir(LAB_PATH_TEMPLATE.'/'.$this->getSkin().'/js'))?$this->getSkin():'default';
		$this->_SetSkinJs(LAB_PATH_TEMPLATE.'/'.$skin.'/js');
	
		foreach($this->modules as $module)
		{
			//если для данного модуля в дизайне совсем не предусмотрено шаблонов (нет соотвествующего каталога),
			//то подключаем шаблоны целиком из дефолтного скина (включая css и js). Если же каталог присутствует, то подключаем из дефолта только недостающие tpl файлы
			if (is_dir(LAB_PATH_TEMPLATE.'/'.$this->getSkin()."/".$module['module'])) $skin=$this->getSkin();
			else $skin='default';

			if (is_dir(LAB_PATH_TEMPLATE.'/'.$skin.'/'.$module['module'].'/css'))
				$this->_SetSkinCss(LAB_PATH_TEMPLATE.'/'.$skin.'/'.$module['module'].'/css');
			elseif($skin!='default' && is_dir(LAB_PATH_TEMPLATE.'/default/'.$module['module'].'/css'))
				$this->_SetSkinCss(LAB_PATH_TEMPLATE.'/default/'.$module['module'].'/css');
				
			if (is_dir(LAB_PATH_TEMPLATE.'/'.$skin.'/'.$module['module'].'/js'))
				$this->_SetSkinJs(LAB_PATH_TEMPLATE.'/'.$skin.'/'.$module['module'].'/js');
			elseif($skin!='default' && is_dir(LAB_PATH_TEMPLATE.'/default/'.$module['module'].'/js'))
				$this->_SetSkinJs(LAB_PATH_TEMPLATE.'/default/'.$module['module'].'/js');
		}
		
		$this->cached=false;
	}
	
	private function _setBase($skin)
	{
		Tpl::i()->assign('base', 'http://'.$_SERVER["HTTP_HOST"].str_replace(LAB_PATH_ROOT, '', LAB_PATH_TEMPLATE).'/'.$skin.'/');
	}
	
	private function _setTemplateDir($skin)
	{
		$dir=array(	$skin=>LAB_PATH_TEMPLATE.'/'.$skin.'/');
		if ($dir!='default') $dir['default']=LAB_PATH_TEMPLATE.'/default/';
		Tpl::i()->setTemplateDir($dir);
	}
	
	private function _SetSkinCss($path)
	{
		if($files=glob($path.'/*.css'))
		{
			sort($files);
			foreach($files as $file)
			{
				$this->skins[$this->getSkin()]['css'][]=array("path"=>str_replace(LAB_PATH_ROOT, '', $file), "media"=>basename($file, ".css"));
			}
		}
	}

	private function _SetSkinJs($path)
	{
		if($js=glob($path.'/*.js'))
		{
			sort($js);
			foreach($js as $js_item)
			{
				$this->skins[$this->getSkin()]['js'][]=str_replace(LAB_PATH_ROOT, '', $js_item);
			}
		}
	}

	/*
	* пытается найти редирект и выводит ошибку 301 в случае успеха
		* @author Loki
	*/
	function error301($url=null)
	{
		if (!$url) $url=$this->cut_sessid($_SERVER['REQUEST_URI']);
		$redirects=Db::i()->select("SELECT pattern, link FROM ?_redirects WHERE `active`=1 ORDER BY pos");
		if ($redirects)
		foreach($redirects as $redirect)
		{
			$pattern=str_replace('/', '\/', $redirect['pattern']);
			if ($url!=$link=preg_replace('/'.$pattern.'/i', $redirect['link'],$url))
			{
				header("HTTP/1.0 301 Moved Permanently");
				header("Location: ".$link);
				exit;
			}
		}
		return false;
	}
	
	/*
	* выводит ошибку 404 из шаблона
		* @author Loki
	*/
	function error404()
	{
		$this->_setTemplateDir(LabConfig::$cfg['skin']);
		Tpl::i()->compile_id=$this->getSkin();
		Header("HTTP/1.0 404 Not Found");
		Header("Date: " . gmdate("D, d M Y H:i:s") . " GMT");
		Header("Content-Type: text/html; charset=utf-8");
		Tpl::i()->assign('sitemap', $this->getModuleNodePath('sitemap'));
		Tpl::i()->display('errors/404.tpl');
		exit;
	}
	
	/*
	* выводит ошибку 403 из шаблона
		* @author Loki
	*/
	function error403()
	{
		$this->_setTemplateDir(LabConfig::$cfg['skin']);
		Tpl::i()->compile_id=$this->getSkin();
		//если пользователь неавторизован
		if ($this->group_id==IS_GUEST)
		{
			if (empty($this->params['error403']))
			{
				$module=LabConfig::$cfg['auth_module']?LabConfig::$cfg['auth_module']:'login';
				$this->params['error403']=$this->getModuleNodePath($module);
			}
			if ($this->params['error403']) refresh('', $this->url(false, false, false, $this->params['error403'], $_SERVER['REQUEST_URI']));
			Tpl::i()->assign('auth_path', $this->params['error403']);
		}
		Header("HTTP/1.0 403 Forbidden");
		Header("Date: " . gmdate("D, d M Y H:i:s") . " GMT");
		Header("Content-Type: text/html; charset=utf-8");
		Tpl::i()->display('errors/403.tpl');
		exit;
	}
	
	/*
	* добавляет в шаблон сообщение об ошибке
	* добавляет значение true параметру класса is_error
	* @param string	текст сообщения
		* @return boolean	true
		* @author Loki
	*/
	function error($msg, $lang=false)
	{
		if ($msg)
		{
			if ($lang) $msg=$msg;
			else $msg=_m($msg);
		}
		else $msg=_m("Произошла неизвестная ошибка!");
		Tpl::i()->append('errors', $msg);
		$this->errors[]=$msg;
		
		$this->is_error=true; //для совместимости со старыми версиями - потом убрать.
		$this->no_errors=false;
	}
	
	
	/*
	* метод определяет всех родителей текущего узла
	* @param integer	id узла
		* @return array	массив родителей отсортированный по подчиненности
		* @author Loki
	*/
	function GetAllParents($id)
	{
		$tree=Db::i()->selectCol("SELECT tree_id AS ARRAY_KEY, parent_id FROM ?_tree");
		$parents=array($id);
		while(!empty($tree[$id]))
		{
			$parents[]=$tree[$id];
			$id=$tree[$id];
		}
		return array_reverse($parents);
	}

	/*
	* проверяет существует ли локализованная версия запрошенной фразы и возвращает ее
	* если нет, то возвращает сам ключ в качестве результата
	* @param string	фраза для перевода
	* @param integer	значение для числительного (пока не реализовано)
	* @return string	локализованная версия, либо ключ
	* @author Loki
	*/
	function _($text)
	{
		$key= mb_strtolower($text);
		if(!$key) return false;
		
		$module=$this->getModuleName();
		if (!$module) $module='core';
		
		if (is_array($this->translate[$module]) && array_key_exists($key, $this->translate[$module]))
		{
			if ($this->translate[$module][$key]) return $this->translate[$module][$key];
		}
		elseif($module!='core' && is_array($this->translate['core']) && array_key_exists($key, $this->translate['core']))
		{
			if ($this->translate['core'][$key]) return $this->translate['core'][$key];
		}
		else
		{
			$this->_addStringToLang($key, $module);
		}
		return $text;
	}
	
	function getNumStatus($number)
	{
		$cases = array (2, 0, 1, 1, 1, 2);
		return ($number%100>4 && $number%100<20)? 2 : $cases[min($number%10, 5)];
	}
	
	private function _escapeKey($text)
	{
		return str_replace(array('\\', "'"), array('\\\\', "\'"), $text);
	}
	
	private function _addStringToLang($key, $module)
	{
		$folder=LAB_PATH_KERNEL.'/languages/'.LabConfig::$cfg['default_lang'];
		$file=$folder.'/lang.'.$module.'.php';
		
		$data='$lang[\''.$this->_escapeKey($key).'\']="";'."\r\n";
	
		if (!file_exists($file) && is_writable($folder))
		{
			file_put_contents($file, "<"."?php\r\n".$data);
			chmod($file, 0666);
		}
		elseif (is_writable($file))
		{
			$handle = fopen($file, "a");
			fwrite($handle, $data);
			fclose($handle);
		}
		elseif(LabConfig::$cfg['debug_mode']) die('Недоступен для записи: '. $file);
		elseif(LabConfig::$cfg['admin_mail'])
		{
			if (!file_exists(LAB_PATH_DATA.'/cache/mail_'.md5($module.$key)))
			{
				$mail=new Mailer(LabConfig::$cfg['admin_mail'], LabConfig::$cfg);
				$mail->From = 'bug@'.preg_replace('/^www\./i', '', $_SERVER["SERVER_NAME"]);
				$mail->FromName = "Ошибка сервера";
				$mail->Subject = "Произошла ошибка на сайте ".$_SERVER["SERVER_NAME"];;
				$mail->Body    = 'Недоступен для записи: '. $file;
				$mail->Send();
				file_put_contents(LAB_PATH_DATA.'/cache/mail_'.md5($module.$key), false);
			}
		}
		$this->cached=false;
		$this->translate[$module][$key]='';
	}
	
	
	/*
	* метод собирает информацию о модуле. Подключает права и настройки всех родительских групп
	* проверяет наличие и подключает шаблоны для текущего или дефолтного скина
	* данные заносятся в свойства метода для возможности последующего кэширования
	* @param array	информация о модуле полученная из БД
	* @param integer	id модуля
	* @author Loki
	*/
	private function _GetModuleInfo($module)
	{
		$config=$access=array();
		//подключаем настройки и права для родительских групп
		foreach (array_merge(User::getParentGroups($this->group_id), array($this->group_id)) as $group_id)
		{
			if (file_exists(LAB_PATH_DATA.'/configs/'.$group_id.'_'.$module['module_id'].'.conf'))
			{
				include_once LAB_PATH_DATA.'/configs/'.$group_id.'_'.$module['module_id'].'.conf';
			}
		}

		if ($config) $this->modules[$module['module_id']]['config']=$config;
		if ($access) $this->modules[$module['module_id']]['access']=$access;
		
		//подключаем файлы с переводами модуля
		$this->_GetTranslate($module['module']);
	}

	/*
	* проверяет существует ли файл локализации для дефолтного языка я для текущего по указанному адресу
	* если существует, то подключает их.
	* @param string	путь для поиска файлов локализации
	* @author Loki
	*/
	private function _GetTranslate($module, $path='/kernel/languages')
	{
		$path=LAB_PATH_ROOT.$path;
		//сначала подключаем язык по умолчанию
		$this->_GetTranslateFile($path.'/'.LabConfig::$cfg['default_lang'].'/lang.'.$module.'.php', $module);
		//затем подключаем язык текущей ветви
		$this->_GetTranslateFile($path.'/'.$this->lang['key'].'/lang.'.$module.'.php', $module);
	}
	
	private function _GetTranslateFile($file, $module)
	{
		$lang=array();
		if (file_exists($file))
		include_once $file;
		if ($lang) foreach ($lang as $key=>$string)
		{
			if (!is_array($this->translate) || !array_key_exists($module, $this->translate) || !array_key_exists($key, $this->translate[$module])) $this->translate[$module][$key]=$string;
			elseif ($string) $this->translate[$module][$key]=$string;
		}
	}
	
	/**
		* метод закидывает отработавший модуль в шаблон, в зависимости от его типа
		* если модуль является компонентом, то добавляем его данные в переменную $_component, в противном случае в переменную $_имя_модуля
		* @param text
		* @author Loki
		*/
	function assignModule($mod_template)
	{
		//проверяем существует ли шаблон для данного модуля
		if (!$template=$this->_checkTempalte($mod_template)) return false;

		//если модуль является компонентом, то добавляем его данные в переменную $_component, в противном случае в переменную $_имя_модуля
		
		if ($this->isModuleComponent())
		{
			Tpl::i()->assign('_component', Tpl::i()->fetch($template, $this->_getCacheId(), $this->compile_id));
		}
		else
		{
			Tpl::i()->assign("_module_".$this->getModuleName(), Tpl::i()->fetch($template, $this->_getCacheId(), $this->compile_id));
		}
		$this->_cache_id=1;
	}
	
	private function _getCacheId()
	{
		return $this->getModuleId()."|".$this->group_id."|".$this->_cache_id;
	}
	
	/**
		* проверяет кэширован ли запрошенный шаблон
		* @param text	имя шаблона
		* @author Loki
		*/
	function is_cached($template, $time, $id=1)
	{
		if (LabConfig::$cfg['cache'] && !LabConfig::$cfg['debug_mode'])
		{
			//сначала должен быть кэширован объект
			if (!$this->cached) return false;

			//включаем кэширование smarty
			Tpl::i()->caching = true;
			$this->_cache_id=$id;

			Tpl::i()->setCacheLifetime($time);

			//если модуль имеет шаблон для вывода, то проверяем кэширован ли шаблон 
			if ($template)
			{
				$template=$this->_checkTempalte($template);
				//если есть кэшированная версия, то возвращаем true
				return Tpl::i()->isCached($template, $this->_getCacheId(), $this->compile_id);
			}
			else //в противном случае проверяем кэширован ли объект.
			{
				return $this->cached;
			}
		}
		return false;
	}
	
	private function _checkTempalte($tpl)
	{
		if (substr($tpl, 0, 7)=='string:')
		{
			$this->compile_id=$this->getSkin();
			return $tpl;
		}
		
		$module=$this->getModuleName();
		$current=$this->getSkin();
		$skins=array($current, 'default');

		foreach($skins as $skin)
		{
			if ($this->cached && !empty($this->skins[$skin][$module][$tpl]))
			{
				$this->compile_id=$skin;
				return $this->skins[$skin][$module][$tpl];
			}
		}

		$this->cached=false;

		foreach($skins as $skin)
		{
			$path=LAB_PATH_TEMPLATE.'/'.$skin.'/'.$module.'/'.$tpl;
			if (file_exists($path))
			{
				$this->compile_id=$skin;
				return $this->skins[$skin][$module][$tpl]=canonicalPath($path);
			}
		}

		return $this->error(sprintf(_m("Шаблон '%s' не найден"), $module.'/'.$tpl), true);
	}
	
	private function _checkMainTemplate($tpl)
	{
		if (!$tpl) $tpl='default.tpl';
		$current=$this->getSkin();
		$skins=array($current, 'default');
		$templates=array($tpl, 'default.tpl');

		foreach($skins as $skin)
		{
			if ($this->cached && !empty($this->skins[$skin][null][$tpl]))
			{
				$this->compile_id=$skin;
				return $this->skins[$skin][null][$tpl];
			}
		}
		
		$this->cached=false;

		foreach($templates as $template)
		{
			foreach($skins as $skin)
			{
				$path=LAB_PATH_TEMPLATE.'/'.$skin.'/'.$template;
				if (file_exists($path))
				{
					$this->compile_id=$skin;
					return $this->skins[$skin][null][$tpl]=canonicalPath($path);
				}
			}
		}
		
		return $this->error(sprintf(_m("Шаблон '%s' не найден"), $tpl), true);
	}
	
	/*
		* сбрасывает кэш
		* если не установлен ключ, то сбрасываются только шаблоны текущего модуля, если установлен - то все шаблоны или/и объекты
		* @param boolean	ключ для полного сброса кэша
		* @author Loki
		*/
	function reset_cache($mode=false)
	{
		switch($mode)
		{
			case('all'):
				self::reset_objects();
				//Tpl::i()->clearCompiledTemplate();
				self::reset_tpl();
				self::reset_files();
				self::reset_memcache();
				self::reset_external();
				break;
			case('html'):
				self::reset_tpl();
				break;
			case('objects'):
				self::reset_objects();
				break;
			case('parsed'):
				Tpl::i()->clearCompiledTemplate();
				break;			
			case('db'):
				self::reset_db();
				break;
			default:
				self::reset_tpl($this->getModuleId());
		}
	}
	
	static function reset_files()
	{
		$files=glob(LAB_PATH_DATA.'/cache/*');
		if ($files) foreach($files as $file)
		{
			if (is_file($file)) unlink($file);
		}
	}
	
	static function reset_memcache()
	{
		if (LabConfig::$cfg['cache']
			&& (LabConfig::$cfg['use_memcached']
			    || LabConfig::$cfg['use_memcached_db']
			    || LabConfig::$cfg['use_memcached_tpl'])
			&& !LabConfig::$cfg['debug_mode'])
		Cache_Memcache::i()->clean();
	}
	
	static function reset_db()
	{
		if (!LabConfig::$cfg['debug_mode'] && LabConfig::$cfg['use_memcached_db'])
		{
			if (is_object(Db::i()->_cacher)) Db::i()->_cacher->clean();
		}
		else
		{
			$files=glob(LAB_PATH_DATA.'/cache/*');
			if ($files) array_map("unlink", $files);
		}
	}
	
	static function reset_tpl($module_id=null)
	{
		if (LabConfig::$cfg['cache'])
		{
			if ($module_id)
				Tpl::i()->clearCache(null,$module_id);
			else
				Tpl::i()->clearAllCache();
		}
	}
	
	static function reset_objects()
	{
		$files=glob(LAB_PATH_DATA.'/objects/*.obj');
		if ($files) array_map("unlink", $files);
	}
	
	static function reset_external()
	{
		$files=glob(LAB_PATH_DATA.'/external/*');
		if ($files) array_map("unlink", $files);
	}
	
	function setMessage($mess, $html=false, $lang=false)
	{
		if ($mess)
		{
			if (!$lang) $mess=_m($mess);
			if (!$html) $type='text';
			else $type='html';
			return Tpl::i()->assign('system_message', array($type=>$mess));
		}
		return false;
	}
	
	/*
		* вывод на экран главного шаблона
		* @author Loki
		*/
	function display()
	{
		//$this->cache - это массив, куда добавлены переменные, которые невозможно кэшировать из-за отстутсвия у модуля собствнного шаблона
		Tpl::i()->assign($this->cache);
		$this->_setBase($this->getSkin());
		Tpl::i()->caching = false;
		
		$this->compile_id=$this->getSkin();
		
		//Tpl::i()->caching = (LabConfig::$cfg['cache'] && !LabConfig::$cfg['debug_mode']);
		//если задан некэшируемый в объекте шаблон, то пытаемся отрисовать его
		if (LabVars::$template)
		{
			$template=$this->_checkMainTemplate(LabVars::$template);
		}
		else
		{
			$template=$this->_checkMainTemplate($this->template);
		}
		
		if (!$template) die('Сборочный шаблон не найден!');
		
		Tpl::i()->display($template, "0|".$this->group_id."|".$this->getSkin()."|".$this->cut_sessid($_SERVER['REQUEST_URI']), $this->compile_id);
	}
	
	function cut_sessid($url)
	{
		$sessid=preg_quote(ini_get('session.name'), "/");
		return preg_replace('/[\?|&]'.$sessid.'=[^&]+/', '', $url);
	}
	
	public function url($params=array(), $action='', $type='', $href='', $redirect='', $token=false)
	{
		if ($token) $token=User::i()->token;
		return call_user_func_array(array('Url_'.LabConfig::$cfg['url_type'], 'create'), array($params, $action, $type, $href, $redirect, $token));
	}
	
	public function isAjax($ext=null)
	{
		if (!$ext) $ext=$_GET['data_type'];
		return in_array($ext, array('js', 'xml', 'json', 'ajax'));
	}
	
	public function access($rules)
	{
		//если суперадмин, то все права у него есть.
		if ($this->group_id==1) return true;
		$access=$this->getModuleAccess();
		if (empty($access)) return false;
		$rules=explode(",", $rules);

		foreach ($rules as $rule)
		{
			$rule=trim($rule);
			if (array_key_exists($rule, $access) && $access[$rule]) return true;
		}
		return false;
	}
	
	function disableForDemo()
	{
		if (DEMO_MODE)
		{
			if (!LabVars::$current_module['component']) return false;
			if (LabVars::$current_module['module']=='login' && (!$_GET['action'] || $_GET['action']=='logout')) return false;
			if (!$_REQUEST['token']) return false;
			$this->error("Данная функция недоступна в демо версии");
			return true;
		}
		
	}
	
	private function _isTemplateExists($template)
	{
		return Tpl::i()->templateExists($template);
	}
	
	public function assignCoreVars()
	{
		Tpl::i()->assign('_lc', array(
			'id'=>$this->id,  
			'parent_id'=>$this->parent_id,  
			'url'=>$this->url,  
			'lang_id'=>$this->lang['id'],  
			'lang_key'=>$this->lang['key'],  
			'lang_name'=>$this->lang['name'],  
			'title'=>$this->title,
			'description'=>$this->description,
			'keywords'=>$this->keywords
		));
		Tpl::i()->assign('_title', $this->title);
		$this->_setBase($this->getSkin());
	}
	
	//может использоваться классами для генерации ошибки
	public function is_object()
	{
		return is_object(self::$instance);
	}
	
	public function getModuleConfig()
	{
		return (empty(LabVars::$current_module['config']))?array():LabVars::$current_module['config'];
	}
	
	public function getModuleId()
	{
		return (empty(LabVars::$current_module['module_id']))?null:LabVars::$current_module['module_id'];
	}
	
	public function getModuleName()
	{
		return (empty(LabVars::$current_module['module']))?null:LabVars::$current_module['module'];
	}
	
	public function getModuleAccess()
	{
		return (empty(LabVars::$current_module['access']))?array():LabVars::$current_module['access'];
	}
	
	public function isModuleComponent()
	{
		return (!empty(LabVars::$current_module['component']));
	}
	
	public function isModuleSystem()
	{
		return (!empty(LabVars::$current_module['system']));
	}
	
	public function isModuleStatic()
	{
		return (!empty(LabVars::$current_module['static']));
	}
	
	public function setModuleTemplate($tpl)
	{
		LabVars::$current_module['template']=$tpl;
	}
	
	public function getModuleTemplate()
	{
		return empty(LabVars::$current_module['template'])?false:LabVars::$current_module['template'];
	}
	
	public function getSkinCSS()
	{
		return $this->skins[$this->getSkin()]['css'];
	}
	public function getSkinJS()
	{
		return $this->skins[$this->getSkin()]['js'];
	}
	
	public function getSkin()
	{
		if (empty(LabVars::$current_skin)) LabVars::$current_skin=LabConfig::$cfg['skin'];
		return LabVars::$current_skin;
	}
	
	public static function getCookieDomain()
	{
		if (LabConfig::$cfg['subdomain_auth'])
		{
			return '.'.preg_replace('/^www\./i', '', $_SERVER["SERVER_NAME"]);
		}
		else
		{
			return null;
		}
	}
	
	public function setAllowFileUpload()
	{
		if (!DEMO_MODE) return $_SESSION['access_files']=1;
		return $_SESSION['access_files']=0;
	}
	
	public function putToCache()
	{
		if (LabConfig::$cfg['cache'] && $this->no_errors && empty($this->cached))
		{
			$this->cached=true;
			file_put_contents(LAB_PATH_DATA.'/objects/'.$this->group_id.'-'.rawurlencode($this->url).'.obj', serialize(self::$instance));
			chmod(LAB_PATH_DATA.'/objects/'.$this->group_id.'-'.rawurlencode($this->url).'.obj', 0666);
		}
		elseif(LabConfig::$cfg['cache'] && empty($this->cached))
			@unlink(LAB_PATH_DATA.'/objects/'.$this->group_id.'-'.rawurlencode($this->url).'.obj');
	}
	
	public function getModuleNodePath($module)
	{
		if (isset($this->path_to_module[$module])) return $this->path_to_module[$module];
		$this->path_to_module[$module]=Db::i()->selectCell("SELECT path
												FROM ?_tree t
												JOIN ?_includes i ON t.tree_id=i.tree_id
												JOIN ?_modules m ON i.module_id=m.module_id
												WHERE module = ? AND `show`=1 LIMIT 1", $module);
		$this->cached=false;
		return $this->path_to_module[$module];
	}
}