source: branches/2.2.0.1/calendar/js/dhtmlx/codebase/connector/base_connector.php @ 4001

Revision 4001, 19.1 KB checked in by rafaelraymundo, 13 years ago (diff)

Ticket #1615 - Componente novo para agenda......................................

Line 
1<?php
2
3require_once("tools.php");
4require_once("db_common.php");
5require_once("dataprocessor.php");
6require_once("update.php");
7
8//enable buffering to catch and ignore any custom output before XML generation
9//because of this command, it strongly recommended to include connector's file before any other libs
10//in such case it will handle any extra output from not well formed code of other libs
11ini_set("output_buffering","On");
12ob_start();
13
14class OutputWriter{
15        private $start;
16        private $end;
17        private $type;
18       
19        public function __construct($start, $end = ""){
20                $this->start = $start;
21                $this->end = $end;
22                $this->type = "xml";
23        }
24        public function add($add){
25                $this->start.=$add;
26        }
27        public function reset(){
28                $this->start="";
29                $this->end="";
30        }
31        public function set_type($add){
32                $this->type=$add;
33        }
34        public function output($name="", $inline=true){
35                ob_clean();
36                if ($this->type == "xml")
37                        header("Content-type: text/xml");
38                       
39                echo $this->__toString();
40        }
41        public function __toString(){
42                return $this->start.$this->end;
43        }
44}
45
46/*! EventInterface
47        Base class , for iterable collections, which are used in event
48**/
49class EventInterface{
50        protected $request; ////!< DataRequestConfig instance
51        public $rules=array(); //!< array of sorting rules
52       
53        /*! constructor
54                creates a new interface based on existing request
55                @param request
56                        DataRequestConfig object
57        */
58        public function __construct($request){
59                $this->request = $request;
60        }
61
62        /*! remove all elements from collection
63                */     
64        public function clear(){
65                array_splice($rules,0);
66        }
67        /*! get index by name
68               
69                @param name
70                        name of field
71                @return
72                        index of named field
73        */
74        public function index($name){
75                $len = sizeof($this->rules);
76                for ($i=0; $i < $len; $i++) {
77                        if ($this->rules[$i]["name"]==$name)
78                                return $i;
79                }
80                return false;
81        }
82}
83/*! Wrapper for collection of sorting rules
84**/
85class SortInterface extends EventInterface{
86        /*! constructor
87                creates a new interface based on existing request
88                @param request
89                        DataRequestConfig object
90        */
91        public function __construct($request){
92                parent::__construct($request);
93                $this->rules = &$request->get_sort_by_ref();
94        }
95        /*! add new sorting rule
96               
97                @param name
98                        name of field
99                @param dir
100                        direction of sorting
101        */
102        public function add($name,$dir){
103                $this->request->set_sort($name,$dir);
104        }
105        public function store(){
106                $this->request->set_sort_by($this->rules);
107        }
108}
109/*! Wrapper for collection of filtering rules
110**/
111class FilterInterface extends EventInterface{
112        /*! constructor
113                creates a new interface based on existing request
114                @param request
115                        DataRequestConfig object
116        */     
117        public function __construct($request){
118                $this->request = $request;
119                $this->rules = &$request->get_filters_ref();
120        }
121        /*! add new filatering rule
122               
123                @param name
124                        name of field
125                @param value
126                        value to filter by
127                @param rule
128                        filtering rule
129        */
130        public function add($name,$value,$rule){
131                $this->request->set_filter($name,$value,$rule);
132        }
133        public function store(){
134                $this->request->set_filters($this->rules);
135        }
136}
137
138/*! base class for component item representation       
139**/
140class DataItem{
141        protected $data; //!< hash of data
142        protected $config;//!< DataConfig instance
143        protected $index;//!< index of element
144        protected $skip;//!< flag , which set if element need to be skiped during rendering
145        /*! constructor
146               
147                @param data
148                        hash of data
149                @param config
150                        DataConfig object
151                @param index
152                        index of element
153        */
154        function __construct($data,$config,$index){
155                $this->config=$config;
156                $this->data=$data;
157                $this->index=$index;
158                $this->skip=false;
159        }
160        /*! get named value
161               
162                @param name
163                        name or alias of field
164                @return
165                        value from field with provided name or alias
166        */
167        public function get_value($name){
168                return $this->data[$name];
169        }
170        /*! set named value
171               
172                @param name
173                        name or alias of field
174                @param value
175                        value for field with provided name or alias
176        */
177        public function set_value($name,$value){
178                return $this->data[$name]=$value;
179        }
180        /*! get id of element
181                @return
182                        id of element
183        */
184        public function get_id(){
185                $id = $this->config->id["name"];
186                if (array_key_exists($id,$this->data))
187                        return $this->data[$id];
188                return false;
189        }
190        /*! change id of element
191               
192                @param value
193                        new id value
194        */
195        public function set_id($value){
196                $this->data[$this->config->id["name"]]=$value;
197        }
198        /*! get index of element
199               
200                @return
201                        index of element
202        */
203        public function get_index(){
204                return $this->index;
205        }
206        /*! mark element for skiping ( such element will not be rendered )
207        */
208        public function skip(){
209                $this->skip=true;
210        }
211       
212        /*! return self as XML string
213        */
214        public function to_xml(){
215                return $this->to_xml_start().$this->to_xml_end();
216        }
217       
218        /*! replace xml unsafe characters
219               
220                @param string
221                        string to be escaped
222                @return
223                        escaped string
224        */
225        protected function xmlentities($string) {
226                return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;', '&apos;' ), $string);
227        }
228       
229        /*! return starting tag for self as XML string
230        */
231        public function to_xml_start(){
232                $str="<item";
233                for ($i=0; $i < sizeof($this->config->data); $i++){
234                        $name=$this->config->data[$i]["name"];
235                        $str.=" ".$name."='".$this->xmlentities($this->data[$name])."'";
236                }
237                return $str.">";
238        }
239        /*! return ending tag for XML string
240        */
241        public function to_xml_end(){
242                return "</item>";
243        }
244}
245
246
247
248
249
250/*! Base connector class
251        This class used as a base for all component specific connectors.
252        Can be used on its own to provide raw data.     
253**/
254class Connector {
255        protected $config;//DataConfig instance
256        protected $request;//DataRequestConfig instance
257        protected $names;//!< hash of names for used classes
258        private $encoding="utf-8";//!< assigned encoding (UTF-8 by default)
259        private $editing=false;//!< flag of edit mode ( response for dataprocessor )
260        private $updating=false;//!< flag of update mode ( response for data-update )
261        private $db; //!< db connection resource
262        protected $dload;//!< flag of dyn. loading mode
263        public $access;  //!< AccessMaster instance
264       
265        public $sql;    //DataWrapper instance
266        public $event;  //EventMaster instance
267        public $limit=false;
268       
269        private $id_seed=0; //!< default value, used to generate auto-IDs
270        protected $live_update = false; // actions table name for autoupdating
271       
272        /*! constructor
273               
274                Here initilization of all Masters occurs, execution timer initialized
275                @param db
276                        db connection resource
277                @param type
278                        string , which hold type of database ( MySQL or Postgre ), optional, instead of short DB name, full name of DataWrapper-based class can be provided
279                @param item_type
280                        name of class, which will be used for item rendering, optional, DataItem will be used by default
281                @param data_type
282                        name of class which will be used for dataprocessor calls handling, optional, DataProcessor class will be used by default.
283        */     
284        public function __construct($db,$type=false, $item_type=false, $data_type=false){
285                $this->exec_time=microtime(true);
286
287                if (!$type) $type="MySQL";
288                if (class_exists($type."DBDataWrapper",false)) $type.="DBDataWrapper";
289                if (!$item_type) $item_type="DataItem";
290                if (!$data_type) $data_type="DataProcessor";
291               
292                $this->names=array(
293                        "db_class"=>$type,
294                        "item_class"=>$item_type,
295                        "data_class"=>$data_type,
296                );
297               
298                $this->config = new DataConfig();
299                $this->request = new DataRequestConfig();
300                $this->event = new EventMaster();
301                $this->access = new AccessMaster();
302
303                if (!class_exists($this->names["db_class"],false))
304                        throw new Exception("DB class not found: ".$this->names["db_class"]);
305                $this->sql = new $this->names["db_class"]($db,$this->config);
306               
307                $this->db=$db;//saved for options connectors, if any
308               
309                EventMaster::trigger_static("connectorCreate",$this);
310        }
311
312        /*! return db connection resource
313                nested class may neeed to access live connection object
314                @return
315                        DB connection resource
316        */
317        protected function get_connection(){
318                return $this->db;
319        }
320
321        public function get_config(){
322                return new DataConfig($this->config);
323        }
324       
325        public function get_request(){
326                return new DataRequestConfig($this->config);
327        }
328
329
330        /*! config connector based on table
331               
332                @param table
333                        name of table in DB
334                @param id
335                        name of id field
336                @param fields
337                        list of fields names
338                @param extra
339                        list of extra fields, optional, such fields will not be included in data rendering, but will be accessible in all inner events
340                @param relation_id
341                        name of field used to define relations for hierarchical data organization, optional
342        */
343        public function render_table($table,$id="",$fields=false,$extra=false,$relation_id=false){
344                $this->configure($table,$id,$fields,$extra,$relation_id);
345                return $this->render();
346        }
347        public function configure($table,$id="",$fields=false,$extra=false,$relation_id=false){
348        if ($fields === false){
349            //auto-config
350            $info = $this->sql->fields_list($table);
351            $fields = implode(",",$info["fields"]);
352            if ($info["key"])
353                $id = $info["key"];
354        }
355                $this->config->init($id,$fields,$extra,$relation_id);
356                $this->request->set_source($table);
357        }
358       
359        protected function uuid(){
360                return time()."x".$this->id_seed++;
361        }
362       
363        /*! config connector based on sql
364               
365                @param sql
366                        sql query used as base of configuration
367                @param id
368                        name of id field
369                @param fields
370                        list of fields names
371                @param extra
372                        list of extra fields, optional, such fields will not be included in data rendering, but will be accessible in all inner events
373                @param relation_id
374                        name of field used to define relations for hierarchical data organization, optional
375        */
376        public function render_sql($sql,$id,$fields,$extra=false,$relation_id=false){
377                $this->config->init($id,$fields,$extra,$relation_id);
378                $this->request->parse_sql($sql);
379                return $this->render();
380        }
381       
382        /*! render already configured connector
383               
384                @param config
385                        configuration of data
386                @param request
387                        configuraton of request
388        */
389        public function render_connector($config,$request){
390                $this->config->copy($config);
391                $this->request->copy($request);
392                return $this->render();
393        }       
394       
395        /*! render self
396                process commands, output requested data as XML
397        */     
398        public function render(){
399                EventMaster::trigger_static("connectorInit",$this);
400               
401                $this->parse_request();
402                if ($this->live_update !== false && $this->updating!==false) {
403                        $this->live_update->get_updates();
404                } else {
405                        if ($this->editing){
406                                $dp = new $this->names["data_class"]($this,$this->config,$this->request);
407                                $dp->process($this->config,$this->request);
408                        }
409                        else {
410                                $wrap = new SortInterface($this->request);
411                                $this->event->trigger("beforeSort",$wrap);
412                                $wrap->store();
413                               
414                                $wrap = new FilterInterface($this->request);
415                                $this->event->trigger("beforeFilter",$wrap);
416                                $wrap->store();
417               
418                                $this->output_as_xml( $this->sql->select($this->request) );
419                        }
420                }
421                $this->end_run();
422        }
423       
424        /*! prevent SQL injection through column names
425                replace dangerous chars in field names
426                @param str
427                        incoming field name
428                @return
429                        safe field name
430        */
431        protected function safe_field_name($str){
432                return strtok($str, " \n\t;',");
433        }
434       
435        /*! limit max count of records
436                connector will ignore any records after outputing max count
437                @param limit
438                        max count of records
439                @return
440                        none
441        */
442        public function set_limit($limit){
443                $this->limit = $limit;
444        }
445       
446        protected function parse_request_mode(){
447                //detect edit mode
448        if (isset($_GET["editing"])){
449                        $this->editing=true;
450        } else if (isset($_POST["ids"])){
451                        $this->editing=true;
452                        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]');
453                } else if (isset($_GET['dhx_version'])){
454                        $this->updating = true;
455        }
456        }
457       
458        /*! parse incoming request, detects commands and modes
459        */
460        protected function parse_request(){
461                //set default dyn. loading params, can be reset in child classes
462                if ($this->dload)
463                        $this->request->set_limit(0,$this->dload);
464                else if ($this->limit)
465                        $this->request->set_limit(0,$this->limit);
466               
467                $this->parse_request_mode();
468
469        if ($this->live_update && ($this->updating || $this->editing)){
470            $this->request->set_version($_GET["dhx_version"]);
471            $this->request->set_user($_GET["dhx_user"]);
472        }
473               
474                if (isset($_GET["dhx_sort"]))
475                        foreach($_GET["dhx_sort"] as $k => $v){
476                                $k = $this->safe_field_name($k);
477                                $this->request->set_sort($this->resolve_parameter($k),$v);
478                        }
479                               
480                if (isset($_GET["dhx_filter"]))
481                        foreach($_GET["dhx_filter"] as $k => $v){
482                                $k = $this->safe_field_name($k);
483                                $this->request->set_filter($this->resolve_parameter($k),$v);
484                        }
485                       
486               
487        }
488
489        /*! convert incoming request name to the actual DB name
490                @param name
491                        incoming parameter name
492                @return
493                        name of related DB field
494        */
495        protected function resolve_parameter($name){
496                return $name;
497        }
498
499
500        /*! replace xml unsafe characters
501
502                @param string
503                        string to be escaped
504                @return
505                        escaped string
506        */
507        private function xmlentities($string) {
508                return str_replace( array( '&', '"', "'", '<', '>', '’' ), array( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;', '&apos;' ), $string);
509        }
510   
511        /*! render from DB resultset
512                @param res
513                        DB resultset
514                process commands, output requested data as XML
515        */
516        protected function render_set($res){
517                $output="";
518                $index=0;
519                $this->event->trigger("beforeRenderSet",$this,$res,$this->config);
520                while ($data=$this->sql->get_next($res)){
521                        $data = new $this->names["item_class"]($data,$this->config,$index);
522                        if ($data->get_id()===false)
523                                $data->set_id($this->uuid());
524                        $this->event->trigger("beforeRender",$data);
525                        $output.=$data->to_xml();
526                        $index++;
527                }
528                return $output;
529        }
530       
531        /*! output fetched data as XML
532                @param res
533                        DB resultset
534        */
535        protected function output_as_xml($res){
536                $start="<?xml version='1.0' encoding='".$this->encoding."' ?>".$this->xml_start();
537                $end=$this->render_set($res).$this->xml_end();
538               
539                $out = new OutputWriter($start, $end);
540                $this->event->trigger("beforeOutput", $this, $out);
541               
542                $out->output();
543        }
544
545
546        /*! end processing
547                stop execution timer, kill the process
548        */
549        protected function end_run(){
550                $time=microtime(true)-$this->exec_time;
551                LogMaster::log("Done in {$time}s");
552                flush();
553                die();
554        }
555       
556        /*! set xml encoding
557               
558                methods sets only attribute in XML, no real encoding conversion occurs 
559                @param encoding
560                        value which will be used as XML encoding
561        */
562        public function set_encoding($encoding){
563                $this->encoding=$encoding;
564        }
565
566        /*! enable or disable dynamic loading mode
567               
568                @param count
569                        count of rows loaded from server, actual only for grid-connector, can be skiped in other cases.
570                        If value is a false or 0 - dyn. loading will be disabled
571        */
572        public function dynamic_loading($count){
573                $this->dload=$count;
574        }       
575               
576        /*! enable logging
577               
578                @param path
579                        path to the log file. If set as false or empty strig - logging will be disabled
580                @param client_log
581                        enable output of log data to the client side
582        */
583        public function enable_log($path=true,$client_log=false){
584                LogMaster::enable_log($path,$client_log);
585        }
586       
587        /*! provides infor about current processing mode
588                @return
589                        true if processing dataprocessor command, false otherwise
590        */
591        public function is_select_mode(){
592                $this->parse_request_mode();
593                return !$this->editing;
594        }
595       
596        public function is_first_call(){
597                $this->parse_request_mode();
598                return !($this->editing || $this->updating || $this->request->get_start() || sizeof($this->request->get_filters()) || sizeof($this->request->get_sort_by()));
599               
600        }
601       
602        /*! renders self as  xml, starting part
603        */
604        protected function xml_start(){
605                return "<data>";
606        }
607        /*! renders self as  xml, ending part
608        */
609        protected function xml_end(){
610                return "</data>";
611        }
612
613
614        public function insert($data) {
615                $action = new DataAction('inserted', false, $data);
616                $request = new DataRequestConfig();
617                $request->set_source($this->request->get_source());
618               
619                $this->config->limit_fields($data);
620                $this->sql->insert($action,$request);
621                $this->config->restore_fields($data);
622               
623                return $action->get_new_id();
624        }
625       
626        public function delete($id) {
627                $action = new DataAction('deleted', $id, array());
628                $request = new DataRequestConfig();
629                $request->set_source($this->request->get_source());
630               
631                $this->sql->delete($action,$request);
632                return $action->get_status();
633}
634
635        public function update($data) {
636                $action = new DataAction('updated', $data[$this->config->id["name"]], $data);
637                $request = new DataRequestConfig();
638                $request->set_source($this->request->get_source());
639
640                $this->config->limit_fields($data);
641                $this->sql->update($action,$request);
642                $this->config->restore_fields($data);
643               
644                return $action->get_status();
645        }
646
647        /*! sets actions_table for Optimistic concurrency control mode and start it
648                @param table_name
649                        name of database table which will used for saving actions
650                @param url
651                        url used for update notifications
652        */     
653        public function enable_live_update($table, $url=false){
654                $this->live_update = new DataUpdate($this->sql, $this->config, $this->request, $table,$url);
655        $this->live_update->set_event($this->event,$this->names["item_class"]);
656                $this->event->attach("beforeOutput",            Array($this->live_update, "version_output"));
657                $this->event->attach("beforeFiltering",         Array($this->live_update, "get_updates"));
658                $this->event->attach("beforeProcessing",        Array($this->live_update, "check_collision"));
659                $this->event->attach("afterProcessing",         Array($this->live_update, "log_operations"));
660        }
661}
662
663
664/*! wrapper around options collection, used for comboboxes and filters
665**/
666class OptionsConnector extends Connector{
667        protected $init_flag=false;//!< used to prevent rendering while initialization
668        public function __construct($res,$type=false,$item_type=false,$data_type=false){
669                if (!$item_type) $item_type="DataItem";
670                if (!$data_type) $data_type=""; //has not sense, options not editable
671                parent::__construct($res,$type,$item_type,$data_type);
672        }
673        /*! render self
674                process commands, return data as XML, not output data to stdout, ignore parameters in incoming request
675                @return
676                        data as XML string
677        */     
678        public function render(){
679                if (!$this->init_flag){
680                        $this->init_flag=true;
681                        return "";
682                }
683                $res = $this->sql->select($this->request);
684                return $this->render_set($res);
685        }
686}
687
688
689
690class DistinctOptionsConnector extends OptionsConnector{
691        /*! render self
692                process commands, return data as XML, not output data to stdout, ignore parameters in incoming request
693                @return
694                        data as XML string
695        */     
696        public function render(){
697                if (!$this->init_flag){
698                        $this->init_flag=true;
699                        return "";
700                }
701                $res = $this->sql->get_variants($this->config->text[0]["db_name"],$this->request);
702                return $this->render_set($res);
703        }
704}
705
706?>
Note: See TracBrowser for help on using the repository browser.