forked from extern/egroupware
- 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:
parent
641817662b
commit
5d50d41004
@ -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)',
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
@ -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'] = '';
|
||||
@ -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')."');");
|
||||
@ -884,25 +918,6 @@ class uiforms extends uical
|
||||
|
||||
$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 {
|
||||
|
||||
}
|
||||
// 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,6 +931,26 @@ 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
|
||||
*
|
||||
|
@ -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 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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';
|
||||
}
|
||||
?>
|
||||
|
Loading…
Reference in New Issue
Block a user