Exemplo n.º 1
0
 private static function processUploadInternal($userID, SimpleXMLElement $xml, $syncQueueID = null, $syncProcessID = null)
 {
     $userLibraryID = Zotero_Users::getLibraryIDFromUserID($userID);
     $affectedLibraries = self::parseAffectedLibraries($xml->asXML());
     // Relations-only uploads don't have affected libraries
     if (!$affectedLibraries) {
         $affectedLibraries = array(Zotero_Users::getLibraryIDFromUserID($userID));
     }
     $processID = self::addUploadProcess($userID, $affectedLibraries, $syncQueueID, $syncProcessID);
     set_time_limit(5400);
     $profile = false;
     if ($profile) {
         $shardID = Zotero_Shards::getByUserID($userID);
         Zotero_DB::profileStart($shardID);
     }
     try {
         Z_Core::$MC->begin();
         Zotero_DB::beginTransaction();
         // Mark libraries as updated
         $timestamp = Zotero_Libraries::updateTimestamps($affectedLibraries);
         Zotero_DB::registerTransactionTimestamp($timestamp);
         // Make sure no other upload sessions use this same timestamp
         // for any of these libraries, since we return >= 1 as the next
         // last sync time
         if (!Zotero_Libraries::setTimestampLock($affectedLibraries, $timestamp)) {
             throw new Exception("Library timestamp already used", Z_ERROR_LIBRARY_TIMESTAMP_ALREADY_USED);
         }
         // Add/update creators
         if ($xml->creators) {
             // DOM
             $keys = array();
             $xmlElements = dom_import_simplexml($xml->creators);
             $xmlElements = $xmlElements->getElementsByTagName('creator');
             Zotero_DB::query("SET foreign_key_checks = 0");
             try {
                 $addedLibraryIDs = array();
                 $addedCreatorDataHashes = array();
                 foreach ($xmlElements as $xmlElement) {
                     $key = $xmlElement->getAttribute('key');
                     if (isset($keys[$key])) {
                         throw new Exception("Creator {$key} already processed");
                     }
                     $keys[$key] = true;
                     $creatorObj = Zotero_Creators::convertXMLToCreator($xmlElement);
                     $addedLibraryIDs[] = $creatorObj->libraryID;
                     $creatorObj->save();
                 }
             } catch (Exception $e) {
                 Zotero_DB::query("SET foreign_key_checks = 1");
                 throw $e;
             }
             Zotero_DB::query("SET foreign_key_checks = 1");
             unset($keys);
             unset($xml->creators);
             //
             // Manual foreign key checks
             //
             // libraryID
             foreach ($addedLibraryIDs as $addedLibraryID) {
                 $shardID = Zotero_Shards::getByLibraryID($addedLibraryID);
                 $sql = "SELECT COUNT(*) FROM shardLibraries WHERE libraryID=?";
                 if (!Zotero_DB::valueQuery($sql, $addedLibraryID, $shardID)) {
                     throw new Exception("libraryID inserted into `creators` not found in `shardLibraries` ({$addedLibraryID}, {$shardID})");
                 }
             }
         }
         // Add/update items
         if ($xml->items) {
             $childItems = array();
             $relatedItemsStore = array();
             // DOM
             $keys = array();
             $xmlElements = dom_import_simplexml($xml->items);
             $xmlElements = $xmlElements->getElementsByTagName('item');
             foreach ($xmlElements as $xmlElement) {
                 $key = $xmlElement->getAttribute('key');
                 if (isset($keys[$key])) {
                     throw new Exception("Item {$key} already processed");
                 }
                 $keys[$key] = true;
                 $missing = Zotero_Items::removeMissingRelatedItems($xmlElement);
                 $itemObj = Zotero_Items::convertXMLToItem($xmlElement);
                 if ($missing) {
                     $relatedItemsStore[$itemObj->libraryID . '_' . $itemObj->key] = $missing;
                 }
                 if (!$itemObj->getSourceKey()) {
                     try {
                         $itemObj->save($userID);
                     } catch (Exception $e) {
                         if (strpos($e->getMessage(), 'libraryIDs_do_not_match') !== false) {
                             throw new Exception($e->getMessage() . " (" . $itemObj->key . ")");
                         }
                         throw $e;
                     }
                 } else {
                     $childItems[] = $itemObj;
                 }
             }
             unset($keys);
             unset($xml->items);
             while ($childItem = array_shift($childItems)) {
                 $childItem->save($userID);
             }
             // Add back related items (which now exist)
             foreach ($relatedItemsStore as $itemLibraryKey => $relset) {
                 $lk = explode('_', $itemLibraryKey);
                 $libraryID = $lk[0];
                 $key = $lk[1];
                 $item = Zotero_Items::getByLibraryAndKey($libraryID, $key);
                 foreach ($relset as $relKey) {
                     $relItem = Zotero_Items::getByLibraryAndKey($libraryID, $relKey);
                     $item->addRelatedItem($relItem->id);
                 }
                 $item->save();
             }
             unset($relatedItemsStore);
         }
         // Add/update collections
         if ($xml->collections) {
             $collections = array();
             $collectionSets = array();
             // DOM
             // Build an array of unsaved collection objects and the keys of child items
             $keys = array();
             $xmlElements = dom_import_simplexml($xml->collections);
             $xmlElements = $xmlElements->getElementsByTagName('collection');
             foreach ($xmlElements as $xmlElement) {
                 $key = $xmlElement->getAttribute('key');
                 if (isset($keys[$key])) {
                     throw new Exception("Collection {$key} already processed");
                 }
                 $keys[$key] = true;
                 $collectionObj = Zotero_Collections::convertXMLToCollection($xmlElement);
                 $xmlItems = $xmlElement->getElementsByTagName('items')->item(0);
                 // Fix an error if there's leading or trailing whitespace,
                 // which was possible in 2.0.3
                 if ($xmlItems) {
                     $xmlItems = trim($xmlItems->nodeValue);
                 }
                 $arr = array('obj' => $collectionObj, 'items' => $xmlItems ? explode(' ', $xmlItems) : array());
                 $collections[] = $collectionObj;
                 $collectionSets[] = $arr;
             }
             unset($keys);
             unset($xml->collections);
             self::saveCollections($collections);
             unset($collections);
             // Set child items
             foreach ($collectionSets as $collection) {
                 // Child items
                 if (isset($collection['items'])) {
                     $ids = array();
                     foreach ($collection['items'] as $key) {
                         $item = Zotero_Items::getByLibraryAndKey($collection['obj']->libraryID, $key);
                         if (!$item) {
                             throw new Exception("Child item '{$key}' of collection {$collection['obj']->id} not found", Z_ERROR_ITEM_NOT_FOUND);
                         }
                         $ids[] = $item->id;
                     }
                     $collection['obj']->setChildItems($ids);
                 }
             }
             unset($collectionSets);
         }
         // Add/update saved searches
         if ($xml->searches) {
             $searches = array();
             $keys = array();
             foreach ($xml->searches->search as $xmlElement) {
                 $key = (string) $xmlElement['key'];
                 if (isset($keys[$key])) {
                     throw new Exception("Search {$key} already processed");
                 }
                 $keys[$key] = true;
                 $searchObj = Zotero_Searches::convertXMLToSearch($xmlElement);
                 $searchObj->save();
             }
             unset($xml->searches);
         }
         // Add/update tags
         if ($xml->tags) {
             $keys = array();
             // DOM
             $xmlElements = dom_import_simplexml($xml->tags);
             $xmlElements = $xmlElements->getElementsByTagName('tag');
             foreach ($xmlElements as $xmlElement) {
                 $key = $xmlElement->getAttribute('key');
                 if (isset($keys[$key])) {
                     throw new Exception("Tag {$key} already processed");
                 }
                 $keys[$key] = true;
                 $tagObj = Zotero_Tags::convertXMLToTag($xmlElement);
                 $tagObj->save(true);
             }
             unset($keys);
             unset($xml->tags);
         }
         // Add/update relations
         if ($xml->relations) {
             // DOM
             $xmlElements = dom_import_simplexml($xml->relations);
             $xmlElements = $xmlElements->getElementsByTagName('relation');
             foreach ($xmlElements as $xmlElement) {
                 $relationObj = Zotero_Relations::convertXMLToRelation($xmlElement, $userLibraryID);
                 if ($relationObj->exists()) {
                     continue;
                 }
                 $relationObj->save();
             }
             unset($keys);
             unset($xml->relations);
         }
         // TODO: loop
         if ($xml->deleted) {
             // Delete collections
             if ($xml->deleted->collections) {
                 Zotero_Collections::deleteFromXML($xml->deleted->collections);
             }
             // Delete items
             if ($xml->deleted->items) {
                 Zotero_Items::deleteFromXML($xml->deleted->items);
             }
             // Delete creators
             if ($xml->deleted->creators) {
                 Zotero_Creators::deleteFromXML($xml->deleted->creators);
             }
             // Delete saved searches
             if ($xml->deleted->searches) {
                 Zotero_Searches::deleteFromXML($xml->deleted->searches);
             }
             // Delete tags
             if ($xml->deleted->tags) {
                 Zotero_Tags::deleteFromXML($xml->deleted->tags);
             }
             // Delete tags
             if ($xml->deleted->relations) {
                 Zotero_Relations::deleteFromXML($xml->deleted->relations);
             }
         }
         self::removeUploadProcess($processID);
         Zotero_DB::commit();
         Z_Core::$MC->commit();
         if ($profile) {
             $shardID = Zotero_Shards::getByUserID($userID);
             Zotero_DB::profileEnd($shardID);
         }
         // Return timestamp + 1, to keep the next /updated call
         // (using >= timestamp) from returning this data
         return $timestamp + 1;
     } catch (Exception $e) {
         Z_Core::$MC->rollback();
         Zotero_DB::rollback(true);
         self::removeUploadProcess($processID);
         throw $e;
     }
 }