* Calendar: fixed issue of deleted first recurrence shortens event and causes it not to be find in CalDAV or eSync ("event has exceptions before startdate"), REQUIRES SCHEMA UPDATE (visit setup)!

r40103: * Calendar: Try alter description to varchar(16384), to not force temp. tables to disk on MySQL (because of text columns)
r40112: MAX(CHAR_LENGTH(cal_description)) returns NULL, if there are no rows --> casting to int
This commit is contained in:
Ralf Becker 2012-08-13 13:27:14 +00:00
parent aa253ea9d6
commit 9898162a65
5 changed files with 139 additions and 43 deletions

View File

@ -1404,6 +1404,7 @@ class calendar_boupdate extends calendar_bo
*/
function delete($cal_id,$recur_date=0,$ignore_acl=false,$skip_notification=false)
{
//error_log(__METHOD__."(cal_id=$cal_id, recur_date=$recur_date, ignore_acl=$ignore_acl, skip_notifications=$skip_notification)");
if (!($event = $this->read($cal_id,$recur_date)) ||
!$ignore_acl && !$this->check_perms(EGW_ACL_DELETE,$event))
{
@ -1446,7 +1447,10 @@ class calendar_boupdate extends calendar_bo
}
else // delete an exception
{
$event['recur_exception'][] = $recur_date = $this->date2ts($event['start']);
// need to read series master, as otherwise existing exceptions will be lost!
$recur_date = $this->date2ts($event['start']);
$event = $this->read($cal_id);
$event['recur_exception'][] = $recur_date;
unset($event['start']);
unset($event['end']);
$this->save($event); // updates the content-history

View File

@ -60,7 +60,7 @@ define('WEEK_s',7*DAY_s);
* - egw_cal_repeats: recur-data: type, optional enddate, etc.
* - egw_cal_extra: custom fields (multiple entries per cal_id possible)
*
* The new UI, BO and SO classes have a strikt definition, in which time-zone they operate:
* The new UI, BO and SO classes have a strict definition, in which timezone they operate:
* UI only operates in user-time, so there have to be no conversation at all !!!
* BO's functions take and return user-time only (!), they convert internaly everything to servertime, because
* SO operates only on server-time
@ -178,9 +178,8 @@ class calendar_so
",$this->dates_table LEFT JOIN $this->repeats_table ON $this->dates_table.cal_id=$this->repeats_table.cal_id".
" WHERE $this->cal_table.cal_id=$this->dates_table.cal_id") as $row)
{
$row['recur_exception'] = $row['recur_exception'] ? explode(',',$row['recur_exception']) : array();
if (!$row['recur_type']) $row['recur_type'] = MCAL_RECUR_NONE;
$row['alarm'] = array();
$row['recur_exception'] = $row['alarm'] = array();
$events[$row['cal_id']] = egw_db::strip_array_keys($row,'cal_');
// if a uid was supplied, convert it for the further code to an id
@ -193,27 +192,20 @@ class calendar_so
if (!isset($event['uid']) || strlen($event['uid']) < $minimum_uid_length)
{
// event (without uid), not strong enough uid => create new uid
$event['uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$event['id']);
$event['uid'] = common::generate_uid('calendar',$event['id']);
$this->db->update($this->cal_table, array('cal_uid' => $event['uid']),
array('cal_id' => $event['id']),__LINE__,__FILE__,'calendar');
}
if ((int) $recur_date == 0 &&
$event['recur_type'] != MCAL_RECUR_NONE &&
!empty($event['recur_exception']))
if (!(int)$recur_date && $event['recur_type'] != MCAL_RECUR_NONE)
{
sort($event['recur_exception']);
if ($event['recur_exception'][0] < $event['start'])
foreach($this->db->select($this->dates_table, 'cal_id,cal_start', array(
'cal_id' => $ids,
'recur_exception' => true,
), __LINE__, __FILE__, false, 'ORDER BY cal_id,cal_start', 'calendar') as $row)
{
/* Do NOT move start- and end-date, to the earliest exception, as they will NOT be found in CalDAV or ActiveSync, because
* we only recognice recuring events which start before or in the current timerange and end in or after it or have no end-date.
* --> give an error message, as it is a debuging/support nightmare, if this get's silently fixed when reading events.
* No idea how this situation (exceptions before startdate) can be created anyway.
*
* $event['end'] -= $event['start'] - $event['recur_exception'][0];
* $event['start'] = $event['recur_exception'][0];
*/
error_log(__METHOD__."() recuring event #$event[id]: $event[title] has exceptions before it's startdate ".date('Y-m-d H:i:s',$event['start']));
$events[$row['cal_id']]['recur_exception'][] = $row['cal_start'];
}
break; // as above select read all exceptions (and I dont think too short uid problem still exists)
}
}
@ -424,7 +416,7 @@ class calendar_so
$cols = self::get_columns('calendar', $this->cal_table);
$cols[0] = $this->db->to_varchar($this->cal_table.'.cal_id');
$cols = isset($params['cols']) ? $params['cols'] : "$this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".implode(',',$cols).",cal_start,cal_end,$this->user_table.cal_recur_date";
$cols = isset($params['cols']) ? $params['cols'] : "$this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,".implode(',',$cols).",cal_start,cal_end,$this->user_table.cal_recur_date";
$where = array();
if (is_array($params['query']))
@ -639,7 +631,7 @@ class calendar_so
// we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns)
foreach(array_keys($selects) as $key)
{
$selects[$key]['cols'] = "DISTINCT $this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".$this->db->to_varchar($this->cal_table.'.cal_id').",cal_start,cal_end,$this->user_table.cal_recur_date";
$selects[$key]['cols'] = "DISTINCT $this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,".$this->db->to_varchar($this->cal_table.'.cal_id').",cal_start,cal_end,$this->user_table.cal_recur_date";
if (!$params['enum_recuring'])
{
$selects[$key]['cols'] = str_replace('cal_start','MIN(cal_start) AS cal_start',$selects[$key]['cols']);
@ -698,8 +690,7 @@ class calendar_so
{
$row['participants'] = array();
}
$row['alarm'] = array();
$row['recur_exception'] = $row['recur_exception'] ? explode(',',$row['recur_exception']) : array();
$row['recur_exception'] = $row['alarm'] = array();
// compile a list of recurrences per cal_id
if (!in_array($id,(array)$recur_ids[$row['cal_id']])) $recur_ids[$row['cal_id']][] = $id;
@ -755,6 +746,20 @@ class calendar_so
$events[$id]['max_user_modified'] = $modified;
}
}
// query recurrance exceptions, if needed
if (!$params['enum_recuring'])
{
foreach($this->db->select($this->dates_table, 'cal_id,cal_start', array(
'cal_id' => $ids,
'recur_exception' => true,
), __LINE__, __FILE__, false, 'ORDER BY cal_id,cal_start', 'calendar') as $row)
{
foreach((array)$recur_ids[$row['cal_id']] as $i)
{
$events[$i]['recurce_id'][] = $row['cal_start'];
}
}
}
// max_user_modified for recurring events has to include all recurrences, above code only querys $recur_date!
if (!$params['enum_recuring'] && $need_max_user_modified)
{
@ -1070,7 +1075,7 @@ ORDER BY cal_user_type, cal_usre_id
// event without uid or not strong enough uid
if (!isset($event['cal_uid']) || strlen($event['cal_uid']) < $minimum_uid_length)
{
$update['cal_uid'] = $event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id);
$update['cal_uid'] = $event['cal_uid'] = common::generate_uid('calendar',$cal_id);
}
// set caldav_name, if not given by caller
if (empty($event['caldav_name']) && version_compare($GLOBALS['egw_info']['apps']['calendar']['version'], '1.9.003', '>='))
@ -1103,11 +1108,13 @@ ORDER BY cal_user_type, cal_usre_id
$old_min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn();
$old_duration = (int) $this->db->select($this->dates_table,'MIN(cal_end)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn() - $old_min;
$old_repeats = $this->db->select($this->repeats_table,'*',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetch();
$old_exceptions = $old_repeats['recur_exception'] ? explode(',',$old_repeats['recur_exception']) : array();
if (!empty($old_exceptions))
$old_exceptions = array();
foreach($this->db->select($this->dates_table, 'cal_start', array(
'cal_id' => $cal_id,
'recur_exception' => true
), __LINE__, __FILE__, false, 'ORDER BY cal_start', 'calendar') as $row)
{
sort($old_exceptions);
if ($old_min > $old_exceptions[0]) $old_min = $old_exceptions[0];
$old_exceptions[] = $row['cal_start'];
}
$event['recur_exception'] = is_array($event['recur_exception']) ? $event['recur_exception'] : array();
@ -1116,8 +1123,10 @@ ORDER BY cal_user_type, cal_usre_id
sort($event['recur_exception']);
}
$where = array('cal_id' => $cal_id,
'cal_recur_date' => 0);
$where = array(
'cal_id' => $cal_id,
'cal_recur_date' => 0,
);
$old_participants = array();
foreach ($this->db->select($this->user_table,'cal_user_type,cal_user_id,cal_status,cal_quantity,cal_role', $where,
__LINE__,__FILE__,false,'','calendar') as $row)
@ -1197,14 +1206,18 @@ ORDER BY cal_user_type, cal_usre_id
// truncate recurrences by given exceptions
if (count($event['recur_exception']))
{
// added and existing exceptions: delete the execeptions from the user and dates table, it could be the first time
// added and existing exceptions: delete the execeptions from the user table, it could be the first time
$this->db->delete($this->user_table,array('cal_id' => $cal_id,'cal_recur_date' => $event['recur_exception']),__LINE__,__FILE__,'calendar');
$this->db->delete($this->dates_table,array('cal_id' => $cal_id,'cal_start' => $event['recur_exception']),__LINE__,__FILE__,'calendar');
// update recur_exception flag based on current exceptions
$this->db->update($this->dates_table, 'recur_exception='.$this->db->expression($this->dates_table,array(
'cal_start' => $event['recur_exception'],
)), array(
'cal_id' => $cal_id,
), __LINE__, __FILE__, 'calendar');
}
}
// write the repeats table
$event['recur_exception'] = empty($event['recur_exception']) ? null : implode(',',$event['recur_exception']);
unset($event[0]); // unset the 'etag=etag+1', as it's not in the repeats table
$this->db->insert($this->repeats_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}

View File

@ -10,7 +10,7 @@
*/
$setup_info['calendar']['name'] = 'calendar';
$setup_info['calendar']['version'] = '1.9.004';
$setup_info['calendar']['version'] = '1.9.006';
$setup_info['calendar']['app_order'] = 3;
$setup_info['calendar']['enable'] = 1;
$setup_info['calendar']['index'] = 'calendar.calendar_uiviews.index';
@ -68,5 +68,3 @@ $setup_info['calendar']['check_install'] = array(
'from' => 'Calendar',
),
);

View File

@ -15,12 +15,12 @@ $phpgw_baseline = array(
'cal_id' => array('type' => 'auto','nullable' => False),
'cal_uid' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'unique id of event(-series)'),
'cal_owner' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'event owner / calendar'),
'cal_category' => array('type' => 'varchar','precision' => '30','comment' => 'category id'),
'cal_category' => array('type' => 'varchar','precision' => '64','comment' => 'category id(s)'),
'cal_modified' => array('type' => 'int','precision' => '8','comment' => 'ts of last modification'),
'cal_priority' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '2'),
'cal_public' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '1','comment' => '1=public, 0=private event'),
'cal_title' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '1'),
'cal_description' => array('type' => 'text'),
'cal_title' => array('type' => 'varchar','precision' => '255','nullable' => False),
'cal_description' => array('type' => 'varchar','precision' => '16384'),
'cal_location' => array('type' => 'varchar','precision' => '255'),
'cal_reference' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0','comment' => 'cal_id of series for exception'),
'cal_modifier' => array('type' => 'int','precision' => '4','comment' => 'user who last modified event'),
@ -61,8 +61,7 @@ $phpgw_baseline = array(
'recur_type' => array('type' => 'int','precision' => '2','nullable' => False),
'recur_enddate' => array('type' => 'int','precision' => '8'),
'recur_interval' => array('type' => 'int','precision' => '2','default' => '1'),
'recur_data' => array('type' => 'int','precision' => '2','default' => '1'),
'recur_exception' => array('type' => 'text','comment' => 'comma-separated start timestamps of exceptions')
'recur_data' => array('type' => 'int','precision' => '2','default' => '1')
),
'pk' => array('cal_id'),
'fk' => array(),
@ -100,11 +99,12 @@ $phpgw_baseline = array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_start' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'starttime in server time'),
'cal_end' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'endtime in server time')
'cal_end' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'endtime in server time'),
'recur_exception' => array('type' => 'bool','nullable' => False,'default' => '','comment' => 'date is an exception')
),
'pk' => array('cal_id','cal_start'),
'fk' => array(),
'ix' => array(),
'ix' => array(array('recur_exception','cal_id')),
'uc' => array()
),
'egw_cal_timezones' => array(

View File

@ -2076,3 +2076,84 @@ function calendar_upgrade1_9_003()
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.004';
}
/**
* Store exceptions as flag in egw_cal_dates.recur_exception, instead of egw_cal_repleats.recur_exception
*
* Keeps information of original start in egw_cal_dates (if first recurrance got deleted) and allows for unlimited number of exceptions.
*/
function calendar_upgrade1_9_004()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal_dates','recur_exception',array(
'type' => 'bool',
'default' => '',
'null' => false,
'comment' => 'date is an exception'
));
// migrate existing exceptions to egw_cal_dates
foreach($GLOBALS['egw_setup']->db->select('egw_cal_repeats',
'egw_cal_repeats.cal_id AS cal_id,egw_cal_repeats.recur_exception AS recur_exception,MIN(cal_start) AS cal_start,MIN(cal_end) AS cal_end',
'egw_cal_repeats.recur_exception IS NOT NULL', __LINE__, __FILE__, false,
'GROUP BY egw_cal_repeats.cal_id,egw_cal_repeats.recur_exception', 'calendar', '',
'JOIN egw_cal_dates ON egw_cal_repeats.cal_id=egw_cal_dates.cal_id') as $row)
{
foreach($row['recur_exception'] ? explode(',', $row['recur_exception']) : array() as $recur_exception)
{
$GLOBALS['egw_setup']->db->insert('egw_cal_dates', array(
'cal_id' => $row['cal_id'],
'cal_start' => $recur_exception,
'cal_end' => $recur_exception+$row['cal_end']-$row['cal_start'],
'recur_exception' => true,
), false, __LINE__, __FILE__, 'calendar');
}
}
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_cal_dates', array('recur_exception', 'cal_id'));
$GLOBALS['egw_setup']->oProc->DropColumn('egw_cal_repeats', array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'recur_type' => array('type' => 'int','precision' => '2','nullable' => False),
'recur_enddate' => array('type' => 'int','precision' => '8'),
'recur_interval' => array('type' => 'int','precision' => '2','default' => '1'),
'recur_data' => array('type' => 'int','precision' => '2','default' => '1'),
),
'pk' => array('cal_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
), 'recur_exception');
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.005';
}
/**
* Try alter description to varchar(16384), to not force temp. tables to disk on MySQL (because of text columns)
*/
function calendar_upgrade1_9_005()
{
// only alter description to varchar(16384), if it does NOT contain longer input and it can be stored as varchar
$max_description_length = $GLOBALS['egw']->db->query('SELECT MAX(CHAR_LENGTH(cal_description)) FROM egw_cal')->fetchColumn();
// returns NULL, if there are no rows!
if ((int)$max_description_length <= 16384 && $GLOBALS['egw_setup']->oProc->max_varchar_length >= 16384)
{
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal','cal_description',array(
'type' => 'varchar',
'precision' => '16384'
));
}
// allow more categories
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal','cal_category',array(
'type' => 'varchar',
'precision' => '64',
'comment' => 'category id(s)'
));
// remove silly default of 1
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal','cal_title',array(
'type' => 'varchar',
'precision' => '255',
'nullable' => False
));
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.006';
}