public function testExistsByLibraryAndKey() { $this->assertFalse(Zotero_Items::existsByLibraryAndKey(self::$config['userLibraryID'], "AAAAAAAA")); $item = new Zotero_Item(); $item->libraryID = self::$config['userLibraryID']; $item->itemTypeID = Zotero_ItemTypes::getID("book"); $item->save(); $key = $item->key; $this->assertTrue(Zotero_Items::existsByLibraryAndKey(self::$config['userLibraryID'], $key)); Zotero_Items::delete(self::$config['userLibraryID'], $key); $this->assertFalse(Zotero_Items::existsByLibraryAndKey(self::$config['userLibraryID'], $key)); }
private static function validateJSONItem($json, $libraryID, Zotero_Item $item = null, $isChild, $requestParams, $partialUpdate = false) { $isNew = !$item || !$item->version; if (!is_object($json)) { throw new Exception("Invalid item object (found " . gettype($json) . " '" . $json . "')", Z_ERROR_INVALID_INPUT); } if (isset($json->items) && is_array($json->items)) { throw new Exception("An 'items' array is not valid for single-item updates", Z_ERROR_INVALID_INPUT); } $apiVersion = $requestParams['v']; if ($partialUpdate) { $requiredProps = []; } else { if (isset($json->itemType) && $json->itemType == "attachment") { $requiredProps = array('linkMode', 'tags'); } else { if (isset($json->itemType) && $json->itemType == "attachment") { $requiredProps = array('tags'); } else { if ($isNew) { $requiredProps = array('itemType'); } else { if ($apiVersion < 2) { $requiredProps = array('itemType', 'tags'); } else { $requiredProps = array('itemType', 'tags', 'relations'); if (!$isChild) { $requiredProps[] = 'collections'; } } } } } } foreach ($requiredProps as $prop) { if (!isset($json->{$prop})) { throw new Exception("'{$prop}' property not provided", Z_ERROR_INVALID_INPUT); } } // For partial updates where item type isn't provided, use the existing item type if (!isset($json->itemType) && $partialUpdate) { $itemType = Zotero_ItemTypes::getName($item->itemTypeID); } else { $itemType = $json->itemType; } foreach ($json as $key => $val) { switch ($key) { // Handled by Zotero_API::checkJSONObjectVersion() case 'key': case 'version': if ($apiVersion < 3) { throw new Exception("Invalid property '{$key}'", Z_ERROR_INVALID_INPUT); } break; case 'itemKey': case 'itemVersion': if ($apiVersion != 2) { throw new Exception("Invalid property '{$key}'", Z_ERROR_INVALID_INPUT); } break; case 'parentItem': if ($apiVersion < 2) { throw new Exception("Invalid property '{$key}'", Z_ERROR_INVALID_INPUT); } if (!Zotero_ID::isValidKey($val) && $val !== false) { throw new Exception("'{$key}' must be a valid item key", Z_ERROR_INVALID_INPUT); } break; case 'itemType': if (!is_string($val)) { throw new Exception("'itemType' must be a string", Z_ERROR_INVALID_INPUT); } if ($isChild || !empty($json->parentItem)) { switch ($val) { case 'note': case 'attachment': break; default: throw new Exception("Child item must be note or attachment", Z_ERROR_INVALID_INPUT); } } else { if ($val == 'attachment' && (!$item || !$item->getSource())) { if ($json->linkMode == 'linked_url' || $json->linkMode == 'imported_url' && (empty($json->contentType) || $json->contentType != 'application/pdf')) { throw new Exception("Only file attachments and PDFs can be top-level items", Z_ERROR_INVALID_INPUT); } } } if (!Zotero_ItemTypes::getID($val)) { throw new Exception("'{$val}' is not a valid itemType", Z_ERROR_INVALID_INPUT); } break; case 'tags': if (!is_array($val)) { throw new Exception("'{$key}' property must be an array", Z_ERROR_INVALID_INPUT); } foreach ($val as $tag) { $empty = true; if (is_string($tag)) { if ($tag === "") { throw new Exception("Tag cannot be empty", Z_ERROR_INVALID_INPUT); } continue; } if (!is_object($tag)) { throw new Exception("Tag must be an object", Z_ERROR_INVALID_INPUT); } foreach ($tag as $k => $v) { switch ($k) { case 'tag': if (!is_scalar($v)) { throw new Exception("Invalid tag name", Z_ERROR_INVALID_INPUT); } if ($v === "") { throw new Exception("Tag cannot be empty", Z_ERROR_INVALID_INPUT); } break; case 'type': if (!is_numeric($v)) { throw new Exception("Invalid tag type '{$v}'", Z_ERROR_INVALID_INPUT); } break; default: throw new Exception("Invalid tag property '{$k}'", Z_ERROR_INVALID_INPUT); } $empty = false; } if ($empty) { throw new Exception("Tag object is empty", Z_ERROR_INVALID_INPUT); } } break; case 'collections': if (!is_array($val)) { throw new Exception("'{$key}' property must be an array", Z_ERROR_INVALID_INPUT); } if ($isChild && $val) { throw new Exception("Child items cannot be assigned to collections", Z_ERROR_INVALID_INPUT); } foreach ($val as $k) { if (!Zotero_ID::isValidKey($k)) { throw new Exception("'{$k}' is not a valid collection key", Z_ERROR_INVALID_INPUT); } } break; case 'relations': if ($apiVersion < 2) { throw new Exception("Invalid property '{$key}'", Z_ERROR_INVALID_INPUT); } if (!is_object($val) && !(is_array($val) && empty($val))) { throw new Exception("'{$key}' property must be an object", Z_ERROR_INVALID_INPUT); } foreach ($val as $predicate => $object) { switch ($predicate) { case 'owl:sameAs': case 'dc:replaces': case 'dc:relation': break; default: throw new Exception("Unsupported predicate '{$predicate}'", Z_ERROR_INVALID_INPUT); } $arr = is_string($object) ? [$object] : $object; foreach ($arr as $uri) { if (!preg_match('/^http:\\/\\/zotero.org\\/(users|groups)\\/[0-9]+\\/(publications\\/)?items\\/[A-Z0-9]{8}$/', $uri)) { throw new Exception("'{$key}' values currently must be Zotero item URIs", Z_ERROR_INVALID_INPUT); } } } break; case 'creators': if (!is_array($val)) { throw new Exception("'{$key}' property must be an array", Z_ERROR_INVALID_INPUT); } foreach ($val as $creator) { $empty = true; if (!isset($creator->creatorType)) { throw new Exception("creator object must contain 'creatorType'", Z_ERROR_INVALID_INPUT); } if ((!isset($creator->name) || trim($creator->name) == "") && (!isset($creator->firstName) || trim($creator->firstName) == "") && (!isset($creator->lastName) || trim($creator->lastName) == "")) { // On item creation, ignore single nameless creator, // because that's in the item template that the API returns if (sizeOf($val) == 1 && $isNew) { continue; } else { throw new Exception("creator object must contain 'firstName'/'lastName' or 'name'", Z_ERROR_INVALID_INPUT); } } foreach ($creator as $k => $v) { switch ($k) { case 'creatorType': $creatorTypeID = Zotero_CreatorTypes::getID($v); if (!$creatorTypeID) { throw new Exception("'{$v}' is not a valid creator type", Z_ERROR_INVALID_INPUT); } $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!Zotero_CreatorTypes::isValidForItemType($creatorTypeID, $itemTypeID)) { // Allow 'author' in all item types, but reject other invalid creator types if ($creatorTypeID != Zotero_CreatorTypes::getID('author')) { throw new Exception("'{$v}' is not a valid creator type for item type '{$itemType}'", Z_ERROR_INVALID_INPUT); } } break; case 'firstName': if (!isset($creator->lastName)) { throw new Exception("'lastName' creator field must be set if 'firstName' is set", Z_ERROR_INVALID_INPUT); } if (isset($creator->name)) { throw new Exception("'firstName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } break; case 'lastName': if (!isset($creator->firstName)) { throw new Exception("'firstName' creator field must be set if 'lastName' is set", Z_ERROR_INVALID_INPUT); } if (isset($creator->name)) { throw new Exception("'lastName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } break; case 'name': if (isset($creator->firstName)) { throw new Exception("'firstName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } if (isset($creator->lastName)) { throw new Exception("'lastName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } break; default: throw new Exception("Invalid creator property '{$k}'", Z_ERROR_INVALID_INPUT); } $empty = false; } if ($empty) { throw new Exception("Creator object is empty", Z_ERROR_INVALID_INPUT); } } break; case 'note': switch ($itemType) { case 'note': case 'attachment': break; default: throw new Exception("'note' property is valid only for note and attachment items", Z_ERROR_INVALID_INPUT); } break; case 'attachments': case 'notes': if ($apiVersion > 1) { throw new Exception("'{$key}' property is no longer supported", Z_ERROR_INVALID_INPUT); } if (!$isNew) { throw new Exception("'{$key}' property is valid only for new items", Z_ERROR_INVALID_INPUT); } if (!is_array($val)) { throw new Exception("'{$key}' property must be an array", Z_ERROR_INVALID_INPUT); } foreach ($val as $child) { // Check child item type ('attachment' or 'note') $t = substr($key, 0, -1); if (isset($child->itemType) && $child->itemType != $t) { throw new Exception("Child {$t} must be of itemType '{$t}'", Z_ERROR_INVALID_INPUT); } if ($key == 'note') { if (!isset($child->note)) { throw new Exception("'note' property not provided for child note", Z_ERROR_INVALID_INPUT); } } } break; case 'deleted': break; // Attachment properties // Attachment properties case 'linkMode': try { $linkMode = Zotero_Attachments::linkModeNameToNumber($val, true); } catch (Exception $e) { throw new Exception("'{$val}' is not a valid linkMode", Z_ERROR_INVALID_INPUT); } // Don't allow changing of linkMode if (!$isNew && $linkMode != $item->attachmentLinkMode) { throw new Exception("Cannot change attachment linkMode", Z_ERROR_INVALID_INPUT); } break; case 'contentType': case 'charset': case 'filename': case 'md5': case 'mtime': if ($itemType != 'attachment') { throw new Exception("'{$key}' is valid only for attachment items", Z_ERROR_INVALID_INPUT); } switch ($key) { case 'filename': case 'md5': case 'mtime': $lm = isset($json->linkMode) ? $json->linkMode : Zotero_Attachments::linkModeNumberToName($item->attachmentLinkMode); if (strpos(strtolower($lm), 'imported_') !== 0) { throw new Exception("'{$key}' is valid only for imported attachment items", Z_ERROR_INVALID_INPUT); } break; } switch ($key) { case 'contentType': case 'charset': case 'filename': $propName = 'attachment' . ucwords($key); break; case 'md5': $propName = 'attachmentStorageHash'; break; case 'mtime': $propName = 'attachmentStorageModTime'; break; } if (Zotero_Libraries::getType($libraryID) == 'group') { if ($item && $item->{$propName} !== $val || !$item && $val !== null && $val !== "") { throw new Exception("Cannot change '{$key}' directly in group library", Z_ERROR_INVALID_INPUT); } } else { if ($key == 'md5') { if ($val && !preg_match("/^[a-f0-9]{32}\$/", $val)) { throw new Exception("'{$val}' is not a valid MD5 hash", Z_ERROR_INVALID_INPUT); } } } break; case 'accessDate': if ($apiVersion >= 3 && $val !== '' && $val != 'CURRENT_TIMESTAMP' && !Zotero_Date::isSQLDate($val) && !Zotero_Date::isSQLDateTime($val) && !Zotero_Date::isISO8601($val)) { throw new Exception("'{$key}' must be in ISO 8601 or UTC 'YYYY-MM-DD[ hh-mm-dd]' format or 'CURRENT_TIMESTAMP' ({$val})", Z_ERROR_INVALID_INPUT); } break; case 'dateAdded': if (!Zotero_Date::isSQLDateTime($val) && !Zotero_Date::isISO8601($val)) { throw new Exception("'{$key}' must be in ISO 8601 or UTC 'YYYY-MM-DD hh-mm-dd' format", Z_ERROR_INVALID_INPUT); } if (!$isNew) { // Convert ISO date to SQL date for equality comparison if (Zotero_Date::isISO8601($val)) { $val = Zotero_Date::iso8601ToSQL($val); } if ($val != $item->{$key}) { throw new Exception("'{$key}' cannot be modified for existing items", Z_ERROR_INVALID_INPUT); } } break; case 'dateModified': if (!Zotero_Date::isSQLDateTime($val) && !Zotero_Date::isISO8601($val)) { throw new Exception("'{$key}' must be in ISO 8601 or UTC 'YYYY-MM-DD hh-mm-dd' format ({$val})", Z_ERROR_INVALID_INPUT); } break; default: if (!Zotero_ItemFields::getID($key)) { throw new Exception("Invalid property '{$key}'", Z_ERROR_INVALID_INPUT); } if (is_array($val)) { throw new Exception("Unexpected array for property '{$key}'", Z_ERROR_INVALID_INPUT); } break; } } // Publications libraries have additional restrictions if (Zotero_Libraries::getType($libraryID) == 'publications') { Zotero_Publications::validateJSONItem($json); } }
public function testNumAttachments() { $item = new Zotero_Item(); $item->libraryID = self::$config['userLibraryID']; $item->itemTypeID = Zotero_ItemTypes::getID("book"); $item->save(); $this->assertEquals(0, $item->numAttachments()); $attachmentItem = new Zotero_Item(); $attachmentItem->libraryID = self::$config['userLibraryID']; $attachmentItem->itemTypeID = Zotero_ItemTypes::getID("attachment"); $attachmentItem->setSource($item->id); $attachmentItem->save(); $this->assertEquals(1, $item->numAttachments()); }
/** * @param {Integer} $itemTypeID itemTypeID to change to * @param {Boolean} [$loadIn=false] Internal call, so don't flag field as changed */ private function setType($itemTypeID, $loadIn=false) { if ($itemTypeID == $this->_itemTypeID) { return true; } // TODO: block switching to/from note or attachment if (!Zotero_ItemTypes::getID($itemTypeID)) { throw new Exception("Invalid itemTypeID", Z_ERROR_INVALID_INPUT); } $copiedFields = array(); $oldItemTypeID = $this->_itemTypeID; if ($oldItemTypeID) { if ($loadIn) { throw new Exception('Cannot change type in loadIn mode'); } if (!$this->loaded['itemData'] && $this->id) { $this->loadItemData(); } $obsoleteFields = $this->getFieldsNotInType($itemTypeID); if ($obsoleteFields) { foreach($obsoleteFields as $oldFieldID) { // Try to get a base type for this field $baseFieldID = Zotero_ItemFields::getBaseIDFromTypeAndField($this->_itemTypeID, $oldFieldID); if ($baseFieldID) { $newFieldID = Zotero_ItemFields::getFieldIDFromTypeAndBase($itemTypeID, $baseFieldID); // If so, save value to copy to new field if ($newFieldID) { $copiedFields[] = array($newFieldID, $this->getField($oldFieldID)); } } // Clear old field $this->setField($oldFieldID, false); } } foreach ($this->itemData as $fieldID => $value) { if (!is_null($this->itemData[$fieldID]) && (!$obsoleteFields || !in_array($fieldID, $obsoleteFields))) { $copiedFields[] = array($fieldID, $this->getField($fieldID)); } } } $this->_itemTypeID = $itemTypeID; if ($oldItemTypeID) { // Reset custom creator types to the default $creators = $this->getCreators(); if ($creators) { foreach ($creators as $orderIndex=>$creator) { if (Zotero_CreatorTypes::isCustomType($creator['creatorTypeID'])) { continue; } if (!Zotero_CreatorTypes::isValidForItemType($creator['creatorTypeID'], $itemTypeID)) { // TODO: port // Reset to contributor (creatorTypeID 2), which exists in all $this->setCreator($orderIndex, $creator['ref'], 2); } } } } // If not custom item type, initialize $this->itemData with type-specific fields $this->itemData = array(); if (!Zotero_ItemTypes::isCustomType($itemTypeID)) { $fields = Zotero_ItemFields::getItemTypeFields($itemTypeID); foreach($fields as $fieldID) { $this->itemData[$fieldID] = null; } } if ($copiedFields) { foreach($copiedFields as $copiedField) { $this->setField($copiedField[0], $copiedField[1]); } } if ($loadIn) { $this->loaded['itemData'] = false; } else { $this->changed['primaryData']['itemTypeID'] = true; } return true; }
public function newItem() { if (empty($_GET['itemType'])) { $this->e400("'itemType' not provided"); } $itemType = $_GET['itemType']; if ($itemType == 'attachment') { if (empty($_GET['linkMode'])) { $this->e400("linkMode required for itemType=attachment"); } $linkModeName = $_GET['linkMode']; try { $linkMode = Zotero_Attachments::linkModeNameToNumber(strtoupper($linkModeName)); } catch (Exception $e) { $this->e400("Invalid linkMode '{$linkModeName}'"); } } $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { $this->e400("Invalid item type '{$itemType}'"); } // TODO: check If-Modified-Since and return 304 if not changed $cacheKey = "newItemJSON_" . $itemTypeID; if ($itemType == 'attachment') { $cacheKey .= "_" . $linkMode; } $ttl = 60; if ($this->queryParams['pprint']) { $cacheKey .= "_pprint"; } $json = Z_Core::$MC->get($cacheKey); if ($json) { header("Content-Type: application/json"); echo $json; exit; } // Generate template $json = array('itemType' => $itemType); if ($itemType == 'attachment') { $json['linkMode'] = $linkModeName; } $fieldIDs = Zotero_ItemFields::getItemTypeFields($itemTypeID); $first = true; foreach ($fieldIDs as $fieldID) { $fieldName = Zotero_ItemFields::getName($fieldID); if ($itemType == 'attachment' && $fieldName == 'url' && !preg_match('/_url$/', $linkModeName)) { continue; } $json[$fieldName] = ""; if ($first && $itemType != 'note' && $itemType != 'attachment') { $creatorTypeID = Zotero_CreatorTypes::getPrimaryIDForType($itemTypeID); $creatorTypeName = Zotero_CreatorTypes::getName($creatorTypeID); $json['creators'] = array(array('creatorType' => $creatorTypeName, 'firstName' => '', 'lastName' => '')); $first = false; } } if ($itemType == 'note' || $itemType == 'attachment') { $json['note'] = ''; } $json['tags'] = array(); if ($itemType != 'note' && $itemType != 'attachment') { $json['attachments'] = array(); $json['notes'] = array(); } if ($itemType == 'attachment') { $json['contentType'] = ''; $json['charset'] = ''; if (preg_match('/^imported_/', $linkModeName)) { $json['filename'] = ''; $json['md5'] = null; $json['mtime'] = null; //$json['zip'] = false; } } header("Content-Type: application/json"); if ($this->queryParams['pprint']) { $json = Zotero_Utilities::json_encode_pretty($json); Z_Core::$MC->set($cacheKey, $json, $ttl); } else { $json = json_encode($json); Z_Core::$MC->set($cacheKey, $json, $ttl); } echo $json; exit; }
private static function validateJSONItem($json, $libraryID, $item = null, $isChild = false) { $isNew = !$item; if (!is_object($json)) { throw new Exception("Invalid item object (found " . gettype($json) . " '" . $json . "')", Z_ERROR_INVALID_INPUT); } if (isset($json->items) && is_array($json->items)) { throw new Exception("An 'items' array is not valid for item updates", Z_ERROR_INVALID_INPUT); } if (isset($json->itemType) && $json->itemType == "attachment") { $requiredProps = array('linkMode', 'tags'); } else { if (isset($json->itemType) && $json->itemType == "attachment") { $requiredProps = array('tags'); } else { if ($isNew) { $requiredProps = array('itemType'); } else { $requiredProps = array('itemType', 'creators', 'tags'); } } } foreach ($requiredProps as $prop) { if (!isset($json->{$prop})) { throw new Exception("'{$prop}' property not provided", Z_ERROR_INVALID_INPUT); } } foreach ($json as $key => $val) { switch ($key) { case 'itemType': if (!is_string($val)) { throw new Exception("'itemType' must be a string", Z_ERROR_INVALID_INPUT); } if ($isChild) { switch ($val) { case 'note': case 'attachment': break; default: throw new Exception("Child item must be note or attachment", Z_ERROR_INVALID_INPUT); } } else { if ($val == 'attachment' && (!$item || !$item->getSource())) { if ($json->linkMode == 'linked_url' || $json->linkMode == 'imported_url' && (empty($json->contentType) || $json->contentType != 'application/pdf')) { throw new Exception("Only file attachments and PDFs can be top-level items", Z_ERROR_INVALID_INPUT); } } } if (!Zotero_ItemTypes::getID($val)) { throw new Exception("'{$val}' is not a valid itemType", Z_ERROR_INVALID_INPUT); } break; case 'tags': if (!is_array($val)) { throw new Exception("'tags' property must be an array", Z_ERROR_INVALID_INPUT); } foreach ($val as $tag) { $empty = true; foreach ($tag as $k => $v) { switch ($k) { case 'tag': if (!is_scalar($v)) { throw new Exception("Invalid tag name", Z_ERROR_INVALID_INPUT); } if ($v === "") { throw new Exception("Tag cannot be empty", Z_ERROR_INVALID_INPUT); } break; case 'type': if (!is_numeric($v)) { throw new Exception("Invalid tag type '{$v}'", Z_ERROR_INVALID_INPUT); } break; default: throw new Exception("Invalid tag property '{$k}'", Z_ERROR_INVALID_INPUT); } $empty = false; } if ($empty) { throw new Exception("Tag object is empty", Z_ERROR_INVALID_INPUT); } } break; case 'creators': if (!is_array($val)) { throw new Exception("'creators' property must be an array", Z_ERROR_INVALID_INPUT); } foreach ($val as $creator) { $empty = true; if (!isset($creator->creatorType)) { throw new Exception("creator object must contain 'creatorType'", Z_ERROR_INVALID_INPUT); } if ((!isset($creator->name) || trim($creator->name) == "") && (!isset($creator->firstName) || trim($creator->firstName) == "") && (!isset($creator->lastName) || trim($creator->lastName) == "")) { // On item creation, ignore single nameless creator, // because that's in the item template that the API returns if (sizeOf($val) == 1 && $isNew) { continue; } else { throw new Exception("creator object must contain 'firstName'/'lastName' or 'name'", Z_ERROR_INVALID_INPUT); } } foreach ($creator as $k => $v) { switch ($k) { case 'creatorType': $creatorTypeID = Zotero_CreatorTypes::getID($v); if (!$creatorTypeID) { throw new Exception("'{$v}' is not a valid creator type", Z_ERROR_INVALID_INPUT); } $itemTypeID = Zotero_ItemTypes::getID($json->itemType); if (!Zotero_CreatorTypes::isValidForItemType($creatorTypeID, $itemTypeID)) { throw new Exception("'{$v}' is not a valid creator type for item type '" . $json->itemType . "'", Z_ERROR_INVALID_INPUT); } break; case 'firstName': if (!isset($creator->lastName)) { throw new Exception("'lastName' creator field must be set if 'firstName' is set", Z_ERROR_INVALID_INPUT); } if (isset($creator->name)) { throw new Exception("'firstName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } break; case 'lastName': if (!isset($creator->firstName)) { throw new Exception("'firstName' creator field must be set if 'lastName' is set", Z_ERROR_INVALID_INPUT); } if (isset($creator->name)) { throw new Exception("'lastName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } break; case 'name': if (isset($creator->firstName)) { throw new Exception("'firstName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } if (isset($creator->lastName)) { throw new Exception("'lastName' and 'name' creator fields are mutually exclusive", Z_ERROR_INVALID_INPUT); } break; default: throw new Exception("Invalid creator property '{$k}'", Z_ERROR_INVALID_INPUT); } $empty = false; } if ($empty) { throw new Exception("Creator object is empty", Z_ERROR_INVALID_INPUT); } } break; case 'note': switch ($json->itemType) { case 'note': case 'attachment': break; default: throw new Exception("'note' property is valid only for note and attachment items", Z_ERROR_INVALID_INPUT); } break; case 'attachments': case 'notes': if (!$isNew) { throw new Exception("'{$key}' property is valid only for new items", Z_ERROR_INVALID_INPUT); } if (!is_array($val)) { throw new Exception("'{$key}' property must be an array", Z_ERROR_INVALID_INPUT); } foreach ($val as $child) { // Check child item type ('attachment' or 'note') $t = substr($key, 0, -1); if (isset($child->itemType) && $child->itemType != $t) { throw new Exception("Child {$t} must be of itemType '{$t}'", Z_ERROR_INVALID_INPUT); } if ($key == 'note') { if (!isset($child->note)) { throw new Exception("'note' property not provided for child note", Z_ERROR_INVALID_INPUT); } } } break; case 'deleted': break; // Attachment properties // Attachment properties case 'linkMode': try { $linkMode = Zotero_Attachments::linkModeNameToNumber($val, true); } catch (Exception $e) { throw new Exception("'{$val}' is not a valid linkMode", Z_ERROR_INVALID_INPUT); } // Don't allow changing of linkMode if ($item && $linkMode != $item->attachmentLinkMode) { throw new Exception("Cannot change attachment linkMode", Z_ERROR_INVALID_INPUT); } break; case 'contentType': case 'charset': case 'filename': case 'md5': case 'mtime': if ($json->itemType != 'attachment') { throw new Exception("'{$key}' is valid only for attachment items", Z_ERROR_INVALID_INPUT); } switch ($key) { case 'filename': case 'md5': case 'mtime': if (strpos($json->linkMode, 'imported_') !== 0) { throw new Exception("'{$key}' is valid only for imported attachment items", Z_ERROR_INVALID_INPUT); } break; } switch ($key) { case 'contentType': case 'charset': case 'filename': $propName = 'attachment' . ucwords($key); break; case 'md5': $propName = 'attachmentStorageHash'; break; case 'mtime': $propName = 'attachmentStorageModTime'; break; } if (Zotero_Libraries::getType($libraryID) == 'group') { if ($item && $item->{$propName} !== $val || !$item && $val !== null && $val !== "") { throw new Exception("Cannot change '{$key}' directly in group library", Z_ERROR_INVALID_INPUT); } } else { if ($key == 'md5') { if ($val && !preg_match("/^[a-f0-9]{32}\$/", $val)) { throw new Exception("'{$val}' is not a valid MD5 hash", Z_ERROR_INVALID_INPUT); } } } break; default: if (!Zotero_ItemFields::getID($key)) { throw new Exception("Invalid property '{$key}'", Z_ERROR_INVALID_INPUT); } if (is_array($val)) { throw new Exception("Unexpected array for property '{$key}'", Z_ERROR_INVALID_INPUT); } break; } } }
public static function getBaseIDFromTypeAndField($itemType, $typeField) { $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { throw new Exception("Invalid item type '{$itemType}'"); } $typeFieldID = self::getID($typeField); if (!$typeFieldID) { throw new Exception("Invalid field '{$typeField}'"); } if (!self::isValidForType($typeFieldID, $itemTypeID)) { throw new Exception("'{$typeField}' is not a valid field for item type '{$itemType}'"); } // If typeField is already a base field, just return that if (self::isBaseField($typeFieldID)) { return $typeFieldID; } return Zotero_DB::valueQuery("SELECT baseFieldID FROM baseFieldMappings\n\t\t\tWHERE itemTypeID=? AND fieldID=?", array($itemTypeID, $typeFieldID)); }
public function newItem() { if (empty($_GET['itemType'])) { $this->e400("'itemType' not provided"); } $itemType = $_GET['itemType']; if ($itemType == 'attachment') { if (empty($_GET['linkMode'])) { $this->e400("linkMode required for itemType=attachment"); } $linkModeName = $_GET['linkMode']; try { $linkMode = Zotero_Attachments::linkModeNameToNumber(strtoupper($linkModeName)); } catch (Exception $e) { $this->e400("Invalid linkMode '{$linkModeName}'"); } } $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { $this->e400("Invalid item type '{$itemType}'"); } // TODO: check If-Modified-Since and return 304 if not changed $cacheVersion = 1; $cacheKey = "newItemJSON" . "_" . $this->apiVersion . "_" . $itemTypeID . "_" . $cacheVersion; if ($itemType == 'attachment') { $cacheKey .= "_" . $linkMode; } $cacheKey .= '_' . $this->apiVersion; $ttl = 60; $json = Z_Core::$MC->get($cacheKey); if ($json) { header("Content-Type: application/json"); echo $json; exit; } // Generate template $json = array('itemType' => $itemType); if ($itemType == 'attachment') { $json['linkMode'] = $linkModeName; } $fieldIDs = Zotero_ItemFields::getItemTypeFields($itemTypeID); $first = true; foreach ($fieldIDs as $fieldID) { $fieldName = Zotero_ItemFields::getName($fieldID); // Before v3, computerProgram's 'versionNumber' was just 'version' if ($this->apiVersion < 3 && $fieldID == 81) { $fieldName = 'version'; } if ($itemType == 'attachment' && $fieldName == 'url' && !preg_match('/_url$/', $linkModeName)) { continue; } $json[$fieldName] = ""; if ($first && $itemType != 'note' && $itemType != 'attachment') { $creatorTypeID = Zotero_CreatorTypes::getPrimaryIDForType($itemTypeID); $creatorTypeName = Zotero_CreatorTypes::getName($creatorTypeID); $json['creators'] = array(array('creatorType' => $creatorTypeName, 'firstName' => '', 'lastName' => '')); $first = false; } } if ($itemType == 'note' || $itemType == 'attachment') { $json['note'] = ''; } $json['tags'] = array(); if ($this->apiVersion >= 2) { $json['collections'] = array(); $json['relations'] = new stdClass(); } if ($this->apiVersion == 1) { if ($itemType != 'note' && $itemType != 'attachment') { $json['attachments'] = array(); $json['notes'] = array(); } } if ($itemType == 'attachment') { $json['contentType'] = ''; $json['charset'] = ''; if (preg_match('/^imported_/', $linkModeName)) { $json['filename'] = ''; $json['md5'] = null; $json['mtime'] = null; //$json['zip'] = false; } } header("Content-Type: application/json"); $json = Zotero_Utilities::formatJSON($json); Z_Core::$MC->set($cacheKey, $json, $ttl); echo $json; exit; }