Beispiel #1
0
 /**
  * Converts a Zotero_Item object to a SimpleXMLElement item
  *
  * @param	object				$item		Zotero_Item object
  * @param	array				$data
  * @return	SimpleXMLElement					Item data as SimpleXML element
  */
 public static function convertItemToXML(Zotero_Item $item, $data = array())
 {
     $t = microtime(true);
     // Check cache for all items except imported attachments,
     // which don't have their versions updated when the client
     // updates their file metadata
     if (!$item->isImportedAttachment()) {
         $cacheVersion = 1;
         $cacheKey = "syncXMLItem_" . $item->libraryID . "/" . $item->id . "_" . $item->version . "_" . md5(json_encode($data)) . "_" . $cacheVersion . (isset(Z_CONFIG::$CACHE_VERSION_SYNC_XML_ITEM) ? "_" . Z_CONFIG::$CACHE_VERSION_SYNC_XML_ITEM : "");
         $xmlstr = Z_Core::$MC->get($cacheKey);
     } else {
         $cacheKey = false;
         $xmlstr = false;
     }
     if ($xmlstr) {
         $xml = new SimpleXMLElement($xmlstr);
         StatsD::timing("api.items.itemToSyncXML.cached", (microtime(true) - $t) * 1000);
         StatsD::increment("memcached.items.itemToSyncXML.hit");
         // Skip the cache every 10 times for now, to ensure cache sanity
         if (Z_Core::probability(10)) {
             //$xmlstr = $xml->saveXML();
         } else {
             Z_Core::debug("Using cached sync XML item");
             return $xml;
         }
     }
     $xml = new SimpleXMLElement('<item/>');
     // Primary fields
     foreach (self::$primaryFields as $field) {
         switch ($field) {
             case 'id':
             case 'serverDateModified':
             case 'version':
                 continue 2;
             case 'itemTypeID':
                 $xmlField = 'itemType';
                 $xmlValue = Zotero_ItemTypes::getName($item->{$field});
                 break;
             default:
                 $xmlField = $field;
                 $xmlValue = $item->{$field};
         }
         $xml[$xmlField] = $xmlValue;
     }
     // Item data
     $itemTypeID = $item->itemTypeID;
     $fieldIDs = $item->getUsedFields();
     foreach ($fieldIDs as $fieldID) {
         $val = $item->getField($fieldID);
         if ($val == '') {
             continue;
         }
         $f = $xml->addChild('field', htmlspecialchars($val));
         $fieldName = Zotero_ItemFields::getName($fieldID);
         // Special handling for renamed computerProgram 'version' field
         if ($itemTypeID == 32 && $fieldName == 'versionNumber') {
             $fieldName = 'version';
         }
         $f['name'] = htmlspecialchars($fieldName);
     }
     // Deleted item flag
     if ($item->deleted) {
         $xml['deleted'] = '1';
     }
     if ($item->isNote() || $item->isAttachment()) {
         $sourceItemID = $item->getSource();
         if ($sourceItemID) {
             $sourceItem = Zotero_Items::get($item->libraryID, $sourceItemID);
             if (!$sourceItem) {
                 throw new Exception("Source item {$sourceItemID} not found");
             }
             $xml['sourceItem'] = $sourceItem->key;
         }
     }
     // Group modification info
     $createdByUserID = null;
     $lastModifiedByUserID = null;
     switch (Zotero_Libraries::getType($item->libraryID)) {
         case 'group':
             $createdByUserID = $item->createdByUserID;
             $lastModifiedByUserID = $item->lastModifiedByUserID;
             break;
     }
     if ($createdByUserID) {
         $xml['createdByUserID'] = $createdByUserID;
     }
     if ($lastModifiedByUserID) {
         $xml['lastModifiedByUserID'] = $lastModifiedByUserID;
     }
     if ($item->isAttachment()) {
         $xml['linkMode'] = $item->attachmentLinkMode;
         $xml['mimeType'] = $item->attachmentMIMEType;
         if ($item->attachmentCharset) {
             $xml['charset'] = $item->attachmentCharset;
         }
         $storageModTime = $item->attachmentStorageModTime;
         if ($storageModTime) {
             $xml['storageModTime'] = $storageModTime;
         }
         $storageHash = $item->attachmentStorageHash;
         if ($storageHash) {
             $xml['storageHash'] = $storageHash;
         }
         // TODO: get from a constant
         if ($item->attachmentLinkMode != 3) {
             $xml->addChild('path', htmlspecialchars($item->attachmentPath));
         }
     }
     // Note
     if ($item->isNote() || $item->isAttachment()) {
         // Get htmlspecialchars'ed note
         $note = $item->getNote(false, true);
         if ($note !== '') {
             $xml->addChild('note', $note);
         } else {
             if ($item->isNote()) {
                 $xml->addChild('note', '');
             }
         }
     }
     // Creators
     $creators = $item->getCreators();
     if ($creators) {
         foreach ($creators as $index => $creator) {
             $c = $xml->addChild('creator');
             $c['key'] = $creator['ref']->key;
             $c['creatorType'] = htmlspecialchars(Zotero_CreatorTypes::getName($creator['creatorTypeID']));
             $c['index'] = $index;
             if (empty($data['updatedCreators']) || !in_array($creator['ref']->id, $data['updatedCreators'])) {
                 $cNode = dom_import_simplexml($c);
                 $creatorXML = Zotero_Creators::convertCreatorToXML($creator['ref'], $cNode->ownerDocument);
                 $cNode->appendChild($creatorXML);
             }
         }
     }
     // Related items
     $relatedKeys = $item->relatedItems;
     $keys = array();
     foreach ($relatedKeys as $relatedKey) {
         if (Zotero_Items::getByLibraryAndKey($item->libraryID, $relatedKey)) {
             $keys[] = $relatedKey;
         }
     }
     if ($keys) {
         $xml->related = implode(' ', $keys);
     }
     if ($xmlstr) {
         $uncached = $xml->saveXML();
         if ($xmlstr != $uncached) {
             error_log("Cached sync XML item does not match");
             error_log("  Cached: " . $xmlstr);
             error_log("Uncached: " . $uncached);
         }
     } else {
         $xmlstr = $xml->saveXML();
         if ($cacheKey) {
             Z_Core::$MC->set($cacheKey, $xmlstr, 3600);
             // 1 hour for now
         }
         StatsD::timing("api.items.itemToSyncXML.uncached", (microtime(true) - $t) * 1000);
         StatsD::increment("memcached.items.itemToSyncXML.miss");
     }
     return $xml;
 }
Beispiel #2
0
	public function toJSON($asArray=false, $requestParams=array(), $includeEmpty=false, $unformattedFields=false) {
		if ($this->id || $this->key) {
			if (!$this->loaded['primaryData']) {
				$this->loadPrimaryData();
			}
			if (!$this->loaded['itemData']) {
				$this->loadItemData();
			}
		}
		
		if (!isset($requestParams['v'])) {
			$requestParams['v'] = 3;
		}
		
		$regularItem = $this->isRegularItem();
		
		$arr = array();
		if ($requestParams['v'] >= 2) {
			if ($requestParams['v'] >= 3) {
				$arr['key'] = $this->key;
				$arr['version'] = $this->version;
			}
			else {
				$arr['itemKey'] = $this->key;
				$arr['itemVersion'] = $this->version;
			}
			
			$key = $this->getSourceKey();
			if ($key) {
				$arr['parentItem'] = $key;
			}
		}
		$arr['itemType'] = Zotero_ItemTypes::getName($this->itemTypeID);
		
		if ($this->isAttachment()) {
			$val = $this->attachmentLinkMode;
			$arr['linkMode'] = strtolower(Zotero_Attachments::linkModeNumberToName($val));
		}
		
		// For regular items, show title and creators first
		if ($regularItem) {
			// Get 'title' or the equivalent base-mapped field
			$titleFieldID = Zotero_ItemFields::getFieldIDFromTypeAndBase($this->itemTypeID, 'title');
			$titleFieldName = Zotero_ItemFields::getName($titleFieldID);
			if ($includeEmpty || $this->itemData[$titleFieldID] !== false) {
				$arr[$titleFieldName] = $this->itemData[$titleFieldID] !== false ? $this->itemData[$titleFieldID] : "";
			}
			
			// Creators
			$arr['creators'] = array();
			$creators = $this->getCreators();
			foreach ($creators as $creator) {
				$c = array();
				$c['creatorType'] = Zotero_CreatorTypes::getName($creator['creatorTypeID']);
				
				// Single-field mode
				if ($creator['ref']->fieldMode == 1) {
					$c['name'] = $creator['ref']->lastName;
				}
				// Two-field mode
				else {
					$c['firstName'] = $creator['ref']->firstName;
					$c['lastName'] = $creator['ref']->lastName;
				}
				$arr['creators'][] = $c;
			}
			if (!$arr['creators'] && !$includeEmpty) {
				unset($arr['creators']);
			}
		}
		else {
			$titleFieldID = false;
		}
		
		// Item metadata
		$fields = array_keys($this->itemData);
		foreach ($fields as $field) {
			if ($field == $titleFieldID) {
				continue;
			}
			
			if ($unformattedFields) {
				$value = $this->itemData[$field];
			}
			else {
				$value = $this->getField($field);
			}
			
			if (!$includeEmpty && ($value === false || $value === "")) {
				continue;
			}
			
			$fieldName = Zotero_ItemFields::getName($field);
			// TEMP
			if ($fieldName == 'versionNumber') {
				if ($requestParams['v'] < 3) {
					$fieldName = 'version';
				}
			}
			else if ($fieldName == 'accessDate') {
				if ($requestParams['v'] >= 3 && $value !== false && $value !== "") {
					$value = Zotero_Date::sqlToISO8601($value);
				}
			}
			$arr[$fieldName] = ($value !== false && $value !== "") ? $value : "";
		}
		
		if ($requestParams['v'] >= 3) {
			$arr['dateAdded'] = Zotero_Date::sqlToISO8601($this->dateAdded);
			$arr['dateModified'] = Zotero_Date::sqlToISO8601($this->dateModified);
		}
		
		// Embedded note for notes and attachments
		if (!$regularItem) {
			// Use sanitized version
			$arr['note'] = $this->getNote(true);
		}
		
		if ($this->isAttachment()) {
			$val = $this->attachmentLinkMode;
			$arr['linkMode'] = strtolower(Zotero_Attachments::linkModeNumberToName($val));
			
			$val = $this->attachmentMIMEType;
			if ($includeEmpty || ($val !== false && $val !== "")) {
				$arr['contentType'] = $val;
			}
			
			$val = $this->attachmentCharset;
			if ($includeEmpty || ($val !== false && $val !== "")) {
				$arr['charset'] = $val;
			}
			
			if ($this->isImportedAttachment()) {
				$arr['filename'] = $this->attachmentFilename;
				
				$val = $this->attachmentStorageHash;
				if ($includeEmpty || $val) {
					$arr['md5'] = $val;
				}
				
				$val = $this->attachmentStorageModTime;
				if ($includeEmpty || $val) {
					$arr['mtime'] = $val;
				}
			}
		}
		
		if ($this->getDeleted()) {
			$arr['deleted'] = 1;
		}
		
		// Tags
		$arr['tags'] = array();
		$tags = $this->getTags();
		if ($tags) {
			foreach ($tags as $tag) {
				$t = array(
					'tag' => $tag->name
				);
				if ($tag->type != 0) {
					$t['type'] = $tag->type;
				}
				$arr['tags'][] = $t;
			}
		}
		
		if ($requestParams['v'] >= 2) {
			if ($this->isTopLevelItem()) {
				$collections = $this->getCollections(true);
				$arr['collections'] = $collections;
			}
			
			$arr['relations'] = $this->getRelations();
		}
		
		if ($asArray) {
			return $arr;
		}
		
		// Before v3, additional characters were escaped in the JSON, for unclear reasons
		$escapeAll = $requestParams['v'] <= 2;
		
		return Zotero_Utilities::formatJSON($arr, $escapeAll);
	}
Beispiel #3
0
 public function toSolrDocument()
 {
     $doc = new SolrInputDocument();
     $uri = Zotero_Solr::getItemURI($this->libraryID, $this->key);
     $doc->addField("uri", $uri);
     // Primary fields
     foreach (Zotero_Items::$primaryFields as $field) {
         switch ($field) {
             case 'itemID':
             case 'numAttachments':
             case 'numNotes':
                 continue 2;
             case 'itemTypeID':
                 $xmlField = 'itemType';
                 $xmlValue = Zotero_ItemTypes::getName($this->{$field});
                 break;
             case 'dateAdded':
             case 'dateModified':
             case 'serverDateModified':
                 $xmlField = $field;
                 $xmlValue = Zotero_Date::sqlToISO8601($this->{$field});
                 break;
             default:
                 $xmlField = $field;
                 $xmlValue = $this->{$field};
         }
         $doc->addField($xmlField, $xmlValue);
     }
     // Title for sorting
     $title = $this->getDisplayTitle(true);
     $title = $title ? $title : '';
     // Strip HTML from note titles
     if ($this->isNote()) {
         // Clean and strip HTML, giving us an HTML-encoded plaintext string
         $title = strip_tags($GLOBALS['HTMLPurifier']->purify($title));
         // Unencode plaintext string
         $title = html_entity_decode($title);
     }
     // Strip some characters
     $sortTitle = preg_replace("/^[\\[\\'\"]*(.*)[\\]\\'\"]*\$/", "\$1", $title);
     if ($sortTitle) {
         $doc->addField('titleSort', $sortTitle);
     }
     // Item data
     $fieldIDs = $this->getUsedFields();
     foreach ($fieldIDs as $fieldID) {
         $val = $this->getField($fieldID);
         if ($val == '') {
             continue;
         }
         $fieldName = Zotero_ItemFields::getName($fieldID);
         switch ($fieldName) {
             // As is
             case 'title':
                 $val = $title;
                 break;
                 // Date fields
             // Date fields
             case 'date':
                 // Add user part as text
                 $doc->addField($fieldName . "_t", Zotero_Date::multipartToStr($val));
                 // Add as proper date, if there is one
                 $sqlDate = Zotero_Date::multipartToSQL($val);
                 if (!$sqlDate || $sqlDate == '0000-00-00') {
                     continue 2;
                 }
                 $fieldName .= "_tdt";
                 $val = Zotero_Date::sqlToISO8601($sqlDate);
                 break;
             case 'accessDate':
                 if (!Zotero_Date::isSQLDateTime($val)) {
                     continue 2;
                 }
                 $fieldName .= "_tdt";
                 $val = Zotero_Date::sqlToISO8601($val);
                 break;
             default:
                 $fieldName .= "_t";
         }
         $doc->addField($fieldName, $val);
     }
     // Deleted item flag
     if ($this->getDeleted()) {
         $doc->addField('deleted', true);
     }
     if ($this->isNote() || $this->isAttachment()) {
         $sourceItemID = $this->getSource();
         if ($sourceItemID) {
             $sourceItem = Zotero_Items::get($this->libraryID, $sourceItemID);
             if (!$sourceItem) {
                 throw new Exception("Source item {$sourceItemID} not found");
             }
             $doc->addField('sourceItem', $sourceItem->key);
         }
     }
     // Group modification info
     $createdByUserID = null;
     $lastModifiedByUserID = null;
     switch (Zotero_Libraries::getType($this->libraryID)) {
         case 'group':
             $createdByUserID = $this->createdByUserID;
             $lastModifiedByUserID = $this->lastModifiedByUserID;
             break;
     }
     if ($createdByUserID) {
         $doc->addField('createdByUserID', $createdByUserID);
     }
     if ($lastModifiedByUserID) {
         $doc->addField('lastModifiedByUserID', $lastModifiedByUserID);
     }
     // Note
     if ($this->isNote()) {
         $doc->addField('note', $this->getNote());
     }
     if ($this->isAttachment()) {
         $doc->addField('linkMode', $this->attachmentLinkMode);
         $doc->addField('mimeType', $this->attachmentMIMEType);
         if ($this->attachmentCharset) {
             $doc->addField('charset', $this->attachmentCharset);
         }
         // TODO: get from a constant
         if ($this->attachmentLinkMode != 3) {
             $doc->addField('path', $this->attachmentPath);
         }
         $note = $this->getNote();
         if ($note) {
             $doc->addField('note', $note);
         }
     }
     // Creators
     $creators = $this->getCreators();
     if ($creators) {
         foreach ($creators as $index => $creator) {
             $c = $creator['ref'];
             $doc->addField('creatorKey', $c->key);
             if ($c->fieldMode == 0) {
                 $doc->addField('creatorFirstName', $c->firstName);
             }
             $doc->addField('creatorLastName', $c->lastName);
             $doc->addField('creatorType', Zotero_CreatorTypes::getName($creator['creatorTypeID']));
             $doc->addField('creatorIndex', $index);
         }
     }
     // Tags
     $tags = $this->getTags();
     if ($tags) {
         foreach ($tags as $tag) {
             $doc->addField('tagKey', $tag->key);
             $doc->addField('tag', $tag->name);
             $doc->addField('tagType', $tag->type);
         }
     }
     // Related items
     /*$related = $this->relatedItems;
     		if ($related) {
     			$related = Zotero_Items::get($this->libraryID, $related);
     			$keys = array();
     			foreach ($related as $item) {
     				$doc->addField('relatedItem', $item->key);
     			}
     		}*/
     return $doc;
 }
 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;
 }
Beispiel #5
0
 /**
  * Converts a Zotero_Item object to a SimpleXMLElement item
  *
  * @param	object				$item		Zotero_Item object
  * @param	array				$data
  * @return	SimpleXMLElement					Item data as SimpleXML element
  */
 public static function convertItemToXML(Zotero_Item $item, $data = array(), $apiVersion = null)
 {
     $xml = new SimpleXMLElement('<item/>');
     // Primary fields
     foreach (Zotero_Items::$primaryFields as $field) {
         switch ($field) {
             case 'itemID':
             case 'serverDateModified':
             case 'itemVersion':
             case 'numAttachments':
             case 'numNotes':
                 continue 2;
             case 'itemTypeID':
                 $xmlField = 'itemType';
                 $xmlValue = Zotero_ItemTypes::getName($item->{$field});
                 break;
             default:
                 $xmlField = $field;
                 $xmlValue = $item->{$field};
         }
         $xml[$xmlField] = $xmlValue;
     }
     // Item data
     $fieldIDs = $item->getUsedFields();
     foreach ($fieldIDs as $fieldID) {
         $val = $item->getField($fieldID);
         if ($val == '') {
             continue;
         }
         $f = $xml->addChild('field', htmlspecialchars($val));
         $f['name'] = htmlspecialchars(Zotero_ItemFields::getName($fieldID));
     }
     // Deleted item flag
     if ($item->deleted) {
         $xml['deleted'] = '1';
     }
     if ($item->isNote() || $item->isAttachment()) {
         $sourceItemID = $item->getSource();
         if ($sourceItemID) {
             $sourceItem = Zotero_Items::get($item->libraryID, $sourceItemID);
             if (!$sourceItem) {
                 throw new Exception("Source item {$sourceItemID} not found");
             }
             $xml['sourceItem'] = $sourceItem->key;
         }
     }
     // Group modification info
     $createdByUserID = null;
     $lastModifiedByUserID = null;
     switch (Zotero_Libraries::getType($item->libraryID)) {
         case 'group':
             $createdByUserID = $item->createdByUserID;
             $lastModifiedByUserID = $item->lastModifiedByUserID;
             break;
     }
     if ($createdByUserID) {
         $xml['createdByUserID'] = $createdByUserID;
     }
     if ($lastModifiedByUserID) {
         $xml['lastModifiedByUserID'] = $lastModifiedByUserID;
     }
     if ($item->isAttachment()) {
         $xml['linkMode'] = $item->attachmentLinkMode;
         $xml['mimeType'] = $item->attachmentMIMEType;
         if ($apiVersion == 1 || $item->attachmentCharset) {
             $xml['charset'] = $item->attachmentCharset;
         }
         $storageModTime = $item->attachmentStorageModTime;
         if ($apiVersion > 1 && $storageModTime) {
             $xml['storageModTime'] = $storageModTime;
         }
         $storageHash = $item->attachmentStorageHash;
         if ($apiVersion > 1 && $storageHash) {
             $xml['storageHash'] = $storageHash;
         }
         // TODO: get from a constant
         if ($item->attachmentLinkMode != 3) {
             $xml->addChild('path', htmlspecialchars($item->attachmentPath));
         }
     }
     // Note
     if ($item->isNote() || $item->isAttachment()) {
         // Get htmlspecialchars'ed note
         $note = $item->getNote(false, true);
         if ($note !== '') {
             $xml->addChild('note', $note);
         } else {
             if ($item->isNote()) {
                 $xml->addChild('note', '');
             }
         }
     }
     // Creators
     $creators = $item->getCreators();
     if ($creators) {
         foreach ($creators as $index => $creator) {
             $c = $xml->addChild('creator');
             $c['key'] = $creator['ref']->key;
             $c['creatorType'] = htmlspecialchars(Zotero_CreatorTypes::getName($creator['creatorTypeID']));
             $c['index'] = $index;
             if (empty($data['updatedCreators']) || !in_array($creator['ref']->id, $data['updatedCreators'])) {
                 $cNode = dom_import_simplexml($c);
                 $creatorXML = Zotero_Creators::convertCreatorToXML($creator['ref'], $cNode->ownerDocument);
                 $cNode->appendChild($creatorXML);
             }
         }
     }
     // Related items
     $related = $item->relatedItems;
     if ($related) {
         $related = Zotero_Items::get($item->libraryID, $related);
         $keys = array();
         foreach ($related as $item) {
             $keys[] = $item->key;
         }
         if ($keys) {
             $xml->related = implode(' ', $keys);
         }
     }
     return $xml;
 }
 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;
 }