Exemplo n.º 1
0
 public static function getDownloadDetails($item)
 {
     // TODO: get attachment link mode value from somewhere
     if (!$item->isAttachment() || !$item->isImportedAttachment()) {
         return false;
     }
     $sql = "SELECT storageFileID FROM storageFileItems WHERE itemID=?";
     $storageFileID = Zotero_DB::valueQuery($sql, $item->id, Zotero_Shards::getByLibraryID($item->libraryID));
     if (!$storageFileID) {
         return false;
     }
     $url = Zotero_API::getItemURI($item) . "/file";
     $info = self::getFileInfoByID($storageFileID);
     if ($info['zip']) {
         return array('url' => $url . "/view");
     } else {
         return array('url' => $url, 'filename' => $info['filename'], 'size' => $info['size']);
     }
 }
Exemplo n.º 2
0
 public function collections()
 {
     // Check for general library access
     if (!$this->permissions->canAccess($this->objectLibraryID)) {
         $this->e403();
     }
     if ($this->isWriteMethod()) {
         // Check for library write access
         if (!$this->permissions->canWrite($this->objectLibraryID)) {
             $this->e403("Write access denied");
         }
         // Make sure library hasn't been modified
         if (!$this->singleObject) {
             $libraryTimestampChecked = $this->checkLibraryIfUnmodifiedSinceVersion();
         }
         Zotero_Libraries::updateVersionAndTimestamp($this->objectLibraryID);
     }
     $collectionIDs = array();
     $collectionKeys = array();
     $results = array();
     // Single collection
     if ($this->singleObject) {
         $this->allowMethods(['HEAD', 'GET', 'PUT', 'PATCH', 'DELETE']);
         if (!Zotero_ID::isValidKey($this->objectKey)) {
             $this->e404();
         }
         $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
         if ($this->isWriteMethod()) {
             $collection = $this->handleObjectWrite('collection', $collection ? $collection : null);
             $this->queryParams['content'] = ['json'];
         }
         if (!$collection) {
             $this->e404("Collection not found");
         }
         $this->libraryVersion = $collection->version;
         if ($this->method == 'HEAD') {
             $this->end();
         }
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_Collections::convertCollectionToAtom($collection, $this->queryParams);
                 break;
             case 'json':
                 $json = $collection->toResponseJSON($this->queryParams, $this->permissions);
                 echo Zotero_Utilities::formatJSON($json);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     } else {
         $this->allowMethods(['HEAD', 'GET', 'POST', 'DELETE']);
         $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID);
         if ($this->scopeObject) {
             $this->allowMethods(array('GET'));
             switch ($this->scopeObject) {
                 case 'collections':
                     $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$collection) {
                         $this->e404("Collection not found");
                     }
                     $title = "Child Collections of ‘{$collection->name}'’";
                     $collectionIDs = $collection->getChildCollections();
                     break;
                 default:
                     throw new Exception("Invalid collections scope object '{$this->scopeObject}'");
             }
         } else {
             // Top-level items
             if ($this->subset == 'top') {
                 $this->allowMethods(array('GET'));
                 $title = "Top-Level Collections";
                 $results = Zotero_Collections::search($this->objectLibraryID, true, $this->queryParams);
             } else {
                 // Create a collection
                 if ($this->method == 'POST') {
                     $this->queryParams['format'] = 'writereport';
                     $obj = $this->jsonDecode($this->body);
                     $results = Zotero_Collections::updateMultipleFromJSON($obj, $this->queryParams, $this->objectLibraryID, $this->userID, $this->permissions, $libraryTimestampChecked ? 0 : 1, null);
                     if ($cacheKey = $this->getWriteTokenCacheKey()) {
                         Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                     }
                     if ($this->apiVersion < 2) {
                         $uri = Zotero_API::getCollectionsURI($this->objectLibraryID);
                         $keys = array_merge(get_object_vars($results['success']), get_object_vars($results['unchanged']));
                         $queryString = "collectionKey=" . urlencode(implode(",", $keys)) . "&format=atom&content=json&order=collectionKeyList&sort=asc";
                         if ($this->apiKey) {
                             $queryString .= "&key=" . $this->apiKey;
                         }
                         $uri .= "?" . $queryString;
                         $this->queryParams = Zotero_API::parseQueryParams($queryString, $this->action, true, $this->apiVersion);
                         $title = "Collections";
                         $results = Zotero_Collections::search($this->objectLibraryID, false, $this->queryParams);
                     }
                 } else {
                     if ($this->method == 'DELETE') {
                         Zotero_DB::beginTransaction();
                         foreach ($this->queryParams['collectionKey'] as $collectionKey) {
                             Zotero_Collections::delete($this->objectLibraryID, $collectionKey);
                         }
                         Zotero_DB::commit();
                         $this->e204();
                     } else {
                         $title = "Collections";
                         $results = Zotero_Collections::search($this->objectLibraryID, false, $this->queryParams);
                     }
                 }
             }
         }
         if ($collectionIDs) {
             $this->queryParams['collectionIDs'] = $collectionIDs;
             $results = Zotero_Collections::search($this->objectLibraryID, false, $this->queryParams);
         }
         $options = ['action' => $this->action, 'uri' => $this->uri, 'results' => $results, 'requestParams' => $this->queryParams, 'permissions' => $this->permissions, 'head' => $this->method == 'HEAD'];
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_API::multiResponse(array_merge($options, ['title' => $this->getFeedNamePrefix($this->objectLibraryID) . $title]));
                 break;
             case 'json':
             case 'keys':
             case 'versions':
             case 'writereport':
                 Zotero_API::multiResponse($options);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     }
     $this->end();
 }
Exemplo n.º 3
0
 public function itemToAtom($itemID)
 {
     if (!is_int($itemID)) {
         throw new Exception("itemID must be an integer (was " . gettype($itemID) . ")");
     }
     if (!$this->loaded) {
         $this->load();
     }
     //$groupUserData = $this->getUserData($itemID);
     $item = Zotero_Items::get($this->libraryID, $itemID);
     if (!$item) {
         throw new Exception("Item {$itemID} doesn't exist");
     }
     $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>' . '<entry xmlns="' . Zotero_Atom::$nsAtom . '" ' . 'xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '" ' . 'xmlns:xfer="' . Zotero_Atom::$nsZoteroTransfer . '"/>');
     $title = $item->getDisplayTitle(true);
     $title = $title ? $title : '[Untitled]';
     // Strip HTML from note titles
     if ($item->isNote()) {
         // Clean and strip HTML, giving us an HTML-encoded plaintext string
         $title = strip_tags(Zotero_Notes::sanitize($title));
         // Unencode plaintext string
         $title = html_entity_decode($title);
     }
     $xml->title = $title;
     $author = $xml->addChild('author');
     $author->name = Zotero_Libraries::getName($item->libraryID);
     $author->uri = Zotero_URI::getLibraryURI($item->libraryID);
     $xml->id = Zotero_URI::getItemURI($item);
     $xml->published = Zotero_Date::sqlToISO8601($item->dateAdded);
     $xml->updated = Zotero_Date::sqlToISO8601($item->dateModified);
     $link = $xml->addChild("link");
     $link['rel'] = "self";
     $link['type'] = "application/atom+xml";
     $link['href'] = Zotero_API::getItemURI($item);
     $link = $xml->addChild('link');
     $link['rel'] = 'alternate';
     $link['type'] = 'text/html';
     $link['href'] = Zotero_URI::getItemURI($item, true);
     $xml->content['type'] = 'application/xml';
     $itemXML = new SimpleXMLElement('<item xmlns="' . Zotero_Atom::$nsZoteroTransfer . '"/>');
     // This method of adding the element seems to be necessary to get the
     // namespace prefix to show up
     $fNode = dom_import_simplexml($xml->content);
     $subNode = dom_import_simplexml($itemXML);
     $importedNode = $fNode->ownerDocument->importNode($subNode, true);
     $fNode->appendChild($importedNode);
     $xml->content->item['id'] = $itemID;
     return $xml;
 }
Exemplo n.º 4
0
 public static function createAtomFeed($title, $url, $entries, $totalResults = null, $queryParams = null, $apiVersion = null, $permissions = null, $fixedValues = array())
 {
     if ($queryParams) {
         $nonDefaultParams = Zotero_API::getNonDefaultQueryParams($queryParams);
         // Convert 'content' array to sorted comma-separated string
         if (isset($nonDefaultParams['content'])) {
             $nonDefaultParams['content'] = implode(',', $nonDefaultParams['content']);
         }
     } else {
         $nonDefaultParams = array();
     }
     $feed = '<feed xmlns="' . Zotero_Atom::$nsAtom . '" ' . 'xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '"';
     if ($queryParams && $queryParams['content'][0] == 'full') {
         $feed .= ' xmlns:zxfer="' . Zotero_Atom::$nsZoteroTransfer . '"';
     }
     $feed .= '/>';
     $xml = new SimpleXMLElement($feed);
     $xml->title = $title;
     $path = parse_url($url, PHP_URL_PATH);
     // Generate canonical URI
     $zoteroURI = Zotero_URI::getBaseURI() . substr($path, 1);
     if ($nonDefaultParams) {
         $zoteroURI .= "?" . http_build_query($nonDefaultParams);
     }
     $atomURI = Zotero_Atom::getBaseURI() . substr($path, 1);
     //
     // Generate URIs for 'self', 'first', 'next' and 'last' links
     //
     // 'self'
     $atomSelfURI = $atomURI;
     if ($nonDefaultParams) {
         $atomSelfURI .= "?" . http_build_query($nonDefaultParams);
     }
     // 'first'
     $atomFirstURI = $atomURI;
     if ($nonDefaultParams) {
         $p = $nonDefaultParams;
         unset($p['start']);
         if ($first = http_build_query($p)) {
             $atomFirstURI .= "?" . $first;
         }
     }
     // 'last'
     if (!$queryParams['start'] && $queryParams['limit'] >= $totalResults) {
         $atomLastURI = $atomSelfURI;
     } else {
         // 'start' past results
         if ($queryParams['start'] >= $totalResults) {
             $lastStart = $totalResults - $queryParams['limit'];
         } else {
             $lastStart = $totalResults - $totalResults % $queryParams['limit'];
             if ($lastStart == $totalResults) {
                 $lastStart = $totalResults - $queryParams['limit'];
             }
         }
         $p = $nonDefaultParams;
         if ($lastStart > 0) {
             $p['start'] = $lastStart;
         } else {
             unset($p['start']);
         }
         $atomLastURI = $atomURI;
         if ($last = http_build_query($p)) {
             $atomLastURI .= "?" . $last;
         }
         // 'next'
         $nextStart = $queryParams['start'] + $queryParams['limit'];
         if ($nextStart < $totalResults) {
             $p = $nonDefaultParams;
             $p['start'] = $nextStart;
             $atomNextURI = $atomURI . "?" . http_build_query($p);
         }
     }
     $xml->id = $zoteroURI;
     $link = $xml->addChild("link");
     $link['rel'] = "self";
     $link['type'] = "application/atom+xml";
     $link['href'] = $atomSelfURI;
     $link = $xml->addChild("link");
     $link['rel'] = "first";
     $link['type'] = "application/atom+xml";
     $link['href'] = $atomFirstURI;
     if (isset($atomNextURI)) {
         $link = $xml->addChild("link");
         $link['rel'] = "next";
         $link['type'] = "application/atom+xml";
         $link['href'] = $atomNextURI;
     }
     $link = $xml->addChild("link");
     $link['rel'] = "last";
     $link['type'] = "application/atom+xml";
     $link['href'] = $atomLastURI;
     // Generate alternate URI
     $alternateURI = Zotero_URI::getBaseURI() . substr($path, 1);
     if ($nonDefaultParams) {
         $p = $nonDefaultParams;
         if (isset($p['content'])) {
             unset($p['content']);
         }
         if ($p) {
             $alternateURI .= "?" . http_build_query($p);
         }
     }
     $link = $xml->addChild("link");
     $link['rel'] = "alternate";
     $link['type'] = "text/html";
     $link['href'] = $alternateURI;
     $xml->addChild("zapi:totalResults", is_numeric($totalResults) ? $totalResults : sizeOf($entries), self::$nsZoteroAPI);
     $xml->addChild("zapi:apiVersion", $apiVersion, self::$nsZoteroAPI);
     $latestUpdated = '';
     // Get bib data using parallel requests
     $sharedData = array();
     if ($entries && $entries[0] instanceof Zotero_Item) {
         if (in_array('citation', $queryParams['content'])) {
             $sharedData["citation"] = Zotero_Cite::multiGetFromCiteServer("citation", $entries, $queryParams['style']);
         }
         if (in_array('bib', $queryParams['content'])) {
             $sharedData["bib"] = Zotero_Cite::multiGetFromCiteServer("bib", $entries, $queryParams['style']);
         }
     }
     $xmlEntries = array();
     foreach ($entries as $entry) {
         if ($entry->dateModified > $latestUpdated) {
             $latestUpdated = $entry->dateModified;
         }
         if ($entry instanceof SimpleXMLElement) {
             $xmlEntries[] = $entry;
         } else {
             if ($entry instanceof Zotero_Collection) {
                 $entry = Zotero_Collections::convertCollectionToAtom($entry, $queryParams['content'], $apiVersion);
                 $xmlEntries[] = $entry;
             } else {
                 if ($entry instanceof Zotero_Creator) {
                     $entry = Zotero_Creators::convertCreatorToAtom($entry, $queryParams['content'], $apiVersion);
                     $xmlEntries[] = $entry;
                 } else {
                     if ($entry instanceof Zotero_Item) {
                         $entry = Zotero_Items::convertItemToAtom($entry, $queryParams, $apiVersion, $permissions, $sharedData);
                         $xmlEntries[] = $entry;
                     } else {
                         if ($entry instanceof Zotero_Search) {
                             $entry = Zotero_Searches::convertSearchToAtom($entry, $queryParams['content'], $apiVersion);
                             $xmlEntries[] = $entry;
                         } else {
                             if ($entry instanceof Zotero_Tag) {
                                 $xmlEntries[] = $entry->toAtom($queryParams['content'], $apiVersion, isset($fixedValues[$entry->id]) ? $fixedValues[$entry->id] : null);
                             } else {
                                 if ($entry instanceof Zotero_Group) {
                                     $entry = $entry->toAtom($queryParams['content'], $apiVersion);
                                     $xmlEntries[] = $entry;
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($latestUpdated) {
         $xml->updated = Zotero_Date::sqlToISO8601($latestUpdated);
     } else {
         $xml->updated = str_replace("+00:00", "Z", date('c'));
     }
     // Import object XML nodes into document
     $doc = dom_import_simplexml($xml);
     foreach ($xmlEntries as $xmlEntry) {
         $subNode = dom_import_simplexml($xmlEntry);
         $importedNode = $doc->ownerDocument->importNode($subNode, true);
         $doc->appendChild($importedNode);
     }
     return $xml;
 }
Exemplo n.º 5
0
 public static function updateFromJSON(Zotero_Item $item, $json, Zotero_Item $parentItem = null, $requestParams, $userID, $requireVersion = 0, $partialUpdate = false)
 {
     $json = Zotero_API::extractEditableJSON($json);
     $exists = Zotero_API::processJSONObjectKey($item, $json, $requestParams);
     // computerProgram used 'version' instead of 'versionNumber' before v3
     if ($requestParams['v'] < 3 && isset($json->version)) {
         $json->versionNumber = $json->version;
         unset($json->version);
     }
     Zotero_API::checkJSONObjectVersion($item, $json, $requestParams, $requireVersion);
     self::validateJSONItem($json, $item->libraryID, $exists ? $item : null, $parentItem || ($exists ? !!$item->getSourceKey() : false), $requestParams, $partialUpdate && $exists);
     $changed = false;
     $twoStage = false;
     if (!Zotero_DB::transactionInProgress()) {
         Zotero_DB::beginTransaction();
         $transactionStarted = true;
     } else {
         $transactionStarted = false;
     }
     // Set itemType first
     if (isset($json->itemType)) {
         $item->setField("itemTypeID", Zotero_ItemTypes::getID($json->itemType));
     }
     $changedDateModified = false;
     foreach ($json as $key => $val) {
         switch ($key) {
             case 'key':
             case 'version':
             case 'itemKey':
             case 'itemVersion':
             case 'itemType':
             case 'deleted':
                 continue;
             case 'parentItem':
                 $item->setSourceKey($val);
                 break;
             case 'creators':
                 if (!$val && !$item->numCreators()) {
                     continue 2;
                 }
                 $orderIndex = -1;
                 foreach ($val as $newCreatorData) {
                     // 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;
                     }
                     // Skip empty creators
                     if (Zotero_Utilities::unicodeTrim($newCreatorData->firstName) === "" && Zotero_Utilities::unicodeTrim($newCreatorData->lastName) === "") {
                         break;
                     }
                     $orderIndex++;
                     $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 ($exists && ($indexes = array_keys($item->getCreators()))) {
                     $i = max($indexes);
                     while ($i > $orderIndex) {
                         $item->removeCreator($i);
                         $i--;
                     }
                 }
                 break;
             case 'tags':
                 $item->setTags($val);
                 break;
             case 'collections':
                 $item->setCollections($val);
                 break;
             case 'relations':
                 $item->setRelations($val);
                 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;
             case 'dateModified':
                 $changedDateModified = $item->setField($key, $val);
                 break;
             default:
                 $item->setField($key, $val);
                 break;
         }
     }
     if ($parentItem) {
         $item->setSource($parentItem->id);
     } else {
         if ($requestParams['v'] >= 2 && !$partialUpdate && $item->getSourceKey() && !isset($json->parentItem)) {
             $item->setSourceKey(false);
         }
     }
     $item->deleted = !empty($json->deleted);
     // If item has changed, update it with the current timestamp
     if ($item->hasChanged() && !$changedDateModified) {
         $item->dateModified = Zotero_DB::getTransactionTimestamp();
     }
     $changed = $item->save($userID) || $changed;
     // 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 $attachmentJSON) {
                         $childItem = new Zotero_Item();
                         $childItem->libraryID = $item->libraryID;
                         self::updateFromJSON($childItem, $attachmentJSON, $item, $requestParams, $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;
             }
         }
     }
     if ($transactionStarted) {
         Zotero_DB::commit();
     }
     return $changed;
 }
Exemplo n.º 6
0
 /**
  * Download file from S3, extract it if necessary, and return a temporary URL
  * pointing to the main file
  */
 public static function getTemporaryURL(Zotero_Item $item, $localOnly = false)
 {
     $extURLPrefix = Z_CONFIG::$ATTACHMENT_SERVER_URL;
     if ($extURLPrefix[strlen($extURLPrefix) - 1] != "/") {
         $extURLPrefix .= "/";
     }
     $info = Zotero_Storage::getLocalFileItemInfo($item);
     $storageFileID = $info['storageFileID'];
     $filename = $info['filename'];
     $mtime = $info['mtime'];
     $zip = $info['zip'];
     $realFilename = preg_replace("/^storage:/", "", $item->attachmentPath);
     $realFilename = self::decodeRelativeDescriptorString($realFilename);
     $realEncodedFilename = rawurlencode($realFilename);
     $docroot = Z_CONFIG::$ATTACHMENT_SERVER_DOCROOT;
     // Check memcached to see if file is already extracted
     $key = "attachmentServerString_" . $storageFileID . "_" . $mtime;
     if ($randomStr = Z_Core::$MC->get($key)) {
         Z_Core::debug("Got attachment path '{$randomStr}/{$realEncodedFilename}' from memcached");
         return $extURLPrefix . "{$randomStr}/{$realEncodedFilename}";
     }
     $localAddr = gethostbyname(gethostname());
     // See if this is an attachment host
     $index = false;
     $skipHost = false;
     for ($i = 0, $len = sizeOf(Z_CONFIG::$ATTACHMENT_SERVER_HOSTS); $i < $len; $i++) {
         $hostAddr = gethostbyname(Z_CONFIG::$ATTACHMENT_SERVER_HOSTS[$i]);
         if ($hostAddr != $localAddr) {
             continue;
         }
         // Make a HEAD request on the local static port to make sure
         // this host is actually functional
         $url = "http://" . Z_CONFIG::$ATTACHMENT_SERVER_HOSTS[$i] . ":" . Z_CONFIG::$ATTACHMENT_SERVER_STATIC_PORT . "/";
         Z_Core::debug("Making HEAD request to {$url}");
         $ch = curl_init($url);
         curl_setopt($ch, CURLOPT_NOBODY, 1);
         curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:"));
         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
         curl_setopt($ch, CURLOPT_TIMEOUT, 2);
         curl_setopt($ch, CURLOPT_HEADER, 0);
         // do not return HTTP headers
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
         $response = curl_exec($ch);
         $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
         if ($code != 200) {
             $skipHost = Z_CONFIG::$ATTACHMENT_SERVER_HOSTS[$i];
             if ($code == 0) {
                 Z_Core::logError("Error connecting to local attachments server");
             } else {
                 Z_Core::logError("Local attachments server returned {$code}");
             }
             break;
         }
         $index = $i + 1;
         break;
     }
     // If not, make an internal root request to trigger the extraction on
     // one of them and retrieve the temporary URL
     if ($index === false) {
         // Prevent redirect madness if target server doesn't think it's an
         // attachment server
         if ($localOnly) {
             throw new Exception("Internal attachments request hit a non-attachment server");
         }
         $prefix = 'http://' . Z_CONFIG::$API_SUPER_USERNAME . ":" . Z_CONFIG::$API_SUPER_PASSWORD . "@";
         $path = Zotero_API::getItemURI($item) . "/file/view?int=1";
         $path = preg_replace('/^[^:]+:\\/\\/[^\\/]+/', '', $path);
         $context = stream_context_create(array('http' => array('follow_location' => 0)));
         $url = false;
         $hosts = Z_CONFIG::$ATTACHMENT_SERVER_HOSTS;
         // Try in random order
         shuffle($hosts);
         foreach ($hosts as $host) {
             // Don't try the local host again if we know it's not working
             if ($host == $skipHost) {
                 continue;
             }
             $intURL = $prefix . $host . ":" . Z_CONFIG::$ATTACHMENT_SERVER_DYNAMIC_PORT . $path;
             Z_Core::debug("Making GET request to {$host}");
             if (file_get_contents($intURL, false, $context) !== false) {
                 foreach ($http_response_header as $header) {
                     if (preg_match('/^Location:\\s*(.+)$/', $header, $matches)) {
                         if (strpos($matches[1], $extURLPrefix) !== 0) {
                             throw new Exception("Redirect location '" . $matches[1] . "'" . " does not begin with {$extURLPrefix}");
                         }
                         return $matches[1];
                     }
                 }
             }
         }
         return false;
     }
     // If this is an attachment host, do the download/extraction inline
     // and generate a random number with an embedded host id.
     //
     // The reverse proxy routes incoming file requests to the proper hosts
     // using the embedded id.
     //
     // A cron job deletes old attachment directories
     $randomStr = rand(1000000, 2147483647);
     // Seventh number is the host id
     $randomStr = substr($randomStr, 0, 6) . $index . substr($randomStr, 6);
     // Download file
     $dir = $docroot . $randomStr . "/";
     $downloadDir = $zip ? $dir . "ztmp/" : $dir;
     Z_Core::debug("Downloading attachment to {$dir}");
     if (!mkdir($downloadDir, 0777, true)) {
         throw new Exception("Unable to create directory '{$downloadDir}'");
     }
     if ($zip) {
         $response = Zotero_Storage::downloadFile($info, $downloadDir);
     } else {
         $response = Zotero_Storage::downloadFile($info, $downloadDir, $realFilename);
     }
     if ($response) {
         if ($zip) {
             $success = self::extractZip($downloadDir . $info['filename'], $dir);
             unlink($downloadDir . $info['filename']);
             rmdir($downloadDir);
             // Make sure charset is just a string with no spaces or newlines
             if (preg_match('/^[^\\s]+/', trim($item->attachmentCharset), $matches)) {
                 $charset = $matches[0];
             } else {
                 $charset = 'Off';
             }
             file_put_contents($dir . ".htaccess", "AddDefaultCharset " . $charset);
         } else {
             $success = true;
             if (preg_match('/^[^\\s]+/', trim($item->attachmentContentType), $matches)) {
                 $contentType = $matches[0];
                 $charset = trim($item->attachmentCharset);
                 if (substr($charset, 0, 5) == 'text/' && preg_match('/^[^\\s]+/', $charset, $matches)) {
                     $contentType .= '; ' . $matches[0];
                 }
                 file_put_contents($dir . ".htaccess", "ForceType " . $contentType);
             }
         }
     }
     if (!$response || !$success) {
         return false;
     }
     Z_Core::$MC->set($key, $randomStr, self::$cacheTime);
     return $extURLPrefix . "{$randomStr}/" . $realEncodedFilename;
 }
Exemplo n.º 7
0
 public function keys()
 {
     $userID = $this->objectUserID;
     $key = $this->objectName;
     $this->allowMethods(['GET', 'POST', 'PUT', 'DELETE']);
     if ($this->method == 'GET') {
         // Single key
         if ($key) {
             $keyObj = Zotero_Keys::getByKey($key);
             if (!$keyObj) {
                 $this->e404("Key not found");
             }
             // /users/<userID>/keys/<keyID> (deprecated)
             if ($userID) {
                 // If we have a userID, make sure it matches
                 if ($keyObj->userID != $userID) {
                     $this->e404("Key not found");
                 }
             } else {
                 if ($this->apiVersion < 3) {
                     $this->e404();
                 }
             }
             if ($this->apiVersion >= 3) {
                 $json = $keyObj->toJSON();
                 // If not super-user, don't include name or recent IP addresses
                 if (!$this->permissions->isSuper()) {
                     unset($json['dateAdded']);
                     unset($json['lastUsed']);
                     unset($json['name']);
                     unset($json['recentIPs']);
                 }
                 header('application/json');
                 echo Zotero_Utilities::formatJSON($json);
             } else {
                 $this->responseXML = $keyObj->toXML();
                 // If not super-user, don't include name or recent IP addresses
                 if (!$this->permissions->isSuper()) {
                     unset($this->responseXML['dateAdded']);
                     unset($this->responseXML['lastUsed']);
                     unset($this->responseXML->name);
                     unset($this->responseXML->recentIPs);
                 }
             }
         } else {
             if (!$this->permissions->isSuper()) {
                 $this->e403();
             }
             $keyObjs = Zotero_Keys::getUserKeys($userID);
             if ($keyObjs) {
                 if ($this->apiVersion >= 3) {
                     $json = [];
                     foreach ($keyObjs as $keyObj) {
                         $json[] = $keyObj->toJSON();
                     }
                     echo Zotero_Utilities::formatJSON($json);
                 } else {
                     $xml = new SimpleXMLElement('<keys/>');
                     $domXML = dom_import_simplexml($xml);
                     foreach ($keyObjs as $keyObj) {
                         $keyXML = $keyObj->toXML();
                         $domKeyXML = dom_import_simplexml($keyXML);
                         $node = $domXML->ownerDocument->importNode($domKeyXML, true);
                         $domXML->appendChild($node);
                     }
                     $this->responseXML = $xml;
                 }
             }
         }
     } else {
         if ($this->method == 'DELETE') {
             if (!$key) {
                 $this->e400("DELETE requests must end with a key");
             }
             Zotero_DB::beginTransaction();
             $keyObj = Zotero_Keys::getByKey($key);
             if (!$keyObj) {
                 $this->e404("Key '{$key}' does not exist");
             }
             $keyObj->erase();
             Zotero_DB::commit();
             header("HTTP/1.1 204 No Content");
             exit;
         } else {
             // Require super-user for modifications
             if (!$this->permissions->isSuper()) {
                 $this->e403();
             }
             if ($this->method == 'POST') {
                 if ($key) {
                     $this->e400("POST requests cannot end with a key (did you mean PUT?)");
                 }
                 if ($this->apiVersion >= 3) {
                     $json = json_decode($this->body, true);
                     if (!$json) {
                         $this->e400("{$this->method} data is not valid JSON");
                     }
                     if (!empty($json['key'])) {
                         $this->e400("POST requests cannot contain a key in '" . $this->body . "'");
                     }
                     $fields = $this->getFieldsFromJSON($json);
                 } else {
                     try {
                         $keyXML = @new SimpleXMLElement($this->body);
                     } catch (Exception $e) {
                         $this->e400("{$this->method} data is not valid XML");
                     }
                     if (!empty($key['key'])) {
                         $this->e400("POST requests cannot contain a key in '" . $this->body . "'");
                     }
                     $fields = $this->getFieldsFromKeyXML($keyXML);
                 }
                 Zotero_DB::beginTransaction();
                 try {
                     $keyObj = new Zotero_Key();
                     $keyObj->userID = $userID;
                     foreach ($fields as $field => $val) {
                         if ($field == 'access') {
                             foreach ($val as $access) {
                                 $this->setKeyPermissions($keyObj, $access);
                             }
                         } else {
                             $keyObj->{$field} = $val;
                         }
                     }
                     $keyObj->save();
                 } catch (Exception $e) {
                     if ($e->getCode() == Z_ERROR_KEY_NAME_TOO_LONG) {
                         $this->e400($e->getMessage());
                     }
                     $this->handleException($e);
                 }
                 if ($this->apiVersion >= 3) {
                     header('application/json');
                     echo Zotero_Utilities::formatJSON($keyObj->toJSON());
                 } else {
                     $this->responseXML = $keyObj->toXML();
                 }
                 Zotero_DB::commit();
                 $url = Zotero_API::getKeyURI($keyObj);
                 $this->responseCode = 201;
                 header("Location: " . $url, false, 201);
             } else {
                 if ($this->method == 'PUT') {
                     if (!$key) {
                         $this->e400("PUT requests must end with a key (did you mean POST?)");
                     }
                     if ($this->apiVersion >= 3) {
                         $json = json_decode($this->body, true);
                         if (!$json) {
                             $this->e400("{$this->method} data is not valid JSON");
                         }
                         $fields = $this->getFieldsFromJSON($json);
                     } else {
                         try {
                             $keyXML = @new SimpleXMLElement($this->body);
                         } catch (Exception $e) {
                             $this->e400("{$this->method} data is not valid XML");
                         }
                         $fields = $this->getFieldsFromKeyXML($keyXML);
                     }
                     // Key attribute is optional, but, if it's there, make sure it matches
                     if (isset($fields['key']) && $fields['key'] != $key) {
                         $this->e400("Key '{$fields['key']}' does not match key '{$key}' from URI");
                     }
                     Zotero_DB::beginTransaction();
                     try {
                         $keyObj = Zotero_Keys::getByKey($key);
                         if (!$keyObj) {
                             $this->e404("Key '{$key}' does not exist");
                         }
                         foreach ($fields as $field => $val) {
                             if ($field == 'access') {
                                 foreach ($val as $access) {
                                     $this->setKeyPermissions($keyObj, $access);
                                 }
                             } else {
                                 $keyObj->{$field} = $val;
                             }
                         }
                         $keyObj->save();
                     } catch (Exception $e) {
                         if ($e->getCode() == Z_ERROR_KEY_NAME_TOO_LONG) {
                             $this->e400($e->getMessage());
                         }
                         $this->handleException($e);
                     }
                     if ($this->apiVersion >= 3) {
                         echo Zotero_Utilities::formatJSON($keyObj->toJSON());
                     } else {
                         $this->responseXML = $keyObj->toXML();
                     }
                     Zotero_DB::commit();
                 }
             }
         }
     }
     if ($this->apiVersion >= 3) {
         $this->end();
     } else {
         header('Content-Type: application/xml');
         $xmlstr = $this->responseXML->asXML();
         $doc = new DOMDocument('1.0');
         $doc->loadXML($xmlstr);
         $doc->formatOutput = true;
         echo $doc->saveXML();
         exit;
     }
 }
Exemplo n.º 8
0
 private function generateMultiResponse($results, $title = '')
 {
     $options = ['action' => $this->action, 'uri' => $this->uri, 'results' => $results, 'requestParams' => $this->queryParams, 'permissions' => $this->permissions, 'head' => $this->method == 'HEAD'];
     switch ($this->queryParams['format']) {
         case 'atom':
             $this->responseXML = Zotero_API::multiResponse(array_merge($options, ['title' => $this->getFeedNamePrefix($this->objectLibraryID) . $title]));
             break;
         case 'bib':
             if ($this->method == 'HEAD') {
                 break;
             }
             if (isset($results['results'])) {
                 echo Zotero_Cite::getBibliographyFromCitationServer($results['results'], $this->queryParams);
             }
             break;
         case 'csljson':
         case 'json':
         case 'keys':
         case 'versions':
         case 'writereport':
             Zotero_API::multiResponse($options);
             break;
         default:
             if ($this->method == 'HEAD') {
                 break;
             }
             $export = Zotero_Translate::doExport($results['results'], $this->queryParams['format']);
             $this->queryParams['format'] = null;
             header("Content-Type: " . $export['mimeType']);
             echo $export['body'];
     }
 }
Exemplo n.º 9
0
 public static function createAtomFeed($action, $title, $url, $entries, $totalResults = null, $queryParams = [], Zotero_Permissions $permissions = null, $fixedValues = array())
 {
     if ($queryParams) {
         $nonDefaultParams = Zotero_API::getNonDefaultParams($action, $queryParams);
     } else {
         $nonDefaultParams = [];
     }
     $feed = '<?xml version="1.0" encoding="UTF-8"?>' . '<feed xmlns="' . Zotero_Atom::$nsAtom . '" ' . 'xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '"/>';
     $xml = new SimpleXMLElement($feed);
     $xml->title = $title;
     $path = parse_url($url, PHP_URL_PATH);
     // Generate canonical URI
     $zoteroURI = Zotero_URI::getBaseURI() . substr($path, 1) . Zotero_API::buildQueryString($queryParams['v'], $action, $nonDefaultParams, ['v']);
     $baseURI = Zotero_API::getBaseURI() . substr($path, 1);
     // API version isn't included in URLs (as with the API key)
     //
     // It could alternatively be made a private parameter so that it didn't appear
     // in the Link header either, but for now it's still there.
     $excludeParams = ['v'];
     $links = Zotero_API::buildLinks($action, $path, $totalResults, $queryParams, $nonDefaultParams, $excludeParams);
     $xml->id = $zoteroURI;
     $link = $xml->addChild("link");
     $link['rel'] = "self";
     $link['type'] = "application/atom+xml";
     $link['href'] = $links['self'];
     $link = $xml->addChild("link");
     $link['rel'] = "first";
     $link['type'] = "application/atom+xml";
     $link['href'] = $links['first'];
     if (isset($links['next'])) {
         $link = $xml->addChild("link");
         $link['rel'] = "next";
         $link['type'] = "application/atom+xml";
         $link['href'] = $links['next'];
     }
     $link = $xml->addChild("link");
     $link['rel'] = "last";
     $link['type'] = "application/atom+xml";
     $link['href'] = $links['last'];
     // Generate alternate URI
     $link = $xml->addChild("link");
     $link['rel'] = "alternate";
     $link['type'] = "text/html";
     $link['href'] = $links['alternate'];
     if ($queryParams['v'] < 3) {
         $xml->addChild("zapi:totalResults", is_numeric($totalResults) ? $totalResults : sizeOf($entries), self::$nsZoteroAPI);
     }
     if ($queryParams['v'] < 2) {
         $xml->addChild("zapi:apiVersion", 1, self::$nsZoteroAPI);
     }
     $latestUpdated = '';
     // Check memcached for bib data
     $sharedData = array();
     if ($entries && $entries[0] instanceof Zotero_Item) {
         if (in_array('citation', $queryParams['content'])) {
             $sharedData["citation"] = Zotero_Cite::multiGetFromMemcached("citation", $entries, $queryParams);
         }
         if (in_array('bib', $queryParams['content'])) {
             $sharedData["bib"] = Zotero_Cite::multiGetFromMemcached("bib", $entries, $queryParams);
         }
     }
     $xmlEntries = array();
     foreach ($entries as $entry) {
         if ($entry->dateModified > $latestUpdated) {
             $latestUpdated = $entry->dateModified;
         }
         if ($entry instanceof SimpleXMLElement) {
             $xmlEntries[] = $entry;
         } else {
             if ($entry instanceof Zotero_Collection) {
                 $entry = Zotero_Collections::convertCollectionToAtom($entry, $queryParams);
                 $xmlEntries[] = $entry;
             } else {
                 if ($entry instanceof Zotero_Item) {
                     $entry = Zotero_Items::convertItemToAtom($entry, $queryParams, $permissions, $sharedData);
                     $xmlEntries[] = $entry;
                 } else {
                     if ($entry instanceof Zotero_Search) {
                         $entry = $entry->toAtom($queryParams);
                         $xmlEntries[] = $entry;
                     } else {
                         if ($entry instanceof Zotero_Tag) {
                             $xmlEntries[] = $entry->toAtom($queryParams, isset($fixedValues[$entry->id]) ? $fixedValues[$entry->id] : null);
                         } else {
                             if ($entry instanceof Zotero_Group) {
                                 $entry = $entry->toAtom($queryParams);
                                 $xmlEntries[] = $entry;
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($latestUpdated) {
         $xml->updated = Zotero_Date::sqlToISO8601($latestUpdated);
     } else {
         $xml->updated = str_replace("+00:00", "Z", date('c'));
     }
     // Import object XML nodes into document
     $doc = dom_import_simplexml($xml);
     foreach ($xmlEntries as $xmlEntry) {
         $subNode = dom_import_simplexml($xmlEntry);
         $importedNode = $doc->ownerDocument->importNode($subNode, true);
         $doc->appendChild($importedNode);
     }
     return $xml;
 }
Exemplo n.º 10
0
 /**
  * Parse query string into parameters, validating and filling in defaults
  */
 public static function parseQueryParams($queryString, $action, $singleObject)
 {
     // Handle multiple identical parameters in the CGI-standard way instead of
     // PHP's foo[]=bar way
     $getParams = Zotero_URL::proper_parse_str($queryString);
     $queryParams = array();
     foreach (self::getDefaultQueryParams() as $key => $val) {
         // Don't overwrite field if already derived from another field
         if (!empty($queryParams[$key])) {
             continue;
         }
         if ($key == 'limit') {
             $val = self::getDefaultLimit(isset($getParams['format']) ? $getParams['format'] : "");
         }
         // Fill defaults
         $queryParams[$key] = $val;
         // If no parameter passed, used default
         if (!isset($getParams[$key])) {
             continue;
         }
         // Some formats need special parameter handling
         if (isset($getParams['format'])) {
             if ($getParams['format'] == 'bib') {
                 switch ($key) {
                     // Invalid parameters
                     case 'order':
                     case 'sort':
                     case 'start':
                     case 'limit':
                         throw new Exception("'{$key}' is not valid for format=bib", Z_ERROR_INVALID_INPUT);
                 }
             } else {
                 if ($getParams['format'] == 'keys') {
                     switch ($key) {
                         // Invalid parameters
                         case 'start':
                             throw new Exception("'{$key}' is not valid for format=bib", Z_ERROR_INVALID_INPUT);
                     }
                 }
             }
         }
         switch ($key) {
             case 'format':
                 $format = $getParams[$key];
                 $isExportFormat = in_array($format, Zotero_Translate::$exportFormats);
                 // All actions other than items must be Atom
                 if ($action != 'items') {
                     if ($format != 'atom') {
                         throw new Exception("Invalid 'format' value '{$format}'", Z_ERROR_INVALID_INPUT);
                     }
                 } else {
                     if ($isExportFormat || $format == 'csljson') {
                         if ($singleObject || !empty($getParams['itemKey'])) {
                             break;
                         }
                         $limitMax = self::getLimitMax($format);
                         if (empty($getParams['limit'])) {
                             throw new Exception("'limit' is required for format={$format}", Z_ERROR_INVALID_INPUT);
                         } else {
                             if ($getParams['limit'] > $limitMax) {
                                 throw new Exception("'limit' cannot be greater than {$limitMax} for format={$format}", Z_ERROR_INVALID_INPUT);
                             }
                         }
                     } else {
                         switch ($format) {
                             case 'atom':
                             case 'bib':
                                 break;
                             default:
                                 if ($format == 'keys' && !$singleObject) {
                                     break;
                                 }
                                 throw new Exception("Invalid 'format' value '{$format}' for request", Z_ERROR_INVALID_INPUT);
                         }
                     }
                 }
                 break;
             case 'start':
                 $queryParams[$key] = (int) $getParams[$key];
                 continue 2;
             case 'limit':
                 // Maximum limit depends on 'format'
                 $limitMax = self::getLimitMax(isset($getParams['format']) ? $getParams['format'] : "");
                 // If there's a maximum, enforce it
                 if ($limitMax && (int) $getParams[$key] > $limitMax) {
                     $getParams[$key] = $limitMax;
                 } else {
                     if ((int) $getParams[$key] == 0) {
                         continue 2;
                     }
                 }
                 $queryParams[$key] = (int) $getParams[$key];
                 continue 2;
             case 'content':
                 if (isset($getParams['format']) && $getParams['format'] != 'atom') {
                     throw new Exception("'content' is valid only for format=atom", Z_ERROR_INVALID_INPUT);
                 }
                 $getParams[$key] = array_values(array_unique(explode(',', $getParams[$key])));
                 sort($getParams[$key]);
                 foreach ($getParams[$key] as $value) {
                     switch ($value) {
                         case 'none':
                         case 'full':
                             if (sizeOf($getParams[$key]) > 1) {
                                 throw new Exception("content={$value} is not valid in " . "multi-format responses", Z_ERROR_INVALID_INPUT);
                             }
                             break;
                         case 'html':
                         case 'citation':
                         case 'bib':
                         case 'json':
                         case 'csljson':
                             break;
                         default:
                             if (in_array($value, Zotero_Translate::$exportFormats)) {
                                 break;
                             }
                             throw new Exception("Invalid 'content' value '{$value}'", Z_ERROR_INVALID_INPUT);
                     }
                 }
                 break;
             case 'order':
                 // Whether to sort empty values first
                 $queryParams['emptyFirst'] = Zotero_API::getSortEmptyFirst($getParams[$key]);
                 switch ($getParams[$key]) {
                     // Valid fields to sort by
                     //
                     // Allow all fields available in client
                     case 'title':
                     case 'creator':
                     case 'itemType':
                     case 'date':
                     case 'publisher':
                     case 'publicationTitle':
                     case 'journalAbbreviation':
                     case 'language':
                     case 'accessDate':
                     case 'libraryCatalog':
                     case 'callNumber':
                     case 'rights':
                     case 'dateAdded':
                     case 'dateModified':
                         //case 'numChildren':
                     //case 'numChildren':
                     case 'addedBy':
                     case 'numItems':
                     case 'serverDateModified':
                         // numItems is valid only for tags requests
                         switch ($getParams[$key]) {
                             case 'numItems':
                                 if ($action != 'tags') {
                                     throw new Exception("Invalid 'order' value '" . $getParams[$key] . "'", Z_ERROR_INVALID_INPUT);
                                 }
                                 break;
                         }
                         if (!isset($getParams['sort'])) {
                             $queryParams['sort'] = self::getDefaultSort($getParams[$key]);
                         } else {
                             if (!in_array($getParams['sort'], array('asc', 'desc'))) {
                                 throw new Exception("Invalid 'sort' value '" . $getParams['sort'] . "'", Z_ERROR_INVALID_INPUT);
                             } else {
                                 $queryParams['sort'] = $getParams['sort'];
                             }
                         }
                         break;
                     default:
                         throw new Exception("Invalid 'order' value '" . $getParams[$key] . "'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'sort':
                 if (!in_array($getParams['sort'], array('asc', 'desc'))) {
                     throw new Exception("Invalid 'sort' value '" . $getParams[$key] . "'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
         }
         $queryParams[$key] = $getParams[$key];
     }
     return $queryParams;
 }
Exemplo n.º 11
0
 public function groups()
 {
     $groupID = $this->objectGroupID;
     //
     // Add a group
     //
     if ($this->method == 'POST') {
         if (!$this->permissions->isSuper()) {
             $this->e403();
         }
         if ($groupID) {
             $this->e400("POST requests cannot end with a groupID (did you mean PUT?)");
         }
         try {
             $group = @new SimpleXMLElement($this->body);
         } catch (Exception $e) {
             $this->e400("{$this->method} data is not valid XML");
         }
         if ((int) $group['id']) {
             $this->e400("POST requests cannot contain a groupID in '" . $this->body . "'");
         }
         $fields = $this->getFieldsFromGroupXML($group);
         Zotero_DB::beginTransaction();
         try {
             $group = new Zotero_Group();
             foreach ($fields as $field => $val) {
                 $group->{$field} = $val;
             }
             $group->save();
         } catch (Exception $e) {
             if (strpos($e->getMessage(), "Invalid") === 0) {
                 $this->e400($e->getMessage() . " in " . $this->body . "'");
             }
             switch ($e->getCode()) {
                 case Z_ERROR_GROUP_NAME_UNAVAILABLE:
                     $this->e400($e->getMessage());
                 default:
                     $this->handleException($e);
             }
         }
         $this->queryParams['content'] = array('full');
         $this->responseXML = $group->toAtom($this->queryParams);
         Zotero_DB::commit();
         $url = Zotero_API::getGroupURI($group);
         $this->responseCode = 201;
         header("Location: " . $url, false, 201);
         $this->end();
     }
     //
     // Update a group
     //
     if ($this->method == 'PUT') {
         if (!$this->permissions->isSuper()) {
             $this->e403();
         }
         if (!$groupID) {
             $this->e400("PUT requests must end with a groupID (did you mean POST?)");
         }
         try {
             $group = @new SimpleXMLElement($this->body);
         } catch (Exception $e) {
             $this->e400("{$this->method} data is not valid XML");
         }
         $fields = $this->getFieldsFromGroupXML($group);
         // Group id is optional, but, if it's there, make sure it matches
         $id = (string) $group['id'];
         if ($id && $id != $groupID) {
             $this->e400("Group ID {$id} does not match group ID {$groupID} from URI");
         }
         Zotero_DB::beginTransaction();
         try {
             $group = Zotero_Groups::get($groupID);
             if (!$group) {
                 $this->e404("Group {$groupID} does not exist");
             }
             foreach ($fields as $field => $val) {
                 $group->{$field} = $val;
             }
             if ($this->ifUnmodifiedSince && strtotime($group->dateModified) > $this->ifUnmodifiedSince) {
                 $this->e412();
             }
             $group->save();
         } catch (Exception $e) {
             if (strpos($e->getMessage(), "Invalid") === 0) {
                 $this->e400($e->getMessage() . " in " . $this->body . "'");
             } else {
                 if ($e->getCode() == Z_ERROR_GROUP_DESCRIPTION_TOO_LONG) {
                     $this->e400($e->getMessage());
                 }
             }
             $this->handleException($e);
         }
         $this->queryParams['content'] = array('full');
         $this->responseXML = $group->toAtom($this->queryParams);
         Zotero_DB::commit();
         $this->end();
     }
     //
     // Delete a group
     //
     if ($this->method == 'DELETE') {
         if (!$this->permissions->isSuper()) {
             $this->e403();
         }
         if (!$groupID) {
             $this->e400("DELETE requests must end with a groupID");
         }
         Zotero_DB::beginTransaction();
         $group = Zotero_Groups::get($groupID);
         if (!$group) {
             $this->e404("Group {$groupID} does not exist");
         }
         $group->erase();
         Zotero_DB::commit();
         header("HTTP/1.1 204 No Content");
         exit;
     }
     //
     // View one or more groups
     //
     // Single group
     if ($groupID) {
         $group = Zotero_Groups::get($groupID);
         if (!$this->permissions->canAccess($this->objectLibraryID)) {
             $this->e403();
         }
         if (!$group) {
             $this->e404("Group not found");
         }
         if ($this->apiVersion >= 3) {
             $this->libraryVersion = $group->version;
         } else {
             header("ETag: " . $group->etag);
         }
         if ($this->method == 'HEAD') {
             $this->end();
         }
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = $group->toAtom($this->queryParams);
                 break;
             case 'json':
                 $json = $group->toResponseJSON($this->queryParams);
                 echo Zotero_Utilities::formatJSON($json);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     } else {
         if ($this->objectUserID) {
             $title = Zotero_Users::getUsername($this->objectUserID) . "’s Groups";
         } else {
             // For now, only root can do unrestricted group searches
             if (!$this->permissions->isSuper()) {
                 $this->e403();
             }
             $title = "Groups";
         }
         try {
             $results = Zotero_Groups::getAllAdvanced($this->objectUserID, $this->queryParams, $this->permissions);
         } catch (Exception $e) {
             switch ($e->getCode()) {
                 case Z_ERROR_INVALID_GROUP_TYPE:
                     $this->e400($e->getMessage());
             }
             throw $e;
         }
         $options = ['action' => $this->action, 'uri' => $this->uri, 'results' => $results, 'requestParams' => $this->queryParams, 'permissions' => $this->permissions, 'head' => $this->method == 'HEAD'];
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_API::multiResponse(array_merge($options, ['title' => $title]));
                 break;
             case 'json':
                 Zotero_API::multiResponse($options);
                 break;
             case 'etags':
             case 'versions':
                 $prop = substr($this->queryParams['format'], 0, -1);
                 // remove 's'
                 $newResults = [];
                 foreach ($results['results'] as $group) {
                     $newResults[$group->id] = $group->{$prop};
                 }
                 $options['results']['results'] = $newResults;
                 Zotero_API::multiResponse($options, 'versions');
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     }
     $this->end();
 }
Exemplo n.º 12
0
 /**
  * @param Zotero_Setting $setting The setting object to update;
  *                                this should be either an existing
  *                                setting or a new setting
  *                                with a library and name assigned.
  * @param object $json Setting data to write
  * @param boolean [$requireVersion=0] See Zotero_API::checkJSONObjectVersion()
  * @return boolean True if the setting was changed, false otherwise
  */
 public static function updateFromJSON(Zotero_Setting $setting, $json, $requestParams, $userID, $requireVersion = 0)
 {
     self::validateJSONObject($setting->name, $json, $requestParams);
     Zotero_API::checkJSONObjectVersion($setting, $json, $requestParams, $requireVersion);
     $changed = false;
     if (!Zotero_DB::transactionInProgress()) {
         Zotero_DB::beginTransaction();
         $transactionStarted = true;
     } else {
         $transactionStarted = false;
     }
     $setting->value = $json->value;
     $changed = $setting->save() || $changed;
     if ($transactionStarted) {
         Zotero_DB::commit();
     }
     return $changed;
 }
Exemplo n.º 13
0
 /**
  * @param Zotero_Searches $search The search object to update;
  *                                this should be either an existing
  *                                search or a new search
  *                                with a library assigned.
  * @param object $json Search data to write
  * @param boolean $requireVersion See Zotero_API::checkJSONObjectVersion()
  * @return bool True if the search was changed, false otherwise
  */
 public static function updateFromJSON(Zotero_Search $search, $json, $requestParams, $userID, $requireVersion = 0, $partialUpdate = false)
 {
     $json = Zotero_API::extractEditableJSON($json);
     $exists = Zotero_API::processJSONObjectKey($search, $json, $requestParams);
     Zotero_API::checkJSONObjectVersion($search, $json, $requestParams, $requireVersion);
     self::validateJSONSearch($json, $requestParams, $partialUpdate && $exists);
     if (isset($json->name)) {
         $search->name = $json->name;
     }
     if (isset($json->conditions)) {
         $conditions = [];
         foreach ($json->conditions as $condition) {
             $newCondition = get_object_vars($condition);
             // Parse 'mode' (e.g., '/regexp') out of condition name
             if (preg_match('/(.+)\\/(.+)/', $newCondition['condition'], $matches)) {
                 $newCondition['condition'] = $matches[1];
                 $newCondition['mode'] = $matches[2];
             } else {
                 $newCondition['mode'] = "";
             }
             $conditions[] = $newCondition;
         }
         $search->updateConditions($conditions);
     }
     return !!$search->save();
 }
Exemplo n.º 14
0
 public function toResponseJSON($requestParams = [])
 {
     $t = microtime(true);
     // Child collections and items can't be cached (easily)
     $numCollections = $this->numCollections();
     $numItems = $this->numItems();
     if (!$requestParams['uncached']) {
         $cacheKey = $this->getCacheKey($requestParams);
         $cached = Z_Core::$MC->get($cacheKey);
         if ($cached) {
             Z_Core::debug("Using cached JSON for {$this->libraryKey}");
             $cached['meta']->numCollections = $numCollections;
             $cached['meta']->numItems = $numItems;
             StatsD::timing("api.collections.toResponseJSON.cached", (microtime(true) - $t) * 1000);
             StatsD::increment("memcached.collections.toResponseJSON.hit");
             return $cached;
         }
     }
     $json = ['key' => $this->key, 'version' => $this->version, 'library' => Zotero_Libraries::toJSON($this->libraryID)];
     // 'links'
     $json['links'] = ['self' => ['href' => Zotero_API::getCollectionURI($this), 'type' => 'application/json'], 'alternate' => ['href' => Zotero_URI::getCollectionURI($this, true), 'type' => 'text/html']];
     $parentID = $this->getParentID();
     if ($parentID) {
         $parentCol = Zotero_Collections::get($this->libraryID, $parentID);
         $json['links']['up'] = ['href' => Zotero_API::getCollectionURI($parentCol), 'type' => "application/atom+xml"];
     }
     // 'meta'
     $json['meta'] = new stdClass();
     $json['meta']->numCollections = $numCollections;
     $json['meta']->numItems = $numItems;
     // 'include'
     $include = $requestParams['include'];
     foreach ($include as $type) {
         if ($type == 'data') {
             $json[$type] = $this->toJSON($requestParams);
         }
     }
     if (!$requestParams['uncached']) {
         Z_Core::$MC->set($cacheKey, $json);
         StatsD::timing("api.collections.toResponseJSON.uncached", (microtime(true) - $t) * 1000);
         StatsD::increment("memcached.collections.toResponseJSON.miss");
     }
     return $json;
 }
Exemplo n.º 15
0
 /**
  * Converts a Zotero_Tag object to a SimpleXMLElement Atom object
  *
  * @return	SimpleXMLElement					Tag data as SimpleXML element
  */
 public function toAtom($queryParams, $fixedValues = null)
 {
     if (!empty($queryParams['content'])) {
         $content = $queryParams['content'];
     } else {
         $content = array('none');
     }
     // TEMP: multi-format support
     $content = $content[0];
     $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>' . '<entry xmlns="' . Zotero_Atom::$nsAtom . '" xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '"/>');
     $xml->title = $this->name;
     $author = $xml->addChild('author');
     $author->name = Zotero_Libraries::getName($this->libraryID);
     $author->uri = Zotero_URI::getLibraryURI($this->libraryID, true);
     $xml->id = Zotero_URI::getTagURI($this);
     $xml->published = Zotero_Date::sqlToISO8601($this->dateAdded);
     $xml->updated = Zotero_Date::sqlToISO8601($this->dateModified);
     $link = $xml->addChild("link");
     $link['rel'] = "self";
     $link['type'] = "application/atom+xml";
     $link['href'] = Zotero_API::getTagURI($this);
     $link = $xml->addChild('link');
     $link['rel'] = 'alternate';
     $link['type'] = 'text/html';
     $link['href'] = Zotero_URI::getTagURI($this, true);
     // Count user's linked items
     if (isset($fixedValues['numItems'])) {
         $numItems = $fixedValues['numItems'];
     } else {
         $numItems = sizeOf($this->getLinkedItems(true));
     }
     $xml->addChild('zapi:numItems', $numItems, Zotero_Atom::$nsZoteroAPI);
     if ($content == 'html') {
         $xml->content['type'] = 'xhtml';
         $contentXML = new SimpleXMLElement("<div/>");
         $contentXML->addAttribute("xmlns", Zotero_Atom::$nsXHTML);
         $fNode = dom_import_simplexml($xml->content);
         $subNode = dom_import_simplexml($contentXML);
         $importedNode = $fNode->ownerDocument->importNode($subNode, true);
         $fNode->appendChild($importedNode);
     } else {
         if ($content == 'json') {
             $xml->content['type'] = 'application/json';
             $xml->content = Zotero_Utilities::formatJSON($this->toJSON());
         }
     }
     return $xml;
 }
Exemplo n.º 16
0
 /**
  * Generate a SimpleXMLElement Atom object for the search
  *
  * @param array $queryParams
  * @return SimpleXMLElement
  */
 public function toAtom($queryParams)
 {
     if (!$this->loaded) {
         $this->load();
     }
     // TEMP: multi-format support
     if (!empty($queryParams['content'])) {
         $content = $queryParams['content'];
     } else {
         $content = array('none');
     }
     $content = $content[0];
     $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>' . '<entry xmlns="' . Zotero_Atom::$nsAtom . '" xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '"/>');
     $xml->title = $this->name ? $this->name : '[Untitled]';
     $author = $xml->addChild('author');
     // TODO: group item creator
     $author->name = Zotero_Libraries::getName($this->libraryID);
     $author->uri = Zotero_URI::getLibraryURI($this->libraryID, true);
     $xml->id = Zotero_URI::getSearchURI($this);
     $xml->published = Zotero_Date::sqlToISO8601($this->dateAdded);
     $xml->updated = Zotero_Date::sqlToISO8601($this->dateModified);
     $link = $xml->addChild("link");
     $link['rel'] = "self";
     $link['type'] = "application/atom+xml";
     $link['href'] = Zotero_API::getSearchURI($this);
     $xml->addChild('zapi:key', $this->key, Zotero_Atom::$nsZoteroAPI);
     $xml->addChild('zapi:version', $this->version, Zotero_Atom::$nsZoteroAPI);
     if ($content == 'json') {
         $xml->content['type'] = 'application/json';
         $xml->content = Zotero_Utilities::formatJSON($this->toJSON($queryParams));
     }
     return $xml;
 }
Exemplo n.º 17
0
 public static function getAllAdvanced($libraryID, $params)
 {
     $results = array('objects' => array(), 'total' => 0);
     $sql = "SELECT SQL_CALC_FOUND_ROWS tagID FROM tags ";
     if (!empty($params['order']) && $params['order'] == 'numItems') {
         $sql .= " LEFT JOIN itemTags USING (tagID)";
     }
     $sql .= "WHERE libraryID=? ";
     $sqlParams = array($libraryID);
     if (!empty($params['q'])) {
         if (!is_array($params['q'])) {
             $params['q'] = array($params['q']);
         }
         foreach ($params['q'] as $q) {
             $sql .= "AND name LIKE ? ";
             $sqlParams[] = "%{$q}%";
         }
     }
     $tagTypeSets = Zotero_API::getSearchParamValues($params, 'tagType');
     if ($tagTypeSets) {
         $positives = array();
         $negatives = array();
         foreach ($tagTypeSets as $set) {
             if ($set['negation']) {
                 $negatives = array_merge($negatives, $set['values']);
             } else {
                 $positives = array_merge($positives, $set['values']);
             }
         }
         if ($positives) {
             $sql .= "AND type IN (" . implode(',', array_fill(0, sizeOf($positives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $positives);
         }
         if ($negatives) {
             $sql .= "AND type NOT IN (" . implode(',', array_fill(0, sizeOf($negatives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $negatives);
         }
     }
     if (!empty($params['order'])) {
         $order = $params['order'];
         if ($order == 'title') {
             // Force a case-insensitive sort
             $sql .= "ORDER BY name COLLATE utf8_unicode_ci ";
         } else {
             if ($order == 'numItems') {
                 $sql .= "GROUP BY tags.tagID ORDER BY COUNT(tags.tagID)";
             } else {
                 $sql .= "ORDER BY {$order} ";
             }
         }
         if (!empty($params['sort'])) {
             $sql .= " " . $params['sort'] . " ";
         }
     }
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $ids = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     if ($ids) {
         $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
         $tags = array();
         foreach ($ids as $id) {
             $tags[] = Zotero_Tags::get($libraryID, $id);
         }
         $results['objects'] = $tags;
     }
     return $results;
 }
Exemplo n.º 18
0
 protected function end()
 {
     if ($this->profile) {
         Zotero_DB::profileEnd($this->objectLibraryID, true);
     }
     switch ($this->responseCode) {
         case 200:
             // Output a Content-Type header for the given format
             //
             // Note that this overrides any Content-Type set elsewhere. To force a content
             // type elsewhere, clear $this->queryParams['format'] when calling header()
             // manually.
             //
             // TODO: Check headers_list so that clearing the format parameter manually isn't
             // necessary? Performance?
             if (isset($this->queryParams['format'])) {
                 Zotero_API::outputContentType($this->queryParams['format']);
             }
             break;
         case 301:
         case 302:
         case 303:
             // Handled in $this->redirect()
             break;
         case 401:
             header('WWW-Authenticate: Basic realm="Zotero API"');
             header('HTTP/1.1 401 Unauthorized');
             break;
             // PHP completes these automatically
         // PHP completes these automatically
         case 201:
         case 204:
         case 300:
         case 304:
         case 400:
         case 403:
         case 404:
         case 405:
         case 409:
         case 412:
         case 413:
         case 422:
         case 500:
         case 501:
         case 503:
             header("HTTP/1.1 " . $this->responseCode);
             break;
         case 428:
             header("HTTP/1.1 428 Precondition Required");
             break;
         case 429:
             header("HTTP/1.1 429 Too Many Requests");
             break;
         default:
             throw new Exception("Unsupported response code " . $this->responseCode);
     }
     if (isset($this->libraryVersion)) {
         if ($this->apiVersion >= 2) {
             header("Last-Modified-Version: " . $this->libraryVersion);
         }
         // Send notification if library has changed
         if ($this->isWriteMethod()) {
             if ($this->libraryVersion > Zotero_Libraries::getOriginalVersion($this->objectLibraryID)) {
                 Zotero_Notifier::trigger('modify', 'library', $this->objectLibraryID);
             }
         }
     }
     if ($this->responseXML instanceof SimpleXMLElement) {
         if (!$this->responseCode) {
             $updated = (string) $this->responseXML->updated;
             if ($updated) {
                 $updated = strtotime($updated);
                 $ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
                 $ifModifiedSince = strtotime($ifModifiedSince);
                 if ($ifModifiedSince >= $updated) {
                     header('HTTP/1.1 304 Not Modified');
                     exit;
                 }
                 $lastModified = substr(date('r', $updated), 0, -5) . "GMT";
                 header("Last-Modified: {$lastModified}");
             }
         }
         $xmlstr = $this->responseXML->asXML();
         // TEMP: Strip control characters
         $xmlstr = Zotero_Utilities::cleanString($xmlstr, true);
         $doc = new DOMDocument('1.0');
         $doc->loadXML($xmlstr);
         $doc->formatOutput = true;
         echo $doc->saveXML();
     }
     $this->logRequestTime();
     self::addHeaders();
     echo ob_get_clean();
     exit;
 }
Exemplo n.º 19
0
 /**
  * @param Zotero_Collection $collection The collection object to update;
  *                                      this should be either an existing
  *                                      collection or a new collection
  *                                      with a library assigned.
  * @param object $json Collection data to write
  * @param boolean [$requireVersion=0] See Zotero_API::checkJSONObjectVersion()
  * @return boolean True if the collection was changed, false otherwise
  */
 public static function updateFromJSON(Zotero_Collection $collection, $json, $requestParams, $userID, $requireVersion = 0, $partialUpdate = false)
 {
     $json = Zotero_API::extractEditableJSON($json);
     $exists = Zotero_API::processJSONObjectKey($collection, $json, $requestParams);
     Zotero_API::checkJSONObjectVersion($collection, $json, $requestParams, $requireVersion);
     self::validateJSONCollection($json, $requestParams, $partialUpdate && $exists);
     $changed = false;
     if (!Zotero_DB::transactionInProgress()) {
         Zotero_DB::beginTransaction();
         $transactionStarted = true;
     } else {
         $transactionStarted = false;
     }
     if (isset($json->name)) {
         $collection->name = $json->name;
     }
     if ($requestParams['v'] >= 2 && isset($json->parentCollection)) {
         $collection->parentKey = $json->parentCollection;
     } else {
         if ($requestParams['v'] < 2 && isset($json->parent)) {
             $collection->parentKey = $json->parent;
         } else {
             if (!$partialUpdate) {
                 $collection->parent = false;
             }
         }
     }
     $changed = $collection->save() || $changed;
     if ($requestParams['v'] >= 2) {
         if (isset($json->relations)) {
             $changed = $collection->setRelations($json->relations, $userID) || $changed;
         } else {
             if (!$partialUpdate) {
                 $changed = $collection->setRelations(new stdClass(), $userID) || $changed;
             }
         }
     }
     if ($transactionStarted) {
         Zotero_DB::commit();
     }
     return $changed;
 }
Exemplo n.º 20
0
	public function toResponseJSON($requestParams=[], Zotero_Permissions $permissions, $sharedData=null) {
		$t = microtime(true);
		
		if (!$this->loaded['primaryData']) {
			$this->loadPrimaryData();
		}
		if (!$this->loaded['itemData']) {
			$this->loadItemData();
		}
		
		// Uncached stuff or parts of the cache key
		$version = $this->version;
		$parent = $this->getSource();
		$isRegularItem = !$parent && $this->isRegularItem();
		$downloadDetails = $permissions->canAccess($this->libraryID, 'files')
								? Zotero_Storage::getDownloadDetails($this)
								: false;
		if ($isRegularItem) {
			$numChildren = $permissions->canAccess($this->libraryID, 'notes')
								? $this->numChildren()
								: $this->numAttachments();
		}
		$libraryType = Zotero_Libraries::getType($this->libraryID);
		
		// Any query parameters that have an effect on the output
		// need to be added here
		$allowedParams = [
			'include',
			'style',
			'css',
			'linkwrap'
		];
		$cachedParams = Z_Array::filterKeys($requestParams, $allowedParams);
		
		$cacheVersion = 1;
		$cacheKey = "jsonEntry_" . $this->libraryID . "/" . $this->id . "_"
			. md5(
				$version
				. json_encode($cachedParams)
				. ($downloadDetails ? 'hasFile' : '')
				// For groups, include the group WWW URL, which can change
				. ($libraryType == 'group' ? Zotero_URI::getItemURI($this, true) : '')
			)
			. "_" . $requestParams['v']
			// For code-based changes
			. "_" . $cacheVersion
			// For data-based changes
			. (isset(Z_CONFIG::$CACHE_VERSION_RESPONSE_JSON_ITEM)
				? "_" . Z_CONFIG::$CACHE_VERSION_RESPONSE_JSON_ITEM
				: "")
			// If there's bib content, include the bib cache version
			. ((in_array('bib', $requestParams['include'])
					&& isset(Z_CONFIG::$CACHE_VERSION_BIB))
				? "_" . Z_CONFIG::$CACHE_VERSION_BIB
				: "");
		
		$cached = Z_Core::$MC->get($cacheKey);
		if (false && $cached) {
			// Make sure numChildren reflects the current permissions
			if ($isRegularItem) {
				$json = json_decode($cached);
				$json['numChildren'] = $numChildren;
				$cached = json_encode($json);
			}
			
			//StatsD::timing("api.items.itemToResponseJSON.cached", (microtime(true) - $t) * 1000);
			//StatsD::increment("memcached.items.itemToResponseJSON.hit");
			
			// Skip the cache every 10 times for now, to ensure cache sanity
			if (!Z_Core::probability(10)) {
				return $cached;
			}
		}
		
		
		$json = [
			'key' => $this->key,
			'version' => $version,
			'library' => Zotero_Libraries::toJSON($this->libraryID)
		];
		
		$json['links'] = [
			'self' => [
				'href' => Zotero_API::getItemURI($this),
				'type' => 'application/json'
			],
			'alternate' => [
				'href' => Zotero_URI::getItemURI($this, true),
				'type' => 'text/html'
			]
		];
		
		if ($parent) {
			$parentItem = Zotero_Items::get($this->libraryID, $parent);
			$json['links']['up'] = [
				'href' => Zotero_API::getItemURI($parentItem),
				'type' => 'application/json'
			];
		}
		
		// If appropriate permissions and the file is stored in ZFS, get file request link
		if ($downloadDetails) {
			$details = $downloadDetails;
			$type = $this->attachmentMIMEType;
			if ($type) {
				$json['links']['enclosure'] = [
					'type' => $type
				];
			}
			$json['links']['enclosure']['href'] = $details['url'];
			if (!empty($details['filename'])) {
				$json['links']['enclosure']['title'] = $details['filename'];
			}
			if (isset($details['size'])) {
				$json['links']['enclosure']['length'] = $details['size'];
			}
		}
		
		// 'meta'
		$json['meta'] = new stdClass;
		
		if (Zotero_Libraries::getType($this->libraryID) == 'group') {
			$createdByUserID = $this->createdByUserID;
			$lastModifiedByUserID = $this->lastModifiedByUserID;
			
			if ($createdByUserID) {
				$json['meta']->createdByUser = Zotero_Users::toJSON($createdByUserID);
			}
			
			if ($lastModifiedByUserID && $lastModifiedByUserID != $createdByUserID) {
				$json['meta']->lastModifiedByUser = Zotero_Users::toJSON($lastModifiedByUserID);
			}
		}
		
		if ($isRegularItem) {
			$val = $this->getCreatorSummary();
			if ($val !== '') {
				$json['meta']->creatorSummary = $val;
			}
			
			$val = $this->getField('date', true, true, true);
			if ($val !== '') {
				$sqlDate = Zotero_Date::multipartToSQL($val);
				if (substr($sqlDate, 0, 4) !== '0000') {
					$json['meta']->parsedDate = Zotero_Date::sqlToISO8601($sqlDate);
				}
			}
			
			$json['meta']->numChildren = $numChildren;
		}
		
		// 'include'
		$include = $requestParams['include'];
		
		foreach ($include as $type) {
			if ($type == 'html') {
				$json[$type] = trim($this->toHTML());
			}
			else if ($type == 'citation') {
				if (isset($sharedData[$type][$this->libraryID . "/" . $this->key])) {
					$html = $sharedData[$type][$this->libraryID . "/" . $this->key];
				}
				else {
					if ($sharedData !== null) {
						//error_log("Citation not found in sharedData -- retrieving individually");
					}
					$html = Zotero_Cite::getCitationFromCiteServer($this, $requestParams);
				}
				$json[$type] = $html;
			}
			else if ($type == 'bib') {
				if (isset($sharedData[$type][$this->libraryID . "/" . $this->key])) {
					$html = $sharedData[$type][$this->libraryID . "/" . $this->key];
				}
				else {
					if ($sharedData !== null) {
						//error_log("Bibliography not found in sharedData -- retrieving individually");
					}
					$html = Zotero_Cite::getBibliographyFromCitationServer([$this], $requestParams);
					
					// Strip prolog
					$html = preg_replace('/^<\?xml.+\n/', "", $html);
					$html = trim($html);
				}
				$json[$type] = $html;
			}
			else if ($type == 'data') {
				$json[$type] = $this->toJSON(true, $requestParams, true);
			}
			else if ($type == 'csljson') {
				$json[$type] = $this->toCSLItem();
			}
			else if (in_array($type, Zotero_Translate::$exportFormats)) {
				$export = Zotero_Translate::doExport([$this], $type);
				$json[$type] = $export['body'];
				unset($export);
			}
		}
		

		// TEMP
		if ($cached) {
			$uncached = Zotero_Utilities::formatJSON($json);
			if ($cached != $uncached) {
				error_log("Cached JSON item entry does not match");
				error_log("  Cached: " . $cached);
				error_log("Uncached: " . $uncached);
				
				//Z_Core::$MC->set($cacheKey, $uncached, 3600); // 1 hour for now
			}
		}
		else {
			/*Z_Core::$MC->set($cacheKey, $xmlstr, 3600); // 1 hour for now
			StatsD::timing("api.items.itemToAtom.uncached", (microtime(true) - $t) * 1000);
			StatsD::increment("memcached.items.itemToAtom.miss");*/
		}
		
		return $json;
	}
Exemplo n.º 21
0
 public static function search($libraryID, $onlyTopLevel = false, $params = array(), $includeTrashed = false, $asKeys = false)
 {
     $rnd = "_" . uniqid($libraryID . "_");
     if ($asKeys) {
         $results = array('keys' => array(), 'total' => 0);
     } else {
         $results = array('items' => array(), 'total' => 0);
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $itemIDs = array();
     $keys = array();
     $deleteTempTable = array();
     // Pass a list of itemIDs, for when the initial search is done via SQL
     if (!empty($params['itemIDs'])) {
         $itemIDs = $params['itemIDs'];
     }
     if (!empty($params['itemKey'])) {
         $keys = explode(',', $params['itemKey']);
     }
     $titleSort = !empty($params['order']) && $params['order'] == 'title';
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT " . ($asKeys ? "I.key" : "I.itemID") . " FROM items I ";
     $sqlParams = array($libraryID);
     if (!empty($params['q']) || $titleSort) {
         $titleFieldIDs = array_merge(array(Zotero_ItemFields::getID('title')), Zotero_ItemFields::getTypeFieldsFromBase('title'));
         $sql .= "LEFT JOIN itemData IDT ON (IDT.itemID=I.itemID AND IDT.fieldID IN (" . implode(',', $titleFieldIDs) . ")) ";
     }
     if (!empty($params['q'])) {
         $sql .= "LEFT JOIN itemCreators IC ON (IC.itemID=I.itemID)\n\t\t\t\t\tLEFT JOIN creators C ON (C.creatorID=IC.creatorID) ";
     }
     if ($onlyTopLevel || !empty($params['q']) || $titleSort) {
         $sql .= "LEFT JOIN itemNotes INo ON (INo.itemID=I.itemID) ";
     }
     if ($onlyTopLevel) {
         $sql .= "LEFT JOIN itemAttachments IA ON (IA.itemID=I.itemID) ";
     }
     if (!$includeTrashed) {
         $sql .= "LEFT JOIN deletedItems DI ON (DI.itemID=I.itemID) ";
     }
     if (!empty($params['order'])) {
         switch ($params['order']) {
             case 'title':
             case 'creator':
                 $sql .= "LEFT JOIN itemSortFields ISF ON (ISF.itemID=I.itemID) ";
                 break;
             case 'date':
                 $dateFieldIDs = array_merge(array(Zotero_ItemFields::getID('date')), Zotero_ItemFields::getTypeFieldsFromBase('date'));
                 $sql .= "LEFT JOIN itemData IDD ON (IDD.itemID=I.itemID AND IDD.fieldID IN (" . implode(',', $dateFieldIDs) . ")) ";
                 break;
             case 'itemType':
                 // Create temporary table to store item type names
                 //
                 // We use IF NOT EXISTS just to make sure there are
                 // no problems with restoration from the binary log
                 $sql2 = "CREATE TEMPORARY TABLE IF NOT EXISTS tmpItemTypeNames{$rnd}\n\t\t\t\t\t\t\t(itemTypeID SMALLINT UNSIGNED NOT NULL,\n\t\t\t\t\t\t\titemTypeName VARCHAR(255) NOT NULL,\n\t\t\t\t\t\t\tPRIMARY KEY (itemTypeID),\n\t\t\t\t\t\t\tINDEX (itemTypeName))";
                 Zotero_DB::query($sql2, false, $shardID);
                 $deleteTempTable['tmpItemTypeNames'] = true;
                 $types = Zotero_ItemTypes::getAll('en-US');
                 foreach ($types as $type) {
                     $sql2 = "INSERT INTO tmpItemTypeNames{$rnd} VALUES (?, ?)";
                     Zotero_DB::query($sql2, array($type['id'], $type['localized']), $shardID);
                 }
                 // Join temp table to query
                 $sql .= "JOIN tmpItemTypeNames{$rnd} TITN ON (TITN.itemTypeID=I.itemTypeID) ";
                 break;
             case 'addedBy':
                 $isGroup = Zotero_Libraries::getType($libraryID) == 'group';
                 if ($isGroup) {
                     // Create temporary table to store usernames
                     //
                     // We use IF NOT EXISTS just to make sure there are
                     // no problems with restoration from the binary log
                     $sql2 = "CREATE TEMPORARY TABLE IF NOT EXISTS tmpCreatedByUsers{$rnd}\n\t\t\t\t\t\t\t\t(userID INT UNSIGNED NOT NULL,\n\t\t\t\t\t\t\t\tusername VARCHAR(255) NOT NULL,\n\t\t\t\t\t\t\t\tPRIMARY KEY (userID),\n\t\t\t\t\t\t\t\tINDEX (username))";
                     Zotero_DB::query($sql2, false, $shardID);
                     $deleteTempTable['tmpCreatedByUsers'] = true;
                     $sql2 = "SELECT DISTINCT createdByUserID FROM items\n\t\t\t\t\t\t\t\tJOIN groupItems USING (itemID) WHERE\n\t\t\t\t\t\t\t\tcreatedByUserID IS NOT NULL AND ";
                     if ($itemIDs) {
                         $sql2 .= "itemID IN (" . implode(', ', array_fill(0, sizeOf($itemIDs), '?')) . ") ";
                         $createdByUserIDs = Zotero_DB::columnQuery($sql2, $itemIDs, $shardID);
                     } else {
                         $sql2 .= "libraryID=?";
                         $createdByUserIDs = Zotero_DB::columnQuery($sql2, $libraryID, $shardID);
                     }
                     // Populate temp table with usernames
                     if ($createdByUserIDs) {
                         $toAdd = array();
                         foreach ($createdByUserIDs as $createdByUserID) {
                             $toAdd[] = array($createdByUserID, Zotero_Users::getUsername($createdByUserID));
                         }
                         $sql2 = "INSERT IGNORE INTO tmpCreatedByUsers{$rnd} VALUES ";
                         Zotero_DB::bulkInsert($sql2, $toAdd, 50, false, $shardID);
                         // Join temp table to query
                         $sql .= "JOIN groupItems GI ON (GI.itemID=I.itemID)\n\t\t\t\t\t\t\t\t\tJOIN tmpCreatedByUsers{$rnd} TCBU ON (TCBU.userID=GI.createdByUserID) ";
                     }
                 }
                 break;
         }
     }
     $sql .= "WHERE I.libraryID=? ";
     if ($onlyTopLevel) {
         $sql .= "AND INo.sourceItemID IS NULL AND IA.sourceItemID IS NULL ";
     }
     if (!$includeTrashed) {
         $sql .= "AND DI.itemID IS NULL ";
     }
     // Search on title and creators
     if (!empty($params['q'])) {
         $sql .= "AND (";
         $sql .= "IDT.value LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR title LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR TRIM(CONCAT(firstName, ' ', lastName)) LIKE ?";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= ") ";
     }
     // Search on itemType
     if (!empty($params['itemType'])) {
         $itemTypes = Zotero_API::getSearchParamValues($params, 'itemType');
         if ($itemTypes) {
             if (sizeOf($itemTypes) > 1) {
                 throw new Exception("Cannot specify 'itemType' more than once", Z_ERROR_INVALID_INPUT);
             }
             $itemTypes = $itemTypes[0];
             $itemTypeIDs = array();
             foreach ($itemTypes['values'] as $itemType) {
                 $itemTypeID = Zotero_ItemTypes::getID($itemType);
                 if (!$itemTypeID) {
                     throw new Exception("Invalid itemType '{$itemType}'", Z_ERROR_INVALID_INPUT);
                 }
                 $itemTypeIDs[] = $itemTypeID;
             }
             $sql .= "AND I.itemTypeID " . ($itemTypes['negation'] ? "NOT " : "") . "IN (" . implode(',', array_fill(0, sizeOf($itemTypeIDs), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $itemTypeIDs);
         }
     }
     // Tags
     //
     // ?tag=foo
     // ?tag=foo bar // phrase
     // ?tag=-foo // negation
     // ?tag=\-foo // literal hyphen (only for first character)
     // ?tag=foo&tag=bar // AND
     // ?tag=foo&tagType=0
     // ?tag=foo bar || bar&tagType=0
     $tagSets = Zotero_API::getSearchParamValues($params, 'tag');
     if ($tagSets) {
         $sql2 = "SELECT itemID FROM items WHERE 1 ";
         $sqlParams2 = array();
         if ($tagSets) {
             foreach ($tagSets as $set) {
                 $positives = array();
                 $negatives = array();
                 $tagIDs = array();
                 foreach ($set['values'] as $tag) {
                     $ids = Zotero_Tags::getIDs($libraryID, $tag);
                     if (!$ids) {
                         $ids = array(0);
                     }
                     $tagIDs = array_merge($tagIDs, $ids);
                 }
                 $tagIDs = array_unique($tagIDs);
                 if ($set['negation']) {
                     $negatives = array_merge($negatives, $tagIDs);
                 } else {
                     $positives = array_merge($positives, $tagIDs);
                 }
                 if ($positives) {
                     $sql2 .= "AND itemID IN (SELECT itemID FROM items JOIN itemTags USING (itemID)\n\t\t\t\t\t\t\t\tWHERE tagID IN (" . implode(',', array_fill(0, sizeOf($positives), '?')) . ")) ";
                     $sqlParams2 = array_merge($sqlParams2, $positives);
                 }
                 if ($negatives) {
                     $sql2 .= "AND itemID NOT IN (SELECT itemID FROM items JOIN itemTags USING (itemID)\n\t\t\t\t\t\t\t\tWHERE tagID IN (" . implode(',', array_fill(0, sizeOf($negatives), '?')) . ")) ";
                     $sqlParams2 = array_merge($sqlParams2, $negatives);
                 }
             }
         }
         $tagItems = Zotero_DB::columnQuery($sql2, $sqlParams2, $shardID);
         // No matches
         if (!$tagItems) {
             return $results;
         }
         // Combine with passed keys
         if ($itemIDs) {
             $itemIDs = array_intersect($itemIDs, $tagItems);
             // None of the tag matches match the passed keys
             if (!$itemIDs) {
                 return $results;
             }
         } else {
             $itemIDs = $tagItems;
         }
     }
     if ($itemIDs) {
         $sql .= "AND I.itemID IN (" . implode(', ', array_fill(0, sizeOf($itemIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $itemIDs);
     }
     if ($keys) {
         $sql .= "AND `key` IN (" . implode(', ', array_fill(0, sizeOf($keys), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $keys);
     }
     $sql .= "ORDER BY ";
     if (!empty($params['order'])) {
         switch ($params['order']) {
             case 'dateAdded':
             case 'dateModified':
             case 'serverDateModified':
                 $orderSQL = "I." . $params['order'];
                 break;
             case 'itemType':
                 $orderSQL = "TITN.itemTypeName";
                 break;
             case 'title':
                 $orderSQL = "IFNULL(COALESCE(sortTitle, IDT.value, INo.title), '')";
                 break;
             case 'creator':
                 $orderSQL = "ISF.creatorSummary";
                 break;
                 // TODO: generic base field mapping-aware sorting
             // TODO: generic base field mapping-aware sorting
             case 'date':
                 $orderSQL = "IDD.value";
                 break;
             case 'addedBy':
                 if ($isGroup && $createdByUserIDs) {
                     $orderSQL = "TCBU.username";
                 } else {
                     $orderSQL = "1";
                 }
                 break;
             default:
                 $fieldID = Zotero_ItemFields::getID($params['order']);
                 if (!$fieldID) {
                     throw new Exception("Invalid order field '" . $params['order'] . "'");
                 }
                 $orderSQL = "(SELECT value FROM itemData WHERE itemID=I.itemID AND fieldID=?)";
                 if (!$params['emptyFirst']) {
                     $sqlParams[] = $fieldID;
                 }
                 $sqlParams[] = $fieldID;
         }
         if (!empty($params['sort'])) {
             $dir = $params['sort'];
         } else {
             $dir = "ASC";
         }
         if (!$params['emptyFirst']) {
             $sql .= "IFNULL({$orderSQL}, '') = '' {$dir}, ";
         }
         $sql .= $orderSQL;
         $sql .= " {$dir}, ";
     }
     $sql .= "I.itemID " . (!empty($params['sort']) ? $params['sort'] : "ASC") . " ";
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $itemIDs = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($itemIDs) {
         if ($asKeys) {
             $results['keys'] = $itemIDs;
         } else {
             $results['items'] = Zotero_Items::get($libraryID, $itemIDs);
         }
     }
     if (!empty($deleteTempTable['tmpCreatedByUsers'])) {
         $sql = "DROP TEMPORARY TABLE IF EXISTS tmpCreatedByUsers{$rnd}";
         Zotero_DB::query($sql, false, $shardID);
     }
     if (!empty($deleteTempTable['tmpItemTypeNames'])) {
         $sql = "DROP TEMPORARY TABLE IF EXISTS tmpItemTypeNames{$rnd}";
         Zotero_DB::query($sql, false, $shardID);
     }
     return $results;
 }
Exemplo n.º 22
0
 public static function search($libraryID, $params)
 {
     $results = array('results' => array(), 'total' => 0);
     // Default empty library
     if ($libraryID === 0) {
         return $results;
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT tagID FROM tags " . "JOIN itemTags USING (tagID) WHERE libraryID=? ";
     $sqlParams = array($libraryID);
     // Pass a list of tagIDs, for when the initial search is done via SQL
     $tagIDs = !empty($params['tagIDs']) ? $params['tagIDs'] : array();
     // Filter for specific tags with "?tag=foo || bar"
     $tagNames = !empty($params['tag']) ? explode(' || ', $params['tag']) : array();
     if ($tagIDs) {
         $sql .= "AND tagID IN (" . implode(', ', array_fill(0, sizeOf($tagIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $tagIDs);
     }
     if ($tagNames) {
         $sql .= "AND `name` IN (" . implode(', ', array_fill(0, sizeOf($tagNames), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $tagNames);
     }
     if (!empty($params['q'])) {
         if (!is_array($params['q'])) {
             $params['q'] = array($params['q']);
         }
         foreach ($params['q'] as $q) {
             $sql .= "AND name LIKE ? ";
             $sqlParams[] = "%{$q}%";
         }
     }
     $tagTypeSets = Zotero_API::getSearchParamValues($params, 'tagType');
     if ($tagTypeSets) {
         $positives = array();
         $negatives = array();
         foreach ($tagTypeSets as $set) {
             if ($set['negation']) {
                 $negatives = array_merge($negatives, $set['values']);
             } else {
                 $positives = array_merge($positives, $set['values']);
             }
         }
         if ($positives) {
             $sql .= "AND type IN (" . implode(',', array_fill(0, sizeOf($positives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $positives);
         }
         if ($negatives) {
             $sql .= "AND type NOT IN (" . implode(',', array_fill(0, sizeOf($negatives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $negatives);
         }
     }
     if (!empty($params['since'])) {
         $sql .= "AND version > ? ";
         $sqlParams[] = $params['since'];
     }
     if (!empty($params['sort'])) {
         $order = $params['sort'];
         if ($order == 'title') {
             // Force a case-insensitive sort
             $sql .= "ORDER BY name COLLATE utf8_unicode_ci ";
         } else {
             if ($order == 'numItems') {
                 $sql .= "GROUP BY tags.tagID ORDER BY COUNT(tags.tagID)";
             } else {
                 $sql .= "ORDER BY {$order} ";
             }
         }
         if (!empty($params['direction'])) {
             $sql .= " " . $params['direction'] . " ";
         }
     }
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $ids = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($ids) {
         $tags = array();
         foreach ($ids as $id) {
             $tags[] = Zotero_Tags::get($libraryID, $id);
         }
         $results['results'] = $tags;
     }
     return $results;
 }
Exemplo n.º 23
0
 public function searches()
 {
     if ($this->apiVersion < 2) {
         $this->e404();
     }
     // Check for general library access
     if (!$this->permissions->canAccess($this->objectLibraryID)) {
         $this->e403();
     }
     if ($this->isWriteMethod()) {
         // Check for library write access
         if (!$this->permissions->canWrite($this->objectLibraryID)) {
             $this->e403("Write access denied");
         }
         // Make sure library hasn't been modified
         if (!$this->singleObject) {
             $libraryTimestampChecked = $this->checkLibraryIfUnmodifiedSinceVersion();
         }
         Zotero_Libraries::updateVersionAndTimestamp($this->objectLibraryID);
     }
     $results = array();
     // Single search
     if ($this->singleObject) {
         $this->allowMethods(['HEAD', 'GET', 'PUT', 'PATCH', 'DELETE']);
         $search = Zotero_Searches::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
         if ($this->isWriteMethod()) {
             $search = $this->handleObjectWrite('search', $search ? $search : null);
             $this->e204();
         }
         if (!$search) {
             $this->e404("Search not found");
         }
         $this->libraryVersion = $search->version;
         if ($this->method == 'HEAD') {
             $this->end();
         }
         // Display search
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = $search->toAtom($this->queryParams);
                 break;
             case 'json':
                 $json = $search->toResponseJSON($this->queryParams, $this->permissions);
                 echo Zotero_Utilities::formatJSON($json);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     } else {
         $this->allowMethods(['HEAD', 'GET', 'POST', 'DELETE']);
         $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID);
         // Create a search
         if ($this->method == 'POST') {
             $this->queryParams['format'] = 'writereport';
             $obj = $this->jsonDecode($this->body);
             $results = Zotero_Searches::updateMultipleFromJSON($obj, $this->objectLibraryID, $this->queryParams, $this->userID, $libraryTimestampChecked ? 0 : 1, null);
             if ($cacheKey = $this->getWriteTokenCacheKey()) {
                 Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
             }
         } else {
             if ($this->method == 'DELETE') {
                 Zotero_DB::beginTransaction();
                 foreach ($this->queryParams['searchKey'] as $searchKey) {
                     Zotero_Searches::delete($this->objectLibraryID, $searchKey);
                 }
                 Zotero_DB::commit();
                 $this->e204();
             } else {
                 $title = "Searches";
                 $results = Zotero_Searches::search($this->objectLibraryID, $this->queryParams);
             }
         }
         $options = ['action' => $this->action, 'uri' => $this->uri, 'results' => $results, 'requestParams' => $this->queryParams, 'permissions' => $this->permissions, 'head' => $this->method == 'HEAD'];
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_API::multiResponse(array_merge($options, ['title' => $this->getFeedNamePrefix($this->objectLibraryID) . $title]));
                 break;
             case 'json':
             case 'keys':
             case 'versions':
             case 'writereport':
                 Zotero_API::multiResponse($options);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     }
     $this->end();
 }
Exemplo n.º 24
0
 public static function multiResponse($options, $overrideFormat = false)
 {
     $format = $overrideFormat ? $overrideFormat : $options['requestParams']['format'];
     if (empty($options['results'])) {
         $options['results'] = ['results' => [], 'total' => 0];
     }
     if ($options['results'] && isset($options['results']['results'])) {
         $totalResults = $options['results']['total'];
         $options['results'] = $options['results']['results'];
         if ($options['requestParams']['v'] >= 3) {
             header("Total-Results: {$totalResults}");
         }
     }
     switch ($format) {
         case 'atom':
         case 'csljson':
         case 'json':
         case 'keys':
         case 'versions':
             $link = Zotero_API::buildLinkHeader($options['action'], $options['uri'], $totalResults, $options['requestParams']);
             if ($link) {
                 header($link);
             }
             break;
     }
     if (!empty($options['head'])) {
         return;
     }
     switch ($format) {
         case 'atom':
             $t = microtime(true);
             $response = Zotero_Atom::createAtomFeed($options['action'], $options['title'], $options['uri'], $options['results'], $totalResults, $options['requestParams'], $options['permissions'], isset($options['fixedValues']) ? $options['fixedValues'] : null);
             StatsD::timing("api." . $options['action'] . ".multiple.createAtomFeed." . implode("-", $options['requestParams']['content']), (microtime(true) - $t) * 1000);
             return $response;
         case 'csljson':
             $json = Zotero_Cite::getJSONFromItems($options['results'], true);
             echo Zotero_Utilities::formatJSON($json);
             break;
         case 'json':
             echo Zotero_API::createJSONResponse($options['results'], $options['requestParams'], $options['permissions']);
             break;
         case 'keys':
             echo implode("\n", $options['results']) . "\n";
             break;
         case 'versions':
             if (!empty($options['results'])) {
                 echo Zotero_Utilities::formatJSON($options['results']);
             } else {
                 echo Zotero_Utilities::formatJSON(new stdClass());
             }
             break;
         case 'writereport':
             echo Zotero_Utilities::formatJSON($options['results']);
             break;
         default:
             throw new Exception("Unexpected format '" . $options['requestParams']['format'] . "'");
     }
 }
Exemplo n.º 25
0
 private function generateMultiResponse($results, $title, $fixedValues)
 {
     $options = ['action' => $this->action, 'uri' => $this->uri, 'results' => $results, 'requestParams' => $this->queryParams, 'permissions' => $this->permissions, 'head' => $this->method == 'HEAD'];
     switch ($this->queryParams['format']) {
         case 'atom':
             $this->responseXML = Zotero_API::multiResponse(array_merge($options, ['title' => $this->getFeedNamePrefix($this->objectLibraryID) . $title, 'fixedValues' => $fixedValues]));
             break;
         case 'json':
             Zotero_API::multiResponse($options);
             break;
         default:
             throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
     }
 }