public function testDeleteItemNotification()
 {
     $json = API::createItem("book", false, $this, 'json');
     $response = API::userDelete(self::$config['userID'], "items/{$json['key']}", ["If-Unmodified-Since-Version: {$json['version']}"]);
     $this->assertCountNotifications(1, $response);
     $this->assertHasNotification(['event' => 'topicUpdated', 'topic' => '/users/' . self::$config['userID']], $response);
 }
Beispiel #2
0
 public function testInvalidCharacters()
 {
     $data = array('title' => "A" . chr(0) . "A", 'creators' => array(array('creatorType' => "author", 'name' => "B" . chr(1) . "B")), 'tags' => array(array('tag' => "C" . chr(2) . "C")));
     $json = API::createItem("book", $data, $this, 'jsonData');
     $this->assertEquals("AA", $json['title']);
     $this->assertEquals("BB", $json['creators'][0]['name']);
     $this->assertEquals("CC", $json['tags'][0]['tag']);
 }
Beispiel #3
0
 public static function setUpBeforeClass()
 {
     parent::setUpBeforeClass();
     API::userClear(self::$config['userID']);
     // Create test data
     $key = API::createItem("book", array("title" => "Title", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"))), null, 'key');
     self::$items[$key] = '<content xmlns:zapi="http://zotero.org/ns/api" type="application/xml"><zapi:subcontent zapi:type="bib"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title</i>, n.d.</div></div></zapi:subcontent><zapi:subcontent zapi:type="json">' . \Zotero_Utilities::formatJSON(json_decode('{"key":"","version":0,"itemType":"book","title":"Title","creators":[{"creatorType":"author","firstName":"First","lastName":"Last"}],"abstractNote":"","series":"","seriesNumber":"","volume":"","numberOfVolumes":"","edition":"","place":"","publisher":"","date":"","numPages":"","language":"","ISBN":"","shortTitle":"","url":"","accessDate":"","archive":"","archiveLocation":"","libraryCatalog":"","callNumber":"","rights":"","extra":"","dateAdded":"","dateModified":"","tags":[],"collections":[],"relations":{}}')) . '</zapi:subcontent></content>';
     $key = API::createItem("book", array("title" => "Title 2", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"), array("creatorType" => "editor", "firstName" => "Ed", "lastName" => "McEditor"))), null, 'key');
     self::$items[$key] = '<content xmlns:zapi="http://zotero.org/ns/api" type="application/xml"><zapi:subcontent zapi:type="bib"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title 2</i>. Edited by Ed McEditor, n.d.</div></div></zapi:subcontent><zapi:subcontent zapi:type="json">' . \Zotero_Utilities::formatJSON(json_decode('{"key":"","version":0,"itemType":"book","title":"Title 2","creators":[{"creatorType":"author","firstName":"First","lastName":"Last"},{"creatorType":"editor","firstName":"Ed","lastName":"McEditor"}],"abstractNote":"","series":"","seriesNumber":"","volume":"","numberOfVolumes":"","edition":"","place":"","publisher":"","date":"","numPages":"","language":"","ISBN":"","shortTitle":"","url":"","accessDate":"","archive":"","archiveLocation":"","libraryCatalog":"","callNumber":"","rights":"","extra":"","dateAdded":"","dateModified":"","tags":[],"collections":[],"relations":{}}')) . '</zapi:subcontent></content>';
 }
Beispiel #4
0
 public static function setUpBeforeClass()
 {
     parent::setUpBeforeClass();
     API::userClear(self::$config['userID']);
     // Create test data
     $key = API::createItem("book", array("title" => "Title", "date" => "January 1, 2014", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"))), null, 'key');
     self::$items[$key] = ['bibtex' => "\n@book{last_title_2014,\n\ttitle = {Title},\n\tauthor = {Last, First},\n\tmonth = jan,\n\tyear = {2014}\n}", 'ris' => "TY  - BOOK\r\nTI  - Title\r\nAU  - Last, First\r\nDA  - 2014/01/01/\r\nPY  - 2014\r\nER  - \r\n\r\n"];
     $key = API::createItem("book", array("title" => "Title 2", "date" => "June 24, 2014", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"), array("creatorType" => "editor", "firstName" => "Ed", "lastName" => "McEditor"))), null, 'key');
     self::$items[$key] = ['bibtex' => "\n@book{last_title_2014,\n\ttitle = {Title 2},\n\tauthor = {Last, First},\n\teditor = {McEditor, Ed},\n\tmonth = jun,\n\tyear = {2014}\n}", 'ris' => "TY  - BOOK\r\nTI  - Title 2\r\nAU  - Last, First\r\nA3  - McEditor, Ed\r\nDA  - 2014/06/24/\r\nPY  - 2014\r\nER  - \r\n\r\n"];
     self::$multiResponses = ['bibtex' => ["contentType" => "application/x-bibtex", "content" => "\n@book{last_title_2014,\n\ttitle = {Title 2},\n\tauthor = {Last, First},\n\teditor = {McEditor, Ed},\n\tmonth = jun,\n\tyear = {2014}\n}\n\n@book{last_title_2014-1,\n\ttitle = {Title},\n\tauthor = {Last, First},\n\tmonth = jan,\n\tyear = {2014}\n}"], 'ris' => ["contentType" => "application/x-research-info-systems", "content" => "TY  - BOOK\r\nTI  - Title 2\r\nAU  - Last, First\r\nA3  - McEditor, Ed\r\nDA  - 2014/06/24/\r\nPY  - 2014\r\nER  - \r\n\r\nTY  - BOOK\r\nTI  - Title\r\nAU  - Last, First\r\nDA  - 2014/01/01/\r\nPY  - 2014\r\nER  - \r\n\r\n"]];
 }
Beispiel #5
0
 public static function setUpBeforeClass()
 {
     parent::setUpBeforeClass();
     API::userClear(self::$config['userID']);
     // Create test data
     $key = API::createItem("book", array("title" => "Title", "date" => "January 1, 2014", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"))), null, 'key');
     self::$items[$key] = ['json' => ["citation" => array("default" => '<span>Last, <i>Title</i>.</span>', "apa" => '<span>(Last, 2014)</span>', "https://www.zotero.org/styles/apa" => '<span>(Last, 2014)</span>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<span>[1]</span>'), "bib" => array("default" => '<div class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title</i>, 2014.</div></div>', "apa" => '<div class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title</i>.</div></div>', "https://www.zotero.org/styles/apa" => '<div class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title</i>.</div></div>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<div class="csl-bib-body" style="line-height: 1.35; "><div class="csl-entry" style="clear: left; "><div class="csl-left-margin" style="float: left; padding-right: 0.5em; text-align: right; width: 1em;">[1]</div><div class="csl-right-inline" style="margin: 0 .4em 0 1.5em;">F. Last, <i>Title</i>. 2014.</div></div></div>')], 'atom' => ["citation" => array("default" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">Last, <i>Title</i>.</span></content>', "apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">(Last, 2014)</span></content>', "https://www.zotero.org/styles/apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">(Last, 2014)</span></content>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">[1]</span></content>'), "bib" => array("default" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title</i>, 2014.</div></div></content>', "apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title</i>.</div></div></content>', "https://www.zotero.org/styles/apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title</i>.</div></div></content>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 1.35; "><div class="csl-entry" style="clear: left; "><div class="csl-left-margin" style="float: left; padding-right: 0.5em; text-align: right; width: 1em;">[1]</div><div class="csl-right-inline" style="margin: 0 .4em 0 1.5em;">F. Last, <i>Title</i>. 2014.</div></div></div></content>')]];
     $key = API::createItem("book", array("title" => "Title 2", "date" => "June 24, 2014", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"), array("creatorType" => "editor", "firstName" => "Ed", "lastName" => "McEditor"))), null, 'key');
     self::$items[$key] = ['json' => ["citation" => array("default" => '<span>Last, <i>Title 2</i>.</span>', "apa" => '<span>(Last, 2014)</span>', "https://www.zotero.org/styles/apa" => '<span>(Last, 2014)</span>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<span>[1]</span>'), "bib" => array("default" => '<div class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title 2</i>. Edited by Ed McEditor, 2014.</div></div>', "apa" => '<div class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title 2</i>. (E. McEditor, Ed.).</div></div>', "https://www.zotero.org/styles/apa" => '<div class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title 2</i>. (E. McEditor, Ed.).</div></div>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<div class="csl-bib-body" style="line-height: 1.35; "><div class="csl-entry" style="clear: left; "><div class="csl-left-margin" style="float: left; padding-right: 0.5em; text-align: right; width: 1em;">[1]</div><div class="csl-right-inline" style="margin: 0 .4em 0 1.5em;">F. Last, <i>Title 2</i>. 2014.</div></div></div>')], 'atom' => ["citation" => array("default" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">Last, <i>Title 2</i>.</span></content>', "apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">(Last, 2014)</span></content>', "https://www.zotero.org/styles/apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">(Last, 2014)</span></content>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="citation" type="xhtml"><span xmlns="http://www.w3.org/1999/xhtml">[1]</span></content>'), "bib" => array("default" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title 2</i>. Edited by Ed McEditor, 2014.</div></div></content>', "apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title 2</i>. (E. McEditor, Ed.).</div></div></content>', "https://www.zotero.org/styles/apa" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014). <i>Title 2</i>. (E. McEditor, Ed.).</div></div></content>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<content xmlns:zapi="http://zotero.org/ns/api" zapi:type="bib" type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml" class="csl-bib-body" style="line-height: 1.35; "><div class="csl-entry" style="clear: left; "><div class="csl-left-margin" style="float: left; padding-right: 0.5em; text-align: right; width: 1em;">[1]</div><div class="csl-right-inline" style="margin: 0 .4em 0 1.5em;">F. Last, <i>Title 2</i>. 2014.</div></div></div></content>')]];
     self::$multiResponses = ["default" => '<?xml version="1.0"?><div class="csl-bib-body" style="line-height: 1.35; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, First. <i>Title</i>, 2014.</div><div class="csl-entry">&#x2014;&#x2014;&#x2014;. <i>Title 2</i>. Edited by Ed McEditor, 2014.</div></div>', "apa" => '<?xml version="1.0"?><div class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014a). <i>Title</i>.</div><div class="csl-entry">Last, F. (2014b). <i>Title 2</i>. (E. McEditor, Ed.).</div></div>', "https://www.zotero.org/styles/apa" => '<?xml version="1.0"?><div class="csl-bib-body" style="line-height: 2; padding-left: 2em; text-indent:-2em;"><div class="csl-entry">Last, F. (2014a). <i>Title</i>.</div><div class="csl-entry">Last, F. (2014b). <i>Title 2</i>. (E. McEditor, Ed.).</div></div>', "https://raw.githubusercontent.com/citation-style-language/styles/master/ieee.csl" => '<?xml version="1.0"?><div class="csl-bib-body" style="line-height: 1.35; "><div class="csl-entry" style="clear: left; "><div class="csl-left-margin" style="float: left; padding-right: 0.5em; text-align: right; width: 1em;">[1]</div><div class="csl-right-inline" style="margin: 0 .4em 0 1.5em;">F. Last, <i>Title 2</i>. 2014.</div></div><div class="csl-entry" style="clear: left; "><div class="csl-left-margin" style="float: left; padding-right: 0.5em; text-align: right; width: 1em;">[2]</div><div class="csl-right-inline" style="margin: 0 .4em 0 1.5em;">F. Last, <i>Title</i>. 2014.</div></div></div>'];
 }
Beispiel #6
0
 /**
  * An object type's primary data cache for a library has to be created before
  * 
  */
 public function testCacheCreatorPrimaryData()
 {
     $data = array("title" => "Title", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"), array("creatorType" => "editor", "firstName" => "Ed", "lastName" => "McEditor")));
     $key = API::createItem("book", $data, $this, 'key');
     $response = API::userGet(self::$config['userID'], "items/{$key}?content=csljson");
     $json = json_decode(API::getContentFromResponse($response));
     $this->assertEquals("First", $json->author[0]->given);
     $this->assertEquals("Last", $json->author[0]->family);
     $this->assertEquals("Ed", $json->editor[0]->given);
     $this->assertEquals("McEditor", $json->editor[0]->family);
 }
Beispiel #7
0
 public function testItemQuickSearchOrderByDate()
 {
     $title1 = "Test Title";
     $title2 = "Another Title";
     $keys = [];
     $keys[] = API::createItem("book", ['title' => $title1, 'date' => "February 12, 2013"], $this, 'key');
     $keys[] = API::createItem("journalArticle", ['title' => $title2, 'date' => "November 25, 2012"], $this, 'key');
     // Search for one by title
     $response = API::userGet(self::$config['userID'], "items?q=" . urlencode($title1));
     $this->assert200($response);
     $this->assertNumResults(1, $response);
     $json = API::getJSONFromResponse($response);
     $this->assertEquals($keys[0], $json[0]['key']);
     // Search by both by title, date asc
     $response = API::userGet(self::$config['userID'], "items?q=title&sort=date&direction=asc");
     $this->assert200($response);
     $this->assertNumResults(2, $response);
     $json = API::getJSONFromResponse($response);
     $this->assertEquals($keys[1], $json[0]['key']);
     $this->assertEquals($keys[0], $json[1]['key']);
     // Search by both by title, date asc, with old-style parameters
     $response = API::userGet(self::$config['userID'], "items?q=title&order=date&sort=asc");
     $this->assert200($response);
     $this->assertNumResults(2, $response);
     $json = API::getJSONFromResponse($response);
     $this->assertEquals($keys[1], $json[0]['key']);
     $this->assertEquals($keys[0], $json[1]['key']);
     // Search by both by title, date desc
     $response = API::userGet(self::$config['userID'], "items?q=title&sort=date&direction=desc");
     $this->assert200($response);
     $this->assertNumResults(2, $response);
     $json = API::getJSONFromResponse($response);
     $this->assertEquals($keys[0], $json[0]['key']);
     $this->assertEquals($keys[1], $json[1]['key']);
     // Search by both by title, date desc, with old-style parameters
     $response = API::userGet(self::$config['userID'], "items?q=title&order=date&sort=desc");
     $this->assert200($response);
     $this->assertNumResults(2, $response);
     $json = API::getJSONFromResponse($response);
     $this->assertEquals($keys[0], $json[0]['key']);
     $this->assertEquals($keys[1], $json[1]['key']);
 }
Beispiel #8
0
 public function testTagDiacritics()
 {
     $data = API::createItem("book", ["tags" => [["tag" => "ëtest"]]], $this, 'jsonData');
     $version = $data['version'];
     // Add 'etest', without accent
     $data['tags'] = [["tag" => "ëtest"], ["tag" => "etest"]];
     $response = API::postItem($data);
     $this->assert200($response);
     $this->assert200ForObject($response);
     // Item version should be one greater than last update
     $data = API::getItem($data['key'], $this, 'json')['data'];
     $this->assertEquals($version + 1, $data['version']);
     $this->assertCount(2, $data['tags']);
     $this->assertContains(["tag" => "ëtest"], $data['tags']);
     $this->assertContains(["tag" => "etest"], $data['tags']);
 }
Beispiel #9
0
	public function testAddFileClientV5Zip() {
		API::userClear(self::$config['userID']);
		
		// Get last storage sync
		$response = API::userGet(
			self::$config['userID'],
			"laststoragesync"
		);
		$this->assert404($response);
		
		$json = API::createItem("book", false, $this, 'jsonData');
		$key = $json['key'];
		
		$json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData');
		$key = $json['key'];
		$version = $json['version'];
		
		$fileContents = self::getRandomUnicodeString();
		$contentType = "text/html";
		$charset = "utf-8";
		$filename = "file.html";
		$mtime = time();
		$hash = md5($fileContents);
		
		// Create ZIP file
		$zip = new \ZipArchive();
		$file = "work/$key.zip";
		if ($zip->open($file, \ZIPARCHIVE::CREATE) !== TRUE) {
			throw new Exception("Cannot open ZIP file");
		}
		$zip->addFromString($filename, $fileContents);
		$zip->addFromString("file.css", self::getRandomUnicodeString());
		$zip->close();
		$zipHash = md5_file($file);
		$zipFilename = $key . ".zip";
		$zipSize = filesize($file);
		$zipFileContents = file_get_contents($file);
		
		// Get a sync timestamp from before the file is updated
		sleep(1);
		require_once 'include/sync.inc.php';
		$sessionID = Sync::login();
		$xml = Sync::updated($sessionID);
		$lastsync = (int) $xml['timestamp'];
		Sync::logout($sessionID);
		
		//
		// Get upload authorization
		//
		$response = API::userPost(
			self::$config['userID'],
			"items/$key/file",
			$this->implodeParams([
				"md5" => $hash,
				"mtime" => $mtime,
				"filename" => $filename,
				"filesize" => $zipSize,
				"zipMD5" => $zipHash,
				"zipFilename" => $zipFilename,
				"contentType" => $contentType,
				"charset" => $charset
			]),
			[
				"Content-Type: application/x-www-form-urlencoded",
				"If-None-Match: *"
			]
		);
		$this->assert200($response);
		$json = API::getJSONFromResponse($response);
		
		self::$toDelete[] = "$zipHash";
		
		// Upload to S3
		$response = HTTP::post(
			$json['url'],
			$json['prefix'] . $zipFileContents . $json['suffix'],
			[
				"Content-Type: {$json['contentType']}"
			]
		);
		$this->assert201($response);
		
		//
		// Register upload
		//
		
		// If-Match with file hash shouldn't match unregistered file
		$response = API::userPost(
			self::$config['userID'],
			"items/$key/file",
			"upload=" . $json['uploadKey'],
			[
				"Content-Type: application/x-www-form-urlencoded",
				"If-Match: $hash"
			]
		);
		$this->assert412($response);
		
		// If-Match with ZIP hash shouldn't match unregistered file
		$response = API::userPost(
			self::$config['userID'],
			"items/$key/file",
			"upload=" . $json['uploadKey'],
			[
				"Content-Type: application/x-www-form-urlencoded",
				"If-Match: $zipHash"
			]
		);
		$this->assert412($response);
		
		$response = API::userPost(
			self::$config['userID'],
			"items/$key/file",
			"upload=" . $json['uploadKey'],
			[
				"Content-Type: application/x-www-form-urlencoded",
				"If-None-Match: *"
			]
		);
		$this->assert204($response);
		
		// Verify attachment item metadata
		$response = API::userGet(
			self::$config['userID'],
			"items/$key"
		);
		$json = API::getJSONFromResponse($response)['data'];
		
		$this->assertEquals($hash, $json['md5']);
		$this->assertEquals($mtime, $json['mtime']);
		$this->assertEquals($filename, $json['filename']);
		$this->assertEquals($contentType, $json['contentType']);
		$this->assertEquals($charset, $json['charset']);
		
		$response = API::userGet(
			self::$config['userID'],
			"laststoragesync"
		);
		$this->assert200($response);
		$this->assertRegExp('/^[0-9]{10}$/', $response->getBody());
		
		// File exists
		$response = API::userPost(
			self::$config['userID'],
			"items/$key/file",
			$this->implodeParams([
				"md5" => $hash,
				"mtime" => $mtime + 1000,
				"filename" => $filename,
				"filesize" => $zipSize,
				"zip" => 1,
				"zipMD5" => $zipHash,
				"zipFilename" => $zipFilename
			]),
			[
				"Content-Type: application/x-www-form-urlencoded",
				"If-Match: $hash"
			]
		);
		$this->assert200($response);
		$json = API::getJSONFromResponse($response);
		$this->assertArrayHasKey("exists", $json);
		
		// Get attachment via classic sync
		$sessionID = Sync::login();
		$xml = Sync::updated($sessionID, 2);
		$this->assertEquals(1, $xml->updated[0]->items->count());
		$itemXML = $xml->xpath("//updated/items/item[@key='$key']")[0];
		$this->assertEquals($contentType, (string) $itemXML['mimeType']);
		$this->assertEquals($charset, (string) $itemXML['charset']);
		$this->assertEquals($hash, (string) $itemXML['storageHash']);
		$this->assertEquals($mtime + 1000, (string) $itemXML['storageModTime']);
		Sync::logout($sessionID);
	}
Beispiel #10
0
 public function testDeleteItemRelation()
 {
     $relations = array("owl:sameAs" => ["http://zotero.org/groups/1/items/AAAAAAAA", "http://zotero.org/groups/1/items/BBBBBBBB"], "dc:relation" => "http://zotero.org/users/" . self::$config['userID'] . "/items/AAAAAAAA");
     $data = API::createItem("book", array("relations" => $relations), $this, 'jsonData');
     $itemKey = $data['key'];
     // Remove a relation
     $data['relations']['owl:sameAs'] = $relations['owl:sameAs'] = $relations['owl:sameAs'][0];
     $response = API::userPut(self::$config['userID'], "items/{$itemKey}", json_encode($data));
     $this->assert204($response);
     // Make sure it's gone
     $data = API::getItem($data['key'], $this, 'json')['data'];
     $this->assertCount(sizeOf($relations), $data['relations']);
     foreach ($relations as $predicate => $object) {
         $this->assertEquals($object, $data['relations'][$predicate]);
     }
     // Delete all
     $data['relations'] = new \stdClass();
     $response = API::userPut(self::$config['userID'], "items/{$itemKey}", json_encode($data));
     $this->assert204($response);
     // Make sure they're gone
     $data = API::getItem($itemKey, $this, 'json')['data'];
     $this->assertCount(0, $data['relations']);
 }
Beispiel #11
0
 public function test204NoCompression()
 {
     $json = API::createItem("book", [], null, 'jsonData');
     $response = API::userDelete(self::$config['userID'], "items/{$json['key']}", ["If-Unmodified-Since-Version: {$json['version']}"]);
     $this->assertHTTPStatus(204, $response);
     $this->assertNoCompression($response);
     $this->assertContentLength(0, $response);
 }
Beispiel #12
0
 private function _testSinceContent($param)
 {
     API::userClear(self::$config['userID']);
     // Store content for one item
     $key = API::createItem("book", false, $this, 'key');
     $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData');
     $key1 = $json['key'];
     $content = "Here is some full-text content";
     $response = API::userPut(self::$config['userID'], "items/{$key1}/fulltext", json_encode(["content" => $content]), array("Content-Type: application/json"));
     $this->assert204($response);
     $contentVersion1 = $response->getHeader("Last-Modified-Version");
     $this->assertGreaterThan(0, $contentVersion1);
     // And another
     $key = API::createItem("book", false, $this, 'key');
     $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData');
     $key2 = $json['key'];
     $response = API::userPut(self::$config['userID'], "items/{$key2}/fulltext", json_encode(["content" => $content]), array("Content-Type: application/json"));
     $this->assert204($response);
     $contentVersion2 = $response->getHeader("Last-Modified-Version");
     $this->assertGreaterThan(0, $contentVersion2);
     // Get newer one
     $response = API::userGet(self::$config['userID'], "fulltext?{$param}={$contentVersion1}");
     $this->assert200($response);
     $this->assertContentType("application/json", $response);
     $this->assertEquals($contentVersion2, $response->getHeader("Last-Modified-Version"));
     $json = API::getJSONFromResponse($response);
     $this->assertCount(1, $json);
     $this->assertArrayHasKey($key2, $json);
     $this->assertEquals($contentVersion2, $json[$key2]);
     // Get both with since=0
     $response = API::userGet(self::$config['userID'], "fulltext?{$param}=0");
     $this->assert200($response);
     $this->assertContentType("application/json", $response);
     $json = API::getJSONFromResponse($response);
     $this->assertCount(2, $json);
     $this->assertArrayHasKey($key1, $json);
     $this->assertEquals($contentVersion1, $json[$key1]);
     $this->assertArrayHasKey($key1, $json);
     $this->assertEquals($contentVersion2, $json[$key2]);
 }
Beispiel #13
0
 public function testCreatorCaseSensitivity()
 {
     API::createItem("book", array("creators" => array(array("creatorType" => "author", "name" => "SMITH"))), $this, 'json');
     $json = API::createItem("book", array("creators" => array(array("creatorType" => "author", "name" => "Smith"))), $this, 'json');
     $this->assertEquals('Smith', $json['data']['creators'][0]['name']);
 }
Beispiel #14
0
	public function testUnicodeTitle() {
		$title = "Tést";
		
		$key = API::createItem("book", array("title" => $title), $this, 'key');
		
		// Test entry (JSON)
		$response = API::userGet(
			self::$config['userID'],
			"items/$key"
		);
		$this->assertContains('"title": "Tést"', $response->getBody());
		
		// Test feed (JSON)
		$response = API::userGet(
			self::$config['userID'],
			"items"
		);
		$this->assertContains('"title": "Tést"', $response->getBody());
		
		// Test entry (Atom)
		$response = API::userGet(
			self::$config['userID'],
			"items/$key?content=json"
		);
		$this->assertContains('"title": "Tést"', $response->getBody());
		
		// Test feed (Atom)
		$response = API::userGet(
			self::$config['userID'],
			"items?content=json"
		);
		$this->assertContains('"title": "Tést"', $response->getBody());
	}
Beispiel #15
0
 public function testAddFileClientZip()
 {
     API::userClear(self::$config['userID']);
     $auth = array('username' => self::$config['username'], 'password' => self::$config['password']);
     // Get last storage sync
     $response = API::userGet(self::$config['userID'], "laststoragesync?auth=1", array(), $auth);
     $this->assert404($response);
     $json = API::createItem("book", false, $this, 'jsonData');
     $key = $json['key'];
     $fileContentType = "text/html";
     $fileCharset = "UTF-8";
     $fileFilename = "file.html";
     $fileModtime = time();
     $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData');
     $key = $json['key'];
     $version = $json['version'];
     $json['contentType'] = $fileContentType;
     $json['charset'] = $fileCharset;
     $json['filename'] = $fileFilename;
     $response = API::userPut(self::$config['userID'], "items/{$key}", json_encode($json), array("Content-Type: application/json"));
     $this->assert204($response);
     // Get a sync timestamp from before the file is updated
     sleep(1);
     require_once 'include/sync.inc.php';
     $sessionID = Sync::login();
     $xml = Sync::updated($sessionID);
     $lastsync = (int) $xml['timestamp'];
     Sync::logout($sessionID);
     // Get file info
     $response = API::userGet(self::$config['userID'], "items/{$json['key']}/file?auth=1&iskey=1&version=1&info=1", array(), $auth);
     $this->assert404($response);
     $zip = new \ZipArchive();
     $file = "work/{$key}.zip";
     if ($zip->open($file, \ZIPARCHIVE::CREATE) !== TRUE) {
         throw new Exception("Cannot open ZIP file");
     }
     $zip->addFromString($fileFilename, self::getRandomUnicodeString());
     $zip->addFromString("file.css", self::getRandomUnicodeString());
     $zip->close();
     $hash = md5_file($file);
     $filename = $key . ".zip";
     $size = filesize($file);
     $fileContents = file_get_contents($file);
     // Get upload authorization
     $response = API::userPost(self::$config['userID'], "items/{$json['key']}/file?auth=1&iskey=1&version=1", $this->implodeParams(array("md5" => $hash, "filename" => $filename, "filesize" => $size, "mtime" => $fileModtime, "zip" => 1)), array("Content-Type: application/x-www-form-urlencoded"), $auth);
     $this->assert200($response);
     $this->assertContentType("application/xml", $response);
     $xml = new SimpleXMLElement($response->getBody());
     self::$toDelete[] = "{$hash}";
     $boundary = "---------------------------" . rand();
     $postData = "";
     foreach ($xml->params->children() as $key => $val) {
         $postData .= "--" . $boundary . "\r\nContent-Disposition: form-data; " . "name=\"{$key}\"\r\n\r\n{$val}\r\n";
     }
     $postData .= "--" . $boundary . "\r\nContent-Disposition: form-data; " . "name=\"file\"\r\n\r\n" . $fileContents . "\r\n";
     $postData .= "--" . $boundary . "--";
     // Upload to S3
     $response = HTTP::post((string) $xml->url, $postData, array("Content-Type: multipart/form-data; boundary=" . $boundary));
     $this->assert201($response);
     //
     // Register upload
     //
     $response = API::userPost(self::$config['userID'], "items/{$json['key']}/file?auth=1&iskey=1&version=1", "update=" . $xml->key . "&mtime=" . $fileModtime, array("Content-Type: application/x-www-form-urlencoded"), $auth);
     $this->assert204($response);
     // Verify attachment item metadata
     $response = API::userGet(self::$config['userID'], "items/{$json['key']}");
     $json = API::getJSONFromResponse($response)['data'];
     $this->assertEquals($hash, $json['md5']);
     $this->assertEquals($fileFilename, $json['filename']);
     $this->assertEquals($fileModtime, $json['mtime']);
     // Make sure attachment item wasn't updated (or else the client
     // will get a conflict when it tries to update the metadata)
     $sessionID = Sync::login();
     $xml = Sync::updated($sessionID, $lastsync);
     Sync::logout($sessionID);
     $this->assertEquals(0, $xml->updated[0]->count());
     $response = API::userGet(self::$config['userID'], "laststoragesync?auth=1", array(), array('username' => self::$config['username'], 'password' => self::$config['password']));
     $this->assert200($response);
     $mtime = $response->getBody();
     $this->assertRegExp('/^[0-9]{10}$/', $mtime);
     // File exists
     $response = API::userPost(self::$config['userID'], "items/{$json['key']}/file?auth=1&iskey=1&version=1", $this->implodeParams(array("md5" => $hash, "filename" => $filename, "filesize" => $size, "mtime" => $fileModtime + 1000, "zip" => 1)), array("Content-Type: application/x-www-form-urlencoded"), $auth);
     $this->assert200($response);
     $this->assertContentType("application/xml", $response);
     $this->assertEquals("<exists/>", $response->getBody());
     // Make sure attachment item still wasn't updated
     $sessionID = Sync::login();
     $xml = Sync::updated($sessionID, $lastsync);
     Sync::logout($sessionID);
     $this->assertEquals(0, $xml->updated[0]->count());
 }
Beispiel #16
0
 private function _testPartialWriteFailureWithUnchanged($objectType)
 {
     API::userClear(self::$config['userID']);
     $objectTypePlural = API::getPluralObjectType($objectType);
     switch ($objectType) {
         case 'collection':
             $json1 = API::createCollection("Test", false, $this, 'jsonData');
             $json2 = array("name" => str_repeat("1234567890", 6554));
             $json3 = array("name" => "Test");
             break;
         case 'item':
             $json1 = API::createItem("book", array("title" => "Title"), $this, 'jsonData');
             $json2 = API::getItemTemplate('book');
             $json3 = clone $json2;
             $json2->title = str_repeat("1234567890", 6554);
             break;
         case 'search':
             $conditions = array(array('condition' => 'title', 'operator' => 'contains', 'value' => 'value'));
             $json1 = API::createSearch("Name", $conditions, $this, 'jsonData');
             $json2 = array("name" => str_repeat("1234567890", 6554), "conditions" => $conditions);
             $json3 = array("name" => "Test", "conditions" => $conditions);
             break;
     }
     $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode([$json1, $json2, $json3]), array("Content-Type: application/json"));
     $this->assert200($response);
     $json = API::getJSONFromResponse($response);
     $this->assertUnchangedForObject($response, 0);
     $this->assert400ForObject($response, false, 1);
     $this->assert200ForObject($response, false, 2);
     $json = API::getJSONFromResponse($response);
     $response = API::userGet(self::$config['userID'], "{$objectTypePlural}?format=keys&key=" . self::$config['apiKey']);
     $this->assert200($response);
     $keys = explode("\n", trim($response->getBody()));
     $this->assertCount(2, $keys);
     foreach ($json['success'] as $key) {
         $this->assertContains($key, $keys);
     }
 }
Beispiel #17
0
 public function testTagDeletePermissions()
 {
     API::userClear(self::$config['userID']);
     API::createItem('book', array("tags" => array(array("tag" => "A"))), $this);
     $libraryVersion = API::getLibraryVersion();
     API::setKeyOption(self::$config['userID'], self::$config['apiKey'], 'libraryWrite', 0);
     $response = API::userDelete(self::$config['userID'], "tags?tag=A&key=" . self::$config['apiKey']);
     $this->assert403($response);
     API::setKeyOption(self::$config['userID'], self::$config['apiKey'], 'libraryWrite', 1);
     $response = API::userDelete(self::$config['userID'], "tags?tag=A&key=" . self::$config['apiKey'], array("If-Unmodified-Since-Version: {$libraryVersion}"));
     $this->assert204($response);
 }
Beispiel #18
0
 public function testSortDirection()
 {
     API::userClear(self::$config['userID']);
     // Setup
     $dataArray = [];
     $dataArray[] = API::createItem("book", ['title' => "B", 'creators' => [["creatorType" => "author", "name" => "B"]], 'dateAdded' => '2014-02-05T00:00:00Z', 'dateModified' => '2014-04-05T01:00:00Z'], $this, 'jsonData');
     $dataArray[] = API::createItem("journalArticle", ['title' => "A", 'creators' => [["creatorType" => "author", "name" => "A"]], 'dateAdded' => '2014-02-04T00:00:00Z', 'dateModified' => '2014-01-04T01:00:00Z'], $this, 'jsonData');
     $dataArray[] = API::createItem("newspaperArticle", ['title' => "F", 'creators' => [["creatorType" => "author", "name" => "F"]], 'dateAdded' => '2014-02-03T00:00:00Z', 'dateModified' => '2014-02-03T01:00:00Z'], $this, 'jsonData');
     $dataArray[] = API::createItem("book", ['title' => "C", 'creators' => [["creatorType" => "author", "name" => "C"]], 'dateAdded' => '2014-02-02T00:00:00Z', 'dateModified' => '2014-03-02T01:00:00Z'], $this, 'jsonData');
     // Get sorted keys
     usort($dataArray, function ($a, $b) {
         return strcmp($a['dateAdded'], $b['dateAdded']);
     });
     $keysByDateAddedAscending = array_map(function ($data) {
         return $data['key'];
     }, $dataArray);
     $keysByDateAddedDescending = array_reverse($keysByDateAddedAscending);
     // Ascending
     $response = API::userGet(self::$config['userID'], "items?format=keys&sort=dateAdded&direction=asc");
     $this->assert200($response);
     $this->assertEquals($keysByDateAddedAscending, explode("\n", trim($response->getBody())));
     $response = API::userGet(self::$config['userID'], "items?format=json&sort=dateAdded&direction=asc");
     $this->assert200($response);
     $json = API::getJSONFromResponse($response);
     $keys = array_map(function ($val) {
         return $val['key'];
     }, $json);
     $this->assertEquals($keysByDateAddedAscending, $keys);
     $response = API::userGet(self::$config['userID'], "items?format=atom&sort=dateAdded&direction=asc");
     $this->assert200($response);
     $xml = API::getXMLFromResponse($response);
     $keys = array_map(function ($val) {
         return (string) $val;
     }, $xml->xpath('//atom:entry/zapi:key'));
     $this->assertEquals($keysByDateAddedAscending, $keys);
     // Ascending using old 'order'/'sort' instead of 'sort'/'direction'
     $response = API::userGet(self::$config['userID'], "items?format=keys&order=dateAdded&sort=asc");
     $this->assert200($response);
     $this->assertEquals($keysByDateAddedAscending, explode("\n", trim($response->getBody())));
     $response = API::userGet(self::$config['userID'], "items?format=json&order=dateAdded&sort=asc");
     $this->assert200($response);
     $json = API::getJSONFromResponse($response);
     $keys = array_map(function ($val) {
         return $val['key'];
     }, $json);
     $this->assertEquals($keysByDateAddedAscending, $keys);
     $response = API::userGet(self::$config['userID'], "items?format=atom&order=dateAdded&sort=asc");
     $this->assert200($response);
     $xml = API::getXMLFromResponse($response);
     $keys = array_map(function ($val) {
         return (string) $val;
     }, $xml->xpath('//atom:entry/zapi:key'));
     $this->assertEquals($keysByDateAddedAscending, $keys);
     // Deprecated 'order'/'sort', but the wrong way
     $response = API::userGet(self::$config['userID'], "items?format=keys&sort=dateAdded&order=asc");
     $this->assert200($response);
     $this->assertEquals($keysByDateAddedAscending, explode("\n", trim($response->getBody())));
     // Descending
     $response = API::userGet(self::$config['userID'], "items?format=keys&sort=dateAdded&direction=desc");
     $this->assert200($response);
     $this->assertEquals($keysByDateAddedDescending, explode("\n", trim($response->getBody())));
     $response = API::userGet(self::$config['userID'], "items?format=json&sort=dateAdded&direction=desc");
     $this->assert200($response);
     $json = API::getJSONFromResponse($response);
     $keys = array_map(function ($val) {
         return $val['key'];
     }, $json);
     $this->assertEquals($keysByDateAddedDescending, $keys);
     $response = API::userGet(self::$config['userID'], "items?format=atom&sort=dateAdded&direction=desc");
     $this->assert200($response);
     $xml = API::getXMLFromResponse($response);
     $keys = array_map(function ($val) {
         return (string) $val;
     }, $xml->xpath('//atom:entry/zapi:key'));
     $this->assertEquals($keysByDateAddedDescending, $keys);
     // Descending
     $response = API::userGet(self::$config['userID'], "items?format=keys&order=dateAdded&sort=desc");
     $this->assert200($response);
     $this->assertEquals($keysByDateAddedDescending, explode("\n", trim($response->getBody())));
     $response = API::userGet(self::$config['userID'], "items?format=json&order=dateAdded&sort=desc");
     $this->assert200($response);
     $json = API::getJSONFromResponse($response);
     $keys = array_map(function ($val) {
         return $val['key'];
     }, $json);
     $this->assertEquals($keysByDateAddedDescending, $keys);
     $response = API::userGet(self::$config['userID'], "items?format=atom&order=dateAdded&sort=desc");
     $this->assert200($response);
     $xml = API::getXMLFromResponse($response);
     $keys = array_map(function ($val) {
         return (string) $val;
     }, $xml->xpath('//atom:entry/zapi:key'));
     $this->assertEquals($keysByDateAddedDescending, $keys);
 }
Beispiel #19
0
 private function _testTagsSince($param)
 {
     $tags1 = array("a", "aa", "b");
     $tags2 = array("b", "c", "cc");
     $data1 = API::createItem("book", array("tags" => array_map(function ($tag) {
         return array("tag" => $tag);
     }, $tags1)), $this, 'jsonData');
     $data2 = API::createItem("book", array("tags" => array_map(function ($tag) {
         return array("tag" => $tag);
     }, $tags2)), $this, 'jsonData');
     // Only newly added tags should be included in 'since',
     // not previously added tags or tags added to items
     $response = API::userGet(self::$config['userID'], "tags?{$param}=" . $data1['version']);
     $this->assertNumResults(2, $response);
     // Deleting an item shouldn't update associated tag versions
     $response = API::userDelete(self::$config['userID'], "items/{$data1['key']}", array("If-Unmodified-Since-Version: " . $data1['version']));
     $this->assert204($response);
     $response = API::userGet(self::$config['userID'], "tags?{$param}=" . $data1['version']);
     $this->assertNumResults(2, $response);
     $libraryVersion = $response->getHeader("Last-Modified-Version");
     $response = API::userGet(self::$config['userID'], "tags?{$param}=" . $libraryVersion);
     $this->assertNumResults(0, $response);
 }
Beispiel #20
0
 public function testCollectionItemMissingCollection()
 {
     $response = API::createItem("book", ['collections' => ["AAAAAAAA"]], $this, 'response');
     $this->assert400ForObject($response, "Collection with key 'AAAAAAAA' not found");
 }