Пример #1
0
 /**
  * Converts a DOMElement item to a Zotero_Item object
  *
  * @param	DOMElement		$xml		Item data as DOMElement
  * @return	Zotero_Item					Zotero item object
  */
 public static function convertXMLToItem(DOMElement $xml)
 {
     // Get item type id, adding custom type if necessary
     $itemTypeName = $xml->getAttribute('itemType');
     $itemTypeID = Zotero_ItemTypes::getID($itemTypeName);
     if (!$itemTypeID) {
         $itemTypeID = Zotero_ItemTypes::addCustomType($itemTypeName);
     }
     // Primary fields
     $libraryID = (int) $xml->getAttribute('libraryID');
     $itemObj = self::getByLibraryAndKey($libraryID, $xml->getAttribute('key'));
     if (!$itemObj) {
         $itemObj = new Zotero_Item();
         $itemObj->libraryID = $libraryID;
         $itemObj->key = $xml->getAttribute('key');
     }
     $itemObj->setField('itemTypeID', $itemTypeID, false, true);
     $itemObj->setField('dateAdded', $xml->getAttribute('dateAdded'), false, true);
     $itemObj->setField('dateModified', $xml->getAttribute('dateModified'), false, true);
     $xmlFields = array();
     $xmlCreators = array();
     $xmlNote = null;
     $xmlPath = null;
     $xmlRelated = null;
     $childNodes = $xml->childNodes;
     foreach ($childNodes as $child) {
         switch ($child->nodeName) {
             case 'field':
                 $xmlFields[] = $child;
                 break;
             case 'creator':
                 $xmlCreators[] = $child;
                 break;
             case 'note':
                 $xmlNote = $child;
                 break;
             case 'path':
                 $xmlPath = $child;
                 break;
             case 'related':
                 $xmlRelated = $child;
                 break;
         }
     }
     // Item data
     $setFields = array();
     foreach ($xmlFields as $field) {
         // TODO: add custom fields
         $fieldName = $field->getAttribute('name');
         // Special handling for renamed computerProgram 'version' field
         if ($itemTypeID == 32 && $fieldName == 'version') {
             $fieldName = 'versionNumber';
         }
         $itemObj->setField($fieldName, $field->nodeValue, false, true);
         $setFields[$fieldName] = true;
     }
     $previousFields = $itemObj->getUsedFields(true);
     foreach ($previousFields as $field) {
         if (!isset($setFields[$field])) {
             $itemObj->setField($field, false, false, true);
         }
     }
     $deleted = $xml->getAttribute('deleted');
     $itemObj->deleted = $deleted == 'true' || $deleted == '1';
     // Creators
     $i = 0;
     foreach ($xmlCreators as $creator) {
         // TODO: add custom creator types
         $pos = (int) $creator->getAttribute('index');
         if ($pos != $i) {
             throw new Exception("No creator in position {$i} for item " . $itemObj->key);
         }
         $key = $creator->getAttribute('key');
         $creatorObj = Zotero_Creators::getByLibraryAndKey($libraryID, $key);
         // If creator doesn't exist locally (e.g., if it was deleted locally
         // and appears in a new/modified item remotely), get it from within
         // the item's creator block, where a copy should be provided
         if (!$creatorObj) {
             $subcreator = $creator->getElementsByTagName('creator')->item(0);
             if (!$subcreator) {
                 throw new Exception("Data for missing local creator {$key} not provided", Z_ERROR_CREATOR_NOT_FOUND);
             }
             $creatorObj = Zotero_Creators::convertXMLToCreator($subcreator, $libraryID);
             if ($creatorObj->key != $key) {
                 throw new Exception("Creator key " . $creatorObj->key . " does not match item creator key {$key}");
             }
         }
         if (Zotero_Utilities::unicodeTrim($creatorObj->firstName) === '' && Zotero_Utilities::unicodeTrim($creatorObj->lastName) === '') {
             continue;
         }
         $creatorTypeID = Zotero_CreatorTypes::getID($creator->getAttribute('creatorType'));
         $itemObj->setCreator($pos, $creatorObj, $creatorTypeID);
         $i++;
     }
     // Remove item's remaining creators not in XML
     $numCreators = $itemObj->numCreators();
     $rem = $numCreators - $i;
     for ($j = 0; $j < $rem; $j++) {
         // Keep removing last creator
         $itemObj->removeCreator($i);
     }
     // Both notes and attachments might have parents and notes
     if ($itemTypeName == 'note' || $itemTypeName == 'attachment') {
         $sourceItemKey = $xml->getAttribute('sourceItem');
         $itemObj->setSource($sourceItemKey ? $sourceItemKey : false);
         $itemObj->setNote($xmlNote ? $xmlNote->nodeValue : "");
     }
     // Attachment metadata
     if ($itemTypeName == 'attachment') {
         $itemObj->attachmentLinkMode = (int) $xml->getAttribute('linkMode');
         $itemObj->attachmentMIMEType = $xml->getAttribute('mimeType');
         $itemObj->attachmentCharset = $xml->getAttribute('charset');
         // Cast to string to be 32-bit safe
         $storageModTime = (string) $xml->getAttribute('storageModTime');
         $itemObj->attachmentStorageModTime = $storageModTime ? $storageModTime : null;
         $storageHash = $xml->getAttribute('storageHash');
         $itemObj->attachmentStorageHash = $storageHash ? $storageHash : null;
         $itemObj->attachmentPath = $xmlPath ? $xmlPath->nodeValue : "";
     }
     // Related items
     if ($xmlRelated && $xmlRelated->nodeValue) {
         $relatedKeys = explode(' ', $xmlRelated->nodeValue);
     } else {
         $relatedKeys = array();
     }
     $itemObj->relatedItems = $relatedKeys;
     return $itemObj;
 }
Пример #2
0
 public static function updateFromJSON(Zotero_Item $item, $json, $isNew = false, Zotero_Item $parentItem = null, $userID = null)
 {
     self::validateJSONItem($json, $item->libraryID, $isNew ? null : $item, !is_null($parentItem));
     Zotero_DB::beginTransaction();
     // Mark library as updated
     if (!$isNew) {
         $timestamp = Zotero_Libraries::updateTimestamps($item->libraryID);
         Zotero_DB::registerTransactionTimestamp($timestamp);
     }
     $forceChange = false;
     $twoStage = false;
     // Set itemType first
     $item->setField("itemTypeID", Zotero_ItemTypes::getID($json->itemType));
     foreach ($json as $key => $val) {
         switch ($key) {
             case 'itemType':
                 continue;
             case 'deleted':
                 continue;
             case 'creators':
                 if (!$val && !$item->numCreators()) {
                     continue 2;
                 }
                 $orderIndex = -1;
                 foreach ($val as $orderIndex => $newCreatorData) {
                     if ((!isset($newCreatorData->name) || trim($newCreatorData->name) == "") && (!isset($newCreatorData->firstName) || trim($newCreatorData->firstName) == "") && (!isset($newCreatorData->lastName) || trim($newCreatorData->lastName) == "")) {
                         // This should never happen, because of check in validateJSONItem()
                         if (!$isNew) {
                             throw new Exception("Nameless creator in update request");
                         }
                         // On item creation, ignore creators with empty names,
                         // because that's in the item template that the API returns
                         break;
                     }
                     // JSON uses 'name' and 'firstName'/'lastName',
                     // so switch to just 'firstName'/'lastName'
                     if (isset($newCreatorData->name)) {
                         $newCreatorData->firstName = '';
                         $newCreatorData->lastName = $newCreatorData->name;
                         unset($newCreatorData->name);
                         $newCreatorData->fieldMode = 1;
                     } else {
                         $newCreatorData->fieldMode = 0;
                     }
                     $newCreatorTypeID = Zotero_CreatorTypes::getID($newCreatorData->creatorType);
                     // Same creator in this position
                     $existingCreator = $item->getCreator($orderIndex);
                     if ($existingCreator && $existingCreator['ref']->equals($newCreatorData)) {
                         // Just change the creatorTypeID
                         if ($existingCreator['creatorTypeID'] != $newCreatorTypeID) {
                             $item->setCreator($orderIndex, $existingCreator['ref'], $newCreatorTypeID);
                         }
                         continue;
                     }
                     // Same creator in a different position, so use that
                     $existingCreators = $item->getCreators();
                     for ($i = 0, $len = sizeOf($existingCreators); $i < $len; $i++) {
                         if ($existingCreators[$i]['ref']->equals($newCreatorData)) {
                             $item->setCreator($orderIndex, $existingCreators[$i]['ref'], $newCreatorTypeID);
                             continue;
                         }
                     }
                     // Make a fake creator to use for the data lookup
                     $newCreator = new Zotero_Creator();
                     $newCreator->libraryID = $item->libraryID;
                     foreach ($newCreatorData as $key => $val) {
                         if ($key == 'creatorType') {
                             continue;
                         }
                         $newCreator->{$key} = $val;
                     }
                     // Look for an equivalent creator in this library
                     $candidates = Zotero_Creators::getCreatorsWithData($item->libraryID, $newCreator, true);
                     if ($candidates) {
                         $c = Zotero_Creators::get($item->libraryID, $candidates[0]);
                         $item->setCreator($orderIndex, $c, $newCreatorTypeID);
                         continue;
                     }
                     // None found, so make a new one
                     $creatorID = $newCreator->save();
                     $newCreator = Zotero_Creators::get($item->libraryID, $creatorID);
                     $item->setCreator($orderIndex, $newCreator, $newCreatorTypeID);
                 }
                 // Remove all existing creators above the current index
                 if (!$isNew && ($indexes = array_keys($item->getCreators()))) {
                     $i = max($indexes);
                     while ($i > $orderIndex) {
                         $item->removeCreator($i);
                         $i--;
                     }
                 }
                 break;
             case 'tags':
                 // If item isn't yet saved, add tags below
                 if (!$item->id) {
                     $twoStage = true;
                     break;
                 }
                 if ($item->setTags($val)) {
                     $forceChange = true;
                 }
                 break;
             case 'attachments':
             case 'notes':
                 if (!$val) {
                     continue;
                 }
                 $twoStage = true;
                 break;
             case 'note':
                 $item->setNote($val);
                 break;
                 // Attachment properties
             // Attachment properties
             case 'linkMode':
                 $item->attachmentLinkMode = Zotero_Attachments::linkModeNameToNumber($val, true);
                 break;
             case 'contentType':
             case 'charset':
             case 'filename':
                 $k = "attachment" . ucwords($key);
                 $item->{$k} = $val;
                 break;
             case 'md5':
                 $item->attachmentStorageHash = $val;
                 break;
             case 'mtime':
                 $item->attachmentStorageModTime = $val;
                 break;
             default:
                 $item->setField($key, $val);
                 break;
         }
     }
     if ($parentItem) {
         $item->setSource($parentItem->id);
     }
     $item->deleted = !empty($json->deleted);
     // For changes that don't register as changes internally, force a dateModified update
     if ($forceChange) {
         $item->setField('dateModified', Zotero_DB::getTransactionTimestamp());
     }
     $item->save($userID);
     // Additional steps that have to be performed on a saved object
     if ($twoStage) {
         foreach ($json as $key => $val) {
             switch ($key) {
                 case 'attachments':
                     if (!$val) {
                         continue;
                     }
                     foreach ($val as $attachment) {
                         $childItem = new Zotero_Item();
                         $childItem->libraryID = $item->libraryID;
                         self::updateFromJSON($childItem, $attachment, true, $item, $userID);
                     }
                     break;
                 case 'notes':
                     if (!$val) {
                         continue;
                     }
                     $noteItemTypeID = Zotero_ItemTypes::getID("note");
                     foreach ($val as $note) {
                         $childItem = new Zotero_Item();
                         $childItem->libraryID = $item->libraryID;
                         $childItem->itemTypeID = $noteItemTypeID;
                         $childItem->setSource($item->id);
                         $childItem->setNote($note->note);
                         $childItem->save();
                     }
                     break;
                 case 'tags':
                     if ($item->setTags($val)) {
                         $forceChange = true;
                     }
                     break;
             }
         }
         // For changes that don't register as changes internally, force a dateModified update
         if ($forceChange) {
             $item->setField('dateModified', Zotero_DB::getTransactionTimestamp());
         }
         $item->save($userID);
     }
     Zotero_DB::commit();
 }