Fixed not working custom fields:

- SQL error when storing cfs
- csv export did not contain cfs (reported by Frank Becker)
- list did not contain cfs
- list caused check of attachments for every single entry, without
  displaying the attachments
- empty cf values got stored in db
--> had to rework the whole cf stuff :-(
This commit is contained in:
Ralf Becker 2008-10-15 16:37:23 +00:00
parent abc26d61ee
commit 5261728792
6 changed files with 104 additions and 103 deletions

View File

@ -19,6 +19,8 @@ if (!defined('TIMESHEET_APP'))
* Business object of the TimeSheet * Business object of the TimeSheet
* *
* Uses eTemplate's so_sql as storage object (Table: egw_timesheet). * Uses eTemplate's so_sql as storage object (Table: egw_timesheet).
*
* @todo Implement sorting&filtering by and searching of custom fields
*/ */
class timesheet_bo extends so_sql class timesheet_bo extends so_sql
{ {
@ -100,17 +102,18 @@ class timesheet_bo extends so_sql
var $customfields=array(); var $customfields=array();
/**
* Name of the timesheet table storing custom fields
*/
const EXTRA_TABLE = 'egw_timesheet_extra';
function __construct() function __construct()
{ {
parent::__construct(TIMESHEET_APP,'egw_timesheet',null,'',true); // true = use global db object! parent::__construct(TIMESHEET_APP,'egw_timesheet',null,'',true); // true = use global db object!
$this->config_data = config::read(TIMESHEET_APP); $this->config_data = config::read(TIMESHEET_APP);
$this->quantity_sum = $this->config_data['quantity_sum'] == 'true'; $this->quantity_sum = $this->config_data['quantity_sum'] == 'true';
$this->customfields = config::get_customfields(TIMESHEET_APP);
if (isset($this->config_data['customfields']) && is_array($this->config_data['customfields']))
{
$this->customfields = $this->config_data['customfields'];
}
$this->tz_offset_s = $GLOBALS['egw']->datetime->tz_offset; $this->tz_offset_s = $GLOBALS['egw']->datetime->tz_offset;
$this->now = time() + $this->tz_offset_s; // time() is server-time and we need a user-time $this->now = time() + $this->tz_offset_s; // time() is server-time and we need a user-time
@ -156,14 +159,15 @@ class timesheet_bo extends so_sql
*/ */
function check_acl($required,$data=null) function check_acl($required,$data=null)
{ {
if (!$data) //error_log(__METHOD__."($required,".array2string($data).")");
if (is_null($data) || (int)$data == $this->data['ts_id'])
{ {
$data =& $this->data; $data =& $this->data;
} }
if (!is_array($data)) if (!is_array($data))
{ {
$save_data = $this->data; $save_data = $this->data;
$data = $this->read($data,true); $data = $this->read($data,true,false); // no need to read cf's
$this->data = $save_data; $this->data = $save_data;
if (!$data) return null; // entry not found if (!$data) return null; // entry not found
@ -173,6 +177,14 @@ class timesheet_bo extends so_sql
return $data && !!($rights & $required); return $data && !!($rights & $required);
} }
/**
* return SQL implementing filtering by date
*
* @param string $name
* @param int &$start
* @param int &$end_param
* @return string
*/
function date_filter($name,&$start,&$end_param) function date_filter($name,&$start,&$end_param)
{ {
$end = $end_param; $end = $end_param;
@ -263,6 +275,7 @@ class timesheet_bo extends so_sql
*/ */
function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='',$need_full_no_count=false,$only_summary=false) function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='',$need_full_no_count=false,$only_summary=false)
{ {
error_log(__METHOD__."(".print_r($criteria,true).",'$only_keys','$order_by',".print_r($extra_cols,true).",'$wildcard','$empty','$op','$start',".print_r($filter,true).",'$join')");
//echo "<p>".__METHOD__."(".print_r($criteria,true).",'$only_keys','$order_by',".print_r($extra_cols,true).",'$wildcard','$empty','$op','$start',".print_r($filter,true).",'$join')</p>\n"; //echo "<p>".__METHOD__."(".print_r($criteria,true).",'$only_keys','$order_by',".print_r($extra_cols,true).",'$wildcard','$empty','$op','$start',".print_r($filter,true).",'$join')</p>\n";
// postgres can't round from double precission, only from numeric ;-) // postgres can't round from double precission, only from numeric ;-)
$total_sql = $this->db->Type != 'pgsql' ? "round(ts_quantity*ts_unitprice,2)" : "round(cast(ts_quantity*ts_unitprice AS numeric),2)"; $total_sql = $this->db->Type != 'pgsql' ? "round(ts_quantity*ts_unitprice,2)" : "round(cast(ts_quantity*ts_unitprice AS numeric),2)";
@ -346,40 +359,55 @@ class timesheet_bo extends so_sql
* *
* @param int $ts_id * @param int $ts_id
* @param boolean $ignore_acl=false should the acl be checked * @param boolean $ignore_acl=false should the acl be checked
* @param boolean $read_cfs=true also read the custom fields
* @return array/boolean array with timesheet entry, null if timesheet not found or false if no rights * @return array/boolean array with timesheet entry, null if timesheet not found or false if no rights
*/ */
function read($ts_id,$ignore_acl=false) function read($ts_id,$ignore_acl=false,$read_cfs=true)
{ {
$ret = null; error_log(__METHOD__."($ts_id,$ignore_acl) ".function_backtrace());
if (!(int)$ts_id || !$ignore_acl && !($ret = $this->check_acl(EGW_ACL_READ,$ts_id)) || if (!(int)$ts_id || (int)$ts_id != $this->data['ts_id'] && !parent::read($ts_id))
$this->data['ts_id'] != (int)$ts_id && !parent::read((int)$ts_id))
{ {
return $ret; // no read rights, or entry not found return null; // entry not found
} }
if (!$ignore_acl && !($ret = $this->check_acl(EGW_ACL_READ)))
//assign custom fields {
foreach($this->customfields as $name => $value) { return false; // no read rights
$row = $this->read_extra($name); }
$this->data['#'.$name] = $row['ts_extra_value']; if ($read_cfs && $this->customfields && ($cfs = $this->read_cfs($ts_id)))
{
$this->data += $cfs[$ts_id];
} }
return $this->data; return $this->data;
} }
/** /**
* reads a timesheet extra entry of the current timesheet dataset * Read the cf's of the given ts_id's and evtl names
* *
* @param int $name => name of the current timesheet extra entry * @param int|array $ts_ids
* @param int $value => value of the current timesheet extra entry * @param array $names=null
* @return array of resultset * @return array with ts_id => array(name => value) pairs
*/ */
function read_extra($name='',$value='') function read_cfs($ts_ids,$names=null)
{ {
strlen($value) > 0 ? $where = ' and ts_extra_value ='.$this->db->quote($value) : ''; error_log(__METHOD__."(".array2string($ts_ids).",".array2string($names).")");
strlen($name) > 0 ? $where .= ' and ts_extra_name ='.$this->db->quote($name) : ''; if (!$this->customfields || !$ts_ids)
{
return $this->db->select('egw_timesheet_extra', 'ts_extra_name, ts_extra_value',$query,__LINE__,__FILE__,False,'', return array();
TIMESHEET_APP,0,'where ts_id='.$this->data['ts_id'].$where)->fetch(); }
$where = array('ts_id' => $ts_ids);
if ($names)
{
foreach($names as $name)
{
if ($name[0] == '#') $where['ts_extra_name'][] = substr($name,1);
}
}
$cfs = array();
foreach($this->db->select(self::EXTRA_TABLE,'ts_id,ts_extra_name,ts_extra_value',$where,__LINE__,__FILE__,false,'',TIMESHEET_APP) as $row)
{
$cfs[$row['ts_id']]['#'.$row['ts_extra_name']] = $row['ts_extra_value'];
}
return $cfs;
} }
/** /**
@ -407,53 +435,28 @@ class timesheet_bo extends so_sql
} }
if (!($err = parent::save())) if (!($err = parent::save()))
{ {
//saves data of custom fields in timesheet_extra if ($this->customfields) //saves data of custom fields in timesheet_extra
$this->save_extra(); {
$this->db->delete(self::EXTRA_TABLE,array('ts_id' => $this->data['ts_id']),__LINE__,__FILE__,TIMESHEET_APP);
foreach($this->customfields as $name => $data)
{
if (isset($this->data['#'.$name]) && !empty($this->data['#'.$name]))
{
$this->db->insert(self::EXTRA_TABLE,array(
'ts_id' => $this->data['ts_id'],
'ts_extra_name' => $name,
'ts_extra_value' => $this->data['#'.$name],
),false,__LINE__,__FILE__,TIMESHEET_APP);
}
}
}
// notify the link-class about the update, as other apps may be subscribt to it // notify the link-class about the update, as other apps may be subscribt to it
egw_link::notify_update(TIMESHEET_APP,$this->data['ts_id'],$this->data); egw_link::notify_update(TIMESHEET_APP,$this->data['ts_id'],$this->data);
} }
return $err; return $err;
} }
/**
* saves a timesheet extra entry based one the "custom fields" settings
*
* @param boolean $updateNames => if true "change timesheet extra name", otherwise update existing datasets or insert new ones
* @param boolean $oldname => original name of the timesheet extra entry
* @param boolean $name => new name of the timesheet extra entry
* @return int true on success else false
*/
function save_extra($updateNames=False,$oldname='',$name='')
{
if($updateNames) {
$keys = array('ts_extra_name' => $oldname);
$fieldAssign = array('ts_extra_name' => $name);
$this->db->update('egw_timesheet_extra',$fieldAssign,$keys,__LINE__,__FILE__,TIMESHEET_APP);
return true;
}
else {
foreach($this->customfields as $namecf => $valuecf)
{
//if entry not exist => insert
if(!$this->read_extra($namecf))
{
$fieldAssign = array('ts_id' => $this->data['ts_id'],'ts_extra_name' => $namecf,'ts_extra_value' => $this->data['#'.$namecf]);
$this->db->insert('egw_timesheet_extra',$fieldAssign,false,__LINE__,__FILE__,TIMESHEET_APP);
}
//otherwise update existing dataset
else
{
$keys = array('ts_extra_name' => $namecf, 'ts_id' => $this->data['ts_id']);
$fieldAssign = array('ts_extra_value' => $this->data['#'.$namecf]);
$this->db->update('egw_timesheet_extra',$fieldAssign,$keys,__LINE__,__FILE__,TIMESHEET_APP);
}
}
return true;
}
return false;
}
/** /**
* deletes a timesheet entry identified by $keys or the loaded one, reimplemented to notify the link class (unlink) * deletes a timesheet entry identified by $keys or the loaded one, reimplemented to notify the link class (unlink)
* *
@ -475,35 +478,16 @@ class timesheet_bo extends so_sql
} }
if (($ret = parent::delete($keys)) && $ts_id) if (($ret = parent::delete($keys)) && $ts_id)
{ {
//delete custom fields entries if ($this->customfields) //delete custom fields entries
$this->delete_extra($ts_id); {
$this->db->delete(self::EXTRA_TABLE,array('ts_id' => $ts_id),__LINE__,__FILE__,TIMESHEET_APP);
}
// delete all links to timesheet entry $ts_id // delete all links to timesheet entry $ts_id
egw_link::unlink(0,TIMESHEET_APP,$ts_id); egw_link::unlink(0,TIMESHEET_APP,$ts_id);
} }
return $ret; return $ret;
} }
/**
* deletes a timesheet extra entry identified by $ts_id and/or $ts_exra_name
*
* @param int $ts_id => number of timesheet
* @param string ts_extra_name => certain custom field name
* @return int false if an error
*/
function delete_extra($ts_id='',$ts_extra_name='')
{
strlen($ts_id) > 0 ? $where['ts_id'] = $ts_id : '';
strlen($ts_extra_name) > 0 ? $where['ts_extra_name'] = $ts_extra_name : '';
if(count($where) > 0)
{
return $this->db->delete('egw_timesheet_extra', $where,__LINE__,__FILE__,TIMESHEET_APP);
}
return false;
}
/** /**
* changes the data from the db-format to your work-format * changes the data from the db-format to your work-format
* *
@ -571,7 +555,7 @@ class timesheet_bo extends so_sql
{ {
if (!is_array($entry)) if (!is_array($entry))
{ {
$entry = $this->read( $entry ); $entry = $this->read( $entry,false,false ); // no need to read cfs
} }
if (!$entry) if (!$entry)
{ {
@ -684,7 +668,7 @@ class timesheet_bo extends so_sql
{ {
if($pm_id && isset($GLOBALS['egw_info']['user']['apps']['projectmanager'])) if($pm_id && isset($GLOBALS['egw_info']['user']['apps']['projectmanager']))
{ {
$pm_ids = ExecMethod('projectmanager.boprojectmanager.children',$pm_id); $pm_ids = ExecMethod('projectmanager.projectmanager_bo.children',$pm_id);
$pm_ids[] = $pm_id; $pm_ids[] = $pm_id;
$links = solink::get_links('projectmanager',$pm_ids,'timesheet'); // solink::get_links not egw_links::get_links! $links = solink::get_links('projectmanager',$pm_ids,'timesheet'); // solink::get_links not egw_links::get_links!
if ($links) if ($links)

View File

@ -487,7 +487,7 @@ class timesheet_ui extends timesheet_bo
$GLOBALS['egw_info']['flags']['app_header'] .= ' - '.$GLOBALS['egw']->common->show_date($query['enddate']+12*60*60,$df,false); $GLOBALS['egw_info']['flags']['app_header'] .= ' - '.$GLOBALS['egw']->common->show_date($query['enddate']+12*60*60,$df,false);
} }
} }
if ($query['filter'] == 'custom') // show the custome dates if ($query['filter'] == 'custom') // show the custom dates
{ {
$GLOBALS['egw']->js->set_onload("set_style_by_class('table','custom_hide','visibility','visible');"); $GLOBALS['egw']->js->set_onload("set_style_by_class('table','custom_hide','visibility','visible');");
} }
@ -504,10 +504,16 @@ class timesheet_ui extends timesheet_bo
$rows = $ids; $rows = $ids;
return $this->total; // no need to set other fields or $readonlys return $this->total; // no need to set other fields or $readonlys
} }
$links = egw_link::get_links_multiple(TIMESHEET_APP,$ids); $links = egw_link::get_links_multiple(TIMESHEET_APP,$ids,true,'projectmanager'); // only check for pm links!
unset($query['col_filter'][0]); unset($query['col_filter'][0]);
// query cf's for the displayed rows
if ($ids && $this->customfields &&
in_array('customfields',$cols_to_show=explode(',',$GLOBALS['egw_info']['user']['preferences'][TIMESHEET_APP]['nextmatch-timesheet.index.rows'])))
{
$cfs = $this->read_cfs($ids,$cols_to_show);
}
$readonlys = array(); $readonlys = array();
$have_cats = false; $have_cats = false;
foreach($rows as &$row) foreach($rows as &$row)
@ -541,6 +547,10 @@ class timesheet_ui extends timesheet_bo
$row['titleClass'] = 'titleSum'; $row['titleClass'] = 'titleSum';
continue; continue;
} }
elseif($cfs && isset($cfs[$row['ts_id']]))
{
$row += $cfs[$row['ts_id']];
}
if (!$this->check_acl(EGW_ACL_EDIT,$row)) if (!$this->check_acl(EGW_ACL_EDIT,$row))
{ {
$readonlys["edit[$row[ts_id]]"] = true; $readonlys["edit[$row[ts_id]]"] = true;
@ -582,10 +592,7 @@ class timesheet_ui extends timesheet_bo
$rows += $this->summary; $rows += $this->summary;
} }
$rows['pm_integration'] = $this->pm_integration; $rows['pm_integration'] = $this->pm_integration;
$rows['ts_viewtype'] = $this->ts_viewtype == 'short';
if($this->ts_viewtype == 'short') {
$rows['ts_viewtype'] = true;
}
return $total; return $total;
} }

File diff suppressed because one or more lines are too long

View File

@ -49,5 +49,8 @@ function timesheet_upgrade0_2_002()
function timesheet_upgrade1_4() function timesheet_upgrade1_4()
{ {
// delete empty cf's generated by 1.4
$GLOBALS['egw_setup']->db->delete('egw_timesheet_extra',"ts_extra_value=''",__LINE__,__FILE__,'timesheet');
return $GLOBALS['setup_info']['timesheet']['currentver'] = '1.6'; return $GLOBALS['setup_info']['timesheet']['currentver'] = '1.6';
} }

View File

@ -81,8 +81,8 @@
</rows> </rows>
</grid> </grid>
</template> </template>
<template id="timesheet.edit.customfields" template="" lang="" group="0" version=""> <template id="timesheet.edit.customfields" template="" lang="" group="0" version="1.5.001">
<grid> <grid width="100%" height="150" overflow="auto">
<columns> <columns>
<column/> <column/>
</columns> </columns>

View File

@ -15,7 +15,7 @@
<template id="timesheet.index.add" template="" lang="" group="0" version="1.3.001"> <template id="timesheet.index.add" template="" lang="" group="0" version="1.3.001">
<button label="Add" id="add" onclick="window.open(egw::link('/index.php','menuaction=timesheet.timesheet_ui.edit'),'_blank','dependent=yes,width=600,height=400,scrollbars=yes,status=yes'); return false;"/> <button label="Add" id="add" onclick="window.open(egw::link('/index.php','menuaction=timesheet.timesheet_ui.edit'),'_blank','dependent=yes,width=600,height=400,scrollbars=yes,status=yes'); return false;"/>
</template> </template>
<template id="timesheet.index.rows" template="" lang="" group="0" version="1.5.001"> <template id="timesheet.index.rows" template="" lang="" group="0" version="1.5.002">
<grid width="100%"> <grid width="100%">
<columns> <columns>
<column width="15%"/> <column width="15%"/>
@ -27,6 +27,7 @@
<column disabled="@ts_viewtype"/> <column disabled="@ts_viewtype"/>
<column disabled="@no_owner_col"/> <column disabled="@no_owner_col"/>
<column/> <column/>
<column/>
</columns> </columns>
<rows> <rows>
<row class="th"> <row class="th">
@ -62,6 +63,7 @@
<textbox type="float" id="price" readonly="true" precision="2"/> <textbox type="float" id="price" readonly="true" precision="2"/>
</vbox> </vbox>
<nextmatch-filterheader id="ts_owner" options="User" no_lang="1" class="$cont[ownerClass]"/> <nextmatch-filterheader id="ts_owner" options="User" no_lang="1" class="$cont[ownerClass]"/>
<nextmatch-customfields id="customfields" readonly="true"/>
<description value="Actions" class="noPrint"/> <description value="Actions" class="noPrint"/>
</row> </row>
<row class="$row_cont[class]"> <row class="$row_cont[class]">
@ -81,6 +83,7 @@
<menulist class="$cont[ownerClass]"> <menulist class="$cont[ownerClass]">
<menupopup type="select-account" id="${row}[ts_owner]" readonly="true"/> <menupopup type="select-account" id="${row}[ts_owner]" readonly="true"/>
</menulist> </menulist>
<customfields-list id="$row" readonly="true"/>
<hbox class="noPrint"> <hbox class="noPrint">
<button image="view" label="View" id="view[$row_cont[ts_id]]" onclick="window.open(egw::link('/index.php','menuaction=timesheet.timesheet_ui.view&amp;ts_id=$row_cont[ts_id]'),'_blank','dependent=yes,width=600,height=400,scrollbars=yes,status=yes'); return false;" statustext="View this entry"/> <button image="view" label="View" id="view[$row_cont[ts_id]]" onclick="window.open(egw::link('/index.php','menuaction=timesheet.timesheet_ui.view&amp;ts_id=$row_cont[ts_id]'),'_blank','dependent=yes,width=600,height=400,scrollbars=yes,status=yes'); return false;" statustext="View this entry"/>
<button image="edit" label="Edit" id="edit[$row_cont[ts_id]]" statustext="Edit this entry" onclick="window.open(egw::link('/index.php','menuaction=timesheet.timesheet_ui.edit&amp;ts_id=$row_cont[ts_id]'),'_blank','dependent=yes,width=600,height=400,scrollbars=yes,status=yes'); return false;"/> <button image="edit" label="Edit" id="edit[$row_cont[ts_id]]" statustext="Edit this entry" onclick="window.open(egw::link('/index.php','menuaction=timesheet.timesheet_ui.edit&amp;ts_id=$row_cont[ts_id]'),'_blank','dependent=yes,width=600,height=400,scrollbars=yes,status=yes'); return false;"/>