Beispiel #1
0
 /**
  * Parse query string into parameters, validating and filling in defaults
  */
 public static function parseQueryParams($queryString, $action, $singleObject, $apiVersion = false, $atomAccepted = false)
 {
     // Handle multiple identical parameters in the CGI-standard way instead of
     // PHP's foo[]=bar way
     $queryParams = Zotero_URL::proper_parse_str($queryString);
     $finalParams = [];
     //
     // Handle some special cases
     //
     // If client accepts Atom, serve it if an explicit format isn't requested
     if ($atomAccepted && empty($queryParams['format'])) {
         $queryParams['format'] = 'atom';
     }
     // Set API version based on header
     if ($apiVersion) {
         if (!empty($queryParams['v']) && $apiVersion != $queryParams['v']) {
             throw new Exception("Zotero-API-Version header does not match 'v' query parameter", Z_ERROR_INVALID_INPUT);
         }
         $queryParams['v'] = $apiVersion;
     } else {
         if (isset($queryParams['version']) && $queryParams['version'] == 1 && !isset($queryParams['v'])) {
             $queryParams['v'] = 1;
             unset($queryParams['version']);
         }
     }
     // If format=json, override version to 3
     if (!isset($queryParams['v']) && isset($queryParams['format']) && $queryParams['format'] == 'json') {
         $queryParams['v'] = 3;
     }
     $apiVersion = isset($queryParams['v']) ? $queryParams['v'] : self::$defaultParams['v'];
     // If 'content', override 'format' to 'atom'
     if (!isset($queryParams['format']) && isset($queryParams['content'])) {
         $queryParams['format'] = 'atom';
     }
     // Handle deprecated (in v3) 'order' parameter
     if (isset($queryParams['order'])) {
         // If 'order' is a direction, move it to 'direction'
         if (in_array($queryParams['order'], ['asc', 'desc'])) {
             $finalParams['direction'] = $queryParams['direction'] = $queryParams['order'];
         } else {
             // If 'sort' already has a direction, move that to 'direction' first
             if (isset($queryParams['sort']) && in_array($queryParams['sort'], ['asc', 'desc'])) {
                 $finalParams['direction'] = $queryParams['direction'] = $queryParams['sort'];
             }
             $queryParams['sort'] = $queryParams['order'];
         }
         unset($queryParams['order']);
     }
     // Handle deprecated (in v3) 'newer' and 'newertime' parameters
     if (isset($queryParams['newer'])) {
         if (!isset($queryParams['since'])) {
             $queryParams['since'] = $queryParams['newer'];
         }
         unset($queryParams['newer']);
     }
     if (isset($queryParams['newertime'])) {
         if (!isset($queryParams['sincetime'])) {
             $queryParams['sincetime'] = $queryParams['newertime'];
         }
         unset($queryParams['newertime']);
     }
     foreach (self::resolveDefaultParams($action, self::$defaultParams, $queryParams) as $key => $value) {
         // Don't overwrite field if already set (either above or derived from another field)
         if (!empty($finalParams[$key])) {
             continue;
         }
         // Fill defaults
         $finalParams[$key] = $value;
         // Ignore private parameters in the URL
         if (in_array($key, self::getPrivateParams($key)) && isset($queryParams[$key])) {
             continue;
         }
         // If no parameter passed, use default
         if (!isset($queryParams[$key])) {
             continue;
         }
         // Use query parameter as value
         $value = $queryParams[$key];
         if (isset($finalParams['format'])) {
             $format = $finalParams['format'];
             // Some formats need special parameter handling
             if ($format == 'bib') {
                 switch ($key) {
                     // Invalid parameters
                     case 'order':
                     case 'sort':
                     case 'start':
                     case 'limit':
                     case 'direction':
                         throw new Exception("'{$key}' is not valid for format=bib", Z_ERROR_INVALID_INPUT);
                 }
             } else {
                 if ($apiVersion < 3 && in_array($format, array('keys', 'versions'))) {
                     switch ($key) {
                         // Invalid parameters
                         case 'start':
                             throw new Exception("'{$key}' is not valid for format={$format}", Z_ERROR_INVALID_INPUT);
                     }
                 }
             }
         }
         switch ($key) {
             case 'v':
                 if (!in_array($value, self::$validAPIVersions)) {
                     throw new Exception("Invalid API version '{$value}'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'format':
                 if (!self::isValidFormatForAction($action, $value, $singleObject)) {
                     throw new Exception("Invalid 'format' value '{$value}'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'since':
             case 'sincetime':
                 if (!is_numeric($value)) {
                     throw new Exception("Invalid value for '{$key}' parameter", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'start':
                 $value = (int) $value;
                 break;
             case 'limit':
                 // Maximum limit depends on 'format'
                 $limitMax = self::getLimitMax($format);
                 // Since the export formats and csljson don't give a clear indication of limiting or
                 // rel="next" links in API v1/2 (before the Link header), require an explicit limit for
                 // everything other than single items and itemKey queries
                 if ($apiVersion < 3 && (in_array($format, Zotero_Translate::$exportFormats) || $format == 'csljson') && !$singleObject && empty($queryParams['itemKey'])) {
                     if (empty($value)) {
                         throw new Exception("'limit' is required for format={$format}", Z_ERROR_INVALID_INPUT);
                     } else {
                         if ($value > $limitMax) {
                             throw new Exception("'limit' cannot be greater than {$limitMax} for format={$format}", Z_ERROR_INVALID_INPUT);
                         }
                     }
                 }
                 // If there's a maximum, enforce it
                 if ($limitMax && (int) $value > $limitMax) {
                     $value = $limitMax;
                 } else {
                     if ((int) $value == 0) {
                         continue 2;
                     }
                 }
                 $value = (int) $value;
                 break;
             case 'include':
             case 'content':
                 if ($key == 'content' && $format != 'atom') {
                     throw new Exception("'content' is valid only for format=atom", Z_ERROR_INVALID_INPUT);
                 } else {
                     if ($key == 'include' && $format != 'json') {
                         throw new Exception("'include' is valid only for format=json", Z_ERROR_INVALID_INPUT);
                     }
                 }
                 $value = array_values(array_unique(explode(',', $value)));
                 sort($value);
                 foreach ($value as $includeType) {
                     switch ($includeType) {
                         case 'none':
                             if (sizeOf($value) > 1) {
                                 throw new Exception("{$key}={$includeType} is not valid in multi-format responses", Z_ERROR_INVALID_INPUT);
                             }
                             break;
                         case 'html':
                         case 'citation':
                         case 'bib':
                         case 'csljson':
                             break;
                         case 'json':
                             if ($format != 'atom') {
                                 throw new Exception("{$key}={$includeType} is valid only for format=atom", Z_ERROR_INVALID_INPUT);
                             }
                             break;
                         case 'data':
                             if ($format != 'json') {
                                 throw new Exception("{$key}={$includeType} is valid only for format=json", Z_ERROR_INVALID_INPUT);
                             }
                             break;
                         default:
                             if (in_array($includeType, Zotero_Translate::$exportFormats)) {
                                 break;
                             }
                             throw new Exception("Invalid '{$key}' value '{$includeType}'", Z_ERROR_INVALID_INPUT);
                     }
                 }
                 break;
             case 'sort':
                 // If direction, move to 'direction' and use default 'sort' value
                 if (in_array($value, array('asc', 'desc'))) {
                     $finalParams['direction'] = $queryParams['direction'] = $value;
                     continue 2;
                 }
                 // Whether to sort empty values first
                 $finalParams['emptyFirst'] = self::getSortEmptyFirst($value);
                 switch ($value) {
                     // 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':
                     case 'collectionKeyList':
                     case 'itemKeyList':
                     case 'searchKeyList':
                         switch ($value) {
                             // numItems is valid only for tags requests
                             case 'numItems':
                                 if ($action != 'tags') {
                                     throw new Exception("Invalid 'order' value '{$value}'", Z_ERROR_INVALID_INPUT);
                                 }
                                 break;
                             case 'collectionKeyList':
                                 if ($action != 'collections') {
                                     throw new Exception("order=collectionKeyList is not valid for this request");
                                 }
                                 if (!isset($queryParams['collectionKey'])) {
                                     throw new Exception("order=collectionKeyList requires the collectionKey parameter");
                                 }
                                 break;
                             case 'itemKeyList':
                                 if ($action != 'items') {
                                     throw new Exception("order=itemKeyList is not valid for this request");
                                 }
                                 if (!isset($queryParams['itemKey'])) {
                                     throw new Exception("order=itemKeyList requires the itemKey parameter");
                                 }
                                 break;
                             case 'searchKeyList':
                                 if ($action != 'searches') {
                                     throw new Exception("order=searchKeyList is not valid for this request");
                                 }
                                 if (!isset($queryParams['searchKey'])) {
                                     throw new Exception("order=searchKeyList requires the searchKey parameter");
                                 }
                                 break;
                         }
                         if (!isset($queryParams['direction'])) {
                             $finalParams['direction'] = self::getDefaultDirection($value);
                         }
                         break;
                     default:
                         throw new Exception("Invalid 'sort' value '{$value}'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'direction':
                 if (!in_array($value, array('asc', 'desc'))) {
                     throw new Exception("Invalid '{$key}' value '{$value}'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'qmode':
                 if (!in_array($value, array('titleCreatorYear', 'everything'))) {
                     throw new Exception("Invalid '{$key}' value '{$value}'", Z_ERROR_INVALID_INPUT);
                 }
                 break;
             case 'collectionKey':
             case 'itemKey':
             case 'searchKey':
                 // Allow leading/trailing commas
                 $objectKeys = trim($value, ",");
                 $objectKeys = explode(",", $objectKeys);
                 // Make sure all keys are plausible
                 foreach ($objectKeys as $objectKey) {
                     if (!Zotero_ID::isValidKey($objectKey)) {
                         throw new Exception("Invalid '{$key}' value '{$value}'", Z_ERROR_INVALID_INPUT);
                     }
                 }
                 $value = $objectKeys;
                 // Force limit if explicit object keys are used
                 $finalParams['limit'] = self::MAX_OBJECT_KEYS;
                 break;
             case 'includeTrashed':
             case 'uncached':
                 $value = !!$value;
                 break;
         }
         $finalParams[$key] = $value;
     }
     return $finalParams;
 }
Beispiel #2
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;
 }