Exemplo n.º 1
0
 protected function handleObjectWrite($objectType, $obj = null)
 {
     if (!is_object($obj) && !is_null($obj)) {
         throw new Exception('$obj must be a data object or null');
     }
     $objectTypePlural = \Zotero\DataObjectUtilities::getObjectTypePlural($objectType);
     $objectsClassName = "Zotero_" . ucwords($objectTypePlural);
     $json = !empty($this->body) ? $this->jsonDecode($this->body) : false;
     $objectVersionValidated = $this->checkSingleObjectWriteVersion($objectType, $obj, $json);
     $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID);
     // Update item
     if ($this->method == 'PUT' || $this->method == 'PATCH') {
         if ($this->apiVersion < 2) {
             $this->allowMethods(['PUT']);
         }
         if (!$obj) {
             $className = "Zotero_" . ucwords($objectType);
             $obj = new $className();
             $obj->libraryID = $this->objectLibraryID;
             $obj->key = $this->objectKey;
         }
         if ($objectType == 'item') {
             $changed = Zotero_Items::updateFromJSON($obj, $json, null, $this->queryParams, $this->userID, $objectVersionValidated ? 0 : 2, $this->method == 'PATCH');
         } else {
             $changed = $objectsClassName::updateFromJSON($obj, $json, $this->queryParams, $this->userID, $objectVersionValidated ? 0 : 2, $this->method == 'PATCH');
         }
         // If not updated, return the original library version
         if (!$changed) {
             $this->libraryVersion = Zotero_Libraries::getOriginalVersion($this->objectLibraryID);
         }
         if ($cacheKey = $this->getWriteTokenCacheKey()) {
             Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
         }
     } else {
         if ($this->method == 'DELETE') {
             $objectsClassName::delete($this->objectLibraryID, $this->objectKey);
         } else {
             throw new Exception("Unexpected method {$this->method}");
         }
     }
     if ($this->apiVersion >= 2 || $this->method == 'DELETE') {
         $this->e204();
     }
     return $obj;
 }
Exemplo n.º 2
0
 public function items()
 {
     if (($this->method == 'POST' || $this->method == 'PUT') && !$this->body) {
         $this->e400("{$this->method} data not provided");
     }
     $itemIDs = array();
     $responseItems = array();
     $responseKeys = array();
     $totalResults = null;
     //
     // Single item
     //
     if (($this->objectID || $this->objectKey) && !$this->subset) {
         if ($this->fileMode) {
             if ($this->fileView) {
                 $this->allowMethods(array('GET', 'HEAD', 'POST'));
             } else {
                 $this->allowMethods(array('GET', 'PUT', 'POST', 'HEAD', 'PATCH'));
             }
         } else {
             $this->allowMethods(array('GET', 'PUT', 'DELETE'));
         }
         // Check for general library access
         if (!$this->permissions->canAccess($this->objectLibraryID)) {
             //var_dump($this->objectLibraryID);
             //var_dump($this->permissions);
             $this->e403();
         }
         if ($this->objectKey) {
             $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
         } else {
             try {
                 $item = Zotero_Items::get($this->objectLibraryID, $this->objectID);
             } catch (Exception $e) {
                 if ($e->getCode() == Z_ERROR_OBJECT_LIBRARY_MISMATCH) {
                     $item = false;
                 } else {
                     throw $e;
                 }
             }
         }
         if (!$item) {
             // Possibly temporary workaround to block unnecessary full syncs
             if ($this->fileMode && $this->method == 'POST') {
                 // If > 2 requests for missing file, trigger a full sync via 404
                 $cacheKey = "apiMissingFile_" . $this->objectLibraryID . "_" . ($this->objectKey ? $this->objectKey : $this->objectID);
                 $set = Z_Core::$MC->get($cacheKey);
                 if (!$set) {
                     Z_Core::$MC->set($cacheKey, 1, 86400);
                 } else {
                     if ($set < 2) {
                         Z_Core::$MC->increment($cacheKey);
                     } else {
                         Z_Core::$MC->delete($cacheKey);
                         $this->e404("A file sync error occurred. Please sync again.");
                     }
                 }
                 $this->e500("A file sync error occurred. Please sync again.");
             }
             // If we have an id, make sure this isn't really an all-numeric key
             if ($this->objectID && strlen($this->objectID) == 8 && preg_match('/[0-9]{8}/', $this->objectID)) {
                 $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectID);
                 if ($item) {
                     $this->objectKey = $this->objectID;
                     unset($this->objectID);
                 }
             }
             if (!$item) {
                 $this->e404("Item does not exist");
             }
         }
         if ($item->isNote() && !$this->permissions->canAccess($this->objectLibraryID, 'notes')) {
             $this->e403();
         }
         // Make sure URL libraryID matches item libraryID
         if ($this->objectLibraryID != $item->libraryID) {
             $this->e404("Item does not exist");
         }
         // File access mode
         if ($this->fileMode) {
             $this->_handleFileRequest($item);
         }
         // If id, redirect to key URL
         if ($this->objectID) {
             $this->allowMethods(array('GET'));
             $qs = !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
             header("Location: " . Zotero_API::getItemURI($item) . $qs);
             exit;
         }
         if ($this->scopeObject) {
             switch ($this->scopeObject) {
                 // Remove item from collection
                 case 'collections':
                     $this->allowMethods(array('DELETE'));
                     if (!$this->permissions->canWrite($this->objectLibraryID)) {
                         $this->e403("Write access denied");
                     }
                     $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$collection) {
                         $this->e404("Collection not found");
                     }
                     if (!$collection->hasItem($item->id)) {
                         $this->e404("Item not found in collection");
                     }
                     Zotero_DB::beginTransaction();
                     $timestamp = Zotero_Libraries::updateTimestamps($this->objectLibraryID);
                     Zotero_DB::registerTransactionTimestamp($timestamp);
                     $collection->removeItem($item->id);
                     Zotero_DB::commit();
                     $this->e204();
                 default:
                     $this->e400();
             }
         }
         if ($this->method == 'PUT' || $this->method == 'DELETE') {
             if (!$this->permissions->canWrite($this->objectLibraryID)) {
                 $this->e403("Write access denied");
             }
             if (!Z_CONFIG::$TESTING_SITE || empty($_GET['skipetag'])) {
                 if (empty($_SERVER['HTTP_IF_MATCH'])) {
                     $this->e400("If-Match header not provided");
                 }
                 if (!preg_match('/^"?([a-f0-9]{32})"?$/', $_SERVER['HTTP_IF_MATCH'], $matches)) {
                     $this->e400("Invalid ETag in If-Match header");
                 }
                 if ($item->etag != $matches[1]) {
                     $this->e412("ETag does not match current version of item");
                 }
             }
             // Update existing item
             if ($this->method == 'PUT') {
                 $obj = $this->jsonDecode($this->body);
                 Zotero_Items::updateFromJSON($item, $obj, false, null, $this->userID);
                 $this->queryParams['format'] = 'atom';
                 $this->queryParams['content'] = array('json');
                 if ($cacheKey = $this->getWriteTokenCacheKey()) {
                     Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                 }
             } else {
                 Zotero_Items::delete($this->objectLibraryID, $this->objectKey, true);
                 try {
                     Zotero_Processors::notifyProcessors('index');
                 } catch (Exception $e) {
                     Z_Core::logError($e);
                 }
                 $this->e204();
             }
         }
         // Display item
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_Items::convertItemToAtom($item, $this->queryParams, $this->apiVersion, $this->permissions);
                 break;
             case 'bib':
                 echo Zotero_Cite::getBibliographyFromCitationServer(array($item), $this->queryParams['style'], $this->queryParams['css']);
                 exit;
             case 'csljson':
                 $json = Zotero_Cite::getJSONFromItems(array($item), true);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                     $json = Zotero_Utilities::json_encode_pretty($json);
                 } else {
                     header("Content-Type: application/vnd.citationstyles.csl+json");
                     $json = json_encode($json);
                 }
                 echo $json;
                 exit;
             default:
                 $export = Zotero_Translate::doExport(array($item), $this->queryParams['format']);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                 } else {
                     header("Content-Type: " . $export['mimeType']);
                 }
                 echo $export['body'];
                 exit;
         }
     } else {
         $this->allowMethods(array('GET', 'POST'));
         if (!$this->permissions->canAccess($this->objectLibraryID)) {
             $this->e403();
         }
         $includeTrashed = false;
         $formatAsKeys = $this->queryParams['format'] == 'keys';
         if ($this->scopeObject) {
             $this->allowMethods(array('GET', 'POST'));
             // If id, redirect to key URL
             if ($this->scopeObjectID) {
                 $this->allowMethods(array('GET'));
                 if (!in_array($this->scopeObject, array("collections", "tags"))) {
                     $this->e400();
                 }
                 $className = 'Zotero_' . ucwords($this->scopeObject);
                 $obj = call_user_func(array($className, 'get'), $this->objectLibraryID, $this->scopeObjectID);
                 if (!$obj) {
                     $this->e404("Scope " . substr($this->scopeObject, 0, -1) . " not found");
                 }
                 $base = call_user_func(array('Zotero_API', 'get' . substr(ucwords($this->scopeObject), 0, -1) . 'URI'), $obj);
                 $qs = !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
                 header("Location: " . $base . "/items" . $qs);
                 exit;
             }
             switch ($this->scopeObject) {
                 case 'collections':
                     $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$collection) {
                         $this->e404("Collection not found");
                     }
                     // Add items to collection
                     if ($this->method == 'POST') {
                         if (!$this->permissions->canWrite($this->objectLibraryID)) {
                             $this->e403("Write access denied");
                         }
                         Zotero_DB::beginTransaction();
                         $timestamp = Zotero_Libraries::updateTimestamps($this->objectLibraryID);
                         Zotero_DB::registerTransactionTimestamp($timestamp);
                         $itemKeys = explode(' ', $this->body);
                         $itemIDs = array();
                         foreach ($itemKeys as $key) {
                             try {
                                 $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $key);
                             } catch (Exception $e) {
                                 if ($e->getCode() == Z_ERROR_OBJECT_LIBRARY_MISMATCH) {
                                     $item = false;
                                 } else {
                                     throw $e;
                                 }
                             }
                             if (!$item) {
                                 throw new Exception("Item '{$key}' not found in library", Z_ERROR_INVALID_INPUT);
                             }
                             if ($item->getSource()) {
                                 throw new Exception("Child items cannot be added to collections directly", Z_ERROR_INVALID_INPUT);
                             }
                             $itemIDs[] = $item->id;
                         }
                         $collection->addItems($itemIDs);
                         Zotero_DB::commit();
                         $this->e204();
                     }
                     $title = "Items in Collection ‘" . $collection->name . "’";
                     $itemIDs = $collection->getChildItems();
                     break;
                 case 'tags':
                     $this->allowMethods(array('GET'));
                     $tagIDs = Zotero_Tags::getIDs($this->objectLibraryID, $this->scopeObjectName);
                     if (!$tagIDs) {
                         $this->e404("Tag not found");
                     }
                     $itemIDs = array();
                     $title = '';
                     foreach ($tagIDs as $tagID) {
                         $tag = new Zotero_Tag();
                         $tag->libraryID = $this->objectLibraryID;
                         $tag->id = $tagID;
                         // Use a real tag name, in case case differs
                         if (!$title) {
                             $title = "Items of Tag ‘" . $tag->name . "’";
                         }
                         $itemIDs = array_merge($itemIDs, $tag->getLinkedItems(true));
                     }
                     $itemIDs = array_unique($itemIDs);
                     break;
                 default:
                     throw new Exception("Invalid items scope object '{$this->scopeObject}'");
             }
         } else {
             // Top-level items
             if ($this->subset == 'top') {
                 $this->allowMethods(array('GET'));
                 $title = "Top-Level Items";
                 $results = Zotero_Items::search($this->objectLibraryID, true, $this->queryParams, false, $formatAsKeys);
             } else {
                 if ($this->subset == 'trash') {
                     $this->allowMethods(array('GET'));
                     $title = "Deleted Items";
                     $itemIDs = Zotero_Items::getDeleted($this->objectLibraryID, true);
                     $includeTrashed = true;
                 } else {
                     if ($this->subset == 'children') {
                         // If we have an id, make sure this isn't really an all-numeric key
                         if ($this->objectID && strlen($this->objectID) == 8 && preg_match('/[0-9]{8}/', $this->objectID)) {
                             $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectID);
                             if ($item) {
                                 $this->objectKey = $this->objectID;
                                 unset($this->objectID);
                             }
                         }
                         // If id, redirect to key URL
                         if ($this->objectID) {
                             $this->allowMethods(array('GET'));
                             $item = Zotero_Items::get($this->objectLibraryID, $this->objectID);
                             if (!$item) {
                                 $this->e404("Item not found");
                             }
                             $qs = !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
                             header("Location: " . Zotero_API::getItemURI($item) . '/children' . $qs);
                             exit;
                         }
                         $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
                         if (!$item) {
                             $this->e404("Item not found");
                         }
                         // Create new child items
                         if ($this->method == 'POST') {
                             if (!$this->permissions->canWrite($this->objectLibraryID)) {
                                 $this->e403("Write access denied");
                             }
                             $obj = $this->jsonDecode($this->body);
                             $keys = Zotero_Items::addFromJSON($obj, $this->objectLibraryID, $item, $this->userID);
                             if ($cacheKey = $this->getWriteTokenCacheKey()) {
                                 Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                             }
                             $uri = Zotero_API::getItemURI($item) . "/children";
                             $queryString = "itemKey=" . urlencode(implode(",", $keys)) . "&content=json";
                             if ($this->apiKey) {
                                 $queryString .= "&key=" . $this->apiKey;
                             }
                             $uri .= "?" . $queryString;
                             $this->responseCode = 201;
                             $this->queryParams = Zotero_API::parseQueryParams($queryString, $this->action, false);
                         }
                         // Display items
                         $title = "Child Items of ‘" . $item->getDisplayTitle() . "’";
                         $notes = $item->getNotes();
                         $attachments = $item->getAttachments();
                         $itemIDs = array_merge($notes, $attachments);
                     } else {
                         // Create new items
                         if ($this->method == 'POST') {
                             if (!$this->permissions->canWrite($this->objectLibraryID)) {
                                 $this->e403("Write access denied");
                             }
                             $obj = $this->jsonDecode($this->body);
                             if (isset($obj->url)) {
                                 $response = Zotero_Items::addFromURL($obj, $this->objectLibraryID, $this->userID, $this->getTranslationToken());
                                 if ($response instanceof stdClass) {
                                     header("Content-Type: application/json");
                                     echo json_encode($response->select);
                                     $this->e300();
                                 } else {
                                     if (is_int($response)) {
                                         switch ($response) {
                                             case 501:
                                                 $this->e501("No translators found for URL");
                                                 break;
                                             default:
                                                 $this->e500("Error translating URL");
                                         }
                                     } else {
                                         $keys = $response;
                                     }
                                 }
                             } else {
                                 $keys = Zotero_Items::addFromJSON($obj, $this->objectLibraryID, null, $this->userID);
                             }
                             if (!$keys) {
                                 throw new Exception("No items added");
                             }
                             if ($cacheKey = $this->getWriteTokenCacheKey()) {
                                 Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                             }
                             $uri = Zotero_API::getItemsURI($this->objectLibraryID);
                             $queryString = "itemKey=" . urlencode(implode(",", $keys)) . "&content=json";
                             if ($this->apiKey) {
                                 $queryString .= "&key=" . $this->apiKey;
                             }
                             $uri .= "?" . $queryString;
                             $this->responseCode = 201;
                             $this->queryParams = Zotero_API::parseQueryParams($queryString, $this->action, false);
                         }
                         $title = "Items";
                         $results = Zotero_Items::search($this->objectLibraryID, false, $this->queryParams, false, $formatAsKeys);
                     }
                 }
             }
             if (!empty($results)) {
                 if ($formatAsKeys) {
                     $responseKeys = $results['keys'];
                 } else {
                     $responseItems = $results['items'];
                 }
                 $totalResults = $results['total'];
             }
         }
         if ($this->queryParams['format'] == 'bib') {
             if (($itemIDs ? sizeOf($itemIDs) : $results['total']) > Zotero_API::$maxBibliographyItems) {
                 $this->e413("Cannot generate bibliography with more than " . Zotero_API::$maxBibliographyItems . " items");
             }
         }
         if ($itemIDs) {
             $this->queryParams['itemIDs'] = $itemIDs;
             $results = Zotero_Items::search($this->objectLibraryID, false, $this->queryParams, $includeTrashed, $formatAsKeys);
             if ($formatAsKeys) {
                 $responseKeys = $results['keys'];
             } else {
                 $responseItems = $results['items'];
             }
             $totalResults = $results['total'];
         } else {
             if (!isset($results)) {
                 if ($formatAsKeys) {
                     $responseKeys = array();
                 } else {
                     $responseItems = array();
                 }
                 $totalResults = 0;
             }
         }
         // Remove notes if not user and not public
         for ($i = 0; $i < sizeOf($responseItems); $i++) {
             if ($responseItems[$i]->isNote() && !$this->permissions->canAccess($responseItems[$i]->libraryID, 'notes')) {
                 array_splice($responseItems, $i, 1);
                 $totalResults--;
                 $i--;
             }
         }
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_Atom::createAtomFeed($this->getFeedNamePrefix($this->objectLibraryID) . $title, $this->uri, $responseItems, $totalResults, $this->queryParams, $this->apiVersion, $this->permissions);
                 break;
             case 'bib':
                 echo Zotero_Cite::getBibliographyFromCitationServer($responseItems, $this->queryParams['style'], $this->queryParams['css']);
                 exit;
             case 'csljson':
                 $json = Zotero_Cite::getJSONFromItems($responseItems, true);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                     $json = Zotero_Utilities::json_encode_pretty($json);
                 } else {
                     header("Content-Type: application/vnd.citationstyles.csl+json");
                     $json = json_encode($json);
                 }
                 echo $json;
                 exit;
             case 'keys':
                 if (!$formatAsKeys) {
                     $responseKeys = array();
                     foreach ($responseItems as $item) {
                         $responseKeys[] = $item->key;
                     }
                 }
                 header("Content-Type: text/plain");
                 echo implode("\n", $responseKeys) . "\n";
                 exit;
             default:
                 $export = Zotero_Translate::doExport($responseItems, $this->queryParams['format']);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                 } else {
                     header("Content-Type: " . $export['mimeType']);
                 }
                 echo $export['body'];
                 exit;
         }
     }
     $this->end();
 }