/**
  * remove protected properties and execute @name annotation
  *
  * @param Record $record
  */
 protected function finalizeRecord(Record $record, DataDimensions $dataDimensions)
 {
     // Apply @name annotation
     if ($record->getDataTypeDefinition()->hasNamingPattern()) {
         $record->setName(Util::applyNamingPattern($record->getProperties(), $record->getDataTypeDefinition()->getNamingPattern()));
     }
     // remove protected properties
     $properties = $record->getProperties();
     foreach ($record->getDataTypeDefinition()->getProtectedProperties($dataDimensions->getViewName()) as $property) {
         unset($properties[$property]);
     }
     $record->setProperties($properties);
     return $record;
 }
 /**
  * @param $record
  *
  * @return int
  * @throws Exception
  */
 public function saveRecord($record, $viewName = 'default', $workspace = 'default', $language = 'default')
 {
     $repositoryName = $this->repository->getName();
     $contentTypeName = $this->contentTypeDefinition->getName();
     $tableName = $repositoryName . '$' . $contentTypeName;
     if ($tableName != Util::generateValidIdentifier($repositoryName) . '$' . Util::generateValidIdentifier($contentTypeName)) {
         throw new RepositoryException('Invalid repository and/or content type name(s).', RepositoryException::REPOSITORY_INVALID_NAMES);
     }
     if ($language != 'default' and $this->contentTypeDefinition->hasLanguages()) {
         if (!array_key_exists($language, $this->contentTypeDefinition->getLanguages())) {
             throw new RepositoryException('Trying to store record, but invalid language ' . $language . ' has been provided', RepositoryException::REPOSITORY_INVALID_LANGUAGE);
         }
     }
     if ($workspace != 'default' and $this->contentTypeDefinition->hasWorkspaces()) {
         if (!array_key_exists($workspace, $this->contentTypeDefinition->getWorkspaces())) {
             throw new RepositoryException('Trying to store record, but invalid workspace ' . $language . ' has been provided', RepositoryException::REPOSITORY_INVALID_WORKSPACE);
         }
     }
     // Apply @name annotation
     if ($this->contentTypeDefinition->hasNamingPattern()) {
         $record['properties']['name'] = Util::applyNamingPattern($record['properties'], $this->contentTypeDefinition->getNamingPattern());
     }
     // remove protected properties
     foreach ($this->contentTypeDefinition->getProtectedProperties($viewName) as $property) {
         unset($record['properties'][$property]);
     }
     $possibleProperties = $this->contentTypeDefinition->getProperties($viewName);
     $notallowed = array_diff(array_keys($record['properties']), $possibleProperties);
     if (count($notallowed) != 0) {
         throw new RepositoryException('Trying to store undefined properties: ' . join(',', $notallowed) . '.', RepositoryException::REPOSITORY_INVALID_PROPERTIES);
     }
     $mandatoryProperties = $this->contentTypeDefinition->getMandatoryProperties($viewName);
     $missing = array();
     foreach ($mandatoryProperties as $property) {
         if (array_key_exists($property, $record['properties'])) {
             if ($record['properties'][$property] == '') {
                 $missing[] = $property;
             }
         } else {
             $missing[] = $property;
         }
     }
     if (count($missing) != 0) {
         throw new RepositoryException('Trying to store record, but missing mandatory properties: ' . join(',', $missing) . '.', RepositoryException::REPOSITORY_MISSING_MANDATORY_PROPERTIES);
     }
     $dbh = $this->repository->getDatabaseConnection();
     $mode = 'insert';
     $record['revision'] = 1;
     // fix record array structure, if someone forgot the id
     if (!isset($record['id'])) {
         $record['id'] = 0;
     }
     if ($record['id'] != 0) {
         try {
             $row = self::getRecordTableRow($record['id'], $workspace, $language);
             $mode = 'update';
             // transfer all properties, which are not set in the record to be saved
             foreach ($row as $key => $value) {
                 if (Helper::startsWith($key, 'property_')) {
                     $property = substr($key, 9);
                     if (!array_key_exists($property, $record['properties'])) {
                         $record['properties'][$property] = $value;
                     }
                 }
             }
             $mode = 'update';
             $record['revision'] = $row['revision'] + 1;
         } catch (RepositoryException $e) {
         }
     }
     if ($mode == 'insert' and $record['id'] == 0) {
         // update counter for new record
         $sql = 'INSERT INTO _counter_ (repository,content_type,counter) VALUES (? , ? ,1) ON DUPLICATE KEY UPDATE counter=counter+1;';
         $params = array();
         $params[] = $repositoryName;
         $params[] = $contentTypeName;
         $stmt = $dbh->prepare($sql);
         $stmt->execute($params);
         $sql = 'SELECT counter FROM _counter_ WHERE repository = ? AND content_type = ?';
         $stmt = $dbh->prepare($sql);
         $stmt->execute($params);
         $result = $stmt->fetchColumn(0);
         $record['id'] = $result;
         // make sure counter is always at least greater than the largest id, e.g. if the counter row got deleted
         $sql = 'SELECT MAX(id)+1 FROM ' . $tableName;
         $stmt = $dbh->prepare($sql);
         $stmt->execute($params);
         $result = $stmt->fetchColumn(0);
         if ($result > $record['id']) {
             $record['id'] = $result;
             $sql = 'INSERT INTO _counter_ (repository,content_type,counter) VALUES (? , ? ,?) ON DUPLICATE KEY UPDATE counter=?;';
             $params = array();
             $params[] = $repositoryName;
             $params[] = $contentTypeName;
             $params[] = $result;
             $params[] = $result;
             $stmt = $dbh->prepare($sql);
             $stmt->execute($params);
         }
     }
     $timestamp = time();
     $timeshiftTimestamp = $this->repository->getTimeshiftTimestamp();
     if ($mode == 'update') {
         // invalidate current revision
         $sql = 'UPDATE ' . $tableName . ' SET validuntil_timestamp = ? WHERE id = ? AND workspace = ? AND language = ? AND deleted = 0 AND validfrom_timestamp <=? AND validuntil_timestamp >?';
         $params = array();
         $params[] = $timeshiftTimestamp;
         $params[] = $record['id'];
         $params[] = $workspace;
         $params[] = $language;
         $params[] = $timeshiftTimestamp;
         $params[] = $timeshiftTimestamp;
         $stmt = $dbh->prepare($sql);
         $stmt->execute($params);
     }
     $values = array();
     $values['id'] = $record['id'];
     $values['hash'] = md5(serialize($record['properties']));
     $values['workspace'] = $workspace;
     $values['language'] = $language;
     $values['revision'] = $record['revision'];
     $values['deleted'] = 0;
     if ($mode == 'insert') {
         $values['creation_timestamp'] = $timestamp;
         $values['creation_apiuser'] = $this->repository->getAPIUser();
         $values['creation_clientip'] = $this->repository->getClientIp();
         $values['creation_username'] = $this->repository->getCurrentUserName();
         $values['creation_firstname'] = $this->repository->getCurrentUserFirstname();
         $values['creation_lastname'] = $this->repository->getCurrentUserLastname();
     } else {
         $values['creation_timestamp'] = $row['creation_timestamp'];
         $values['creation_apiuser'] = $row['creation_apiuser'];
         $values['creation_clientip'] = $row['creation_clientip'];
         $values['creation_username'] = $row['creation_username'];
         $values['creation_firstname'] = $row['creation_firstname'];
         $values['creation_lastname'] = $row['creation_lastname'];
     }
     $values['lastchange_timestamp'] = $timestamp;
     $values['lastchange_apiuser'] = $this->repository->getAPIUser();
     $values['lastchange_clientip'] = $this->repository->getClientIp();
     $values['lastchange_username'] = $this->repository->getCurrentUserName();
     $values['lastchange_firstname'] = $this->repository->getCurrentUserFirstname();
     $values['lastchange_lastname'] = $this->repository->getCurrentUserLastname();
     $values['validfrom_timestamp'] = $timeshiftTimestamp;
     $values['validuntil_timestamp'] = $this->repository->getMaxTimestamp();
     foreach ($record['properties'] as $property => $value) {
         $property = Util::generateValidIdentifier($property);
         $values['property_' . $property] = $value;
     }
     if ($mode == 'update') {
         $values['parent_id'] = $row['parent_id'];
         $values['position'] = $row['position'];
         $values['position_left'] = $row['position_left'];
         $values['position_right'] = $row['position_right'];
         $values['position_level'] = $row['position_level'];
     }
     if ($mode == 'insert') {
         $event = new ContentRecordEvent($this->contentTypeDefinition, $values);
         $this->app['dispatcher']->dispatch('content.record.before.insert', $event);
         $values = $event->getValues();
     } else {
         $event = new ContentRecordEvent($this->contentTypeDefinition, $values, $row);
         $this->app['dispatcher']->dispatch('content.record.before.update', $event);
         $values = $event->getValues();
     }
     $sql = 'INSERT INTO ' . $tableName;
     $sql .= ' (' . join(',', array_keys($values)) . ')';
     $sql .= ' VALUES ( ?';
     $sql .= str_repeat(' , ?', count($values) - 1);
     $sql .= ')';
     $stmt = $dbh->prepare($sql);
     $stmt->execute(array_values($values));
     if ($mode == 'insert') {
         $event = new ContentRecordEvent($this->contentTypeDefinition, $values);
         $this->app['dispatcher']->dispatch('content.record.after.insert', $event);
     } else {
         $event = new ContentRecordEvent($this->contentTypeDefinition, $values, $row);
         $this->app['dispatcher']->dispatch('content.record.after.update', $event);
     }
     if ($this->contentTypeDefinition->hasSynchronizedProperties() && $this->currentlySynchronizingProperties === false) {
         $this->synchronizeProperties($record, $viewName, $workspace, $language);
     }
     return $record['id'];
 }