/** * 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; }