<?php
/**
* @version	$Id: admin_events_handler.php 14440 2011-07-08 10:06:03Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class AdminEventsHandler extends kDBEventHandler {

	function mapPermissions()
	{
		parent::mapPermissions();

		$permissions = Array(
			'OnSaveColumns'		=>	array('self' => true),
			'OnClosePopup'		=>	array('self' => true),
			'OnSaveSetting'		=>	array('self' => true),
			// export/import permissions is checked within events
			'OnExportCSV'		=>	Array('self' => true),
			'OnGetCSV'			=>	Array('self' => true),
			'OnCSVImportBegin'	=>	Array('self' => true),
			'OnCSVImportStep'	=>	Array('self' => true),
			'OnDropTempTablesByWID' => Array('self' => true),
		);
		$this->permMapping = array_merge($this->permMapping, $permissions);
	}

	/**
	 * Checks permissions of user
	 *
	 * @param kEvent $event
	 */
	function CheckPermission(&$event)
	{
		$perm_value = null;

		$system_events = Array (
			'OnResetModRwCache', 'OnResetSections', 'OnResetConfigsCache', 'OnResetParsedData', 'OnResetMemcache',
			'OnDeleteCompiledTemplates', 'OnCompileTemplates', 'OnGenerateTableStructure', 'OnSynchronizeDBRevisions',
			'OnDeploy', 'OnRebuildThemes', 'OnCheckPrefixConfig', 'OnMemoryCacheGet', 'OnMemoryCacheSet'
		);

		if (in_array($event->Name, $system_events)) {
			// events from "Tools -> System Tools" section are controlled via that section "edit" permission
			$perm_value = /*$this->Application->isDebugMode() ||*/ $this->Application->CheckPermission($event->getSection() . '.edit');
		}

		$tools_events = Array (
			'OnBackup' => 'in-portal:backup.view',
			'OnBackupProgress' => 'in-portal:backup.view',
			'OnDeleteBackup' => 'in-portal:backup.view',
			'OnBackupCancel' => 'in-portal:backup.view',

			'OnRestore' => 'in-portal:restore.view',
			'OnRestoreProgress' => 'in-portal:restore.view',
			'OnRestoreCancel' => 'in-portal:backup.view',

			'OnSqlQuery' => 'in-portal:sql_query.view',
		);

		if (array_key_exists($event->Name, $tools_events)) {
			$perm_value = $this->Application->CheckPermission($tools_events[$event->Name]);
		}

		if ($event->Name == 'OnSaveMenuFrameWidth') {
			$perm_value = $this->Application->isAdminUser;
		}

		if (isset($perm_value)) {
			$perm_helper =& $this->Application->recallObject('PermissionsHelper');
			/* @var $perm_helper kPermissionsHelper */

			return $perm_helper->finalizePermissionCheck($event, $perm_value);
		}

		return parent::CheckPermission($event);
	}

	/**
	 * Enter description here...
	 *
	 * @param kEvent $event
	 */
	function OnResetModRwCache(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		$this->Conn->Query('DELETE FROM ' . TABLE_PREFIX . 'CachedUrls');
	}

	function OnResetSections(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
			$this->Application->deleteCache('master:sections_parsed');
		}
		else {
			$this->Application->deleteDBCache('sections_parsed');
		}

		$event->SetRedirectParam('refresh_tree', 1);
	}

	function OnResetConfigsCache(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
			$this->Application->deleteCache('master:config_files');
		}
		else {
			$this->Application->deleteDBCache('config_files');
		}

		$this->OnResetParsedData($event);

		$skin_helper =& $this->Application->recallObject('SkinHelper');
		/* @var $skin_helper SkinHelper */

		$skin_helper->deleteCompiled();
	}

	/**
	 * Resets parsed data from unit configs
	 *
	 * @param kEvent $event
	 */
	function OnResetParsedData(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		$this->Application->UnitConfigReader->ResetParsedData();

		if ( $this->Application->GetVar('validate_configs') ) {
			$event->SetRedirectParam('validate_configs', 1);
		}
	}

	function OnResetMemcache(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
			$this->Application->memoryCache->reset();
		}
	}

	function OnCompileTemplates(&$event)
	{
		$compiler =& $this->Application->recallObject('NParserCompiler');
		/* @var $compiler NParserCompiler */

		$compiler->CompileTemplatesStep();
		$event->status = erSTOP;
	}

	/**
	 * Deletes all compiled templates
	 *
	 * @param kEvent $event
	 */
	function OnDeleteCompiledTemplates(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		$base_path = WRITEABLE . DIRECTORY_SEPARATOR . 'cache';

		// delete debugger reports
		$debugger_reports = glob($base_path . '/debug_@*@.txt');

		foreach ($debugger_reports as $debugger_report) {
			unlink($debugger_report);
		}

		$this->_deleteCompiledTemplates($base_path);
	}

	function _deleteCompiledTemplates($folder, $unlink_folder = false)
	{
		$sub_folders = glob($folder . '/*', GLOB_ONLYDIR);

		foreach ($sub_folders as $sub_folder) {
			$this->_deleteCompiledTemplates($sub_folder, true);
		}

		$files = glob($folder . '/*.php');

		foreach ($files as $file) {
			unlink($file);
		}

		if ($unlink_folder) {
			rmdir($folder);
		}
	}

	/**
	 * Generates sturcture for specified table
	 *
	 * @param kEvent $event
	 * @author Alex
	 */
	function OnGenerateTableStructure(&$event)
	{
		$types_hash = Array(
			'string'	=>	'varchar|text|mediumtext|longtext|date|datetime|time|timestamp|char|year|enum|set',
			'int'		=>	'smallint|mediumint|int|bigint|tinyint',
			'float'		=>	'float|double|decimal',
		);

		$table_name = $this->Application->GetVar('table_name');
		if (!$table_name) {
			echo 'error: no table name specified';
			return ;
		}

		if (TABLE_PREFIX && !preg_match('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', $table_name) && (strtolower($table_name) != $table_name)) {
			// table name without prefix, then add it (don't affect K3 tables named in lowercase)
			$table_name = TABLE_PREFIX.$table_name;
		}

		if (!$this->Conn->TableFound($table_name)) {
			// table with prefix doesn't exist, assume that just config prefix passed -> resolve table name from it
			$prefix = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '/', '', $table_name);
			if ($this->Application->prefixRegistred($prefix)) {
				// when prefix is found -> use it's table (don't affect K3 tables named in lowecase)
				$table_name = $this->Application->getUnitOption($prefix, 'TableName');
			}
		}

		$table_info = $this->Conn->Query('DESCRIBE '.$table_name);

		// 1. prepare config keys
		$grids = Array (
			'Default' => Array (
				'Icons' => Array ('default' => 'icon16_item.png'),
				'Fields' => Array (),
			)
		);

		$grid_fields = Array();

		$id_field = '';
		$fields = Array();
		$float_types = Array ('float', 'double', 'numeric');
		foreach ($table_info as $field_info) {
			if (preg_match('/l[\d]+_.*/', $field_info['Field'])) {
				// don't put multilingual fields in config
				continue;
			}

			$field_options = Array ();
			$grid_col_options = Array(
				'title' => 'la_col_' . $field_info['Field'],
				'filter_block' => 'grid_like_filter',
			);

			// 1. get php field type by mysql field type
			foreach ($types_hash as $php_type => $db_types) {
				if (preg_match('/'.$db_types.'/', $field_info['Type'])) {
					$field_options['type'] = $php_type;
					break;
				}
			}

			// 2. get field default value
			$default_value = $field_info['Default'];
			$not_null = $field_info['Null'] != 'YES';

			if (is_numeric($default_value)) {
				$default_value = preg_match('/[\.,]/', $default_value) ? (float)$default_value : (int)$default_value;
			}

			if ( is_null($default_value) && $not_null ) {
				$default_value = $field_options['type'] == 'string' ? '' : 0;
			}

			if ( in_array($php_type, $float_types) ) {
				// this is float number
				if (preg_match('/'.$db_types.'\([\d]+,([\d]+)\)/i', $field_info['Type'], $regs)) {
					// size is described in structure -> add formatter
					$field_options['formatter'] = 'kFormatter';
					$field_options['format'] = '%01.'.$regs[1].'f';

					if ($not_null) {
						// null fields, will most likely have NULL as default value
						$default_value = 0;
					}
				}
				elseif ($not_null) {
					// no size information, just convert to float
					// null fields, will most likely have NULL as default value
					$default_value = (float)$default_value;

				}
			}

			if (preg_match('/varchar\(([\d]+)\)/i', $field_info['Type'], $regs)) {
				$field_options['max_len'] = (int)$regs[1];
			}

			if (preg_match('/tinyint\([\d]+\)/i', $field_info['Type'])) {
				$field_options['formatter'] = 'kOptionsFormatter';
				$field_options['options'] = Array (1 => 'la_Yes', 0 => 'la_No');
				$field_options['use_phrases'] = 1;
				$grid_col_options['filter_block'] = 'grid_options_filter';
			}

			if ($not_null) {
				$field_options['not_null'] = 1;
			}

			if ($field_info['Key'] == 'PRI') {
				$default_value = 0;
				$id_field = $field_info['Field'];
			}

			if ($php_type == 'int' && !$not_null) {
				// numeric null field
				if (preg_match('/(On|Date)$/', $field_info['Field']) || $field_info['Field'] == 'Modified') {
					$field_options['formatter'] = 'kDateFormatter';
					$grid_col_options['filter_block'] = 'grid_date_rage_filter';
				}
			}

			if ($php_type == 'int' && ($not_null || is_numeric($default_value))) {
				// is integer field AND not null
				$field_options['default'] = (int)$default_value;
			}
			else {
				$field_options['default'] = $default_value;
			}

			$fields[ $field_info['Field'] ] = $field_options;
			$grids_fields[ $field_info['Field'] ] = $grid_col_options;
		}

		$grids['Default']['Fields'] = $grids_fields;

		$ret = Array (
			'IDField' => $id_field,
			'Fields' => $fields,
			'Grids' => $grids,
		);

		$decorator = new UnitConfigDecorator();
		$ret = $decorator->decorate($ret);

		$this->Application->InitParser();
		ob_start();
		echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"'));
	?>
		<script type="text/javascript">
			set_window_title('Table "<?php echo $table_name; ?>" Structure');
		</script>

		<a href="javascript:window_close();">Close Window</a><br /><br />
		<?php echo $GLOBALS['debugger']->highlightString($ret); ?>
		<br /><br /><a href="javascript:window_close();">Close Window</a><br />
	<?php
		echo $this->Application->ParseBlock(Array('name' => 'incs/footer'));
		echo ob_get_clean();
		$event->status = erSTOP;
	}

	/**
	 * Refreshes ThemeFiles & Theme tables by actual content on HDD
	 *
	 * @param kEvent $event
	 */
	function OnRebuildThemes(&$event)
	{
		if ($this->Application->GetVar('ajax') == 'yes') {
			$event->status = erSTOP;
		}

		$themes_helper =& $this->Application->recallObject('ThemesHelper');
		/* @var $themes_helper kThemesHelper */

		$themes_helper->refreshThemes();
	}

	function OnSaveColumns(&$event)
	{
		$picker_helper =& $this->Application->recallObject('ColumnPickerHelper');
		/* @var $picker_helper kColumnPickerHelper */

		$picker_helper->SetGridName($this->Application->GetLinkedVar('grid_name'));

		$picked = trim($this->Application->GetVar('picked_str'), '|');
		$hidden = trim($this->Application->GetVar('hidden_str'), '|');

		$main_prefix = $this->Application->GetVar('main_prefix');

		$picker_helper->SaveColumns($main_prefix, $picked, $hidden);
		$this->finalizePopup($event);
	}

	/**
	 * Saves various admin settings via ajax
	 *
	 * @param kEvent $event
	 */
	function OnSaveSetting(&$event)
	{
		if ($this->Application->GetVar('ajax') != 'yes') {
			return ;
		}

		$var_name = $this->Application->GetVar('var_name');
		$var_value = $this->Application->GetVar('var_value');

		$this->Application->StorePersistentVar($var_name, $var_value);

		$event->status = erSTOP;
	}

	/**
	 * Just closes popup & deletes last_template & opener_stack if popup, that is closing
	 *
	 * @param kEvent $event
	 */
	function OnClosePopup(&$event)
	{
		$event->SetRedirectParam('opener', 'u');
	}

	/**
	 * Occurs right after initialization of the kernel, used mainly as hook-to event
	 *
	 * @param kEvent $event
	 */
	function OnStartup(&$event)
	{
		if ( $this->Application->isAdmin ) {
			return ;
		}

		$base_url = preg_quote($this->Application->BaseURL(), '/');
		$referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';

		if ( $referrer && !preg_match('/^' . $base_url . '/', $referrer) ) {
			$this->Application->Session->SetCookie('original_referrer', $referrer);
			$this->Application->SetVar('original_referrer', $referrer);
		}
	}

	/**
	 * Occurs right before echoing the output, in Done method of application, used mainly as hook-to event
	 *
	 * @param kEvent $event
	 */
	function OnBeforeShutdown(&$event)
	{
	}

	/**
	 * Is called after tree was build (when not from cache)
	 *
	 * @param kEvent $event
	 */
	function OnAfterBuildTree(&$event)
	{

	}

	/**
	 * Called by AJAX to perform CSV export
	 *
	 * @param kEvent $event
	 */
	function OnExportCSV(&$event)
	{
		$export_helper =& $this->Application->recallObject('CSVHelper');
		/* @var $export_helper kCSVHelper */

		$prefix_special = $this->Application->GetVar('PrefixSpecial');
		if(!$prefix_special) {
			$prefix_special = $export_helper->ExportData('prefix');
		}
		$prefix_elems = preg_split('/\.|_/', $prefix_special, 2);
		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');

		if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) {
			$event->status = erPERM_FAIL;
			return ;
		}

		$export_helper->PrefixSpecial = $prefix_special;
		$export_helper->grid = $this->Application->GetVar('grid');
		$export_helper->ExportStep();
		$event->status = erSTOP;
	}

	/**
	 * Returning created by AJAX CSV file
	 *
	 * @param kEvent $event
	 */
	function OnGetCSV(&$event)
	{
		$export_helper =& $this->Application->recallObject('CSVHelper');
		/* @var $export_helper kCSVHelper */

		$prefix_special = $export_helper->ExportData('prefix');
		$prefix_elems = preg_split('/\.|_/', $prefix_special, 2);
		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');

		if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) {
			$event->status = erPERM_FAIL;
			return ;
		}

		$export_helper->GetCSV();
	}

	/**
	 * Enter description here...
	 *
	 * @param kEvent $event
	 */
	function OnCSVImportBegin(&$event)
	{
		$prefix_special = $this->Application->GetVar('PrefixSpecial');
		$prefix_elems = preg_split('/\.|_/', $prefix_special, 2);
		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');

		if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) {
			$event->status = erPERM_FAIL;
			return ;
		}

		$object =& $event->getObject( Array('skip_autoload' => true) );
		/* @var $object kDBItem */
		$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
		$field_values = array_shift($items_info);
		$object->SetFieldsFromHash($field_values);

		$event->redirect = false;
		$result = 'required';
		if($object->GetDBField('ImportFile')) {
			$import_helper =& $this->Application->recallObject('CSVHelper');
			/* @var $import_helper kCSVHelper */
			$import_helper->PrefixSpecial = $this->Application->GetVar('PrefixSpecial');
			$import_helper->grid = $this->Application->GetVar('grid');
			$result = $import_helper->ImportStart( $object->GetField('ImportFile', 'file_paths') );
			if($result === true) {
				$event->redirect = $this->Application->GetVar('next_template');
				$event->SetRedirectParam('PrefixSpecial', $this->Application->GetVar('PrefixSpecial'));
				$event->SetRedirectParam('grid', $this->Application->GetVar('grid'));
			}
		}

		if($event->redirect === false) {
			$object->SetError('ImportFile', $result);
			$event->status = erFAIL;
		}
	}

	/**
	 * Enter description here...
	 *
	 * @param kEvent $event
	 */
	function OnCSVImportStep(&$event)
	{
		$import_helper =& $this->Application->recallObject('CSVHelper');
		/* @var $export_helper kCSVHelper */

		$prefix_special = $import_helper->ImportData('prefix');
		$prefix_elems = preg_split('/\.|_/', $prefix_special, 2);
		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
		if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) {
			$event->status = erPERM_FAIL;
			return ;
		}

		$import_helper->ImportStep();
		$event->status = erSTOP;
	}

	/**
	 * Shows unit config filename, where requested prefix is defined
	 *
	 * @param kEvent $event
	 */
	function OnCheckPrefixConfig(&$event)
	{
		$prefix = $this->Application->GetVar('config_prefix');
		$config_file = $this->Application->UnitConfigReader->prefixFiles[$prefix];

		$this->Application->InitParser();

		ob_start();
		echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"'));
		?>
		<script type="text/javascript">
			set_window_title('Unit Config of "<?php echo $prefix; ?>" prefix');
		</script>

		<a href="javascript:window_close();">Close Window</a><br /><br />
		<strong>Prefix:</strong> <?php echo $prefix; ?><br />
		<strong>Unit Config:</strong> <?php echo $GLOBALS['debugger']->highlightString($config_file); ?><br />
		<br /><a href="javascript:window_close();">Close Window</a><br />

		<?php
		echo $this->Application->ParseBlock(Array('name' => 'incs/footer'));
		echo ob_get_clean();

		$event->status = erSTOP;
	}

	function OnDropTempTablesByWID(&$event)
	{
		$sid = $this->Application->GetSID();
		$wid = $this->Application->GetVar('m_wid');
		$tables = $this->Conn->GetCol('SHOW TABLES');
		$mask_edit_table = '/'.TABLE_PREFIX.'ses_'.$sid.'_'.$wid.'_edit_(.*)$/';
		foreach($tables as $table)
		{
			if( preg_match($mask_edit_table,$table,$rets) )
			{
				$this->Conn->Query('DROP TABLE IF EXISTS '.$table);
			}
		}
		echo 'OK';
		$event->status = erSTOP;
		return ;
	}


	/**
	 * Backup all data
	 *
	 * @param kEvent $event
	 */
	function OnBackup(&$event)
	{
		$backup_path = $this->Application->ConfigValue('Backup_Path');

		$file_helper =& $this->Application->recallObject('FileHelper');
		/* @var $file_helper FileHelper */

		if (!$file_helper->CheckFolder($backup_path) || !is_writable($backup_path)) {
			$event->status = erFAIL;

			$this->Application->SetVar('error_msg', $this->Application->Phrase('la_Text_backup_access'));
			return ;
		}

		$a_tables = $this->Conn->GetCol('SHOW TABLES'); //  array_keys($tables);
		$TableNames = Array();
		for($x=0;$x<count($a_tables);$x++)
		{
			if(substr($a_tables[$x],0,strlen(TABLE_PREFIX))==TABLE_PREFIX)
		  	{
		  	  	if (!strstr($a_tables[$x], 'ses_')) {
		  			$TableNames[] = $a_tables[$x];
		  	  	}
		  	}
		}

		$backupProgress = Array (
			'table_num' => 0,
			'table_names' => $TableNames,
			'table_count' => count($TableNames),
			'record_count' => 0,
			'file_name' => $backup_path."/dump".adodb_mktime().".txt",
		);
		$this->Application->RemoveVar('adm.backupcomplete_filename');
		$this->Application->RemoveVar('adm.backupcomplete_filesize');

		$out = array();

		for($x=0;$x<count($TableNames);$x++) {
			if (!strstr($TableNames[$x], 'ses_')) {
				$out[] = $this->GetTableCreate($TableNames[$x]);
			}
		}

		$fp = fopen($backupProgress['file_name'], 'a');

		$sql = "SELECT Name, Version FROM ".TABLE_PREFIX."Modules";
		$r = $this->Conn->Query($sql);
		foreach ($r AS $a_module) {
			$version = $a_module['Version'];
			fwrite($fp, "# ".$a_module['Name']." Version: $version;\n");
		}
		fwrite($fp, "#------------------------------------------\n\n");

		fwrite($fp,implode("\n",$out));
		fwrite($fp,"\n");

		fclose($fp);

		$this->Application->StoreVar('adm.backup_status', serialize($backupProgress));
		$event->redirect = 'tools/backup2';
	}

	/**
	 * Perform next backup step
	 *
	 * @param kEvent $event
	 */
	function OnBackupProgress(&$event)
	{
		$done_percent = $this->performBackup();

		if ($done_percent == 100) {
			$event->redirect = 'tools/backup3';
			return ;
		}

		echo $done_percent;
		$event->status = erSTOP;
	}

	/**
	 * Stops Backup & redirect to Backup template
	 *
	 * @param kEvent $event
	 */
	function OnBackupCancel(&$event)
	{
		$event->redirect = 'tools/backup1';
	}

	/**
	 * Stops Restore & redirect to Restore template
	 *
	 * @param kEvent $event
	 */
	function OnRestoreCancel(&$event)
	{
		$event->redirect = 'tools/restore1';
	}

	function performBackup()
	{
		$backupProgress = unserialize($this->Application->RecallVar('adm.backup_status'));
//			echo "<pre>"; print_r($backupProgress); echo "</pre>";
//			exit;
		$CurrentTable = $backupProgress['table_names'][$backupProgress['table_num']];

		// get records
		$a_records = $this->insert_data($CurrentTable,$backupProgress['record_count'],50,"");
//			echo "<pre>"; print_r($a_records); echo "</pre>";
//			exit;

		if ($a_records['num'] < 50) {
			$backupProgress['table_num']++;
//			if ($backupProgress['table_names'][$backupProgress['table_num']] == TABLE_PREFIX.'Cache') {
//				$backupProgress['table_num']++;
//			}
			$backupProgress['record_count'] = 0;
		} else {
			$backupProgress['record_count']+=50;
		}

		if ($a_records['sql']) {
			$fp = fopen($backupProgress['file_name'], 'a');
			fwrite($fp, $a_records['sql']);
			fclose($fp);
		}
		$percent = ($backupProgress['table_num'] / $backupProgress['table_count']) * 100;
		if  ($percent >= 100) {
			$percent = 100;
			$this->Application->StoreVar('adm.backupcomplete_filename', $backupProgress['file_name']);
			$this->Application->StoreVar('adm.backupcomplete_filesize', round(filesize($backupProgress['file_name'])/1024/1024, 2)); // Mbytes
		} else {
			$this->Application->StoreVar('adm.backup_status', serialize($backupProgress));
		}

		return round($percent);

	}

	//extracts the rows of data from tables using limits
	function insert_data($table, $start, $limit, $mywhere)
	{
//			global $out;

	    if ($mywhere !="")
	    {
	        $whereclause= " WHERE ".$mywhere." ";
	    }
	    else
	    {
	        $whereclause = "";
	    }

	    $a_data = $this->Conn->Query("SELECT * from $table $whereclause LIMIT $start, $limit");
//			echo "SELECT * from $table $whereclause LIMIT $start, $limit";
//			echo "<pre>"; print_r($a_records); echo "</pre>";
//			exit;
	    if (!$a_data) {
	    	return Array(
	    		'num' => 0,
	    		'sql' => '',
	    	);
	    }
//			$prefix = GetTablePrefix();
	    $rowcount = 0;
	    $a_fields = array_keys($a_data[0]);
	    $fields_sql = '';
	    foreach ($a_fields AS $field_name) {
			$fields_sql .= '`'.$field_name.'`,';
	    }
		$fields_sql = substr($fields_sql, 0, -1);
		$temp = '';
	    foreach ($a_data AS $a_row)
	    {
			$values_sql = '';
			foreach ($a_row as $field_name => $field_value) {
				$values_sql .= $this->Conn->qstr($field_value).',';
			}
			$values_sql = substr($values_sql, 0, -1);
			$sql = 'INSERT INTO '.$table.' ('.$fields_sql.') VALUES ('.$values_sql.');';
			$sql = str_replace("\n", "\\n", $sql);
			$sql = str_replace("\r", "\\r", $sql);
			$temp .= $sql."\n";
	    }

		if(strlen(TABLE_PREFIX))
		{
			$temp = str_replace("INSERT INTO ".TABLE_PREFIX, "INSERT INTO ", $temp);
		}

    	return Array(
    		'num' => count($a_data),
    		'sql' => $temp,
    	);
	}



	function GetTableCreate($table, $crlf="\n")
	{
	    $schema_create = 'DROP TABLE IF EXISTS ' . $table . ';' . $crlf;
		$schema_create .="# --------------------------------------------------------".$crlf;
	    $this->Conn->Query("SET SQL_QUOTE_SHOW_CREATE = 0");
	    $tmpres = $this->Conn->Query("SHOW CREATE TABLE $table");
//			echo "<pre>"; print_r($tmpres); echo "</pre>";
//			exit;
	    if(is_array($tmpres) && isset($tmpres[0]))
	    {
		    $tmpres = $tmpres[0];
	        $pos           = strpos($tmpres["Create Table"], ' (');
	        $pos2          = strpos($tmpres["Create Table"], '(');
	        if ($pos2 != $pos + 1)
	        {
	            $pos = $pos2;
	            $tmpres["Create Table"] = str_replace(",", ",\n     ", $tmpres["Create Table"]);
	        }

	        $tmpres["Create Table"]     = substr($tmpres["Create Table"], 0, 13)
	                       . (($use_backquotes) ? $tmpres["Table"] : $tmpres["Table"])
	                       . substr($tmpres["Create Table"], $pos);
	        $tmpres["Create Table"]     = str_replace("\n", $crlf, $tmpres["Create Table"]);
	        if (preg_match_all('((,\r?\n[\s]*(CONSTRAINT|FOREIGN[\s]*KEY)[^\r\n,]+)+)', $tmpres["Create Table"], $regs)) {
	            if (!isset($sql_constraints)) {
	                if (isset($GLOBALS['no_constraints_comments'])) {
	                    $sql_constraints = '';
	                } else {
	                    $sql_constraints = $crlf . '#' . $crlf
	                                        . '# ' . $GLOBALS['strConstraintsForDumped'] . $crlf
	                                        . '#' . $crlf;
	                }
	            }
	            if (!isset($GLOBALS['no_constraints_comments'])) {
	                $sql_constraints .= $crlf .'#' . $crlf .'# ' . $GLOBALS['strConstraintsForTable'] . ' ' . $table . $crlf . '#' . $crlf;
	            }
	            $sql_constraints .= 'ALTER TABLE $table $crlf '
	                             . preg_replace('/(,\r?\n|^)([\s]*)(CONSTRAINT|FOREIGN[\s]*KEY)/', '\1\2ADD \3', substr($regs[0][0], 2))
	                            . ";\n";
	            $tmpres["Create Table"]     = preg_replace('((,\r?\n[\s]*(CONSTRAINT|FOREIGN[\s]*KEY)[^\r\n,]+)+)', '', $tmpres["Create Table"]);
	        }
	        $schema_create .= $tmpres["Create Table"];
	    }
		if(strlen($schema_create))
		{
			$schema_create = str_replace("DROP TABLE IF EXISTS ".TABLE_PREFIX,"DROP TABLE ",$schema_create);
			$schema_create = str_replace("CREATE TABLE ".TABLE_PREFIX,"CREATE TABLE ",$schema_create);
			while(strlen($schema_create && substr($schema_create,-1)!=")"))
			{
				$schema_create = substr($schema_create,0,-1);
			}
		}
		$schema_create .= "\n# --------------------------------------------------------\n";
	    return $schema_create;
	}

	/**
	 * Deletes one backup file
	 *
	 * @param kEvent $event
	 */
	function OnDeleteBackup(&$event)
	{
		@unlink($this->get_backup_file());
	}

	function get_backup_file()
	{
		return $this->Application->ConfigValue('Backup_Path').'/dump'.$this->Application->GetVar('backupdate').'.txt';
	}

	/**
	 * Starts restore process
	 *
	 * @param kEvent $event
	 */
	function OnRestore(&$event)
	{
		$file = $this->get_backup_file();

		$restoreProgress = Array (
			'file_pos' => 0,
			'file_name' => $file,
			'file_size' => filesize($file),
		);
		$this->Application->RemoveVar('adm.restore_success');
		$this->Application->StoreVar('adm.restore_status', serialize($restoreProgress));
		$event->redirect = 'tools/restore3';
	}

	function OnRestoreProgress(&$event)
	{
		$done_percent = $this->performRestore();

		if ($done_percent == -3) {
			$event->redirect = 'tools/restore4';
			return ;
		}

		if ($done_percent < 0) {
			$this->Application->StoreVar('adm.restore_error', 'File read error');			$event->redirect = 'tools/restore4';
			return ;
		}

		if ($done_percent == 100) {
			$this->replaceRestoredFiles();
			$this->Application->StoreVar('adm.restore_success', 1);
			$event->redirect = 'tools/restore4';
			return ;
		}

		echo $done_percent;
		$event->status = erSTOP;

	}

	function replaceRestoredFiles()
	{
		// gather restored table names
		$tables = $this->Conn->GetCol('SHOW TABLES');
		$mask_restore_table = '/^restore'.TABLE_PREFIX.'(.*)$/';
		foreach($tables as $table)
		{
			if( preg_match($mask_restore_table,$table,$rets) )
			{
				$old_table = substr($table, 7);
				$this->Conn->Query('DROP TABLE IF EXISTS '.$old_table);
				$this->Conn->Query('CREATE TABLE '.$old_table.' LIKE '.$table);
				$this->Conn->Query('INSERT INTO '.$old_table.' SELECT * FROM '.$table);
				$this->Conn->Query('DROP TABLE '.$table);
			}
		}

	}

	function performRestore()
	{
		$restoreProgress = unserialize($this->Application->RecallVar('adm.restore_status'));
		$filename = $restoreProgress['file_name'];
		$FileOffset = $restoreProgress['file_pos'];
		$MaxLines = 200;
		$size = filesize($filename);

		if($FileOffset > $size) {
			return -2;
		}

		$fp = fopen($filename,"r");
		if(!$fp) {
			return -1;
		}


		if($FileOffset>0)
		{
			fseek($fp,$FileOffset);
		}
		else
		{
			$EndOfSQL = FALSE;
			$sql = "";
			while(!feof($fp) && !$EndOfSQL)
			{
				$l = fgets($fp);
				if(substr($l,0,11)=="INSERT INTO")
				{
				  $EndOfSQL = TRUE;
				}
				else
				{
				  $sql .= $l;
				  $FileOffset = ftell($fp) - strlen($l);
				}
			}
			if(strlen($sql))
			{
				$error = $this->runSchemaText($sql);
				if ($error != '') {

					$this->Application->StoreVar('adm.restore_error', $error);
					return -3;
				}
			}
			fseek($fp,$FileOffset);
		}

		$LinesRead = 0;
		$sql = "";
		$AllSql = array();
		while($LinesRead < $MaxLines && !feof($fp))
		{
			$sql = fgets($fp);
			if(strlen($sql))
			{
				$AllSql[] = $sql;
				$LinesRead++;
			}
		}
		if(!feof($fp))
		{
			$FileOffset = ftell($fp);
		}
		else
		{
			$FileOffset = $size;
		}
		fclose($fp);

		if(count($AllSql)>0) {
			$error = $this->runSQLText($AllSql);
			if ($error != '') {

				$this->Application->StoreVar('adm.restore_error', $error);
				return -3;
			}

		}
		$restoreProgress['file_pos'] = $FileOffset;
		$this->Application->StoreVar('adm.restore_status', serialize($restoreProgress));

		return round($FileOffset/$size * 100);
//		$this->Application->StoreVar('adm.restore_error', 'lalalal');
//		$event->redirect = 'tools/restore4';
	}


	function runSchemaText($sql)
	{
		$table_prefix = 'restore'.TABLE_PREFIX;
//		$table_prefix = TABLE_PREFIX;

		if (strlen($table_prefix) > 0) {
			$replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO ');
			foreach ($replacements as $replacement) {
				$sql = str_replace($replacement, $replacement . $table_prefix, $sql);
			}
		}

		$sql = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sql);
		$sql = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sql);

	    $commands = explode("# --------------------------------------------------------",$sql);
	    if(count($commands)>0)
	    {
//	    	$query_func = getConnectionInterface('query',$dbo_type);
//	    	$errorno_func = getConnectionInterface('errorno',$dbo_type);
//	    	$errormsg_func = getConnectionInterface('errormsg',$dbo_type);

	    	for($i = 0; $i < count($commands); $i++)
	    	{
	    		$cmd = $commands[$i];
	    		$cmd = trim($cmd);
	    		if(strlen($cmd)>0)
	    		{
	    			$this->Conn->Query($cmd);
	    			if($this->Conn->errorCode != 0)
	    			{
	    				return $this->Conn->errorMessage." COMMAND:<PRE>$cmd</PRE>";
	    			}
	    		}
	    	}
	    }
	}

	function runSQLText($allsql)
	{
		$line = 0;
//		$query_func = getConnectionInterface('query',$dbo_type);
//		$errorno_func = getConnectionInterface('errorno',$dbo_type);
//		$errormsg_func = getConnectionInterface('errormsg',$dbo_type);

		while($line<count($allsql))
		{
			$sql = $allsql[$line];
			if(strlen(trim($sql))>0 && substr($sql,0,1)!="#")
			{
				$table_prefix = 'restore'.TABLE_PREFIX;

				if (strlen($table_prefix) > 0) {
					$replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO ');
					foreach ($replacements as $replacement) {
						$sql = str_replace($replacement, $replacement . $table_prefix, $sql);
					}
				}

				$sql = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sql);
				$sql = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sql);

				$sql = trim($sql);
				if(strlen($sql)>0)
				{
					$this->Conn->Query($sql);

	    			if($this->Conn->errorCode != 0)
	    			{
	    				return $this->Conn->errorMessage." COMMAND:<PRE>$sql</PRE>";
	    			}
				}
			}
			$line++;
		}
	}


	/**
	 * Starts restore process
	 *
	 * @param kEvent $event
	 */
	function OnSqlQuery(&$event)
	{
		$sql = $this->Application->GetVar('sql');
		if ($sql) {
			$start = $this->getMoment();
			$result = $this->Conn->Query($sql);
			$this->Application->SetVar('sql_time', round($this->getMoment() - $start, 7));


			if ($result)
			{
				if (is_array($result))
				{
					$this->Application->SetVar('sql_has_rows', 1);
					$this->Application->SetVar('sql_rows', serialize($result));
				}
			}

			$check_sql = trim(strtolower($sql));
			if (
				(substr($check_sql, 0, 6) == 'insert')
				|| (substr($check_sql, 0, 6) == 'update')
				|| (substr($check_sql, 0, 7) == 'replace')
				|| (substr($check_sql, 0, 6) == 'delete')
			) {
				$this->Application->SetVar('sql_has_affected', 1);
				$this->Application->SetVar('sql_affected', $this->Conn->getAffectedRows());
			}

		}
		$this->Application->SetVar('query_status', 1);
		$event->status = erFAIL;
	}

	function getMoment()
	{
		list($usec, $sec) = explode(' ', microtime());
		return ((float)$usec + (float)$sec);
	}

	/**
	 * Occurs after unit config cache was successfully rebuilt
	 *
	 * @param kEvent $event
	 */
	function OnAfterCacheRebuild(&$event)
	{

	}

	/**
	 * Removes "Community -> Groups" section when it is not allowed
	 *
	 * @param kEvent $event
	 */
	function OnAfterConfigRead(&$event)
	{
		parent::OnAfterConfigRead($event);

		$section_ajustments = $this->Application->getUnitOption($event->Prefix, 'SectionAdjustments', Array());

		if ( !$this->Application->ConfigValue('AdvancedUserManagement') ) {
			$section_ajustments['in-portal:user_groups'] = 'remove';
		}

		if ( $this->Application->ConfigValue('UsePopups') ) { // 1 - pop-up, 2 - modal
			$section_ajustments['in-portal:main_import'] = Array('onclick' => 'direct_edit(\'adm\', this.href);');
		}

		$this->Application->setUnitOption($event->Prefix, 'SectionAdjustments', $section_ajustments);
	}

	/**
	 * Saves menu (tree) frame width
	 *
	 * @param kEvent $event
	 */
	function OnSaveMenuFrameWidth(&$event)
	{
		$event->status = erSTOP;

		if (!$this->Application->ConfigValue('ResizableFrames')) {
			return ;
		}

		$sql = 'UPDATE ' . $this->Application->getUnitOption('conf', 'TableName') . '
				SET VariableValue = ' . (int)$this->Application->GetVar('width')  . '
				WHERE VariableName = "MenuFrameWidth"';
		$this->Conn->Query($sql);

		if ($this->Conn->getAffectedRows()) {
			$this->Application->UnitConfigReader->ResetParsedData(false);
		}
	}

	/**
	 * Retrieves data from memory cache
	 *
	 * @param kEvent $event
	 */
	function OnMemoryCacheGet(&$event)
	{
		$event->status = erSTOP;

		$ret = Array ('message' => '', 'code' => 0); // 0 - ok, > 0 - error
		$key = $this->Application->GetVar('key');

		if (!$key) {
			$ret['code'] = 1;
			$ret['message'] = 'Key name missing';
		}
		else {
			$value = $this->Application->getCache($key);

			$ret['value'] =& $value;
			$ret['size'] = is_string($value) ? formatSize( strlen($value) ) : '?';
			$ret['type'] = gettype($value);

			if (IsSerialized($value)) {
				$value = unserialize($value);
			}

			if (is_array($value)) {
				$ret['value'] = print_r($value, true);
			}

			if ($ret['value'] === false) {
				$ret['code'] = 2;
				$ret['message'] = 'Key "' . $key . '" doesn\'t exist';
			}
		}

		$json_helper =& $this->Application->recallObject('JSONHelper');
		/* @var $json_helper JSONHelper */

		echo $json_helper->encode($ret);
	}

	/**
	 * Retrieves data from memory cache
	 *
	 * @param kEvent $event
	 */
	function OnMemoryCacheSet(&$event)
	{
		$event->status = erSTOP;

		$ret = Array ('message' => '', 'code' => 0); // 0 - ok, > 0 - error
		$key = $this->Application->GetVar('key');

		if (!$key) {
			$ret['code'] = 1;
			$ret['message'] = 'Key name missing';
		}
		else {
			$value = $this->Application->GetVar('value');
			$res = $this->Application->setCache($key, $value);

			$ret['result'] = $res ? 'OK' : 'FAILED';
		}

		$json_helper =& $this->Application->recallObject('JSONHelper');
		/* @var $json_helper JSONHelper */

		echo $json_helper->encode($ret);
	}

	/**
	 * Deploy changes
	 *
	 * Usage: "php tools/run_event.php adm:OnDeploy b674006f3edb1d9cd4d838c150b0567d"
	 *
	 * @param kEvent $event
	 */
	function OnDeploy(&$event)
	{
		if ( isset($GLOBALS['argv']) ) {
			// command line invocation -> don't perform redirect
			$event->status = erSTOP;
		}

		$deployment_helper =& $this->Application->recallObject('DeploymentHelper');
		/* @var $deployment_helper DeploymentHelper */

		$deployment_helper->deployAll();
	}

	/**
	 * Synchronizes database revisions from "project_upgrades.sql" file
	 *
	 * @param kEvent $event
	 */
	function OnSynchronizeDBRevisions(&$event)
	{
		$deployment_helper =& $this->Application->recallObject('DeploymentHelper');
		/* @var $deployment_helper DeploymentHelper */

		if ( !$deployment_helper->deployAll(true) ) {
			$event->status = erFAIL;
		}
	}
}


class UnitConfigDecorator {

	var $parentPath = Array ();

	function decorate($var, $level = 0)
	{
		$ret = '';

		$deep_level = count($this->parentPath);

		if ($deep_level && ($this->parentPath[0] == 'Fields')) {
			$expand = $level < 2;
		}
		elseif ($deep_level && ($this->parentPath[0] == 'Grids')) {
			if ($deep_level == 3 && $this->parentPath[2] == 'Icons') {
				$expand = false;
			}
			else {
				$expand = $level < 4;
			}
		}
		else {
			$expand = $level == 0;
		}

		if (is_array($var)) {
			$ret .= 'Array (';
			$prepend = $expand ? "\n" . str_repeat("\t", $level + 1) : '';

			foreach ($var as $key => $value) {
				array_push($this->parentPath, $key);
				$ret .= $prepend . (is_string($key) ? "'" . $key . "'" : $key) . ' => ' . $this->decorate($value, $level + 1) . ', ';
				array_pop($this->parentPath);
			}

			$prepend = $expand ? "\n" . str_repeat("\t", $level) : '';
			$ret = rtrim($ret, ', ') . $prepend . ')';
		}
		else {
			if (is_null($var)) {
				$ret = 'NULL';
			}
			elseif (is_string($var)) {
				$ret = "'" . $var . "'";
			}
			else {
				$ret = $var;
			}
		}

		return $ret;
	}
}