diff options
-rw-r--r-- | codebase/base_connector.php | 1595 | ||||
-rw-r--r-- | codebase/crosslink_connector.php | 14 | ||||
-rw-r--r-- | codebase/data_connector.php | 902 | ||||
-rw-r--r-- | codebase/grid_connector.php | 525 | ||||
-rw-r--r-- | codebase/scheduler_connector.php | 473 | ||||
-rw-r--r-- | codebase/strategy.php | 40 |
6 files changed, 1780 insertions, 1769 deletions
diff --git a/codebase/base_connector.php b/codebase/base_connector.php index f21cfc7..2c924a9 100644 --- a/codebase/base_connector.php +++ b/codebase/base_connector.php @@ -1,782 +1,813 @@ -<?php
-/*
- @author dhtmlx.com
- @license GPL, see license.txt
-*/
-require_once("tools.php");
-require_once("db_common.php");
-require_once("dataprocessor.php");
-require_once("strategy.php");
-require_once("update.php");
-
-//enable buffering to catch and ignore any custom output before XML generation
-//because of this command, it strongly recommended to include connector's file before any other libs
-//in such case it will handle any extra output from not well formed code of other libs
-ini_set("output_buffering","On");
-ob_start();
-
-class OutputWriter{
- private $start;
- private $end;
- private $type;
-
- public function __construct($start, $end = ""){
- $this->start = $start;
- $this->end = $end;
- $this->type = "xml";
- }
- public function add($add){
- $this->start.=$add;
- }
- public function reset(){
- $this->start="";
- $this->end="";
- }
- public function set_type($add){
- $this->type=$add;
- }
- public function output($name="", $inline=true, $encoding=""){
- ob_clean();
-
- if ($this->type == "xml"){
- $header = "Content-type: text/xml";
- if ("" != $encoding)
- $header.="; charset=".$encoding;
- header($header);
- }
-
- echo $this->__toString();
- }
- public function __toString(){
- return $this->start.$this->end;
- }
-}
-
-/*! EventInterface
- Base class , for iterable collections, which are used in event
-**/
-class EventInterface{
- protected $request; ////!< DataRequestConfig instance
- public $rules=array(); //!< array of sorting rules
-
- /*! constructor
- creates a new interface based on existing request
- @param request
- DataRequestConfig object
- */
- public function __construct($request){
- $this->request = $request;
- }
-
- /*! remove all elements from collection
- */
- public function clear(){
- array_splice($rules,0);
- }
- /*! get index by name
-
- @param name
- name of field
- @return
- index of named field
- */
- public function index($name){
- $len = sizeof($this->rules);
- for ($i=0; $i < $len; $i++) {
- if ($this->rules[$i]["name"]==$name)
- return $i;
- }
- return false;
- }
-}
-/*! Wrapper for collection of sorting rules
-**/
-class SortInterface extends EventInterface{
- /*! constructor
- creates a new interface based on existing request
- @param request
- DataRequestConfig object
- */
- public function __construct($request){
- parent::__construct($request);
- $this->rules = &$request->get_sort_by_ref();
- }
- /*! add new sorting rule
-
- @param name
- name of field
- @param dir
- direction of sorting
- */
- public function add($name,$dir){
- $this->request->set_sort($name,$dir);
- }
- public function store(){
- $this->request->set_sort_by($this->rules);
- }
-}
-/*! Wrapper for collection of filtering rules
-**/
-class FilterInterface extends EventInterface{
- /*! constructor
- creates a new interface based on existing request
- @param request
- DataRequestConfig object
- */
- public function __construct($request){
- $this->request = $request;
- $this->rules = &$request->get_filters_ref();
- }
- /*! add new filatering rule
-
- @param name
- name of field
- @param value
- value to filter by
- @param rule
- filtering rule
- */
- public function add($name,$value,$rule){
- $this->request->set_filter($name,$value,$rule);
- }
- public function store(){
- $this->request->set_filters($this->rules);
- }
-}
-
-/*! base class for component item representation
-**/
-class DataItem{
- protected $data; //!< hash of data
- protected $config;//!< DataConfig instance
- protected $index;//!< index of element
- protected $skip;//!< flag , which set if element need to be skiped during rendering
- protected $userdata;
-
- /*! constructor
-
- @param data
- hash of data
- @param config
- DataConfig object
- @param index
- index of element
- */
- function __construct($data,$config,$index){
- $this->config=$config;
- $this->data=$data;
- $this->index=$index;
- $this->skip=false;
- $this->userdata=false;
- }
-
- //set userdata for the item
- function set_userdata($name, $value){
- if ($this->userdata === false)
- $this->userdata = array();
-
- $this->userdata[$name]=$value;
- }
- /*! get named value
-
- @param name
- name or alias of field
- @return
- value from field with provided name or alias
- */
- public function get_value($name){
- return $this->data[$name];
- }
- /*! set named value
-
- @param name
- name or alias of field
- @param value
- value for field with provided name or alias
- */
- public function set_value($name,$value){
- return $this->data[$name]=$value;
- }
- /*! get id of element
- @return
- id of element
- */
- public function get_id(){
- $id = $this->config->id["name"];
- if (array_key_exists($id,$this->data))
- return $this->data[$id];
- return false;
- }
- /*! change id of element
-
- @param value
- new id value
- */
- public function set_id($value){
- $this->data[$this->config->id["name"]]=$value;
- }
- /*! get index of element
-
- @return
- index of element
- */
- public function get_index(){
- return $this->index;
- }
- /*! mark element for skiping ( such element will not be rendered )
- */
- public function skip(){
- $this->skip=true;
- }
-
- /*! return self as XML string
- */
- public function to_xml(){
- return $this->to_xml_start().$this->to_xml_end();
- }
-
- /*! replace xml unsafe characters
-
- @param string
- string to be escaped
- @return
- escaped string
- */
- public function xmlentities($string) {
- return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&' , '"', ''' , '<' , '>', ''' ), $string);
- }
-
- /*! return starting tag for self as XML string
- */
- public function to_xml_start(){
- $str="<item";
- for ($i=0; $i < sizeof($this->config->data); $i++){
- $name=$this->config->data[$i]["name"];
- $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'";
- }
- //output custom data
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value){
- $str.=" ".$key."='".$this->xmlentities($value)."'";
- }
-
- return $str.">";
- }
- /*! return ending tag for XML string
- */
- public function to_xml_end(){
- return "</item>";
- }
-}
-
-
-
-
-
-/*! Base connector class
- This class used as a base for all component specific connectors.
- Can be used on its own to provide raw data.
-**/
-class Connector {
- protected $config;//DataConfig instance
- protected $request;//DataRequestConfig instance
- protected $names;//!< hash of names for used classes
- protected $encoding="utf-8";//!< assigned encoding (UTF-8 by default)
- protected $editing=false;//!< flag of edit mode ( response for dataprocessor )
-
- public $model=false;
-
- private $updating=false;//!< flag of update mode ( response for data-update )
- private $db; //!< db connection resource
- protected $dload;//!< flag of dyn. loading mode
- public $access; //!< AccessMaster instance
- protected $data_separator = "\n";
-
- public $sql; //DataWrapper instance
- public $event; //EventMaster instance
- public $limit=false;
-
- private $id_seed=0; //!< default value, used to generate auto-IDs
- protected $live_update = false; // actions table name for autoupdating
-
- /*! constructor
-
- Here initilization of all Masters occurs, execution timer initialized
- @param db
- db connection resource
- @param type
- string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
- @param item_type
- name of class, which will be used for item rendering, optional, DataItem will be used by default
- @param data_type
- name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
- */
- public function __construct($db,$type=false, $item_type=false, $data_type=false, $render_type = false){
- $this->exec_time=microtime(true);
-
- if (!$type) $type="MySQL";
- if (class_exists($type."DBDataWrapper",false)) $type.="DBDataWrapper";
- if (!$item_type) $item_type="DataItem";
- if (!$data_type) $data_type="DataProcessor";
- if (!$render_type) $render_type="RenderStrategy";
-
- $this->names=array(
- "db_class"=>$type,
- "item_class"=>$item_type,
- "data_class"=>$data_type,
- "render_class"=>$render_type
- );
-
- $this->config = new DataConfig();
- $this->request = new DataRequestConfig();
- $this->event = new EventMaster();
- $this->access = new AccessMaster();
-
- if (!class_exists($this->names["db_class"],false))
- throw new Exception("DB class not found: ".$this->names["db_class"]);
- $this->sql = new $this->names["db_class"]($db,$this->config);
- $this->render = new $this->names["render_class"]($this);
-
- $this->db=$db;//saved for options connectors, if any
-
- EventMaster::trigger_static("connectorCreate",$this);
- }
-
- /*! return db connection resource
- nested class may neeed to access live connection object
- @return
- DB connection resource
- */
- protected function get_connection(){
- return $this->db;
- }
-
- public function get_config(){
- return new DataConfig($this->config);
- }
-
- public function get_request(){
- return new DataRequestConfig($this->request);
- }
-
-
- //model is a class, which will be used for all data operations
- //we expect that it has next methods get, update, insert, delete
- //if method was not defined - we will use default logic
- public function useModel($model){
- $this->model = $model;
- }
-
-
- /*! config connector based on table
-
- @param table
- name of table in DB
- @param id
- name of id field
- @param fields
- list of fields names
- @param extra
- list of extra fields, optional, such fields will not be included in data rendering, but will be accessible in all inner events
- @param relation_id
- name of field used to define relations for hierarchical data organization, optional
- */
- public function render_table($table,$id="",$fields=false,$extra=false,$relation_id=false){
- $this->configure($table,$id,$fields,$extra,$relation_id);
- return $this->render();
- }
- public function configure($table,$id="",$fields=false,$extra=false,$relation_id=false){
- if ($fields === false){
- //auto-config
- $info = $this->sql->fields_list($table);
- $fields = implode(",",$info["fields"]);
- if ($info["key"])
- $id = $info["key"];
- }
- $this->config->init($id,$fields,$extra,$relation_id);
- $this->request->set_source($table);
- }
-
- public function uuid(){
- return time()."x".$this->id_seed++;
- }
-
- /*! config connector based on sql
-
- @param sql
- sql query used as base of configuration
- @param id
- name of id field
- @param fields
- list of fields names
- @param extra
- list of extra fields, optional, such fields will not be included in data rendering, but will be accessible in all inner events
- @param relation_id
- name of field used to define relations for hierarchical data organization, optional
- */
- public function render_sql($sql,$id,$fields,$extra=false,$relation_id=false){
- $this->config->init($id,$fields,$extra,$relation_id);
- $this->request->parse_sql($sql);
- return $this->render();
- }
-
- public function render_complex_sql($sql,$id,$fields,$extra=false,$relation_id=false){
- $this->config->init($id,$fields,$extra,$relation_id);
- $this->request->parse_sql($sql, true);
- return $this->render();
- }
-
- /*! render already configured connector
-
- @param config
- configuration of data
- @param request
- configuraton of request
- */
- public function render_connector($config,$request){
- $this->config->copy($config);
- $this->request->copy($request);
- return $this->render();
- }
-
- /*! render self
- process commands, output requested data as XML
- */
- public function render(){
- $this->event->trigger("onInit", $this);
- EventMaster::trigger_static("connectorInit",$this);
-
- $this->parse_request();
- $this->set_relation();
-
- if ($this->live_update !== false && $this->updating!==false) {
- $this->live_update->get_updates();
- } else {
- if ($this->editing){
- $dp = new $this->names["data_class"]($this,$this->config,$this->request);
- $dp->process($this->config,$this->request);
- } else {
- if (!$this->access->check("read")){
- LogMaster::log("Access control: read operation blocked");
- echo "Access denied";
- die();
- }
- $wrap = new SortInterface($this->request);
- $this->event->trigger("beforeSort",$wrap);
- $wrap->store();
-
- $wrap = new FilterInterface($this->request);
- $this->event->trigger("beforeFilter",$wrap);
- $wrap->store();
-
-
- if ($this->model && method_exists($this->model, "get")){
- $this->sql = new ArrayDBDataWrapper();
- $result = new ArrayQueryWrapper(call_user_func(array($this->model, "get"), $this->request));
- $this->output_as_xml($result);
- } else
- $this->output_as_xml($this->get_resource());
- }
- }
- $this->end_run();
- }
-
-
- /*! empty call which used for tree-logic
- * to prevent code duplicating
- */
- protected function set_relation() {}
-
- /*! gets resource for rendering
- */
- protected function get_resource() {
- return $this->sql->select($this->request);
- }
-
-
- /*! prevent SQL injection through column names
- replace dangerous chars in field names
- @param str
- incoming field name
- @return
- safe field name
- */
- protected function safe_field_name($str){
- return strtok($str, " \n\t;',");
- }
-
- /*! limit max count of records
- connector will ignore any records after outputing max count
- @param limit
- max count of records
- @return
- none
- */
- public function set_limit($limit){
- $this->limit = $limit;
- }
-
- protected function parse_request_mode(){
- //detect edit mode
- if (isset($_GET["editing"])){
- $this->editing=true;
- } else if (isset($_POST["ids"])){
- $this->editing=true;
- LogMaster::log('While there is no edit mode mark, POST parameters similar to edit mode detected. \n Switching to edit mode ( to disable behavior remove POST[ids]');
- } else if (isset($_GET['dhx_version'])){
- $this->updating = true;
- }
- }
-
- /*! parse incoming request, detects commands and modes
- */
- protected function parse_request(){
- //set default dyn. loading params, can be reset in child classes
- if ($this->dload)
- $this->request->set_limit(0,$this->dload);
- else if ($this->limit)
- $this->request->set_limit(0,$this->limit);
-
- $this->parse_request_mode();
-
- if ($this->live_update && ($this->updating || $this->editing)){
- $this->request->set_version($_GET["dhx_version"]);
- $this->request->set_user($_GET["dhx_user"]);
- }
-
- if (isset($_GET["dhx_sort"]))
- foreach($_GET["dhx_sort"] as $k => $v){
- $k = $this->safe_field_name($k);
- $this->request->set_sort($this->resolve_parameter($k),$v);
- }
-
- if (isset($_GET["dhx_filter"]))
- foreach($_GET["dhx_filter"] as $k => $v){
- $k = $this->safe_field_name($k);
- $this->request->set_filter($this->resolve_parameter($k),$v);
- }
-
-
- }
-
- /*! convert incoming request name to the actual DB name
- @param name
- incoming parameter name
- @return
- name of related DB field
- */
- protected function resolve_parameter($name){
- return $name;
- }
-
-
- /*! replace xml unsafe characters
-
- @param string
- string to be escaped
- @return
- escaped string
- */
- protected function xmlentities($string) {
- return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&' , '"', ''' , '<' , '>', ''' ), $string);
- }
-
- public function getRecord($id){
- LogMaster::log("Retreiving data for record: ".$id);
- $source = new DataRequestConfig($this->request);
- $source->set_filter($this->config->id["name"],$id, "=");
-
- $res = $this->sql->select($source);
-
- $temp = $this->data_separator;
- $this->data_separator="";
- $output = $this->render_set($res);
- $this->data_separato=$temp;
-
- return $output;
- }
-
- /*! render from DB resultset
- @param res
- DB resultset
- process commands, output requested data as XML
- */
- protected function render_set($res){
- return $this->render->render_set($res, $this->names["item_class"], $this->dload, $this->data_separator);
- }
-
- /*! output fetched data as XML
- @param res
- DB resultset
- */
- protected function output_as_xml($res){
- $start="<?xml version='1.0' encoding='".$this->encoding."' ?>".$this->xml_start();
- $end=$this->render_set($res).$this->xml_end();
-
- $out = new OutputWriter($start, $end);
- $this->event->trigger("beforeOutput", $this, $out);
- $out->output("", true, $this->encoding);
- }
-
-
- /*! end processing
- stop execution timer, kill the process
- */
- protected function end_run(){
- $time=microtime(true)-$this->exec_time;
- LogMaster::log("Done in {$time}s");
- flush();
- die();
- }
-
- /*! set xml encoding
-
- methods sets only attribute in XML, no real encoding conversion occurs
- @param encoding
- value which will be used as XML encoding
- */
- public function set_encoding($encoding){
- $this->encoding=$encoding;
- }
-
- /*! enable or disable dynamic loading mode
-
- @param count
- count of rows loaded from server, actual only for grid-connector, can be skiped in other cases.
- If value is a false or 0 - dyn. loading will be disabled
- */
- public function dynamic_loading($count){
- $this->dload=$count;
- }
-
- /*! enable logging
-
- @param path
- path to the log file. If set as false or empty strig - logging will be disabled
- @param client_log
- enable output of log data to the client side
- */
- public function enable_log($path=true,$client_log=false){
- LogMaster::enable_log($path,$client_log);
- }
-
- /*! provides infor about current processing mode
- @return
- true if processing dataprocessor command, false otherwise
- */
- public function is_select_mode(){
- $this->parse_request_mode();
- return !$this->editing;
- }
-
- public function is_first_call(){
- $this->parse_request_mode();
- return !($this->editing || $this->updating || $this->request->get_start() || isset($_GET['dhx_no_header']));
-
- }
-
- /*! renders self as xml, starting part
- */
- protected function xml_start(){
- return "<data>";
- }
- /*! renders self as xml, ending part
- */
- protected function xml_end(){
- return "</data>";
- }
-
-
- public function insert($data) {
- $action = new DataAction('inserted', false, $data);
- $request = new DataRequestConfig();
- $request->set_source($this->request->get_source());
-
- $this->config->limit_fields($data);
- $this->sql->insert($action,$request);
- $this->config->restore_fields($data);
-
- return $action->get_new_id();
- }
-
- public function delete($id) {
- $action = new DataAction('deleted', $id, array());
- $request = new DataRequestConfig();
- $request->set_source($this->request->get_source());
-
- $this->sql->delete($action,$request);
- return $action->get_status();
-}
-
- public function update($data) {
- $action = new DataAction('updated', $data[$this->config->id["name"]], $data);
- $request = new DataRequestConfig();
- $request->set_source($this->request->get_source());
-
- $this->config->limit_fields($data);
- $this->sql->update($action,$request);
- $this->config->restore_fields($data);
-
- return $action->get_status();
- }
-
- /*! sets actions_table for Optimistic concurrency control mode and start it
- @param table_name
- name of database table which will used for saving actions
- @param url
- url used for update notifications
- */
- public function enable_live_update($table, $url=false){
- $this->live_update = new DataUpdate($this->sql, $this->config, $this->request, $table,$url);
- $this->live_update->set_event($this->event,$this->names["item_class"]);
- $this->event->attach("beforeOutput", Array($this->live_update, "version_output"));
- $this->event->attach("beforeFiltering", Array($this->live_update, "get_updates"));
- $this->event->attach("beforeProcessing", Array($this->live_update, "check_collision"));
- $this->event->attach("afterProcessing", Array($this->live_update, "log_operations"));
- }
-}
-
-
-/*! wrapper around options collection, used for comboboxes and filters
-**/
-class OptionsConnector extends Connector{
- protected $init_flag=false;//!< used to prevent rendering while initialization
- public function __construct($res,$type=false,$item_type=false,$data_type=false){
- if (!$item_type) $item_type="DataItem";
- if (!$data_type) $data_type=""; //has not sense, options not editable
- parent::__construct($res,$type,$item_type,$data_type);
- }
- /*! render self
- process commands, return data as XML, not output data to stdout, ignore parameters in incoming request
- @return
- data as XML string
- */
- public function render(){
- if (!$this->init_flag){
- $this->init_flag=true;
- return "";
- }
- $res = $this->sql->select($this->request);
- return $this->render_set($res);
- }
-}
-
-
-
-class DistinctOptionsConnector extends OptionsConnector{
- /*! render self
- process commands, return data as XML, not output data to stdout, ignore parameters in incoming request
- @return
- data as XML string
- */
- public function render(){
- if (!$this->init_flag){
- $this->init_flag=true;
- return "";
- }
- $res = $this->sql->get_variants($this->config->text[0]["db_name"],$this->request);
- return $this->render_set($res);
- }
-}
-
-?>
+<?php +/* + @author dhtmlx.com + @license GPL, see license.txt +*/ +require_once("tools.php"); +require_once("db_common.php"); +require_once("dataprocessor.php"); +require_once("strategy.php"); +require_once("update.php"); + +//enable buffering to catch and ignore any custom output before XML generation +//because of this command, it strongly recommended to include connector's file before any other libs +//in such case it will handle any extra output from not well formed code of other libs +ini_set("output_buffering","On"); +ob_start(); + +class OutputWriter{ + private $start; + private $end; + private $type; + + public function __construct($start, $end = ""){ + $this->start = $start; + $this->end = $end; + $this->type = "xml"; + } + public function add($add){ + $this->start.=$add; + } + public function reset(){ + $this->start=""; + $this->end=""; + } + public function set_type($add){ + $this->type=$add; + } + public function output($name="", $inline=true, $encoding=""){ + ob_clean(); + + if ($this->type == "xml"){ + $header = "Content-type: text/xml"; + if ("" != $encoding) + $header.="; charset=".$encoding; + header($header); + } + + echo $this->__toString(); + } + public function __toString(){ + return $this->start.$this->end; + } +} + +/*! EventInterface + Base class , for iterable collections, which are used in event +**/ +class EventInterface{ + protected $request; ////!< DataRequestConfig instance + public $rules=array(); //!< array of sorting rules + + /*! constructor + creates a new interface based on existing request + @param request + DataRequestConfig object + */ + public function __construct($request){ + $this->request = $request; + } + + /*! remove all elements from collection + */ + public function clear(){ + array_splice($rules,0); + } + /*! get index by name + + @param name + name of field + @return + index of named field + */ + public function index($name){ + $len = sizeof($this->rules); + for ($i=0; $i < $len; $i++) { + if ($this->rules[$i]["name"]==$name) + return $i; + } + return false; + } +} +/*! Wrapper for collection of sorting rules +**/ +class SortInterface extends EventInterface{ + /*! constructor + creates a new interface based on existing request + @param request + DataRequestConfig object + */ + public function __construct($request){ + parent::__construct($request); + $this->rules = &$request->get_sort_by_ref(); + } + /*! add new sorting rule + + @param name + name of field + @param dir + direction of sorting + */ + public function add($name,$dir){ + $this->request->set_sort($name,$dir); + } + public function store(){ + $this->request->set_sort_by($this->rules); + } +} +/*! Wrapper for collection of filtering rules +**/ +class FilterInterface extends EventInterface{ + /*! constructor + creates a new interface based on existing request + @param request + DataRequestConfig object + */ + public function __construct($request){ + $this->request = $request; + $this->rules = &$request->get_filters_ref(); + } + /*! add new filatering rule + + @param name + name of field + @param value + value to filter by + @param rule + filtering rule + */ + public function add($name,$value,$rule){ + $this->request->set_filter($name,$value,$rule); + } + public function store(){ + $this->request->set_filters($this->rules); + } +} + +/*! base class for component item representation +**/ +class DataItem{ + protected $data; //!< hash of data + protected $config;//!< DataConfig instance + protected $index;//!< index of element + protected $skip;//!< flag , which set if element need to be skiped during rendering + protected $userdata; + + /*! constructor + + @param data + hash of data + @param config + DataConfig object + @param index + index of element + */ + function __construct($data,$config,$index){ + $this->config=$config; + $this->data=$data; + $this->index=$index; + $this->skip=false; + $this->userdata=false; + } + + //set userdata for the item + function set_userdata($name, $value){ + if ($this->userdata === false) + $this->userdata = array(); + + $this->userdata[$name]=$value; + } + /*! get named value + + @param name + name or alias of field + @return + value from field with provided name or alias + */ + public function get_value($name){ + return $this->data[$name]; + } + /*! set named value + + @param name + name or alias of field + @param value + value for field with provided name or alias + */ + public function set_value($name,$value){ + return $this->data[$name]=$value; + } + /*! get id of element + @return + id of element + */ + public function get_id(){ + $id = $this->config->id["name"]; + if (array_key_exists($id,$this->data)) + return $this->data[$id]; + return false; + } + /*! change id of element + + @param value + new id value + */ + public function set_id($value){ + $this->data[$this->config->id["name"]]=$value; + } + /*! get index of element + + @return + index of element + */ + public function get_index(){ + return $this->index; + } + /*! mark element for skiping ( such element will not be rendered ) + */ + public function skip(){ + $this->skip=true; + } + + /*! return self as XML string + */ + public function to_xml(){ + return $this->to_xml_start().$this->to_xml_end(); + } + + /*! replace xml unsafe characters + + @param string + string to be escaped + @return + escaped string + */ + public function xmlentities($string) { + return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&' , '"', ''' , '<' , '>', ''' ), $string); + } + + /*! return starting tag for self as XML string + */ + public function to_xml_start(){ + $str="<item"; + for ($i=0; $i < sizeof($this->config->data); $i++){ + $name=$this->config->data[$i]["name"]; + $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'"; + } + //output custom data + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value){ + $str.=" ".$key."='".$this->xmlentities($value)."'"; + } + + return $str.">"; + } + /*! return ending tag for XML string + */ + public function to_xml_end(){ + return "</item>"; + } +} + + + + + +/*! Base connector class + This class used as a base for all component specific connectors. + Can be used on its own to provide raw data. +**/ +class Connector { + protected $config;//DataConfig instance + protected $request;//DataRequestConfig instance + protected $names;//!< hash of names for used classes + protected $encoding="utf-8";//!< assigned encoding (UTF-8 by default) + protected $editing=false;//!< flag of edit mode ( response for dataprocessor ) + + public $model=false; + + private $updating=false;//!< flag of update mode ( response for data-update ) + private $db; //!< db connection resource + protected $dload;//!< flag of dyn. loading mode + public $access; //!< AccessMaster instance + protected $data_separator = "\n"; + + public $sql; //DataWrapper instance + public $event; //EventMaster instance + public $limit=false; + + private $id_seed=0; //!< default value, used to generate auto-IDs + protected $live_update = false; // actions table name for autoupdating + protected $options = array(); + + /*! constructor + + Here initilization of all Masters occurs, execution timer initialized + @param db + db connection resource + @param type + string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided + @param item_type + name of class, which will be used for item rendering, optional, DataItem will be used by default + @param data_type + name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default. + */ + public function __construct($db,$type=false, $item_type=false, $data_type=false, $render_type = false){ + $this->exec_time=microtime(true); + + if (!$type) $type="MySQL"; + if (class_exists($type."DBDataWrapper",false)) $type.="DBDataWrapper"; + if (!$item_type) $item_type="DataItem"; + if (!$data_type) $data_type="DataProcessor"; + if (!$render_type) $render_type="RenderStrategy"; + + $this->names=array( + "db_class"=>$type, + "item_class"=>$item_type, + "data_class"=>$data_type, + "render_class"=>$render_type + ); + + $this->config = new DataConfig(); + $this->request = new DataRequestConfig(); + $this->event = new EventMaster(); + $this->access = new AccessMaster(); + + if (!class_exists($this->names["db_class"],false)) + throw new Exception("DB class not found: ".$this->names["db_class"]); + $this->sql = new $this->names["db_class"]($db,$this->config); + $this->render = new $this->names["render_class"]($this); + + $this->db=$db;//saved for options connectors, if any + + EventMaster::trigger_static("connectorCreate",$this); + } + + /*! return db connection resource + nested class may neeed to access live connection object + @return + DB connection resource + */ + protected function get_connection(){ + return $this->db; + } + + public function get_config(){ + return new DataConfig($this->config); + } + + public function get_request(){ + return new DataRequestConfig($this->request); + } + + + //model is a class, which will be used for all data operations + //we expect that it has next methods get, update, insert, delete + //if method was not defined - we will use default logic + public function useModel($model){ + $this->model = $model; + } + + + /*! config connector based on table + + @param table + name of table in DB + @param id + name of id field + @param fields + list of fields names + @param extra + list of extra fields, optional, such fields will not be included in data rendering, but will be accessible in all inner events + @param relation_id + name of field used to define relations for hierarchical data organization, optional + */ + public function render_table($table,$id="",$fields=false,$extra=false,$relation_id=false){ + $this->configure($table,$id,$fields,$extra,$relation_id); + return $this->render(); + } + public function configure($table,$id="",$fields=false,$extra=false,$relation_id=false){ + if ($fields === false){ + //auto-config + $info = $this->sql->fields_list($table); + $fields = implode(",",$info["fields"]); + if ($info["key"]) + $id = $info["key"]; + } + $this->config->init($id,$fields,$extra,$relation_id); + $this->request->set_source($table); + } + + public function uuid(){ + return time()."x".$this->id_seed++; + } + + /*! config connector based on sql + + @param sql + sql query used as base of configuration + @param id + name of id field + @param fields + list of fields names + @param extra + list of extra fields, optional, such fields will not be included in data rendering, but will be accessible in all inner events + @param relation_id + name of field used to define relations for hierarchical data organization, optional + */ + public function render_sql($sql,$id,$fields,$extra=false,$relation_id=false){ + $this->config->init($id,$fields,$extra,$relation_id); + $this->request->parse_sql($sql); + return $this->render(); + } + + public function render_complex_sql($sql,$id,$fields,$extra=false,$relation_id=false){ + $this->config->init($id,$fields,$extra,$relation_id); + $this->request->parse_sql($sql, true); + return $this->render(); + } + + /*! render already configured connector + + @param config + configuration of data + @param request + configuraton of request + */ + public function render_connector($config,$request){ + $this->config->copy($config); + $this->request->copy($request); + return $this->render(); + } + + /*! render self + process commands, output requested data as XML + */ + public function render(){ + $this->event->trigger("onInit", $this); + EventMaster::trigger_static("connectorInit",$this); + + $this->parse_request(); + $this->set_relation(); + + if ($this->live_update !== false && $this->updating!==false) { + $this->live_update->get_updates(); + } else { + if ($this->editing){ + $dp = new $this->names["data_class"]($this,$this->config,$this->request); + $dp->process($this->config,$this->request); + } else { + if (!$this->access->check("read")){ + LogMaster::log("Access control: read operation blocked"); + echo "Access denied"; + die(); + } + $wrap = new SortInterface($this->request); + $this->event->trigger("beforeSort",$wrap); + $wrap->store(); + + $wrap = new FilterInterface($this->request); + $this->event->trigger("beforeFilter",$wrap); + $wrap->store(); + + + if ($this->model && method_exists($this->model, "get")){ + $this->sql = new ArrayDBDataWrapper(); + $result = new ArrayQueryWrapper(call_user_func(array($this->model, "get"), $this->request)); + $this->output_as_xml($result); + } else + $this->output_as_xml($this->get_resource()); + } + } + $this->end_run(); + } + + + /*! empty call which used for tree-logic + * to prevent code duplicating + */ + protected function set_relation() {} + + /*! gets resource for rendering + */ + protected function get_resource() { + return $this->sql->select($this->request); + } + + + /*! prevent SQL injection through column names + replace dangerous chars in field names + @param str + incoming field name + @return + safe field name + */ + protected function safe_field_name($str){ + return strtok($str, " \n\t;',"); + } + + /*! limit max count of records + connector will ignore any records after outputing max count + @param limit + max count of records + @return + none + */ + public function set_limit($limit){ + $this->limit = $limit; + } + + protected function parse_request_mode(){ + //detect edit mode + if (isset($_GET["editing"])){ + $this->editing=true; + } else if (isset($_POST["ids"])){ + $this->editing=true; + LogMaster::log('While there is no edit mode mark, POST parameters similar to edit mode detected. \n Switching to edit mode ( to disable behavior remove POST[ids]'); + } else if (isset($_GET['dhx_version'])){ + $this->updating = true; + } + } + + /*! parse incoming request, detects commands and modes + */ + protected function parse_request(){ + //set default dyn. loading params, can be reset in child classes + if ($this->dload) + $this->request->set_limit(0,$this->dload); + else if ($this->limit) + $this->request->set_limit(0,$this->limit); + + $this->parse_request_mode(); + + if ($this->live_update && ($this->updating || $this->editing)){ + $this->request->set_version($_GET["dhx_version"]); + $this->request->set_user($_GET["dhx_user"]); + } + + if (isset($_GET["dhx_sort"])) + foreach($_GET["dhx_sort"] as $k => $v){ + $k = $this->safe_field_name($k); + $this->request->set_sort($this->resolve_parameter($k),$v); + } + + if (isset($_GET["dhx_filter"])) + foreach($_GET["dhx_filter"] as $k => $v){ + $k = $this->safe_field_name($k); + $this->request->set_filter($this->resolve_parameter($k),$v); + } + + + } + + /*! convert incoming request name to the actual DB name + @param name + incoming parameter name + @return + name of related DB field + */ + protected function resolve_parameter($name){ + return $name; + } + + + /*! replace xml unsafe characters + + @param string + string to be escaped + @return + escaped string + */ + protected function xmlentities($string) { + return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&' , '"', ''' , '<' , '>', ''' ), $string); + } + + public function getRecord($id){ + LogMaster::log("Retreiving data for record: ".$id); + $source = new DataRequestConfig($this->request); + $source->set_filter($this->config->id["name"],$id, "="); + + $res = $this->sql->select($source); + + $temp = $this->data_separator; + $this->data_separator=""; + $output = $this->render_set($res); + $this->data_separato=$temp; + + return $output; + } + + /*! render from DB resultset + @param res + DB resultset + process commands, output requested data as XML + */ + protected function render_set($res){ + return $this->render->render_set($res, $this->names["item_class"], $this->dload, $this->data_separator, $this->config); + } + + /*! output fetched data as XML + @param res + DB resultset + */ + protected function output_as_xml($res){ + $start="<?xml version='1.0' encoding='".$this->encoding."' ?>".$this->xml_start(); + $end=$this->render_set($res).$this->xml_end(); + + $out = new OutputWriter($start, $end); + $this->event->trigger("beforeOutput", $this, $out); + $out->output("", true, $this->encoding); + } + + + /*! end processing + stop execution timer, kill the process + */ + protected function end_run(){ + $time=microtime(true)-$this->exec_time; + LogMaster::log("Done in {$time}s"); + flush(); + die(); + } + + /*! set xml encoding + + methods sets only attribute in XML, no real encoding conversion occurs + @param encoding + value which will be used as XML encoding + */ + public function set_encoding($encoding){ + $this->encoding=$encoding; + } + + /*! enable or disable dynamic loading mode + + @param count + count of rows loaded from server, actual only for grid-connector, can be skiped in other cases. + If value is a false or 0 - dyn. loading will be disabled + */ + public function dynamic_loading($count){ + $this->dload=$count; + } + + /*! enable logging + + @param path + path to the log file. If set as false or empty strig - logging will be disabled + @param client_log + enable output of log data to the client side + */ + public function enable_log($path=true,$client_log=false){ + LogMaster::enable_log($path,$client_log); + } + + /*! provides infor about current processing mode + @return + true if processing dataprocessor command, false otherwise + */ + public function is_select_mode(){ + $this->parse_request_mode(); + return !$this->editing; + } + + public function is_first_call(){ + $this->parse_request_mode(); + return !($this->editing || $this->updating || $this->request->get_start() || isset($_GET['dhx_no_header'])); + + } + + /*! renders self as xml, starting part + */ + protected function xml_start(){ + return "<data>"; + } + /*! renders self as xml, ending part + */ + protected function xml_end(){ + $this->fill_collections(); + return $this->extra_output."</data>"; + } + + protected function fill_collections(){ + foreach ($this->options as $k=>$v) { + $name = $k; + $this->extra_output.="<coll_options for='{$name}'>"; + if (!is_string($this->options[$name])) + $this->extra_output.=$this->options[$name]->render(); + else + $this->extra_output.=$this->options[$name]; + $this->extra_output.="</coll_options>"; + } + } + + /*! assign options collection to the column + + @param name + name of the column + @param options + array or connector object + */ + public function set_options($name,$options){ + if (is_array($options)){ + $str=""; + foreach($options as $k => $v) + $str.="<item value='".$this->xmlentities($k)."' label='".$this->xmlentities($v)."' />"; + $options=$str; + } + $this->options[$name]=$options; + } + + + public function insert($data) { + $action = new DataAction('inserted', false, $data); + $request = new DataRequestConfig(); + $request->set_source($this->request->get_source()); + + $this->config->limit_fields($data); + $this->sql->insert($action,$request); + $this->config->restore_fields($data); + + return $action->get_new_id(); + } + + public function delete($id) { + $action = new DataAction('deleted', $id, array()); + $request = new DataRequestConfig(); + $request->set_source($this->request->get_source()); + + $this->sql->delete($action,$request); + return $action->get_status(); +} + + public function update($data) { + $action = new DataAction('updated', $data[$this->config->id["name"]], $data); + $request = new DataRequestConfig(); + $request->set_source($this->request->get_source()); + + $this->config->limit_fields($data); + $this->sql->update($action,$request); + $this->config->restore_fields($data); + + return $action->get_status(); + } + + /*! sets actions_table for Optimistic concurrency control mode and start it + @param table_name + name of database table which will used for saving actions + @param url + url used for update notifications + */ + public function enable_live_update($table, $url=false){ + $this->live_update = new DataUpdate($this->sql, $this->config, $this->request, $table,$url); + $this->live_update->set_event($this->event,$this->names["item_class"]); + $this->event->attach("beforeOutput", Array($this->live_update, "version_output")); + $this->event->attach("beforeFiltering", Array($this->live_update, "get_updates")); + $this->event->attach("beforeProcessing", Array($this->live_update, "check_collision")); + $this->event->attach("afterProcessing", Array($this->live_update, "log_operations")); + } +} + + +/*! wrapper around options collection, used for comboboxes and filters +**/ +class OptionsConnector extends Connector{ + protected $init_flag=false;//!< used to prevent rendering while initialization + public function __construct($res,$type=false,$item_type=false,$data_type=false){ + if (!$item_type) $item_type="DataItem"; + if (!$data_type) $data_type=""; //has not sense, options not editable + parent::__construct($res,$type,$item_type,$data_type); + } + /*! render self + process commands, return data as XML, not output data to stdout, ignore parameters in incoming request + @return + data as XML string + */ + public function render(){ + if (!$this->init_flag){ + $this->init_flag=true; + return ""; + } + $res = $this->sql->select($this->request); + return $this->render_set($res); + } +} + + + +class DistinctOptionsConnector extends OptionsConnector{ + /*! render self + process commands, return data as XML, not output data to stdout, ignore parameters in incoming request + @return + data as XML string + */ + public function render(){ + if (!$this->init_flag){ + $this->init_flag=true; + return ""; + } + $res = $this->sql->get_variants($this->config->text[0]["db_name"],$this->request); + return $this->render_set($res); + } +} + +?>
\ No newline at end of file diff --git a/codebase/crosslink_connector.php b/codebase/crosslink_connector.php index f92d0c5..d6df689 100644 --- a/codebase/crosslink_connector.php +++ b/codebase/crosslink_connector.php @@ -3,6 +3,8 @@ @author dhtmlx.com @license GPL, see license.txt */ +require_once("data_connector.php"); + class DelayedConnector extends Connector{ protected $init_flag=false;//!< used to prevent rendering while initialization private $data_mode=false;//!< flag to separate xml and data request modes @@ -120,4 +122,16 @@ class CrossOptionsConnector extends Connector{ } } + +class JSONCrossOptionsConnector extends CrossOptionsConnector{ + public $options, $link; + private $master_name, $link_name, $master_value; + + public function __construct($res,$type=false,$item_type=false,$data_type=false){ + $this->options = new JSONOptionsConnector($res,$type,$item_type,$data_type); + $this->link = new DelayedConnector($res,$type,$item_type,$data_type); + + EventMaster::attach_static("connectorInit",array($this, "handle")); + } +} ?>
\ No newline at end of file diff --git a/codebase/data_connector.php b/codebase/data_connector.php index de6d2f0..a5419e0 100644 --- a/codebase/data_connector.php +++ b/codebase/data_connector.php @@ -1,453 +1,449 @@ -<?php
-/*
- @author dhtmlx.com
- @license GPL, see license.txt
-*/
-require_once("base_connector.php");
-
-class CommonDataProcessor extends DataProcessor{
- protected function get_post_values($ids){
- if (isset($_GET['action'])){
- $data = array();
- if (isset($_POST["id"])){
- $dataset = array();
- foreach($_POST as $key=>$value)
- $dataset[$key] = ConnectorSecurity::filter($value);
-
- $data[$_POST["id"]] = $dataset;
- }
- else
- $data["dummy_id"] = $_POST;
- return $data;
- }
- return parent::get_post_values($ids);
- }
-
- protected function get_ids(){
- if (isset($_GET['action'])){
- if (isset($_POST["id"]))
- return array($_POST['id']);
- else
- return array("dummy_id");
- }
- return parent::get_ids();
- }
-
- protected function get_operation($rid){
- if (isset($_GET['action']))
- return $_GET['action'];
- return parent::get_operation($rid);
- }
-
- public function output_as_xml($results){
- if (isset($_GET['action'])){
- LogMaster::log("Edit operation finished",$results);
- ob_clean();
- $type = $results[0]->get_status();
- if ($type == "error" || $type == "invalid"){
- echo "false";
- } else if ($type=="insert"){
- echo "true\n".$results[0]->get_new_id();
- } else
- echo "true";
- } else
- return parent::output_as_xml($results);
- }
-};
-
-/*! DataItem class for DataView component
-**/
-class CommonDataItem extends DataItem{
- /*! return self as XML string
- */
- function to_xml(){
- if ($this->skip) return "";
- return $this->to_xml_start().$this->to_xml_end();
- }
-
- function to_xml_start(){
- $str="<item id='".$this->get_id()."' ";
- for ($i=0; $i < sizeof($this->config->text); $i++){
- $name=$this->config->text[$i]["name"];
- $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'";
- }
-
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $str.=" ".$key."='".$this->xmlentities($value)."'";
-
- return $str.">";
- }
-}
-
-
-/*! Connector class for DataView
-**/
-class DataConnector extends Connector{
-
- /*! constructor
-
- Here initilization of all Masters occurs, execution timer initialized
- @param res
- db connection resource
- @param type
- string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
- @param item_type
- name of class, which will be used for item rendering, optional, DataItem will be used by default
- @param data_type
- name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
- */
- public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){
- if (!$item_type) $item_type="CommonDataItem";
- if (!$data_type) $data_type="CommonDataProcessor";
-
- $this->sections = array();
- $this->attributes = array();
-
- if (!$render_type) $render_type="RenderStrategy";
- parent::__construct($res,$type,$item_type,$data_type,$render_type);
-
- }
-
- protected $sections;
- public function add_section($name, $string){
- $this->sections[$name] = $string;
- }
-
- protected $attributes;
- public function add_top_attribute($name, $string){
- $this->attributes[$name] = $string;
- }
-
- protected function parse_request_mode(){
- //do nothing, at least for now
- }
-
- //parse GET scoope, all operations with incoming request must be done here
- protected function parse_request(){
- if (isset($_GET['action'])){
- $action = $_GET['action'];
- //simple request mode
- if ($action == "get"){
- //data request
- if (isset($_GET['id'])){
- //single entity data request
- $this->request->set_filter($this->config->id["name"],$_GET['id'],"=");
- } else {
- //loading collection of items
- }
- } else {
- //data saving
- $this->editing = true;
- }
- } else {
- if (isset($_GET["dhx_colls"]))
- $this->fill_collections($_GET["dhx_colls"]);
-
- if (isset($_GET['editing']) && isset($_POST['ids']))
- $this->editing = true;
-
- parent::parse_request();
- }
-
- if (isset($_GET["start"]) && isset($_GET["count"]))
- $this->request->set_limit($_GET["start"],$_GET["count"]);
-
- $key = ConnectorSecurity::checkCSRF($this->editing);
- if ($key !== "")
- $this->add_top_attribute("dhx_security", $key);
- }
-
- /*! renders self as xml, starting part
- */
- protected function xml_start(){
- $start = "<data";
- foreach($this->attributes as $k=>$v)
- $start .= " ".$k."='".$v."'";
- $start.= ">";
-
- foreach($this->sections as $k=>$v)
- $start .= "<".$k.">".$v."</".$k.">\n";
- return $start;
- }
-};
-
-class JSONDataConnector extends DataConnector{
-
- public function __construct($res,$type=false,$item_type=false,$data_type=false){
- if (!$item_type) $item_type="JSONCommonDataItem";
- if (!$data_type) $data_type="CommonDataProcessor";
- $this->data_separator = ",\n";
- parent::__construct($res,$type,$item_type,$data_type);
- }
-
- /*! assign options collection to the column
-
- @param name
- name of the column
- @param options
- array or connector object
- */
- public function set_options($name,$options){
- if (is_array($options)){
- $str=array();
- foreach($options as $k => $v)
- $str[]='{"id":"'.$this->xmlentities($k).'", "value":"'.$this->xmlentities($v).'"}';
- $options=implode(",",$str);
- }
- $this->options[$name]=$options;
- }
-
-
- protected function fill_collections($list){
- $names=explode(",",$list);
- $options=array();
- for ($i=0; $i < sizeof($names); $i++) {
- $name = $this->resolve_parameter($names[$i]);
- if (!array_key_exists($name,$this->options)){
- $this->options[$name] = new JSONDistinctOptionsConnector($this->get_connection(),$this->names["db_class"]);
- $c = new DataConfig($this->config);
- $r = new DataRequestConfig($this->request);
- $c->minimize($name);
-
- $this->options[$name]->render_connector($c,$r);
- }
-
- $option="\"{$name}\":[";
- if (!is_string($this->options[$name]))
- $option.=substr($this->options[$name]->render(),0,-2);
- else
- $option.=$this->options[$name];
- $option.="]";
- $options[] = $option;
- }
- $this->extra_output .= implode(",", $options);
- }
-
- protected function resolve_parameter($name){
- if (intval($name).""==$name)
- return $this->config->text[intval($name)]["db_name"];
- return $name;
- }
-
- protected function output_as_xml($res){
- $start = "[\n";
- $end = substr($this->render_set($res),0,-2)."\n]";
-
- $is_sections = sizeof($this->sections) && $this->is_first_call();
- if ($this->dload || $is_sections){
- $start = "{ \"data\":".$start.$end;
- $end="";
-
- if ($is_sections){
- //extra sections
- foreach($this->sections as $k=>$v)
- $end.= ", ".$k.":".$v;
- }
-
- if ($this->dload){
- //info for dyn. loadin
- if ($pos=$this->request->get_start())
- $end .= ", \"pos\":".$pos." }";
- else
- $end .= ", \"pos\":0, \"total_count\":".$this->sql->get_size($this->request)." }";
- } else
- $end .= " }";
- }
- $out = new OutputWriter($start, $end);
- $out->set_type("json");
- $this->event->trigger("beforeOutput", $this, $out);
- $out->output("", true, $this->encoding);
- }
-}
-
-class JSONCommonDataItem extends DataItem{
- /*! return self as XML string
- */
- function to_xml(){
- if ($this->skip) return "";
-
- $data = array();
- for ($i=0; $i<sizeof($this->config->text); $i++){
- $extra = $this->config->text[$i]["name"];
- $data[$extra]=$this->data[$extra];
- }
-
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $data["key"]=$value;
-
- return json_encode($data);
- }
-}
-
-
-/*! wrapper around options collection, used for comboboxes and filters
-**/
-class JSONOptionsConnector extends JSONDataConnector{
- protected $init_flag=false;//!< used to prevent rendering while initialization
- public function __construct($res,$type=false,$item_type=false,$data_type=false){
- if (!$item_type) $item_type="JSONCommonDataItem";
- if (!$data_type) $data_type=""; //has not sense, options not editable
- parent::__construct($res,$type,$item_type,$data_type);
- }
- /*! render self
- process commands, return data as XML, not output data to stdout, ignore parameters in incoming request
- @return
- data as XML string
- */
- public function render(){
- if (!$this->init_flag){
- $this->init_flag=true;
- return "";
- }
- $res = $this->sql->select($this->request);
- return $this->render_set($res);
- }
-}
-
-
-class JSONDistinctOptionsConnector extends JSONOptionsConnector{
- /*! render self
- process commands, return data as XML, not output data to stdout, ignore parameters in incoming request
- @return
- data as XML string
- */
- public function render(){
- if (!$this->init_flag){
- $this->init_flag=true;
- return "";
- }
- $res = $this->sql->get_variants($this->config->text[0]["db_name"],$this->request);
- return $this->render_set($res);
- }
-}
-
-
-
-class TreeCommonDataItem extends CommonDataItem{
- protected $kids=-1;
-
- function to_xml_start(){
- $str="<item id='".$this->get_id()."' ";
- for ($i=0; $i < sizeof($this->config->text); $i++){
- $name=$this->config->text[$i]["name"];
- $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'";
- }
-
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $str.=" ".$key."='".$this->xmlentities($value)."'";
-
- if ($this->kids === true)
- $str .=" dhx_kids='1'";
-
- return $str.">";
- }
-
- function has_kids(){
- return $this->kids;
- }
-
- function set_kids($value){
- $this->kids=$value;
- }
-}
-
-
-class TreeDataConnector extends DataConnector{
- protected $parent_name = 'parent';
-
- /*! constructor
-
- Here initilization of all Masters occurs, execution timer initialized
- @param res
- db connection resource
- @param type
- string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
- @param item_type
- name of class, which will be used for item rendering, optional, DataItem will be used by default
- @param data_type
- name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
- * @param render_type
- * name of class which will provides data rendering
- */
- public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){
- if (!$item_type) $item_type="TreeCommonDataItem";
- if (!$data_type) $data_type="CommonDataProcessor";
- if (!$render_type) $render_type="TreeRenderStrategy";
- parent::__construct($res,$type,$item_type,$data_type,$render_type);
- }
-
- //parse GET scoope, all operations with incoming request must be done here
- protected function parse_request(){
- parent::parse_request();
-
- if (isset($_GET[$this->parent_name]))
- $this->request->set_relation($_GET[$this->parent_name]);
- else
- $this->request->set_relation("0");
-
- $this->request->set_limit(0,0); //netralize default reaction on dyn. loading mode
- }
-
- /*! renders self as xml, starting part
- */
- protected function xml_start(){
- return "<data parent='".$this->request->get_relation()."'>";
- }
-}
-
-
-class JSONTreeDataConnector extends TreeDataConnector{
-
- public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type){
- if (!$item_type) $item_type="JSONTreeCommonDataItem";
- if (!$data_type) $data_type="CommonDataProcessor";
- if (!$render_type) $render_type="JSONTreeRenderStrategy";
- parent::__construct($res,$type,$item_type,$data_type,$render_type);
- }
-
- protected function output_as_xml($res){
- $data = array();
- $data["parent"] = $this->request->get_relation();
- $data["data"] = $this->render_set($res);
- $out = new OutputWriter(json_encode($data), "");
- $out->set_type("json");
- $this->event->trigger("beforeOutput", $this, $out);
- $out->output("", true, $this->encoding);
- }
-
-}
-
-
-class JSONTreeCommonDataItem extends TreeCommonDataItem{
- /*! return self as XML string
- */
- function to_xml_start(){
- if ($this->skip) return "";
-
- $data = array( "id" => $this->get_id() );
- for ($i=0; $i<sizeof($this->config->text); $i++){
- $extra = $this->config->text[$i]["name"];
- $data[$extra]=$this->data[$extra];
- }
-
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $data["key"]=$value;
-
- if ($this->kids === true)
- $data["dhx_kids"] = 1;
-
- return $data;
- }
-
- function to_xml_end(){
- return "";
- }
-}
-
-
-?>
-
+<?php +/* + @author dhtmlx.com + @license GPL, see license.txt +*/ +require_once("base_connector.php"); + +class CommonDataProcessor extends DataProcessor{ + protected function get_post_values($ids){ + if (isset($_GET['action'])){ + $data = array(); + if (isset($_POST["id"])){ + $dataset = array(); + foreach($_POST as $key=>$value) + $dataset[$key] = ConnectorSecurity::filter($value); + + $data[$_POST["id"]] = $dataset; + } + else + $data["dummy_id"] = $_POST; + return $data; + } + return parent::get_post_values($ids); + } + + protected function get_ids(){ + if (isset($_GET['action'])){ + if (isset($_POST["id"])) + return array($_POST['id']); + else + return array("dummy_id"); + } + return parent::get_ids(); + } + + protected function get_operation($rid){ + if (isset($_GET['action'])) + return $_GET['action']; + return parent::get_operation($rid); + } + + public function output_as_xml($results){ + if (isset($_GET['action'])){ + LogMaster::log("Edit operation finished",$results); + ob_clean(); + $type = $results[0]->get_status(); + if ($type == "error" || $type == "invalid"){ + echo "false"; + } else if ($type=="insert"){ + echo "true\n".$results[0]->get_new_id(); + } else + echo "true"; + } else + return parent::output_as_xml($results); + } +}; + +/*! DataItem class for DataView component +**/ +class CommonDataItem extends DataItem{ + /*! return self as XML string + */ + function to_xml(){ + if ($this->skip) return ""; + return $this->to_xml_start().$this->to_xml_end(); + } + + function to_xml_start(){ + $str="<item id='".$this->get_id()."' "; + for ($i=0; $i < sizeof($this->config->text); $i++){ + $name=$this->config->text[$i]["name"]; + $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'"; + } + + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $str.=" ".$key."='".$this->xmlentities($value)."'"; + + return $str.">"; + } +} + + +/*! Connector class for DataView +**/ +class DataConnector extends Connector{ + + /*! constructor + + Here initilization of all Masters occurs, execution timer initialized + @param res + db connection resource + @param type + string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided + @param item_type + name of class, which will be used for item rendering, optional, DataItem will be used by default + @param data_type + name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default. + */ + public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){ + if (!$item_type) $item_type="CommonDataItem"; + if (!$data_type) $data_type="CommonDataProcessor"; + + $this->sections = array(); + $this->attributes = array(); + + if (!$render_type) $render_type="RenderStrategy"; + parent::__construct($res,$type,$item_type,$data_type,$render_type); + + } + + protected $sections; + public function add_section($name, $string){ + $this->sections[$name] = $string; + } + + protected $attributes; + public function add_top_attribute($name, $string){ + $this->attributes[$name] = $string; + } + + protected function parse_request_mode(){ + //do nothing, at least for now + } + + //parse GET scoope, all operations with incoming request must be done here + protected function parse_request(){ + if (isset($_GET['action'])){ + $action = $_GET['action']; + //simple request mode + if ($action == "get"){ + //data request + if (isset($_GET['id'])){ + //single entity data request + $this->request->set_filter($this->config->id["name"],$_GET['id'],"="); + } else { + //loading collection of items + } + } else { + //data saving + $this->editing = true; + } + } else { + if (isset($_GET['editing']) && isset($_POST['ids'])) + $this->editing = true; + + parent::parse_request(); + } + + if (isset($_GET["start"]) && isset($_GET["count"])) + $this->request->set_limit($_GET["start"],$_GET["count"]); + + $key = ConnectorSecurity::checkCSRF($this->editing); + if ($key !== "") + $this->add_top_attribute("dhx_security", $key); + } + + /*! renders self as xml, starting part + */ + protected function xml_start(){ + $start = "<data"; + foreach($this->attributes as $k=>$v) + $start .= " ".$k."='".$v."'"; + $start.= ">"; + + foreach($this->sections as $k=>$v) + $start .= "<".$k.">".$v."</".$k.">\n"; + return $start; + } + +}; + +class JSONDataConnector extends DataConnector{ + + public function __construct($res,$type=false,$item_type=false,$data_type=false){ + if (!$item_type) $item_type="JSONCommonDataItem"; + if (!$data_type) $data_type="CommonDataProcessor"; + $this->data_separator = ",\n"; + parent::__construct($res,$type,$item_type,$data_type); + } + + /*! assign options collection to the column + + @param name + name of the column + @param options + array or connector object + */ + public function set_options($name,$options){ + if (is_array($options)){ + $str=array(); + foreach($options as $k => $v) + $str[]='{"id":"'.$this->xmlentities($k).'", "value":"'.$this->xmlentities($v).'"}'; + $options=implode(",",$str); + } + $this->options[$name]=$options; + } + + /*! generates xml description for options collections + + @param list + comma separated list of column names, for which options need to be generated + */ + protected function fill_collections(){ + $options = array(); + foreach ($this->options as $k=>$v) { + $name = $k; + $option="\"{$name}\":["; + if (!is_string($this->options[$name])) + $option.=substr($this->options[$name]->render(),0,-2); + else + $option.=$this->options[$name]; + $option.="]"; + $options[] = $option; + } + $this->extra_output .= implode($this->data_separator, $options); + } + + protected function resolve_parameter($name){ + if (intval($name).""==$name) + return $this->config->text[intval($name)]["db_name"]; + return $name; + } + + protected function output_as_xml($res){ + $start = ""; + $end = "{ \"data\":[\n".substr($this->render_set($res),0,-2)."\n]"; + + $collections = $this->fill_collections(); + if (!empty($this->extra_output)) + $end .= ', "collections": {'.$this->extra_output.'}'; + + + $is_sections = sizeof($this->sections) && $this->is_first_call(); + if ($this->dload || $is_sections){ + $start = $start.$end; + $end=""; + + if ($is_sections){ + //extra sections + foreach($this->sections as $k=>$v) + $end.= ", ".$k.":".$v; + } + + if ($this->dload){ + //info for dyn. loadin + if ($pos=$this->request->get_start()) + $end .= ", \"pos\":".$pos; + else + $end .= ", \"pos\":0, \"total_count\":".$this->sql->get_size($this->request); + } + } + $end .= " }"; + $out = new OutputWriter($start, $end); + $out->set_type("json"); + $this->event->trigger("beforeOutput", $this, $out); + $out->output("", true, $this->encoding); + } +} + +class JSONCommonDataItem extends DataItem{ + /*! return self as XML string + */ + function to_xml(){ + if ($this->skip) return ""; + + $data = array(); + for ($i=0; $i<sizeof($this->config->text); $i++){ + $extra = $this->config->text[$i]["name"]; + $data[$extra]=$this->data[$extra]; + } + + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $data["key"]=$value; + + return json_encode($data); + } +} + + +/*! wrapper around options collection, used for comboboxes and filters +**/ +class JSONOptionsConnector extends JSONDataConnector{ + protected $init_flag=false;//!< used to prevent rendering while initialization + public function __construct($res,$type=false,$item_type=false,$data_type=false){ + if (!$item_type) $item_type="JSONCommonDataItem"; + if (!$data_type) $data_type=""; //has not sense, options not editable + parent::__construct($res,$type,$item_type,$data_type); + } + /*! render self + process commands, return data as XML, not output data to stdout, ignore parameters in incoming request + @return + data as XML string + */ + public function render(){ + if (!$this->init_flag){ + $this->init_flag=true; + return ""; + } + $res = $this->sql->select($this->request); + return $this->render_set($res); + } +} + + +class JSONDistinctOptionsConnector extends JSONOptionsConnector{ + /*! render self + process commands, return data as XML, not output data to stdout, ignore parameters in incoming request + @return + data as XML string + */ + public function render(){ + if (!$this->init_flag){ + $this->init_flag=true; + return ""; + } + $res = $this->sql->get_variants($this->config->text[0]["db_name"],$this->request); + return $this->render_set($res); + } +} + + + +class TreeCommonDataItem extends CommonDataItem{ + protected $kids=-1; + + function to_xml_start(){ + $str="<item id='".$this->get_id()."' "; + for ($i=0; $i < sizeof($this->config->text); $i++){ + $name=$this->config->text[$i]["name"]; + $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'"; + } + + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $str.=" ".$key."='".$this->xmlentities($value)."'"; + + if ($this->kids === true) + $str .=" dhx_kids='1'"; + + return $str.">"; + } + + function has_kids(){ + return $this->kids; + } + + function set_kids($value){ + $this->kids=$value; + } +} + + +class TreeDataConnector extends DataConnector{ + protected $parent_name = 'parent'; + + /*! constructor + + Here initilization of all Masters occurs, execution timer initialized + @param res + db connection resource + @param type + string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided + @param item_type + name of class, which will be used for item rendering, optional, DataItem will be used by default + @param data_type + name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default. + * @param render_type + * name of class which will provides data rendering + */ + public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){ + if (!$item_type) $item_type="TreeCommonDataItem"; + if (!$data_type) $data_type="CommonDataProcessor"; + if (!$render_type) $render_type="TreeRenderStrategy"; + parent::__construct($res,$type,$item_type,$data_type,$render_type); + } + + //parse GET scoope, all operations with incoming request must be done here + protected function parse_request(){ + parent::parse_request(); + + if (isset($_GET[$this->parent_name])) + $this->request->set_relation($_GET[$this->parent_name]); + else + $this->request->set_relation("0"); + + $this->request->set_limit(0,0); //netralize default reaction on dyn. loading mode + } + + /*! renders self as xml, starting part + */ + protected function xml_start(){ + return "<data parent='".$this->request->get_relation()."'>"; + } +} + + +class JSONTreeDataConnector extends TreeDataConnector{ + + public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type){ + if (!$item_type) $item_type="JSONTreeCommonDataItem"; + if (!$data_type) $data_type="CommonDataProcessor"; + if (!$render_type) $render_type="JSONTreeRenderStrategy"; + parent::__construct($res,$type,$item_type,$data_type,$render_type); + } + + protected function output_as_xml($res){ + $data = array(); + $data["parent"] = $this->request->get_relation(); + $data["data"] = $this->render_set($res); + $out = new OutputWriter(json_encode($data), ""); + $out->set_type("json"); + $this->event->trigger("beforeOutput", $this, $out); + $out->output("", true, $this->encoding); + } + +} + + +class JSONTreeCommonDataItem extends TreeCommonDataItem{ + /*! return self as XML string + */ + function to_xml_start(){ + if ($this->skip) return ""; + + $data = array( "id" => $this->get_id() ); + for ($i=0; $i<sizeof($this->config->text); $i++){ + $extra = $this->config->text[$i]["name"]; + $data[$extra]=$this->data[$extra]; + } + + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $data["key"]=$value; + + if ($this->kids === true) + $data["dhx_kids"] = 1; + + return $data; + } + + function to_xml_end(){ + return ""; + } +} + + +?>
\ No newline at end of file diff --git a/codebase/grid_connector.php b/codebase/grid_connector.php index f821df3..6f41467 100644 --- a/codebase/grid_connector.php +++ b/codebase/grid_connector.php @@ -1,264 +1,263 @@ -<?php
-/*
- @author dhtmlx.com
- @license GPL, see license.txt
-*/
-require_once("base_connector.php");
-require_once("grid_config.php");
-
-//require_once("grid_dataprocessor.php");
-
-/*! DataItem class for Grid component
-**/
-
-class GridDataItem extends DataItem{
- protected $row_attrs;//!< hash of row attributes
- protected $cell_attrs;//!< hash of cell attributes
-
- function __construct($data,$name,$index=0){
- parent::__construct($data,$name,$index);
-
- $this->row_attrs=array();
- $this->cell_attrs=array();
- }
- /*! set color of row
-
- @param color
- color of row
- */
- function set_row_color($color){
- $this->row_attrs["bgColor"]=$color;
- }
- /*! set style of row
-
- @param color
- color of row
- */
- function set_row_style($color){
- $this->row_attrs["style"]=$color;
- }
- /*! assign custom style to the cell
-
- @param name
- name of column
- @param value
- css style string
- */
- function set_cell_style($name,$value){
- $this->set_cell_attribute($name,"style",$value);
- }
- /*! assign custom class to specific cell
-
- @param name
- name of column
- @param value
- css class name
- */
- function set_cell_class($name,$value){
- $this->set_cell_attribute($name,"class",$value);
- }
- /*! set custom cell attribute
-
- @param name
- name of column
- @param attr
- name of attribute
- @param value
- value of attribute
- */
- function set_cell_attribute($name,$attr,$value){
- if (!array_key_exists($name, $this->cell_attrs)) $this->cell_attrs[$name]=array();
- $this->cell_attrs[$name][$attr]=$value;
- }
-
- /*! set custom row attribute
-
- @param attr
- name of attribute
- @param value
- value of attribute
- */
- function set_row_attribute($attr,$value){
- $this->row_attrs[$attr]=$value;
- }
-
- /*! return self as XML string, starting part
- */
- public function to_xml_start(){
- if ($this->skip) return "";
-
- $str="<row id='".$this->get_id()."'";
- foreach ($this->row_attrs as $k=>$v)
- $str.=" ".$k."='".$v."'";
- $str.=">";
- for ($i=0; $i < sizeof($this->config->text); $i++){
- $str.="<cell";
- $name=$this->config->text[$i]["name"];
- if (isset($this->cell_attrs[$name])){
- $cattrs=$this->cell_attrs[$name];
- foreach ($cattrs as $k => $v)
- $str.=" ".$k."='".$this->xmlentities($v)."'";
- }
- $value = isset($this->data[$name]) ? $this->data[$name] : '';
- $str.="><![CDATA[".$value."]]></cell>";
- }
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $str.="<userdata name='".$key."'><![CDATA[".$value."]]></userdata>";
-
- return $str;
- }
- /*! return self as XML string, ending part
- */
- public function to_xml_end(){
- if ($this->skip) return "";
-
- return "</row>";
- }
-}
-/*! Connector for the dhtmlxgrid
-**/
-class GridConnector extends Connector{
- protected $extra_output="";//!< extra info which need to be sent to client side
- private $options=array();//!< hash of OptionsConnector
-
- /*! constructor
-
- Here initilization of all Masters occurs, execution timer initialized
- @param res
- db connection resource
- @param type
- string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
- @param item_type
- name of class, which will be used for item rendering, optional, DataItem will be used by default
- @param data_type
- name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
- */
- public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){
- if (!$item_type) $item_type="GridDataItem";
- if (!$data_type) $data_type="GridDataProcessor";
- if (!$render_type) $render_type="RenderStrategy";
- parent::__construct($res,$type,$item_type,$data_type,$render_type);
- }
-
-
- protected function parse_request(){
- parent::parse_request();
-
- if (isset($_GET["dhx_colls"]))
- $this->fill_collections($_GET["dhx_colls"]);
-
- if (isset($_GET["posStart"]) && isset($_GET["count"]))
- $this->request->set_limit($_GET["posStart"],$_GET["count"]);
- }
- protected function resolve_parameter($name){
- if (intval($name).""==$name)
- return $this->config->text[intval($name)]["db_name"];
- return $name;
- }
-
- /*! replace xml unsafe characters
-
- @param string
- string to be escaped
- @return
- escaped string
- */
- protected function xmlentities($string) {
- return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&' , '"', ''' , '<' , '>', ''' ), $string);
- }
-
- /*! assign options collection to the column
-
- @param name
- name of the column
- @param options
- array or connector object
- */
- public function set_options($name,$options){
- if (is_array($options)){
- $str="";
- foreach($options as $k => $v)
- $str.="<item value='".$this->xmlentities($k)."' label='".$this->xmlentities($v)."' />";
- $options=$str;
- }
- $this->options[$name]=$options;
- }
- /*! generates xml description for options collections
-
- @param list
- comma separated list of column names, for which options need to be generated
- */
- protected function fill_collections($list){
- $names=explode(",",$list);
- for ($i=0; $i < sizeof($names); $i++) {
- $name = $this->resolve_parameter($names[$i]);
- if (!array_key_exists($name,$this->options)){
- $this->options[$name] = new DistinctOptionsConnector($this->get_connection(),$this->names["db_class"]);
- $c = new DataConfig($this->config);
- $r = new DataRequestConfig($this->request);
- $c->minimize($name);
-
- $this->options[$name]->render_connector($c,$r);
- }
-
- $this->extra_output.="<coll_options for='{$names[$i]}'>";
- if (!is_string($this->options[$name]))
- $this->extra_output.=$this->options[$name]->render();
- else
- $this->extra_output.=$this->options[$name];
- $this->extra_output.="</coll_options>";
- }
- }
-
- /*! renders self as xml, starting part
- */
- protected function xml_start(){
- if ($this->dload){
- if ($pos=$this->request->get_start())
- return "<rows pos='".$pos."'>";
- else
- return "<rows total_count='".$this->sql->get_size($this->request)."'>";
- }
- else
- return "<rows>";
- }
-
-
- /*! renders self as xml, ending part
- */
- protected function xml_end(){
- return $this->extra_output."</rows>";
- }
-
- public function set_config($config = false){
- if (gettype($config) == 'boolean')
- $config = new GridConfiguration($config);
-
- $this->event->attach("beforeOutput", Array($config, "attachHeaderToXML"));
- $this->event->attach("onInit", Array($config, "defineOptions"));
- }
-}
-
-/*! DataProcessor class for Grid component
-**/
-class GridDataProcessor extends DataProcessor{
-
- /*! convert incoming data name to valid db name
- converts c0..cN to valid field names
- @param data
- data name from incoming request
- @return
- related db_name
- */
- function name_data($data){
- if ($data == "gr_id") return $this->config->id["name"];
- $parts=explode("c",$data);
- if ($parts[0]=="" && ((string)intval($parts[1]))==$parts[1])
- if (sizeof($this->config->text)>intval($parts[1]))
- return $this->config->text[intval($parts[1])]["name"];
- return $data;
- }
-}
-
+<?php +/* + @author dhtmlx.com + @license GPL, see license.txt +*/ +require_once("base_connector.php"); +require_once("grid_config.php"); + +//require_once("grid_dataprocessor.php"); + +/*! DataItem class for Grid component +**/ + +class GridDataItem extends DataItem{ + protected $row_attrs;//!< hash of row attributes + protected $cell_attrs;//!< hash of cell attributes + + function __construct($data,$name,$index=0){ + parent::__construct($data,$name,$index); + + $this->row_attrs=array(); + $this->cell_attrs=array(); + } + /*! set color of row + + @param color + color of row + */ + function set_row_color($color){ + $this->row_attrs["bgColor"]=$color; + } + /*! set style of row + + @param color + color of row + */ + function set_row_style($color){ + $this->row_attrs["style"]=$color; + } + /*! assign custom style to the cell + + @param name + name of column + @param value + css style string + */ + function set_cell_style($name,$value){ + $this->set_cell_attribute($name,"style",$value); + } + /*! assign custom class to specific cell + + @param name + name of column + @param value + css class name + */ + function set_cell_class($name,$value){ + $this->set_cell_attribute($name,"class",$value); + } + /*! set custom cell attribute + + @param name + name of column + @param attr + name of attribute + @param value + value of attribute + */ + function set_cell_attribute($name,$attr,$value){ + if (!array_key_exists($name, $this->cell_attrs)) $this->cell_attrs[$name]=array(); + $this->cell_attrs[$name][$attr]=$value; + } + + /*! set custom row attribute + + @param attr + name of attribute + @param value + value of attribute + */ + function set_row_attribute($attr,$value){ + $this->row_attrs[$attr]=$value; + } + + /*! return self as XML string, starting part + */ + public function to_xml_start(){ + if ($this->skip) return ""; + + $str="<row id='".$this->get_id()."'"; + foreach ($this->row_attrs as $k=>$v) + $str.=" ".$k."='".$v."'"; + $str.=">"; + for ($i=0; $i < sizeof($this->config->text); $i++){ + $str.="<cell"; + $name=$this->config->text[$i]["name"]; + if (isset($this->cell_attrs[$name])){ + $cattrs=$this->cell_attrs[$name]; + foreach ($cattrs as $k => $v) + $str.=" ".$k."='".$this->xmlentities($v)."'"; + } + $value = isset($this->data[$name]) ? $this->data[$name] : ''; + $str.="><![CDATA[".$value."]]></cell>"; + } + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $str.="<userdata name='".$key."'><![CDATA[".$value."]]></userdata>"; + + return $str; + } + /*! return self as XML string, ending part + */ + public function to_xml_end(){ + if ($this->skip) return ""; + + return "</row>"; + } +} +/*! Connector for the dhtmlxgrid +**/ +class GridConnector extends Connector{ + protected $extra_output="";//!< extra info which need to be sent to client side + protected $options=array();//!< hash of OptionsConnector + + /*! constructor + + Here initilization of all Masters occurs, execution timer initialized + @param res + db connection resource + @param type + string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided + @param item_type + name of class, which will be used for item rendering, optional, DataItem will be used by default + @param data_type + name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default. + */ + public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){ + if (!$item_type) $item_type="GridDataItem"; + if (!$data_type) $data_type="GridDataProcessor"; + if (!$render_type) $render_type="RenderStrategy"; + parent::__construct($res,$type,$item_type,$data_type,$render_type); + } + + + protected function parse_request(){ + parent::parse_request(); + + if (isset($_GET["dhx_colls"])) + $this->fill_collections($_GET["dhx_colls"]); + + if (isset($_GET["posStart"]) && isset($_GET["count"])) + $this->request->set_limit($_GET["posStart"],$_GET["count"]); + } + protected function resolve_parameter($name){ + if (intval($name).""==$name) + return $this->config->text[intval($name)]["db_name"]; + return $name; + } + + /*! replace xml unsafe characters + + @param string + string to be escaped + @return + escaped string + */ + protected function xmlentities($string) { + return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&' , '"', ''' , '<' , '>', ''' ), $string); + } + + /*! assign options collection to the column + + @param name + name of the column + @param options + array or connector object + */ + public function set_options($name,$options){ + if (is_array($options)){ + $str=""; + foreach($options as $k => $v) + $str.="<item value='".$this->xmlentities($k)."' label='".$this->xmlentities($v)."' />"; + $options=$str; + } + $this->options[$name]=$options; + } + /*! generates xml description for options collections + + @param list + comma separated list of column names, for which options need to be generated + */ + protected function fill_collections($list){ + $names=explode(",",$list); + for ($i=0; $i < sizeof($names); $i++) { + $name = $this->resolve_parameter($names[$i]); + if (!array_key_exists($name,$this->options)){ + $this->options[$name] = new DistinctOptionsConnector($this->get_connection(),$this->names["db_class"]); + $c = new DataConfig($this->config); + $r = new DataRequestConfig($this->request); + $c->minimize($name); + + $this->options[$name]->render_connector($c,$r); + } + + $this->extra_output.="<coll_options for='{$names[$i]}'>"; + if (!is_string($this->options[$name])) + $this->extra_output.=$this->options[$name]->render(); + else + $this->extra_output.=$this->options[$name]; + $this->extra_output.="</coll_options>"; + } + } + + /*! renders self as xml, starting part + */ + protected function xml_start(){ + if ($this->dload){ + if ($pos=$this->request->get_start()) + return "<rows pos='".$pos."'>"; + else + return "<rows total_count='".$this->sql->get_size($this->request)."'>"; + } + else + return "<rows>"; + } + + + /*! renders self as xml, ending part + */ + protected function xml_end(){ + return $this->extra_output."</rows>"; + } + + public function set_config($config = false){ + if (gettype($config) == 'boolean') + $config = new GridConfiguration($config); + + $this->event->attach("beforeOutput", Array($config, "attachHeaderToXML")); + $this->event->attach("onInit", Array($config, "defineOptions")); + } +} + +/*! DataProcessor class for Grid component +**/ +class GridDataProcessor extends DataProcessor{ + + /*! convert incoming data name to valid db name + converts c0..cN to valid field names + @param data + data name from incoming request + @return + related db_name + */ + function name_data($data){ + if ($data == "gr_id") return $this->config->id["name"]; + $parts=explode("c",$data); + if ($parts[0]=="" && ((string)intval($parts[1]))==$parts[1]) + if (sizeof($this->config->text)>intval($parts[1])) + return $this->config->text[intval($parts[1])]["name"]; + return $data; + } +} ?>
\ No newline at end of file diff --git a/codebase/scheduler_connector.php b/codebase/scheduler_connector.php index dbf2a4f..7032fd8 100644 --- a/codebase/scheduler_connector.php +++ b/codebase/scheduler_connector.php @@ -1,250 +1,225 @@ -<?php
-/*
- @author dhtmlx.com
- @license GPL, see license.txt
-*/
-require_once("base_connector.php");
-require_once("data_connector.php");
-
-/*! DataItem class for Scheduler component
-**/
-class SchedulerDataItem extends DataItem{
- /*! return self as XML string
- */
- function to_xml(){
- if ($this->skip) return "";
-
- $str="<event id='".$this->get_id()."' >";
- $str.="<start_date><![CDATA[".$this->data[$this->config->text[0]["name"]]."]]></start_date>";
- $str.="<end_date><![CDATA[".$this->data[$this->config->text[1]["name"]]."]]></end_date>";
- $str.="<text><![CDATA[".$this->data[$this->config->text[2]["name"]]."]]></text>";
- for ($i=3; $i<sizeof($this->config->text); $i++){
- $extra = $this->config->text[$i]["name"];
- $str.="<".$extra."><![CDATA[".$this->data[$extra]."]]></".$extra.">";
- }
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $str.="<".$key."><![CDATA[".$value."]]></".$key.">";
-
- return $str."</event>";
- }
-}
-
-
-/*! Connector class for dhtmlxScheduler
-**/
-class SchedulerConnector extends Connector{
-
- protected $extra_output="";//!< extra info which need to be sent to client side
- protected $options=array();//!< hash of OptionsConnector
-
-
- /*! assign options collection to the column
-
- @param name
- name of the column
- @param options
- array or connector object
- */
- public function set_options($name,$options){
- if (is_array($options)){
- $str="";
- foreach($options as $k => $v)
- $str.="<item value='".$this->xmlentities($k)."' label='".$this->xmlentities($v)."' />";
- $options=$str;
- }
- $this->options[$name]=$options;
- }
- /*! generates xml description for options collections
-
- @param list
- comma separated list of column names, for which options need to be generated
- */
- protected function fill_collections(){
- foreach ($this->options as $k=>$v) {
- $name = $k;
- $this->extra_output.="<coll_options for='{$name}'>";
- if (!is_string($this->options[$name]))
- $this->extra_output.=$this->options[$name]->render();
- else
- $this->extra_output.=$this->options[$name];
- $this->extra_output.="</coll_options>";
- }
- }
-
- /*! renders self as xml, ending part
- */
- protected function xml_end(){
- $this->fill_collections();
- return $this->extra_output."</data>";
- }
-
-
- /*! constructor
-
- Here initilization of all Masters occurs, execution timer initialized
- @param res
- db connection resource
- @param type
- string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
- @param item_type
- name of class, which will be used for item rendering, optional, DataItem will be used by default
- @param data_type
- name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
- * @param render_type
- name of class which will be used for rendering.
- */
- public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){
- if (!$item_type) $item_type="SchedulerDataItem";
- if (!$data_type) $data_type="SchedulerDataProcessor";
- if (!$render_type) $render_type="RenderStrategy";
- parent::__construct($res,$type,$item_type,$data_type,$render_type);
- }
-
- //parse GET scoope, all operations with incoming request must be done here
- function parse_request(){
- parent::parse_request();
- if (count($this->config->text)){
- if (isset($_GET["to"]))
- $this->request->set_filter($this->config->text[0]["name"],$_GET["to"],"<");
- if (isset($_GET["from"]))
- $this->request->set_filter($this->config->text[1]["name"],$_GET["from"],">");
- }
- }
-}
-
-/*! DataProcessor class for Scheduler component
-**/
-class SchedulerDataProcessor extends DataProcessor{
- function name_data($data){
- if ($data=="start_date")
- return $this->config->text[0]["db_name"];
- if ($data=="id")
- return $this->config->id["db_name"];
- if ($data=="end_date")
- return $this->config->text[1]["db_name"];
- if ($data=="text")
- return $this->config->text[2]["db_name"];
-
- return $data;
- }
-}
-
-
-class JSONSchedulerDataItem extends SchedulerDataItem{
- /*! return self as XML string
- */
- function to_xml(){
- if ($this->skip) return "";
-
- $obj = array();
- $obj['id'] = $this->get_id();
- $obj['start_date'] = $this->data[$this->config->text[0]["name"]];
- $obj['end_date'] = $this->data[$this->config->text[1]["name"]];
- $obj['text'] = $this->data[$this->config->text[2]["name"]];
- for ($i=3; $i<sizeof($this->config->text); $i++){
- $extra = $this->config->text[$i]["name"];
- $obj[$extra]=$this->data[$extra];
- }
-
- if ($this->userdata !== false)
- foreach ($this->userdata as $key => $value)
- $obj[$key]=$value;
-
- return $obj;
- }
-}
-
-
-class JSONSchedulerConnector extends SchedulerConnector {
-
- protected $data_separator = ",";
-
- /*! constructor
-
- Here initilization of all Masters occurs, execution timer initialized
- @param res
- db connection resource
- @param type
- string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
- @param item_type
- name of class, which will be used for item rendering, optional, DataItem will be used by default
- @param data_type
- name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
- */
- public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){
- if (!$item_type) $item_type="JSONSchedulerDataItem";
- if (!$data_type) $data_type="SchedulerDataProcessor";
- if (!$render_type) $render_type="JSONRenderStrategy";
- parent::__construct($res,$type,$item_type,$data_type,$render_type);
- }
-
- protected function xml_start() {
- return '{ "data":';
- }
-
- protected function xml_end() {
- $this->fill_collections();
- if (empty($this->extra_output))
- return ' }';
- else
- return ', "collections": {'.$this->extra_output.'} }';
- }
-
-
- /*! assign options collection to the column
-
- @param name
- name of the column
- @param options
- array or connector object
- */
- public function set_options($name,$options){
- if (is_array($options)){
- $str=array();
- foreach($options as $k => $v)
- $str[]='{"id":"'.$this->xmlentities($k).'", "value":"'.$this->xmlentities($v).'"}';
- $options=implode(",",$str);
- }
- $this->options[$name]=$options;
- }
-
-
- /*! generates xml description for options collections
-
- @param list
- comma separated list of column names, for which options need to be generated
- */
- protected function fill_collections(){
- $options = array();
- foreach ($this->options as $k=>$v) {
- $name = $k;
- $option="\"{$name}\":[";
- if (!is_string($this->options[$name]))
- $option.=substr($this->options[$name]->render(),0,-2);
- else
- $option.=$this->options[$name];
- $option.="]";
- $options[] = $option;
- }
- $this->extra_output .= implode($this->data_separator, $options);
- }
-
-
- /*! output fetched data as XML
- @param res
- DB resultset
- */
- protected function output_as_xml($res){
- $data=$this->xml_start();
- $data.=$this->render_set($res);
- $data.=$this->xml_end();
-
- $out = new OutputWriter($data, "");
- $out->set_type("json");
- $this->event->trigger("beforeOutput", $this, $out);
- $out->output("", true, $this->encoding);
- }
-}
-
+<?php +/* + @author dhtmlx.com + @license GPL, see license.txt +*/ +require_once("base_connector.php"); +require_once("data_connector.php"); + +/*! DataItem class for Scheduler component +**/ +class SchedulerDataItem extends DataItem{ + /*! return self as XML string + */ + function to_xml(){ + if ($this->skip) return ""; + + $str="<event id='".$this->get_id()."' >"; + $str.="<start_date><![CDATA[".$this->data[$this->config->text[0]["name"]]."]]></start_date>"; + $str.="<end_date><![CDATA[".$this->data[$this->config->text[1]["name"]]."]]></end_date>"; + $str.="<text><![CDATA[".$this->data[$this->config->text[2]["name"]]."]]></text>"; + for ($i=3; $i<sizeof($this->config->text); $i++){ + $extra = $this->config->text[$i]["name"]; + $str.="<".$extra."><![CDATA[".$this->data[$extra]."]]></".$extra.">"; + } + + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $str.="<".$key."><![CDATA[".$value."]]></".$key.">"; + + return $str."</event>"; + } +} + + +/*! Connector class for dhtmlxScheduler +**/ +class SchedulerConnector extends Connector{ + + protected $extra_output="";//!< extra info which need to be sent to client side + protected $options=array();//!< hash of OptionsConnector + + + /*! assign options collection to the column + + @param name + name of the column + @param options + array or connector object + */ + public function set_options($name,$options){ + if (is_array($options)){ + $str=""; + foreach($options as $k => $v) + $str.="<item value='".$this->xmlentities($k)."' label='".$this->xmlentities($v)."' />"; + $options=$str; + } + $this->options[$name]=$options; + } + + + /*! constructor + + Here initilization of all Masters occurs, execution timer initialized + @param res + db connection resource + @param type + string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided + @param item_type + name of class, which will be used for item rendering, optional, DataItem will be used by default + @param data_type + name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default. + * @param render_type + name of class which will be used for rendering. + */ + public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){ + if (!$item_type) $item_type="SchedulerDataItem"; + if (!$data_type) $data_type="SchedulerDataProcessor"; + if (!$render_type) $render_type="RenderStrategy"; + parent::__construct($res,$type,$item_type,$data_type,$render_type); + } + + //parse GET scoope, all operations with incoming request must be done here + function parse_request(){ + parent::parse_request(); + if (count($this->config->text)){ + if (isset($_GET["to"])) + $this->request->set_filter($this->config->text[0]["name"],$_GET["to"],"<"); + if (isset($_GET["from"])) + $this->request->set_filter($this->config->text[1]["name"],$_GET["from"],">"); + } + } +} + +/*! DataProcessor class for Scheduler component +**/ +class SchedulerDataProcessor extends DataProcessor{ + function name_data($data){ + if ($data=="start_date") + return $this->config->text[0]["db_name"]; + if ($data=="id") + return $this->config->id["db_name"]; + if ($data=="end_date") + return $this->config->text[1]["db_name"]; + if ($data=="text") + return $this->config->text[2]["db_name"]; + + return $data; + } +} + + +class JSONSchedulerDataItem extends SchedulerDataItem{ + /*! return self as XML string + */ + function to_xml(){ + if ($this->skip) return ""; + + $obj = array(); + $obj['id'] = $this->get_id(); + $obj['start_date'] = $this->data[$this->config->text[0]["name"]]; + $obj['end_date'] = $this->data[$this->config->text[1]["name"]]; + $obj['text'] = $this->data[$this->config->text[2]["name"]]; + for ($i=3; $i<sizeof($this->config->text); $i++){ + $extra = $this->config->text[$i]["name"]; + $obj[$extra]=$this->data[$extra]; + } + + if ($this->userdata !== false) + foreach ($this->userdata as $key => $value) + $obj[$key]=$value; + + return $obj; + } +} + + +class JSONSchedulerConnector extends SchedulerConnector { + + protected $data_separator = ","; + + /*! constructor + + Here initilization of all Masters occurs, execution timer initialized + @param res + db connection resource + @param type + string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided + @param item_type + name of class, which will be used for item rendering, optional, DataItem will be used by default + @param data_type + name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default. + */ + public function __construct($res,$type=false,$item_type=false,$data_type=false,$render_type=false){ + if (!$item_type) $item_type="JSONSchedulerDataItem"; + if (!$data_type) $data_type="SchedulerDataProcessor"; + if (!$render_type) $render_type="JSONRenderStrategy"; + parent::__construct($res,$type,$item_type,$data_type,$render_type); + } + + protected function xml_start() { + return '{ "data":'; + } + + protected function xml_end() { + $this->fill_collections(); + $end = (!empty($this->extra_output)) ? ', "collections": {'.$this->extra_output.'}' : ''; + $end .= '}'; + return $end; + } + + /*! assign options collection to the column + + @param name + name of the column + @param options + array or connector object + */ + public function set_options($name,$options){ + if (is_array($options)){ + $str=array(); + foreach($options as $k => $v) + $str[]='{"id":"'.$this->xmlentities($k).'", "value":"'.$this->xmlentities($v).'"}'; + $options=implode(",",$str); + } + $this->options[$name]=$options; + } + + + /*! generates xml description for options collections + + @param list + comma separated list of column names, for which options need to be generated + */ + protected function fill_collections(){ + $options = array(); + foreach ($this->options as $k=>$v) { + $name = $k; + $option="\"{$name}\":["; + if (!is_string($this->options[$name])) + $option.=substr($this->options[$name]->render(),0,-2); + else + $option.=$this->options[$name]; + $option.="]"; + $options[] = $option; + } + $this->extra_output .= implode($this->data_separator, $options); + } + + + /*! output fetched data as XML + @param res + DB resultset + */ + protected function output_as_xml($res){ + $data=$this->xml_start(); + $data.=$this->render_set($res); + $data.=$this->xml_end(); + + $out = new OutputWriter($data, ""); + $out->set_type("json"); + $this->event->trigger("beforeOutput", $this, $out); + $out->output("", true, $this->encoding); + } +} ?>
\ No newline at end of file diff --git a/codebase/strategy.php b/codebase/strategy.php index 5f02cf0..837e394 100644 --- a/codebase/strategy.php +++ b/codebase/strategy.php @@ -13,13 +13,13 @@ class RenderStrategy { DB resultset process commands, output requested data as XML */ - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=""; $index=0; $conn = $this->conn; - $conn->event->trigger("beforeRenderSet",$conn,$res,$conn->get_config()); + $conn->event->trigger("beforeRenderSet",$conn,$res,$config); while ($data=$conn->sql->get_next($res)){ - $data = new $name($data,$conn->get_config(),$index); + $data = new $name($data,$config,$index); if ($data->get_id()===false) $data->set_id($conn->uuid()); $conn->event->trigger("beforeRender",$data); @@ -38,13 +38,13 @@ class JSONRenderStrategy extends RenderStrategy { DB resultset process commands, output requested data as json */ - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=array(); $index=0; $conn = $this->conn; - $conn->event->trigger("beforeRenderSet",$conn,$res,$conn->get_config()); + $conn->event->trigger("beforeRenderSet",$conn,$res,$config); while ($data=$conn->sql->get_next($res)){ - $data = new $name($data,$conn->get_config(),$index); + $data = new $name($data,$config,$index); if ($data->get_id()===false) $data->set_id($conn->uuid()); $conn->event->trigger("beforeRender",$data); @@ -66,12 +66,12 @@ class TreeRenderStrategy extends RenderStrategy { $conn->event->attach("beforeProcessing",array($this,"parent_id_correction_b")); } - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=""; $index=0; $conn = $this->conn; while ($data=$conn->sql->get_next($res)){ - $data = new $name($data,$conn->get_config(),$index); + $data = new $name($data,$config,$index); $conn->event->trigger("beforeRender",$data); //there is no info about child elements, //if we are using dyn. loading - assume that it has, @@ -82,7 +82,7 @@ class TreeRenderStrategy extends RenderStrategy { if ($data->has_kids()===-1 || ( $data->has_kids()==true && !$dload)){ $sub_request = new DataRequestConfig($conn->get_request()); $sub_request->set_relation($data->get_id()); - $output.=$this->render_set($conn->sql->select($sub_request), $name, $dload, $sep); + $output.=$this->render_set($conn->sql->select($sub_request), $name, $dload, $sep, $config); } $output.=$data->to_xml_end(); $index++; @@ -115,12 +115,12 @@ class TreeRenderStrategy extends RenderStrategy { class JSONTreeRenderStrategy extends TreeRenderStrategy { - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=array(); $index=0; $conn = $this->conn; while ($data=$conn->sql->get_next($res)){ - $data = new $name($data,$conn->get_config(),$index); + $data = new $name($data,$config,$index); $conn->event->trigger("beforeRender",$data); //there is no info about child elements, //if we are using dyn. loading - assume that it has, @@ -131,7 +131,7 @@ class JSONTreeRenderStrategy extends TreeRenderStrategy { if ($data->has_kids()===-1 || ( $data->has_kids()==true && !$dload)){ $sub_request = new DataRequestConfig($conn->get_request()); $sub_request->set_relation($data->get_id()); - $temp = $this->render_set($conn->sql->select($sub_request), $name, $dload, $sep); + $temp = $this->render_set($conn->sql->select($sub_request), $name, $dload, $sep, $config); if (sizeof($temp)) $record["data"] = $temp; } @@ -160,11 +160,10 @@ class MultitableTreeRenderStrategy extends TreeRenderStrategy { $this->sep = $sep; } - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=""; $index=0; $conn = $this->conn; - $config = $conn->get_config(); while ($data=$conn->sql->get_next($res)){ $data[$config->id['name']] = $this->level_id($data[$config->id['name']]); $data = new $name($data,$config,$index); @@ -259,11 +258,10 @@ class MultitableTreeRenderStrategy extends TreeRenderStrategy { class JSONMultitableTreeRenderStrategy extends MultitableTreeRenderStrategy { - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=array(); $index=0; $conn = $this->conn; - $config = $conn->get_config(); while ($data=$conn->sql->get_next($res)){ $data[$config->id['name']] = $this->level_id($data[$config->id['name']]); $data = new $name($data,$config,$index); @@ -295,11 +293,10 @@ class GroupRenderStrategy extends RenderStrategy { $conn->event->attach("onInit", Array($this, 'replace_postfix')); } - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=""; $index=0; $conn = $this->conn; - $config = $conn->get_config(); while ($data=$conn->sql->get_next($res)){ if (isset($data[$config->id['name']])) { $has_kids = false; @@ -320,7 +317,7 @@ class GroupRenderStrategy extends RenderStrategy { if (($data->has_kids()===-1 || ( $data->has_kids()==true && !$dload))&&($has_kids == true)){ $sub_request = new DataRequestConfig($conn->get_request()); $sub_request->set_relation(str_replace($this->id_postfix, "", $data->get_id())); - $output.=$this->render_set($conn->sql->select($sub_request), $name, $dload, $sep); + $output.=$this->render_set($conn->sql->select($sub_request), $name, $dload, $sep, $config); } $output.=$data->to_xml_end(); $index++; @@ -362,11 +359,10 @@ class GroupRenderStrategy extends RenderStrategy { class JSONGroupRenderStrategy extends GroupRenderStrategy { - public function render_set($res, $name, $dload, $sep){ + public function render_set($res, $name, $dload, $sep, $config){ $output=array(); $index=0; $conn = $this->conn; - $config = $conn->get_config(); while ($data=$conn->sql->get_next($res)){ if (isset($data[$config->id['name']])) { $has_kids = false; @@ -387,7 +383,7 @@ class JSONGroupRenderStrategy extends GroupRenderStrategy { if (($data->has_kids()===-1 || ( $data->has_kids()==true && !$dload))&&($has_kids == true)){ $sub_request = new DataRequestConfig($conn->get_request()); $sub_request->set_relation(str_replace($this->id_postfix, "", $data->get_id())); - $temp = $this->render_set($conn->sql->select($sub_request), $name, $dload, $sep); + $temp = $this->render_set($conn->sql->select($sub_request), $name, $dload, $sep, $config); if (sizeof($temp)) $record["data"] = $temp; } |