/**
  * Tries to perform real update over the concept without loosing any old data and properly chaning the update data.
  *
  * @param array $updateData Leave empty array if no normal data is updated.
  * @param array $updateExtraData Leave empty array if no extra data is updated.
  * @param bool $commit, optional, Default: true
  * @param bool $ignoreValidation, optional, Default: false If set to true the validation on save will not be performed.
  * @return bool True if the save is successfull. False otherwise. You can see errors by calling getErrors();
  */
 public function update($updateData, $updateExtraData = [], $commit = true, $ignoreValidation = false)
 {
     $data = $this->getData();
     $extraData = $this->getCurrentRequiredData();
     // Fix for preventing multiplying of the notation.
     unset($extraData['notation']);
     //!TODO The fallowing should be added to required data or all the process of editing concept should be refactored so that old data is not lost.
     // Data which will be lost on update if not remembered...
     if (isset($data['deleted'])) {
         $extraData['deleted'] = $data['deleted'];
     }
     if (isset($data['toBeChecked'])) {
         $extraData['toBeChecked'] = $data['toBeChecked'];
     }
     if (isset($data['created_by'])) {
         $extraData['created_by'] = $data['created_by'];
     }
     if (isset($data['created_timestamp'])) {
         $extraData['created_timestamp'] = $data['created_timestamp'];
     }
     if (isset($data['modified_by'])) {
         $extraData['modified_by'] = $data['modified_by'];
     }
     if (isset($data['modified_timestamp'])) {
         $extraData['modified_timestamp'] = $data['modified_timestamp'];
     }
     if (isset($data['approved_by'])) {
         $extraData['approved_by'] = $data['approved_by'];
     }
     if (isset($data['approved_timestamp'])) {
         $extraData['approved_timestamp'] = $data['approved_timestamp'];
     }
     if (isset($data['deleted_by'])) {
         $extraData['deleted_by'] = $data['deleted_by'];
     }
     if (isset($data['deleted_timestamp'])) {
         $extraData['deleted_timestamp'] = $data['deleted_timestamp'];
     }
     if (isset($data['status'])) {
         $extraData['status'] = $data['status'];
     }
     $data = array_merge($data, $updateData);
     $extraData = array_merge($extraData, $updateExtraData);
     if (isset($extraData['status'])) {
         if ($extraData['status'] !== OpenSKOS_Concept_Status::APPROVED) {
             $data['approved_by'] = '';
             $data['approved_timestamp'] = '';
             $extraData['approved_by'] = '';
             $extraData['approved_timestamp'] = '';
         }
         if ($extraData['status'] !== OpenSKOS_Concept_Status::isStatusLikeDeleted($extraData['status'])) {
             $data['deleted_by'] = '';
             $data['deleted_timestamp'] = '';
             $extraData['deleted_by'] = '';
             $extraData['deleted_timestamp'] = '';
         }
     }
     // The actual update...
     $this->setConceptData($data, $extraData);
     return $this->save($extraData, $commit, $ignoreValidation);
 }
 public function saveAction()
 {
     $concept = $this->_getConcept();
     $form = Editor_Forms_Concept::getInstance($concept);
     $formData = $this->getRequest()->getParams();
     if (!$this->getRequest()->isPost()) {
         $this->getHelper('FlashMessenger')->setNamespace('error')->addMessage(_('No POST data recieved'));
         $this->_helper->redirector('edit');
     }
     $this->_checkConceptTenantForEdit($concept);
     if (!$form->isValid($formData)) {
         return $this->_forward('edit');
     } else {
         //@FIXME should upgrade multi hidden fields to allow easy submission (change name to template something)
         array_shift($formData['inScheme']);
         $form->populate($formData);
         if (null === $concept) {
             $this->_requireAccess('editor.concepts', 'propose', self::RESPONSE_TYPE_PARTIAL_HTML);
             $concept = new Editor_Models_Concept(new Api_Models_Concept());
         } else {
             $this->_requireAccess('editor.concepts', 'edit', self::RESPONSE_TYPE_PARTIAL_HTML);
         }
         $formData = $form->getValues();
         $oldData = $concept->getData();
         //by reference.
         $extraData = $concept->transformFormData($formData);
         $concept->setConceptData($formData, $extraData);
         try {
             $user = OpenSKOS_Db_Table_Users::fromIdentity();
             $extraData = array_merge($extraData, array('tenant' => $user->tenant, 'modified_by' => (int) $user->id, 'modified_timestamp' => date("Y-m-d\\TH:i:s\\Z"), 'toBeChecked' => isset($extraData['toBeChecked']) ? (bool) $extraData['toBeChecked'] : false));
             if (!isset($extraData['uuid']) || empty($extraData['uuid'])) {
                 $extraData['uuid'] = $concept['uuid'];
                 $extraData['created_by'] = $extraData['modified_by'];
                 $extraData['created_timestamp'] = $extraData['modified_timestamp'];
             } else {
                 if (isset($oldData['created_by'])) {
                     $extraData['created_by'] = $oldData['created_by'];
                 }
                 if (isset($oldData['created_timestamp'])) {
                     $extraData['created_timestamp'] = $oldData['created_timestamp'];
                 }
                 if (isset($oldData['collection'])) {
                     $extraData['collection'] = $oldData['collection'];
                 }
                 if (isset($oldData['approved_by'])) {
                     $extraData['approved_by'] = $oldData['approved_by'];
                 }
                 if (isset($oldData['approved_timestamp'])) {
                     $extraData['approved_timestamp'] = $oldData['approved_timestamp'];
                 }
                 if (isset($oldData['deleted_by'])) {
                     $extraData['deleted_by'] = $oldData['deleted_by'];
                 }
                 if (isset($oldData['deleted_timestamp'])) {
                     $extraData['deleted_timestamp'] = $oldData['deleted_timestamp'];
                 }
             }
             if ($extraData['status'] === OpenSKOS_Concept_Status::APPROVED && (!isset($oldData['status']) || $oldData['status'] !== OpenSKOS_Concept_Status::APPROVED)) {
                 $extraData['approved_timestamp'] = $extraData['modified_timestamp'];
                 $extraData['approved_by'] = $extraData['modified_by'];
             }
             if ($extraData['status'] !== OpenSKOS_Concept_Status::APPROVED) {
                 $formData['approved_by'] = '';
                 $formData['approved_timestamp'] = '';
                 $extraData['approved_by'] = '';
                 $extraData['approved_timestamp'] = '';
             }
             if (OpenSKOS_Concept_Status::isStatusLikeDeleted($extraData['status'])) {
                 $formData['deleted_by'] = '';
                 $formData['deleted_timestamp'] = '';
                 $extraData['deleted_by'] = '';
                 $extraData['deleted_timestamp'] = '';
             }
             if (!isset($extraData['collection'])) {
                 if (isset($concept['inScheme']) && isset($concept['inScheme'][0])) {
                     $firstConceptScheme = Editor_Models_ApiClient::factory()->getConceptSchemes($concept['inScheme'][0]);
                     $firstConceptScheme = array_shift($firstConceptScheme);
                     if (!empty($firstConceptScheme) && isset($firstConceptScheme['collection'])) {
                         $extraData['collection'] = $firstConceptScheme['collection'];
                     }
                 }
             }
             $this->_handleStatusAutomatedActions($concept, $formData, $extraData);
             $concept->setConceptData($formData, $extraData);
             if ($concept->save($extraData)) {
                 if (!isset($concept['inScheme'])) {
                     $newSchemes = array();
                 } else {
                     $newSchemes = $concept['inScheme'];
                 }
                 if (!isset($oldData['inScheme'])) {
                     $oldSchemes = array();
                 } else {
                     $oldSchemes = $oldData['inScheme'];
                 }
                 $concept->updateConceptSchemes($newSchemes, $oldSchemes);
             } else {
                 return $this->_forward('edit', 'concept', 'editor', array('errors' => $concept->getErrors()));
             }
         } catch (Zend_Exception $e) {
             return $this->_forward('edit', 'concept', 'editor', array('errors' => array(new Editor_Models_ConceptValidator_Error('unknown', $e->getMessage()))));
         }
         $this->_helper->redirector('view', 'concept', 'editor', array('uuid' => $extraData['uuid']));
     }
 }
 /**
  * Converts a RDF structure to a Solr Document
  * 
  * @param DOMNode $Description
  * @param array $extradata
  * @param DOMXPath $xpath
  * @param string $fallbackStatus The status which will be used if no other status is detected.
  * @param bool $autoGenerateUri If the script should auto generate uri and notation
  * @param OpenSKOS_Db_Table_Row_Collection $collection
  * @return OpenSKOS_Solr_Document
  */
 public static function DomNode2SolrDocument(DOMNode $Description, array $extradata = array(), DOMXPath $xpath = null, $fallbackStatus = '', $autoGenerateUri = false, $collection = null)
 {
     if ($Description->nodeName != 'rdf:Description') {
         throw new OpenSKOS_Rdf_Parser_Exception('wrong nodeName, expected `rdf:Description`, got `' . $Description->nodeName . '`');
     }
     if (null === $xpath) {
         $xpath = new DOMXPath($Description->ownerDocument);
         //support for only these namespaces:
         foreach (self::$namespaces as $prefix => $uri) {
             $xpath->registerNamespace($prefix, $uri);
         }
     }
     // Sets created_timestamp, modified_timestamp and approved_timestamp.
     $autoExtraData = array();
     $dateSubmittedNodes = $xpath->query('dcterms:dateSubmitted', $Description);
     if ($dateSubmittedNodes->length > 0) {
         $autoExtraData['created_timestamp'] = date(self::SOLR_DATETIME_FORMAT, strtotime($dateSubmittedNodes->item(0)->nodeValue));
     } else {
         $autoExtraData['created_timestamp'] = date(self::SOLR_DATETIME_FORMAT);
     }
     $dateModifiedNodes = $xpath->query('dcterms:modified', $Description);
     if ($dateModifiedNodes->length > 0) {
         $autoExtraData['modified_timestamp'] = date(self::SOLR_DATETIME_FORMAT, strtotime($dateModifiedNodes->item(0)->nodeValue));
     }
     $dateAcceptedNodes = $xpath->query('dcterms:dateAccepted', $Description);
     if ($dateAcceptedNodes->length > 0) {
         $autoExtraData['approved_timestamp'] = date(self::SOLR_DATETIME_FORMAT, strtotime($dateAcceptedNodes->item(0)->nodeValue));
     }
     // Sets status. If we have info for date submited the status is candidate, if we have info for date accepted the status is approved.
     $openskosStatusNodes = $xpath->query('openskos:status', $Description);
     if ($openskosStatusNodes->length > 0) {
         $autoExtraData['status'] = $openskosStatusNodes->item(0)->nodeValue;
     } elseif (!empty($fallbackStatus)) {
         $autoExtraData['status'] = $fallbackStatus;
     } elseif ($collection !== null && !$collection->getTenant()['enableStatusesSystem']) {
         $autoExtraData['status'] = OpenSKOS_Concept_Status::APPROVED;
     } else {
         $autoExtraData['status'] = OpenSKOS_Concept_Status::CANDIDATE;
     }
     // Merges the incoming extra data with the auto detected extra data.
     $extradata = array_merge($autoExtraData, $extradata);
     // Validates status
     if (!empty($extradata['status']) && !in_array($extradata['status'], OpenSKOS_Concept_Status::getStatuses())) {
         throw new OpenSKOS_Rdf_Parser_Exception('Status "' . $extradata['status'] . '" not recognized.');
     }
     // Status deleted equals soft deletion and soft deleting equals status deleted.
     if (isset($extradata['status']) && $extradata['status'] == OpenSKOS_Concept_Status::DELETED) {
         $extradata['deleted'] = true;
     } elseif (isset($extradata['deleted']) && $extradata['deleted']) {
         $extradata['status'] = OpenSKOS_Concept_Status::DELETED;
     }
     // Set deleted timestamp if status is OBSOLETE(expired) and deleted timestamp is not already set.
     if (empty($extradata['deleted_timestamp']) && (isset($extradata['status']) && OpenSKOS_Concept_Status::isStatusLikeDeleted($extradata['status']) || isset($extradata['deleted']) && $extradata['deleted'])) {
         $extradata['deleted_timestamp'] = date(self::SOLR_DATETIME_FORMAT);
     }
     // Fix empty values
     if (empty($extradata['approved_timestamp'])) {
         unset($extradata['approved_timestamp']);
     }
     if (empty($extradata['approved_by'])) {
         unset($extradata['approved_by']);
     }
     if (empty($extradata['deleted_timestamp'])) {
         unset($extradata['deleted_timestamp']);
     }
     if (empty($extradata['deleted_by'])) {
         unset($extradata['deleted_by']);
     }
     // Creates the solr document from the description and the extra data.
     $document = new OpenSKOS_Solr_Document();
     foreach ($extradata as $key => $var) {
         $document->{$key} = is_bool($var) ? true === $var ? 'true' : 'false' : $var;
     }
     if (!isset($extradata['uuid'])) {
         $document->uuid = OpenSKOS_Utils::uuid();
     }
     if ($type = $xpath->query('./rdf:type', $Description)->item(0)) {
         $resource = $type->getAttributeNS(self::$namespaces['rdf'], 'resource');
         if (0 !== strpos($resource, self::$namespaces['skos'])) {
             return;
         }
         $className = parse_url($resource, PHP_URL_FRAGMENT);
         $document->class = parse_url($type->getAttributeNS(self::$namespaces['rdf'], 'resource'), PHP_URL_FRAGMENT);
     } else {
         throw new OpenSKOS_Rdf_Parser_Exception('missing required attribute rdf:type');
         return;
     }
     $skosElements = $xpath->query('./skos:*', $Description);
     foreach ($skosElements as $skosElement) {
         $fieldname = str_replace('skos:', '', $skosElement->nodeName);
         if (in_array($fieldname, self::$langMapping)) {
             if ($xml_lang = $skosElement->getAttribute('xml:lang')) {
                 $fieldname = $fieldname . '@' . $xml_lang;
             }
         }
         $document->{$fieldname} = trim($skosElement->nodeValue) ? trim($skosElement->nodeValue) : $skosElement->getAttributeNS(self::$namespaces['rdf'], 'resource');
         //store every first preflabel/notation in a sortable field:
         if (0 === strpos($fieldname, 'prefLabel') || 0 === strpos($fieldname, 'notation')) {
             $sortFieldName = str_replace(array('prefLabel', 'notation'), array('prefLabelSort', 'notationSort'), $fieldname);
             if (!$document->offsetExists($sortFieldName)) {
                 $offset = $document->offsetGet($fieldname);
                 $document->{$sortFieldName} = array_shift($offset);
             }
             //also store the first language in a generic field:
             if (strpos($fieldname, '@')) {
                 $sortFieldName = preg_replace('/@.+/', 'Sort', $fieldname);
                 if (!$document->offsetExists($sortFieldName)) {
                     $offset = $document->offsetGet($fieldname);
                     $document->{$sortFieldName} = array_shift($offset);
                 }
             }
         }
     }
     foreach (array('dc', 'dcterms') as $ns) {
         foreach ($xpath->query('./' . $ns . ':*', $Description) as $element) {
             $fieldname = str_replace($ns . ':', 'dcterms_', $element->nodeName);
             $document->{$fieldname} = trim($element->nodeValue);
         }
     }
     //some XML files use rdfs:label/rdfs:comment
     // let's map those to dcterms:title/dcterms:description
     foreach ($xpath->query('./rdfs:label | ./dcterms:description', $Description) as $element) {
         $fieldname = str_replace(array('rdfs:label', 'rdfs:comment'), array('dcterms:title', 'dcterms:description'), $element->nodeName);
         $document->{$fieldname} = trim($element->nodeValue);
     }
     $document->xml = $Description->ownerDocument->saveXML($Description);
     // Checks or generate uri
     if (!$document->offsetExists('uri')) {
         $uri = $Description->getAttributeNS(self::$namespaces['rdf'], 'about');
         if ($uri) {
             $document->uri = $uri;
         } else {
             if ($autoGenerateUri) {
                 $document->autoGenerateUri($collection);
             } else {
                 throw new OpenSKOS_Rdf_Parser_Exception('missing required attribute rdf:about');
             }
         }
     }
     // Puts status in the Document
     $document->updateStatusInGeneratedXml();
     //store namespaces:
     $availableNamespaces = array();
     foreach ($Description->childNodes as $childNode) {
         if ($childNode->nodeType === XML_ELEMENT_NODE) {
             $prefix = preg_replace('/^([a-z0-9\\-\\_]+)\\:.+$/', '$1', $childNode->nodeName);
             if (!in_array($prefix, $availableNamespaces)) {
                 $availableNamespaces[] = $prefix;
             }
         }
     }
     if ($availableNamespaces) {
         $document->xmlns = $availableNamespaces;
     }
     return $document;
 }