Logiciel Open Source pour la gestion d'entreprise : CRM, GED, Comptabilité, Groupware, Projets, DRH
www.dg-server.com >>
Présentation >>
Architecture
Une conception moderne, facilement maintenable et personnalisable
DG-Server a été développé en PHP/mySQL avec une architecture MVC :
PHP 5
MySQL 5
Abstraction des bases de données
Développement objet
Scaffolding
AJAX
A propos du scaffolding
DG-Server a été conçu sur le principe du scaffolding. Une classe mère détermine comment la base de données sera utilisée de façon générale : affichage d'un individu, liste, enregistrement de données, modification de données, suppression, etc.
Les concepteurs peuvent ensuite créer des tables dans la base de données et les manipuler directement dans DG-Server à partir d'URL normalisées sans avoir eu à coder quoi que ce soit. S'ils veulent préciser des fonctionnements particuliers pour l'enregistrement, la modification ou l'affichage, il leur suffit alors de créer une classe héritée de la classe principale et de surcharger les comportements qu'ils souhaitent personnaliser.
Cette méthode de conception permet un gain de productivité considérable et une parfaite organisation du code.
Code source de la classe mère : '; //class bigtext to create
else
return '
'.$update_value.'
';
case SEE_LIST:
return
$this->html_page->getShortTextHTML($update_value,DATAOBJECT_SIZE_CONTENT_SHOW_SEE_LIST);
}
}
// use it from subclasses
protected function getFieldHtmlDuration($field_name,$display_type,$update_value="") {
if(($display_type==INSERT||$display_type==I_MASSIVE) && $update_value=="")
$update_value = 0;
if($display_type!=SEARCH || $update_value!="")
$update_value = $this->html_page->getDuration($update_value);
return DataObject::getFieldHtmlOther($field_name,$display_type,$update_value,6);
}
// not used for now
protected function getFieldHtmlHidden($field_name,$display_type,$update_value="") {
return '
';
}
//produce a code that should be evalued by caller
protected function displayObjectMapAjax($table,$id) {
header('Content-type: text/html; charset=utf-8');
$data_object = $this->findOne($table,$id);
$this->html_response.="var objectMap = new Hash({});\n";
if($data_object!=null) //i.e. an object
foreach($data_object as $key => $field)
if(!is_numeric($key))
$this->html_response.='objectMap = objectMap.merge({'.
$key.':"'.str_replace(array("\"","\r","\n"),array("\\\"","","\\n\"+\n\""),$field).'"}); '."\n";
}
//produce a code that should be evalued by caller
protected function displayObjectMapListAjax($table,$args=array()) {
if(count($args)==0)
$args = $this->getDisplayArgs();
$data_objects = $this->getList($args,$table);
header('Content-type: text/html; charset=utf-8');
$this->html_response.="var objectMapList = [];\n";
foreach($data_objects as $n=>$data_object) {
$this->html_response.="objectMapList[".$n."] = new Hash({});\n";
if($data_object!=null) //i.e. an object
foreach($data_object as $key => $field)
if(!is_numeric($key))
$this->html_response.='objectMapList['.$n.'] = objectMapList['.$n.'].merge({'.
$key.':"'.str_replace(array("\r","\n"),array("","\"+\n\""),$field).'"}); '."\n";
}
}
/* overide this */
public function getFieldSpecialAttributes($field_name="",$display_type=SEE,$update_value="") {
return "";
}
/* return html code for main submit button */
protected function getSubmitButtonHtml($display_type=SEE) {
return '
';
}
public function duplicate($id) {
$field_names = $this->getFieldNames();
$id_field_name = array_shift($field_names);
foreach ($field_names as $n=>$field_name) {
if(strstr($field_name, "file")!=FALSE) {
unset($field_names[$n]);
}
}
$sql = "INSERT INTO ".$this->table."(".implode(",",$field_names).") SELECT ".
implode(",",$field_names)." FROM ".$this->table." ".
"WHERE ".$id_field_name." = ".$id;
$this->db->sql_query($sql);
$this->next_id = $this->db->sql_nextid();
$this->html_response .= "
Modification du nouveau document ";
return $this->next_id;
}
public function delete($id) {
$this->html_response.= '
'.t("Delete_question","DataObject",false,$id,$this->table).' '.
'
'.
t('Delete_message1','DataObject').' '.
'
'.t('Delete_message2','DataObject').' ';
$this->html_response .= $this->getDataFormHtml(QUICK_SHOW,$id);
}
/* do a real insert */
public function validateInsert() {
if(!Policy::canDo("contact.see"))
return;
$field_names = $this->getFieldNames();
$sql = "INSERT INTO ".$this->table." VALUES (''";
$id_field_name = array_shift($field_names); //id
foreach($field_names as $field_name){
if (strstr($field_name, "file")){
$new_filename = '';
if($_FILES[$field_name]['tmp_name']!='') {
$new_filename = $this->html_page->getEncodedDocumentName($_FILES[$field_name]['name']);
if(!move_uploaded_file($_FILES[$field_name]['tmp_name'],
$this->getDirectoryName().$new_filename))
$this->html_page->displayError("send",t("Failed_insert","DataObject",
false,$this->getDirectoryName().$new_filename));
} elseif(isset($_POST['check_new'.$field_name])) {
$new_filename = $this->html_page->getEncodedDocumentName(
$this->table.substr(md5(time().$field_name),0,8).'.txt');
$f = fopen($this->getDirectoryName().$new_filename,"w");
fputs($f," ");
fclose($f);
}
$sql.= ', \''.addslashes($new_filename).'\'';
} elseif(strstr($field_name, "password")!==false &&
strlen($_POST[$field_name])<32) {
$sql .= ', \''.md5(trim($_POST[$field_name])).'\'';
} else {
$v = addslashes(trim($_POST[$field_name]));
if(preg_match("/^(\d+)[:h'](\d+)(.*)/",$v,$matches)>0) //duration
$v = $matches[1]+$matches[2]/60;
$sql .= ',\''.$v.'\' ';
}
$i++;
}
$sql.= ')';
$id = 0;
if($this->db->sql_query($sql)){
$id = $this->next_id = $this->db->sql_nextid();
$this->html_response .= "
".t("Insert_message","DataObject",false,$this->table)." ";
$this->html_response .= $this->getDataFormHtml(QUICK_SHOW,$id);
if(isset($_POST['opener_field_name']))
$this->html_page->javascript_items .= 'var sel = window.opener.document.getElementById("'.
$_POST['opener_field_name'].'")'."\n".
'for (var i=0; i
getDescForRecord($this->findOne(null,$id)).' (nouveau)",'.$id.');'."\n".
'sel.options[sel.options.length-1].selected = true;'."\n";
$sql2 = 'INSERT INTO modification(mo_table, mo_field, mo_object_id, '.
'mo_datetime, mo_value_int, mo_contact) VALUES ('.
'"'.$this->table.'","'.$id_field_name.'",'.$id.',"'.date('Y-m-d H:i:s',time()).'",'.
$id.','.$_SESSION['user']['co_id'].")";
$this->db->sql_query($sql2);
} else {
$this->html_page->displayError("send",t("Failed_insert","DataObject")." - ".$sql);
}
}
public function validateInsertMassive() {
if(!Policy::canDo("contact.see"))
return;
$field_names = $this->getFieldNames();
array_shift($field_names); //id
$old_response = $this->html_response;
if ($_FILES['csv']['type']=="text/comma-separated-values") {
$all_field_names = $this->getFieldNames();
$field_names = array();
foreach($all_field_names as $name)
if($this->getFieldHtml($name, I_MASSIVE)!="")
$field_names[] = $name;
array_shift($field_names);//id
$handle = fopen($_FILES['csv']['tmp_name'], "r");
//Verify if the column nane of the first line correspond to table structure
$data = fgetcsv($handle, 0, ',', '"');
foreach ($data as $i => $column_name) {
if ($field_names[$i]!=$column_name) {
$this->html_response = "Fichier CSV non valide!";
return;
}
}
$row = 0;
while (($data = fgetcsv($handle, 0, ',', '"')) !== FALSE) {
//$num = count($data);
foreach($field_names as $col => $field_name){
$error = false;
if (empty($data[$col])) {
$error = true;
}
$_POST[$field_name][$row] = htmlentities($data[$col], ENT_QUOTES,"ISO-8859-1");
}
if($error == false)
$_POST['do_insert'][$row]=true;
$row++;
}
fclose($handle);
$nbr_records = $row;
}
else {
$nbr_records = DATAOBJECT_NBINSERT_MASSIVE;
}
$field_names = $this->getFieldNames();
array_shift($field_names);//id
$nb = 0;
for($i=0;$i<$nbr_records;$i++) {
if(isset($_POST['do_insert'][$i])) {
$sql = "INSERT INTO ".$this->table." VALUES (''";
foreach($field_names as $field_name){
if($_POST[$field_name][$i]!="") {
//no massive for password and files
$v = addslashes(trim($_POST[$field_name][$i]));
if(preg_match("/(\d+)[:h'](\d+)/",$v,$matches)>0) //duration
$v = $matches[1]+$matches[2]/60;
$sql .= ',\''.$v.'\' ';
$should_query = true;
} else {
$sql .= ',\''.$this->getDefaultValue($field_name).'\' ';
}
}
$sql .=");";
$this->db->sql_query($sql);
$nb++;
}
}
$this->html_response = $old_response;
$this->html_response .= "".t("Insert_massive_message","DataObject",
false,$this->table,$nb)." ";
$this->html_response .= $this->getDataFormHtml(QUICK_SHOW,$this->db->sql_nextid());
}
public function displayMassiveCsv() {
$this->html_page->layout_name = "void";
$all_field_names = $this->getFieldNames();
$field_names = array();
foreach($all_field_names as $name)
if($this->getFieldHtml($name, I_MASSIVE)!="")
$field_names[] = $name;
array_shift($field_names);//id
foreach($field_names as $name)
$csv .='"'.htmlentities($name, ENT_QUOTES,"ISO-8859-1").'",';
$csv = substr($csv,0,-1)."\n";
foreach($field_names as $key => $name)
$csv .='"valeur'.$key.'",';
$csv = substr($csv,0,-1)."\n";
//echo $csv;
header('Content-Disposition: attachment; filename="exemple.csv"');
echo html_entity_decode($csv,ENT_NOQUOTES,"ISO-8859-1");
}
public function validateSearch() {
if(!Policy::canDo("contact.see"))
return;
//build the filter
$filter = "";
$args = $this->getDisplayArgs();
$order_value = "0";
foreach($this->params as $index => $value)
if(trim($value)!="" && $value!=="0") {
$order_value .= "+IF(".$index."='".$value."',0,1)"; //best if exact matching
if(is_numeric($value)) {
$args['filter'] .= " AND ".$index."=".$value." ";
} else {
$args['filter'] .= " AND ".$index." LIKE'%".addslashes($value)."%'";
}
}
if($order_value!="0")
$args['order'] = $order_value.",".$args['order'];
$this->displayList($args);
$this->html_response.="".t("New_search","DataObject")." ";
$this->html_response .= $this->getDataFormHtml(SEARCH);
}
public function validateUpdate() {
if(!Policy::canDo("contact.see"))
return;
$all_field_names = $this->getFieldNames();
$field_names = $all_field_names;
$id_fieldname = array_shift($field_names);
$fields = array();
foreach($field_names as $field_name){
//check for a file : delete, delete&add or nothing
$new_value = "";
$is_set_a_value = true;
if (strstr($field_name, "file")!==false){
$is_set_a_value = false;
if(isset($_POST["check".$field_name])){ //delete
$is_set_a_value = true;
$new_value = '';
}
if($_FILES[$field_name]['tmp_name']!='') {
$new_filename = $this->html_page->getEncodedDocumentName($_FILES[$field_name]['name']);
if(move_uploaded_file($_FILES[$field_name]['tmp_name'],
$this->getDirectoryName().$new_filename)) {
$is_set_a_value = true;
$new_value = $new_filename;
} else
$this->html_page->displayError("send",t("Failed_update","DataObject",
false,$this->getDirectoryName().$new_filename));
} elseif(isset($_POST['check_new'.$field_name])) {
$is_set_a_value = true;
$new_value = $this->html_page->getEncodedDocumentName(
$this->table.substr(md5(time().$field_name),0,8).'.txt');
$f = fopen($this->getDirectoryName().$new_value,"w");
fputs($f," ");
fclose($f);
}
} elseif(strstr($field_name, "password")!==false &&
strlen($_POST[$field_name])<32) {
$new_value = md5(trim($_POST[$field_name]));
} else {
$new_value = trim($_POST[$field_name]);
if(preg_match("/^\s*(\d+)[:h'](\d+)\s*$/",$new_value,$matches)>0) //duration
$new_value = $matches[1]+$matches[2]/60;
}
if($is_set_a_value)
$fields[$field_name] = $new_value;
}
if($this->updateRecord($this->table,$_POST[$id_fieldname],$fields) || $some_file_uploaded) {
$this->html_response.= "".t("Update_message","DataObject",false,$this->table).
' '.
t($this->table."_record").' modifié(e) ';
$this->html_response .= $this->getDataFormHtml(QUICK_SHOW,$_POST[$id_fieldname]);
} else { //(no modification)
$this->html_response.= ''.t('No_modification',"DataObject").' ';
$this->html_response .= $this->getDataFormHtml(SEE,$_POST[$id_fieldname]);
}
}
//TODO cascade deletion (ex:delete contact if entreprise deleted) : analysis and dev.
public function validateDelete($id) {
if(!Policy::canDo("contact.see"))
return;
if($this->deleteRecord($this->table,$id,&$nb_files_moved)){
$this->html_response.= t('Delete_message3','DataObject',false,$nb_files_moved).' ';
$this->displayList();
} else {
$this->html_response.= t('Query_result','DataObject')." ";
$this->html_response.= $sql;
}
}
//sending an email about a document
public function displaySendForm($id) {
$document_name = $this->getDocumentName($id,true);
$user = $this->findOne("contact",$_SESSION['user']['co_id']);
$from = $user['co_mail'];
$name = $user['co_name'];
$firstname = $user['co_firstname'];
$object = $this->findOne("",$id);
if($object==null) {
$this->html_page->displayError("",t('No_document','DataObject'));
return;
}
$receivers = $this->getUsualReceivers($object);
if(count($receivers)>0) {
$co_id = $receivers[0]['co_id'];
$en_id = $receivers[0]['co_entreprise'];
} else {
$co_id = 0;
$en_id = 0;
}
$receivers_html = "";
foreach($receivers as $row) {
if($row['is_main_receiver'])
$selected_html = ' selected = "selected" ';
else
$selected_html = "";
$receivers_html.=''.
$row['co_firstname']." ".$row['co_name'].", ".$row['co_mail']." ";
}
$data=array();
$data['get_url']=$this->html_page->getUrl("validate_send",$this->table,"&id=".$id);
$data['id']=$id;
$data['co_id']=$co_id;
$data['entreprise']=$en_id;
if(file_exists($this->getDocumentName($id)))
$data['document_info']= ''.t('Document','DataObject').' '.
''.$document_name.' ';
else
$data['document_info']= ''.t('Without_document','DataObject').' ';
$data['document_name_encoded']=urlencode($document_name);
$data['name']=urlencode($name);
$data['firstname']=urlencode($firstname);
$data['subject']=$this->getDefaultMailSubject($id);
$data['text']=$this->getDefaultMailText($id);
$data['from']=$from;
$data['receivers']=$receivers_html;
$data['additionnal_receivers']=$this->getMailAdditionnalReceivers();
$data['cols']=DEFAULT_TEXTAREA_COLS;
$data['original_action']=$this->action;
$this->displayActions($this->action,$id);
$this->html_response .= $this->html_page->getView("dataobject.send",$data);
}
//$_POST[] should contain : id, co_id, co_name, co_firstname, co_entreprise, url, from, receivers, subject, text
public function validateSend() {
if(!Policy::canDo("contact.see"))
return false;
$additionnal_receivers = explode("\n",$_POST['additionnalReceivers']);
$tmp = $additionnal_receivers;
if(is_array($_POST['receivers']))
$tmp = array_merge($_POST['receivers'],$tmp);
$receivers = array();
foreach($tmp as $receiver)
if(trim($receiver)!='')
$receivers[] = trim($receiver);
if(count($receivers)==0) {
$this->html_page->displayError("send",t('No_receiver','DataObject'));
return false;
}
$headers = "";
if(isset($_POST['from']) && trim($_POST['from'])!="")
$headers .= "From: ".trim($_POST['from'])."\n";
for ($i=1;$isendMail($receivers[0],$_POST['subject'], $text, $headers)) {
$this->html_page->displayError("send",t('Mail_error','DataObject'));
return false;
}
$this->html_response .= ''.t('Mail_message1','DataObject').' ';
$this->saveNoteSend($_POST['co_id'], $_POST['co_entreprise'],
$receivers, $_POST['url'], $_POST['text'], $_POST['original_action']);
$this->html_response .= $this->getDataFormHtml(QUICK_SHOW,$_POST['id']);
return true;
}
//Part 3 : Model
/*
NB : only mysql is completly valid $dbms now (a function miss everywhere else)
*/
public static function connect($dbms = "mysql", $bdd = DEFAULT_DB_DB, $server = DEFAULT_DB_SERVEUR,
$login = DEFAULT_DB_LOGIN, $pwd = DEFAULT_DB_PWD) {
include('db/'.$dbms.'.php');
return new sql_db($server, $login, $pwd, $bdd, false);
}
//replace new ...
public function getDataObject($table = "") {
if($table=="")
return $this;
$class_name = str_replace(" ","",ucwords(str_replace("_"," ",$table)));
if (@class_exists($class_name))
return new $class_name($this->html_page,"",$table);
else
return new DataObject($this->html_page,"",$table);
}
protected function getDefaultMailSubject($id) {
return sprintf(t('Mail_default_subject','DataObject'),
t($this->table."_record"));
}
/* may contain some pseudo-attribute, beginning with $, replaced at sending time
$URL : file sended, if any
$name, $firstname : sender */
protected function getDefaultMailText($id) {
return(t('Mail_message2','DataObject'));
}
protected function getUsualReceivers($object) {
$en_id = 0;
foreach($object as $k=>$v)
if(strpos($k,"entreprise")==3)
$en_id = $v;
$co_id = 0;
foreach($object as $k=>$v)
if(strpos($k,"contact")==3)
$co_id = $v;
$res = array();
if($en_id!=0||$co_id!=0) {
$sql = "SELECT co_id, co_mail, co_firstname, co_name, co_entreprise FROM contact ".
"WHERE (co_entreprise = ".$en_id." OR co_id = ".$co_id.") ".
"AND (co_mail <> '') AND (co_entreprise>0) ".
"GROUP BY co_id ORDER BY co_mail";
$result = $this->db->sql_query($sql);
while ($result && ($row = $this->db->sql_fetchrow($result))) {
$row['is_main_receiver'] = $row['co_id']==$co_id;
$res[] = $row;
}
}
return $res;
}
//override it
protected function getMailAdditionnalReceivers(){
return "";
}
//cm_entreprise -> entreprise
protected function getTableNameFromForeignFieldname($field_name) {
$s = substr($field_name,3);
if(strpos($s,'_')!==false)
$s = substr($s,0,strpos($s,'_'));
return $s;
}
// return : string to show - name of a field
//no need for overriding, use translations (will search in table file,
//then in htmlpage *_record)
//should be used more in this file
protected function getNameFromFieldname($field_name) {
$translation = t($field_name,ucfirst($this->table));
if($translation!=$field_name)
return $translation;
else {
$name = substr($field_name,3);
$name_min = $name;
if(strpos($name_min,'_')!==false) {
$name_end = str_replace("_"," ",substr($name_min,strpos($name_min,'_')));
$name_min = substr($name_min,0,strpos($name_min,'_'));
}
$translation = t($name_min.'_record');
if($translation!=$name_min.'_record')
return ucfirst($translation)." ".$name_end;
else
return ucwords(str_replace("_"," ",$name));
}
}
// return : string (valid html) to show enough to identify a record
// override it
public function getDescForRecord($record, $full_and_slow=true) {
return $this->html_page->getShortTextHTML(array_i($record,1),50);
}
//used only by massive inserts
protected function getDefaultValue($field_name,$display_type=SEE) {
return "";
}
//return field frefix (eg. "co" for "contact")
public function getPrefix($table="") {
if($table=="")
$table = $this->table;
if(isset(self::$table_prefixes[$table]))
return self::$table_prefixes[$table];
$sql = "SELECT * FROM ".$table." LIMIT 1";
$result = $this->db->sql_query($sql);
$res = substr($this->db->sql_fieldname(0,$result),0,2);
self::$table_prefixes[$table] = $res;
return $res;
}
public function getDirectoryName() {
return FILES_DIRECTORY.$this->table."/";
}
public function getDocumentName($id,$abs_URL=false,$only_file_name=false,$suffix="") {
if($abs_URL)
$base = self::DOCUMENTS_PROTOCOL."://".$_SERVER['SERVER_NAME'].
substr($_SERVER['SCRIPT_NAME'],0,strrpos($_SERVER['SCRIPT_NAME'],"/")).
substr($this->getDirectoryName(),1); //ommitting a dot
else if($only_file_name)
$base = "";
else
$base = $this->getDirectoryName();
$object = $this->findOne("",$id);
if($object==null)
return false;
foreach($object as $k=>$v)
if(substr($k,3)=="reference" && trim($v)!="")
return $this->html_page->getEncodedDocumentName($base.$v.$suffix.".html");
$found_file_field = false;
foreach($object as $k=>$v)
if(strpos(substr($k,3),"file")===0 && !$found_file_field)
if(trim($v)!="")
return $base.$v;
else
$found_file_field = true;
return $this->html_page->getEncodedDocumentName($base.$this->table.$id.$suffix.".html");
}
protected function getNextActions($id = 0){
$sql_need_id = '';
if($id==0)
$sql_need_id = "AND (ac_is_id_needed = '') ";
$sql = "SELECT * FROM action WHERE 1 ".
"AND (ac_previous = '".$this->action."' OR ac_previous = '*') ".
"AND ((ac_previous_table = '".$this->table."') OR
((ac_table='' OR ac_table='".$this->table."') AND ac_previous_table = '*')) ".
//uncomment below to show all action but the previous one
//"AND (ac_name <> '".$previous."' OR (ac_table <> '".$this->table."' AND ac_table <> '')) ".
$sql_need_id;
$result = $this->db->sql_query($sql);
$actions = array();
while($result && ($action = $this->db->sql_fetchrow($result)))
$actions[] = $action;
foreach($actions as $n1=>$a1)
foreach($actions as $n2=>$a2)
if($n1!=$n2 && $a1['ac_name']==$a2['ac_name'] && $a1['ac_table']==$this->table && $a2['ac_table']=='')
unset($actions[$n2]);
$actions = Policy::getAllowedActions($actions);
return $actions;
}
/*unbiquitous method, return a full list of this table records*/
public function getList($args = null, $table="") {
if($table=="")
$table = $this->table;
if($args===null)
$args = $this->getDataObject($table)->getDisplayArgs(null);
elseif(!isset($args['order'])||$args['order']=='')
$args = $this->mergeArgs($this->getDataObject($table)->getDefaultArgs(),$args);
$fields = $args['fields'];
if(trim($fields)=="")
$fields = "*";
$sql = "SELECT ".$fields." FROM ".$table." ".$args['join']." ".
"WHERE 1 ".$args['filter'];
if(isset($args['order']))
$sql .= " ORDER BY ".$args['order']." ";
$limit_start = 0;
if(isset($args['limit_start']) && trim($args['limit_start'])!="")
$limit_start = $args['limit_start'];
$limit_nb = 1e6;
if(isset($args['limit_nb']))
$limit_nb = $args['limit_nb'];
$sql .= " LIMIT ".$limit_start.','.$limit_nb;
//echo " ".$sql." ";
$result = $this->db->sql_query($sql);
$records = array();
if (!$result) {
$error = $this->db->sql_error();
$this->html_page->displayError(t('Sql_error','DataObject'),$error['message']." (SQL was : ".$sql.")");
return $records;
}
while ($row = $this->db->sql_fetchrow($result))
$records[] = $row;
return $records;
}
/**
* similar to getList, result : count only
**/
public function getCount($args = null, $table="") {
if($table=="")
$table = $this->table;
if($args===null)
$args = $this->getDataObject($table)->getDefaultArgs();
elseif(!isset($args['order'])||$args['order']=='')
$args = $this->mergeArgs($this->getDataObject($table)->getDefaultArgs(),$args);
$sql = "SELECT count(*) as c FROM ".$table." ".$args['join'].
" WHERE 1 ".$args['filter'];
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
return $row['c'];
}
//can be called statically called, if $table indicated
//filter : "AND ...."
public function findOne($table="",$id=0,$filter="",$order="",$fields=""){
$args = $this->getDataObject($table)->getDefaultArgs();
if($table=="")
$table = $this->table;
if($order=="")
$order = $args["order"];
if($fields=="")
$fields = $args['fields'];
if($fields=="")
$fields = "*";
$sql = "SELECT ".$fields." FROM ".$table." ".$args['join']." WHERE 1 ";
if($id>0)
$sql .= " AND ".$this->getPrefix($table)."_id =".$id;
if($fields!="*" || strpos($fields,',')==false)
$order = "1";
$sql .= " ".$filter." ORDER BY ".$order." LIMIT 1";
//echo $sql."\n";
$result = $this->db->sql_query($sql);
if($result)
return $this->db->sql_fetchrow($result);
else
return null;
}
public function findModifications($table="",$id=1) {
if($table=="")
$table = $this->table;
//+1 : in case we just inserted/modified
$now = date('Y-m-d H:i:s',time()+1);
$res = array($now => $this->findOne($table,$id));
$sql = 'SELECT * FROM modification '.
'WHERE mo_table="'.$table.'" AND mo_object_id="'.$id.'" ORDER BY mo_datetime DESC ';
$result = $this->db->sql_query($sql);
while($result && ($row = $this->db->sql_fetchrow($result))) {
foreach(array('varchar','text','int','double','datetime') as $type)
if($row['mo_value_'.$type]!==null)
$res[$row['mo_datetime']][$row['mo_field']] = $row['mo_value_'.$type];
$res[$row['mo_datetime']]['mo_contact'] = $row['mo_contact'];
}
$last = null;
foreach($res as $time=>$record) {
if($last != null) {
//reorder also
$res[$time] = array('mo_contact'=>$record['mo_contact']);
foreach($last as $field_name => $value)
if(isset($record[$field_name]))
$res[$time][$field_name] = $record[$field_name];
else
$res[$time][$field_name] = $value;
}
$last = $res[$time];
}
return $res;
}
public function findFieldModifications($field_name="",$table="",$id=1,$actual_included=true) {
if($table=="")
$table = $this->table;
$res = array();
if($actual_included) {
$id_field = $this->getPrefix($table)."_id";
$now = date('Y-m-d H:i:s');
$sql = "SELECT ".$field_name." as v FROM ".$table." WHERE ".$id_field." = ".$id;
$result = $this->db->sql_query($sql);
if($result && ($row = $this->db->sql_fetchrow($result)))
$res[$now] = $row['v'];
}
$field_sql = "";
if($field_name!="")
$field_sql = ' AND mo_field="'.$field_name.'" ';
$sql = 'SELECT * FROM modification '.
'WHERE mo_table="'.$table.'" AND mo_object_id='.$id.' '.$field_sql.
'ORDER BY mo_datetime DESC ';
$result = $this->db->sql_query($sql);
while($result && ($row = $this->db->sql_fetchrow($result))) {
foreach(array('varchar','text','int','double','datetime') as $type)
if($row['mo_value_'.$type]!==null)
$res[$row['mo_datetime']] = $row['mo_value_'.$type];
}
return $res;
}
public function updateRecord($table, $id, $fields) {
$all_field_names = $this->getFieldNames($table);
$field_names = $all_field_names;
$id_fieldname = array_shift($field_names);
$old_record = $this->findOne($table,$id);
$field_types = array();
foreach($all_field_names as $n=>$field_name)
$field_types[$field_name] = $this->db->sql_fieldtype($n);
$now = date('Y-m-d H:i:s');
$sql = "UPDATE ".$table." SET ";
$sep = "";
$user_id = 0;
if(isset($_SESSION['user']['co_id']))
$user_id = $_SESSION['user']['co_id'];
foreach($fields as $field_name=>$new_value){
if($new_value!=$old_record[$field_name]) {
$tab_types = array("int"=>"int","real"=>"double","date"=>"datetime",
"time"=>"datetime","datetime"=>"datetime","string"=>"varchar",
"timestamp"=>"varchar","blob"=>"text");
$type = $tab_types[$field_types[$field_name]];
if($type == "varchar" && strlen($old_record[$field_name])>255)
$type = "text";
$sql2 = "INSERT INTO modification(mo_table, mo_field, mo_object_id, ".
"mo_datetime, mo_value_".$type.", mo_contact) VALUES (".
"'".$table."','".$field_name."',".$id.",'".$now."',".
"'".addslashes($old_record[$field_name])."',".$user_id.")";
$this->db->sql_query($sql2);
$sql.= $sep.$field_name.'=\''.addslashes($new_value).'\' ';
$sep=",";
}
}
if($sep=="")
return false;
$sql.= 'WHERE '.$id_fieldname.'='.$id;
if(!$this->db->sql_query($sql)) {
$this->html_page->displayError("send",t("Failed_insert","DataObject")." - ".$sql);
return false;
}
return true;
}
public function deleteRecord($table, $id, &$nb_file_moved = null) {
$old_record = $this->findOne($table,$id);
$all_field_names = $this->getFieldNames($table);
$field_names = $all_field_names;
$id_fieldname = array_shift($field_names);
$field_types = array();
foreach($all_field_names as $n=>$field_name)
$field_types[$field_name] = $this->db->sql_fieldtype($n);
$nb_files_moved = 0;
$now = date('Y-m-d H:i:s');
foreach($field_names as $field_name) {
if (strstr($field_name, "file")!==false && trim($old_record[$field_name])!='')
if(@rename($this->getDirectoryName().$old_record[$field_name],
'./files/bin/'.$old_record[$field_name]))
$nb_files_moved++;
$tab_types = array("int"=>"int","real"=>"double","date"=>"datetime",
"time"=>"datetime","datetime"=>"datetime","string"=>"varchar",
"timestamp"=>"varchar");
$type = $tab_types[$field_types[$field_name]];
if($type == "varchar" && strlen($old_record[$field_name])>255)
$type = "text";
$sql2 = "INSERT INTO modification(mo_table, mo_field, mo_object_id, ".
"mo_datetime, mo_value_".$type.", mo_contact) VALUES (".
"'".$table."','".$field_name."',".$id.",'".$now."',".
"'".addslashes($old_record[$field_name])."',".$_SESSION['user']['co_id'].")";
$this->db->sql_query($sql2);
}
$sql = "DELETE FROM ".$table." WHERE ".$id_fieldname."=".$id;
return $this->db->sql_query($sql);
}
public function getListById($args = null, $table="") {
$list = $this->getList($args,$table);
$res = array();
foreach($list as $l)
$res[array_i($l,0)] = $l;
return $res;
}
/* override it */
public function getDefaultArgs() {
return array("fields"=>"", "join"=>"", "filter"=>"",
"order"=>2, "special"=>"","limit_start"=>0,
"limit_nb" => DATAOBJECT_NBRECORDS_SEE_LIST);
}
//use $get (or $this->params) to merge received listing args with default ones
//"special" : depends on class, often: id of asked object in the list
protected function getDisplayArgs($get = null) {
if($get===null)
$get = $this->params;
$args = $get;
if(!isset($args['filter']))
$args['filter'] = '';
//sample request : ...value=1&restriction=co_id&value1=3&restriction_sup1=co_ca
for($i = "";isset($get["value".$i]);$i++) { //notice:type change for $i
$text_value = "'".addslashes($get["value".$i])."'";
if(isset($get["restriction".$i]))
$args['filter'] .= " AND(".$get["restriction".$i]."=".$text_value.")";
if(isset($get["interdiction".$i]))
$args['filter'] .= " AND(".$get["interdiction".$i]."!=".$text_value.")";
if(isset($get["restriction_sup".$i]))
$args['filter'] .= " AND(".$get["restriction_sup".$i].">".$text_value.")";
if(isset($get["restriction_inf".$i]))
$args['filter'] .= " AND(".$get["restriction_inf".$i]."<".$text_value.")";
}
//sample request : ...values=1,4&restriction=co_role
for($i = "";isset($get["values".$i]);$i++) { //
$values_tab = array();
foreach(explode(",",$get["values".$i]) as $v)
$values_tab[] = "'".addslashes($v)."'";
$values_sql = " IN('-10000',".implode(",",$values_tab).")";
if(isset($get["restriction".$i]))
$args['filter'] .= " AND(".$get["restriction".$i].$values_sql.")";
if(isset($get["interdiction".$i]))
$args['filter'] .= " AND(NOT ".$get["interdiction".$i].$values_sql.")";
}
return $this->mergeArgs($this->getDefaultArgs(),$args);
}
// used by ::displayListBy()
protected function getByArgs($previous_table, $previous_id, $real_field="") {
if($real_field!="")
$used_field = $real_field;
else
$used_field = $this->getPrefix().'_'.$previous_table;
return $this->mergeArgs($this->getDisplayArgs(),
array('filter'=>' AND ('.$used_field.' = '.$previous_id.')'));
}
//do more than array_merge, only for args (i.e. what could be in $_GET)
//$args2 win
protected function mergeArgs($args1, $args2) {
$res = $args1;
foreach(array('fields','join','filter','order','special','limit_start','limit_nb') as $arg)
if(!isset($res[$arg]))
$res[$arg] = '';
if(isset($args2['fields']))
$res['fields'] .= " ".$args2['fields'];
if(isset($args2['join']))
$res['join'] .= " ".$args2['join'];
if(isset($args2['filter']))
$res['filter'] .= " ".$args2['filter'];
if(isset($args2['order']))
if($res['order']!='') {
//problem : x,y,a(z,2) should be split only on characters 1 and 3, not 7
//solution : replace (..,..) with (..|||..), then merge, then replace back
$tmp = $args1['order'];
do {
$order1 = $tmp;
$tmp = preg_replace("/\(([^\)]*)\,/i","($1|||$2",$order1);
} while($order1 != $tmp);
$tmp = $args2['order'];
do {
$order2 = $tmp;
$tmp = preg_replace("/\(([^\)]*)\,/i","($1|||$2",$order2);
} while($order2 != $tmp);
$order_array=array_merge(explode(",",$order2),explode(",",$order1));
foreach($order_array as $n=>$v)
$order_array[$n] = trim($v);
foreach($order_array as $n1=>$v1) //search and remove field 2 times here
foreach($order_array as $n2=>$v2) {
//warning, bug prone due to same probleme 15 lines above
$v1s = explode(" ",$v1); //avoid DESC
$v2s = explode(" ",$v2);
if(($n1<$n2 && $v1s[0]==$v2s[0]) || $v2s[0]=='')
unset($order_array[$n2]);
}
$res['order'] = str_replace("|||",",",implode(",",array_slice($order_array,0,2)));
} else
$res['order'] = $args2['order'];
if(isset($args2['special']))
$res['special'] .= $args2['special'];
if(isset($args2['limit_start']))
$res['limit_start'] = $args2['limit_start'];
if(isset($args2['limit_nb']))
$res['limit_nb'] = $args2['limit_nb'];
return $res;
}
protected function mergeWithDefault($args) {
return $this->mergeArgs($this->getDefaultArgs(),$args);
}
protected function stopWorkflow($key) {
if(isset($_SESSION['wflw'][$key])) {
$this->html_response .= "".
t('Workflow_message1','DataObject',false,$_SESSION['wflw'][$key]['name'])." ";
unset($_SESSION['wflw'][$key]);
} else
$this->html_response .= ''.
t('Workflow_message2','DataObject',false,$key)." ";
}
protected function stopAnyWorkflow() {
if(isset($_SESSION['wflw']))
foreach($_SESSION['wflw'] as $key=>$wflw)
$this->stopWorkflow($key);
}
protected function stopOldWorkflows() {
if(isset($_SESSION['wflw']))
foreach($_SESSION['wflw'] as $key=>$wflw)
if(isset($wflw['time_limit'])&&date('Y-m-d H:i:s')>$wflw['time_limit'])
$this->stopWorkflow($key);
}
protected static function log($message,$source_file="",$severity="DEBUG") {
if($source_file == "")
$source_file = "dataobject.class.php";
$filename = LOGS_DIRECTORY.$source_file.".log";
//archiving sometimes
if(file_exists($filename)) {
$filemtime = filemtime($filename);
if(date(LOGS_ROTATION_PATTERN,$filemtime)!=date(LOGS_ROTATION_PATTERN))
rename($filename, LOGS_DIRECTORY.$source_file.'.'.
date(LOGS_ROTATION_PATTERN,$filemtime).'.log');
}
//adding
$flog=fopen($filename,"a");
if($flog!=false) {
fputs($flog,date("Y-m-d H:i:s")." ".$severity." ".$_SERVER['REMOTE_ADDR']." ".$message."\n");
fclose($flog);
}
}
//file : array('name'=>...,'content'=>...,'mime_type'=>...)
protected function sendMail($receiver,$subject,$text,$headers="",$file=null) {
$random_hash = md5(date('r', time()));
if($headers!="" && substr($headers,-1)!="\n")
$headers .= "\n";
if($file==null)
$headers .= "Content-Type: text/plain; charset=".t("page_encoding")."\n".
"Content-Transfer-Encoding: 8bit\n";
else
$headers .= "Content-Type: multipart/mixed; boundary=\"file-".$random_hash."\"\n";
$headers .= "MIME-Version: 1.0\n".
"X-Mailer: ".MAIN_TITLE."\n".
"Return-Path: nowhere@example.org\n";
if(stripos($headers,"From:")!==false) {
$from = substr(trim(stristr($headers,"From:")),strlen("From:"));
if(strpos($from,"\n")!==false)
$from = substr($from,0,strpos($from,"\n"));
$headers .= "Reply-To: ".$from."\n";
} else {
if(isset($_SESSION['user']))
$from=$_SESSION['user']['co_mail'];
else
$from=DEFAULT_MAIL;
$headers .= "From: ".$from."\nReply-To: ".$from."\n";
}
$text = $this->html_page->getTextNotHtml(str_replace("\'","'",$text));
$headers = str_replace("\r\n","\n",$headers);
$headers = str_replace("\n",EMAIL_HEADER_NL,$headers);
$text = str_replace("\r\n","\n",$text);
$text = str_replace("\n",EMAIL_TEXT_NL,$text);
if($file!=null) {
$text = "--file-".$random_hash."\n".
"Content-Type: text/plain; charset=".t("page_encoding")."\n".
"Content-Transfer-Encoding: 8bit\n\n".
$text."\n\n".
"--file-".$random_hash."\n".
"Content-Type: ".$file['mime_type'].";\n name=\"".$file['name']."\"\n".
"Content-Disposition: attachment;\n filename=\"".$file['name']."\"\n".
"Content-Transfer-Encoding: base64\n\n".
chunk_split(base64_encode($file['content']))."\n".
"--file-".$random_hash."--";
}
$this->log("Sending mail : ".$receiver.",".
t("Mail_subject_prefix",'DataObject').
$subject.",\n".$text."\n,".$headers.",-f".$from,
"","INFO");
return mail($receiver,t('Mail_subject_prefix','DataObject').$subject,
$text,$headers,"-f$from");
}
//TODO create insert as function in dataobject, in order to save a modification
protected function saveNoteSend($co_id, $co_entreprise, $receivers, $url, $text,
$original_action) {
$sql = 'INSERT INTO note VALUES("","'.date("Y-m-d").'",'.
'"'.t('Sql_note1','DataObject',false,t($this->table."_record")).'","",'.
$co_entreprise.','.$co_id.','.
'"'.t('Sql_note2','DataObject',false,$url,implode(',',$receivers),$text).'",'.
$_SESSION['user']['wo_id'].',0,"'.$this->getNoteType($original_action).'",'.
'"'.$this->getNoteSubType($original_action).'")';
$this->db->sql_query($sql);
}
protected function getNoteType($original_action) {
return 'sending';
}
protected function getNoteSubType($original_action) {
return 'other';
}
}