forked from extern/egroupware
History for InfoLog (incl. the ability to disable real deletes) and delete notifications
This commit is contained in:
parent
4b8657013e
commit
9e49e8c50d
@ -195,14 +195,16 @@ class bo_tracking
|
||||
$changes = 0;
|
||||
foreach($this->field2history as $name => $status)
|
||||
{
|
||||
if ($old[$name] != $data[$name])
|
||||
if ($old[$name] != $data[$name] && !(!$old[$name] && !$data[$name]))
|
||||
{
|
||||
if (!is_object($this->historylog))
|
||||
{
|
||||
require_once(EGW_API_INC.'/class.historylog.inc.php');
|
||||
$this->historylog =& new historylog($this->app);
|
||||
}
|
||||
$this->historylog->add($status,$data[$this->id_field],$data[$name],$old[$name]);
|
||||
$this->historylog->add($status,$data[$this->id_field],
|
||||
is_array($data[$name]) ? implode(',',$data[$name]) : $data[$name],
|
||||
is_array($old[$name]) ? implode(',',$old[$name]) : $old[$name]);
|
||||
++$changes;
|
||||
}
|
||||
}
|
||||
@ -536,10 +538,18 @@ class bo_tracking
|
||||
$link .= '&'.$this->id_field.'='.$data[$this->id_field];
|
||||
}
|
||||
}
|
||||
elseif (($view = $GLOBALS['egw']->link->view($this->app,$data[$this->id_field])))
|
||||
else
|
||||
{
|
||||
$link = $GLOBALS['egw']->link('/index.php',$view);
|
||||
$popup = $GLOBALS['egw']->link->is_popup($this->app,'view');
|
||||
if (!is_object($GLOBALS['egw']->link))
|
||||
{
|
||||
require_once(EGW_API_INC.'/class.bolink.inc.php');
|
||||
$GLOBALS['egw']->link =& new bolink();
|
||||
}
|
||||
if (($view = $GLOBALS['egw']->link->view($this->app,$data[$this->id_field])))
|
||||
{
|
||||
$link = $GLOBALS['egw']->link('/index.php',$view);
|
||||
$popup = $GLOBALS['egw']->link->is_popup($this->app,'view');
|
||||
}
|
||||
}
|
||||
if ($link{0} == '/')
|
||||
{
|
||||
@ -585,7 +595,7 @@ class bo_tracking
|
||||
// if there's no old entry, the entry is not modified by definition
|
||||
// if both values are '', 0 or null, we count them as equal too
|
||||
$modified = $old && $data[$name] != $old[$name] && !(!$data[$name] && !$old[$name]);
|
||||
//if ($modified) error_log("data[$name]='{$data[$name]}', old[$name]='{$old[$name]}' --> modified=".(int)$modified);
|
||||
//if ($modified) error_log("data[$name]=".print_r($data[$name],true).", old[$name]=".print_r($old[$name],true)." --> modified=".(int)$modified);
|
||||
if (empty($detail['value']) && !$modified) continue; // skip unchanged, empty values
|
||||
|
||||
$body .= $this->format_line($html_email,$detail['type'],$modified,
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
include_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.soinfolog.inc.php');
|
||||
|
||||
define('EGW_ACL_UNDELETE',EGW_ACL_CUSTOM_1); // undelete right
|
||||
|
||||
/**
|
||||
* This class is the BO-layer of InfoLog, it also handles xmlrpc requests
|
||||
*/
|
||||
@ -32,7 +34,7 @@ class boinfolog
|
||||
var $link;
|
||||
var $vfs;
|
||||
var $vfs_basedir='/infolog';
|
||||
var $valid_pathes = array();
|
||||
var $link_pathes = array();
|
||||
var $send_file_ips = array();
|
||||
|
||||
var $xmlrpc_methods = array();
|
||||
@ -111,6 +113,18 @@ class boinfolog
|
||||
* @var int
|
||||
*/
|
||||
var $user;
|
||||
/**
|
||||
* History loggin: ''=no, 'history'=history & delete allowed, 'history_admin_delete', 'history_no_delete'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $history;
|
||||
/**
|
||||
* Instance of infolog_tracking, only instaciated if needed!
|
||||
*
|
||||
* @var infolog_tracking
|
||||
*/
|
||||
var $tracking;
|
||||
|
||||
/**
|
||||
* Constructor Infolog BO
|
||||
@ -151,11 +165,11 @@ class boinfolog
|
||||
'billed' => 'billed' ), // --> DONE
|
||||
'note' => array(
|
||||
'ongoing' => 'ongoing', // iCal has no status on notes
|
||||
'done' => 'done'),
|
||||
'done' => 'done' ),
|
||||
'email' => array(
|
||||
'ongoing' => 'ongoing', // iCal has no status on notes
|
||||
'done' => 'done'
|
||||
));
|
||||
'done' => 'done' ),
|
||||
);
|
||||
|
||||
if (!is_object($GLOBALS['egw']->link) && $instanciate_link)
|
||||
{
|
||||
@ -211,14 +225,15 @@ class boinfolog
|
||||
}
|
||||
if ($save_config) $this->config->save_repository();
|
||||
}
|
||||
if (count(explode(',',$this->config->config_data['responsible_edit'])))
|
||||
if (is_array($this->config->config_data['responsible_edit']))
|
||||
{
|
||||
$this->responsible_edit = array_merge($this->responsible_edit,explode(',',$this->config->config_data['responsible_edit']));
|
||||
$this->responsible_edit = array_merge($this->responsible_edit,$this->config->config_data['responsible_edit']);
|
||||
}
|
||||
if ($this->config->config_data['implicit_rights'] == 'edit')
|
||||
{
|
||||
$this->implicit_rights = 'edit';
|
||||
}
|
||||
$this->history = $this->config->config_data['history'];
|
||||
}
|
||||
// sort types by there translation
|
||||
foreach($this->enums['type'] as $key => $val)
|
||||
@ -305,6 +320,33 @@ class boinfolog
|
||||
{
|
||||
return $cache[$info_id][$required_rights];
|
||||
}
|
||||
// handle delete for the various history modes
|
||||
if ($this->history)
|
||||
{
|
||||
if (!is_array($info) && !($info = $this->so->read($info_id))) return false;
|
||||
|
||||
if ($info['info_status'] == 'deleted' &&
|
||||
($required_rights == EGW_ACL_EDIT || // no edit rights for deleted entries
|
||||
$required_rights == EGW_ACL_ADD || // no add rights for deleted entries
|
||||
$required_rights == EGW_ACL_DELETE && ($this->history == 'history_no_delete' || // no delete at all!
|
||||
$this->history == 'history_admin_delete' && !isset($GLOBALS['egw_info']['user']['apps']['admin'])))) // delete only for admins
|
||||
{
|
||||
return $cache[$info_id][$required_rights] = false;
|
||||
}
|
||||
if ($required_rights == EGW_ACL_UNDELETE)
|
||||
{
|
||||
if ($info['info_status'] != 'deleted')
|
||||
{
|
||||
return $cache[$info_id][$required_rights] = false; // can only undelete deleted items
|
||||
}
|
||||
// undelete requires edit rights
|
||||
return $cache[$info_id][$required_rights] = $this->so->check_access( $info,EGW_ACL_EDIT,$this->implicit_rights == 'edit' );
|
||||
}
|
||||
}
|
||||
elseif ($required_rights == EGW_ACL_UNDELETE)
|
||||
{
|
||||
return $cache[$info_id][$required_rights] = false;
|
||||
}
|
||||
return $cache[$info_id][$required_rights] = $this->so->check_access( $info,$required_rights,$this->implicit_rights == 'edit' );
|
||||
}
|
||||
|
||||
@ -467,18 +509,59 @@ class boinfolog
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
$this->link->unlink(0,'infolog',$info_id);
|
||||
|
||||
$info = $this->read($info_id);
|
||||
|
||||
$this->so->delete($info_id,$delete_children,$new_parent);
|
||||
|
||||
$GLOBALS['egw']->contenthistory->updateTimeStamp('infolog_'.$info['info_type'], $info_id, 'delete', time());
|
||||
// check if we have children and delete or re-parent them
|
||||
if (($children = $this->so->get_children($info_id)))
|
||||
{
|
||||
foreach($children as $id => $owner)
|
||||
{
|
||||
if ($delete_children && $this->so->grants[$owner] & EGW_ACL_DELETE)
|
||||
{
|
||||
$this->delete($id,$delete_children,$new_parent); // call ourself recursive to delete the child
|
||||
}
|
||||
else // dont delete or no rights to delete the child --> re-parent it
|
||||
{
|
||||
$this->so->write(array(
|
||||
'info_id' => $id,
|
||||
'info_parent_id' => $new_parent,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!($info = $this->read($info_id))) return false; // should not happen
|
||||
|
||||
$deleted = $info;
|
||||
$deleted['info_status'] = 'deleted';
|
||||
$deleted['info_datemodified'] = time();
|
||||
$deleted['info_modifier'] = $this->user;
|
||||
|
||||
// if we have history switched on and not an already deleted item --> set only status deleted
|
||||
if ($this->history && $info['info_status'] != 'deleted')
|
||||
{
|
||||
if ($info['info_status'] == 'deleted') return false; // entry already deleted
|
||||
|
||||
$this->so->write($deleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->so->delete($info_id,false); // we delete the children via bo to get all notifications!
|
||||
}
|
||||
if ($info['info_status'] != 'deleted') // dont notify of final purge of already deleted items
|
||||
{
|
||||
$this->link->unlink(0,'infolog',$info_id);
|
||||
|
||||
$GLOBALS['egw']->contenthistory->updateTimeStamp('infolog_'.$info['info_type'], $info_id, 'delete', time());
|
||||
|
||||
// send email notifications and do the history logging
|
||||
require_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_tracking.inc.php');
|
||||
if (!is_object($this->tracking))
|
||||
{
|
||||
$this->tracking =& new infolog_tracking($this);
|
||||
}
|
||||
$this->tracking->track($deleted,$info,$this->user);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* writes the given $values to InfoLog, a new entry gets created if info_id is not set or 0
|
||||
*
|
||||
@ -517,6 +600,10 @@ class boinfolog
|
||||
{
|
||||
$status_only = !!array_intersect($responsible,array_keys($GLOBALS['egw']->accounts->memberships($this->user)));
|
||||
}
|
||||
if (!$status_only && $values['info_status'] != 'deleted')
|
||||
{
|
||||
$status_only = $undelete = $this->check_access($values['info_id'],EGW_ACL_UNDELETE);
|
||||
}
|
||||
}
|
||||
if ($values['info_id'] && !$this->check_access($values['info_id'],EGW_ACL_EDIT) && !$status_only ||
|
||||
!$values['info_id'] && $values['info_id_parent'] && !$this->check_access($values['info_id_parent'],EGW_ACL_ADD))
|
||||
@ -531,7 +618,7 @@ class boinfolog
|
||||
{
|
||||
$values = $this->xmlrpc2data($values);
|
||||
}
|
||||
if ($status_only) // make sure only status gets writen
|
||||
if ($status_only && !$undelete) // make sure only status gets writen
|
||||
{
|
||||
$set_completed = !$values['info_datecompleted'] && // set date completed of finished job, only if its not already set
|
||||
(in_array($values['info_status'],array('done','billed','cancelled')) || (int)$values['info_percent'] == 100);
|
||||
@ -606,7 +693,7 @@ class boinfolog
|
||||
$values['info_modifier'] = $this->so->user;
|
||||
}
|
||||
$to_write = $values;
|
||||
if ($status_only) $values = array_merge($backup_values,$values);
|
||||
if ($status_only && !$undelete) $values = array_merge($backup_values,$values);
|
||||
// convert user- to system-time
|
||||
foreach($this->timestamps as $time)
|
||||
{
|
||||
@ -623,7 +710,7 @@ class boinfolog
|
||||
{
|
||||
$values = $this->read($info_id);
|
||||
}
|
||||
if($values['info_id'])
|
||||
if($values['info_id'] && $old['info_status'] != 'deleted')
|
||||
{
|
||||
// update
|
||||
$GLOBALS['egw']->contenthistory->updateTimeStamp(
|
||||
@ -640,7 +727,11 @@ class boinfolog
|
||||
);
|
||||
}
|
||||
$values['info_id'] = $info_id;
|
||||
|
||||
|
||||
if (!is_array($values['info_responsible'])) // this should not happen, bug it does ;-)
|
||||
{
|
||||
$values['info_responsible'] = $values['info_responsible'] ? explode(',',$values['info_responsible']) : array();
|
||||
}
|
||||
// create (and remove) links in custom fields
|
||||
$this->update_customfield_links($values,$old);
|
||||
|
||||
|
@ -46,7 +46,33 @@ class infolog_tracking extends bo_tracking
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $field2history = array();
|
||||
var $field2history = array(
|
||||
'info_type' => 'Ty',
|
||||
'info_from' => 'Fr',
|
||||
'info_addr' => 'Ad',
|
||||
'info_link_id' => 'Li',
|
||||
'info_cat' => 'Ca',
|
||||
'info_priority' => 'Pr',
|
||||
'info_owner' => 'Ow',
|
||||
'info_access' => 'Ac',
|
||||
'info_status' => 'St',
|
||||
'info_percent' => 'Pe',
|
||||
'info_datecompleted' => 'Co',
|
||||
'info_location' => 'Lo',
|
||||
'info_startdate' => 'st',
|
||||
'info_enddate' => 'En',
|
||||
'info_responsible' => 'Re',
|
||||
'info_subject' => 'Su',
|
||||
'info_des' => 'De',
|
||||
'info_location' => 'Lo',
|
||||
// PM fields
|
||||
'info_planned_time' => 'pT',
|
||||
'info_used_time' => 'uT',
|
||||
'pl_id' => 'pL',
|
||||
'info_price' => 'pr',
|
||||
// all custom fields together
|
||||
'custom' => '#c',
|
||||
);
|
||||
/**
|
||||
* Translate field-names to labels
|
||||
*
|
||||
@ -56,9 +82,11 @@ class infolog_tracking extends bo_tracking
|
||||
'info_type' => 'Type',
|
||||
'info_from' => 'Contact',
|
||||
'info_addr' => 'Phone/Email',
|
||||
'info_link_id' => 'primary link',
|
||||
'info_cat' => 'Category',
|
||||
'info_priority' => 'Priority',
|
||||
'info_owner' => 'Owner',
|
||||
'info_access' => 'Access',
|
||||
'info_status' => 'Status',
|
||||
'info_percent' => 'Completed',
|
||||
'info_datecompleted' => 'Date completed',
|
||||
@ -67,6 +95,14 @@ class infolog_tracking extends bo_tracking
|
||||
'info_enddate' => 'Enddate',
|
||||
'info_responsible' => 'Responsible',
|
||||
'info_subject' => 'Subject',
|
||||
'info_des' => 'Description',
|
||||
// PM fields
|
||||
'info_planned_time' => 'planned time',
|
||||
'info_used_time' => 'used time',
|
||||
'pl_id' => 'pricelist',
|
||||
'info_price' => 'price',
|
||||
// custom fields
|
||||
'custom' => 'custom fields'
|
||||
);
|
||||
|
||||
/**
|
||||
@ -90,29 +126,6 @@ class infolog_tracking extends bo_tracking
|
||||
$this->infolog =& $boinfolog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the changes in one entry $data, by comparing it with the last version in $old
|
||||
*
|
||||
* Reimplemented to fix some fields, who otherwise allways show up as modified
|
||||
*
|
||||
* @param array $data current entry
|
||||
* @param array $old=null old/last state of the entry or null for a new entry
|
||||
* @param int $user=null user who made the changes, default to current user
|
||||
* @return int/boolean false on error, integer number of changes logged or true for new entries ($old == null)
|
||||
*/
|
||||
function track($data,$old=null,$user=null)
|
||||
{
|
||||
if ($old)
|
||||
{
|
||||
foreach($this->infolog->timestamps as $name)
|
||||
{
|
||||
if (!$old[$name]) $old[$name] = '';
|
||||
}
|
||||
}
|
||||
return parent::track($data,$old,$user);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a notification-config value
|
||||
*
|
||||
@ -129,22 +142,54 @@ class infolog_tracking extends bo_tracking
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subject for a given entry
|
||||
*
|
||||
* Reimpleneted to use a New|deleted|modified prefix.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $old
|
||||
* @return string
|
||||
*/
|
||||
function get_subject($data,$old)
|
||||
{
|
||||
if (!$old || $old['info_status'] == 'deleted')
|
||||
{
|
||||
$prefix = lang('New %1',lang($this->infolog->enums['type'][$data['info_type']]));
|
||||
}
|
||||
elseif($data['info_status'] == 'deleted')
|
||||
{
|
||||
$prefix = lang('%1 deleted',lang($this->infolog->enums['type'][$data['info_type']]));
|
||||
}
|
||||
else
|
||||
{
|
||||
$prefix = lang('%1 modified',lang($this->infolog->enums['type'][$data['info_type']]));
|
||||
}
|
||||
return $prefix.': '.$data['info_subject'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modified / new message (1. line of mail body) for a given entry, can be reimplemented
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $old
|
||||
* @return array/string array(message,user-id,timestamp-in-servertime) or string
|
||||
* @return string
|
||||
*/
|
||||
function get_message($data,$old)
|
||||
{
|
||||
if ($data['message']) return $data['message']; // async notification
|
||||
|
||||
if (!$data['info_datemodified'] || !$old)
|
||||
if (!$old || $old['info_status'] == 'deleted')
|
||||
{
|
||||
return lang('New %1 created by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
|
||||
$GLOBALS['egw']->common->grab_owner_name($this->infolog->user),$this->datetime(time()));
|
||||
}
|
||||
elseif($data['info_status'] == 'deleted')
|
||||
{
|
||||
return lang('%1 deleted by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
|
||||
$GLOBALS['egw']->common->grab_owner_name($data['info_modifier']),
|
||||
$this->datetime($data['info_datemodified']-$this->infolog->tz_offset_s));
|
||||
}
|
||||
return lang('%1 modified by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
|
||||
$GLOBALS['egw']->common->grab_owner_name($data['info_modifier']),
|
||||
$this->datetime($data['info_datemodified']-$this->infolog->tz_offset_s));
|
||||
@ -184,7 +229,7 @@ class infolog_tracking extends bo_tracking
|
||||
'info_cat' => $data['info_cat'] ? $GLOBALS['egw']->categories->id2name($data['info_cat']) : '',
|
||||
'info_priority' => lang($this->infolog->enums['priority'][$data['info_priority']]),
|
||||
'info_owner' => $GLOBALS['egw']->common->grab_owner_name($data['info_owner']),
|
||||
'info_status' => lang($this->infolog->status[$data['info_type']][$data['info_status']]),
|
||||
'info_status' => lang($data['info_status']=='deleted'?'deleted':$this->infolog->status[$data['info_type']][$data['info_status']]),
|
||||
'info_percent' => (int)$data['info_percent'].'%',
|
||||
'info_datecompleted' => $data['info_datecomplete'] ? $this->datetime($data['info_datecompleted']-$this->infolog->tz_offset_s) : '',
|
||||
'info_location' => $data['info_location'],
|
||||
@ -209,7 +254,7 @@ class infolog_tracking extends bo_tracking
|
||||
{
|
||||
foreach($this->infolog->customfields as $name => $field)
|
||||
{
|
||||
if ($field['type2'] && $field['type2'] != $data['info_type']) continue; // different type
|
||||
if ($field['type2'] && !in_array($data['info_type'],explode(',',$field['type2']))) continue; // different type
|
||||
|
||||
if (!$header_done)
|
||||
{
|
||||
@ -219,7 +264,7 @@ class infolog_tracking extends bo_tracking
|
||||
);
|
||||
$header_done = true;
|
||||
}
|
||||
$details[$name] = array(
|
||||
$details['#'.$name] = array(
|
||||
'label' => $field['label'],
|
||||
'value' => $data['#'.$name],
|
||||
);
|
||||
@ -227,4 +272,27 @@ class infolog_tracking extends bo_tracking
|
||||
}
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes to the history log
|
||||
*
|
||||
* Reimplemented to store all customfields in a single field, as the history-log has only 2-char field-ids
|
||||
*
|
||||
* @param array $data current entry
|
||||
* @param array $old=null old/last state of the entry or null for a new entry
|
||||
* @param int number of log-entries made
|
||||
*/
|
||||
function save_history($data,$old)
|
||||
{
|
||||
$data_custom = $old_custom = array();
|
||||
foreach($this->infolog->customfields as $name => $custom)
|
||||
{
|
||||
if (isset($data['#'.$name]) && (string)$data['#'.$name]!=='') $data_custom[] = $custom['label'].': '.$data['#'.$name];
|
||||
if (isset($old['#'.$name]) && (string)$old['#'.$name]!=='') $old_custom[] = $custom['label'].': '.$old['#'.$name];
|
||||
}
|
||||
$data['custom'] = implode("\n",$data_custom);
|
||||
$old['custom'] = implode("\n",$old_custom);
|
||||
|
||||
return parent::save_history($data,$old);
|
||||
}
|
||||
}
|
@ -262,21 +262,24 @@ class soinfolog // DB-Layer
|
||||
/**
|
||||
* generate sql to filter based on the status of the log-entry
|
||||
*
|
||||
* @param $filter done = done or billed, open = not ()done or billed), offer = offer
|
||||
* @param string $filter done = done or billed, open = not (done, billed, cancelled or deleted), offer = offer
|
||||
* @param boolean $prefix_and=true if true prefix the fileter with ' AND '
|
||||
* @return string the necesary sql
|
||||
*/
|
||||
function statusFilter($filter = '')
|
||||
function statusFilter($filter = '',$prefix_and=true)
|
||||
{
|
||||
preg_match('/(done|open|offer)/',$filter,$vars);
|
||||
preg_match('/(done|open|offer|deleted)/',$filter,$vars);
|
||||
$filter = $vars[1];
|
||||
|
||||
switch ($filter)
|
||||
{
|
||||
case 'done': return " AND info_status IN ('done','billed','cancelled')";
|
||||
case 'open': return " AND NOT (info_status IN ('done','billed','cancelled'))";
|
||||
case 'offer': return " AND info_status = 'offer'";
|
||||
case 'done': $filter = "info_status IN ('done','billed','cancelled')"; break;
|
||||
case 'open': $filter = "NOT (info_status IN ('done','billed','cancelled','deleted'))"; break;
|
||||
case 'offer': $filter = "info_status = 'offer'"; break;
|
||||
case 'deleted': $filter = "info_status = 'deleted'"; break;
|
||||
default: $filter = "info_status <> 'deleted'"; break;
|
||||
}
|
||||
return '';
|
||||
return ($prefix_and ? ' AND ' : '').$filter;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -444,6 +447,26 @@ class soinfolog // DB-Layer
|
||||
// set parent_id to $new_parent or 0 for all not deleted children
|
||||
$this->db->update($this->info_table,array('info_id_parent'=>$new_parent),array('info_id_parent'=>$info_id),__LINE__,__FILE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array with children of $info_id as info_id => info_owner pairs
|
||||
*
|
||||
* @param int $info_id
|
||||
* @return array with info_id => info_owner pairs
|
||||
*/
|
||||
function get_children($info_id)
|
||||
{
|
||||
$this->db->select($this->info_table,'info_id,info_owner',array(
|
||||
'info_id_parent' => $info_id,
|
||||
),__LINE__,__FILE__);
|
||||
|
||||
$children = array();
|
||||
while (($row = $this->db->row(true)))
|
||||
{
|
||||
$children[$row['info_id']] = $row['info_owner'];
|
||||
}
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* changes or deletes entries with a spezified owner (for hook_delete_account)
|
||||
@ -466,6 +489,7 @@ class soinfolog // DB-Layer
|
||||
{
|
||||
$this->db->update($this->info_table,array('info_owner'=>$new_owner),array('info_owner'=>$owner),__LINE__,__FILE__);
|
||||
}
|
||||
// ToDo: does not work with multiple owners!!!
|
||||
$this->db->update($this->info_table,array('info_responsible'=>$new_owner),array('info_responsible'=>$owner),__LINE__,__FILE__);
|
||||
}
|
||||
|
||||
@ -560,7 +584,7 @@ class soinfolog // DB-Layer
|
||||
//echo "<p>anzSubs($info_id) = ".$this->db->f(0)." ($sql)</p>\n";
|
||||
return $this->db->f(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* searches InfoLog for a certain pattern in $query
|
||||
*
|
||||
@ -743,7 +767,7 @@ class soinfolog // DB-Layer
|
||||
{
|
||||
$users[] = $this->db->f(0);
|
||||
}
|
||||
$this->db->select($this->info_table,'DISTINCT info_responsible',str_replace(' AND ','',$this->statusFilter('open')),__LINE__,__FILE__);
|
||||
$this->db->select($this->info_table,'DISTINCT info_responsible',$this->statusFilter('open',false),__LINE__,__FILE__);
|
||||
while ($this->db->next_record())
|
||||
{
|
||||
foreach(explode(',',$this->db->f(0)) as $responsible)
|
||||
|
@ -142,6 +142,10 @@ class uiinfolog
|
||||
$this->duration_format = str_replace(',','',$pm_config->config_data['duration_units']).','.$pm_config->config_data['hours_per_workday'];
|
||||
unset($pm_config);
|
||||
}
|
||||
if ($this->bo->history)
|
||||
{
|
||||
$this->filters['deleted'] = 'deleted';
|
||||
}
|
||||
/* these are just for testing of the notifications
|
||||
for($i = -1; $i <= 3; ++$i)
|
||||
{
|
||||
@ -196,7 +200,8 @@ class uiinfolog
|
||||
$readonlys["close[$id]"] = $done || ($readonlys["edit_status[$id]"] =
|
||||
!($this->bo->check_access($info,EGW_ACL_EDIT) || $this->bo->is_responsible($info)));
|
||||
$readonlys["edit_status[$id]"] = $readonlys["edit_percent[$id]"] =
|
||||
!$this->bo->check_access($info,EGW_ACL_EDIT) && !$this->bo->is_responsible($info);
|
||||
!$this->bo->check_access($info,EGW_ACL_EDIT) && !$this->bo->is_responsible($info) &&
|
||||
!$this->bo->check_access($info,EGW_ACL_UNDELETE); // undelete is handled like status edit
|
||||
$readonlys["delete[$id]"] = !$this->bo->check_access($info,EGW_ACL_DELETE);
|
||||
$readonlys["sp[$id]"] = !$this->bo->check_access($info,EGW_ACL_ADD);
|
||||
$readonlys["view[$id]"] = $info['info_anz_subs'] < 1;
|
||||
@ -237,7 +242,8 @@ class uiinfolog
|
||||
}
|
||||
}
|
||||
$info['info_type_label'] = $this->bo->enums['type'][$info['info_type']];
|
||||
$info['info_status_label'] = $this->bo->status[$info['info_type']][$info['info_status']];
|
||||
$info['info_status_label'] = isset($this->bo->status[$info['info_type']][$info['info_status']]) ?
|
||||
$this->bo->status[$info['info_type']][$info['info_status']] : $info['info_status'];
|
||||
|
||||
if (!$this->prefs['show_percent'] || $this->prefs['show_percent'] == 2 && !$details)
|
||||
{
|
||||
@ -691,7 +697,7 @@ class uiinfolog
|
||||
*/
|
||||
function edit($content = null,$action = '',$action_id=0,$type='',$referer='')
|
||||
{
|
||||
$tabs = 'description|links|delegation|project|customfields';
|
||||
$tabs = 'description|links|delegation|project|customfields|history';
|
||||
|
||||
if (is_array($content))
|
||||
{
|
||||
@ -722,9 +728,10 @@ class uiinfolog
|
||||
{
|
||||
$old = $this->bo->read($info_id);
|
||||
$status_only = $this->bo->is_responsible($old);
|
||||
$undelete = $this->bo->check_access($old,EGW_ACL_UNDELETE);
|
||||
}
|
||||
}
|
||||
if (($button == 'save' || $button == 'apply') && (!$info_id || $edit_acl || $status_only))
|
||||
if (($button == 'save' || $button == 'apply') && (!$info_id || $edit_acl || $status_only || $undelete))
|
||||
{
|
||||
if ($content['info_contact'])
|
||||
{
|
||||
@ -912,7 +919,9 @@ class uiinfolog
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($info_id && !$this->bo->check_access($info_id,EGW_ACL_EDIT) && !$this->bo->is_responsible($content))
|
||||
if ($info_id && !$this->bo->check_access($info_id,EGW_ACL_EDIT) &&
|
||||
!($undelete = $this->bo->check_access($info_id,EGW_ACL_UNDELETE)) &&
|
||||
!$this->bo->is_responsible($content))
|
||||
{
|
||||
if ($no_popup)
|
||||
{
|
||||
@ -1018,7 +1027,7 @@ class uiinfolog
|
||||
}
|
||||
$preserv = $content;
|
||||
// for implizit edit of responsible user make all fields readonly, but status and percent
|
||||
if ($info_id && !$this->bo->check_access($info_id,EGW_ACL_EDIT) && $this->bo->is_responsible($content))
|
||||
if ($info_id && !$this->bo->check_access($info_id,EGW_ACL_EDIT) && $this->bo->is_responsible($content) && !$undelete)
|
||||
{
|
||||
$content['status_only'] = !in_array('link_to',$this->bo->responsible_edit);
|
||||
foreach(array_diff(array_merge(array_keys($content),array('pm_id')),$this->bo->responsible_edit) as $name)
|
||||
@ -1032,6 +1041,9 @@ class uiinfolog
|
||||
$readonlys['#'.$name] = true;
|
||||
}
|
||||
}
|
||||
// ToDo: use the old status before the delete
|
||||
if ($undelete) $content['info_status'] = $this->bo->status['defaults'][$content['info_type']];
|
||||
|
||||
$content['hide_from_css'] = $content['info_custom_from'] ? '' : 'hideFrom';
|
||||
|
||||
if (!($readonlys['button[delete]'] = !$info_id || !$this->bo->check_access($info_id,EGW_ACL_DELETE)))
|
||||
@ -1051,7 +1063,7 @@ class uiinfolog
|
||||
}
|
||||
else
|
||||
{
|
||||
$readonlys[$tabs] = array('customfields' => true);
|
||||
$readonlys[$tabs]['customfields'] = true;
|
||||
}
|
||||
if (!isset($GLOBALS['egw_info']['user']['apps']['projectmanager']))
|
||||
{
|
||||
@ -1066,6 +1078,44 @@ class uiinfolog
|
||||
$old_pm_id = is_array($pm_links) ? array_shift($pm_links) : $content['old_pm_id'];
|
||||
if (!isset($content['pm_id']) && $old_pm_id) $content['pm_id'] = $old_pm_id;
|
||||
|
||||
if ($info_id && $this->bo->history)
|
||||
{
|
||||
$content['history'] = array(
|
||||
'id' => $info_id,
|
||||
'app' => 'infolog',
|
||||
'status-widgets' => array(
|
||||
'Ty' => $types,
|
||||
//'Li', // info_link_id
|
||||
'Ca' => 'select-cat',
|
||||
'Pr' => $this->bo->enums['priority'],
|
||||
'Ow' => 'select-account',
|
||||
//'Ac', // info_access: private||public
|
||||
'St' => $this->bo->status[$content['info_type']]+array('deleted' => 'deleted'),
|
||||
'Pe' => 'select-percent',
|
||||
'Co' => 'date-time',
|
||||
'st' => 'date-time',
|
||||
'En' => 'date',
|
||||
'Re' => 'select-account',
|
||||
// PM fields, ToDo: access control!!!
|
||||
'pT' => 'date-duration',
|
||||
'uT' => 'date-duration',
|
||||
// 'pL' => 'projectmanager-pricelist',
|
||||
'pr' => 'float',
|
||||
),
|
||||
);
|
||||
$history_stati = array();
|
||||
require_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_tracking.inc.php');
|
||||
$tracking = new infolog_tracking($this);
|
||||
foreach($tracking->field2history as $field => $history)
|
||||
{
|
||||
$history_stati[$history] = $tracking->field2label[$field];
|
||||
}
|
||||
unset($tracking);
|
||||
}
|
||||
else
|
||||
{
|
||||
$readonlys[$tabs]['history'] = true;
|
||||
}
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('InfoLog').' - '.
|
||||
($content['status_only'] ? lang('Edit Status') : lang('Edit'));
|
||||
$GLOBALS['egw_info']['flags']['params']['manual'] = array('page' => ($info_id ? 'ManualInfologEdit' : 'ManualInfologAdd'));
|
||||
@ -1074,7 +1124,8 @@ class uiinfolog
|
||||
'info_type' => $types,
|
||||
'info_priority' => $this->bo->enums['priority'],
|
||||
'info_confirm' => $this->bo->enums['confirm'],
|
||||
'info_status' => $this->bo->status[$content['info_type']]
|
||||
'info_status' => $this->bo->status[$content['info_type']],
|
||||
'status' => $history_stati,
|
||||
),$readonlys,$preserv+array( // preserved values
|
||||
'info_id' => $info_id,
|
||||
'action' => $action,
|
||||
@ -1110,6 +1161,33 @@ class uiinfolog
|
||||
return $icon ? $this->html->image('infolog',$icon,lang($alt),'border=0') : lang($alt);
|
||||
}
|
||||
|
||||
/**
|
||||
* stripping slashes from an array
|
||||
*
|
||||
* @static
|
||||
* @param array $arr
|
||||
* @return array
|
||||
*/
|
||||
function array_stripslashes($arr)
|
||||
{
|
||||
foreach($arr as $key => $val)
|
||||
{
|
||||
if (is_array($val))
|
||||
{
|
||||
$arr[$key] = self::array_stripslashes($var);
|
||||
}
|
||||
else
|
||||
{
|
||||
$arr[$key] = stripslashes($val);
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infolog's site configuration
|
||||
*
|
||||
*/
|
||||
function admin( )
|
||||
{
|
||||
$fields = array(
|
||||
@ -1126,41 +1204,43 @@ class uiinfolog
|
||||
);
|
||||
if($_POST['save'] || $_POST['apply'])
|
||||
{
|
||||
$this->link_pathes = $this->bo->send_file_ips = array();
|
||||
if (get_magic_quotes_gpc())
|
||||
{
|
||||
$_POST = self::array_stripslashes($_POST);
|
||||
}
|
||||
$this->bo->config->config_data['link_pathes'] = $this->bo->link_pathes = array();
|
||||
$this->bo->config->config_data['send_file_ips'] = $this->bo->send_file_ips = array();
|
||||
|
||||
$valid = get_var('valid',Array('POST'));
|
||||
$trans = get_var('trans',Array('POST'));
|
||||
$ip = get_var('ip',Array('POST'));
|
||||
while(list($key,$val) = each($valid))
|
||||
foreach($valid as $key => $val)
|
||||
{
|
||||
if($val = stripslashes($val))
|
||||
if($val)
|
||||
{
|
||||
$this->link_pathes[$val] = stripslashes($trans[$key]);
|
||||
$this->bo->send_file_ips[$val] = stripslashes($ip[$key]);
|
||||
$this->bo->config->config_data['link_pathes'][$val] = $this->bo->link_pathes[$val] = $trans[$key];
|
||||
$this->bo->config->config_data['send_file_ips'][$val] = $this->bo->send_file_ips[$val] = $ip[$key];
|
||||
}
|
||||
}
|
||||
$this->bo->responsible_edit = array('info_status','info_percent','info_datecompleted');
|
||||
|
||||
if ($_POST['responsible_edit'])
|
||||
{
|
||||
$extra = array_intersect($_POST['responsible_edit'],array_keys($fields));
|
||||
$this->bo->responsible_edit = array_merge($this->bo->responsible_edit,$extra);
|
||||
$this->bo->config->config_data['responsible_edit'] = $this->bo->responsible_edit = array_merge($this->bo->responsible_edit,$extra);
|
||||
}
|
||||
$this->bo->implicit_rights = $_POST['implicit_rights'] == 'edit' ? 'edit' : 'read';
|
||||
$this->bo->config->config_data['implicit_rights'] = $this->bo->implicit_rights = $_POST['implicit_rights'] == 'edit' ? 'edit' : 'read';
|
||||
|
||||
$this->bo->config->config_data['history'] = $this->bo->history = $_POST['history'];
|
||||
|
||||
$this->bo->config->config_data += array( // only "adding" the changed items, to not delete other config like custom fields
|
||||
'link_pathes' => $this->link_pathes,
|
||||
'send_file_ips' => $this->bo->send_file_ips,
|
||||
'implicit_rights' => $this->bo->implicit_rights,
|
||||
'responsible_edit' => is_array($extra) ? implode(',',$extra) : $extra,
|
||||
);
|
||||
$this->bo->config->save_repository(True);
|
||||
}
|
||||
if($_POST['cancel'] || $_POST['save'])
|
||||
{
|
||||
$GLOBALS['egw']->redirect_link('/admin/index.php');
|
||||
$GLOBALS['egw']->redirect_link('/infolog/index.php');
|
||||
}
|
||||
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('InfoLog').' - '.lang('Configuration');
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('InfoLog').' - '.lang('Site configuration');
|
||||
$GLOBALS['egw']->common->egw_header();
|
||||
|
||||
$GLOBALS['egw']->template->set_file(array('info_admin_t' => 'admin.tpl'));
|
||||
@ -1183,16 +1263,24 @@ class uiinfolog
|
||||
'cancel_button' => $this->html->submit_button('cancel','Cancel'),
|
||||
'lang_valid' => lang('valid path on clientside<br>eg. \\\\Server\\Share or e:\\'),
|
||||
'lang_trans' => lang('path on (web-)serverside<br>eg. /var/samba/Share'),
|
||||
'lang_ip' => lang('reg. expr. for local IP\'s<br>eg. ^192\\.168\\.1\\.')
|
||||
'lang_ip' => lang('reg. expr. for local IP\'s<br>eg. ^192\\.168\\.1\\.'),
|
||||
'lang_history'=> lang('History logging'),
|
||||
'lang_history2'=> lang('History logging and deleting of items'),
|
||||
'history' => $this->html->select('history',$this->bo->history,array(
|
||||
'' => lang('No'),
|
||||
'history' => lang('Yes, with purging of deleted items possible'),
|
||||
'history_admin_delete' => lang('Yes, only admins can purge deleted items'),
|
||||
'history_no_delete' => lang('Yes, noone can purge deleted items'),
|
||||
))
|
||||
));
|
||||
|
||||
if (!is_array($this->bo->send_file_ips))
|
||||
{
|
||||
$this->bo->send_file_ips = $this->link_pathes = array();
|
||||
$this->bo->send_file_ips = $this->bo->link_pathes = array();
|
||||
}
|
||||
$i = 0; @reset($this->link_pathes);
|
||||
$i = 0; @reset($this->bo->link_pathes);
|
||||
do {
|
||||
list($valid,$trans) = @each($this->link_pathes);
|
||||
list($valid,$trans) = @each($this->bo->link_pathes);
|
||||
$GLOBALS['egw']->template->set_var(array(
|
||||
'tr_color' => $i & 1 ? 'row_off' : 'row_on',
|
||||
'num' => $i+1,
|
||||
@ -1322,23 +1410,6 @@ class uiinfolog
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes langfile with all templates and messages registered here
|
||||
*
|
||||
* called via [write Langfile] in the etemplate-editor or as http://domain/egroupware/index.php?menuaction=infolog.uiinfolog.writeLangFile
|
||||
*/
|
||||
function writeLangFile()
|
||||
{
|
||||
$extra = $this->messages + $this->filters;
|
||||
$enums = $this->bo->enums + $this->bo->status;
|
||||
unset($enums['defaults']);
|
||||
foreach($enums as $key => $msg_arr)
|
||||
{
|
||||
$extra += $msg_arr;
|
||||
}
|
||||
return $this->tmpl->writeLangFile('infolog','en',$extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* shows infolog in other applications
|
||||
*
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,7 @@
|
||||
%1 days in advance infolog de %1 Tage im Vorraus
|
||||
%1 deleted infolog de %1 gelöscht
|
||||
%1 deleted by %2 at %3 infolog de %1 wurde von %2 am %3 gelöscht
|
||||
%1 modified infolog de %1 geändert
|
||||
%1 modified by %2 at %3 infolog de %1 wurde von %2 am %3 geändert
|
||||
%1 records imported infolog de %1 Datensätze importiert
|
||||
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog de %1 Datensätze gelesen (noch nicht importiert, sie können %2zurück%3 gehen und Test Import ausschalten)
|
||||
@ -97,6 +100,7 @@ delete one record by passing its id. infolog de Einen Datensatz spezifiziert dur
|
||||
delete the entry infolog de Eintrag löschen
|
||||
delete this entry infolog de diesen Eintrag löschen
|
||||
delete this entry and all listed sub-entries infolog de Diesen Eintrag und all aufgelisteten Untereinträge löschen
|
||||
deleted infolog de gelöscht
|
||||
deletes the selected typ infolog de löscht den ausgewählten Typ
|
||||
deletes this field infolog de löscht dieses Feld
|
||||
deletes this status infolog de löscht diesen Status
|
||||
@ -142,6 +146,8 @@ from infolog de Von
|
||||
general infolog de Allgemein
|
||||
group owner for infolog de Gruppeneigentümer für
|
||||
high infolog de hoch
|
||||
history logging infolog de Protokollierung der Historie
|
||||
history logging and deleting of items infolog de Protokollierung der Historie und löschen von Einträgen
|
||||
id infolog de Id
|
||||
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog de Wenn ein Typ einen Gruppeneigentümer hat, gehören alle Einträge dieses Typs der angegebenen Gruppe und NICHT dem Benutzer der sie angelegt hat!
|
||||
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog de Falls nicht gesetzt, wird die Suche und die Filter ausgeblendet für weniger Einträge als "maximale Treffer pro Seite" (in ihren allg. Einstellungen definiert).
|
||||
@ -181,6 +187,7 @@ max length of the input [, length of the inputfield (optional)] infolog de max.
|
||||
name must not be empty !!! infolog de Name darf nicht leer sein !!!
|
||||
name of new type to create infolog de Name des neu anzulegenden Types
|
||||
never hide search and filters infolog de Suche und Filter niemals ausblenden
|
||||
new %1 infolog de Neue %1
|
||||
new %1 created by %2 at %3 infolog de Neue %1 wurde von %2 am %3 angelegt
|
||||
new name infolog de neuer name
|
||||
new search infolog de Neue Suche
|
||||
@ -322,7 +329,6 @@ urgent infolog de Dringend
|
||||
used time infolog de benötigte Zeit
|
||||
valid path on clientside<br>eg. \\server\share or e:\ infolog de gültiger Pfad clientseitig<br>zB. \\Server\Share oder e:\
|
||||
valid path on clientside<br>eg. \servershare or e: infolog de gültiger Pfad clientseitig<br>zB. \\Server\Share oder e:\
|
||||
valid path on clientside<br>eg. servershare or e: infolog de gültiger Pfad clientseitig<br>zB. \\Server\Share oder e:\
|
||||
values for selectbox infolog de Werte für die Auswahlbox
|
||||
view all subs of this entry infolog de alle Untereinträge dieses Eintrag anzeigen
|
||||
view other subs infolog de andere Untereinträge anzeigen
|
||||
@ -337,6 +343,9 @@ will-call infolog de ruft zur
|
||||
write (add or update) a record by passing its fields. infolog de Schreiben (zufügen oder aktualisieren) eines Datensatzes durch Angabe seiner Felder.
|
||||
yes - delete infolog de Ja - Löschen
|
||||
yes - delete including sub-entries infolog de Ja - Löschen einschließlich Untereinträge
|
||||
yes, noone can purge deleted items infolog de Ja, niemand darf gelöschte Einträge bereinigen
|
||||
yes, only admins can purge deleted items infolog de Ja, nur Admins dürfen gelöschte Einträge bereinigen
|
||||
yes, with purging of deleted items possible infolog de Ja, jeder darf gelöschte Einträge bereinigen
|
||||
you can't delete one of the stock types !!! infolog de Sie können keinen der Standardtypen löschen!!!
|
||||
you have entered an invalid ending date infolog de Sie haben ein ungültiges Fälligkeitsdatum eingegeben
|
||||
you have entered an invalid starting date infolog de Sie haben ein ungültiges Startdatum eingegeben
|
||||
|
@ -1,4 +1,7 @@
|
||||
%1 days in advance infolog en %1 days in advance
|
||||
%1 deleted infolog en %1 deleted
|
||||
%1 deleted by %2 at %3 infolog en %1 deleted by %2 at %3
|
||||
%1 modified infolog en %1 modified
|
||||
%1 modified by %2 at %3 infolog en %1 modified by %2 at %3
|
||||
%1 records imported infolog en %1 records imported
|
||||
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog en %1 records read (not yet imported, you may go %2back%3 and uncheck Test Import)
|
||||
@ -97,6 +100,7 @@ delete one record by passing its id. infolog en Delete one record by passing its
|
||||
delete the entry infolog en Delete the entry
|
||||
delete this entry infolog en delete this entry
|
||||
delete this entry and all listed sub-entries infolog en Delete this entry and all listed sub-entries
|
||||
deleted infolog en deleted
|
||||
deletes the selected typ infolog en deletes the selected type
|
||||
deletes this field infolog en deletes this field
|
||||
deletes this status infolog en deletes this status
|
||||
@ -142,6 +146,8 @@ from infolog en From
|
||||
general infolog en General
|
||||
group owner for infolog en Group owner for
|
||||
high infolog en high
|
||||
history logging infolog en History logging
|
||||
history logging and deleting of items infolog en History logging and deleting of items
|
||||
id infolog en Id
|
||||
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog en If a type has a group owner, all entries of that type will be owned by the given group and NOT the user who created it!
|
||||
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog en If not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences).
|
||||
@ -181,6 +187,7 @@ max length of the input [, length of the inputfield (optional)] infolog en max l
|
||||
name must not be empty !!! infolog en Name must not be empty !!!
|
||||
name of new type to create infolog en name of new type to create
|
||||
never hide search and filters infolog en Never hide search and filters
|
||||
new %1 infolog en New %1
|
||||
new %1 created by %2 at %3 infolog en New %1 created by %2 at %3
|
||||
new name infolog en new name
|
||||
new search infolog en New search
|
||||
@ -334,6 +341,9 @@ will-call infolog en will call
|
||||
write (add or update) a record by passing its fields. infolog en Write (add or update) a record by passing its fields.
|
||||
yes - delete infolog en Yes - Delete
|
||||
yes - delete including sub-entries infolog en Yes - Delete including sub-entries
|
||||
yes, noone can purge deleted items infolog en Yes, noone can purge deleted items
|
||||
yes, only admins can purge deleted items infolog en Yes, only admins can purge deleted items
|
||||
yes, with purging of deleted items possible infolog en Yes, with purging of deleted items possible
|
||||
you can't delete one of the stock types !!! infolog en You can't delete one of the stock types !!!
|
||||
you have entered an invalid ending date infolog en You have entered an invalid due date
|
||||
you have entered an invalid starting date infolog en You have entered an invalid starting date
|
||||
|
@ -22,8 +22,12 @@
|
||||
<td colspan="3">{lang_responsible_edit}</td>
|
||||
<td>{responsible_edit}</td>
|
||||
</tr>
|
||||
<tr class="th">
|
||||
<td colspan="4"><b>{lang_history}</b></td>
|
||||
</tr>
|
||||
<tr class="row_off">
|
||||
<td colspan="4"> </td>
|
||||
<td colspan="3">{lang_history2}</td>
|
||||
<td>{history}</td>
|
||||
</tr>
|
||||
<tr class="th">
|
||||
<td colspan="4">{text}</td>
|
||||
|
@ -126,7 +126,19 @@
|
||||
</rows>
|
||||
</grid>
|
||||
</template>
|
||||
<template id="infolog.edit" template="" lang="" group="0" version="1.3.001">
|
||||
<template id="infolog.edit.history" template="" lang="" group="0" version="1.3.002">
|
||||
<grid width="100%" height="250" overflow="auto">
|
||||
<columns>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row valign="top">
|
||||
<historylog id="history"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</template>
|
||||
<template id="infolog.edit" template="" lang="" group="0" version="1.3.002">
|
||||
<grid width="100%">
|
||||
<columns>
|
||||
<column width="103"/>
|
||||
@ -180,6 +192,7 @@
|
||||
<tab label="Delegation" statustext="responsible user, priority"/>
|
||||
<tab label="Projectmanager" statustext="Project settings: price, times"/>
|
||||
<tab label="Customfields" statustext="Custom fields"/>
|
||||
<tab label="History" statustext="Change history"/>
|
||||
</tabs>
|
||||
<tabpanels>
|
||||
<template id="infolog.edit.description"/>
|
||||
@ -187,6 +200,7 @@
|
||||
<template id="infolog.edit.delegation"/>
|
||||
<template id="infolog.edit.project"/>
|
||||
<template id="infolog.edit.customfields"/>
|
||||
<template id="infolog.edit.history"/>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
</row>
|
||||
|
Loading…
Reference in New Issue
Block a user