<?php
/**
 *	Project:	Quicty: Quick application build environment depends on PEAR and Smarty
 *	File:		Quicty.class.php
 *
 *	@copyright	Tomoyuki Negishi and ZubaPitaTech, Inc.
 *	@author		Tomoyuki Negishi <tomoyu-n@zubapita.jp>
 *	@license	http://www.opensource.org/licenses/bsd-license.php The BSD License
 *	@package	Quicty
 *	@version	$Id:$
 */

// {{{ Quicty
/**
 *	Quicty Framework class
 *
 *	@author		Tomoyuki Negishi <tomoyu-n@zubapita.jp>
 *	@access		public
 *	@package	Quicty
 */
class Quicty extends Smarty {
	public $QuictyStatus;
	public $debug_mode = 0;
	protected $user_quicty_name = 'myQuicty';
	protected $regular_class_ext = '.class.php';
	protected $root_class_lib = 'index';
	protected $current_template;
	protected $dispatch_module;
	protected $dispatch_action;
	protected $class_name; 
	protected $module_dir;
	protected $component_dir;
	protected $form_result;
	protected $quicty_plugin_names;
	protected $pathlist = array(0=>array(name=>'top',url=>'/'));
	
	public $form_error_message;
	public $form_insert_confirm_message;
	public $form_insert_message;
	public $form_update_confirm_message;
	public $form_update_message;
	public $form_delete_message;
	public $external_error_message;
	public $__auth_obj;
	public $__pager_params;
	public $__mail_queue;
	public $__plugin;
	public $C;
	public $home_dir = '..';

	const SEND = 'SEND';
	const START = 'START';
	const RESTART = 'RESTART';
	const REDO = 'REDO';
	const INSERT_CONFIRM = 'INSERT_CONFIRM';
	const INSERT = 'INSERT';
	const UPDATE_CONFIRM = 'UPDATE_CONFIRM';
	const UPDATE = 'UPDATE';
	const DELETE_CONFIRM = 'DELETE_CONFIRM';
	const DELETE = 'DELETE';
	const BROWSE = 'BROWSE';
	const EDIT = 'EDIT';
	const TABLE_VIEW = 'TABLE_VIEW';


function __construct($home_dir='..',$control='submit') {
	$this->Smarty();
	$this->home_dir = realpath($home_dir);
	$this->template_dir = $home_dir.'/view';
	$this->compile_dir = $home_dir.'/var/compiled_view';
	$this->plugins_dir[] = $home_dir.'/lib/smarty_plugins';
	$this->cache_dir = $home_dir.'/var/smarty_cache';
	$this->cache_lifetime = 3600;
	$this->caching = 0; // smarty caching is off
	$this->config_dir = $home_dir.'/etc';
	$this->module_dir = $home_dir.'/lib/Pages';
	$this->include_module_dir = $home_dir.'/lib/includes';
	$this->component_dir = $home_dir.'/lib/Components';
	
	if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])
		AND $_SERVER['HTTP_X_FORWARDED_FOR'] != ""
		AND $_SERVER['HTTP_X_FORWARDED_FOR'] != $_SERVER['REMOTE_ADDR'])
		$HTTP_SERVER_VARS['REMOTE_ADDR'] = $REMOTE_ADDR = $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
	
	if($_SERVER['DOCUMENT_ROOT']) {
		$this->root_dir = substr($_SERVER['DOCUMENT_ROOT'],0,strrpos($_SERVER['DOCUMENT_ROOT'],'/'));
	}
	
	$this->quicty_plugin_dir = $home_dir.'/lib/Plugin';


	if(is_subclass_of($this,$this->user_quicty_name)) {
		if(is_file($home_dir.'/etc/conf/conf_values.conf')) {
			require_once $home_dir.'/etc/conf/conf_values.conf';
			$this->assign('C',$this->C);
		}

		if($this->C['language']) {
			$this->language = $this->C['language'];
		} else {
			$this->language = 'japanese';
		}

		if(is_file('Quicty/etc/resource/'.$this->language.'/system_word_values.conf')) {
			require 'Quicty/etc/resource/'.$this->language.'/system_word_values.conf';
		}
		$this->form_error_message = $this->sysW['form_error_message'];
		$this->form_insert_confirm_message = $this->sysW['form_insert_confirm_message'];
		$this->form_insert_message = $this->sysW['form_insert_message'];
		$this->form_update_confirm_message = $this->sysW['form_update_confirm_message'];
		$this->form_update_message = $this->sysW['form_update_message'];
		$this->form_delete_message = $this->sysW['form_delete_message'];


		if(is_file($home_dir.'/etc/conf/word_values.conf')) {
			require $home_dir.'/etc/conf/word_values.conf';
			$this->assign('W',$this->W);
		}
	}


	$this->class_name = get_class($this);
	if(!is_subclass_of($this,$this->user_quicty_name)) {
		if($_SERVER['QtRootShift']=='Yes') {
			$tmp_script_url = explode('/',substr($_SERVER['SCRIPT_URL'],1));
			$dmy = array_shift($tmp_script_url);
			$request_path = implode('/',$tmp_script_url);
		} elseif($_SERVER['SCRIPT_URL']) {
			$request_path = substr($_SERVER['SCRIPT_URL'],1);
		} else {
			if($p = strpos($_SERVER['REQUEST_URI'],'?')) {
				$SCRIPT_URL_BASE = substr($_SERVER['REQUEST_URI'],0,$p);
			} else {
				$SCRIPT_URL_BASE = $_SERVER['REQUEST_URI'];
			}
			$request_path = substr($SCRIPT_URL_BASE,strlen(quicty_base_url().'/'));
			$this->force_post_action = $SCRIPT_URL_BASE;
		}

		if(substr($request_path,-1)=='/') $request_path = substr($request_path,0,-1);
		if(!$request_path or $request_path=='index.php') { // ex) http://qs.zubapita.jp/
			if(is_file($this->module_dir.'/'.$this->root_class_lib.$this->regular_class_ext)) {
				$this->request_type = "File / use index class";
				$this->current_template = 'index.html';
				$this->dispatch_module = $this->root_class_lib;
				$this->dispatch_action = 'index';
			} else {
				$this->request_type = "Default Root";
				$this->current_template = 'index.html';
				$this->dispatch_module = $this;
				$this->dispatch_action = 'index';
			}
		} elseif(is_dir($this->template_dir.'/'.$request_path)) { // ex) http://qs.zubapita.jp/usr/
			$this->request_type = "Dir / use index.html";
			$this->current_template = $request_path.'/index.html';
			$this->dispatch_module = $request_path;
			$this->dispatch_action = 'index';
		} elseif(is_file($this->template_dir.'/'.$request_path) or 		// ex) http://qs.zubapita.jp/usr/login.html
				is_file($this->template_dir.'/'.$request_path.'.html')) { // ex) http://qs.zubapita.jp/usr/login
			if(is_file($this->template_dir.'/'.$request_path.'.html')) $request_path = $request_path.'.html';
			$this->current_template = $request_path;
			if($slash_p = strrpos($request_path,'/')) {
				$this->request_type = "File / request file under directory";
				$this->dispatch_module = substr($request_path,0,$slash_p);
				$this->dispatch_action = substr($request_path,$slash_p+1);
				$this->dispatch_action = substr($this->dispatch_action,0,strrpos($this->dispatch_action,'.'));
			} else {
				if(is_file($this->module_dir.'/'.$this->root_class_lib.$this->regular_class_ext)) { // ex) http://qs.zubapita.jp/index.html
					$this->request_type = "File / use index class";
					$this->dispatch_module = $this->root_class_lib;
				} else {
					$this->request_type = "File / use default quicksmart class";
					$this->dispatch_module = $this;
				}
				$this->dispatch_action = substr($request_path,0,strrpos($request_path,'.'));
			}
		} else {
			if($slash_p = strrpos($request_path,'/')) {  // ex) http://qs.zubapita.jp/usr/edit/?id=xx
				$this->dispatch_module = substr($request_path,0,$slash_p);
				$this->dispatch_action = substr($request_path,$slash_p+1);
				if(is_file($this->template_dir.'/'.$this->dispatch_module.'/edit.html') 
					and in_array(basename($this->dispatch_action,'.html'),array(1=>'add','delete')) ) {
					$this->dispatch_action = basename($this->dispatch_action,'.html');
					$this->request_type = "Virtual Module A";
					$this->current_template = $this->dispatch_module.'/edit.html';
				} elseif(is_file($this->template_dir.'/'.$this->dispatch_module.'/info.html') 
					and in_array(basename($this->dispatch_action,'.html'),array(1=>'add','edit','delete')) ) {
					$this->dispatch_action = basename($this->dispatch_action,'.html');
					$this->request_type = "Virtual Module A";
					$this->current_template = $this->dispatch_module.'/info.html';
				} else {
					$this->request_type = "Virtual Module B";
					$this->current_template = $this->dispatch_module.'/index.html';
				}
			} elseif(in_array(basename($request_path,'.html'),array(1=>'add','delete'))
				  and is_file($this->template_dir.'/edit.html') ) {
				$this->request_type = "Virtual Module (root A)";
				$this->dispatch_module = $this->root_class_lib;
				$this->dispatch_action = basename($request_path,'.html');
				$this->current_template = 'edit.html';
			} elseif(in_array(basename($request_path,'.html'),array(1=>'add','edit','delete'))
				  and is_file($this->template_dir.'/info.html') ) {
				$this->request_type = "Virtual Module (root A)";
				$this->dispatch_module = $this->root_class_lib;
				$this->dispatch_action = basename($request_path,'.html');
				$this->current_template = 'info.html';
			//} elseif($request_path=='index' or $request_path=='info' or $request_path=='browse'
			//	   or $request_path=='add' or $request_path=='edit' or $request_path=='delete') {
			} elseif(in_array(basename($request_path,'.html'),array(1=>'index','info','browse','add','edit','delete'))) {
				$this->request_type = "Virtual Module (root B)";
				$this->dispatch_module = $this->root_class_lib;
				//$this->dispatch_action = $request_path;
				$this->dispatch_action = basename($request_path,'.html');
				$this->current_template = 'index.html';
			} else {
				$this->request_type = "Default Root because file not found"; // For Debug
				$this->current_template = 'index.html';
				$this->dispatch_module = $this->root_class_lib;
				$this->dispatch_action = $request_path;
			}
		}
		$this->request_path=$request_path;
		$this->QuictyStatus = $this->decide_status($control);
	} // end of if($this->class_name=='Quicty')
	$this->log = new QtLog($this->class_name,$display=true);
} // end of  __construct

function __destruct() {
	// nothing to do;
}

/**
 *	Decides Quicty status of current request from current action and POST 'control'.
 *
 * Quicty status variations are:
 *
 *	'START'	:	Display input form for entry new data.
 *	'INSERT_CONFIRM'	:	Confirm contents before insert data into table.
 *	'INSERT'	:	Insert data into table.
 *	'BROWSE'	:	Display contents of row on table.
 *	'EDIT'	:	Display input form for update existing data of row on table.
 *	'UPDATE_CONFIRM'	:	Confirm contents before update row(s) on table.
 *	'UPDATE'	:	Update row(s) on table.
 *	'DELTE_CONFIRM'	: Confirm contents before delete row(s) on table.
 *	'DELETE'	:	Delete row(s) on table.
 *	'SEND'	:	simple send data.
 *
 *	@access	protected
 *	@param	string:name of 'control'
 *	@return	status:string
 */
protected function decide_status($control) {
	if(!isset($_POST[$control]) or isset($_POST[$control]['SEND'])) {
		switch($this->dispatch_action) {
			case 'browse':
			case 'info':
				$status = self::BROWSE;
				break;
			case 'edit':
			case 'batch_update':
				$status = self::EDIT;
				break;
			case 'delete':
				$status = self::DELETE_CONFIRM;
				break;
			default:
				$status = self::START;
		}
	} elseif(isset($_POST[$control]['RESTART'])) {
		$status = self::START;
	} elseif(isset($_POST[$control]['REDO'])) {
		$status = self::START;
	} elseif(isset($_POST[$control]['DELETE_CONFIRM'])) {
		$status = self::DELETE_CONFIRM;
	} elseif(isset($_POST[$control]['DELETE'])) {
		$status = self::DELETE;
	} elseif(isset($_POST[$control]['INSERT_CONFIRM'])) {
		$status = self::INSERT_CONFIRM;
	} elseif(isset($_POST[$control]['INSERT'])) {
		$status = self::INSERT;
	} elseif(isset($_POST[$control]['UPDATE_CONFIRM'])) {
		$status = self::UPDATE_CONFIRM;
	} elseif(isset($_POST[$control]['UPDATE'])) {
		$status = self::UPDATE;
	} else {
		// When you write '<input type="submit" name="submit[XXXX]" value="Send" />',
		// The value 'XXXX' insert to PHP Instance var '$this->QuictyStatus' 
		// and Smarty var '{$q.status}'.
		list($key,$val) = each($_POST[$control]);
		$status = $key;
	}
	$this->assign('Quicty_status',$status);
	return $status;
}

/**
 *	Create instance of current dispatch module. And dispatch action method of instance.
 *
 *	Quicty's action method name must be start 'dispatch_' and action name.
 *
 *	If current template is 'index.html', action name is 'index' and dispatcher will
 *	dispatch 'dispatch_index'.
 *
 *	dispatcher disptach action 3 times in 1 request.there are 
 *	1) 'pre_dispatch_' + action name.
 *	2) 'dispatch_' + action name.
 *	3) 'post_dispatch_' + action name.
 *
 *	In case of action name is 'hoge' (because tempate is 'hoge.html'),
 *	dispatcher try dispatch 'pre_dispatch_hoge','dispatch_hoge','post_dispatch_hoge'.
 *	But if you don't need make 'pre_dispatch_hoge' and 'post_dispatch_hoge',
 *	You don't have to make them.dispatcher will try disptach 'pre_dispatch_index' and
 *	'post_dispatch_index' of current dispatch module.
 *	If you don't make them too,disptacher dispatch method of base Quicty class(they are empty).
 *
 *	Also if you don't make 'dispatch_hoge' method,dispatcher will try dispatch 'dispatch_index'.
 * It is empty too.
 *
 *	@access	protected
 *	@param	string $prefix:prefix of dispatch action name.
 *	@param	boolean $main_action:flag of main action or pre/post action.default is true.
 *	@return	string return of dispacthed action.
 */
public function dispatcher($prefix='dispatch_',$main_action=true) {
	if(!$this->current_template or !file_exists($this->template_dir.'/'.$this->current_template)) {
		$this->dispatch_error_404();
	}
	if($main_action)
		$this->pre_dispatch_result = $this->dispatcher('pre_dispatch_',false);
	$dispatch_action = $prefix.$this->dispatch_action;
	if(!$this->dispatch_obj) {
		if(is_file($this->module_dir.'/'.$this->dispatch_module.$this->regular_class_ext)) {
			require_once $this->module_dir.'/'.$this->dispatch_module.$this->regular_class_ext;
			if($p=strrpos($this->dispatch_module,'/')) {
				$dispatch_module = substr($this->dispatch_module,$p+1);
			} else {
				$dispatch_module = $this->dispatch_module;
			}
			if(class_exists($dispatch_module)) {
				$this->dispatch_obj = new $dispatch_module;
				$this->dispatch_obj->force_post_action = $this->force_post_action;
				$this->dispatch_obj->request_type = $this->request_type;
				$this->dispatch_obj->current_template = $this->current_template;
				$this->dispatch_obj->dispatch_module = $this->dispatch_module;
				$this->dispatch_obj->dispatch_action = $this->dispatch_action;
				$this->dispatch_obj->request_path = $this->request_path;
				$this->dispatch_obj->QuictyStatus = $this->QuictyStatus;
			} else {
				$message = "'".$this->module_dir.'/'.$this->dispatch_module.$this->regular_class_ext."' is loaded. But '".$dispatch_module."' class is not exists.";
				$this->log->err("$message.  source file:".__FILE__.' line:'.__LINE__);
				exit;
			}
		} else {
			$this->dispatch_module = ' - ';
			$this->dispatch_obj = $this;
		}
		//$this->dispatch_obj->assign('quicty_object',$this);
		$this->Quicty['object'] = $this;
		$this->Quicty['template'] = $this->current_template;
		$this->Quicty['module'] = $this->dispatch_module;
		$this->Quicty['action'] = $this->dispatch_action;
		$this->Quicty['request_path'] = $this->request_path;
		$this->Quicty['status'] = $this->QuictyStatus;
		if($this->dispatch_obj!=$this) $this->dispatch_obj->Quicty = $this->Quicty;
		$this->dispatch_obj->assign('quicty',$this->Quicty);
	}

	if(is_callable(array($this->dispatch_obj,$dispatch_action))) {
		if($this->debug_mode and function_exists(apd_set_pprof_trace)) {
			apd_set_pprof_trace();
		}
		$result = call_user_func(array($this->dispatch_obj,$dispatch_action));
	} else {
		//$this->dispatch_error_404();
		$result = call_user_func(array($this->dispatch_obj,$prefix.'index'));
	}
	if($main_action) {
		//if($this->debug_mode) $this->dispatch_obj->debug_monitor();
		$this->post__dispatch_result = $this->dispatcher('post_dispatch_',false);
	}
	return $result;
} // end of dispatcher

/**
 *	pre processing default action method.
 *
 *	If you want make common pre processing logic,
 *	you have to override this method instead of class constructer.
 *
 *	@access	public
 *	@return	boolean
 */
public function pre_dispatch_index() {
	return true; // nothing to do;
}

/**
 *	default action method.
 *
 * 	Usually, you have to override this method to processing default request.
 *
 *	@access	public
 *	@return	boolean
 */
public function dispatch_index() {
	$this->assign_pathlist();
	$result = $this->display($this->current_template);
	return $result;
}

/**
 *	post processing default action method.
 *
 *	If you want make common post processing logic,
 *	you have to override this method instead of class destructer.
 *
 *	@access	public
 *	@return	boolean
 */
public function post_dispatch_index() {
	return true; // nothing to do;
}

/**
 *	404 page not found action
 *
 *	@access	public
 *	@return	boolean
 */
public function dispatch_error_404() {
	$this->assign('b',quicty_base_url());
	$this->assign('g',$_GET);
	$this->assign('p',$_POST);
	$this->assign('q',$this->Quicty);
	$this->assign('r',$_REQUEST);
	$this->assign('s',$_SERVER);

	header("HTTP/1.0 404 Not Found");
	$result = $this->display('errors/404.html');
}

/**
 *	Quicty satatus accesor method.
 *
 *	@access	public
 *	@param	string $status it must be START | INSERT_CONFIRM | INSERT | BROWSE | EDIT | UPDATE_CONFIRM | UPDATE | DELETE_CONFIRM | DELETE | SEND.
 *	@return	string Quicty satatus.
 */
public function set_qt_status($status) {
	switch($status) {
		case 'START':
		case 'INSERT_CONFIRM':
		case 'INSERT':
		case 'BROWSE':
		case 'EDIT':
		case 'UPDATE_CONFIRM':
		case 'UPDATE':
		case 'DELETE_CONFIRM':
		case 'DELETE':
		case 'SEND':
			$this->QuictyStatus = $status;
			break;
		default:
			$this->log->warning("set_qt_status:$status is invalid.  source file:".__FILE__.' line:'.__LINE__);
	}
	return $this->QuictyStatus;
}
// old version for conpatible
public function set_qs_status($status) { 
	return $this->set_qt_status($status);
}

public function get_qt_status() {
	return $this->QuictyStatus;
}

/**
 *	DI Component experimental implementation.
 *
 */

/**
 *	Create new Component instance.
 *
 *	@access	public
 *	@param	string $file_path kind and name of plugin. such as 'sidebar/calendar'.
 *	@return	object plugin instance object.
 */
public function new_component($file_path) {
	require_once $this->component_dir.'/'.$file_path.$this->regular_class_ext;
	$file = pathinfo($file_path);
	$class_name = $file['basename'];
	$component = new $class_name($this,$params);
	return $component;
}


/**
 *	Create Quicty::DataView object.
 *	DataView object can create, validate and process HTML forms with easy settings.
 *	This method use Quicty::DataView class that override PEAR::HTML_QuickForm.
 *
 *	@access	public
 *	@param	string $formName Form's name.
 *	@param	array $params From's option
 *		string $params['method'] (optional)Form's method defaults to 'POST'
 *		string $params['action (optional)Form's action
 *		string $params['target (optional)Form's target defaults to '_self'
 *		array $params['attributes (optional)Extra attributes for <form> tag
 *	@return	object DataView class object that override PEAR::HTML_QuickForm
 */
public function new_form_obj($name='',$params=array()) {
	if(!$name) {
		$name = 'form'.$this->quick_form_no;
		$this->quick_form_no++;
	}
	$method = $params['method'] ? $params['method'] : 'post';
	$action = $params['action'] ? $params['action'] : '';
	if(!$action and $this->force_post_action) $action = $this->force_post_action;
	$target = $params['target'] ? $params['target'] : '_self';
	$attributes = $params['attributes'] ? $params['attributes'] : null;
	$trackSubmit = $params['trackSubmit'] ? $params['trackSubmit'] : false;
	$form = new DataView($name,$method,$action,$target,$attribute,$trackSubmit);
	$form->__page_obj = $this;
	return $form;
}

/**
 *	Set DataView / DataSet form msessage.
 *
 *
 *	@access	public
 *	@param	string $message string of message to display with form.
 *	@param	string $status string of Quicty status. default is 'auto'.It's mean current status.
 *	@return	void.
 */
public function set_form_message($message,$status='auto') {
	if($status=='auto')
		$status = $this->QuictyStatus;
	switch($status) {
		case 'INSERT_CONFIRM':
			$this->form_insert_confirm_message = $message;
			break;
		case 'INSERT':
			$this->form_insert_message = $message;
			break;
		case 'UPDATE_CONFIRM':
			$this->form_update_confirm_message = $message;
			break;
		case 'UPDATE':
			$this->form_update_message = $message;
			break;
		case 'DELETE':
			$this->form_delete_message = $message;
			break;
	}
}

/**
 *	Bind form values to array for display Smarty template.
 *
 *	@access	public
 *	@param	object $form DataView / DataSet form object.
 *	@return	array form values mapped array for Smarty.
 */
public function get_form_result($form) {
	$renderer = new HTML_QuickForm_Renderer_ArraySmarty($this);
	$form->accept($renderer);
	return $renderer->toArray();
}

/**
 *	get value from table or array.
 *
 *	@access	public
 *	@param	string $key key of array.
 *	@param 	array $data_set_array parameters of table or array spec.
 *	@return	string value from table or array.
 */
function get_value_from_table($key,$data_set_array) {
	if($data_set = $data_set_array['_TABLE']) {
		$field = $data_set_array['_GET_COLUMN'];
		$form = $this->new_data_set($data_set);
		if($this->reference_cache[$data_set][$key][$field]) {
			$result = $this->reference_cache[$data_set][$key][$field];
		} else {
			if(!is_numeric($key)) $key = "'$key'";
			$where = 'where '.$data_set_array['_WHERE_COLUMN'].'='.$key;
			$table = $form->select_table($where,$field);
			$result = $table[0][$field];
			$this->reference_cache[$data_set][$key][$field] = $result;
		}
	} elseif($data_set = $data_set_array['_THIS_ARRAY']) {
		$array = $this->$data_set;
		if($key_2nd = $data_set_array['_2ND_COLUMN']) {
			if($key_3rd = $data_set_array['_3RD_COLUMN']) {
				$result = $array[$key][$key_2nd][$key_3rd];
			} else {
				$result = $array[$key][$key_2nd];
			}
		} else {
			$result = $array[$key];
		}
	}
	return $result;
}

/**
 *	Assign DataView / DataSet values to Smarty template value.
 *
 *	@access	public
 *	@param	string $name name of smarty tempalte value.
 *	@param	object $form instance of DataView / DataSet form object.
 *	@return	array values of form value.
 */
public function assign_form($name,$form) {
	if(!$form) {
		echo $error['information'] = "Quicty error - Quicty::assign_form - There is no form object. for '$name'.\n";
		$this->assign($name,$error);
		return false;
	}
	$renderer = new HTML_QuickForm_Renderer_ArraySmarty($this);
	$form->accept($renderer);
	$this->form_result = $renderer->toArray();
	if(is_array($form->data_view) and count($form->data_view)>0) {
		foreach($form->data_view as $column=>$attributes) {
			if($attributes['type']=='hidden') {
				$this->form_result['hiddens'][$column]['value'] = $form->form_element[$column]->_attributes['value'];
			}
			if($attributes['reference']) {
				$this->form_result[$column]['html'] = 
					$this->get_value_from_table($this->form_result[$column]['value'],$attributes['reference']);
			}
		}
	}
	if(count($this->form_result['errors'])) {
		$this->form_result['information'] = $this->form_error_message;
	} elseif($this->external_error_message) {
		$this->form_result['information'] = $this->external_error_message;
	} else {
		switch($this->QuictyStatus) {
			case 'INSERT_CONFIRM':
				$this->form_result['information'] = $this->form_insert_confirm_message;
				break;
			case 'INSERT':
				$this->form_result['information'] = $this->form_insert_message;
				break;
			case 'UPDATE_CONFIRM':
				$this->form_result['information'] = $this->form_update_confirm_message;
				break;
			case 'UPDATE':
				$this->form_result['information'] = $this->form_update_message;
				break;
			case 'DELETE':
				$this->form_result['information'] = $this->form_delete_message;
				break;
		}
	}
	$this->form_result['qt_status'] = $this->form_result['qs_status'] = $this->QuictyStatus;
	$this->assign($name,$this->form_result);
	return $this->form_result;
}


public function assign_batch_form($name,$table_data,$max_column=5,$form_option=array()) {
	$update_form['max_column'] = $max_column;
	$column_no = 1;
	foreach($table_data['values'] as $row_no=>$row) {
		if(!$update_form['submit']) {
			$update_form['attributes'] = $row['attributes'];
			$update_form['submit'] = $row['submit'];
		}
		foreach($row as $column=>$attributes) {
			switch($column) {
				case 'frozen':
				case 'javascript':
				case 'attributes':
				case 'requirednote':
				case 'requirednote':
				case 'submit':
					continue 2;
					break;
				case 'errors':
					$update_form[$row_no]['errors'] = $attributes;
					break;
				case 'hidden':
					$update_form[$row_no]['hidden'] = $attributes;
					break;
				default:
					$table_view[$row_no][$column] = $attributes;
					if($row_no==0) $column_no++;
			}
		}
	
	}
	switch($this->QuictyStatus) {
		case 'INSERT_CONFIRM':
			$update_form['information'] = $this->form_insert_confirm_message;
			break;
		case 'INSERT':
			$update_form['information'] = $this->form_insert_message;
			break;
		case 'UPDATE_CONFIRM':
			$update_form['information'] = $this->form_update_confirm_message;
			break;
		case 'UPDATE':
			$update_form['information'] = $this->form_update_message;
			break;
		case 'DELETE':
			$update_form['information'] = $this->form_delete_message;
			break;
	}
	$update_form['qt_status'] = $update_form['qs_status'] = $this->QuictyStatus;
	$update_form['column_no'] = $column_no;
	if($_REQUEST['pageID']) $update_form['pageID'] = addslashes($_REQUEST['pageID']);
	
	$update_form = array_merge($update_form,$form_option);
	$this->assign('update_form',$update_form);
	$this->assign($name,$table_view);
	$this->form_result = $form;
	return $this->form_result;
}



/**
 *	Set DataView / DataSet form's error message.
 *
 *	@access	public
 *	@param	string $message message to display with form.
 *	@return	void.
 */
public function set_form_error($message) {
	$this->external_error_message = $message;
}

/**
 *	Create Quicty::DataSet object.
 *	DataSet object can create, validate and process HTML forms with easy settings.
 *	And bind SQL Database controller methods.
 *
 *	This method use Quicty::DataSet class that override Quicty::DataView.
 *	And Quicty::DataView class override PEAR::HTML_QuickForm.
 *
 *	@access	public
 *	@param	string $formName Form's name.
 *	@param	array $params From's option
 *		string $params['method'] (optional)Form's method defaults to 'POST'
 *		string $params['action (optional)Form's action
 *		string $params['target (optional)Form's target defaults to '_self'
 *		array $params['attributes (optional)Extra attributes for <form> tag
 *	@return	object DataSet class object that override PEAR::HTML_QuickForm
 */
public function new_data_obj($name='',$params=array()) {
	if(!$name) {
		$name = 'form'.$this->quick_form_no;
		$this->quick_form_no++;
	}
	$method = $params['method'] ? $params['method'] : 'post';
	$action = $params['action'] ? $params['action'] : '';
	if(!$action and $this->force_post_action) $action = $this->force_post_action;
	$target = $params['target'] ? $params['target'] : '_self';
	$attributes = $params['attributes'] ? $params['attributes'] : null;
	$trackSubmit = $params['trackSubmit'] ? $params['trackSubmit'] : false;
	$form = new DataSet($name,$method,$action,$target,$attributes,$trackSubmit);
	$form->__page_obj = $this;
	$form->memcached = $this->memcached;
	$form->memcache_compress = $this->memcache_compress;
	$form->memcache_expire = $this->memcache_expire;
	return $form;
}

/**
 *	Get row data from table.
 *
 *	@access	public
 *	@param	string $data_set_name name of data_view and data_set file.
 *	@param	string $condition sql condition phrase. such as 'where column=hoge' or 'order by id',etc.
 *	@return	array contents of row data.
 */
public function get_row_from_table($data_set_name,$condition) {
	if($data_set_name and $condition) {
		$form = $this->new_data_set($data_set_name);
		$result = $form->select_table($condition);
		if(count($result)>0) $row = $result;
	}
	return $row;
}


/**
 *	Quick build DataSet object and bind data_view and data_set
 *
 *	@access	public
 *	@param	array $params From's option
 *		string $params['method'] (optional)Form's method defaults to 'POST'
 *		string $params['action (optional)Form's action
 *		string $params['target (optional)Form's target defaults to '_self'
 *		string $params['attributes (optional)Extra attributes for <form> tag
 *	@param	string $set_name DataSet form's name and data_view file's name and data_set file's name.
 *	@return	object DataSet object.
 */
public function new_data_set($set_name,$params=array(),$cache=true) {
	//if($cache and $this->memcached) {
	//	$DATA_SET_CACHE = $this->memcache->get('DATA_SET_'.$set_name);
	//} elseif($cache and function_exists(apc_fetch)) {
	//	$DATA_SET_CACHE = unserialize(apc_fetch('DATA_SET_'.$set_name));
	//}
	if(is_object($DATA_SET_CACHE)) {
		$data_set = $DATA_SET_CACHE;
		$this->memcache_use .= $set_name." : get from cache.\n";
	} else {
		$data_set = $this->new_data_obj($set_name,$params);
		$data_set->bind_data_view($set_name.'.view.conf');
		$data_set->bind_data_set($set_name.'.set.conf');	
		$this->memcache_use .= $set_name." : generated.\n";
		//if($cache and $this->memcached) {
		//	$result = $this->memcache->set('DATA_SET_'.$set_name,$data_set,
		//			$this->memcache_compress,$this->memcache_expire);
		//} elseif($cache and function_exists(apc_store)) {
		//	$result = apc_store('DATA_SET_'.$set_name,serialize($data_set));
		//}
	}

	return $data_set;
}

public function select_all($set_name,$condition='') {
	$data_set = $this->new_data_set($set_name);
	$data = $data_set->select_table($condition);
	return $data;
}


public function select_row($set_name,$condition='') {
	$data_set = $this->new_data_set($set_name);
	$data = $data_set->select_table($condition);
	return $data[0];
}

/**
 *	Quick build DataView object and bind data_view for Search form.
 *
 *	@access	public
 *	@param	array $params From's option
 *		string $params['method'] (optional)Form's method defaults to 'POST'
 *		string $params['action (optional)Form's action
 *		string $params['target (optional)Form's target defaults to '_self'
 *		string $params['attributes (optional)Extra attributes for <form> tag
 *	@param	string $set_name DataSet form's name and data_view file's name and data_set file's name.
 *	@return	object DataSet object.
 */
public function new_search_obj($set_name,$params=array(),$cache=false) {
	if(!isset($_POST['keyword']) and isset($_GET['keyword'])) {
		$_POST['keyword'] = $_GET['keyword'];
	}
	if(!isset($_POST['kind']) and isset($_GET['kind'])) {
		$_POST['kind'] = $_GET['kind'];
	}
	//if($cache and $this->memcached) {
	//	$DATA_VIEW_CACHE = $this->memcache->get('DATA_VIEW_'.$set_name);
	//} elseif($cache and function_exists(apc_fetch)) {
	//	$DATA_VIEW_CACHE = unserialize(apc_fetch('DATA_VIEW_'.$set_name));
	//}
	if(is_object($DATA_VIEW_CACHE)) {
		$data_view = $DATA_VIEW_CACHE;
		$this->memcache_use .= $set_name." : get from cache.\n";
	} else {
		$data_view = $this->new_data_obj($set_name,$params);
		if(file_exists($this->home_dir.'/etc/search_view/'.$set_name.'.view.conf')) {
			$conf_file = realpath($this->home_dir.'/etc/search_view/'.$set_name.'.view.conf');
			$data_view->bind_data_view($conf_file);
		} elseif(file_exists($this->home_dir.'/etc/data_view/'.$set_name.'.view.conf')) {
			$data_view->bind_data_view($set_name.'.view.conf');
		} else {
			return false;
		}
		$original_qt_status = $this->get_qt_status();
		$this->set_qt_status('SEND');
		$data_view->set_button_label('send',$this->sysW['search']);
		$data_view->bind_button_control();
		$this->set_qt_status($original_qt_status);
		$this->memcache_use .= $set_name." : generated.\n";
		//if($cache and $this->memcached) {
		//	$result = $this->memcache->set('DATA_VIEW_'.$set_name,$data_view,
		//	$this->memcache_compress,$this->memcache_expire);
		//} elseif($cache and function_exists(apc_store)) {
		//	$result = apc_store('DATA_VIEW_'.$set_name,serialize($data_view));
		//}
	}

	return $data_view;
}


/**
 *	Create Quicty::QT_Auth object.
 *	AuthSet object can provides authentication system with easy settings.
 *
 *	This method use Quicty::QT_Auth class that override PEAR::Auth.
 *
 *	Also this method use Quicty::AuthSet class that override Quicty::DataView.
 *	And Quicty::DataView class override PEAR::HTML_QuickForm.
 *
 *	@access	public
 *	@param	string $data_set_file name of data_set file for auth.
 *	@param	string $strage_driver Type of the storage driver. Default is 'DB'.
 *	@return	object QT_Auth object.
 */
public function new_auth_obj($data_set_file,$strage_driver='DB',$session_id='QTSESSID',$expire=2592000,$idle=2592000) {
	$form = new AuthSet('auth');
	$form->__page_obj = $this;
	$auth_params = $form->bind_auth_set($data_set_file);
	$this->__auth_obj = new QT_Auth($strage_driver,$auth_params,NULL,false,$this);
	$this->__auth_obj->setSessionname($session_id);
	//$this->__auth_obj->setExpire($expire); // 30 days
	//$this->__auth_obj->setIdle($idle); // 30 days
	$this->__auth_obj->start();
	if($this->__auth_obj->getAuth()) {
		$userdata = $this->__auth_obj->getAuthData();
		$userdata['authorized'] = true;
	} else {
		$userdata['authorized'] = false;
	}
	$this->__auth_obj->__userdata = $userdata;
	return $this->__auth_obj;
}

/**
 *	Get QT_Auth object of cureent request.
 *
 *	@access	public
 *	@return	object current QT_Auth object.
 */
public function get_auth_obj() {
	return $this->__auth_obj;
}

/**
 *	Create new PEAR::Pager object.
 *
 *	@access	public
 *	@param	array PEAR::Pager parameters.
 *	@return	object PEAR::Pager object.
 */
public function new_pager_obj($params=array()) {
	if(!isset($params['totalItems'])) {
		$message = "function new_pager_obj() must need 'totalItems' parameter. ex) new_pager_obj(array('totalItems'=>10))";
		$this->log->err("$message.  source file:".__FILE__.' line:'.__LINE__);
	}
	foreach($params as $key=>$value) {
		$this->__pager_params[$key] = $value;
	}
	$this->__pager_params['perPage'] = isset($params['perPage']) ? $params['perPage'] : 20;
	$pager = Pager::factory($this->__pager_params);
	return $pager;
}

/**
 *	Create new PEAR::Mail_Queue object.
 *
 *	@access	public
 *	@param	string $data_set_file name of data_set file name for Mail_Queue.
 *	@param	string $table name of table for Mail_Queue.
 *	@param	array PEAR::Mail_Queue parameters.
 *	@return	object PEAR::Mail_Queue object.
 */
public function new_mail_queue($data_set_file,$table='mail_queue',$mail_option=array("driver"=>"mail")) {
	$form = new QueueSet('queue');
	$form->__page_obj = $this;
	$mail_queue_params = $form->bind_queue_set($data_set_file);
	$db_option=array(
		'type'=>'db',
		'dsn'=>$mail_queue_params['dsn'],
		'mail_table'=>$table
	);
	$this->__mail_queue = new QS_Mail_Queue($db_option,$mail_option);
	return $this->__mail_queue;

}

/**
 *	Create new PEAR::Mail_Mime object.
 *
 *	@access	public
 *	@param	string $from email address of sender.
 *	@param	string $to email address of receiver.
 *	@param	string $subject subject of mail.
  *	@param	string $article message body of mail.
 *	@return	object PEAR::Mail_Mime object.
 */
public function new_mail_obj($from,$to,$subject,$article) {
	$MIME_obj = new QS_Mail_mime();
	$MIME_obj ->set_data($from,$to,$subject,$article);
	return $MIME_obj;
}


/**
 *	Set Pager Option 
 *
 *	@access	public
 *	@param	string $perPage amount of display item in one page.
 *	@param	string $condition where condition prase in sql.
 *	@return	string condition for sql parameter.
 */
function set_pager_option($perPage,$condition) {
	$pager_option = array(
		'perPage'=>$perPage,
		'condition'=>$condition,
		'prevImg'=>$this->sysW['prev'].$perPage.$this->sysW['records'],
		'nextImg'=>$this->sysW['next'].$perPage.$this->sysW['records'],
		'separator'=>"&nbsp;",
		'curPageLinkClassName'=>'current_page',
		'firstPageText'=>$this->sysW['first_page'],'firstPagePre'=>'','firstPagePost'=>'',
		'lastPageText'=>$this->sysW['last_page'],'lastPagePre'=>'','lastPagePost'=>'',
	);
	if(preg_match("/.*list[0-9]*?\.html$/",basename($_SERVER['PHP_SELF'])) ) {
		$pager_option['importQuery'] = FALSE;
		$pager_option['append'] = FALSE;
		$pager_option['fileName'] = 'list%d.html';
	}
	return $pager_option;
}

/**
 *	Set directory path list (a.k.a 'bread crumbs') for Quicty::default template.
 *	pathlist parameter array must be have 'name' key and 'url' key. Such as
 *		$pathlist = array(
 *			array('name'=>'top','url'=>'/'),
 *			array('name'=>'item list','url'=>'/items/index'),
 *			array('name'=>'item detail of no.1','url'=>'/items/info?id=1')
 *			);
 *
 *	@access	public
 *	@param	array $pathlist array of each pages.
 *	@return	bolean if you give correct pathlist,return true.
 */
//public function set_pathlist($pathlist=array()) {
public function set_pathlist($url,$name,$assign=true) {
	$this->pathlist = array(0=>array('name'=>$name,'url'=>$url));
	return $this->assign('pathlist',$this->pathlist);
}

/**
 *	Add page to end of directory path list for Quicty::default template.
 *
 *	@access	public
 *	@param	string $url url or path of page.
 *	@param	string $name name of page.
 *	@param	boolean $assign assign new path list to template value 'pathlist'. Default is true.
 *	@return	boolean if you success to set path list,return true.
 */
public function add_pathlist($url,$name,$assign=true) {
	if($name) {
		$this->pathlist = array_merge(
			$this->pathlist,array(array('name'=>$name,'url'=>$url)
			));
		if($assign) {
			return $this->assign('pathlist',$this->pathlist);
		} else {
			return true;
		}
	} else {
		return false;
	}
}

/**
 *	Clear current path list.
 *
 *	@access	public
 *	@return	boolean always return true.
 */
public function clear_pathlist() {
	$this->pathlist = array();
	return true;
}

/**
 *	Set path list and assign path list to template value 'pathlist'.
 *
 *	@access	public
 *	@param	array $pathlist array of each pages.
 *	@return	bolean if you give correct pathlist,return true.
 */
public function assign_pathlist($pathlist=array()) {
	if(is_array($pathlist) and count($pathlist)>0) {
		$this->pathlist = $pathlist;
	}
	return $this->assign('pathlist',$this->pathlist);
}

/**
 *	Get all Quicty plugin name list in plugin directory.
 *	Default plugin directory is 'lib/Plugin'.
 *
 *	@access	public
 *	@param	string $sub_dir sub directory name in plugin directry.
 *	@return	array plugin name list.
 */
public function get_plugin_names($sub_dir='') {
	foreach(glob($this->quicty_plugin_dir.$sub_dir.'/*.php') as $file_path) {
		$file_name = substr($file_path,strrpos($file_path,'/')+1);
		$plugin_names[] = substr($file_name,0,strpos($file_name,'.'));
	}
	return $plugin_names;
}

/**
 *	Checks whether a Quicty plugin exists
 *
 *	@access	public
 *	@param	string $plugin_name base name of plugin file.
 *	@return	boolean return true if plugin exists.
 */
public function is_exist_plugin_file($plugin_name) {
	if(is_file($this->quicty_plugin_dir.'/'.$plugin_name.$this->regular_class_ext)) {
		return TRUE;
	} else {
		return FALSE;
	}
}

/**
 *	Create new Quicty plugin instance.
 *
 *	You must be set unique plugin id for each plugin class.
 *
 *	@access	public
 *	@param	string $file_path kind and name of plugin. such as 'sidebar/calendar'.
 *	@param	array parameters of plugin.
 *	@return	object plugin instance object.
 */
public function new_plugin($file_path,$params=array()) {
	require_once $this->quicty_plugin_dir.'/'.$file_path.$this->regular_class_ext;
	$file = pathinfo($file_path);
	$class_name = $file['basename'];
	$plugin = new $class_name($this,$params);
	return $plugin;
}

/**
 *	Create all plugin instances in specific sub directory.
 *	And return array of plugin instances list.
 *	You must be set unique plugin id for each plugin class.
 *
 *	@access	public
 *	@param	string $sub_dir sub directory of Quicty plugin directoty.
 *	@return	array plugin instances array.
 */
public function include_plugin($sub_dir='') {
	foreach(glob($this->quicty_plugin_dir.$sub_dir.'/*.php') as $file_path) {
		require_once $file_path;
		$file_name = substr($file_path,strrpos($file_path,'/')+1);
		$plugin_name = substr($file_name,0,strpos($file_name,'.'));
		if($sub_dir) {
			$plugin_class = substr($sub_dir,1).'_'.$plugin_name;
		} else {
			$plugin_class = $plugin_name;
		}
		if(!$this->__plugin[$plugin_class])
			$this->__plugin[$plugin_class] = new $plugin_class($this,$plugin_params);
		$plugin_id = $this->__plugin[$plugin_class]->get_id();
		$plugins[$plugin_id] = $plugin_name;
	}
	return $plugins;
}



public function __toString() {
	return $this->class_name;
}

/*
public function __autoload($class_name) {
	echo "class_name-$class_name<br>";
	$class_file_path = $this->include_module_dir.'/'.$class_name.$this->regular_class_ext;
	if(file_exists($class_file_path)) {
		require_once $class_file_path;
	} else {
		$tmp_name = explode('_',$class_name);
		$base_name = array_pop($tmp_name);
		$dir_name = implode('_',$class_name);
		$class_file_path = $dir_name.'/'.$base_name.$class_name.$this->regular_class_ext;
		if(file_exists($class_file_path)) {
			require_once $class_file_path;
		} elseif(file_exists($this->include_module_dir.'/'.$class_file_path)) {
			require_once $this->include_module_dir.'/'.$class_file_path;
		} else {
			$class_file_path = $dir_name.'/_base'.$class_name.$this->regular_class_ext;
		}
	}
}
*/

//----------------------------------------------------
// utilities


function set_pager() {
	$pager = $this->list->get_pager_links();
	$pager['pages_array'] = explode('&nbsp;&nbsp;',$pager['pages']);
	return $pager;
}

public function set_sort_option($sort,$order) {
	$this->sort_option = $sort;
	$this->order_option = $order;
	return $this->sort_option.' '.$this->order_option;
}

function add_join_table($table,$condition) {
	return $this->add_joins[] = "LEFT JOIN $table ON $condition";
}

function add_outer_join_table($table,$condition) {
	return $this->add_joins[] = "LEFT OUTER JOIN $table ON $condition";
}

function add_select_fields($table,$field='') {
	if(is_array($table)) {
		foreach($table as $table_name=>$field_array) {
			foreach($field_array as $key=>$field) {
				$this->add_fields[$table_name][$key] = $field;
			}
		}
	} elseif($table AND $field) {
		$this->add_fields[$table][] = $field;
	}
	return $this->add_fields;
}


//----------------------------------------------------
// debug monitors

/**
 *	debug monitor.
 *	
 *	@param  array or string(conma separeted var names) 
 *	@access	public
 *	@return	void.
 */

public function display_debug_monitor($vars) {
	$system_var = $this->set_system_var();
	$this->assign('system_var',$system_var);
	
	$debug_sql = $GLOBALS['__debug_sql__'];
	//var_check($GLOBALS);
	$this->assign('debug_sql',$debug_sql);
	//$this->assign('debug_sql',$_GLOBALS);

	if(is_array($vars)) {
		$var_names = $var;
	} elseif($vars) {
		$var_names = explode(',',$vars);
	}
	if(is_array($var_names) AND count($var_names)) {
		foreach($var_names as $var) {
			$debug_var[$var] = $this->get_template_vars($var);
		}
		$this->assign('debug_var',$debug_var);
	}
	
	echo $this->fetch('includes/common/debug_monitor.inc');
	echo "</body>\n";
	echo "</html>\n";
}

public function set_system_var() {
	$system_var['root_dir'] = $this->root_dir;
	$system_var['request_path'] = $this->request_path;
	$system_var['request_type'] = $this->request_type;
	$system_var['dispatch_module'] = $this->dispatch_module;
	$system_var['dispatch_obj'] = $this->dispatch_obj;
	$system_var['dispatch_action'] = $this->dispatch_action;
	$system_var['current_template'] = $this->current_template;
	$system_var['quicty_base_url'] = $quicty_base_url;
	$system_var['QuictyStatus'] = $this->QuictyStatus;
	return $system_var;
}

/**
 *	old debug monitor.
 *	display current status and values.
 *
 *	@access	public
 *	@return	void.
 */
public function debug_monitor() {
	$quicty_base_url = quicty_base_url();
	echo "\n".<<<END_OF_MONITOR
	<html><head>
	<meta http-equiv="Content-Style-Type" content="text/css">
	<link href="/css/main.css" rel="stylesheet" type="text/css">
	</head><body>
	<table id="container"><tr><td>
	<hr>
	<strong>[ debug monitor ]</strong><br>
	<br>
	<pre>
END_OF_MONITOR;
	echo "\n";
	echo "memcache_use=".$this->memcache_use."\n";
	echo "use_cache=".$this->use_memcache."\n";
	//echo "this->memcached=";var_dump($this->memcached);echo "\n";
	/*
	if($this->memcached) {
		$DATA_SET_CACHE = $this->memcache->get('DATA_SET');
		echo "DATA_SET_CACHE=";print_r($DATA_SET_CACHE);
	} elseif(function_exists(apc_fetch)) {
		//$DATA_SET_CACHE = apc_fetch('DATA_SET');
		echo "DATA_SET_CACHE=";print_r(apc_cache_info());
	}
	*/
	echo <<<END_OF_MONITOR
	</pre>
	<hr>
	</td></tr></table>
	</body></html>
END_OF_MONITOR;
}

} 
// end of class Quicty
// }}}
?>