Merge branch '2014.11-develop' into 2015.07
authorPhilipp Schüle <p.schuele@metaways.de>
Tue, 28 Jul 2015 13:46:47 +0000 (15:46 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Tue, 28 Jul 2015 13:46:47 +0000 (15:46 +0200)
Conflicts:
tine20/Calendar/Controller/Event.php
tine20/Calendar/Controller/MSEventFacade.php
tine20/Calendar/Frontend/iMIP.php
tine20/Calendar/Model/iMIP.php
tine20/Calendar/Setup/Update/Release8.php
tine20/Calendar/Setup/setup.xml
tine20/Calendar/js/EventEditDialog.js

Change-Id: Ie2a26449f2fc1239a8f5cfc23719f8521db8c6c7

13 files changed:
1  2 
tests/tine20/Calendar/Frontend/iMIPTest.php
tine20/Calendar/Config.php
tine20/Calendar/Controller/Event.php
tine20/Calendar/Controller/MSEventFacade.php
tine20/Calendar/Frontend/iMIP.php
tine20/Calendar/Model/iMIP.php
tine20/Calendar/Setup/Update/Release8.php
tine20/Calendar/Setup/setup.xml
tine20/Calendar/js/EventEditDialog.js
tine20/Calendar/js/EventUI.js
tine20/Calendar/js/MainScreenCenterPanel.js
tine20/Tinebase/js/widgets/ActivitiesPanel.js
tine20/composer.json

Simple merge
Simple merge
@@@ -31,7 -31,7 +31,7 @@@ class Calendar_Frontend_iMI
              return;
          }
          
-         if (! $_iMIP->getExistingEvent(TRUE) && $_iMIP->method != Calendar_Model_iMIP::METHOD_CANCEL) {
 -        if (! $this->getExistingEvent($_iMIP, TRUE)) {
++        if (! $this->getExistingEvent($_iMIP, TRUE) && $_iMIP->method != Calendar_Model_iMIP::METHOD_CANCEL) {
              if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " skip auto processing of iMIP component whose event is not in our db yet");
              return;
          }
                  $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
                  $result = FALSE;
              }
-                 if ($_iMIP->getExistingEvent(TRUE, TRUE)) {
 +        } else {
 +            try {
++                if ($this->getExistingEvent($_iMIP, TRUE, TRUE)) {
 +                    // Event was deleted/cancelled
 +                    $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_NOTDELETED, "old iMIP message is deleted");
 +                    $result = FALSE;
 +                }
 +            } catch (Tinebase_Exception_AccessDenied $e) {
 +                // attendee was removed from the event, continue ...
 +                $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_NOTCANCELLED, "event or attendee was cancelled from this event");
 +                $result = FALSE;
 +            }
          }
          
          return $result;
          
          return $result;
      }
-     
+     /**
+      * find existing event by uid
+      *
+      * @param $_iMIP
+      * @param bool $_refetch
++     * @param bool $_getDeleted
+      * @return NULL|Tinebase_Record_Abstract
+      * @throws Exception
+      */
 -    public function getExistingEvent($_iMIP, $_refetch = FALSE)
++    public function getExistingEvent($_iMIP, $_refetch = FALSE, $_getDeleted = FALSE)
+     {
+         if ($_refetch || ! $_iMIP->existing_event instanceof Calendar_Model_Event) {
+             $iMIPEvent = $_iMIP->getEvent();
 -            $events = Calendar_Controller_MSEventFacade::getInstance()->search(new Calendar_Model_EventFilter(array(
++            $filters = new Calendar_Model_EventFilter(array(
+                 array('field' => 'uid',          'operator' => 'equals', 'value' => $iMIPEvent->uid),
 -            )));
++            ));
++            if ($_getDeleted) {
++                $deletedFilter = new Tinebase_Model_Filter_Bool('is_deleted', 'equals', Tinebase_Model_Filter_Bool::VALUE_NOTSET);
++                $filters->addFilter($deletedFilter);
++            }
++            $events = $this->_backend->search($filters);
 -            $event = $events->filter(Tinebase_Model_Grants::GRANT_READ, TRUE)->getFirstRecord();
++            // NOTE: cancelled attendees from ext. organizers don't have read grant
++            // find a better way to check grants
++            if (! $_getDeleted) {
++                $event = $events->filter(Tinebase_Model_Grants::GRANT_READ, TRUE)->getFirstRecord();
++            }
+             Calendar_Model_Attender::resolveAttendee($event['attendee']);
+             $_iMIP->existing_event = $event;
+         }
+         return $_iMIP->existing_event;
+     }
      /**
       * process request
       * 
      */
      protected function _checkCancelPreconditions($_iMIP)
      {
-         $existingEvent = $_iMIP->getExistingEvent(FALSE, TRUE);
 -        $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_SUPPORTED, 'processing CANCEL is not supported yet');
 -    
 -        return FALSE;
++        $existingEvent = $this->getExistingEvent($_iMIP, FALSE, TRUE);
 +        $result = TRUE;
 +
 +        if ($existingEvent) {
 +            if ($existingEvent->is_deleted) {
 +                $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_NOTDELETED, "old iMIP message is deleted");
 +                $result = FALSE;
 +            } else if (! $existingEvent->hasExternalOrganizer()) {
 +                $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
 +                $result = FALSE;
 +            }
 +        }
 +        return $result;
      }
      
      /**
      {
          // organizer cancelled meeting/recurrence of an existing event -> update event
          // the iMIP is already the notification mail!
-         $existingEvent = $_iMIP->getExistingEvent(FALSE, TRUE);
++        $existingEvent = $this->getExistingEvent($_iMIP, FALSE, TRUE);
 +        $event = $_iMIP->getEvent();
 +
 +        if ($existingEvent) {
 +            if (! $existingEvent->is_deleted) {
 +                if ($event->status == Calendar_Model_Event::STATUS_CANCELED) {
 +                    // Event cancelled
 +                    Calendar_Controller_MSEventFacade::getInstance()->delete($existingEvent->getId());
 +                } else {
 +                    // Attendees cancelled
 +                    Calendar_Controller_MSEventFacade::getInstance()->deleteAttendees($existingEvent, $event);
 +                }
 +            }
 +        } else {
 +            // create a deleted/cancelled event
 +            $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE);
 +
 +            $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event);
 +            Calendar_Controller_MSEventFacade::getInstance()->delete($event->getId());
 +
 +            Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications);
 +        }
      }
      
      /**
Simple merge
@@@ -314,12 -314,108 +314,118 @@@ class Calendar_Setup_Update_Release8 ex
      }
  
      /**
-     public function update_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>base_event_id</name>
+                 <field>
+                     <name>base_event_id</name>
+                 </field>
+             </index>');
+         $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');
+     }
++
++    /**
 +     * update to 9.0
 +     *
 +     * @return void
 +     */
++    public function update_9()
 +    {
 +        $this->setApplicationVersion('Calendar', '9.0');
 +    }
  }
Simple merge
Simple merge
Simple merge
Simple merge