/** * Lets update a record tree times * * @access protected */ protected function setUp() { $now = new Tinebase_DateTime(); $this->_modLogClass = Tinebase_Timemachine_ModificationLog::getInstance(); $this->_persistantLogEntries = new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog'); $this->_recordIds = array('5dea69be9c72ea3d263613277c3b02d529fbd8bc'); $tinebaseApp = Tinebase_Application::getInstance()->getApplicationByName('Tinebase'); $this->_logEntries = new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog', array(array('application_id' => $tinebaseApp, 'record_id' => $this->_recordIds[0], 'record_type' => 'TestType', 'record_backend' => 'TestBackend', 'modification_time' => $this->Cloner($now)->addDay(-2), 'modification_account' => 7, 'modified_attribute' => 'FirstTestAttribute', 'old_value' => 'Hamburg', 'new_value' => 'Bremen'), array('application_id' => $tinebaseApp, 'record_id' => $this->_recordIds[0], 'record_type' => 'TestType', 'record_backend' => 'TestBackend', 'modification_time' => $this->Cloner($now)->addDay(-1), 'modification_account' => 7, 'modified_attribute' => 'FirstTestAttribute', 'old_value' => 'Bremen', 'new_value' => 'Frankfurt'), array('application_id' => $tinebaseApp, 'record_id' => $this->_recordIds[0], 'record_type' => 'TestType', 'record_backend' => 'TestBackend', 'modification_time' => $this->Cloner($now), 'modification_account' => 7, 'modified_attribute' => 'FirstTestAttribute', 'old_value' => 'Frankfurt', 'new_value' => 'Stuttgart'), array('application_id' => $tinebaseApp, 'record_id' => $this->_recordIds[0], 'record_type' => 'TestType', 'record_backend' => 'TestBackend', 'modification_time' => $this->Cloner($now)->addDay(-2), 'modification_account' => 7, 'modified_attribute' => 'SecondTestAttribute', 'old_value' => 'Deutschland', 'new_value' => 'Östereich'), array('application_id' => $tinebaseApp, 'record_id' => $this->_recordIds[0], 'record_type' => 'TestType', 'record_backend' => 'TestBackend', 'modification_time' => $this->Cloner($now)->addDay(-1)->addSecond(1), 'modification_account' => 7, 'modified_attribute' => 'SecondTestAttribute', 'old_value' => 'Östereich', 'new_value' => 'Schweitz'), array('application_id' => $tinebaseApp->getId(), 'record_id' => $this->_recordIds[0], 'record_type' => 'TestType', 'record_backend' => 'TestBackend', 'modification_time' => $this->Cloner($now), 'modification_account' => 7, 'modified_attribute' => 'SecondTestAttribute', 'old_value' => 'Schweitz', 'new_value' => 'Italien')), true, false); foreach ($this->_logEntries as $logEntry) { $id = $this->_modLogClass->setModification($logEntry); $this->_persistantLogEntries->addRecord($this->_modLogClass->getModification($id)); } }
/** * testRelatedModlog * * @see 0000996: add changes in relations/linked objects to modlog/history */ public function testRelatedModlog() { // create lead with tag, customfield and related contacts $savedLead = $this->_saveLead(); // change relations, customfields + tags $savedLead['tags'][] = array('name' => 'another tag', 'type' => Tinebase_Model_Tag::TYPE_PERSONAL); foreach ($savedLead['relations'] as $key => $value) { if ($value['type'] == 'PARTNER') { $savedLead['relations'][$key]['type'] = 'CUSTOMER'; } if ($value['type'] == 'TASK') { unset($savedLead['relations'][$key]); } } $savedLead['customfields'][$this->_cfcName] = '5678'; $updatedLead = $this->_instance->saveLead($savedLead); // check modlog + history $modifications = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications('Crm', $updatedLead['id']); //print_r($updatedLead); $this->assertEquals(3, count($modifications), 'expected 3 modifications: ' . print_r($modifications->toArray(), TRUE)); foreach ($modifications as $modification) { switch ($modification->modified_attribute) { case 'customfields': $this->assertEquals('{"' . $this->_cfcName . '":"5678"}', $modification->new_value); break; case 'relations': $diff = new Tinebase_Record_RecordSetDiff(Zend_Json::decode($modification->new_value)); $this->assertEquals(0, count($diff->added)); $this->assertEquals(1, count($diff->removed)); $this->assertEquals(1, count($diff->modified), 'relations modified mismatch: ' . print_r($diff->toArray(), TRUE)); $this->assertTrue(isset($diff->modified[0]['diff']['type'])); $this->assertEquals('CUSTOMER', $diff->modified[0]['diff']['type'], 'type diff is not correct: ' . print_r($diff->toArray(), TRUE)); break; case 'tags': $diff = new Tinebase_Record_RecordSetDiff(Zend_Json::decode($modification->new_value)); $this->assertEquals(1, count($diff->added)); $this->assertEquals(0, count($diff->removed)); $this->assertEquals(0, count($diff->modified), 'tags modified mismatch: ' . print_r($diff->toArray(), TRUE)); break; default: $this->fail('Invalid modification: ' . print_r($modification->toArray(), TRUE)); } } }
/** * undo changes to records defined by certain criteria (user, date, fields, ...) * * example: $ php tine20.php --username pschuele --method Tinebase.undo -d * -- record_type=Addressbook_Model_Contact modification_time=2013-05-08 modification_account=3263 * * @param Zend_Console_Getopt $opts */ public function undo(Zend_Console_Getopt $opts) { if (!$this->_checkAdminRight()) { return FALSE; } $data = $this->_parseArgs($opts, array('modification_time')); // build filter from params $filterData = array(); $allowedFilters = array('record_type', 'modification_time', 'modification_account', 'record_id', 'modified_attribute'); foreach ($data as $key => $value) { if (in_array($key, $allowedFilters)) { $operator = $key === 'modification_time' ? 'within' : 'equals'; $filterData[] = array('field' => $key, 'operator' => $operator, 'value' => $value); } } $filter = new Tinebase_Model_ModificationLogFilter($filterData); $dryrun = $opts->d; $overwrite = isset($data['overwrite']) && $data['overwrite'] ? TRUE : FALSE; $result = Tinebase_Timemachine_ModificationLog::getInstance()->undo($filter, $overwrite, $dryrun); if (!$dryrun) { echo 'Reverted ' . $result['totalcount'] . " change(s)\n"; } else { echo "Dry run\n"; echo 'Would revert ' . $result['totalcount'] . " change(s):\n"; foreach ($result['undoneModlogs'] as $modlog) { echo 'id ' . $modlog->record_id . ' [' . $modlog->modified_attribute . ']: ' . $modlog->new_value . ' -> ' . $modlog->old_value . "\n"; } } echo 'Failcount: ' . $result['failcount'] . "\n"; return 0; }
/** * update node * * @param Tinebase_Model_Tree_Node $_node * @return Tinebase_Model_Tree_Node */ public function updateNode(Tinebase_Model_Tree_Node $_node) { $currentNodeObject = $this->_treeNodeBackend->get($_node->getId()); $modLog = Tinebase_Timemachine_ModificationLog::getInstance(); $modLog->setRecordMetaData($_node, 'update', $currentNodeObject); return $this->_treeNodeBackend->update($_node); }
/** * update file object with hash file info * * @param string $_id * @param string $_hash * @param string $_hashFile * @return Tinebase_Model_Tree_FileObject */ protected function _updateFileObject($_id, $_hash, $_hashFile = null) { $currentFileObject = $_id instanceof Tinebase_Record_Abstract ? $_id : $this->_fileObjectBackend->get($_id); $_hashFile = $_hashFile ?: $this->_basePath . '/' . substr($_hash, 0, 3) . '/' . substr($_hash, 3); $updatedFileObject = clone $currentFileObject; $updatedFileObject->hash = $_hash; $updatedFileObject->size = filesize($_hashFile); if (version_compare(PHP_VERSION, '5.3.0', '>=') && function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $_hashFile); if ($mimeType !== false) { $updatedFileObject->contenttype = $mimeType; } finfo_close($finfo); } else { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' finfo_open() is not available: Could not get file information.'); } } $modLog = Tinebase_Timemachine_ModificationLog::getInstance(); $modLog->setRecordMetaData($updatedFileObject, 'update', $currentFileObject); // quick hack for 2014.11 - will be resolved correctly in 2015.11-develop if (isset($_SERVER['HTTP_X_OC_MTIME'])) { $updatedFileObject->last_modified_time = new Tinebase_DateTime($_SERVER['HTTP_X_OC_MTIME']); Tinebase_Server_WebDAV::getResponse()->setHeader('X-OC-MTime', 'accepted'); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " using X-OC-MTIME: {$updatedFileObject->last_modified_time->format(Tinebase_Record_Abstract::ISO8601LONG)} for {$updatedFileObject->id}"); } } // sanitize file size, somehow filesize() seems to return empty strings on some systems if (empty($updatedFileObject->size)) { $updatedFileObject->size = 0; } return $this->_fileObjectBackend->update($updatedFileObject); }
/** * uninstall app * * @param Tinebase_Model_Application $_application * @throws Setup_Exception */ protected function _uninstallApplication(Tinebase_Model_Application $_application, $uninstallAll = false) { if ($this->_backend === null) { throw new Setup_Exception('No setup backend available'); } Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Uninstall ' . $_application); try { $applicationTables = Tinebase_Application::getInstance()->getApplicationTables($_application); } catch (Zend_Db_Statement_Exception $zdse) { Setup_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . " " . $zdse); throw new Setup_Exception('Could not uninstall ' . $_application . ' (you might need to remove the tables by yourself): ' . $zdse->getMessage()); } $disabledFK = FALSE; $db = Tinebase_Core::getDb(); do { $oldCount = count($applicationTables); if ($_application->name == 'Tinebase') { $installedApplications = Tinebase_Application::getInstance()->getApplications(NULL, 'id'); if (count($installedApplications) !== 1) { throw new Setup_Exception_Dependency('Failed to uninstall application "Tinebase" because of dependencies to other installed applications.'); } } foreach ($applicationTables as $key => $table) { Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Remove table: {$table}"); try { // drop foreign keys which point to current table first $foreignKeys = $this->_backend->getExistingForeignKeys($table); foreach ($foreignKeys as $foreignKey) { Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Drop index: " . $foreignKey['table_name'] . ' => ' . $foreignKey['constraint_name']); $this->_backend->dropForeignKey($foreignKey['table_name'], $foreignKey['constraint_name']); } // drop table $this->_backend->dropTable($table); if ($_application->name != 'Tinebase') { Tinebase_Application::getInstance()->removeApplicationTable($_application, $table); } unset($applicationTables[$key]); } catch (Zend_Db_Statement_Exception $e) { // we need to catch exceptions here, as we don't want to break here, as a table // might still have some foreign keys // this works with mysql only $message = $e->getMessage(); Setup_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " Could not drop table {$table} - " . $message); // remove app table if table not found in db if (preg_match('/SQLSTATE\\[42S02\\]: Base table or view not found/', $message) && $_application->name != 'Tinebase') { Tinebase_Application::getInstance()->removeApplicationTable($_application, $table); unset($applicationTables[$key]); } else { Setup_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . " Disabling foreign key checks ... "); if ($db instanceof Zend_Db_Adapter_Pdo_Mysql) { $db->query("SET FOREIGN_KEY_CHECKS=0"); } $disabledFK = TRUE; } } } if ($oldCount > 0 && count($applicationTables) == $oldCount) { throw new Setup_Exception('dead lock detected oldCount: ' . $oldCount); } } while (count($applicationTables) > 0); if ($disabledFK) { if ($db instanceof Zend_Db_Adapter_Pdo_Mysql) { Setup_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . " Enabling foreign key checks again... "); $db->query("SET FOREIGN_KEY_CHECKS=1"); } } if ($_application->name != 'Tinebase') { if (!$uninstallAll) { Tinebase_Relations::getInstance()->removeApplication($_application->name); Tinebase_Timemachine_ModificationLog::getInstance()->removeApplication($_application); // delete containers, config options and other data for app Tinebase_Application::getInstance()->removeApplicationData($_application); } // remove application from table of installed applications Tinebase_Application::getInstance()->deleteApplication($_application); } Setup_Uninitialize::uninitialize($_application); Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Removed app: " . $_application->name); }
/** * update modlog / metadata / add systemnote for multiple records defined by filter * * NOTE: this should be done in a transaction because of the concurrency handling as * we want the same seq in the record and in the modlog * * @param Tinebase_Model_Filter_FilterGroup|array $_filterOrIds * @param array $_oldData * @param array $_newData */ public function concurrencyManagementAndModlogMultiple($_filterOrIds, $_oldData, $_newData) { $ids = $_filterOrIds instanceof Tinebase_Model_Filter_FilterGroup ? $this->search($_filterOrIds, NULL, FALSE, TRUE, 'update') : $_filterOrIds; if (!is_array($ids) || count($ids) === 0) { return; } if ($this->_omitModLog !== TRUE) { $recordSeqs = $this->_backend->getPropertyByIds($ids, 'seq'); list($currentAccountId, $currentTime) = Tinebase_Timemachine_ModificationLog::getCurrentAccountIdAndTime(); $updateMetaData = array('last_modified_by' => $currentAccountId, 'last_modified_time' => $currentTime, 'seq' => new Zend_Db_Expr('seq + 1'), 'recordSeqs' => $recordSeqs); } else { $updateMetaData = array(); } $this->_backend->updateMultiple($ids, $updateMetaData); if ($this->_omitModLog !== TRUE && is_object(Tinebase_Core::getUser())) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Writing modlog for ' . count($ids) . ' records ...'); } $currentMods = Tinebase_Timemachine_ModificationLog::getInstance()->writeModLogMultiple($ids, $_oldData, $_newData, $this->_modelName, $this->_getBackendType(), $updateMetaData); Tinebase_Notes::getInstance()->addMultipleModificationSystemNotes($currentMods, $currentAccountId, $this->_modelName); } }
/** * set modified timestamp for container * * @param int|Tinebase_Model_Container $container * @param string $action one of {create|update|delete} * @return Tinebase_Model_Container */ protected function _setRecordMetaDataAndUpdate($container, $action) { if (!$container instanceof Tinebase_Model_Container) { $container = $this->getContainerById($container); } Tinebase_Timemachine_ModificationLog::getInstance()->setRecordMetaData($container, $action, $container); $this->_clearCache($container); return $this->update($container); }
/** * updates an attender * * @param Calendar_Model_Attender $attender * @param Calendar_Model_Attender $currentAttender * @param Calendar_Model_Event $event * @param bool $isRescheduled event got rescheduled reset all attendee status * @param Tinebase_Model_Container $calendar */ protected function _updateAttender($attender, $currentAttender, $event, $isRescheduled, $calendar = NULL) { $userAccountId = $currentAttender->getUserAccountId(); // update display calendar if attender has/is a useraccount if ($userAccountId) { if ($calendar->type == Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $calendar, Tinebase_Model_Grants::GRANT_ADMIN)) { // if attender has admin grant to personal physical container, this phys. cal also gets displ. cal $attender->displaycontainer_id = $calendar->getId(); } else { if ($userAccountId == Tinebase_Core::getUser()->getId() && Tinebase_Container::getInstance()->hasGrant($userAccountId, $attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN)) { // allow user to set his own displ. cal $attender->displaycontainer_id = $attender->displaycontainer_id; } else { $attender->displaycontainer_id = $currentAttender->displaycontainer_id; } } } // reset status if user has no right and authkey is wrong if ($attender->displaycontainer_id) { if (!Tinebase_Core::getUser()->hasGrant($attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_EDIT) && $attender->status_authkey != $currentAttender->status_authkey) { if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Wrong authkey, resetting status (' . $attender->status . ' -> ' . $currentAttender->status . ')'); } $attender->status = $currentAttender->status; } } // reset all status but calUser on reschedule except resources (Resources might have a configured default value) if ($isRescheduled && !$attender->isSame($this->getCalendarUser())) { if ($attender->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE) { //If resource has a default status reset to this $resource = Calendar_Controller_Resource::getInstance()->get($attender->user_id); $attender->status = isset($resource->status) ? $resource->status : Calendar_Model_Attender::STATUS_NEEDSACTION; } else { $attender->status = Calendar_Model_Attender::STATUS_NEEDSACTION; } $attender->transp = null; } // preserve old authkey $attender->status_authkey = $currentAttender->status_authkey; if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " Updating attender: " . print_r($attender->toArray(), TRUE)); } Tinebase_Timemachine_ModificationLog::getInstance()->setRecordMetaData($attender, 'update', $currentAttender); Tinebase_Timemachine_ModificationLog::getInstance()->writeModLog($attender, $currentAttender, get_class($attender), $this->_getBackendType(), $attender->getId()); $this->_backend->updateAttendee($attender); if ($attender->displaycontainer_id !== $currentAttender->displaycontainer_id) { $this->_increaseDisplayContainerContentSequence($currentAttender, $event, Tinebase_Model_ContainerContent::ACTION_DELETE); $this->_increaseDisplayContainerContentSequence($attender, $event, Tinebase_Model_ContainerContent::ACTION_CREATE); } else { $this->_increaseDisplayContainerContentSequence($attender, $event); } }
/** * Returns instance of Tinebase_Timemachine_ModificationLog * (only create instace on demand) * * @return Tinebase_Timemachine_ModificationLog */ protected function getModificationLog() { if (!$this->_modificationLog) { $this->_modificationLog = Tinebase_Timemachine_ModificationLog::getInstance(); } return $this->_modificationLog; }
/** * testConcurrencyLinebreakMismatch * * @see 0007140: normalize linebreaks in concurrency check */ public function testConcurrencyLinebreakMismatch() { $utask = $this->_persistantTestTask1; $utask->description = 'description' . "\n"; $utask = $this->_controller->update($utask); // change linebreak in db to \r\n $loggedMods = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications('Tasks', $utask->getId(), 'Tasks_Model_Task', 'Sql', Tinebase_DateTime::now()->subMinute(5), $utask->last_modified_time); $this->assertEquals(1, count($loggedMods)); $mod = $loggedMods[0]->toArray(); $this->assertEquals('description', $mod['modified_attribute']); $mod['new_value'] = 'description' . "\r\n"; $modlog = new Tinebase_Db_Table(array('name' => SQL_TABLE_PREFIX . 'timemachine_modlog')); $modlog->update($mod, Tinebase_Core::getDb()->quoteInto('id = ?', $mod['id'])); // this should still work as we normalize linebreaks in concurrency check $resolvableConcurrencyTask = clone $utask; $resolvableConcurrencyTask->last_modified_time = Tinebase_DateTime::now()->addHour(-1); $resolvableConcurrencyTask->description = 'description' . "\n"; $task = $this->_controller->update($resolvableConcurrencyTask); $this->assertEquals('description' . "\n", $task->description); }
/** * testConcurrentAttendeeChangeUpdate * * @see 0008078: concurrent attendee change should be merged */ public function testConcurrentAttendeeChangeUpdate() { $eventData = $this->testCreateEvent(); $currentAttendee = $eventData['attendee']; $adminIndex = $eventData['attendee'][0]['user_id']['n_fn'] === 'Susan Clever' ? 1 : 0; $eventData['attendee'][$adminIndex]['status'] = Calendar_Model_Attender::STATUS_TENTATIVE; $event = $this->_uit->saveEvent($eventData); $loggedMods = Tinebase_Timemachine_ModificationLog::getInstance()->getModificationsBySeq(Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(), new Calendar_Model_Attender($eventData['attendee'][$adminIndex]), 2); $this->assertEquals(1, count($loggedMods), 'attender modification has not been logged'); $eventData['attendee'] = $currentAttendee; $scleverIndex = $adminIndex === 1 ? 0 : 1; $attendeeBackend = new Calendar_Backend_Sql_Attendee(); $eventData['attendee'][$scleverIndex]['status_authkey'] = $attendeeBackend->get($eventData['attendee'][$scleverIndex]['id'])->status_authkey; $eventData['attendee'][$scleverIndex]['status'] = Calendar_Model_Attender::STATUS_TENTATIVE; $event = $this->_uit->saveEvent($eventData); foreach ($event['attendee'] as $attender) { $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $attender['status'], 'both attendee status should be TENTATIVE: ' . print_r($attender, TRUE)); } }
/** * add new note * * @param Tinebase_Model_Note $_note */ public function addNote(Tinebase_Model_Note $_note) { if (!$_note->getId()) { $id = $_note->generateUID(); $_note->setId($id); } Tinebase_Timemachine_ModificationLog::getInstance()->setRecordMetaData($_note, 'create'); $data = $_note->toArray(FALSE, FALSE); $this->_notesTable->insert($data); }
/** * get email addresses this attendee had in the past * * @return array */ public function getEmailsFromHistory() { $emails = array(); $typeMap = array(self::USERTYPE_USER => 'Addressbook_Model_Contact', self::USERTYPE_GROUPMEMBER => 'Addressbook_Model_Contact', self::USERTYPE_RESOURCE => 'Calendar_Model_Resource'); if (isset($typeMap[$this->user_type])) { $type = $typeMap[$this->user_type]; $id = $this->user_id instanceof Tinebase_Record_Abstract ? $this->user_id->getId() : $this->user_id; $modifications = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications(Tinebase_Helper::array_value(0, explode('_', $type)), $this->user_id instanceof Tinebase_Record_Abstract ? $this->user_id->getId() : $this->user_id, $type, 'Sql', $this->creation_time); foreach ($modifications as $modification) { if (in_array($modification->modified_attribute, array('email', 'email_home'))) { if ($modification->old_value) { $emails[] = $modification->old_value; } } } } return $emails; }
/** * add new note * * @param Tinebase_Model_Note $_note */ public function addNote(Tinebase_Model_Note $_note) { if (!$_note->getId()) { $id = $_note->generateUID(); $_note->setId($id); } Tinebase_Timemachine_ModificationLog::getInstance()->setRecordMetaData($_note, 'create'); $data = $_note->toArray(FALSE, FALSE); //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($data, TRUE)); $this->_notesTable->insert($data); }
/** * update file object with hash file info * * @param string $_id * @param string $_hash * @param string $_hashFile * @return Tinebase_Model_Tree_FileObject */ protected function _updateFileObject($_id, $_hash, $_hashFile = null) { $currentFileObject = $_id instanceof Tinebase_Record_Abstract ? $_id : $this->_fileObjectBackend->get($_id); $_hashFile = $_hashFile ?: $this->_basePath . '/' . substr($_hash, 0, 3) . '/' . substr($_hash, 3); $updatedFileObject = clone $currentFileObject; $updatedFileObject->hash = $_hash; $updatedFileObject->size = filesize($_hashFile); if (version_compare(PHP_VERSION, '5.3.0', '>=') && function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $_hashFile); if ($mimeType !== false) { $updatedFileObject->contenttype = $mimeType; } finfo_close($finfo); } else { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' finfo_open() is not available: Could not get file information.'); } } $modLog = Tinebase_Timemachine_ModificationLog::getInstance(); $modLog->setRecordMetaData($updatedFileObject, 'update', $currentFileObject); // sanitize file size, somehow filesize() seems to return empty strings on some systems if (empty($updatedFileObject->size)) { $updatedFileObject->size = 0; } return $this->_fileObjectBackend->update($updatedFileObject); }