Merge branch '2013.10' into 2014.11
[tine20] / tine20 / Calendar / Setup / Update / Release8.php
index c788940..9611bc9 100644 (file)
@@ -171,75 +171,290 @@ class Calendar_Setup_Update_Release8 extends Setup_Update_Abstract
      */
     public function update_3()
     {
-        $declaration = new Setup_Backend_Schema_Field_Xml('
-            <field>
-                <name>etag</name>
-                <type>text</type>
-                <length>60</length>
-            </field>');
-        $this->_backend->addCol('cal_events', $declaration);
-        
-        $declaration = new Setup_Backend_Schema_Index_Xml('
-            <index>
-                <name>etag</name>
+        if (! $this->_backend->columnExists('etag', 'cal_events')) {
+            $declaration = new Setup_Backend_Schema_Field_Xml('
                 <field>
                     <name>etag</name>
-                </field>
-            </index>');
-        $this->_backend->addIndex('cal_events', $declaration);
+                    <type>text</type>
+                    <length>60</length>
+                </field>');
+            $this->_backend->addCol('cal_events', $declaration);
+
+            $declaration = new Setup_Backend_Schema_Index_Xml('
+                <index>
+                    <name>etag</name>
+                    <field>
+                        <name>etag</name>
+                    </field>
+                </index>');
+            $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_4()
+    public function update_5()
     {
-        $seqCol = '<field>
-            <name>external_seq</name>
-            <type>integer</type>
-            <notnull>true</notnull>
-            <default>0</default>
-        </field>';
-        
-        $declaration = new Setup_Backend_Schema_Field_Xml($seqCol);
-        $this->_backend->addCol('cal_events', $declaration);
+        if (! $this->_backend->columnExists('external_seq', 'cal_events')) {
+            $seqCol = '<field>
+                <name>external_seq</name>
+                <type>integer</type>
+                <notnull>true</notnull>
+                <default>0</default>
+            </field>';
+            
+            $declaration = new Setup_Backend_Schema_Field_Xml($seqCol);
+            $this->_backend->addCol('cal_events', $declaration);
+        }
         
-        $this->setTableVersion('cal_events', 8);
-        $this->setApplicationVersion('Calendar', '8.5');
+        $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_5()
+    public function update_6()
+    {
+//        $declaration = new Setup_Backend_Schema_Index_Xml('
+//            <index>
+//                <name>rrule</name>
+//                <field>
+//                    <name>rrule</name>
+//                </field>
+//            </index>');
+//        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('
+            <field>
+                <name>base_event_id</name>
+                <type>text</type>
+                <length>40</length>
+            </field>');
+        $this->_backend->addCol('cal_events', $declaration);
+
         $declaration = new Setup_Backend_Schema_Index_Xml('
             <index>
-                <name>rrule</name>
+                <name>base_event_id</name>
                 <field>
-                    <name>rrule</name>
+                    <name>base_event_id</name>
                 </field>
             </index>');
-        try {
-            $this->_backend->addIndex('cal_events', $declaration);
-        } catch (Zend_Db_Statement_Exception $e) {
-            Tinebase_Exception::log($e);
+        $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)
+            );
         }
-        
-        $this->setTableVersion('cal_events', '9');
-        $this->setApplicationVersion('Calendar', '8.6');
+
+        // 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 = '<field>
+                <name>' . $name . '</name>
+                <type>text</type>
+                <length>1024</length>
+            </field>';
+
+            $declaration = new Setup_Backend_Schema_Field_Xml($seqCol);
+            $this->_backend->alterCol('cal_events', $declaration);
+        }
+
+        $this->setTableVersion('cal_events', 11);
+        $this->setApplicationVersion('Calendar', '8.10');
     }
 
     /**
      * force activesync calendar resync for iOS devices
      */
-    public function update_6()
+    public function update_10()
     {
         $deviceBackend = new ActiveSync_Backend_Device();
         $usersWithiPhones = $deviceBackend->search(new ActiveSync_Model_DeviceFilter(array(
@@ -251,6 +466,6 @@ class Calendar_Setup_Update_Release8 extends Setup_Update_Abstract
             $activeSyncController->resetSyncForUser($userId, 'Calendar');
         }
 
-        $this->setApplicationVersion('Calendar', '8.7');
+        $this->setApplicationVersion('Calendar', '8.11');
     }
 }