From 5d50d41004772d61e606ad94505a6cac698424c5 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 7 May 2008 22:12:25 +0000 Subject: [PATCH] - 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 --- calendar/inc/class.bocalupdate.inc.php | 71 +++----- calendar/inc/class.socal.inc.php | 96 +++-------- calendar/inc/class.uiforms.inc.php | 229 ++++++++++++++----------- calendar/setup/setup.inc.php | 3 +- calendar/setup/tables_current.inc.php | 4 +- calendar/setup/tables_update.inc.php | 112 ++++++++---- 6 files changed, 269 insertions(+), 246 deletions(-) diff --git a/calendar/inc/class.bocalupdate.inc.php b/calendar/inc/class.bocalupdate.inc.php index 619534f26d..961c5f0d31 100644 --- a/calendar/inc/class.bocalupdate.inc.php +++ b/calendar/inc/class.bocalupdate.inc.php @@ -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 "

bocalupdate::check4update() new participants = ".print_r($new_event['participants'],true).", old participants =".print_r($old_event['participants'],true)."

\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); - } } diff --git a/calendar/inc/class.socal.inc.php b/calendar/inc/class.socal.inc.php index b92d04250c..1b639a3310 100644 --- a/calendar/inc/class.socal.inc.php +++ b/calendar/inc/class.socal.inc.php @@ -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 "

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; - } - } } diff --git a/calendar/inc/class.uiforms.inc.php b/calendar/inc/class.uiforms.inc.php index be8cc91e98..29b7ca2eff 100644 --- a/calendar/inc/class.uiforms.inc.php +++ b/calendar/inc/class.uiforms.inc.php @@ -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 "

_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)."

\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!').'
'. lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','',''); $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 "

lock_path=$lock_path, lock_owner=$lock_owner, lock_token=$preserv[lock_token], msg=$msg

\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'] .= "\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(); \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'] .= "\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 "

freetime: ".date('D d.m.Y H:i',$ft_start)." - ".date('D d.m.Y H:i',$ft_end)."

\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); } } diff --git a/calendar/setup/setup.inc.php b/calendar/setup/setup.inc.php index 8d12dead45..430688b606 100755 --- a/calendar/setup/setup.inc.php +++ b/calendar/setup/setup.inc.php @@ -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 @@ + diff --git a/calendar/setup/tables_current.inc.php b/calendar/setup/tables_current.inc.php index a6a6177003..ff85a87add 100644 --- a/calendar/setup/tables_current.inc.php +++ b/calendar/setup/tables_current.inc.php @@ -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(), diff --git a/calendar/setup/tables_update.inc.php b/calendar/setup/tables_update.inc.php index 8252aa273f..9e5c8bd659 100644 --- a/calendar/setup/tables_update.inc.php +++ b/calendar/setup/tables_update.inc.php @@ -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'; } ?>