/** * @param string $liftFilePath * @param LexiconProjectModel $projectModel * @param LiftMergeRule $mergeRule * @param boolean $skipSameModTime * @param boolean $deleteMatchingEntry * @return \Api\Model\Languageforge\Lexicon\LiftImport */ public function merge($liftFilePath, $projectModel, $mergeRule = LiftMergeRule::CREATE_DUPLICATES, $skipSameModTime = true, $deleteMatchingEntry = false) { ini_set('max_execution_time', 180); // Sufficient time to import webster. TODO Make this async CP 2014-10 $entryList = new LexEntryListModel($projectModel); $entryList->read(); $hasExistingData = $entryList->count != 0; if (!$hasExistingData) { $projectModel->config->clearAllInputSystems(); // save and clear input systems $savedInputSystems = $projectModel->inputSystems->getArrayCopy(); $projectModel->inputSystems->exchangeArray(array()); } $reader = new \XMLReader(); $reader->open($liftFilePath); $this->liftDecoder = new LiftDecoder($projectModel); $this->stats = new LiftImportStats($entryList->count); $this->report = new ImportErrorReport(); $this->liftImportNodeError = new LiftImportNodeError(LiftImportNodeError::FILE, basename($liftFilePath)); $liftRangeDecoder = new LiftRangeDecoder($projectModel); $liftRangeFiles = array(); // Keys: filenames. Values: parsed files. $liftRanges = array(); // Keys: @id attributes of <range> elements. Values: parsed <range> elements. $liftFolderPath = dirname($liftFilePath); while ($reader->read()) { // Read LIFT ranges in the header of the LIFT file if ($reader->nodeType == \XMLReader::ELEMENT && $reader->localName == 'range') { $node = $reader->expand(); $rangeId = $node->attributes->getNamedItem('id')->textContent; $rangeHref = $node->attributes->getNamedItem('href')->textContent; $hrefPath = parse_url($rangeHref, PHP_URL_PATH); $rangeFilename = basename($hrefPath); $rangeImportNodeError = new LiftRangeImportNodeError(LiftRangeImportNodeError::FILE, $rangeFilename); if (!array_key_exists($rangeFilename, $liftRangeFiles)) { // Haven't parsed the .lift-ranges file yet. We'll assume it is alongside the .lift file. $rangeFilePath = $liftFolderPath . "/" . $rangeFilename; if (file_exists($rangeFilePath)) { $sxeNode = simplexml_load_file($rangeFilePath); $parsedRanges = $liftRangeDecoder->decode($sxeNode); $liftRanges = array_merge($liftRanges, $parsedRanges); $liftRangeFiles[] = $rangeFilename; } else { // Range file was NOT found in alongside the .lift file $rangeImportNodeError->addRangeFileNotFound(basename($liftFilePath)); } } // pull out the referenced range if (isset($liftRanges[$rangeId])) { $range = $liftRanges[$rangeId]; } else { $range = null; if (file_exists($rangeFilePath)) { // Range was NOT found in referenced .lift-ranges file after parsing it $rangeImportNodeError->addRangeNotFound($rangeId); } } // Range elements defined in LIFT file override any values defined in .lift-ranges file. if ($node->hasChildNodes()) { $rangeNode = self::domNode_to_sxeNode($node); $range = $liftRangeDecoder->readRange($rangeNode, $range); $liftRanges[$rangeId] = $range; } if ($rangeImportNodeError->hasErrors()) { $this->liftImportNodeError->addSubnodeError($rangeImportNodeError); } } // Read the custom 'fields' spec in the header of the LIFT file if ($reader->nodeType == \XMLReader::ELEMENT && $reader->localName == 'fields') { $isInFieldsSectionOfLift = true; $this->liftDecoder->liftFields = array(); while ($isInFieldsSectionOfLift && $reader->read()) { if ($reader->nodeType == \XMLReader::ELEMENT && $reader->localName == 'field') { $node = $reader->expand(); $sxeNode = self::domNode_to_sxeNode($node); $LiftFieldTag = (string) $sxeNode['tag']; $liftField = array(); foreach ($sxeNode as $element) { if ($element->getName() === 'form') { $inputSystemTag = (string) $element['lang']; $liftField[$inputSystemTag] = (string) $element->text; } } $this->liftDecoder->liftFields[$LiftFieldTag] = $liftField; } elseif ($reader->nodeType == \XMLReader::END_ELEMENT && $reader->localName == 'fields') { $isInFieldsSectionOfLift = false; } } } // Read an entry node if ($reader->nodeType == \XMLReader::ELEMENT && $reader->localName == 'entry') { $this->stats->importEntries++; $node = $reader->expand(); $sxeNode = self::domNode_to_sxeNode($node); $guid = $reader->getAttribute('guid'); $existingEntry = $entryList->searchEntriesFor('guid', $guid); if ($existingEntry) { $entry = new LexEntryModel($projectModel, $existingEntry['id']); $dateModified = $reader->getAttribute('dateModified'); if (self::differentModTime($dateModified, $entry->authorInfo->modifiedDate) || !$skipSameModTime) { if ($mergeRule == LiftMergeRule::CREATE_DUPLICATES) { $entry = new LexEntryModel($projectModel); $this->readEntryWithErrorReport($sxeNode, $entry, $mergeRule); $entry->guid = ''; $entry->write(); $this->stats->entriesDuplicated++; } else { if (isset($sxeNode->{'lexical-unit'})) { $this->readEntryWithErrorReport($sxeNode, $entry, $mergeRule); $entry->write(); $this->stats->entriesMerged++; } elseif (isset($sxeNode->attributes()->dateDeleted) && $deleteMatchingEntry) { LexEntryModel::remove($projectModel, $existingEntry['id']); $this->stats->entriesDeleted++; } } } else { // skip because same mod time and skip enabled if (!isset($sxeNode->{'lexical-unit'}) && isset($sxeNode->attributes()->dateDeleted) && $deleteMatchingEntry) { LexEntryModel::remove($projectModel, $existingEntry['id']); $this->stats->entriesDeleted++; } } } else { if (isset($sxeNode->{'lexical-unit'})) { $entry = new LexEntryModel($projectModel); $this->readEntryWithErrorReport($sxeNode, $entry, $mergeRule); $entry->write(); $this->stats->newEntries++; } } } } $reader->close(); // put back saved input systems if none found in the imported data if (!$hasExistingData && $projectModel->inputSystems->count() <= 0) { $projectModel->inputSystems->exchangeArray($savedInputSystems); } // add lift ranges if ($mergeRule != LiftMergeRule::IMPORT_LOSES) { foreach ($liftRanges as $liftRangeCode => $liftRange) { // add everything except semantic domains if (strpos($liftRangeCode, 'semantic-domain') === false) { self::rangeToOptionList($projectModel, $liftRangeCode, LexiconConfigObj::flexOptionlistName($liftRangeCode), $liftRange); } } } $this->report->nodeErrors[] = $this->liftImportNodeError; if ($this->report->hasError()) { error_log($this->report->toString()); } return $this; }
public function testZipImport_ImportReport_FormatOk() { $rangeImportNodeError = new LiftRangeImportNodeError(LiftRangeImportNodeError::FILE, 'Test.lift-ranges'); $rangeImportNodeError->addRangeNotFound('rangeId_01'); $rangeImportNodeError->addRangeNotFound('rangeId_02'); $exampleImportNodeError = new LiftImportNodeError(LiftImportNodeError::EXAMPLE, ''); $exampleImportNodeError->addUnhandledElement('elementName01'); $senseImportNodeError = new LiftImportNodeError(LiftImportNodeError::SENSE, '00001-00001'); $senseImportNodeError->addUnhandledField('typeName01'); $senseImportNodeError->addUnhandledMedia('url01', 'context01'); $senseImportNodeError->addSubnodeError($exampleImportNodeError); $entryImportNodeError = new LiftImportNodeError(LiftImportNodeError::ENTRY, '00000-00001'); $entryImportNodeError->addUnhandledNote('noteType01'); $entryImportNodeError->addUnhandledTrait('traitName01'); $entryImportNodeError->addSubnodeError($senseImportNodeError); $liftImportNodeError = new LiftImportNodeError(LiftImportNodeError::FILE, 'Test.lift'); $liftImportNodeError->addSubnodeError($rangeImportNodeError); $liftImportNodeError->addSubnodeError($entryImportNodeError); $zipNodeError = new ZipImportNodeError(ZipImportNodeError::FILE, 'Test.zip'); $zipNodeError->addSubnodeError($liftImportNodeError); $report = new ImportErrorReport(); $report->nodeErrors[] = $zipNodeError; // echo "<pre>"; // echo $report->toFormattedString(); // echo "</pre>"; $this->assertRegExp("/" . self::zipImportReport . "/", $report->toFormattedString()); }