/** * updates dependent records on update the parent record * * @param Tinebase_Record_Interface $_record * @param Tinebase_Record_Interface $_oldRecord * @param string $_property * @param array $_fieldConfig */ protected function _updateDependentRecords(Tinebase_Record_Interface $_record, Tinebase_Record_Interface $_oldRecord, $_property, $_fieldConfig) { if (!(isset($_fieldConfig['dependentRecords']) || array_key_exists('dependentRecords', $_fieldConfig)) || !$_fieldConfig['dependentRecords']) { return; } if (!isset($_fieldConfig['refIdField'])) { throw new Tinebase_Exception_Record_DefinitionFailure('If a record is dependent, a refIdField has to be defined!'); } // don't handle dependent records on property if it is set to null or doesn't exist. if ($_record->{$_property} === NULL || !$_record->has($_property)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Skip updating dependent records (got NULL) on property ' . $_property . ' for ' . $this->_applicationName . ' ' . $this->_modelName . ' with id = "' . $_record->getId() . '"'); } return; } $ccn = $_fieldConfig['controllerClassName']; $controller = $ccn::getInstance(); $recordClassName = $_fieldConfig['recordClassName']; $filterClassName = $_fieldConfig['filterClassName']; $existing = new Tinebase_Record_RecordSet($recordClassName); if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_record->{$_property}, TRUE)); } if (!empty($_record->{$_property}) && $_record->{$_property}) { // legacy - should be already done in frontend json - remove if all record properties are record sets before getting to controller if (is_array($_record->{$_property})) { $rs = new Tinebase_Record_RecordSet($recordClassName); foreach ($_record->{$_property} as $recordArray) { $rec = new $recordClassName(array(), true); $rec->setFromJsonInUsersTimezone($recordArray); $rs->addRecord($rec); } $_record->{$_property} = $rs; } $idProperty = $_record->{$_property}->getFirstRecord()->getIdProperty(); // legacy end $oldFilter = new $filterClassName(array(array('field' => $idProperty, 'operator' => 'in', 'value' => $_record->{$_property}->getId()))); $oldRecords = $controller->search($oldFilter); foreach ($_record->{$_property} as $record) { $record->{$_fieldConfig['refIdField']} = $_record->getId(); // update record if ID exists and has a length of 40 (it has a length of 10 if it is a timestamp) if ($record->getId() && strlen($record->getId()) == 40) { // do not try to update if the record hasn't changed $oldRecord = $oldRecords->getById($record->getId()); if ($oldRecord && !empty($oldRecord->diff($record)->diff)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Updating dependent record with id = "' . $record->getId() . '" on property ' . $_property . ' for ' . $this->_applicationName . ' ' . $this->_modelName); } $existing->addRecord($controller->update($record)); } else { $existing->addRecord($record); } // create if is not existing already } else { // try to find if it already exists (with corrupted id) if ($record->getId() == NULL) { $crc = $controller->create($record); $existing->addRecord($crc); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating dependent record with id = "' . $crc->getId() . '" on property ' . $_property . ' for ' . $this->_applicationName . ' ' . $this->_modelName); } } else { try { $prevRecord = $controller->get($record->getId()); if (!empty($prevRecord->diff($record)->diff)) { $existing->addRecord($controller->update($record)); } else { $existing->addRecord($record); } } catch (Tinebase_Exception_NotFound $e) { $record->id = NULL; $crc = $controller->create($record); $existing->addRecord($crc); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating dependent record with id = "' . $crc->getId() . '" on property ' . $_property . ' for ' . $this->_applicationName . ' ' . $this->_modelName); } } } } } } $filter = new $filterClassName(isset($_fieldConfig['addFilters']) ? $_fieldConfig['addFilters'] : array(), 'AND'); $filter->addFilter(new Tinebase_Model_Filter_Text($_fieldConfig['refIdField'], 'equals', $_record->getId())); // an empty array will remove all records on this property if (!empty($_record->{$_property})) { $filter->addFilter(new Tinebase_Model_Filter_Id('id', 'notin', $existing->getId())); } $deleteIds = $controller->search($filter, NULL, FALSE, TRUE); if (!empty($deleteIds)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Deleting dependent records with id = "' . print_r($deleteIds, 1) . '" on property ' . $_property . ' for ' . $this->_applicationName . ' ' . $this->_modelName); } $controller->delete($deleteIds); } $_record->{$_property} = $existing->toArray(); }
/** * delete linked objects (notes, relations, ...) of record * * @param Tinebase_Record_Interface $_record */ protected function _deleteLinkedObjects(Tinebase_Record_Interface $_record) { // use textfilter for employee_id $eFilter = new Tinebase_Model_Filter_Text(array('field' => 'employee_id', 'operator' => 'equals', 'value' => $_record->getId())); // delete free times $filter = new HumanResources_Model_FreeTimeFilter(array(), 'AND'); $filter->addFilter($eFilter); HumanResources_Controller_FreeTime::getInstance()->deleteByFilter($filter); // delete contracts $filter = new HumanResources_Model_ContractFilter(array(), 'AND'); $filter->addFilter($eFilter); HumanResources_Controller_Contract::getInstance()->deleteByFilter($filter); // delete costcenters if ($_record->has('costcenters')) { $filter = new HumanResources_Model_CostCenterFilter(array(), 'AND'); $filter->addFilter($eFilter); HumanResources_Controller_CostCenter::getInstance()->deleteByFilter($filter); } // delete accounts $filter = new HumanResources_Model_AccountFilter(array()); $filter->addFilter(new Tinebase_Model_Filter_Text(array('field' => 'employee_id', 'operator' => 'equals', 'value' => $_record->getId()))); HumanResources_Controller_Account::getInstance()->deleteByFilter($filter); parent::_deleteLinkedObjects($_record); }
/** * update one phone * * @param Voipmanager_Model_Snom_Phone $_phone * @return Voipmanager_Model_Snom_Phone * @throws Voipmanager_Exception_Validation * * @todo do not overwrite update() -> use inspectBefore/After functions */ public function update(Tinebase_Record_Interface $_phone) { // check first if mac address is already used if ($_phone->has('macaddress')) { try { $phoneWithMac = $this->getByMacAddress($_phone->macaddress); if ($phoneWithMac->getId() !== $_phone->getId()) { throw new Voipmanager_Exception_Validation('A phone with this mac address already exists.'); } } catch (Voipmanager_Exception_NotFound $venf) { // everything ok } } try { $db = $this->_backend->getAdapter(); $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction($db); $phone = $this->_backend->update($_phone); $_phoneSettings = $_phone->settings; if ($_phoneSettings instanceof Voipmanager_Model_Snom_PhoneSettings) { // force the right phone_id $_phoneSettings->setId($phone->getId()); // set all settings which are equal to the default settings to NULL $template = Voipmanager_Controller_Snom_Template::getInstance()->get($phone->template_id); $settingDefaults = Voipmanager_Controller_Snom_Setting::getInstance()->get($template->setting_id); foreach ($_phoneSettings->toArray() as $key => $value) { if ($key == 'phone_id') { continue; } if ($settingDefaults->{$key} == $value) { $_phoneSettings->{$key} = NULL; } } if (Voipmanager_Controller_Snom_PhoneSettings::getInstance()->get($phone->getId())) { $phoneSettings = Voipmanager_Controller_Snom_PhoneSettings::getInstance()->update($_phoneSettings); } else { $phoneSettings = Voipmanager_Controller_Snom_PhoneSettings::getInstance()->create($_phoneSettings); } } Voipmanager_Controller_Snom_Line::getInstance()->deletePhoneLines($phone->getId()); $this->_createLines($phone, $_phone->lines); // save phone rights if (isset($_phone->rights)) { $this->_backend->setPhoneRights($_phone); } Tinebase_TransactionManager::getInstance()->commitTransaction($transactionId); } catch (Exception $e) { $this->_handleRecordCreateOrUpdateException($e); } return $this->get($phone->getId()); }
/** * check grant for action (CRUD) * * @param Tinebase_Record_Interface $_record * @param string $_action * @param boolean $_throw * @param string $_errorMessage * @param Tinebase_Record_Interface $_oldRecord * @return boolean * @throws Tinebase_Exception_AccessDenied * * @todo use this function in other create + update functions * @todo invent concept for simple adding of grants (plugins?) */ protected function _checkGrant($_record, $_action, $_throw = TRUE, $_errorMessage = 'No Permission.', $_oldRecord = NULL) { if (!$this->_doContainerACLChecks || !$_record->has('container_id') || $this->_currentAccount->hasGrant($_record->container_id, Tinebase_Model_Grants::GRANT_ADMIN)) { return TRUE; } $hasGrant = FALSE; switch ($_action) { case 'get': $hasGrant = $this->_currentAccount->hasGrant($_record->container_id, Tinebase_Model_Grants::GRANT_READ); break; case 'create': $hasGrant = $this->_currentAccount->hasGrant($_record->container_id, Tinebase_Model_Grants::GRANT_ADD); break; case 'update': $hasGrant = $this->_currentAccount->hasGrant($_record->container_id, Tinebase_Model_Grants::GRANT_EDIT); break; case 'delete': $container = Tinebase_Container::getInstance()->getContainerById($_record->container_id); $hasGrant = $this->_currentAccount->hasGrant($_record->container_id, Tinebase_Model_Grants::GRANT_DELETE); break; } if (!$hasGrant) { if ($_throw) { throw new Tinebase_Exception_AccessDenied($_errorMessage); } else { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' No permissions to ' . $_action . ' in container ' . $_record->container_id); } } return $hasGrant; }
/** * update one phone * * @param Voipmanager_Model_Snom_Phone $_phone * @param Voipmanager_Model_Snom_PhoneSettings|optional $_phoneSettings * @return Voipmanager_Model_Snom_Phone * @throws Voipmanager_Exception_Validation */ public function update(Tinebase_Record_Interface $_phone) { //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_phone->toArray(), true)); // check first if mac address is already used if ($_phone->has('macaddress')) { try { $phoneWithMac = $this->getByMacAddress($_phone->macaddress); if ($phoneWithMac->getId() !== $_phone->getId()) { throw new Voipmanager_Exception_Validation('A phone with this mac address already exists.'); } } catch (Voipmanager_Exception_NotFound $venf) { // everything ok } } $phone = $this->_backend->update($_phone); $_phoneSettings = $_phone->settings; if ($_phoneSettings instanceof Voipmanager_Model_Snom_PhoneSettings) { // force the right phone_id $_phoneSettings->setId($phone->getId()); // set all settings which are equal to the default settings to NULL $template = Voipmanager_Controller_Snom_Template::getInstance()->get($phone->template_id); $settingDefaults = Voipmanager_Controller_Snom_Setting::getInstance()->get($template->setting_id); foreach ($_phoneSettings->toArray() as $key => $value) { if ($key == 'phone_id') { continue; } if ($settingDefaults->{$key} == $value) { $_phoneSettings->{$key} = NULL; } } if (Voipmanager_Controller_Snom_PhoneSettings::getInstance()->get($phone->getId())) { //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_phoneSettings->toArray(), true)); $phoneSettings = Voipmanager_Controller_Snom_PhoneSettings::getInstance()->update($_phoneSettings); } else { $phoneSettings = Voipmanager_Controller_Snom_PhoneSettings::getInstance()->create($_phoneSettings); } } Voipmanager_Controller_Snom_Line::getInstance()->deletePhoneLines($phone->getId()); $this->_createLines($phone, $_phone->lines); // save phone rights if (isset($_phone->rights)) { $this->_backend->setPhoneRights($_phone); } return $this->get($phone->getId()); }
/** * sets record modification data and protects it from spoofing * * @param Tinebase_Record_Interface $_newRecord record from user data * @param string $_action one of {create|update|delete} * @param Tinebase_Record_Interface $_curRecord record from storage * @throws Tinebase_Exception_InvalidArgument */ public static function setRecordMetaData($_newRecord, $_action, $_curRecord = NULL) { // disable validation as this is slow and we are setting valid data here $bypassFilters = $_newRecord->bypassFilters; $_newRecord->bypassFilters = TRUE; list($currentAccountId, $currentTime) = self::getCurrentAccountIdAndTime(); // spoofing protection $_newRecord->created_by = $_curRecord ? $_curRecord->created_by : NULL; $_newRecord->creation_time = $_curRecord ? $_curRecord->creation_time : NULL; $_newRecord->last_modified_by = $_curRecord ? $_curRecord->last_modified_by : NULL; $_newRecord->last_modified_time = $_curRecord ? $_curRecord->last_modified_time : NULL; if ($_newRecord->has('is_deleted')) { $_newRecord->is_deleted = $_curRecord ? $_curRecord->is_deleted : 0; $_newRecord->deleted_time = $_curRecord ? $_curRecord->deleted_time : NULL; $_newRecord->deleted_by = $_curRecord ? $_curRecord->deleted_by : NULL; } switch ($_action) { case 'create': $_newRecord->created_by = $currentAccountId; $_newRecord->creation_time = $currentTime; if ($_newRecord->has('seq')) { $_newRecord->seq = 1; } break; case 'update': $_newRecord->last_modified_by = $currentAccountId; $_newRecord->last_modified_time = $currentTime; self::increaseRecordSequence($_newRecord, $_curRecord); break; case 'delete': $_newRecord->deleted_by = $currentAccountId; $_newRecord->deleted_time = $currentTime; $_newRecord->is_deleted = true; self::increaseRecordSequence($_newRecord, $_curRecord); break; default: throw new Tinebase_Exception_InvalidArgument('Action must be one of {create|update|delete}.'); break; } $_newRecord->bypassFilters = $bypassFilters; }
/** * merges changes made to local storage on concurrent updates into the new record * * @param string $applicationId * @param Tinebase_Record_Interface $newRecord record from user data * @param Tinebase_Record_Interface $curRecord record from storage * @return Tinebase_Record_RecordSet with resolved concurrent updates (Tinebase_Model_ModificationLog records) * @throws Tinebase_Timemachine_Exception_ConcurrencyConflict */ public function manageConcurrentUpdates($applicationId, Tinebase_Record_Interface $newRecord, Tinebase_Record_Interface $curRecord) { if (!$newRecord->has('seq')) { return $this->manageConcurrentUpdatesByTimestamp($newRecord, $curRecord, get_class($newRecord), 'Sql', $newRecord->getId()); } $this->_applicationId = $applicationId; $resolved = new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog'); if ($curRecord->seq != $newRecord->seq) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Concurrent updates: current record last updated '" . ($curRecord->last_modified_time instanceof DateTime ? $curRecord->last_modified_time : 'unknown') . "' where record to be updated was last updated '" . ($newRecord->last_modified_time instanceof DateTime ? $newRecord->last_modified_time : ($curRecord->creation_time instanceof DateTime ? $curRecord->creation_time : 'unknown')) . "' / current sequence: " . $curRecord->seq . " - new record sequence: " . $newRecord->seq); } $loggedMods = $this->getModificationsBySeq($applicationId, $newRecord, $curRecord->seq); // effective modifications made to the record after current user got his record $diffs = $this->computeDiff($loggedMods); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " During the concurrent update, the following changes have been made: " . print_r($diffs->toArray(), true)); } $this->_resolveDiffs($resolved, $diffs, $newRecord); } else { if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " No concurrent updates."); } } return $resolved; }