/**
  * delete duplicate events defined by an event filter
  * 
  * @param Calendar_Model_EventFilter $filter
  * @param boolean $dryrun
  * @return integer number of deleted events
  */
 public function deleteDuplicateEvents($filter, $dryrun = TRUE)
 {
     if ($dryrun && Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' - Running in dry run mode - using filter: ' . print_r($filter->toArray(), true));
     }
     $duplicateFields = array('summary', 'dtstart', 'dtend');
     $select = $this->_db->select();
     $select->from(array($this->_tableName => $this->_tablePrefix . $this->_tableName), $duplicateFields);
     $select->where($this->_db->quoteIdentifier($this->_tableName . '.is_deleted') . ' = 0');
     $this->_addFilter($select, $filter);
     $select->group($duplicateFields)->having('count(*) > 1');
     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
         Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $select);
     }
     $rows = $this->_fetch($select, self::FETCH_ALL);
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($rows, TRUE));
     }
     $toDelete = array();
     foreach ($rows as $row) {
         $index = $row['summary'] . ' / ' . $row['dtstart'] . ' - ' . $row['dtend'];
         $filter = new Calendar_Model_EventFilter(array(array('field' => 'summary', 'operator' => 'equals', 'value' => $row['summary']), array('field' => 'dtstart', 'operator' => 'equals', 'value' => new Tinebase_DateTime($row['dtstart'])), array('field' => 'dtend', 'operator' => 'equals', 'value' => new Tinebase_DateTime($row['dtend']))));
         $pagination = new Tinebase_Model_Pagination(array('sort' => array($this->_tableName . '.last_modified_time', $this->_tableName . '.creation_time')));
         $select = $this->_db->select();
         $select->from(array($this->_tableName => $this->_tablePrefix . $this->_tableName));
         $select->where($this->_db->quoteIdentifier($this->_tableName . '.is_deleted') . ' = 0');
         $this->_addFilter($select, $filter);
         $pagination->appendPaginationSql($select);
         $rows = $this->_fetch($select, self::FETCH_ALL);
         $events = $this->_rawDataToRecordSet($rows);
         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
             Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($events->toArray(), TRUE));
         }
         $deleteIds = $events->getArrayOfIds();
         // keep the first
         array_shift($deleteIds);
         if (!empty($deleteIds)) {
             $deleteContainerIds = $events->container_id;
             $origContainer = array_shift($deleteContainerIds);
             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
                 Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Deleting ' . count($deleteIds) . ' duplicates of: ' . $index . ' in container_ids ' . implode(',', $deleteContainerIds) . ' (origin container: ' . $origContainer . ')');
             }
             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
                 Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($deleteIds, TRUE));
             }
             $toDelete = array_merge($toDelete, $deleteIds);
         } else {
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' No duplicates found for ' . $index);
             }
         }
     }
     if (empty($toDelete)) {
         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' No duplicates found.');
         }
         $result = 0;
     } else {
         $result = $dryrun ? count($toDelete) : $this->delete($toDelete);
     }
     return $result;
 }
 /**
  * returns freebusy information for given period and given attendee
  * 
  * @todo merge overlapping events to one freebusy entry
  * 
  * @param  array of array with from and until                   $_periods
  * @param  Tinebase_Record_RecordSet of Calendar_Model_Attender $_attendee
  * @param  array of UIDs                                        $_ignoreUIDs
  * @return Tinebase_Record_RecordSet of Calendar_Model_FreeBusy
  */
 public function getFreeBusyInfo($_periods, $_attendee, $_ignoreUIDs = array())
 {
     $fbInfoSet = new Tinebase_Record_RecordSet('Calendar_Model_FreeBusy');
     // map groupmembers to users
     $attendee = clone $_attendee;
     $attendee->addIndices(array('user_type'));
     $groupmembers = $attendee->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER);
     $groupmembers->user_type = Calendar_Model_Attender::USERTYPE_USER;
     // base filter data
     $filterData = array(array('field' => 'attender', 'operator' => 'in', 'value' => $_attendee), array('field' => 'transp', 'operator' => 'equals', 'value' => Calendar_Model_Event::TRANSP_OPAQUE));
     // add all periods to filterdata
     $periodFilters = array();
     foreach ($_periods as $period) {
         $periodFilters[] = array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $period['from'], 'until' => $period['until']));
     }
     $filterData[] = array('condition' => 'OR', 'filters' => $periodFilters);
     // finaly create filter
     $filter = new Calendar_Model_EventFilter($filterData);
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' free/busy filter: ' . print_r($filter->toArray(), true));
     }
     $events = $this->search($filter, new Tinebase_Model_Pagination(), FALSE, FALSE);
     foreach ($_periods as $period) {
         Calendar_Model_Rrule::mergeRecurrenceSet($events, $period['from'], $period['until']);
     }
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' value: ' . print_r($events->toArray(), true));
     }
     // create a typemap
     $typeMap = array();
     foreach ($attendee as $attender) {
         if (!(isset($typeMap[$attender['user_type']]) || array_key_exists($attender['user_type'], $typeMap))) {
             $typeMap[$attender['user_type']] = array();
         }
         $typeMap[$attender['user_type']][$attender['user_id']] = array();
     }
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' value: ' . print_r($typeMap, true));
     }
     // generate freeBusyInfos
     foreach ($events as $event) {
         // skip events with ignoreUID
         if (in_array($event->uid, $_ignoreUIDs)) {
             continue;
         }
         // check if event is conflicting one of the given periods
         $conflicts = FALSE;
         foreach ($_periods as $period) {
             if ($event->dtstart->isEarlier($period['until']) && $event->dtend->isLater($period['from'])) {
                 $conflicts = TRUE;
                 break;
             }
         }
         if (!$conflicts) {
             continue;
         }
         // map groupmembers to users
         $event->attendee->addIndices(array('user_type'));
         $groupmembers = $event->attendee->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER);
         $groupmembers->user_type = Calendar_Model_Attender::USERTYPE_USER;
         foreach ($event->attendee as $attender) {
             // skip declined/transp events
             if ($attender->status == Calendar_Model_Attender::STATUS_DECLINED || $attender->transp == Calendar_Model_Event::TRANSP_TRANSP) {
                 continue;
             }
             if ((isset($typeMap[$attender->user_type]) || array_key_exists($attender->user_type, $typeMap)) && (isset($typeMap[$attender->user_type][$attender->user_id]) || array_key_exists($attender->user_id, $typeMap[$attender->user_type]))) {
                 $fbInfo = new Calendar_Model_FreeBusy(array('user_type' => $attender->user_type, 'user_id' => $attender->user_id, 'dtstart' => clone $event->dtstart, 'dtend' => clone $event->dtend, 'type' => Calendar_Model_FreeBusy::FREEBUSY_BUSY), true);
                 if ($event->{Tinebase_Model_Grants::GRANT_READ}) {
                     $fbInfo->event = clone $event;
                     unset($fbInfo->event->attendee);
                 }
                 //$typeMap[$attender->user_type][$attender->user_id][] = $fbInfo;
                 $fbInfoSet->addRecord($fbInfo);
             }
         }
     }
     return $fbInfoSet;
 }