public function setUp() { API::userClear(self::$config['userID']); API::groupClear(self::$config['ownedPrivateGroupID']); API::groupClear(self::$config['ownedPublicGroupID']); self::$sessionID = Sync::login(); Sync::useZoteroVersion(); }
public function setUp() { API::userClear(self::$config['userID']); API::groupClear(self::$config['ownedPrivateGroupID']); API::groupClear(self::$config['ownedPrivateGroupID2']); self::$sessionID = Sync::login(); self::$sessionID2 = Sync::login(array('username' => self::$config['username2'], 'password' => self::$config['password2'])); }
public function testUnlimited() { $quota = 'unlimited'; $expiration = time() + 86400 * 365; $response = API::post('users/' . self::$config['userID'] . '/storageadmin', "quota={$quota}&expiration={$expiration}", [], ["username" => self::$config['rootUsername'], "password" => self::$config['rootPassword']]); $this->assert200($response); $xml = API::getXMLFromResponse($response); $this->assertEquals($quota, (string) $xml->quota); $this->assertEquals($expiration, (int) $xml->expiration); }
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"))); $xml = API::createItem("book", $data, $this, 'atom'); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $this->assertEquals("AA", $json->title); $this->assertEquals("BB", $json->creators[0]->name); $this->assertEquals("CC", $json->tags[0]->tag); }
/** * 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}?key=" . self::$config['apiKey'] . "&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); }
public function testWebTranslationMultiple() { $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["url" => "http://www.amazon.com/s/field-keywords=zotero+guide+librarians"]), array("Content-Type: application/json")); $this->assert300($response); $json = json_decode($response->getBody()); $results = get_object_vars($json); $key = array_keys($results)[0]; $val = array_values($results)[0]; $this->assertEquals(0, strpos($key, 'http')); $this->assertEquals('Zotero: A Guide for Librarians, Researchers and Educators', $val); // Can't test posting on v1, because generated token isn't returned }
public function testCreateItemWithChildren() { $json = API::getItemTemplate("newspaperArticle"); $noteJSON = API::getItemTemplate("note"); $noteJSON->note = "<p>Here's a test note</p>"; $json->notes = array($noteJSON); $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(array("items" => array($json)))); $this->assert201($response); $xml = API::getXMLFromResponse($response); $this->assertNumResults(1, $response); $this->assertEquals(1, (int) array_shift($xml->xpath('//atom:entry/zapi:numChildren'))); }
public function testComputerProgramVersion() { $response = API::get("items/new?itemType=computerProgram"); $this->assert200($response); $json = json_decode($response->getBody()); $this->assertObjectHasAttribute('version', $json); $this->assertObjectNotHasAttribute('versionNumber', $json); $response = API::get("itemTypeFields?itemType=computerProgram"); $this->assert200($response); $json = json_decode($response->getBody()); $fields = array_map(function ($val) { return $val->field; }, $json); $this->assertContains('version', $fields); $this->assertNotContains('versionNumber', $fields); }
public function testMultiContent() { $keys = array_keys(self::$items); $keyStr = implode(',', $keys); $response = API::userGet(self::$config['userID'], "items?key=" . self::$config['apiKey'] . "&itemKey={$keyStr}&content=bib,json"); $this->assert200($response); $xml = API::getXMLFromResponse($response); $this->assertEquals(sizeOf($keys), (int) array_shift($xml->xpath('/atom:feed/zapi:totalResults'))); $entries = $xml->xpath('//atom:entry'); foreach ($entries as $entry) { $key = (string) $entry->children("http://zotero.org/ns/api")->key; $content = $entry->content->asXML(); // Add namespace prefix (from <entry>) $content = str_replace('<content ', '<content xmlns:zapi="http://zotero.org/ns/api" ', $content); // Strip variable key and version $content = preg_replace('%"itemKey": "[A-Z0-9]{8}",(\\s+)"itemVersion": [0-9]+%', '"itemKey": "",$1"itemVersion": 0', $content); $this->assertXmlStringEqualsXmlString(self::$items[$key], $content); } }
public function testSettings() { $settingKey = 'tagColors'; $value = array(array("name" => "_READ", "color" => "#990000")); $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $lastSyncTimestamp = (int) $xml['timestamp']; $libraryVersion = API::getLibraryVersion(); // Create item via sync $data = '<data version="9"><settings><setting libraryID="' . self::$config['libraryID'] . '" name="' . $settingKey . '">' . htmlspecialchars(json_encode($value)) . '</setting></settings></data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data); Sync::waitForUpload(self::$sessionID, $response, $this); // Check via sync $xml = Sync::updated(self::$sessionID, $lastSyncTimestamp); $updateKey = (string) $xml['updateKey']; $lastSyncTimestamp = $xml['timestamp']; $settingXML = $xml->updated[0]->settings[0]->setting[0]; $this->assertEquals(self::$config['libraryID'], (int) $settingXML['libraryID']); $this->assertEquals($settingKey, (string) $settingXML['name']); $this->assertEquals($value, json_decode((string) $settingXML, true)); // Get setting via API and check value $response = API::userGet(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey']); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertNotNull($json); $this->assertEquals($value, $json['value']); $this->assertEquals($libraryVersion + 1, $json['version']); // Delete via sync $xmlstr = '<data version="9">' . '<deleted>' . '<settings>' . '<setting libraryID="' . self::$config['libraryID'] . '" key="' . $settingKey . '"/>' . '</settings>' . '</deleted>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); $xml = Sync::waitForUpload(self::$sessionID, $response, $this); // Get setting via API and check value $response = API::userGet(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey']); $this->assertEquals(404, $response->getStatus()); // Check for missing via sync $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $lastSyncTimestamp = $xml['timestamp']; $this->assertEquals(0, $xml->updated[0]->settings->count()); $this->assertEquals(1, $xml->updated[0]->deleted[0]->settings[0]->setting->count()); $this->assertEquals(self::$config['libraryID'], (int) $xml->updated[0]->deleted[0]->settings[0]->setting[0]['libraryID']); $this->assertEquals($settingKey, (string) $xml->updated[0]->deleted[0]->settings[0]->setting[0]['key']); }
public function testCreatorSummary() { $xml = API::createItem("book", array("creators" => array(array("creatorType" => "author", "name" => "Test"))), $this); $data = API::parseDataFromAtomEntry($xml); $itemKey = $data['key']; $json = json_decode($data['content'], true); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Test", $creatorSummary); $json['creators'][] = array("creatorType" => "author", "firstName" => "Alice", "lastName" => "Foo"); $response = API::userPut(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); $xml = API::getItemXML($itemKey); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Test and Foo", $creatorSummary); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $json['creators'][] = array("creatorType" => "author", "firstName" => "Bob", "lastName" => "Bar"); $response = API::userPut(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); $xml = API::getItemXML($itemKey); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Test et al.", $creatorSummary); }
public function testOverlongSetting() { $settingKey = "tagColors"; $value = array(array("name" => $this->content = str_repeat("abcdefghij", 1001), "color" => "#990000")); $json = array("value" => $value, "version" => 0); $response = API::userPut(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey'], json_encode($json), array("Content-Type: application/json")); $this->assert400($response, "'value' cannot be longer than 1000 characters"); }
public function testCollectionItemUpdate() { $collectionKey = Sync::createCollection(self::$sessionID, self::$config['libraryID'], "Test", null, $this); $itemKey = Sync::createItem(self::$sessionID, self::$config['libraryID'], "book", null, $this); $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; // Get the item version $itemXML = API::getItemXML($itemKey); $data = API::parseDataFromAtomEntry($itemXML); $json = json_decode($data['content'], true); $itemVersion = $json['itemVersion']; $this->assertNotNull($itemVersion); // Add via sync $collectionXML = $xml->updated[0]->collections[0]->collection[0]; $collectionXML['libraryID'] = self::$config['libraryID']; $collectionXML->addChild("items", $itemKey); $data = '<data version="9"><collections>' . $collectionXML->asXML() . '</collections>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data, true); Sync::waitForUpload(self::$sessionID, $response, $this); // Make sure item was updated $itemXML = API::getItemXML($itemKey); $data = API::parseDataFromAtomEntry($itemXML); $json = json_decode($data['content'], true); $this->assertGreaterThan($itemVersion, $json['itemVersion']); $itemVersion = $json['itemVersion']; $this->assertCount(1, $json['collections']); $this->assertContains($collectionKey, $json['collections']); // Remove via sync $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $collectionXML = $xml->updated[0]->collections[0]->collection[0]; $collectionXML['libraryID'] = self::$config['libraryID']; unset($collectionXML->items); $data = '<data version="9"><collections>' . $collectionXML->asXML() . '</collections>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data, true); Sync::waitForUpload(self::$sessionID, $response, $this); // Make sure item was removed $itemXML = API::getItemXML($itemKey); $data = API::parseDataFromAtomEntry($itemXML); $json = json_decode($data['content'], true); $this->assertGreaterThan($itemVersion, $json['itemVersion']); $this->assertCount(0, $json['collections']); }
public function testUnicodeTitle() { $title = "Tést"; $xml = API::createItem("book", array("title" => $title), $this); $data = API::parseDataFromAtomEntry($xml); $key = $data['key']; // Test entry $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json"); $this->assertContains('"title": "Tést"', $response->getBody()); // Test feed $response = API::userGet(self::$config['userID'], "items?key=" . self::$config['apiKey'] . "&content=json"); $this->assertContains('"title": "Tést"', $response->getBody()); }
/** * Changing a group's metadata should change its ETag */ public function testUpdateMetadata() { $response = API::userGet(self::$config['userID'], "groups?content=json&key=" . self::$config['apiKey']); $this->assert200($response); // Get group API URI and ETag $xml = API::getXMLFromResponse($response); $xml->registerXPathNamespace('atom', 'http://www.w3.org/2005/Atom'); $xml->registerXPathNamespace('zapi', 'http://zotero.org/ns/api'); $groupID = (string) array_shift($xml->xpath("//atom:entry/zapi:groupID")); $url = (string) array_shift($xml->xpath("//atom:entry/atom:link[@rel='self']/@href")); $url = str_replace(self::$config['apiURLPrefix'], '', $url); $etag = (string) array_shift($xml->xpath("//atom:entry/atom:content/@etag")); // Make sure format=etags returns the same ETag $response = API::userGet(self::$config['userID'], "groups?format=etags&key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody()); $this->assertEquals($etag, $json->{$groupID}); // Update group metadata $json = json_decode(array_shift($xml->xpath("//atom:entry/atom:content"))); $xml = new SimpleXMLElement("<group/>"); foreach ($json as $key => $val) { switch ($key) { case 'id': case 'members': continue; case 'name': $name = "My Test Group " . uniqid(); $xml['name'] = $name; break; case 'description': $description = "This is a test description " . uniqid(); $xml->{$key} = $description; break; case 'url': $urlField = "http://example.com/" . uniqid(); $xml->{$key} = $urlField; break; default: $xml[$key] = $val; } } $xml = trim(preg_replace('/^<\\?xml.+\\n/', "", $xml->asXML())); $response = API::put($url, $xml, array("Content-Type: text/xml"), array("username" => self::$config['rootUsername'], "password" => self::$config['rootPassword'])); $this->assert200($response); $xml = API::getXMLFromResponse($response); $xml->registerXPathNamespace('zxfer', 'http://zotero.org/ns/transfer'); $group = $xml->xpath('//atom:entry/atom:content/zxfer:group'); $this->assertCount(1, $group); $this->assertEquals($name, $group[0]['name']); $response = API::userGet(self::$config['userID'], "groups?format=etags&key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody()); $newETag = $json->{$groupID}; $this->assertNotEquals($etag, $newETag); // Check ETag header on individual group request $response = API::groupGet($groupID, "?content=json&key=" . self::$config['apiKey']); $this->assert200($response); $this->assertEquals($newETag, $response->getHeader('ETag')); $json = json_decode(API::getContentFromResponse($response)); $this->assertEquals($name, $json->name); $this->assertEquals($description, $json->description); $this->assertEquals($urlField, $json->url); }
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?key=" . self::$config['apiKey'] . "&content=json&q=" . urlencode($title1)); $this->assert200($response); $this->assertNumResults(1, $response); $xml = API::getXMLFromResponse($response); $xpath = $xml->xpath('//atom:entry/zapi:key'); $key = (string) array_shift($xpath); $this->assertEquals($keys[0], $key); // Search by both by title, date asc $response = API::userGet(self::$config['userID'], "items?key=" . self::$config['apiKey'] . "&content=json&q=title&order=date&sort=asc"); $this->assert200($response); $this->assertNumResults(2, $response); $xml = API::getXMLFromResponse($response); $xpath = $xml->xpath('//atom:entry/zapi:key'); $key = (string) array_shift($xpath); $this->assertEquals($keys[1], $key); $key = (string) array_shift($xpath); $this->assertEquals($keys[0], $key); // Search by both by title, date desc $response = API::userGet(self::$config['userID'], "items?key=" . self::$config['apiKey'] . "&content=json&q=title&order=date&sort=desc"); $this->assert200($response); $this->assertNumResults(2, $response); $xml = API::getXMLFromResponse($response); $xpath = $xml->xpath('//atom:entry/zapi:key'); $key = (string) array_shift($xpath); $this->assertEquals($keys[0], $key); $key = (string) array_shift($xpath); $this->assertEquals($keys[1], $key); }
public function testSyncUploadUnchanged() { $data1 = API::createItem("audioRecording", array("title" => "Test", "relations" => array('owl:sameAs' => 'http://zotero.org/groups/1/items/AAAAAAAA')), null, 'data'); // dc:relation already exists, so item shouldn't change $data2 = API::createItem("interview", array("relations" => array('dc:relation' => 'http://zotero.org/users/' . self::$config['userID'] . '/items/' . $data1['key'])), null, 'data'); // Upload unchanged via sync $xml = Sync::updated(self::$sessionID); $updateKey = $xml['updateKey']; $lastSyncTimestamp = $xml['timestamp']; $itemXML1 = array_shift($xml->updated[0]->items[0]->xpath("item[@key='{$data1['key']}']")); $itemXML2 = array_shift($xml->updated[0]->items[0]->xpath("item[@key='{$data2['key']}']")); $itemXML1['libraryID'] = self::$config['libraryID']; $itemXML2['libraryID'] = self::$config['libraryID']; $xmlstr = '<data version="9">' . '<items>' . $itemXML1->asXML() . $itemXML2->asXML() . '</items>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); // Check via API to make sure they're the same $response = API::userGet(self::$config['userID'], "items?key=" . self::$config['apiKey'] . "&format=versions"); $json = API::getJSONFromResponse($response); $this->assertEquals($data1['version'], $json[$data1['key']]); $this->assertEquals($data2['version'], $json[$data2['key']]); }
public function testTagDeleteUnmodifiedItemChange() { $key = 'AAAAAAAA'; $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; // Create item via sync $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="book" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $key . '"/></items>' . '<tags><tag libraryID="' . self::$config['libraryID'] . '" name="Test" ' . 'dateAdded="2009-03-07 04:54:56" ' . 'dateModified="2009-03-07 04:54:56" ' . 'key="BBBBBBBB">' . '<items>' . $key . '</items>' . '</tag></tags></data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data); Sync::waitForUpload(self::$sessionID, $response, $this); // Get item via API $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json"); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $originalVersion = $data['version']; $this->assertCount(1, $json->tags); $this->assertTrue(isset($json->tags[0]->tag)); $this->assertEquals("Test", $json->tags[0]->tag); // Get item via sync $xml = Sync::updated(self::$sessionID); $this->assertEquals(1, sizeOf($xml->updated->items->item)); $this->assertEquals(1, sizeOf($xml->updated->tags->tag)); $this->assertEquals(1, sizeOf($xml->updated->tags->tag[0]->items)); $lastsync = (int) $xml['timestamp']; usleep(1500000); // Increment the library version, since we're testing the // version below API::createItem('newspaperArticle', false, false, 'key'); $libraryVersion = API::getLibraryVersion(); $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; // Delete tag via sync, with unmodified item $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="book" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $key . '"/></items>' . '<deleted><tags><tag libraryID="' . self::$config['libraryID'] . '" key="BBBBBBBB"/>' . '</tags></deleted></data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data); Sync::waitForUpload(self::$sessionID, $response, $this); // Get item via sync $xml = Sync::updated(self::$sessionID); $this->assertEquals(1, sizeOf(isset($xml->updated->tags->tag))); $this->assertFalse(isset($xml->updated->tags->tag[0]->items)); // Get item version via API $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json"); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $this->assertEquals(0, (int) array_shift($xml->xpath('/atom:entry/zapi:numTags'))); $this->assertCount(0, $json->tags); // New item version should be greater than before $this->assertGreaterThan($originalVersion, $data['version']); // And should be one more than previous version $this->assertEquals($libraryVersion + 1, $data['version']); // Only the newspaperArticle should be updated $xml = Sync::updated(self::$sessionID, $lastsync); $this->assertEquals(1, $xml->updated[0]->items[0]->count()); }
public function testCircularRelatedItems() { $parentKey = API::createItem("book", false, null, 'key'); $noteKeys = [API::createNoteItem("Note 1", $parentKey, null, 'key'), API::createNoteItem("Note 2", $parentKey, null, 'key'), API::createNoteItem("Note 3", $parentKey, null, 'key'), API::createNoteItem("Note 4", $parentKey, null, 'key')]; $xml = Sync::updated(self::$sessionID); $updateKey = $xml['updateKey']; $note1XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[0]}']")); $note2XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[1]}']")); $note3XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[2]}']")); $note4XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[3]}']")); $note1XML['libraryID'] = self::$config['libraryID']; $note2XML['libraryID'] = self::$config['libraryID']; $note3XML['libraryID'] = self::$config['libraryID']; $note4XML['libraryID'] = self::$config['libraryID']; $note1XML->related = implode(' ', [$parentKey, (string) $note2XML['key'], (string) $note3XML['key'], (string) $note4XML['key']]); $note2XML->related = implode(' ', [$parentKey, (string) $note1XML['key'], (string) $note3XML['key'], (string) $note4XML['key']]); $note3XML->related = implode(' ', [$parentKey, (string) $note1XML['key'], (string) $note2XML['key'], (string) $note4XML['key']]); $note4XML->related = implode(' ', [$parentKey, (string) $note1XML['key'], (string) $note2XML['key'], (string) $note3XML['key']]); $xmlstr = '<data version="9">' . '<items>' . $note1XML->asXML() . $note2XML->asXML() . $note3XML->asXML() . $note4XML->asXML() . '</items>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); $xml = Sync::updated(self::$sessionID); $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[0]}']")); $keys = split(' ', $noteXML->related); $this->assertCount(4, $keys); $this->assertContains($parentKey, $keys); $this->assertContains((string) $noteKeys[1], $keys); $this->assertContains((string) $noteKeys[2], $keys); $this->assertContains((string) $noteKeys[3], $keys); $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[1]}']")); $keys = split(' ', $noteXML->related); $this->assertCount(4, $keys); $this->assertContains($parentKey, $keys); $this->assertContains((string) $noteKeys[0], $keys); $this->assertContains((string) $noteKeys[2], $keys); $this->assertContains((string) $noteKeys[3], $keys); $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[2]}']")); $keys = split(' ', $noteXML->related); $this->assertCount(4, $keys); $this->assertContains($parentKey, $keys); $this->assertContains((string) $noteKeys[0], $keys); $this->assertContains((string) $noteKeys[1], $keys); $this->assertContains((string) $noteKeys[3], $keys); $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[3]}']")); $keys = split(' ', $noteXML->related); $this->assertCount(4, $keys); $this->assertContains($parentKey, $keys); $this->assertContains((string) $noteKeys[0], $keys); $this->assertContains((string) $noteKeys[1], $keys); $this->assertContains((string) $noteKeys[2], $keys); }
public function testDownloadCache() { $keys = []; $keys[] = API::createItem("book", false, false, 'key'); $keys[] = API::createItem("journalArticle", false, false, 'key'); $keys[] = API::createItem("newspaperArticle", false, false, 'key'); $keys[] = API::createItem("magazineArticle", false, false, 'key'); $keys[] = API::createItem("bookSection", false, false, 'key'); $keys[] = API::createItem("audioRecording", false, false, 'key'); $xml1 = Sync::updated(self::$sessionID); $xml2 = Sync::updated(self::$sessionID); $this->assertEquals(preg_replace('/timestamp="\\d+"/', 'timestamp="--"', $xml1->asXML()), preg_replace('/timestamp="\\d+"/', 'timestamp="--"', $xml2->asXML())); }
private function _testDeleteAndDeleted($objectType) { API::userClear(self::$config['userID']); $objectTypePlural = API::getPluralObjectType($objectType); $xml = Sync::updated(self::$sessionID); $lastSyncTimestamp = (int) $xml['timestamp']; // Create via sync switch ($objectType) { case 'item': $keys[] = Sync::createItem(self::$sessionID, self::$config['libraryID'], "book", false, $this); break; case 'setting': $settingKey = "tagColors"; $response = API::userPut(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey'], json_encode(array("value" => array(array("name" => "_READ", "color" => "#990000")))), array("Content-Type: application/json", "If-Unmodified-Since-Version: 0")); $this->assertEquals(204, $response->getStatus()); $keys[] = $settingKey; break; } // Check via API foreach ($keys as $key) { $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$key}?key=" . self::$config['apiKey']); $this->assertEquals(200, $response->getStatus()); $version = $response->getHeader("Last-Modified-Version"); $this->assertNotNull($version); } // Get empty deleted via API $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newer={$version}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertEmpty($json[$objectTypePlural]); // Get empty deleted via API with newertime $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime={$lastSyncTimestamp}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertEmpty($json[$objectTypePlural]); // Delete via sync foreach ($keys as $key) { switch ($objectType) { case 'item': Sync::deleteItem(self::$sessionID, self::$config['libraryID'], $key, $this); break; case 'setting': // Delete via sync $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $xmlstr = '<data version="9">' . '<deleted>' . '<settings>' . '<setting libraryID="' . self::$config['libraryID'] . '" key="' . $key . '"/>' . '</settings>' . '</deleted>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); break; } } // Check 404 via API foreach ($keys as $key) { $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$key}?key=" . self::$config['apiKey']); $this->assertEquals(404, $response->getStatus()); } // Get deleted via API $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newer={$version}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertArrayHasKey($objectTypePlural, $json); $this->assertCount(sizeOf($keys), $json[$objectTypePlural]); foreach ($keys as $key) { $this->assertContains($key, $json[$objectTypePlural]); } // Get deleted via API with newertime $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime={$lastSyncTimestamp}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertArrayHasKey($objectTypePlural, $json); $this->assertCount(sizeOf($keys), $json[$objectTypePlural]); foreach ($keys as $key) { $this->assertContains($key, $json[$objectTypePlural]); } // Should be empty with later newertime $xml = Sync::updated(self::$sessionID); $lastSyncTimestamp = (int) $xml['timestamp']; $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime=" . ($lastSyncTimestamp + 2)); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertEmpty($json[$objectTypePlural]); }
public function testAddFileLinkedAttachment() { $xml = API::createAttachmentItem("linked_file", [], false, $this); $data = API::parseDataFromAtomEntry($xml); $file = "work/file"; $fileContents = self::getRandomUnicodeString(); file_put_contents($file, $fileContents); $hash = md5_file($file); $filename = "test_" . $fileContents; $mtime = filemtime($file) * 1000; $size = filesize($file); $contentType = "text/plain"; $charset = "utf-8"; // Get upload authorization $response = API::userPost(self::$config['userID'], "items/{$data['key']}/file?key=" . self::$config['apiKey'], $this->implodeParams(array("md5" => $hash, "filename" => $filename, "filesize" => $size, "mtime" => $mtime, "contentType" => $contentType, "charset" => $charset)), array("Content-Type: application/x-www-form-urlencoded", "If-None-Match: *")); $this->assert400($response); }
public function testFullTextNoAccess() { API::groupClear(self::$config['ownedPrivateGroupID2']); // Add item to group as user 2 $user2SessionID = Sync::login(['username' => self::$config['username2'], 'password' => self::$config['password2']]); $xml = Sync::updated($user2SessionID); $updateKey = (string) $xml['updateKey']; $key = Zotero_Utilities::randomString(8, 'key', true); $dateAdded = date('Y-m-d H:i:s', time() - 1); $dateModified = date('Y-m-d H:i:s'); $xmlstr = '<data version="9">' . '<items>' . '<item libraryID="' . self::$config['ownedPrivateGroupLibraryID2'] . '" ' . 'itemType="attachment" ' . 'dateAdded="' . $dateAdded . '" ' . 'dateModified="' . $dateModified . '" ' . 'key="' . $key . '"/>' . '</items>' . '</data>'; $response = Sync::upload($user2SessionID, $updateKey, $xmlstr); Sync::waitForUpload($user2SessionID, $response, $this); // Make sure item exists $xml = Sync::updated($user2SessionID, 1); $this->assertEquals(1, $xml->updated[0]->items->count()); $this->assertEquals(1, $xml->updated[0]->items[0]->item->count()); // Try to add full-text content as user 1 $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $content = "This is some full-text content."; $totalChars = 2500; $xmlstr = '<data version="9">' . '<fulltexts>' . '<fulltext libraryID="' . self::$config['ownedPrivateGroupLibraryID2'] . '" ' . 'key="' . $key . '" ' . 'indexedChars="' . strlen($content) . '" ' . 'totalChars="' . $totalChars . '" ' . 'indexedPages="0" ' . 'totalPages="0">' . htmlspecialchars($content) . '</fulltext>' . '</fulltexts>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); // Retrieve it as user 2 $xml = Sync::updated($user2SessionID, 1, false, false, ["ft" => 1]); $this->assertEquals(0, $xml->updated[0]->fulltexts->count()); API::groupClear(self::$config['ownedPrivateGroupID2']); }
public function testDeleteItemContent() { $key = API::createItem("book", false, $this, 'key'); $xml = API::createAttachmentItem("imported_file", [], $key, $this, 'atom'); $data = API::parseDataFromAtomEntry($xml); $content = "Ыюм мютат дэбетиз конвынёры эю, ку мэль жкрипта трактатоз.\nПро ут чтэт эрепюят граэкйж, дуо нэ выро рыкючабо пырикюлёз."; // Store content $response = API::userPut(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey'], json_encode(["content" => $content, "indexedPages" => 50]), array("Content-Type: application/json")); $this->assert204($response); $contentVersion = $response->getHeader("Last-Modified-Version"); // Retrieve it $response = API::userGet(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody(), true); $this->assertEquals($content, $json['content']); $this->assertEquals(50, $json['indexedPages']); // Set to empty string $response = API::userPut(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey'], json_encode(["content" => ""]), array("Content-Type: application/json")); $this->assert204($response); $this->assertGreaterThan($contentVersion, $response->getHeader("Last-Modified-Version")); // Make sure it's gone $response = API::userGet(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody(), true); $this->assertEquals("", $json['content']); $this->assertArrayNotHasKey("indexedPages", $json); }
public function testNoteTooLongWithinHTMLTags() { $this->json->note = " \n<p><!-- " . $this->content . " --></p>"; $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(array("items" => array($this->json))), array("Content-Type: application/json")); $this->assert400ForObject($response, "Note '<p><!-- 1234567890123456789012345678901234567890123456789012345678901234...' too long"); }
/** * When modifying a tag on an item, only the item itself should have its * version updated, not other items that had (and still have) the same tag */ public function testTagAddItemVersionChange() { $data1 = API::createItem("book", array("tags" => array(array("tag" => "a"), array("tag" => "b"))), $this, 'data'); $json1 = json_decode($data1['content'], true); $version1 = $data1['version']; $data2 = API::createItem("book", array("tags" => array(array("tag" => "a"), array("tag" => "c"))), $this, 'data'); $json2 = json_decode($data2['content'], true); $version2 = $data2['version']; // Remove tag 'a' from item 1 $json1['tags'] = array(array("tag" => "d"), array("tag" => "c")); $response = API::postItem($json1); $this->assert200($response); // Item 1 version should be one greater than last update $xml1 = API::getItemXML($json1['itemKey']); $data1 = API::parseDataFromAtomEntry($xml1); $this->assertEquals($version2 + 1, $data1['version']); // Item 2 version shouldn't have changed $xml2 = API::getItemXML($json2['itemKey']); $data2 = API::parseDataFromAtomEntry($xml2); $this->assertEquals($version2, $data2['version']); }
public function testDeleteCollectionRelation() { $relations = array("owl:sameAs" => "http://zotero.org/groups/1/collections/AAAAAAAA"); $data = API::createCollection("Test", array("relations" => $relations), $this, 'data'); $json = json_decode($data['content'], true); // Remove all relations $json['relations'] = new \stdClass(); unset($relations['owl:sameAs']); $response = API::userPut(self::$config['userID'], "collections/{$data['key']}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); // Make sure it's gone $xml = API::getCollectionXML($data['key']); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $this->assertCount(sizeOf($relations), $json['relations']); foreach ($relations as $predicate => $object) { $this->assertEquals($object, $json['relations'][$predicate]); } }
public function testCollectionItems() { $collectionKey = API::createCollection('Test', false, $this, 'key'); $xml = API::createItem("book", array('collections' => array($collectionKey)), $this); $data = API::parseDataFromAtomEntry($xml); $itemKey1 = $data['key']; $itemVersion1 = $data['version']; $json = json_decode($data['content']); $this->assertEquals(array($collectionKey), $json->collections); $xml = API::createItem("journalArticle", array('collections' => array($collectionKey)), $this); $data = API::parseDataFromAtomEntry($xml); $itemKey2 = $data['key']; $itemVersion2 = $data['version']; $json = json_decode($data['content']); $this->assertEquals(array($collectionKey), $json->collections); $childItemKey1 = API::createAttachmentItem("linked_url", [], $itemKey1, $this, 'key'); $childItemKey2 = API::createAttachmentItem("linked_url", [], $itemKey2, $this, 'key'); $response = API::userGet(self::$config['userID'], "collections/{$collectionKey}/items?key=" . self::$config['apiKey'] . "&format=keys"); $this->assert200($response); $keys = explode("\n", trim($response->getBody())); $this->assertCount(4, $keys); $this->assertContains($itemKey1, $keys); $this->assertContains($itemKey2, $keys); $this->assertContains($childItemKey1, $keys); $this->assertContains($childItemKey2, $keys); $response = API::userGet(self::$config['userID'], "collections/{$collectionKey}/items/top?key=" . self::$config['apiKey'] . "&format=keys"); $this->assert200($response); $keys = explode("\n", trim($response->getBody())); $this->assertCount(2, $keys); $this->assertContains($itemKey1, $keys); $this->assertContains($itemKey2, $keys); }
public function testTagDeletePermissions() { API::userClear(self::$config['userID']); $xml = 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); }
private function _testUploadUnmodified($objectType) { $objectTypePlural = API::getPluralObjectType($objectType); switch ($objectType) { case 'collection': $xml = API::createCollection("Name", false, $this); break; case 'item': $xml = API::createItem("book", array("title" => "Title"), $this); break; case 'search': $xml = API::createSearch("Name", 'default', $this); break; } $version = (int) array_shift($xml->xpath('//atom:entry/zapi:version')); $this->assertNotEquals(0, $version); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $response = API::userPut(self::$config['userID'], "{$objectTypePlural}/{$data['key']}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); $this->assertEquals($version, $response->getHeader("Last-Modified-Version")); switch ($objectType) { case 'collection': $xml = API::getCollectionXML($data['key']); break; case 'item': $xml = API::getItemXML($data['key']); break; case 'search': $xml = API::getSearchXML($data['key']); break; } $data = API::parseDataFromAtomEntry($xml); $this->assertEquals($version, $data['version']); }