connector= $connector; $this->config=$config; $this->request=$request; } /*! convert incoming data name to valid db name redirect to Connector->name_data by default @param data data name from incoming request @return related db_name */ function name_data($data){ return $data; } /*! retrieve data from incoming request and normalize it @param ids array of extected IDs @return hash of data */ function get_post_values($ids){ $data=array(); for ($i=0; $i < sizeof($ids); $i++) $data[$ids[$i]]=array(); foreach ($_POST as $key => $value) { $details=explode("_",$key,2); if (sizeof($details)==1) continue; $name=$this->name_data($details[1]); $data[$details[0]][$name]=$value; } return $data; } /*! process incoming request ( save|update|delete ) */ function process(){ LogMaster::log("DataProcessor object initialized",$_POST); $results=array(); if (!isset($_POST["ids"])) throw new Exception("Incorrect incoming data, ID of incoming records not recognized"); $ids=explode(",",$_POST["ids"]); $rows_data=$this->get_post_values($ids); $failed=false; try{ if ($this->connector->sql->is_global_transaction()) $this->connector->sql->begin_transaction(); for ($i=0; $i < sizeof($ids); $i++) { $rid = $ids[$i]; LogMaster::log("Row data [{$rid}]",$rows_data[$rid]); if (!isset($_POST[$rid."_!nativeeditor_status"])) throw new Exception("Status of record [{$rid}] not found in incoming request"); $status = $_POST[$rid."_!nativeeditor_status"]; $action=new DataAction($status,$rid,$rows_data[$rid]); $results[]=$action; $this->inner_process($action); } } catch(Exception $e){ $failed=true; } if ($this->connector->sql->is_global_transaction()){ if (!$failed) for ($i=0; $i < sizeof($results); $i++) if ($results[$i]->get_status()=="error" || $results[$i]->get_status()=="invalid"){ $failed=true; break; } if ($failed){ for ($i=0; $i < sizeof($results); $i++) $results[$i]->error(); $this->connector->sql->rollback_transaction(); } else $this->connector->sql->commit_transaction(); } $this->output_as_xml($results); } /*! converts status string to the inner mode name @param status external status string @return inner mode name */ protected function status_to_mode($status){ switch($status){ case "updated": return "update"; break; case "inserted": return "insert"; break; case "deleted": return "delete"; break; default: return $status; break; } } /*! process data updated request received @param action DataAction object @return DataAction object with details of processing */ protected function inner_process($action){ if ($this->connector->sql->is_record_transaction()) $this->connector->sql->begin_transaction(); try{ $mode = $this->status_to_mode($action->get_status()); if (!$this->connector->access->check($mode)){ LogMaster::log("Access control: {$operation} operation blocked"); $action->error(); } else { $check = $this->connector->event->trigger("beforeProcessing",$action); if (!$action->is_ready()) $this->check_exts($action,$mode); $check = $this->connector->event->trigger("afterProcessing",$action); } } catch (Exception $e){ $action->set_status("error"); } if ($this->connector->sql->is_record_transaction()){ if ($action->get_status()=="error" || $action->get_status()=="invalid") $this->connector->sql->rollback_transaction(); else $this->connector->sql->commit_transaction(); } return $action; } /*! check if some event intercepts processing, send data to DataWrapper in other case @param action DataAction object @param mode name of inner mode ( will be used to generate event names ) */ function check_exts($action,$mode){ $old_config = new DataConfig($this->config); $this->connector->event->trigger("before".$mode,$action); if ($action->is_ready()) LogMaster::log("Event code for ".$mode." processed"); else { //check if custom sql defined $sql = $this->connector->sql->get_sql($mode,$action); if ($sql) $this->connector->sql->query($sql); else{ $action->sync_config($this->config); $method=array($this->connector->sql,$mode); if (!is_callable($method)) throw new Exception("Unknown dataprocessing action: ".$mode); call_user_func($method,$action,$this->request); } } $this->connector->event->trigger("after".$mode,$action); $this->config = $old_config; } /*! output xml response for dataprocessor @param results array of DataAction objects */ function output_as_xml($results){ LogMaster::log("Edit operation finished",$results); ob_clean(); header("Content-type:text/xml"); echo ""; echo ""; for ($i=0; $i < sizeof($results); $i++) echo $results[$i]->to_xml(); echo ""; } } /*! contain all info related to action and controls customizaton **/ class DataAction{ private $status; //!< cuurent status of record private $id;//!< id of record private $data;//!< data hash of record private $userdata;//!< hash of extra data , attached to record private $nid;//!< new id value , after operation executed private $output;//!< custom output to client side code private $attrs;//!< hash of custtom attributes private $ready;//!< flag of operation's execution private $addf;//!< array of added fields private $delf;//!< array of deleted fields /*! constructor @param status current operation status @param id record id @param data hash of data */ function __construct($status,$id,$data){ $this->status=$status; $this->id=$id; $this->data=$data; $this->nid=$id; $this->output=""; $this->attrs=array(); $this->ready=false; $this->addf=array(); $this->delf=array(); } /*! add custom field and value to DB operation @param name name of field which will be added to DB operation @param value value which will be used for related field in DB operation */ function add_field($name,$value){ LogMaster::log("adding field: ".$name.", with value: ".$value); $this->data[$name]=$value; $this->addf[]=$name; } /*! remove field from DB operation @param name name of field which will be removed from DB operation */ function remove_field($name){ LogMaster::log("removing field: ".$name); $this->delf[]=$name; } /*! sync field configuration with external object @param slave SQLMaster object @todo check , if all fields removed then cancel action */ function sync_config($slave){ foreach ($this->addf as $k => $v) $slave->add_field($v); foreach ($this->delf as $k => $v) $slave->remove_field($v); } /*! get value of some record's propery @param name name of record's property ( name of db field or alias ) @return value of related property */ function get_value($name){ if (!array_key_exists($name,$this->data)){ LogMaster::log("Incorrect field name used: ".$name); LogMaster::log("data",$this->data); return ""; } return $this->data[$name]; } /*! set value of some record's propery @param name name of record's property ( name of db field or alias ) @param value value of related property */ function set_value($name,$value){ LogMaster::log("change value of: ".$name." as: ".$value); $this->data[$name]=$value; } /*! get hash of data properties @return hash of data properties */ function get_data(){ return $this->data; } /*! get some extra info attached to record deprecated, exists just for backward compatibility, you can use set_value instead of it @param name name of userdata property @return value of related userdata property */ function get_userdata_value($name){ return $this->get_value($name); } /*! set some extra info attached to record deprecated, exists just for backward compatibility, you can use get_value instead of it @param name name of userdata property @param value value of userdata property */ function set_userdata_value($name,$value){ return $this->set_value($name,$value); } /*! get current status of record @return string with status value */ function get_status(){ return $this->status; } /*! assign new status to the record @param status new status value */ function set_status($status){ $this->status=$status; } /*! get id of current record @return id of record */ function get_id(){ return $this->id; } /*! sets custom response text can be accessed through defineAction on client side. Text wrapped in CDATA, so no extra escaping necessary @param text custom response text */ function set_response_text($text){ $this->set_response_xml(""); } /*! sets custom response xml can be accessed through defineAction on client side @param text string with XML data */ function set_response_xml($text){ $this->output=$text; } /*! sets custom response attributes can be accessed through defineAction on client side @param name name of custom attribute @param value value of custom attribute */ function set_response_attribute($name,$value){ $this->attrs[$name]=$value; } /*! check if action finished @return true if action finished, false otherwise */ function is_ready(){ return $this->ready; } /*! return new id value equal to original ID normally, after insert operation - value assigned for new DB record @return new id value */ function get_new_id(){ return $this->nid; } /*! set result of operation as error */ function error(){ $this->status="error"; $this->ready=true; } /*! set result of operation as invalid */ function invalid(){ $this->status="invalid"; $this->ready=true; } /*! confirm successful opeation execution @param id new id value, optional */ function success($id=false){ if ($id!==false) $this->nid = $id; $this->ready=true; } /*! convert DataAction to xml format compatible with client side dataProcessor @return DataAction operation report as XML string */ function to_xml(){ $str="attrs as $k => $v) { $str.=$k."='".$v."' "; } $str.=">{$this->output}"; return $str; } /*! convert self to string ( for logs ) @return DataAction operation report as plain string */ function __toString(){ return "action:{$this->status}; sid:{$this->id}; tid:{$this->nid};"; } } ?>