/** * * @return type */ public function getFilterImap() { $format = "d-M-Y"; // prepare value $value = (array) $this->_getDateValues($this->_operator, $this->_value); $timezone = Tinebase_Helper::array_value('timezone', $this->_options); $timezone = $timezone ? $timezone : Tinebase_Core::getUserTimezone(); foreach ($value as &$date) { $date = new Tinebase_DateTime($date); // should be in user timezone $date->setTimezone(new DateTimeZone($timezone)); } switch ($this->_operator) { case 'within': case 'inweek': $value[1]->add(new DateInterval('P1D')); // before is not inclusive, so we have to add a day $return = "SINCE {$value[0]->format($format)} BEFORE {$value[1]->format($format)}"; break; case 'before': $return = "BEFORE {$value[0]->format($format)}"; break; case 'after': $return = "SINCE {$value[0]->format($format)}"; break; case 'equals': $return = "ON {$value[0]->format($format)}"; } return $return; }
/** * * @return type * @todo fix method setFromArray() */ public function getFilterImap() { $format = "d-M-Y"; // prepare value $value = (array) $this->_getDateValues($this->_operator, $this->_value); $timezone = array_value('timezone', $this->_options); foreach ($value as &$date) { $date = new Tinebase_DateTime($date); // should be in user timezone $date->setTimezone(new DateTimeZone($timezone)); } switch ($this->_operator) { case 'within': case 'inweek': $return = "SINCE {$value[0]->format($format)} BEFORE {$value[1]->format($format)}"; break; case 'before': $return = "BEFORE {$value[0]->format($format)}"; break; case 'after': $return = "SINCE {$value[0]->format($format)}"; break; case 'equals': // errado $return = "ON {$value[0]->format($format)}"; } return $return; }
/** * returns array with the filter settings of this filter * - convert value to user timezone * * @param bool $_valueToJson resolve value for json api? * @return array */ public function toArray($_valueToJson = false) { $result = parent::toArray($_valueToJson); if ($this->_operator != 'within' && $this->_operator != 'inweek' && $_valueToJson == true) { $date = new Tinebase_DateTime($result['value']); $date->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)); $result['value'] = $date->toString(Tinebase_Record_Abstract::ISO8601LONG); } return $result; }
public function testHasTime() { $date = new Tinebase_DateTime('2011-11-11 11:11:11', 'Europe/Berlin'); $this->assertTrue($date->hasTime(), 'date must have the hasTime flag'); $date->hasTime(FALSE); $this->assertFalse($date->hasTime(), 'date must not have the hasTime flag'); $this->assertEquals('00:00:00', $date->format('H:i:s'), 'time info has not been reset'); $date->setTimezone('Asia/Tehran'); $this->assertEquals('2011-11-11', $date->format('Y-m-d'), 'date must not chage'); $this->assertEquals('00:00:00', $date->format('H:i:s'), 'time must not chage'); }
protected function _setReferenceDate() { // set reference date to the 1st january of last year $this->_referenceDate = Tinebase_DateTime::now(); $this->_referenceDate->setTimezone(Tinebase_Core::getUserTimezone()); $this->_referenceDate->subYear(1); $this->_referenceDate->setDate($this->_referenceDate->format('Y'), 1, 1); $this->_referenceDate->setTime(0, 0, 0); $this->_referenceYear = $this->_referenceDate->format('Y'); $this->_lastMonthDays = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); // find out if year is a leap year if ((bool) $this->_referenceDate->format('L')) { $this->_isLeapYear = TRUE; $this->_lastMonthDays[1] = 29; } }
/** * convert date from sent/received * * @param string $_dateString * @return Zend_Date */ public static function convertDate($_dateString) { try { $date = new Tinebase_DateTime($_dateString ? $_dateString : '@0'); $date->setTimezone('UTC'); } catch (Exception $e) { // try to fix missing timezone char if (preg_match('/UT$/', $_dateString)) { $_dateString .= 'C'; } // try some explicit formats foreach (self::$dateFormats as $format) { $date = DateTime::createFromFormat($format, $_dateString); if ($date) { break; } } if (!$date) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Date {$_dateString} could not be converted to DateTime -> using 1970-01-01 00:00:00."); $date = new Tinebase_DateTime('@0'); } } return $date; }
/** * returns the current date if no $date string is given (needed for mocking only) * * @param string $date * @param boolean $usertimezone */ protected function _getDate($date = NULL, $usertimezone = FALSE) { if (!$date) { $date = Tinebase_DateTime::now(); } else { $date = new Tinebase_DateTime($date); } if ($usertimezone) { $date->setTimezone(Tinebase_Core::getUserTimezone()); } return $date; }
/** * test import with mergeTheirs resolve strategy * * @see 0007226: tag handling and other import problems */ public function testImportWithResolveStrategyMergeTheirs() { $this->_testNeedsTransaction(); $result = $this->_importHelper(array('dryrun' => 0)); $this->assertEquals(2, count($result['results']), 'no import results'); $fritz = $result['results'][1]; $fritz['tags'][] = array('name' => 'new import tag'); $fritz['tel_work'] = '04040'; $clientRecords = array(array('recordData' => $fritz, 'resolveStrategy' => 'mergeTheirs', 'index' => 1)); $result = $this->_importHelper(array('dryrun' => 0), $clientRecords); $this->assertEquals(1, $result['totalcount'], 'Should merge fritz'); $this->assertEquals(2, count($result['results'][0]['tags']), 'Should merge tags'); $this->assertEquals(0, $result['failcount'], 'no failures expected'); $this->assertEquals(1, $result['duplicatecount'], 'klaus should still be a duplicate'); $fritz = $result['results'][0]; $fritz['tel_work'] = '04040'; // need to adjust user TZ $lastModified = new Tinebase_DateTime($fritz['last_modified_time']); $fritz['last_modified_time'] = $lastModified->setTimezone(Tinebase_Core::getUserTimezone())->toString(); $clientRecords = array(array('recordData' => $fritz, 'resolveStrategy' => 'mergeTheirs', 'index' => 1)); $result = $this->_importHelper(array('dryrun' => 0), $clientRecords); $this->assertEquals(1, $result['totalcount'], 'Should merge fritz: ' . print_r($result, TRUE)); }
/** * test adding a contract with manually setting the end_date of the contract before */ public function testAddContract() { $sdate = new Tinebase_DateTime('2013-01-01 00:00:00'); $sdate->setTimezone(Tinebase_Core::getUserTimezone()); $employee = $this->_getEmployee('rwright'); $contractController = HumanResources_Controller_Contract::getInstance(); $employeeController = HumanResources_Controller_Employee::getInstance(); $employee = $employeeController->create($employee); $contract = $this->_getContract($sdate); $contract->workingtime_json = '{"days": [8,8,8,8,8,0,0]}'; $contract->employee_id = $employee->getId(); $feastCalendar = $this->_getFeastCalendar(); $contract->feast_calendar_id = $feastCalendar->getId(); $contract->start_date = $sdate; $contractController->create($contract); $employeeJson = $this->_json->getEmployee($employee->getId()); $accountController = HumanResources_Controller_Account::getInstance(); // should not be created, exist already $accountController->createMissingAccounts(2013, $employee); $account = $accountController->getAll()->getFirstRecord(); $employeeJson['vacation'] = array(array('account_id' => $account->getId(), 'type' => 'vacation', 'status' => 'ACCEPTED', 'freedays' => array(array('duration' => '1', 'date' => '2013-01-11 00:00:00')))); $employeeJson = $this->_json->saveEmployee($employeeJson); $this->assertEquals(1, count($employeeJson['vacation'])); // manually set the end date and add a new contract $employeeJson['contracts'][0]['end_date'] = '2013-05-31 00:00:00'; $employeeJson['contracts'][1] = array('start_date' => '2013-06-01 00:00:00', 'workingtime_json' => '{"days": [8,8,8,8,8,0,0]}', 'vacation_days' => 27, 'feast_calendar_id' => $feastCalendar->getId()); // no exception should be thrown $employeeJson = $this->_json->saveEmployee($employeeJson); $this->assertEquals(2, count($employeeJson['contracts'])); // an exception should be thrown $employeeJson['contracts'][0]['vacation_days'] = 31; $this->setExpectedException('HumanResources_Exception_ContractNotEditable'); $employeeJson = $this->_json->saveEmployee($employeeJson); }
public function testCalcYearlyByMonthDayLeapYear() { $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'yearly 29.feb', 'dtstart' => '2008-02-29 08:00:00', 'dtend' => '2008-02-29 10:00:00', 'rrule' => 'FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=29', 'originator_tz' => 'Europe/Berlin', Tinebase_Model_Grants::GRANT_EDIT => true)); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); // note: 2009-03-29 Europe/Berlin switched to DST $from = new Tinebase_DateTime('2008-02-25 00:00:00'); $until = new Tinebase_DateTime('2013-03-01 23:59:59'); $until->setTimezone('Europe/Berlin'); $from->setTimezone('Europe/Berlin'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until); $this->assertEquals(1, count($recurSet), 'leapyear only failed'); $this->assertEquals('2012-02-29 08:00:00', $recurSet[0]->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG)); }
/** * creates the invoices - no containers, just "shared" */ protected function _createSharedInvoices() { $sic = Sales_Controller_Invoice::getInstance(); $now = new Tinebase_DateTime(); $now->setTimezone(Tinebase_Core::getUserTimezone()); $now->setDate($now->format('Y'), $now->format('m'), 1); $now->setTime(3, 0, 0); $date = clone $this->_referenceDate; while ($date < $now) { $sic->createAutoInvoices($date); $date->addMonth(1); } }
/** * resolves virtual field "is_editable" * * @param array $resultSet */ public function getEditableState($resultSet) { for ($i = 0; $i < count($resultSet); $i++) { $sDate = new Tinebase_DateTime($resultSet[$i]['start_date']); $sDate->setTimezone(Tinebase_Core::getUserTimezone()); $eDate = NULL; if ($resultSet[$i]['end_date']) { $eDate = new Tinebase_DateTime($resultSet[$i]['end_date']); $eDate->setTimezone(Tinebase_Core::getUserTimezone()); } $freeTimeFilter = new HumanResources_Model_FreeTimeFilter(array(array('field' => 'firstday_date', 'operator' => 'after', 'value' => $sDate), array('field' => 'type', 'operator' => 'equals', 'value' => 'vacation'))); if ($eDate) { $freeTimeFilter->addFilter(new Tinebase_Model_Filter_Date(array('field' => 'firstday_date', 'operator' => 'before', 'value' => $eDate))); } $freeTimeFilter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'employee_id', 'operator' => 'equals', 'value' => $resultSet[$i]['employee_id']))); $freeTimeController = HumanResources_Controller_FreeTime::getInstance(); $resultSet[$i]['is_editable'] = $freeTimeController->search($freeTimeFilter)->count() > 0 ? FALSE : TRUE; } return $resultSet; }
/** * parse valarm properties * * @param Tinebase_Record_Abstract $record * @param iteratable $valarms * @param \Sabre\VObject\Component $vcalendar */ protected function _parseAlarm(Tinebase_Record_Abstract $record, $valarms, \Sabre\VObject\Component $vcomponent) { foreach ($valarms as $valarm) { if ($valarm->ACTION == 'NONE') { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' We can\'t cope with action NONE: iCal 6.0 sends default alarms in the year 1976 with action NONE. Skipping alarm.'); } continue; } if (!is_object($valarm->TRIGGER)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Alarm has no TRIGGER value. Skipping it.'); } continue; } # TRIGGER:-PT15M if (is_string($valarm->TRIGGER->getValue()) && $valarm->TRIGGER instanceof Sabre\VObject\Property\ICalendar\Duration) { if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Adding DURATION trigger value for ' . $valarm->TRIGGER->getValue()); } $valarm->TRIGGER->add('VALUE', 'DURATION'); } $trigger = is_object($valarm->TRIGGER['VALUE']) ? $valarm->TRIGGER['VALUE'] : (is_object($valarm->TRIGGER['RELATED']) ? $valarm->TRIGGER['RELATED'] : NULL); if ($trigger === NULL) { // added Trigger/Related for eM Client alarms // 2014-01-03 - Bullshit, why don't we have testdata for emclient alarms? // this alarm handling should be refactored, the logic is scrambled // @see 0006110: handle iMIP messages from outlook // @todo fix 0007446: handle broken alarm in outlook invitation message if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Alarm has no TRIGGER value. Skipping it.'); } continue; } switch (strtoupper($trigger->getValue())) { # TRIGGER;VALUE=DATE-TIME:20111031T130000Z case 'DATE-TIME': $alarmTime = new Tinebase_DateTime($valarm->TRIGGER->getValue()); $alarmTime->setTimezone('UTC'); $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime, 'minutes_before' => 'custom', 'model' => $this->_modelName)); break; # TRIGGER;VALUE=DURATION:-PT1H15M # TRIGGER;VALUE=DURATION:-PT1H15M case 'DURATION': default: $durationBaseTime = isset($vcomponent->DTSTART) ? $vcomponent->DTSTART : $vcomponent->DUE; $alarmTime = $this->_convertToTinebaseDateTime($durationBaseTime); $alarmTime->setTimezone('UTC'); preg_match('/(?P<invert>[+-]?)(?P<spec>P.*)/', $valarm->TRIGGER->getValue(), $matches); $duration = new DateInterval($matches['spec']); $duration->invert = !!($matches['invert'] === '-'); $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime->add($duration), 'minutes_before' => $duration->format('%d') * 60 * 24 + $duration->format('%h') * 60 + $duration->format('%i'), 'model' => $this->_modelName)); if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Adding DURATION alarm ' . print_r($alarm->toArray(), true)); } } if ($valarm->ACKNOWLEDGED) { $dtack = $valarm->ACKNOWLEDGED->getDateTime(); Calendar_Controller_Alarm::setAcknowledgeTime($alarm, $dtack); } $record->alarms->addRecord($alarm); } }
/** * testCreateRecurEventWithRruleUntil * * @see 0008906: rrule_until is saved in usertime */ public function testCreateRecurEventWithRruleUntil() { $eventData = $this->testCreateRecurEvent(); $localMidnight = Tinebase_DateTime::now()->setTime(23, 59, 59)->toString(); $eventData['rrule']['until'] = $localMidnight; //$eventData['rrule']['freq'] = 'WEEKLY'; $updatedEventData = $this->_uit->saveEvent($eventData); $this->assertGreaterThanOrEqual($localMidnight, $updatedEventData['rrule']['until']); // check db record $calbackend = new Calendar_Backend_Sql(); $db = $calbackend->getAdapter(); $select = $db->select(); $select->from(array($calbackend->getTableName() => $calbackend->getTablePrefix() . $calbackend->getTableName()), array('rrule_until', 'rrule'))->limit(1); $select->where($db->quoteIdentifier($calbackend->getTableName() . '.id') . ' = ?', $updatedEventData['id']); $stmt = $db->query($select); $queryResult = $stmt->fetch(); // echo Tinebase_Core::getUserTimezone(); // echo date_default_timezone_get(); $midnightInUTC = new Tinebase_DateTime($queryResult['rrule_until']); $this->assertEquals(Tinebase_DateTime::now()->setTime(23, 59, 59)->toString(), $midnightInUTC->setTimezone(Tinebase_Core::getUserTimezone(), TRUE)->toString()); }
/** * create auto invoices for one contract * * @param Sales_Model_Contract $contract * @param Tinebase_DateTime $currentDate * @param boolean $merge */ protected function _createAutoInvoicesForContract(Sales_Model_Contract $contract, Tinebase_DateTime $currentDate, $merge = false) { // set this current billing date (user timezone) $this->_currentBillingDate = clone $currentDate; $this->_currentBillingDate->setDate($this->_currentBillingDate->format('Y'), $this->_currentBillingDate->format('m'), 1); $this->_currentBillingDate->setTime(0, 0, 0); // check all prerequisites needed for billing of the contract if (!$this->_validateContract($contract)) { return false; } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Processing contract "' . $this->_currentBillingContract->number . '"'); } // fire event to allow other applications do some work before billing $this->_firePrebillEvent(); // find product aggregates of the current contract $productAggregates = $this->_findProductAggregates(); // find month that needs to be billed next (note: _currentMonthToBill is the 01-01 00:00:00 of the next month, its the border, like last_autobill) $this->_currentMonthToBill = null; foreach ($productAggregates as $productAggregate) { if (null != $productAggregate->last_autobill) { $tmp = clone $productAggregate->last_autobill; $tmp->setDate($tmp->format('Y'), $tmp->format('m'), 1); $tmp->setTime(0, 0, 0); if (null == $this->_currentMonthToBill || $tmp->isLater($this->_currentMonthToBill)) { $this->_currentMonthToBill = $tmp; } } } // this contract has no productAggregates, maybe just time accounts? use last invoice to find already billed month if (null == $this->_currentMonthToBill) { // find newest invoice of contract (probably can be done more efficient!) $invoiceRelations = Tinebase_Relations::getInstance()->getRelations('Sales_Model_Contract', 'Sql', $contract->getId(), NULL, array(), TRUE, array('Sales_Model_Invoice')); // do not modify $newestInvoiceTime!!!! it does NOT get cloned! $newestInvoiceTime = null; $newestInvoice = null; foreach ($invoiceRelations as $invoiceRelation) { $invoiceRelation->related_record->setTimezone(Tinebase_Core::getUserTimezone()); if (null == $newestInvoiceTime || $invoiceRelation->related_record->creation_time->isLater($newestInvoiceTime)) { $newestInvoiceTime = $invoiceRelation->related_record->creation_time; $newestInvoice = $invoiceRelation->related_record; } } if (null != $newestInvoice) { // we can only take the end_date because there are no product aggregates (that have a last_autobill set) in this contract, otherwise it might be one interval ahead! $this->_currentMonthToBill = clone $newestInvoice->end_date; $this->_currentMonthToBill->addDay(4); $this->_currentMonthToBill->subMonth(1); //$this->_currentMonthToBill->setTimezone(Tinebase_Core::getUserTimezone()); } } $_addMonth = true; if (null == $this->_currentMonthToBill) { $this->_currentMonthToBill = clone $contract->start_date; $_addMonth = false; } $this->_currentMonthToBill->setTimezone(Tinebase_Core::getUserTimezone()); $this->_currentMonthToBill->setDate($this->_currentMonthToBill->format('Y'), $this->_currentMonthToBill->format('m'), 1); $this->_currentMonthToBill->setTime(0, 0, 0); if ($_addMonth) { $this->_currentMonthToBill->addMonth(1); } $doSleep = false; if (($merge || $contract->merge_invoices) && $this->_currentMonthToBill->isEarlier($this->_currentBillingDate)) { $this->_currentMonthToBill = clone $this->_currentBillingDate; } while ($this->_currentMonthToBill->isEarlierOrEquals($this->_currentBillingDate)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $this->_currentMonthToBill: ' . $this->_currentMonthToBill . ' $this->_currentBillingDate ' . $this->_currentBillingDate); foreach ($productAggregates as $productAggregate) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $productAggregate->id . ' ' . $productAggregate->last_autobill . ' ' . $productAggregate->interval); } } //required to have one sec difference in the invoice creation_time, can be optimized to look for milliseconds if ($doSleep) { sleep(1); $doSleep = false; } // prepare relations and find all billable accountables of the current contract list($relations, $billableAccountables) = $this->_prepareInvoiceRelationsAndFindBillableAccountables($productAggregates); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' count $billableAccountables: ' . count($billableAccountables)); foreach ($billableAccountables as $ba) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' accountable: ' . get_class($ba['ac']) . ' id: ' . $ba['ac']->getId()); } } // find invoice positions and the first start date and last end date of all billables list($invoicePositions, $earliestStartDate, $latestEndDate) = $this->_findInvoicePositionsAndInvoiceInterval($billableAccountables); /**** TODO ****/ // if there are no positions, no more bills need to be created, // but the last_autobill info is set, if the current date is later if ($invoicePositions->count() > 0) { // prepare invoice $invoice = new Sales_Model_Invoice(array('is_auto' => TRUE, 'description' => $this->_currentBillingContract->title . ' (' . $this->_currentMonthToBill->toString() . ')', 'type' => 'INVOICE', 'address_id' => $this->_currentBillingContract->billing_address_id, 'credit_term' => $this->_currentBillingCustomer['credit_term'], 'customer_id' => $this->_currentBillingCustomer['id'], 'costcenter_id' => $this->_currentBillingCostCenter->getId(), 'start_date' => $earliestStartDate, 'end_date' => $latestEndDate, 'positions' => $invoicePositions->toArray(), 'date' => clone $this->_currentMonthToBill, 'sales_tax' => 19)); $invoice->relations = $relations; $invoice->setTimezone('UTC', TRUE); // create invoice $invoice = $this->create($invoice); $this->_autoInvoiceIterationResults[] = $invoice->getId(); $this->_autoInvoiceIterationDetailResults[] = $invoice; $paToUpdate = array(); // conjunct billables with invoice, find out which productaggregates to update foreach ($billableAccountables as $ba) { $ba['ac']->conjunctInvoiceWithBillables($invoice); if ($ba['pa']->getId()) { $paToUpdate[$ba['pa']->getId()] = $ba['pa']; } } foreach ($paToUpdate as $paId => $productAggregate) { $firstBill = !$productAggregate->last_autobill; $lab = $productAggregate->last_autobill ? clone $productAggregate->last_autobill : ($productAggregate->start_date ? clone $productAggregate->start_date : clone $this->_currentBillingContract->start_date); $lab->setTimezone(Tinebase_Core::getUserTimezone()); $lab->setDate($lab->format('Y'), $lab->format('m'), 1); $lab->setTime(0, 0, 0); if (!$firstBill) { $lab->addMonth($productAggregate->interval); } else { if ($productAggregate->billing_point == 'end') { // if first bill, add interval on billing_point end $lab->addMonth($productAggregate->interval); } } while ($this->_currentMonthToBill->isLater($lab)) { $lab->addMonth($productAggregate->interval); } if ($lab->isLater($this->_currentMonthToBill)) { $lab->subMonth($productAggregate->interval); } $productAggregate->last_autobill = $lab; $productAggregate->setTimezone('UTC'); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Updating last_autobill of "' . $productAggregate->getId() . '": ' . $lab->__toString()); } Sales_Controller_ProductAggregate::getInstance()->update($productAggregate); $productAggregate->setTimezone(Tinebase_Core::getUserTimezone()); } $doSleep = true; } elseif (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' $invoicePositions->count() == false'); } $this->_currentMonthToBill->addMonth(1); } }
/** * adds diff to date and applies dst fix * * @param Tinebase_DateTime $_dateInUTC * @param DateTimeInterval $_diff * @param string $_timezoneForDstFix */ public static function addUTCDateDstFix($_dateInUTC, $_diff, $_timezoneForDstFix) { $_dateInUTC->setTimezone($_timezoneForDstFix); $_dateInUTC->add($_dateInUTC->get('I') ? 1 : 0, Tinebase_DateTime::MODIFIER_HOUR); $_dateInUTC->add($_diff); $_dateInUTC->subHour($_dateInUTC->get('I') ? 1 : 0); $_dateInUTC->setTimezone('UTC'); }
/** * test__call(). */ public function test__call() { $now = new Tinebase_DateTime(); $now->setTimezone('Europe/Berlin'); $this->object->date_single = clone $now; $this->object->setTimezone('America/Los_Angeles'); foreach ($this->object as $record) { $this->assertNotEquals($record->date_single->get(Tinebase_Record_Abstract::ISO8601LONG), $now->get(Tinebase_Record_Abstract::ISO8601LONG)); } }
/** * appends sql to given select statement * * @param Zend_Db_Select $_select * @param Tinebase_Backend_Sql_Abstract $_backend */ function appendFilterSql($_select, $_backend) { $months = array(); $db = $_backend->getAdapter(); $date = new Tinebase_DateTime(); $format = 'Y-m'; $like = FALSE; if ($this->_operator == 'within') { switch ($this->_value) { case 'monthThis': $months = array($date->format($format)); break; case 'monthLast': $months = array($date->subMonth(1)->format($format)); break; case 'beforeLastMonth': $months = array($date->subMonth(2)->format($format)); break; case 'quarterThis': $month = ceil(intval($date->format('m')) / 3) * 3; $date->setDate($date->format('Y'), $month, 15); $months = array($date->format($format), $date->subMonth(1)->format($format), $date->subMonth(1)->format($format)); break; case 'quarterLast': $date->subMonth(3); $month = ceil(intval($date->format('m')) / 3) * 3; $date->setDate($date->format('Y'), $month, 15); $months = array($date->format($format), $date->subMonth(1)->format($format), $date->subMonth(1)->format($format)); break; case 'beforeLastQuarter': $date->subMonth(6); $month = ceil(intval($date->format('m')) / 3) * 3; $date->setDate($date->format('Y'), $month, 15); $months = array($date->format($format), $date->subMonth(1)->format($format), $date->subMonth(1)->format($format)); break; case 'yearThis': $like = $date->format('Y') . '-%'; break; case 'yearLast': $date->subYear(1); $like = $date->format('Y') . '-%'; break; default: throw new Tinebase_Exception_InvalidArgument('The value for the within operator is not supported: ' . $this->_value); } if ($like) { $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " LIKE (?)", $like)); } else { $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " IN (?)", $months)); } } elseif ($this->_operator == 'equals') { if ('' == $this->_value) { $_select->where($this->_getQuotedFieldName($_backend) . " = '0000-00-00 00:00:00' OR " . $this->_getQuotedFieldName($_backend) . ' IS NULL'); } else { $split = explode('-', $this->_value); if (!(strlen($this->_value) == 7 && (int) $split[0] > 1900 && (int) $split[1] > 0 && (int) $split[1] < 13)) { throw new Tinebase_Exception_MonthFormat(); } $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " = (?)", $this->_value)); } } else { $date = new Tinebase_DateTime($this->_value); $date->setTimezone(Tinebase_Core::getUserTimezone()); $dateString = $date->format('Y-m'); switch ($this->_operator) { case 'before': $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " < (?)", $dateString)); break; case 'after': $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " > (?)", $dateString)); break; case 'before_or_equals': $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " <= (?)", $dateString)); break; case 'after_or_equals': $_select->where($db->quoteInto($this->_getQuotedFieldName($_backend) . " >= (?)", $dateString)); break; default: throw new Tinebase_Exception_InvalidArgument('The operator ' . $this->_operator . ' is not supported for this filter!'); } } }
/** * test add contact */ public function testAddContact() { $controller = new ActiveSync_Controller_Contacts($this->objects['deviceAndroid'], new Tinebase_DateTime()); $xml = new SimpleXMLElement($this->_xmlContactBirthdayWithoutTimeAndroid); $result = $controller->createEntry($this->objects['containerWithSyncGrant']->getId(), $xml->Collections->Collection->Commands->Add->ApplicationData); $userTimezone = Tinebase_Core::get(Tinebase_Core::USERTIMEZONE); $bday = new Tinebase_DateTime('1969-12-31', $userTimezone); $bday->setTimezone('UTC'); $this->assertTrue(!empty($result)); }
/** * rebills an invoice * * @param string $id * @param string $date */ public function billContract($id, $date) { $contract = Sales_Controller_Contract::getInstance()->get($id); $date = new Tinebase_DateTime($date); $date->setTimezone(Tinebase_Core::getUserTimezone()); return Sales_Controller_Invoice::getInstance()->createAutoInvoices($date, $contract); }
/** * creates missing accounts * * * optional params: * - day=YYYY-MM-DD * - remove_unbilled=1 * - contract=CONTRACT_ID or contract=NUMBER * * @param Zend_Console_Getopt $_opts * @return boolean */ public function create_auto_invoices($_opts) { if (!Sales_Config::getInstance()->featureEnabled(Sales_Config::FEATURE_INVOICES_MODULE)) { Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' create_auto_invoices ran allthoug feature ' . Sales_Config::FEATURE_INVOICES_MODULE . ' is disabled'); return false; } $executionLifeTime = Tinebase_Core::setExecutionLifeTime(3600 * 8); $this->_addOutputLogWriter(); $freeLock = $this->_aquireMultiServerLock(__CLASS__ . '::' . __FUNCTION__); if (!$freeLock) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Job already running - ' . __CLASS__ . '::' . __FUNCTION__); } return false; } $date = NULL; $args = $this->_parseArgs($_opts, array()); // if day argument is given, validate if (array_key_exists('day', $args)) { $split = explode('-', $args['day']); if (!count($split == 3)) { // failure } else { if (strlen($split[0]) != 4 || strlen($split[1]) != 2 || strlen($split[2]) != 2) { // failure } elseif (intval($split[1]) == 0 || intval($split[2]) == 0) { // failure } else { // other errors are caught by datetime try { $date = new Tinebase_DateTime(); // use usertimezone $date->setTimezone(Tinebase_Core::getUserTimezone()); // if a date is given, set hour to 3 $date->setDate($split[0], $split[1], $split[2])->setTime(3, 0, 0); } catch (Exception $e) { Tinebase_Exception::log($e); } } } if (!$date) { die('The day must have the following format: YYYY-MM-DD!' . PHP_EOL); } } if (!$date) { $date = Tinebase_DateTime::now(); $date->setTimezone(Tinebase_Core::getUserTimezone()); } if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating invoices for ' . $date->toString()); } $contract = NULL; if (array_key_exists('contract', $args)) { try { $contract = Sales_Controller_Contract::getInstance()->get($args['contract']); } catch (Tinebase_Exception_NotFound $e) { $filter = new Sales_Model_ContractFilter(array(array('field' => 'number', 'operator' => 'equals', 'value' => $args['contract']))); $contract = Sales_Controller_Contract::getInstance()->search($filter, NULL, TRUE); if ($contract->count() == 1) { $contract = $contract->getFirstRecord(); } elseif ($contract->count() > 1) { die('The number you have given is not unique! Please use the ID instead!' . PHP_EOL); } else { die('A contract could not be found!' . PHP_EOL); } } } if (array_key_exists('remove_unbilled', $args) && $args['remove_unbilled'] == 1) { $this->removeUnbilledAutoInvoices($contract); } if (array_key_exists('check_updates', $args) && $args['check_updates'] == 1) { Sales_Controller_Invoice::getInstance()->checkForContractOrInvoiceUpdates($contract); } $result = Sales_Controller_Invoice::getInstance()->createAutoInvoices($date, $contract); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { unset($result['created']); Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' ' . print_r($result, true)); } Tinebase_Core::setExecutionLifeTime($executionLifeTime); return true; }
/** * parse VEVENT part of VCALENDAR * * @param Sabre_VObject_Component $_vevent the VEVENT to parse * @param Calendar_Model_Event $_event the Tine 2.0 event to update */ protected function _convertVevent(Sabre_VObject_Component $_vevent, Calendar_Model_Event $_event) { $event = $_event; $newAttendees = array(); // unset supported fields foreach ($this->_supportedFields as $field) { if ($field == 'alarms') { $event->{$field} = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); } else { $event->{$field} = null; } } foreach ($_vevent->children() as $property) { switch ($property->name) { case 'CREATED': case 'DTSTAMP': // do nothing break; case 'LAST-MODIFIED': $event->last_modified_time = new Tinebase_DateTime($property->value); break; case 'ATTENDEE': $newAttendees[] = $this->_getAttendee($property); break; case 'CLASS': if (in_array($property->value, array(Calendar_Model_Event::CLASS_PRIVATE, Calendar_Model_Event::CLASS_PUBLIC))) { $event->class = $property->value; } else { $event->class = Calendar_Model_Event::CLASS_PUBLIC; } break; case 'DTEND': if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') { // all day event $event->is_all_day_event = true; $dtend = $this->_convertToTinebaseDateTime($property, TRUE); // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in vcalendar $dtend->subSecond(1); } else { $event->is_all_day_event = false; $dtend = $this->_convertToTinebaseDateTime($property); } $event->dtend = $dtend; break; case 'DTSTART': if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') { // all day event $event->is_all_day_event = true; $dtstart = $this->_convertToTinebaseDateTime($property, TRUE); } else { $event->is_all_day_event = false; $dtstart = $this->_convertToTinebaseDateTime($property); } $event->originator_tz = $dtstart->getTimezone()->getName(); $event->dtstart = $dtstart; break; case 'SEQUENCE': $event->seq = $property->value; break; case 'DESCRIPTION': case 'LOCATION': case 'UID': case 'SUMMARY': $key = strtolower($property->name); $event->{$key} = $property->value; break; case 'ORGANIZER': if (preg_match('/mailto:(?P<email>.*)/i', $property->value, $matches)) { // it's not possible to change the organizer by spec if (empty($event->organizer)) { $name = isset($property['CN']) ? $property['CN']->value : $matches['email']; $contact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $matches['email'], 'lastName' => $name)); $event->organizer = $contact->getId(); } // Lightning attaches organizer ATTENDEE properties to ORGANIZER property and does not add an ATTENDEE for the organizer if (isset($property['PARTSTAT'])) { $newAttendees[] = $this->_getAttendee($property); } } break; case 'RECURRENCE-ID': // original start of the event $event->recurid = $this->_convertToTinebaseDateTime($property); // convert recurrence id to utc $event->recurid->setTimezone('UTC'); break; case 'RRULE': $event->rrule = $property->value; // convert date format $event->rrule = preg_replace_callback('/UNTIL=([\\dTZ]+)(?=;?)/', function ($matches) { if (strlen($matches[1]) < 10) { $dtUntil = date_create($matches[1], new DateTimeZone((string) Tinebase_Core::get(Tinebase_Core::USERTIMEZONE))); $dtUntil->setTimezone(new DateTimeZone('UTC')); } else { $dtUntil = date_create($matches[1]); } return 'UNTIL=' . $dtUntil->format(Tinebase_Record_Abstract::ISO8601LONG); }, $event->rrule); // remove additional days from BYMONTHDAY property $event->rrule = preg_replace('/(BYMONTHDAY=)([\\d]+)([,\\d]+)/', '$1$2', $event->rrule); // process exceptions if (isset($_vevent->EXDATE)) { $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event'); foreach ($_vevent->EXDATE as $exdate) { foreach ($exdate->getDateTimes() as $exception) { if (isset($exdate['VALUE']) && strtoupper($exdate['VALUE']) == 'DATE') { $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), (string) Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)); } else { $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), $exception->getTimezone()); } $recurid->setTimezone(new DateTimeZone('UTC')); $eventException = new Calendar_Model_Event(array('recurid' => $recurid, 'is_deleted' => true)); $exdates->addRecord($eventException); } } $event->exdate = $exdates; } break; case 'TRANSP': if (in_array($property->value, array(Calendar_Model_Event::TRANSP_OPAQUE, Calendar_Model_Event::TRANSP_TRANSP))) { $event->transp = $property->value; } else { $event->transp = Calendar_Model_Event::TRANSP_TRANSP; } break; case 'UID': // it's not possible to change the uid by spec if (!empty($event->uid)) { continue; } $event->uid = $property->value; break; case 'VALARM': foreach ($property as $valarm) { switch (strtoupper($valarm->TRIGGER['VALUE']->value)) { # TRIGGER;VALUE=DATE-TIME:20111031T130000Z case 'DATE-TIME': //@TODO fixme $alarmTime = new Tinebase_DateTime($valarm->TRIGGER->value); $alarmTime->setTimezone('UTC'); $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime, 'minutes_before' => 'custom', 'model' => 'Calendar_Model_Event')); $event->alarms->addRecord($alarm); break; # TRIGGER;VALUE=DURATION:-PT1H15M # TRIGGER;VALUE=DURATION:-PT1H15M case 'DURATION': default: $alarmTime = $this->_convertToTinebaseDateTime($_vevent->DTSTART); $alarmTime->setTimezone('UTC'); preg_match('/(?P<invert>[+-]?)(?P<spec>P.*)/', $valarm->TRIGGER->value, $matches); $duration = new DateInterval($matches['spec']); $duration->invert = !!($matches['invert'] === '-'); $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime->add($duration), 'minutes_before' => $duration->format('%d') * 60 * 24 + $duration->format('%h') * 60 + $duration->format('%i'), 'model' => 'Calendar_Model_Event')); $event->alarms->addRecord($alarm); break; } } break; case 'CATEGORIES': // @todo handle categories break; case 'X-MOZ-LASTACK': $lastAck = $this->_convertToTinebaseDateTime($property); break; case 'X-MOZ-SNOOZE-TIME': $snoozeTime = $this->_convertToTinebaseDateTime($property); break; default: break; } } // merge old and new attendees Calendar_Model_Attender::emailsToAttendee($event, $newAttendees); if (($ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee)) !== null) { if (isset($lastAck)) { $ownAttendee->alarm_ack_time = $lastAck; } if (isset($snoozeTime)) { $ownAttendee->alarm_snooze_time = $snoozeTime; } } if (empty($event->seq)) { $event->seq = 0; } if (empty($event->class)) { $event->class = Calendar_Model_Event::CLASS_PUBLIC; } // convert all datetime fields to UTC $event->setTimezone('UTC'); }
/** * convert string in user time to UTC * * @param string $_string * @return string */ protected function _convertStringToUTC($_string) { if (empty($_string)) { $date = new Tinebase_DateTime(); $result = $date->toString(Tinebase_Record_Abstract::ISO8601LONG); } elseif (isset($this->_options['timezone']) && $this->_options['timezone'] !== 'UTC') { $date = new Tinebase_DateTime($_string, $this->_options['timezone']); $date->setTimezone('UTC'); $result = $date->toString(Tinebase_Record_Abstract::ISO8601LONG); } else { $result = $_string; } return $result; }
/** * parse VEVENT part of VCALENDAR * * @param \Sabre\VObject\Component\VEvent $vevent the VEVENT to parse * @param Calendar_Model_Event $event the Tine 2.0 event to update * @param array $options */ protected function _convertVevent(\Sabre\VObject\Component\VEvent $vevent, Calendar_Model_Event $event, $options) { if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' vevent ' . $vevent->serialize()); } $newAttendees = array(); $attachments = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node'); $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); $skipFieldsIfOnlyBasicData = array('ATTENDEE', 'UID', 'ORGANIZER', 'VALARM', 'ATTACH', 'CATEGORIES'); foreach ($vevent->children() as $property) { if (isset($this->_options['onlyBasicData']) && $this->_options['onlyBasicData'] && in_array((string) $property->name, $skipFieldsIfOnlyBasicData)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Skipping ' . $property->name . ' (using option onlyBasicData)'); } continue; } switch ($property->name) { case 'CREATED': case 'DTSTAMP': if (!isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) { $event->{$property->name == 'CREATED' ? 'creation_time' : 'last_modified_time'} = $this->_convertToTinebaseDateTime($property); } break; case 'LAST-MODIFIED': $event->last_modified_time = new Tinebase_DateTime($property->getValue()); break; case 'ATTENDEE': $newAttendee = $this->_getAttendee($property); if ($newAttendee) { $newAttendees[] = $newAttendee; } break; case 'CLASS': if (in_array($property->getValue(), array(Calendar_Model_Event::CLASS_PRIVATE, Calendar_Model_Event::CLASS_PUBLIC))) { $event->class = $property->getValue(); } else { $event->class = Calendar_Model_Event::CLASS_PUBLIC; } break; case 'STATUS': if (in_array($property->getValue(), array(Calendar_Model_Event::STATUS_CONFIRMED, Calendar_Model_Event::STATUS_TENTATIVE, Calendar_Model_Event::STATUS_CANCELED))) { $event->status = $property->getValue(); } else { if ($property->getValue() == 'CANCELLED') { $event->status = Calendar_Model_Event::STATUS_CANCELED; } else { $event->status = Calendar_Model_Event::STATUS_CONFIRMED; } } break; case 'DTEND': if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') { // all day event $event->is_all_day_event = true; $dtend = $this->_convertToTinebaseDateTime($property, TRUE); // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in vcalendar $dtend->subSecond(1); } else { $event->is_all_day_event = false; $dtend = $this->_convertToTinebaseDateTime($property); } $event->dtend = $dtend; break; case 'DTSTART': if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') { // all day event $event->is_all_day_event = true; $dtstart = $this->_convertToTinebaseDateTime($property, TRUE); } else { $event->is_all_day_event = false; $dtstart = $this->_convertToTinebaseDateTime($property); } $event->originator_tz = $dtstart->getTimezone()->getName(); $event->dtstart = $dtstart; break; case 'SEQUENCE': if (!isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) { $event->seq = $property->getValue(); } // iMIP only $event->external_seq = $property->getValue(); break; case 'DESCRIPTION': case 'LOCATION': case 'SUMMARY': $key = strtolower($property->name); $value = $property->getValue(); $event->{$key} = $value; break; case 'ORGANIZER': $email = null; if (!empty($property['EMAIL'])) { $email = $property['EMAIL']; } elseif (preg_match('/mailto:(?P<email>.*)/i', $property->getValue(), $matches)) { $email = $matches['email']; } if ($email !== null) { // it's not possible to change the organizer by spec if (empty($event->organizer)) { $name = isset($property['CN']) ? $property['CN']->getValue() : $email; $contact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $email, 'lastName' => $name)); $event->organizer = $contact->getId(); } // Lightning attaches organizer ATTENDEE properties to ORGANIZER property and does not add an ATTENDEE for the organizer if (isset($property['PARTSTAT'])) { $newAttendees[] = $this->_getAttendee($property); } } break; case 'RECURRENCE-ID': // original start of the event $event->recurid = $this->_convertToTinebaseDateTime($property); // convert recurrence id to utc $event->recurid->setTimezone('UTC'); break; case 'RRULE': $rruleString = $property->getValue(); // convert date format $rruleString = preg_replace_callback('/UNTIL=([\\dTZ]+)(?=;?)/', function ($matches) { $dtUntil = Calendar_Convert_Event_VCalendar_Abstract::getUTCDateFromStringInUsertime($matches[1]); return 'UNTIL=' . $dtUntil->format(Tinebase_Record_Abstract::ISO8601LONG); }, $rruleString); // remove additional days from BYMONTHDAY property (BYMONTHDAY=11,15 => BYMONTHDAY=11) $rruleString = preg_replace('/(BYMONTHDAY=)([\\d]+),([,\\d]+)/', '$1$2', $rruleString); $event->rrule = $rruleString; // process exceptions if (isset($vevent->EXDATE)) { $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event'); foreach ($vevent->EXDATE as $exdate) { foreach ($exdate->getDateTimes() as $exception) { if (isset($exdate['VALUE']) && strtoupper($exdate['VALUE']) == 'DATE') { $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), (string) Tinebase_Core::getUserTimezone()); } else { $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), $exception->getTimezone()); } $recurid->setTimezone(new DateTimeZone('UTC')); $eventException = new Calendar_Model_Event(array('recurid' => $recurid, 'is_deleted' => true)); $exdates->addRecord($eventException); } } $event->exdate = $exdates; } break; case 'TRANSP': if (in_array($property->getValue(), array(Calendar_Model_Event::TRANSP_OPAQUE, Calendar_Model_Event::TRANSP_TRANSP))) { $event->transp = $property->getValue(); } else { $event->transp = Calendar_Model_Event::TRANSP_TRANSP; } break; case 'UID': // it's not possible to change the uid by spec if (!empty($event->uid)) { continue; } $event->uid = $property->getValue(); break; case 'VALARM': $this->_parseAlarm($event, $property, $vevent); break; case 'CATEGORIES': $tags = Tinebase_Model_Tag::resolveTagNameToTag($property->getParts(), 'Calendar'); if (!isset($event->tags)) { $event->tags = $tags; } else { $event->tags->merge($tags); } break; case 'ATTACH': $name = (string) $property['FILENAME']; $managedId = (string) $property['MANAGED-ID']; $value = (string) $property['VALUE']; $attachment = NULL; $readFromURL = false; $url = ''; if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' attachment found: ' . $name . ' ' . $managedId); } if ($managedId) { $attachment = $event->attachments instanceof Tinebase_Record_RecordSet ? $event->attachments->filter('hash', $property['MANAGED-ID'])->getFirstRecord() : NULL; // NOTE: we might miss a attachment here for the following reasons // 1. client reuses a managed id (we are server): // We havn't observerd this yet. iCal client reuse manged id's // from base events in exceptions but this is covered as we // initialize new exceptions with base event attachments // // When a client reuses a managed id it's not clear yet if // this managed id needs to be in the same series/calendar/server // // As we use the object hash the managed id might be used in the // same files with different names. We need to evaluate the name // (if attached) in this case as well. // // 2. server send his managed id (we are client) // * we need to download the attachment (here?) // * we need to have a mapping externalid / internalid (where?) if (!$attachment) { $readFromURL = true; $url = $property->getValue(); } else { $attachments->addRecord($attachment); } } elseif ('URI' === $value) { /* * ATTACH;VALUE=URI:https://server.com/calendars/__uids__/0AA0 3A3B-F7B6-459A-AB3E-4726E53637D0/dropbox/4971F93F-8657-412B-841A-A0FD913 9CD61.dropbox/Canada.png */ $url = $property->getValue(); $urlParts = parse_url($url); $host = $urlParts['host']; $name = pathinfo($urlParts['path'], PATHINFO_BASENAME); // iCal 10.7 places URI before uploading if (parse_url(Tinebase_Core::getHostname(), PHP_URL_HOST) != $host) { $readFromURL = true; } } else { // @TODO: implement (check if add / update / update is needed) if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' attachment found that could not be imported due to missing managed id'); } } if ($readFromURL) { if (preg_match('#^(https?://)(.*)$#', str_replace(array("\n", "\r"), '', $url), $matches)) { // we are client and found an external hosted attachment that we need to import $userCredentialCache = Tinebase_Core::getUserCredentialCache(); $url = $matches[1] . $userCredentialCache->username . ':' . $userCredentialCache->password . '@' . $matches[2]; $attachmentInfo = $matches[1] . $matches[2] . ' ' . $name . ' ' . $managedId; if (Tinebase_Helper::urlExists($url)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Downloading attachment: ' . $attachmentInfo); } $stream = fopen($url, 'r'); $attachment = new Tinebase_Model_Tree_Node(array('name' => rawurldecode($name), 'type' => Tinebase_Model_Tree_Node::TYPE_FILE, 'contenttype' => (string) $property['FMTTYPE'], 'tempFile' => $stream), true); $attachments->addRecord($attachment); } else { if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Url not found (got 404): ' . $attachmentInfo . ' - Skipping attachment'); } } } else { if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Attachment found with malformed url: ' . $url); } } } break; case 'X-MOZ-LASTACK': $lastAck = $this->_convertToTinebaseDateTime($property); break; case 'X-MOZ-SNOOZE-TIME': $snoozeTime = $this->_convertToTinebaseDateTime($property); break; default: // thunderbird saves snooze time for recurring event occurrences in properties with names like this - // we just assume that the event/recur series has only one snooze time if (preg_match('/^X-MOZ-SNOOZE-TIME-[0-9]+$/', $property->name)) { $snoozeTime = $this->_convertToTinebaseDateTime($property); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Found snooze time for recur occurrence: ' . $snoozeTime->toString()); } } break; } } // NOTE: X-CALENDARSERVER-ACCESS overwrites CLASS if (isset($vevent->{'X-CALENDARSERVER-ACCESS'})) { $event->class = $vevent->{'X-CALENDARSERVER-ACCESS'} == 'PUBLIC' ? Calendar_Model_Event::CLASS_PUBLIC : Calendar_Model_Event::CLASS_PRIVATE; } if (isset($lastAck)) { Calendar_Controller_Alarm::setAcknowledgeTime($event->alarms, $lastAck); } if (isset($snoozeTime)) { Calendar_Controller_Alarm::setSnoozeTime($event->alarms, $snoozeTime); } // merge old and new attendee Calendar_Model_Attender::emailsToAttendee($event, $newAttendees); if (empty($event->seq)) { $event->seq = 1; } if (empty($event->class)) { $event->class = Calendar_Model_Event::CLASS_PUBLIC; } $this->_manageAttachmentsFromClient($event, $attachments); if (empty($event->dtend)) { if (empty($event->dtstart)) { throw new Tinebase_Exception_UnexpectedValue("Got event without dtstart and dtend"); } // TODO find out duration (see TRIGGER DURATION) // if (isset($vevent->DURATION)) { // } if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Got event without dtend. Assuming 30 minutes duration'); } $event->dtend = clone $event->dtstart; $event->dtend->addMinute(30); } $this->_manageAttachmentsFromClient($event, $attachments); // convert all datetime fields to UTC $event->setTimezone('UTC'); }