*/
class Calendar_Setup_Update_Release8 extends Setup_Update_Abstract
{
/**
* update to 8.1
* - move ack & snooze time from attendee to alarm
*/
public function update_0()
{
// find all events with ack or snooze times set
$eventIds = $this->_db->query(
"SELECT DISTINCT " . $this->_db->quoteIdentifier('cal_event_id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_attendee") .
" WHERE " . $this->_db->quoteIdentifier("alarm_ack_time") . " IS NOT NULL OR ". $this->_db->quoteIdentifier("alarm_snooze_time") . " IS NOT NULL"
)->fetchAll(Zend_Db::FETCH_ASSOC);
$attendeeBE = new Calendar_Backend_Sql_Attendee();
$alarmBE = Tinebase_Alarm::getInstance();
foreach ($eventIds as $eventId) {
$eventId = $eventId['cal_event_id'];
$attendeeFilter = new Tinebase_Model_Filter_FilterGroup();
$attendeeFilter->addFilter(new Tinebase_Model_Filter_Text('cal_event_id', 'equals', $eventId));
$attendees = $attendeeBE->search($attendeeFilter);
$alarms = $alarmBE->search(new Tinebase_Model_AlarmFilter(array(
array('field' => 'model', 'operator' => 'equals', 'value' =>'Calendar_Model_Event'),
array('field' => 'record_id', 'operator' => 'equals', 'value' => $eventId)
)));
foreach ($alarms as $alarm) {
foreach ($attendees as $attendee) {
if ($attendee->alarm_ack_time instanceof Tinebase_DateTime) {
$alarm->setOption("acknowledged-{$attendee->user_id}", $attendee->alarm_ack_time->format(Tinebase_Record_Abstract::ISO8601LONG));
}
if ($attendee->alarm_snooze_time instanceof Tinebase_DateTime) {
$alarm->setOption("snoozed-{$attendee->user_id}", $attendee->alarm_snooze_time->format(Tinebase_Record_Abstract::ISO8601LONG));
}
}
$alarmBE->update($alarm);
}
}
// delte ack & snooze from attendee
$this->_backend->dropCol('cal_attendee', 'alarm_ack_time');
$this->_backend->dropCol('cal_attendee', 'alarm_snooze_time');
$this->setTableVersion('cal_attendee', 5);
$this->setApplicationVersion('Calendar', '8.1');
}
/**
* update to 8.2
*
*/
public function update_1()
{
$eventBE = new Tinebase_Backend_Sql(array(
'modelName' => 'Calendar_Model_Event',
'tableName' => 'cal_events',
'modlogActive' => false
));
// find all events without organizer
$eventIds = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" WHERE " . $this->_db->quoteIdentifier("organizer") . " IS NULL OR " .
$this->_db->quoteIdentifier("organizer") . " = ''"
)->fetchAll(Zend_Db::FETCH_ASSOC);
foreach ($eventIds as $eventId) {
$event = $eventBE->get($eventId['id']);
$event->organizer = (string) Tinebase_User::getInstance()->getFullUserById($event->created_by)->contact_id;
$where = array(
$this->_db->quoteInto($this->_db->quoteIdentifier('id') . ' = ?', $eventId),
);
try {
$this->_db->update(SQL_TABLE_PREFIX . "cal_events", $event->toArray(), $where);
} catch (Tinebase_Exception_Record_Validation $terv) {
if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
. ' Could not fix invalid event record: ' . print_r($event->toArray(), true));
Tinebase_Exception::log($terv);
}
}
// find all events CONFIDENTIAL class
$eventIds = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" WHERE " . $this->_db->quoteIdentifier("class") . " = 'CONFIDENTIAL'"
)->fetchAll(Zend_Db::FETCH_ASSOC);
foreach ($eventIds as $eventId) {
$event = $eventBE->get($eventId['id']);
$class = Calendar_Model_Event::CLASS_PRIVATE;
$event->class = (string) $class;
$where = array(
$this->_db->quoteInto($this->_db->quoteIdentifier('id') . ' = ?', $eventId),
);
try {
$this->_db->update(SQL_TABLE_PREFIX . "cal_events", $event->toArray(), $where);
} catch (Tinebase_Exception_Record_Validation $terv) {
if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
. ' Could not fix invalid event record: ' . print_r($event->toArray(), true));
Tinebase_Exception::log($terv);
}
}
$this->setApplicationVersion('Calendar', '8.2');
}
/**
* update to 8.3
* - normalize all rrules
*/
public function update_2()
{
// find all events with rrule
$eventIds = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" WHERE " . $this->_db->quoteIdentifier("rrule") . " IS NOT NULL"
)->fetchAll(Zend_Db::FETCH_ASSOC);
// NOTE: we need a generic sql BE to circumvent calendar specific acl issues
$eventBE = new Tinebase_Backend_Sql(array(
'modelName' => 'Calendar_Model_Event',
'tableName' => 'cal_events',
'modlogActive' => false
));
foreach ($eventIds as $eventId) {
$event = $eventBE->get($eventId['id']);
$oldRruleString = (string) $event->rrule;
$rrule = Calendar_Model_Rrule::getRruleFromString($oldRruleString);
$rrule->normalize($event);
if ($oldRruleString != (string) $rrule) {
$event->rrule = (string) $rrule;
try {
$eventBE->update($event);
} catch (Tinebase_Exception_Record_Validation $terv) {
Tinebase_Exception::log($terv, null, $event->toArray());
} catch (Tinebase_Exception_UnexpectedValue $teuv) {
Tinebase_Exception::log($teuv, null, $event->toArray());
}
}
}
$this->setApplicationVersion('Calendar', '8.3');
}
/**
* update to 8.4
*
* - adds etag column
*/
public function update_3()
{
if (! $this->_backend->columnExists('etag', 'cal_events')) {
$declaration = new Setup_Backend_Schema_Field_Xml('
etag
text
60
');
$this->_backend->addCol('cal_events', $declaration);
$declaration = new Setup_Backend_Schema_Index_Xml('
etag
etag
');
$this->_backend->addIndex('cal_events', $declaration);
}
$this->setTableVersion('cal_events', 7);
$this->setApplicationVersion('Calendar', '8.4');
}
/**
* - update import / export
*/
public function update_4()
{
Setup_Controller::getInstance()->createImportExportDefinitions(Tinebase_Application::getInstance()->getApplicationByName('Calendar'));
$this->setTableVersion('cal_events', 7);
$this->setApplicationVersion('Calendar', '8.5');
}
/**
* adds external_seq col
*
* @see 0009890: improve external event invitation support
*/
public function update_5()
{
if (! $this->_backend->columnExists('external_seq', 'cal_events')) {
$seqCol = '
external_seq
integer
true
0
';
$declaration = new Setup_Backend_Schema_Field_Xml($seqCol);
$this->_backend->addCol('cal_events', $declaration);
}
$this->setTableVersion('cal_events', '8');
$this->setApplicationVersion('Calendar', '8.6');
}
/**
* add rrule index
*
* @see 0010214: improve calendar performance / yearly base events
*
* TODO re-enable this when it is fixed for postgresql
* @see 0011194: only drop index if it exists
*/
public function update_6()
{
// $declaration = new Setup_Backend_Schema_Index_Xml('
//
// rrule
//
// rrule
//
// ');
// try {
// $this->_backend->addIndex('cal_events', $declaration);
// } catch (Zend_Db_Statement_Exception $e) {
// Tinebase_Exception::log($e);
// }
$this->setTableVersion('cal_events', '9');
$this->setApplicationVersion('Calendar', '8.7');
}
/**
* repair missing displaycontainer_id
*/
public function update_7()
{
$allUser = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('id') . "," . $this->_db->quoteIdentifier('contact_id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "accounts") .
" WHERE " . $this->_db->quoteIdentifier("contact_id") . " IS NOT NULL"
)->fetchAll(Zend_Db::FETCH_ASSOC);
$contactUserMap = array();
foreach ($allUser as $id => $user) {
$contactUserMap[$user['contact_id']] = $user['id'];
}
// find all user/groupmember attendees with missing displaycontainer
$attendees = $this->_db->query(
"SELECT DISTINCT" . $this->_db->quoteIdentifier('user_type') . "," . $this->_db->quoteIdentifier('user_id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_attendee") .
" WHERE " . $this->_db->quoteIdentifier("displaycontainer_id") . " IS NULL" .
" AND " . $this->_db->quoteIdentifier("user_type") . $this->_db->quoteInto(" IN (?)", array('user', 'groupmemeber')) .
" AND " . $this->_db->quoteIdentifier("user_id") . $this->_db->quoteInto(" IN (?)", array_keys($contactUserMap))
)->fetchAll(Zend_Db::FETCH_ASSOC);
// find all user/groupmember attendees with missing displaycontainer
$attendees = array_merge($attendees, $this->_db->query(
"SELECT DISTINCT" . $this->_db->quoteIdentifier('user_type') . "," . $this->_db->quoteIdentifier('user_id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_attendee") .
" WHERE " . $this->_db->quoteIdentifier("displaycontainer_id") . " IS NULL" .
" AND " . $this->_db->quoteIdentifier("user_type") . $this->_db->quoteInto(" IN (?)", array('resource'))
)->fetchAll(Zend_Db::FETCH_ASSOC));
$resources = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('id') . "," . $this->_db->quoteIdentifier('container_id') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_resources")
)->fetchAll(Zend_Db::FETCH_ASSOC);
$resourceContainerMap = array();
foreach ($resources as $resource) {
$resourceContainerMap[$resource['id']] = $resource['container_id'];
}
foreach ($attendees as $attendee) {
//find out displaycontainer
if ($attendee['user_type'] != 'resource') {
$userAccountId = $contactUserMap[$attendee['user_id']];
try {
$attendee['displaycontainerId'] = Calendar_Controller_Event::getDefaultDisplayContainerId($userAccountId);
} catch (Tinebase_Exception_NotFound $tenf) {
Setup_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . " Could not find user with id " . $attendee['user_id']);
continue;
}
} else {
$attendee['displaycontainerId'] = $resourceContainerMap[$attendee['user_id']];
}
// update displaycontainer
$this->_db->query(
"UPDATE" . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_attendee") .
" SET " . $this->_db->quoteIdentifier("displaycontainer_id") . " = " . $this->_db->quote($attendee['displaycontainerId']) .
" WHERE " . $this->_db->quoteIdentifier("user_type") . " = " . $this->_db->quote($attendee['user_type']) .
" AND " . $this->_db->quoteIdentifier("user_id") . " = " . $this->_db->quote($attendee['user_id'])
);
}
$this->setApplicationVersion('Calendar', '8.8');
}
/**
* identify base event via new base_event_id field instead of UID
*/
public function update_8()
{
/* find possibly broken events
SELECT group_concat(id), uid, count(id) as cnt from tine20_cal_events
WHERE rrule IS NOT NULL
GROUP BY uid
HAVING cnt > 1;
*/
$declaration = new Setup_Backend_Schema_Field_Xml('
base_event_id
text
40
');
$this->_backend->addCol('cal_events', $declaration);
$declaration = new Setup_Backend_Schema_Index_Xml('
base_event_id
base_event_id
');
$this->_backend->addIndex('cal_events', $declaration);
// find all events with rrule
$events = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('id') .
', ' . $this->_db->quoteIdentifier('uid') .
', ' . $this->_db->quoteIdentifier('container_id') .
', ' . $this->_db->quoteIdentifier('created_by') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" WHERE " . $this->_db->quoteIdentifier("rrule") . " IS NOT NULL" .
" AND " . $this->_db->quoteIdentifier("is_deleted") . " = " . $this->_db->quote(0, Zend_Db::INT_TYPE)
)->fetchAll(Zend_Db::FETCH_ASSOC);
// update all exdates in same container
foreach($events as $event) {
$this->_db->query(
"UPDATE " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" SET " . $this->_db->quoteIdentifier('base_event_id') . ' = ' . $this->_db->quote($event['id']) .
" WHERE " . $this->_db->quoteIdentifier('uid') . ' = ' . $this->_db->quote($event['uid']) .
" AND " . $this->_db->quoteIdentifier("container_id") . ' = ' . $this->_db->quote($event['container_id']) .
" AND " . $this->_db->quoteIdentifier("recurid") . " IS NOT NULL" .
" AND " . $this->_db->quoteIdentifier("is_deleted") . " = " . $this->_db->quote(0, Zend_Db::INT_TYPE)
);
}
// find all container move exdates
$danglingExdates = $this->_db->query(
"SELECT " . $this->_db->quoteIdentifier('uid') .
', ' . $this->_db->quoteIdentifier('id') .
', ' . $this->_db->quoteIdentifier('created_by') .
" FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" WHERE " . $this->_db->quoteIdentifier("recurid") . " IS NOT NULL" .
" AND " . $this->_db->quoteIdentifier("base_event_id") . " IS NULL" .
" AND " . $this->_db->quoteIdentifier("is_deleted") . " = " . $this->_db->quote(0, Zend_Db::INT_TYPE)
)->fetchAll(Zend_Db::FETCH_ASSOC);
// try to match by creator
foreach ($danglingExdates as $exdate) {
$possibleBaseEvents = array();
$matches = array_filter($events, function ($event) use ($exdate, $possibleBaseEvents) {
if ($event['uid'] == $exdate['uid']) {
$possibleBaseEvents[] = $event;
return $event['created_by'] == $exdate['created_by'];
}
return false;
});
switch(count($matches)) {
case 0:
// no match :-(
if (count($possibleBaseEvents) == 0) {
// garbage? exdate without any base event
Setup_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . " dangling exdate with id {$exdate['id']}");
continue 2;
}
Setup_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . " no match for exdate with id {$exdate['id']}");
$baseEvent = current($possibleBaseEvents);
break;
case 1:
// exact match :-)
$baseEvent = current($matches);
break;
default:
// to much matches :-(
Setup_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . " multiple matches for exdate with id {$exdate['id']}");
$baseEvent = current($matches);
}
$this->_db->query(
"UPDATE " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") .
" SET " . $this->_db->quoteIdentifier('base_event_id') . ' = ' . $this->_db->quote($baseEvent['id']) .
" WHERE " . $this->_db->quoteIdentifier('id') . ' = ' . $this->_db->quote($exdate['id'])
);
}
$this->setTableVersion('cal_events', '10');
$this->setApplicationVersion('Calendar', '8.9');
}
/**
* @see 0011266: increase size of event fields summary and location
*/
public function update_9()
{
$fieldsToChange = array('location', 'summary');
foreach ($fieldsToChange as $name) {
$seqCol = '
' . $name . '
text
1024
';
$declaration = new Setup_Backend_Schema_Field_Xml($seqCol);
$this->_backend->alterCol('cal_events', $declaration);
}
$this->setTableVersion('cal_events', 11);
$this->setApplicationVersion('Calendar', '8.10');
}
}