- modified etag/optimistic locking, to always increment the etag, was before only via the GUI

- replaced own "locks" via egw_cal.cal_edit_user/time with the new egw_vfs::lock() methods
This commit is contained in:
Ralf Becker 2008-05-07 22:12:25 +00:00
parent 641817662b
commit 5d50d41004
6 changed files with 269 additions and 246 deletions

View File

@ -51,7 +51,7 @@ class bocalupdate extends bocal
* @var mixed
*/
var $debug;
/**
* @var string/boolean $log_file filename to enable the login or false for no update-logging
*/
@ -65,12 +65,12 @@ class bocalupdate extends bocal
if ($this->debug > 0) $this->debug_message('bocalupdate::bocalupdate() started',True);
$this->bocal(); // calling the parent constructor
if ($this->debug > 0) $this->debug_message('bocalupdate::bocalupdate() finished',True);
}
/**
* updates or creates an event, it (optionaly) checks for conflicts and sends the necessary notifications
* updates or creates an event, it (optionaly) checks for conflicts and sends the necessary notifications
*
* @param array &$event event-array, on return some values might be changed due to set defaults
* @param boolean $ignore_conflicts=false just ignore conflicts or do a conflict check and return the conflicting events
@ -81,6 +81,7 @@ class bocalupdate extends bocal
*/
function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false)
{
//error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$ignore_conflicts,$touch_modified,$ignore_acl)");
if ($this->debug > 1 || $this->debug == 'update')
{
$this->debug_message('bocalupdate::update(%1,ignore_conflict=%2,touch_modified=%3,ignore_acl=%4)',
@ -90,7 +91,7 @@ class bocalupdate extends bocal
// - new events need start, end and title
// - updated events cant set start, end or title to empty
if (!$event['id'] && (!$event['start'] || !$event['end'] || !$event['title']) ||
$event['id'] && (isset($event['start']) && !$event['start'] || isset($event['end']) && !$event['end'] ||
$event['id'] && (isset($event['start']) && !$event['start'] || isset($event['end']) && !$event['end'] ||
isset($event['title']) && !$event['title']))
{
return false;
@ -104,7 +105,7 @@ class bocalupdate extends bocal
if (!$event['id'] && (!is_array($event['participants']) || !count($event['participants'])))
{
$event['participants'][$event['owner']] = 'U';
}
}
// set the status of the current user to 'A' = accepted
if (isset($event['participants'][$this->user]) && $event['participants'][$this->user] != 'A')
{
@ -113,7 +114,7 @@ class bocalupdate extends bocal
}
// check if user has the permission to update / create the event
if (!$ignore_acl && ($event['id'] && !$this->check_perms(EGW_ACL_EDIT,$event['id']) ||
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) &&
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) &&
!$this->check_perms(EGW_ACL_ADD,0,$event['owner']))
{
// Just update the status, if the user is in the event already
@ -147,7 +148,7 @@ class bocalupdate extends bocal
foreach($event['participants'] as $uid => $status)
{
if ($status[0] == 'R') continue; // ignore rejected participants
if ($uid < 0) // group, check it's members too
{
$users += $GLOBALS['egw']->accounts->members($uid,true);
@ -187,7 +188,7 @@ class bocalupdate extends bocal
$common_parts = array_intersect($users,array_keys($overlap['participants']));
foreach($common_parts as $n => $uid)
{
if ($overlap['participants'][$uid]{0} == 'R')
if ($overlap['participants'][$uid]{0} == 'R')
{
unset($common_parts[$uid]);
continue;
@ -231,7 +232,7 @@ class bocalupdate extends bocal
}
}
unset($possible_quantity_conflicts);
if (count($conflicts))
{
foreach($conflicts as $key => $conflict)
@ -252,7 +253,7 @@ class bocalupdate extends bocal
$this->debug_message('bocalupdate::update() %1 conflicts found %2',false,count($conflicts),$conflicts);
}
return $conflicts;
}
}
}
// save the event to the database
@ -304,11 +305,11 @@ class bocalupdate extends bocal
*
* @param array $new_event the updated event
* @param array $old_event the event before the update
*/
*/
function check4update($new_event,$old_event)
{
$modified = $added = $deleted = array();
//echo "<p>bocalupdate::check4update() new participants = ".print_r($new_event['participants'],true).", old participants =".print_r($old_event['participants'],true)."</p>\n";
// Find modified and deleted participants ...
@ -509,7 +510,7 @@ class bocalupdate extends bocal
$notify_msg = $this->cal_prefs['notifyAdded']; // use a default
}
$details = $this->_get_event_details($event,$action,$event_arr,$disinvited);
// add all group-members to the notification, unless they are already participants
foreach($to_notify as $userid => $statusid)
{
@ -532,7 +533,7 @@ class bocalupdate extends bocal
{
$res_info = $this->resource_info($userid);
$userid = $res_info['responsible'];
if (!isset($userid)) continue;
if (!isset($userid)) continue;
}
if ($statusid == 'R' || $GLOBALS['egw']->accounts->get_type($userid) == 'g')
@ -550,11 +551,11 @@ class bocalupdate extends bocal
}
$GLOBALS['egw']->accounts->get_account_name($userid,$lid,$details['to-firstname'],$details['to-lastname']);
$details['to-fullname'] = $GLOBALS['egw']->common->display_fullname('',$details['to-firstname'],$details['to-lastname']);
$GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'] = $part_prefs['common']['tz_offset'];
$GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] = $part_prefs['common']['timeformat'];
$GLOBALS['egw_info']['user']['preferences']['common']['dateformat'] = $part_prefs['common']['dateformat'];
$GLOBALS['egw']->datetime->tz_offset = 3600 * (int) $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'];
// event is in user-time of current user, now we need to calculate the tz-difference to the notified user and take it into account
@ -587,7 +588,7 @@ class bocalupdate extends bocal
case 'ical':
$ics = ExecMethod2('calendar.boical.exportVCal',$event['id'],'2.0',$method);
if ($method == 'REQUEST')
if ($method == 'REQUEST')
{
$attachment = array( 'string' => $ics,
'filename' => 'cal.ics',
@ -638,7 +639,7 @@ class bocalupdate extends bocal
* Function called via async service, when an alarm is to be send
*
* @param array $alarm array with keys owner, cal_id, all
* @return boolean
* @return boolean
*/
function send_alarm($alarm)
{
@ -684,6 +685,7 @@ class bocalupdate extends bocal
*/
function save($event)
{
//error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$etag)");
// check if user has the permission to update / create the event
if ($event['id'] && !$this->check_perms(EGW_ACL_EDIT,$event['id']) ||
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner']) &&
@ -702,9 +704,6 @@ class bocalupdate extends bocal
// we convert here from user-time to timestamps in server-time!
if (isset($event[$ts])) $event[$ts] = $event[$ts] ? $this->date2ts($event[$ts],true) : 0;
}
// Lock realized with a counter, that is checked and incremented as we save the entry
$check_etag = ($event['etag'] ? $event['etag']:0);
// same with the recur exceptions
if (isset($event['recur_exception']) && is_array($event['recur_exception']))
{
@ -721,7 +720,7 @@ class bocalupdate extends bocal
$event['alarm'][$id]['time'] = $this->date2ts($alarm['time'],true);
}
}
if (($cal_id = $this->so->save($event,$set_recurrences,NULL,$check_etag)) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE)
if (($cal_id = $this->so->save($event,$set_recurrences,NULL,$event['etag'])) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE)
{
$save_event['id'] = $cal_id;
$this->set_recurrences($save_event);
@ -730,10 +729,10 @@ class bocalupdate extends bocal
return $cal_id;
}
/**
* Check if the current user has the necessary ACL rights to change the status of $uid
*
*
* For contacts we use edit rights of the owner of the event (aka. edit rights of the event).
*
* @param int/string $uid account_id or 1-char type-identifer plus id (eg. c15 for addressbook entry #15)
@ -804,19 +803,19 @@ class bocalupdate extends bocal
function delete($cal_id,$recur_date=0)
{
$event = $this->read($cal_id,$recur_date);
if (!($event = $this->read($cal_id,$recur_date)) ||
!$this->check_perms(EGW_ACL_DELETE,$event))
{
return false;
}
$this->send_update(MSG_DELETED,$event['participants'],$event);
if (!$recur_date || $event['recur_type'] == MCAL_RECUR_NONE)
{
$this->so->delete($cal_id);
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'delete',time());
// delete all links to the event
egw_link::unlink(0,'calendar',$cal_id);
}
@ -858,7 +857,7 @@ class bocalupdate extends bocal
$link;
}
$event_arr['link']['data'] = $details['link'] = $link;
/* this is needed for notification-app
* notification-app creates the link individual for
* every user, so we must provide a neutral link-style
@ -872,7 +871,7 @@ class bocalupdate extends bocal
);
$link_arr['popup'] = '750x400';
$details['link_arr'] = $link_arr;
$dis = array();
foreach($disinvited as $uid)
{
@ -1102,18 +1101,4 @@ class bocalupdate extends bocal
return $cat_list;
}
/**
* updates the edit user information for timelocking an event
*
* @param array &$event2update event-array, on return some values might be changed due to set defaults
* this is a wrapper for the socal function
* cal_edit_time is set to current timestamp
* @return the returnvalue of so->save_edit_user (0 (someone else modified the entry), true (saved) or false (could not save)))
*/
function update_edit_user(&$event2update)
{
$event2update['edit_time']=$this->now_su;
return $this->so->save_edit_user($event2update);
}
}

View File

@ -68,7 +68,7 @@ class socal
/**
* internal copy of the global db-object
*
*
* @var egw_db
*/
var $db;
@ -464,12 +464,13 @@ ORDER BY cal_user_type, cal_usre_id
* @param array $event
* @param boolean &$set_recurrences on return: true if the recurrences need to be written, false otherwise
* @param int $change_since=0 time from which on the repetitions should be changed, default 0=all
* @return boolean/int false on error, cal_id otherwise
* @return int $check_etag check etag from GUI, if there is any Change since the last save SB:Lock for etag
* @param int &$etag etag=null etag to check or null, on return new etag
* @return boolean/int false on error, 0 if etag does not match, cal_id otherwise
*/
function save($event,&$set_recurrences,$change_since=0,$check_modified=0)
function save($event,&$set_recurrences,$change_since=0,&$etag=null)
{
//echo "<p>socal::save(,$change_since) event="; _debug_array($event);
error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$set_recurrences,$change_since,$etag)");
$cal_id = (int) $event['id'];
unset($event['id']);
@ -486,36 +487,21 @@ ORDER BY cal_user_type, cal_usre_id
}
if (is_array($event['cal_category'])) $event['cal_category'] = implode(',',$event['cal_category']);
// while saving handle the etag as condition for the update, to check if an entry was saved before this action occured
$check_etag = ($check_modified ? $check_modified : $event['cal_etag']);
if ($cal_id && $check_etag && $check_modified)
{
//$event2update[]= 'cal_etag=cal_etag+1';
$event2update['cal_etag']= $event['cal_etag']=$check_etag+1;
$event2update['cal_edit_user']= $event['cal_edit_user']=NULL;
$event2update['cal_edit_time']= $event['cal_edit_time']=NULL;
// cal_etag will be set on first save (if not set)
$where = array('cal_id' => $cal_id,'(cal_etag is NULL or cal_etag='.$check_etag.')');
#if ($check_etag) $where['cal_etag'] = $check_etag;
if (!$this->db->update($this->cal_table,$event2update,$where,__LINE__,__FILE__,'calendar'))
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning false");
return false; // Error
}
//echo $this->db->affected_rows()."##";
if ($check_etag && $this->db->affected_rows() < 1)
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning 0 (nothing updated, eg. condition not met)");
return 0; // someone else updated the modtime or deleted the entry
}
} else {
$event['cal_etag']=($check_etag?$check_etag:1);
}
if ($cal_id)
{
$this->db->update($this->cal_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
$where = array('cal_id' => $cal_id);
if (!is_null($etag)) $where['cal_etag'] = $etag;
unset($event['cal_etag']);
$event[] = 'cal_etag=cal_etag+1'; // always update the etag, even if none given to check
$this->db->update($this->cal_table,$event,$where,__LINE__,__FILE__,'calendar');
if (!is_null($etag) && $this->db->affected_rows() < 1)
{
return 0; // wrong etag, someone else updated the entry
}
if (!is_null($etag)) ++$etag;
}
else
{
@ -534,6 +520,7 @@ ORDER BY cal_user_type, cal_usre_id
$event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id);
$this->db->update($this->cal_table,array('cal_uid' => $event['cal_uid']),array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}
$etag = 0;
// new events need to have at least one participant, default to the owner
if (!isset($event['cal_participants']))
{
@ -635,6 +622,10 @@ ORDER BY cal_user_type, cal_usre_id
$this->save_alarm($cal_id,$alarm);
}
}
if (is_null($etag))
{
$etag = $this->db->select($this->cal_table,'cal_etag',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetchSingle();
}
return $cal_id;
}
@ -1104,45 +1095,4 @@ ORDER BY cal_user_type, cal_usre_id
),__LINE__,__FILE__,'calendar');
}
}
/**
* Save actually User, who is working on the Calenar Data if there is no user set or the timestamp is "expired"
*
* @param array $event2update
*
* @return (0 (someone else modified the entry), true (saved) or false (could not save)))
*/
function save_edit_user($event2update)
{
$cal_id = (int) $event2update['id'];
//unset($event2update['id']);
// add colum prefix 'cal_' if there's not already a 'recur_' prefix
foreach($event2update as $col => $val)
{
if ($col{0} != '#' && substr($col,0,6) != 'recur_' && $col != 'alarm')
{
$event2update['cal_'.$col] = $val;
unset($event2update[$col]);
}
}
if ($cal_id && $event2update['cal_edit_user'] && $event2update['cal_edit_time'])
{
$locktime = ($GLOBALS['egw_info']['server']['Lock_Time_Calender'] ? $GLOBALS['egw_info']['server']['Lock_Time_Calender'] : 1);
$lockborder=$event2update['cal_edit_time']-$locktime;
$where = array('cal_id' => $cal_id,'(cal_edit_user is NULL or cal_edit_time<'.$lockborder.')');
if (!$this->db->update($this->cal_table,$event2update,$where,__LINE__,__FILE__,'calendar'))
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning false");
return false; // Error
}
//echo $this->db->affected_rows()."##";
if ($this->db->affected_rows() < 1)
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning 0 (nothing updated, eg. condition not met)");
return 0; // someone else updated the modtime or deleted the entry
}
return true;
}
}
}

View File

@ -38,21 +38,21 @@ class uiforms extends uical
* @var bolink
*/
var $link;
/**
* Standard durations used in edit and freetime search
*
* @var array
*/
var $durations = array();
/**
* Name of the tabs used in edit
*
* @var string
*/
var $tabs = 'general|description|participants|recurrence|custom|links|alarms';
/**
* default timelock for entries, that are opened by another user
*
@ -66,20 +66,20 @@ class uiforms extends uical
function uiforms()
{
$this->uical(true); // call the parent's constructor
$this->link =& $this->bo->link;
for ($n=15; $n <= 8*60; $n+=($n < 60 ? 15 : ($n < 240 ? 30 : 60)))
{
$this->durations[$n*60] = sprintf('%d:%02d',$n/60,$n%60);
}
}
/**
* Create a default event (adding a new event) by evaluating certain _GET vars
*
* @return array event-array
*/
*/
function &default_add_event()
{
$extra_participants = $_GET['participants'] ? explode(',',$_GET['participants']) : array();
@ -93,7 +93,7 @@ class uiforms extends uical
{
$owner = $this->owner;
}
if (!$owner || !is_numeric($owner) || $GLOBALS['egw']->accounts->get_type($owner) != 'u' ||
if (!$owner || !is_numeric($owner) || $GLOBALS['egw']->accounts->get_type($owner) != 'u' ||
!$this->bo->check_perms(EGW_ACL_ADD,0,$owner))
{
if ($owner) // make an owner who is no user or we have no add-rights a participant
@ -113,7 +113,7 @@ class uiforms extends uical
'minute' => (int) $_GET['minute'],
));
//echo "<p>_GET[date]=$_GET[date], _GET[hour]=$_GET[hour], _GET[minute]=$_GET[minute], this->date=$this->date ==> start=$start=".date('Y-m-d H:i',$start)."</p>\n";
$participant_types['u'] = $participant_types = $participants = array();
foreach($extra_participants as $uid)
{
@ -130,7 +130,7 @@ class uiforms extends uical
$participants[$uid] = $participant_types[$uid{0}][$id] = ($res_data['new_status'] ? ExecMethod($res_data['new_status'],$id) : 'U').
((int) $quantity > 1 ? (int)$quantity : '');
// if new_status == 'x', resource is not bookable
if(strpos($participant_types[$uid{0}][$id],'x') !== false)
if(strpos($participant_types[$uid{0}][$id],'x') !== false)
{
unset($participant_types[$uid{0}][$id]);
unset($participants[$uid]);
@ -148,7 +148,7 @@ class uiforms extends uical
'alarm' => array(),
);
}
/**
* Process the edited event and evtl. call edit to redisplay it
*
@ -182,7 +182,7 @@ class uiforms extends uical
{
list($id) = each($content['alarm']['delete_alarm']);
//echo "delete alarm $id"; _debug_array($content['alarm']['delete_alarm']);
if ($content['id'])
{
if ($this->bo->delete_alarm($id))
@ -192,7 +192,7 @@ class uiforms extends uical
}
else
{
$msg = lang('Permission denied');
$msg = lang('Permission denied');
}
}
else
@ -256,7 +256,7 @@ class uiforms extends uical
{
$status = isset($this->bo->resources[$type]['new_status']) ? ExecMethod($this->bo->resources[$type]['new_status'],$id) : 'U';
$quantity = $content['participants']['quantity'] ? $content['participants']['quantity'] : 1;
if ($uid) $event['participants'][$uid] = $event['participant_types'][$type][$id] =
if ($uid) $event['participants'][$uid] = $event['participant_types'][$type][$id] =
$status.((int) $quantity > 1 ? (int)$quantity : '');
break;
}
@ -264,7 +264,7 @@ class uiforms extends uical
case 'account':
foreach(is_array($data) ? $data : explode(',',$data) as $uid)
{
if ($uid) $event['participants'][$uid] = $event['participant_types']['u'][$uid] =
if ($uid) $event['participants'][$uid] = $event['participant_types']['u'][$uid] =
$uid == $this->bo->user ? 'A' : 'U';
}
break;
@ -282,12 +282,12 @@ class uiforms extends uical
{
if (is_numeric($uid))
{
$id = $uid;
$id = $uid;
$type = 'u';
}
else
{
$id = substr($uid,1);
$id = substr($uid,1);
$type = $uid{0};
}
if ($data['old_status'] != $status)
@ -307,7 +307,7 @@ class uiforms extends uical
}
if ($uid && $status != 'G')
{
$event['participants'][$uid] = $event['participant_types'][$type][$id] =
$event['participants'][$uid] = $event['participant_types'][$type][$id] =
$status.((int) $quantity > 1 ? (int)$quantity : '');
}
}
@ -344,7 +344,7 @@ class uiforms extends uical
$msg = lang('Event copied - the copy can now be edited');
$event['title'] = lang('Copy of:').' '.$event['title'];
break;
case 'ignore':
$ignore_conflicts = true;
$button = $event['button_was']; // save or apply
@ -353,15 +353,15 @@ class uiforms extends uical
case 'mail':
case 'save':
case 'apply':
if ($event['id'] && !$this->bo->check_perms(EGW_ACL_EDIT,$event))
{
if ($event['id'] && !$this->bo->check_perms(EGW_ACL_EDIT,$event))
{
if ($button == 'mail') // just mail without edit-rights is ok
{
$js = $this->custom_mail($event,false);
break;
}
$msg = lang('Permission denied');
break;
$msg = lang('Permission denied');
break;
}
if ($event['start'] > $event['end'])
{
@ -409,7 +409,7 @@ class uiforms extends uical
$event['button_was'] = $button; // remember for ignore
return $this->conflicts($event,$conflicts,$preserv);
}
elseif ($conflicts ===0)
elseif ($conflicts ===0)
{
$msg .= ($msg ? ', ' : '') .lang('Error: the entry has been updated since you opened it for editing!').'<br />'.
lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','<a href="'.
@ -419,14 +419,14 @@ class uiforms extends uical
'referer' => $referer,
))).'">','</a>');
$noerror=false;
}
elseif ($conflicts>0)
elseif ($conflicts>0)
{
$msg .= ($msg ? ', ' : '') . lang('Event saved');
// writing links for new entry, existing ones are handled by the widget itself
if (!$content['id'] && is_array($content['link_to']['to_id']))
if (!$content['id'] && is_array($content['link_to']['to_id']))
{
egw_link::link('calendar',$event['id'],$content['link_to']['to_id']);
}
@ -445,7 +445,7 @@ class uiforms extends uical
$msg = lang('Error: saving the event !!!');
}
break;
case 'cancel':
if($content['cancel_needs_refresh'])
{
@ -466,12 +466,12 @@ class uiforms extends uical
))).'\';';
}
break;
case 'freetime':
// the "click" has to be in onload, to make sure the button is already created
$GLOBALS['egw']->js->set_onload("document.getElementsByName('exec[freetime]')[0].click();");
break;
case 'add_alarm':
if ($this->bo->check_perms(EGW_ACL_EDIT,!$content['new_alarm']['owner'] ? $event : 0,$content['new_alarm']['owner']))
{
@ -492,7 +492,7 @@ class uiforms extends uical
{
$alarm['id'] = $alarm_id;
$event['alarm'][$alarm_id] = $alarm;
$msg = lang('Alarm added');
}
else
@ -514,6 +514,10 @@ class uiforms extends uical
}
if (in_array($button,array('cancel','save','delete')) && $noerror)
{
if ($content['lock_token']) // remove an existing lock
{
egw_vfs::unlock(egw_vfs::app_entry_lock_path('calendar',$content['id']),$content['lock_token'],false);
}
if ($content['no_popup'])
{
$GLOBALS['egw']->redirect_link('/index.php',array(
@ -527,10 +531,10 @@ class uiforms extends uical
}
return $this->edit($event,$preserv,$msg,$js,$event['id'] ? $event['id'] : $content['link_to']['to_id']);
}
/**
* Create an exception from the clicked event
*
*
* It's not stored to the DB unless the user saves it!
*
* @param array &$event
@ -562,13 +566,13 @@ class uiforms extends uical
foreach($event['participants'] as $uid => $status)
{
if ($status == 'R' || $uid == $this->user) continue;
if (is_numeric($uid) && $GLOBALS['egw']->accounts->get_type($uid) == 'u')
{
if (!($email = $GLOBALS['egw']->accounts->id2name($uid,'account_email'))) continue;
$GLOBALS['egw']->accounts->get_account_name($uid,$lid,$firstname,$lastname);
$to[] = $firstname.' '.$lastname.' <'.$email.'>';
}
elseif ($uid < 0)
@ -578,7 +582,7 @@ class uiforms extends uical
if (!($email = $GLOBALS['egw']->accounts->id2name($uid,'account_email'))) continue;
$GLOBALS['egw']->accounts->get_account_name($uid,$lid,$firstname,$lastname);
$to[] = $firstname.' '.$lastname.' <'.$email.'>';
}
}
@ -657,7 +661,7 @@ class uiforms extends uical
if (!$cal_id || $cal_id && !($event = $this->bo->read($cal_id,$_GET['date'])) || !$this->bo->check_perms(EGW_ACL_READ,$event))
{
if ($cal_id)
if ($cal_id)
{
if (!$preserv['no_popup'])
{
@ -702,6 +706,39 @@ class uiforms extends uical
}
$view = $preserv['view'] = $preserv['view'] || $event['id'] && !$this->bo->check_perms(EGW_ACL_EDIT,$event);
//echo "view=$view, event="; _debug_array($event);
// shared locking of entries to edit
if (!$view && ($locktime = $GLOBALS['egw_info']['server']['Lock_Time_Calender']) && $event['id'])
{
$lock_path = egw_vfs::app_entry_lock_path('calendar',$event['id']);
$lock_owner = 'mailto:'.$GLOBALS['egw_info']['user']['account_email'];
if (($preserv['lock_token'] = $content['lock_token'])) // already locked --> refresh the lock
{
egw_vfs::lock($lock_path,$preserv['lock_token'],$locktime,$lock_owner,$scope='shared',$type='write',true,false);
}
if (($lock = egw_vfs::checkLock($lock_path)) && $lock['owner'] != $lock_owner)
{
$msg .= ' '.lang('This entry is currently opened by %1!',
(($lock_uid = $GLOBALS['egw']->accounts->name2id(substr($lock['owner'],7),'account_email')) ?
$GLOBALS['egw']->common->grab_owner_name($lock_uid) : $lock['owner']));
}
elseif($lock)
{
$preserv['lock_token'] = $lock['token'];
}
elseif(egw_vfs::lock($lock_path,$preserv['lock_token'],$locktime,$lock_owner,$scope='shared',$type='write',false,false))
{
// install ajax handler to unlock the entry again, if the window get's closed by the user
$GLOBALS['egw']->js->set_onunload("xajax_doXMLHTTP('calendar.uiforms.ajax_unlock',$event[id],'$preserv[lock_token]');");
}
else
{
$msg .= ' '.lang("Can't aquire lock!"); // eg. an exclusive lock via CalDAV ...
$view = true;
}
//echo "<p>lock_path=$lock_path, lock_owner=$lock_owner, lock_token=$preserv[lock_token], msg=$msg</p>\n";
}
$content = array_merge($event,array(
'link_to' => array(
'to_id' => $link_to_id,
@ -710,6 +747,7 @@ class uiforms extends uical
'edit_single' => $preserv['edit_single'], // need to be in content too, as it is used in the template
$this->tabs => $preserv[$this->tabs],
'view' => $view,
'msg' => $msg,
));
$content['duration'] = $content['end'] - $content['start'];
if (isset($this->durations[$content['duration']])) $content['end'] = '';
@ -736,9 +774,9 @@ class uiforms extends uical
$readonlys[$row.'[quantity]'] = $type == 'u' || !isset($this->bo->resources[$type]['max_quantity']);
$readonlys[$row.'[status]'] = $readonlys[$row.'[status_recurrence]'] = !$this->bo->check_status_perms($uid,$event);
$readonlys["delete[$uid]"] = !$this->bo->check_perms(EGW_ACL_EDIT,$event);
$content['participants'][$row++]['title'] = $name == 'accounts' ?
$content['participants'][$row++]['title'] = $name == 'accounts' ?
$GLOBALS['egw']->common->grab_owner_name($id) : egw_link::title($name,$id);
// enumerate group-invitations, so people can accept/reject them
if ($name == 'accounts' && $GLOBALS['egw']->accounts->get_type($id) == 'g' &&
($members = $GLOBALS['egw']->accounts->members($id,true)))
@ -787,7 +825,7 @@ class uiforms extends uical
if ($minutes) $label[] = $minutes.' '.lang('Minutes');
$alarm['offset'] = implode(', ',$label);
$content['alarm'][] = $alarm;
$readonlys['delete_alarm['.$id.']'] = !$this->bo->check_perms(EGW_ACL_EDIT,$alarm['all'] ? $event : 0,$alarm['owner']);
}
}
@ -811,7 +849,7 @@ class uiforms extends uical
$readonlys['button[save]'] = $readonlys['button[apply]'] = $readonlys['freetime'] = true;
$readonlys['link_to'] = $readonlys['customfields'] = true;
$readonlys['duration'] = true;
if ($event['recur_type'] != MCAL_RECUR_NONE)
{
$onclick =& $etpl->get_cell_attribute('button[delete]','onclick');
@ -821,10 +859,6 @@ class uiforms extends uical
}
else
{
if (!is_object($GLOBALS['egw']->js))
{
$GLOBALS['egw']->js = CreateObject('phpgwapi.javascript');
}
// We hide the enddate if one of our predefined durations fits
// the call to set_style_by_class has to be in onload, to make sure the function and the element is already created
$GLOBALS['egw']->js->set_onload("set_style_by_class('table','end_hide','visibility','".($content['duration'] && isset($sel_options['duration'][$content['duration']]) ? 'hidden' : 'visible')."');");
@ -868,7 +902,7 @@ class uiforms extends uical
$content['no_add_alarm'] = !count($sel_options['owner']); // no rights to set any alarm
if (!$event['id'])
{
$etpl->set_cell_attribute('button[new_alarm]','type','checkbox');
$etpl->set_cell_attribute('button[new_alarm]','type','checkbox');
}
if ($preserv['no_popup'])
{
@ -878,31 +912,12 @@ class uiforms extends uical
//echo "preserv="; _debug_array($preserv);
//echo "readonlys="; _debug_array($readonlys);
//echo "sel_options="; _debug_array($sel_options);
$GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . (!$event['id'] ? lang('Add') : ($view ? lang('View') :
$GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . (!$event['id'] ? lang('Add') : ($view ? lang('View') :
($content['edit_single'] ? lang('Create exception') : ($content['recur_type'] ? lang('Edit series') : lang('Edit')))));
$GLOBALS['egw_info']['flags']['java_script'] .= "<script>\n$js\n</script>\n";
$content['cancel_needs_refresh'] = (bool)$_GET['cancel_needs_refresh'];
// time locking for entries
$locktime = $GLOBALS['egw_info']['server']['Lock_Time_Calender'];
if ($locktime) {
// the warning and the saving of edit_user and time will only be performed, if there is a lock time set
if (($this->bo->now_su>($event['edit_time']+$locktime)) || ($event['edit_time']==''))
{
//echo "write Lock!!->DB";
$event2update['id']=$event['id'];
$event2update['edit_user']=$this->user;
$event2update['edit_time']=''; // this is set in bo->update_edit_user
$this->bo->update_edit_user($event2update);
}
else
{
if ($event['edit_user'] && $event['edit_user']!=$this->user) $content['msg'].=" ".lang('This entry is opened by user: ').$GLOBALS['egw']->accounts->id2name($event['edit_user']);
}
} else {
}
$content['cancel_needs_refresh'] = (bool)$_GET['cancel_needs_refresh'];
// non_interactive==true from $_GET calls immediate save action without displaying the edit form
if(isset($_GET['non_interactive']) && (bool)$_GET['non_interactive'] === true)
{
@ -916,21 +931,41 @@ class uiforms extends uical
}
}
/**
* Remove (shared) lock via ajax, when edit popup get's closed
*
* @param int $id
* @param string $token
*/
function ajax_unlock($id,$token)
{
$lock_path = egw_vfs::app_entry_lock_path('calendar',$id);
$lock_owner = 'mailto:'.$GLOBALS['egw_info']['user']['account_email'];
if (($lock = egw_vfs::checkLock($lock_path)) && $lock['owner'] == $lock_owner || $lock['token'] == $token)
{
egw_vfs::unlock($lock_path,$token,false);
}
$response = new xajaxResponse();
$response->addScript('window.close();');
return $response->getXML();
}
/**
* displays a sheduling conflict
*
* @param array $event
* @param array $event
* @param array $conflicts array with conflicting events, the events are not garantied to be readable by the user!
* @param array $preserv data to preserv
*/
function conflicts($event,$conflicts,$preserv)
{
$etpl =& CreateObject('etemplate.etemplate','calendar.conflicts');
foreach($conflicts as $k => $conflict)
{
$is_readable = $this->bo->check_perms(EGW_ACL_READ,$conflict);
$conflicts[$k] += array(
'icon_participants' => $is_readable ? (count($conflict['participants']) > 1 ? 'users' : 'single') : 'private',
'tooltip_participants' => $is_readable ? implode(', ',$this->bo->participants($conflict)) : '',
@ -940,7 +975,7 @@ class uiforms extends uical
),true,true)), // show group invitations too
'icon_recur' => $conflict['recur_type'] != MCAL_RECUR_NONE ? 'recur' : '',
'text_recur' => $conflict['recur_type'] != MCAL_RECUR_NONE ? lang('Recurring event') : ' ',
);
);
}
$content = $event + array(
'conflicts' => array_values($conflicts), // conflicts have id-start as key
@ -1018,12 +1053,12 @@ class uiforms extends uical
$content['end_time'] = 0; // no end-time limit, as duration would never fit
}
$content['weekdays'] = MCAL_M_WEEKDAYS;
$content['search_window'] = 7 * DAY_s;
// pick a searchwindow fitting the duration (search for a 10 day slot in a one week window never succeeds)
foreach($sel_options['search_window'] as $window => $label)
{
if ($window > $content['duration'])
if ($window > $content['duration'])
{
$content['search_window'] = $window;
break;
@ -1032,8 +1067,8 @@ class uiforms extends uical
}
else
{
if (!$content['duration']) $content['duration'] = $content['end'] - $content['start'];
if (!$content['duration']) $content['duration'] = $content['end'] - $content['start'];
if (is_array($content['freetime']['select']))
{
list($selected) = each($content['freetime']['select']);
@ -1059,7 +1094,7 @@ class uiforms extends uical
'exec[end][a]' => date('a',$end),
);
}
else
else
{
$fields_to_set += array(
'exec[start][H]' => (int) date('H',$start),
@ -1081,7 +1116,7 @@ class uiforms extends uical
}
}
}
window.close();
window.close();
</script>
</html>\n";
exit;
@ -1098,7 +1133,7 @@ class uiforms extends uical
$GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('freetime search');
// let the window popup, if its already there
$GLOBALS['egw_info']['flags']['java_script'] .= "<script>\nwindow.focus();\n</script>\n";
if (!is_object($GLOBALS['egw']->js))
{
$GLOBALS['egw']->js = CreateObject('phpgwapi.javascript');
@ -1113,9 +1148,9 @@ class uiforms extends uical
'participants' => $content['participants'],
'cal_id' => $content['cal_id'],
'recur_type' => $content['recur_type'],
),2);
),2);
}
/**
* calculate the freetime of given $participants in a certain time-span
*
@ -1139,7 +1174,7 @@ class uiforms extends uical
$busy[] = array( // add end-of-search-date as event, to cope with empty search and get freetime til that date
'start' => $end,
'end' => $end,
);
);
$ft_start = $start;
$freetime = array();
$n = 0;
@ -1179,14 +1214,14 @@ class uiforms extends uical
'end' => $ft_end,
);
if ($this->debug > 1) echo "<p>freetime: ".date('D d.m.Y H:i',$ft_start)." - ".date('D d.m.Y H:i',$ft_end)."</p>\n";
}
}
$ft_start = $event['end'];
}
if ($this->debug > 0) $this->bo->debug_message('uiforms::freetime(participants=%1, start=%2, end=%3, duration=%4, cal_id=%5) freetime=%6',true,$participants,$start,$end,$duration,$cal_id,$freetime);
return $freetime;
}
/**
* split the freetime in daywise slot, taking into account weekdays, start- and stop-times
*
@ -1203,18 +1238,18 @@ class uiforms extends uical
function split_freetime_daywise($freetime,$duration,$weekdays,$start_time,$end_time,&$sel_options)
{
if ($this->debug > 1) $this->bo->debug_message('uiforms::split_freetime_daywise(freetime=%1, duration=%2, start_time=%3, end_time=%4)',true,$freetime,$duration,$start_time,$end_time);
$freetime_daywise = array();
if (!is_array($sel_options)) $sel_options = array();
$time_format = $this->common_prefs['timeformat'] == 12 ? 'h:i a' : 'H:i';
$start_time = (int) $start_time; // ignore leading zeros
$end_time = (int) $end_time;
// ignore the end_time, if duration would never fit
if (($end_time - $start_time)*HOUR_s < $duration)
if (($end_time - $start_time)*HOUR_s < $duration)
{
$end_time = 0;
$end_time = 0;
if ($this->debug > 1) $this->bo->debug_message('uiforms::split_freetime_daywise(, duration=%2, start_time=%3,..) end_time set to 0, it never fits durationn otherwise',true,$duration,$start_time);
}
$n = 0;
@ -1224,7 +1259,7 @@ class uiforms extends uical
$daybegin['hour'] = $daybegin['minute'] = $daybegin['second'] = 0;
unset($daybegin['raw']);
$daybegin = $this->bo->date2ts($daybegin);
for($t = $daybegin; $t < $ft['end']; $t += DAY_s,$daybegin += DAY_s)
{
$dow = date('w',$daybegin+DAY_s/2); // 0=Sun, .., 6=Sat
@ -1235,7 +1270,7 @@ class uiforms extends uical
continue; // wrong day of week
}
$start = $t < $ft['start'] ? $ft['start'] : $t;
if ($start-$daybegin < $start_time*HOUR_s) // start earlier then start_time
{
$start = $daybegin + $start_time*HOUR_s;
@ -1266,7 +1301,7 @@ class uiforms extends uical
}
return $freetime_daywise;
}
/**
* Export events as vCalendar version 2.0 files (iCal)
*
@ -1281,7 +1316,7 @@ class uiforms extends uical
if (!($ical =& ExecMethod2('calendar.boical.exportVCal',$cal_id,'2.0')))
{
$msg = lang('Permission denied');
if ($return_error) return $msg;
}
else
@ -1325,13 +1360,13 @@ class uiforms extends uical
);
}
$content['msg'] = $msg;
$GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('iCal Export');
$etpl =& CreateObject('etemplate.etemplate','calendar.export');
$etpl->exec('calendar.uiforms.export',$content);
}
/**
* Import events as vCalendar version 2.0 files (iCal)
*
@ -1362,7 +1397,7 @@ class uiforms extends uical
);
$GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('iCal Import');
$etpl =& CreateObject('etemplate.etemplate','calendar.import');
$etpl->exec('calendar.uiforms.import',$content);
}
}

View File

@ -12,7 +12,7 @@
/* $Id$ */
$setup_info['calendar']['name'] = 'calendar';
$setup_info['calendar']['version'] = '1.5';
$setup_info['calendar']['version'] = '1.5.001';
$setup_info['calendar']['app_order'] = 3;
$setup_info['calendar']['enable'] = 1;
@ -79,3 +79,4 @@

View File

@ -28,9 +28,7 @@
'cal_modifier' => array('type' => 'int','precision' => '4'),
'cal_non_blocking' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_special' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_etag' => array('type' => 'int','precision' => '4'),
'cal_edit_user' => array('type' => 'int','precision' => '4'),
'cal_edit_time' => array('type' => 'int','precision' => '8')
'cal_etag' => array('type' => 'int','precision' => '4','default' => '0')
),
'pk' => array('cal_id'),
'fk' => array(),

View File

@ -208,7 +208,7 @@
'uc' => array()
)
);
$GLOBALS['egw_setup']->oProc->query('SELECT count(*) FROM webcal_entry',__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->next_record();
if($GLOBALS['egw_setup']->oProc->f(0))
@ -233,10 +233,10 @@
.'VALUES('.$cal_id.",'".$cal_owner."','".$cal_group."',".$datetime.",".$moddatetime.",".$cal_duration.",".$cal_priority.",'".$cal_type."','".$cal_access."','".$cal_name."','".$cal_description."')",__LINE__,__FILE__);
}
}
$GLOBALS['egw_setup']->oProc->DropTable('webcal_entry_groups');
$GLOBALS['egw_setup']->oProc->DropTable('webcal_entry');
$GLOBALS['egw_setup']->oProc->CreateTable('calendar_entry_user',
Array(
'fd' => array(
@ -250,7 +250,7 @@
'uc' => array()
)
);
$GLOBALS['egw_setup']->oProc->query('SELECT count(*) FROM webcal_entry_user',__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->next_record();
if($GLOBALS['egw_setup']->oProc->f(0))
@ -264,9 +264,9 @@
$db2->query('INSERT INTO calendar_entry_user(cal_id,cal_login,cal_status) VALUES('.$cal_id.','.$cal_login.",'".$cal_status."')",__LINE__,__FILE__);
}
}
$GLOBALS['egw_setup']->oProc->DropTable('webcal_entry_user');
$GLOBALS['egw_setup']->oProc->CreateTable('calendar_entry_repeats',
Array(
'fd' => array(
@ -283,7 +283,7 @@
'uc' => array()
)
);
$GLOBALS['egw_setup']->oProc->query('SELECT count(*) FROM webcal_entry_repeats',__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->next_record();
if($GLOBALS['egw_setup']->oProc->f(0))
@ -308,10 +308,10 @@
$db2->query('INSERT INTO calendar_entry_repeats(cal_id,cal_type,cal_use_end,cal_end,cal_frequency,cal_days) VALUES('.$cal_id.",'".$cal_type."',".$useend.",".$enddate.",".$cal_frequency.",'".$cal_days."')",__LINE__,__FILE__);
}
}
$GLOBALS['egw_setup']->oProc->DropTable('webcal_entry_repeats');
$GLOBALS['egw_setup']->oProc->query("UPDATE {$GLOBALS['egw_setup']->applications_table} SET app_tables='calendar_entry,calendar_entry_user,calendar_entry_repeats' WHERE app_name='calendar'",__LINE__,__FILE__);
$GLOBALS['setup_info']['calendar']['currentver'] = '0.9.7pre2';
return $GLOBALS['setup_info']['calendar']['currentver'];
}
@ -320,7 +320,7 @@
function calendar_upgrade0_9_7pre2()
{
$db2 = $GLOBALS['egw_setup']->db;
$GLOBALS['egw_setup']->oProc->RenameColumn('calendar_entry', 'cal_duration', 'cal_edatetime');
$GLOBALS['egw_setup']->oProc->query('SELECT cal_id,cal_datetime,cal_owner,cal_edatetime,cal_mdatetime FROM calendar_entry ORDER BY cal_id',__LINE__,__FILE__);
if($GLOBALS['egw_setup']->oProc->num_rows())
@ -337,7 +337,7 @@
$db2->query('UPDATE calendar_entry SET cal_datetime='.$datetime.', cal_edatetime='.$edatetime.', cal_mdatetime='.$mdatetime.' WHERE cal_id='.$cal_id,__LINE__,__FILE__);
}
}
$GLOBALS['setup_info']['calendar']['currentver'] = '0.9.7pre3';
return $GLOBALS['setup_info']['calendar']['currentver'];
}
@ -590,7 +590,7 @@
define(RECUR_MONTHLY_MDAY,3);
define(RECUR_MONTHLY_WDAY,4);
define(RECUR_YEARLY,5);
define(M_SUNDAY,1);
define(M_MONDAY,2);
define(M_TUESDAY,4);
@ -856,7 +856,7 @@
$GLOBALS['egw_setup']->oProc->CreateTable('phpgw_cal_alarm',
Array(
'fd' => array(
'alarm_id' => array('type' => 'auto','nullable' => False),
'alarm_id' => array('type' => 'auto','nullable' => False),
'cal_id' => array('type' => 'int', 'precision' => 8, 'nullable' => False),
'cal_owner' => array('type' => 'int', 'precision' => 8, 'nullable' => False),
'cal_time' => array('type' => 'int', 'precision' => 8, 'nullable' => False),
@ -900,7 +900,7 @@
@reset($calendar_data);
while($calendar_data && list($cal_id,$category) = each($calendar_data))
{
$GLOBALS['egw_setup']->oProc->query("UPDATE phpgw_cal SET category='".$category."' WHERE cal_id=".$cal_id,__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->query("UPDATE phpgw_cal SET category='".$category."' WHERE cal_id=".$cal_id,__LINE__,__FILE__);
}
$GLOBALS['setup_info']['calendar']['currentver'] = '0.9.13.006';
return $GLOBALS['setup_info']['calendar']['currentver'];
@ -1061,14 +1061,14 @@
// re-run the update as very old entries only have an empty uid
return calendar_upgrade0_9_16_005();
}
$test[] = '0.9.16.007';
function calendar_upgrade0_9_16_007()
{
// update the sequenzes for refreshed tables (postgres only)
$GLOBALS['egw_setup']->oProc->UpdateSequence('phpgw_cal_holidays','hol_id');
$GLOBALS['setup_info']['calendar']['currentver'] = '1.0.0';
return $GLOBALS['setup_info']['calendar']['currentver'];
}
@ -1498,14 +1498,14 @@
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.2';
}
$test[] = '1.2';
function calendar_upgrade1_2()
{
// get old alarms (saved before 1.2) working again
$GLOBALS['egw_setup']->db->query("UPDATE egw_async SET async_method ='calendar.bocalupdate.send_alarm' WHERE async_method ='calendar.bocalendar.send_alarm'",__LINE__,__FILE__);
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.2.001';
}
@ -1532,17 +1532,71 @@
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_etag',array(
'type' => 'int',
'precision' => '4'
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_edit_user',array(
'type' => 'int',
'precision' => '4'
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_edit_time',array(
'type' => 'int',
'precision' => '8'
'precision' => '4',
'default' => '0'
));
// as we no longer create cal_edit_time|user and already set default 0 for cal_etag, we skip the 1.5 update
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.5.001';
}
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.5';
$test[] = '1.5';
function calendar_upgrade1_5()
{
$GLOBALS['egw_setup']->oProc->DropColumn('egw_cal',array(
'fd' => array(
'cal_id' => array('type' => 'auto','nullable' => False),
'cal_uid' => array('type' => 'varchar','precision' => '255','nullable' => False),
'cal_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_category' => array('type' => 'varchar','precision' => '30'),
'cal_modified' => array('type' => 'int','precision' => '8'),
'cal_priority' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '2'),
'cal_public' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '1'),
'cal_title' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '1'),
'cal_description' => array('type' => 'text'),
'cal_location' => array('type' => 'varchar','precision' => '255'),
'cal_reference' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cal_modifier' => array('type' => 'int','precision' => '4'),
'cal_non_blocking' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_special' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_etag' => array('type' => 'int','precision' => '4'),
'cal_edit_time' => array('type' => 'int','precision' => '8')
),
'pk' => array('cal_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),'cal_edit_user');
$GLOBALS['egw_setup']->oProc->DropColumn('egw_cal',array(
'fd' => array(
'cal_id' => array('type' => 'auto','nullable' => False),
'cal_uid' => array('type' => 'varchar','precision' => '255','nullable' => False),
'cal_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_category' => array('type' => 'varchar','precision' => '30'),
'cal_modified' => array('type' => 'int','precision' => '8'),
'cal_priority' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '2'),
'cal_public' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '1'),
'cal_title' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '1'),
'cal_description' => array('type' => 'text'),
'cal_location' => array('type' => 'varchar','precision' => '255'),
'cal_reference' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cal_modifier' => array('type' => 'int','precision' => '4'),
'cal_non_blocking' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_special' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_etag' => array('type' => 'int','precision' => '4')
),
'pk' => array('cal_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),'cal_edit_time');
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal','cal_etag',array(
'type' => 'int',
'precision' => '4',
'default' => '0'
));
$GLOBALS['egw_setup']->db->query('UPDATE egw_cal SET cal_etag=0 WHERE cal_etag IS NULL',__LINE__,__FILE__);
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.5.001';
}
?>