diff options
-rw-r--r-- | doc/ChangeLog | 15 | ||||
-rw-r--r-- | doc/developers/api | 10 | ||||
-rw-r--r-- | src/SemanticScuttle/Service/Bookmark.php | 18 | ||||
-rw-r--r-- | tests/AllTests.php | 4 | ||||
-rw-r--r-- | tests/Api/PostsAddTest.php | 435 | ||||
-rw-r--r-- | tests/Api/PostsDeleteTest.php | 303 | ||||
-rw-r--r-- | tests/Api/PostsUpdateTest.php | 135 | ||||
-rw-r--r-- | tests/TestBaseApi.php | 21 | ||||
-rw-r--r-- | www/api/httpauth.inc.php | 31 | ||||
-rw-r--r-- | www/api/posts_add.php | 151 | ||||
-rw-r--r-- | www/api/posts_delete.php | 62 | ||||
-rw-r--r-- | www/api/posts_update.php | 46 |
12 files changed, 1137 insertions, 94 deletions
diff --git a/doc/ChangeLog b/doc/ChangeLog index e2562e4..0a60bff 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -3,12 +3,21 @@ ChangeLog for SemantiScuttle 0.9X.X - 2010-XX-XX ------------------- -- Fix bug getTagsForBookmarks() that fetched all tags +- Fix bug in getTagsForBookmarks() that fetched all tags +- Fix bug #3073215: Updating bookmark time does not work +- Fix bug #3074816: French translation breaks edit javascript - Show error message on mysqli connection errors - Implement patch #3059829: update FR_CA translation - Update php-gettext library to 1.0.10 -- Fix bug #3073215: Updating bookmark time does not work -- Fix bug #3074816: French translation breaks edit javascript +- api/posts/add respects the "replace" parameter now + + +0.97.1 - 2010-09-30 +------------------- +This is a security release! We do highly recommend to update +your SemanticScuttle installations! + +- Fix bug #3077187: Permission problem when deleting bookmarks 0.97.0 - 2010-06-09 diff --git a/doc/developers/api b/doc/developers/api new file mode 100644 index 0000000..efa05fe --- /dev/null +++ b/doc/developers/api @@ -0,0 +1,10 @@ +SemanticScuttle API +=================== + +SemanticScuttle tries to implement the delicious API v1 as closely as sensible. + +Where it makes sense and the delicious API just does things plainly wrong +(i.e. when returning a wrong status code on an error), we do it better. + +- http://www.delicious.com/help/api +- http://support.delicious.com/forum/comments.php?DiscussionID=5286&page=1 diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php index dde1df5..4e18d3f 100644 --- a/src/SemanticScuttle/Service/Bookmark.php +++ b/src/SemanticScuttle/Service/Bookmark.php @@ -176,7 +176,10 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService * Retrieves a bookmark with the given URL. * DOES NOT RESPECT PRIVACY SETTINGS! * - * @param string $address URL to get bookmarks for + * @param string $address URL to get bookmarks for + * @param boolean $all Retrieve from all users (true) + * or only bookmarks owned by the current + * user (false) * * @return mixed Array with bookmark data or false in case * of an error (i.e. not found). @@ -184,9 +187,9 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService * @uses getBookmarkByHash() * @see getBookmarkByShortname() */ - public function getBookmarkByAddress($address) + public function getBookmarkByAddress($address, $all = true) { - return $this->getBookmarkByHash($this->getHash($address)); + return $this->getBookmarkByHash($this->getHash($address), $all); } @@ -195,16 +198,19 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService * Retrieves a bookmark with the given hash. * DOES NOT RESPECT PRIVACY SETTINGS! * - * @param string $hash URL hash + * @param string $hash URL hash + * @param boolean $all Retrieve from all users (true) + * or only bookmarks owned by the current + * user (false) * * @return mixed Array with bookmark data or false in case * of an error (i.e. not found). * * @see getHash() */ - public function getBookmarkByHash($hash) + public function getBookmarkByHash($hash, $all = true) { - return $this->_getbookmark('bHash', $hash, true); + return $this->_getbookmark('bHash', $hash, $all); } diff --git a/tests/AllTests.php b/tests/AllTests.php index d29de7f..ded6824 100644 --- a/tests/AllTests.php +++ b/tests/AllTests.php @@ -64,6 +64,10 @@ class AllTests extends PHPUnit_Framework_TestSuite $suite->addTestFile($tdir . '/TagTest.php'); $suite->addTestFile($tdir . '/VoteTest.php'); $suite->addTestFile($tdir . '/UserTest.php'); + $suite->addTestFile($tdir . '/Api/ExportCsvTest.php'); + $suite->addTestFile($tdir . '/Api/PostsAddTest.php'); + $suite->addTestFile($tdir . '/Api/PostsDeleteTest.php'); + $suite->addTestFile($tdir . '/Api/PostsUpdateTest.php'); return $suite; } diff --git a/tests/Api/PostsAddTest.php b/tests/Api/PostsAddTest.php new file mode 100644 index 0000000..1f21d04 --- /dev/null +++ b/tests/Api/PostsAddTest.php @@ -0,0 +1,435 @@ +<?php +/** + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +require_once dirname(__FILE__) . '/../prepare.php'; +require_once 'HTTP/Request2.php'; + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'Api_PostsAddTest::main'); +} + +/** + * Unit tests for the SemanticScuttle post addition API. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class Api_PostsAddTest extends TestBaseApi +{ + protected $urlPart = 'api/posts/add'; + + + + /** + * Used to run this test class standalone + * + * @return void + */ + public static function main() + { + require_once 'PHPUnit/TextUI/TestRunner.php'; + PHPUnit_TextUI_TestRunner::run( + new PHPUnit_Framework_TestSuite(__CLASS__) + ); + } + + + + /** + * Test if authentication is required when sending no auth data + */ + public function testAuthWithoutAuthData() + { + $req = $this->getRequest(null, false); + $res = $req->send(); + $this->assertEquals(401, $res->getStatus()); + } + + + + /** + * Test if authentication is required when sending wrong user data + */ + public function testAuthWrongCredentials() + { + $req = $this->getRequest(null, false); + $req->setAuth('user', 'password', HTTP_Request2::AUTH_BASIC); + $res = $req->send(); + $this->assertEquals(401, $res->getStatus()); + } + + + + /** + * Test if adding a bookmark via POST works. + */ + public function testAddBookmarkPost() + { + $this->bs->deleteAll(); + + $bmUrl = 'http://example.org/tag-1'; + $bmTags = array('foo', 'bar', 'baz'); + $bmDatetime = '2010-09-08T03:02:01Z'; + $bmTitle = 'This is a foo title'; + $bmDescription = <<<TXT +This is the description of +my bookmark with some +newlines and <some>?&\$ÄÖ'"§special"' +characters +TXT; + + list($req, $uId) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $bmUrl); + $req->addPostParameter('description', $bmTitle); + $req->addPostParameter('extended', $bmDescription); + $req->addPostParameter('tags', implode(' ', $bmTags)); + $req->addPostParameter('dt', $bmDatetime); + $res = $req->send(); + + //all should be well + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + null, false + ); + + //user should have one bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $bm = $data['bookmarks'][0]; + + $this->assertEquals($bmUrl, $bm['bAddress']); + $this->assertEquals($bmTitle, $bm['bTitle']); + $this->assertEquals($bmDescription, $bm['bDescription']); + $this->assertEquals($bmTags, $bm['tags']); + $this->assertEquals( + gmdate('Y-m-d H:i:s', strtotime($bmDatetime)), + $bm['bDatetime'] + ); + } + + + + /** + * Test if adding a bookmark via GET works. + */ + public function testAddBookmarkGet() + { + $this->bs->deleteAll(); + + $bmUrl = 'http://example.org/tag-1'; + $bmTags = array('foo', 'bar', 'baz'); + $bmDatetime = '2010-09-08T03:02:01Z'; + $bmTitle = 'This is a foo title'; + $bmDescription = <<<TXT +This is the description of +my bookmark with some +newlines and <some>?&\$ÄÖ'"§special"' +characters +TXT; + + list($req, $uId) = $this->getAuthRequest( + '?url=' . urlencode($bmUrl) + . '&description=' . urlencode($bmTitle) + . '&extended=' . urlencode($bmDescription) + . '&tags=' . urlencode(implode(' ', $bmTags)) + . '&dt=' . urlencode($bmDatetime) + ); + $res = $req->send(); + + //all should be well + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + null, false + ); + + //user should have one bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $bm = $data['bookmarks'][0]; + + $this->assertEquals($bmUrl, $bm['bAddress']); + $this->assertEquals($bmTitle, $bm['bTitle']); + $this->assertEquals($bmDescription, $bm['bDescription']); + $this->assertEquals($bmTags, $bm['tags']); + $this->assertEquals( + gmdate('Y-m-d H:i:s', strtotime($bmDatetime)), + $bm['bDatetime'] + ); + } + + /** + * Verify that the URL and description/title are enough parameters + * to add a bookmark. + */ + public function testUrlDescEnough() + { + $this->bs->deleteAll(); + + list($req, $uId) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', 'http://example.org/tag2'); + $req->addPostParameter('description', 'foo bar'); + $res = $req->send(); + + //all should be well + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + null, false + ); + + //user has 1 bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + } + + /** + * Verify that the URL is required + */ + public function testUrlRequired() + { + $this->bs->deleteAll(); + + list($req, $uId) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + //$req->addPostParameter('url', 'http://example.org/tag2'); + $req->addPostParameter('description', 'foo bar'); + $res = $req->send(); + + //all should be well + $this->assertEquals(400, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'URL missing') + ), + $res->getBody(), + null, false + ); + + //user still has 0 bookmarks + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(0, $data['total']); + } + + /** + * Verify that the description/title is required + */ + public function testDescriptionRequired() + { + $this->bs->deleteAll(); + + list($req, $uId) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', 'http://example.org/tag2'); + $res = $req->send(); + + //all should be well + $this->assertEquals(400, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'Description missing') + ), + $res->getBody(), + null, false + ); + + //user still has 0 bookmarks + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(0, $data['total']); + } + + /** + * Test that the replace=no parameter prevents the bookmark from being + * overwritten. + */ + public function testReplaceNo() + { + $this->bs->deleteAll(); + + $url = 'http://example.org/tag2'; + $title1 = 'foo bar 1'; + $title2 = 'bar 2 foo'; + + list($req, $uId) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $url); + $req->addPostParameter('description', $title1); + $res = $req->send(); + + //all should be well + $this->assertEquals(200, $res->getStatus()); + + //send it a second time, with different title + list($req, $dummy) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $url); + $req->addPostParameter('description', $title2); + $req->addPostParameter('replace', 'no'); + $res = $req->send(); + + //this time we should get an error + $this->assertEquals(409, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'bookmark does already exist') + ), + $res->getBody(), + null, false + ); + + //user still has 1 bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $this->assertEquals($title1, $data['bookmarks'][0]['bTitle']); + + //send it a third time, without the replace parameter + // it defaults to "no", so the bookmark should not get overwritten + list($req, $dummy) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $url); + $req->addPostParameter('description', $title2); + $res = $req->send(); + + //this time we should get an error + $this->assertEquals(409, $res->getStatus()); + + //bookmark should not have changed + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $this->assertEquals($title1, $data['bookmarks'][0]['bTitle']); + } + + /** + * Test that the replace=yes parameter causes the bookmark to be updated. + */ + public function testReplaceYes() + { + $this->bs->deleteAll(); + + $url = 'http://example.org/tag2'; + $title1 = 'foo bar 1'; + $title2 = 'bar 2 foo'; + + list($req, $uId) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $url); + $req->addPostParameter('description', $title1); + $res = $req->send(); + + //all should be well + $this->assertEquals(200, $res->getStatus()); + + //send it a second time, with different title + list($req, $dummy) = $this->getAuthRequest(); + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $url); + $req->addPostParameter('description', $title2); + $req->addPostParameter('replace', 'yes'); + $res = $req->send(); + + //no error + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + null, false + ); + + //user still has 1 bookmark now, but with the new title + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $this->assertEquals($title2, $data['bookmarks'][0]['bTitle']); + } +} + +if (PHPUnit_MAIN_METHOD == 'Api_PostsAddTest::main') { + Api_PostsAddTest::main(); +} +?>
\ No newline at end of file diff --git a/tests/Api/PostsDeleteTest.php b/tests/Api/PostsDeleteTest.php new file mode 100644 index 0000000..d9fb6cd --- /dev/null +++ b/tests/Api/PostsDeleteTest.php @@ -0,0 +1,303 @@ +<?php +/** + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +require_once dirname(__FILE__) . '/../prepare.php'; +require_once 'HTTP/Request2.php'; + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'Api_PostsDeleteTest::main'); +} + +/** + * Unit tests for the SemanticScuttle post deletion API. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class Api_PostsDeleteTest extends TestBaseApi +{ + protected $urlPart = 'api/posts/delete'; + + + + /** + * Used to run this test class standalone + * + * @return void + */ + public static function main() + { + require_once 'PHPUnit/TextUI/TestRunner.php'; + PHPUnit_TextUI_TestRunner::run( + new PHPUnit_Framework_TestSuite(__CLASS__) + ); + } + + + + /** + * Test if authentication is required when sending no auth data + */ + public function testAuthWithoutAuthData() + { + $req = $this->getRequest(null, false); + $res = $req->send(); + $this->assertEquals(401, $res->getStatus()); + } + + + + /** + * Test if authentication is required when sending wrong user data + + */ + public function testAuthWrongCredentials() + { + $req = $this->getRequest(null, false); + $req->setAuth('user', 'password', HTTP_Request2::AUTH_BASIC); + $res = $req->send(); + $this->assertEquals(401, $res->getStatus()); + } + + + + /** + * Test if deleting an own bookmark works. + */ + public function testDeleteOwnBookmark() + { + $this->bs->deleteAll(); + + $bookmarkUrl = 'http://example.org/tag-1'; + + list($req, $uId) = $this->getAuthRequest( + '?url=' . urlencode($bookmarkUrl) + ); + + $bId = $this->addBookmark( + $uId, $bookmarkUrl, 0, + array('unittest', 'tag1') + ); + //user has one bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + + //send request + $res = $req->send(); + + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + null, false + ); + + //bookmark should be deleted now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(0, $data['total']); + } + + + + /** + * Test if deleting an own bookmark via POST works. + */ + public function testDeleteOwnBookmarkPost() + { + $this->bs->deleteAll(); + + $bookmarkUrl = 'http://example.org/tag-1'; + + list($req, $uId) = $this->getAuthRequest(); + + $bId = $this->addBookmark( + $uId, $bookmarkUrl, 0, + array('unittest', 'tag1') + ); + //user has one bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + + //send request + $req->setMethod(HTTP_Request2::METHOD_POST); + $req->addPostParameter('url', $bookmarkUrl); + $res = $req->send(); + + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + null, false + ); + + //bookmark should be deleted now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(0, $data['total']); + } + + + + /** + * Verify that deleting a bookmark of a different does not work + */ + public function testDeleteOtherBookmark() + { + $this->bs->deleteAll(); + + $bookmarkUrl = 'http://example.org/tag-1'; + + list($req, $uId) = $this->getAuthRequest( + '?url=' . urlencode($bookmarkUrl) + ); + $uId2 = $this->addUser(); + + $bId = $this->addBookmark( + $uId2, $bookmarkUrl, 0, + array('unittest', 'tag1') + ); + //user 1 has no bookmarks + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(0, $data['total']); + //user 2 has one bookmark + $data = $this->bs->getBookmarks(0, null, $uId2); + $this->assertEquals(1, $data['total']); + + //send request + $res = $req->send(); + + //404 - user does not have that bookmark + $this->assertEquals(404, $res->getStatus()); + + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'item not found') + ), + $res->getBody(), + '', false + ); + + //bookmark should still be there + $data = $this->bs->getBookmarks(0, null, $uId2); + $this->assertEquals(1, $data['total']); + } + + + + /** + * Test if deleting a bookmark works that also other users + * bookmarked. + */ + public function testDeleteBookmarkOneOfTwo() + { + $this->bs->deleteAll(); + + $bookmarkUrl = 'http://example.org/tag-1'; + + list($req, $uId) = $this->getAuthRequest( + '?url=' . urlencode($bookmarkUrl) + ); + $uId2 = $this->addUser(); + $uId3 = $this->addUser(); + + //important: the order of addition is crucial here + $this->addBookmark( + $uId2, $bookmarkUrl, 0, + array('unittest', 'tag1') + ); + $bId = $this->addBookmark( + $uId, $bookmarkUrl, 0, + array('unittest', 'tag1') + ); + $this->addBookmark( + $uId3, $bookmarkUrl, 0, + array('unittest', 'tag1') + ); + + //user one and two have a bookmark now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $data = $this->bs->getBookmarks(0, null, $uId2); + $this->assertEquals(1, $data['total']); + + //send request + $res = $req->send(); + + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $res->getBody(), + '', false + ); + + //bookmark should be deleted now + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(0, $data['total']); + //user 2 should still have his + $data = $this->bs->getBookmarks(0, null, $uId2); + $this->assertEquals(1, $data['total']); + //user 3 should still have his, too + $data = $this->bs->getBookmarks(0, null, $uId3); + $this->assertEquals(1, $data['total']); + } + +} + +if (PHPUnit_MAIN_METHOD == 'Api_PostsDeleteTest::main') { + Api_PostsDeleteTest::main(); +} +?>
\ No newline at end of file diff --git a/tests/Api/PostsUpdateTest.php b/tests/Api/PostsUpdateTest.php new file mode 100644 index 0000000..c497a55 --- /dev/null +++ b/tests/Api/PostsUpdateTest.php @@ -0,0 +1,135 @@ +<?php +/** + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +require_once dirname(__FILE__) . '/../prepare.php'; +require_once 'HTTP/Request2.php'; + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'Api_PostsUpdateTest::main'); +} + +/** + * Unit tests for the SemanticScuttle last-update time API. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class Api_PostsUpdateTest extends TestBaseApi +{ + protected $urlPart = 'api/posts/update'; + + + + /** + * Used to run this test class standalone + * + * @return void + */ + public static function main() + { + require_once 'PHPUnit/TextUI/TestRunner.php'; + PHPUnit_TextUI_TestRunner::run( + new PHPUnit_Framework_TestSuite(__CLASS__) + ); + } + + + + /** + * Test if authentication is required when sending no auth data + */ + public function testAuthWithoutAuthData() + { + $req = $this->getRequest(null, false); + $res = $req->send(); + $this->assertEquals(401, $res->getStatus()); + } + + + + /** + * Test if authentication is required when sending wrong user data + + */ + public function testAuthWrongCredentials() + { + $req = $this->getRequest(null, false); + $req->setAuth('user', 'password', HTTP_Request2::AUTH_BASIC); + $res = $req->send(); + $this->assertEquals(401, $res->getStatus()); + } + + + + /** + * See if posts/update behaves correct if there is one bookmark + */ + public function testPostUpdateOneBookmark() + { + $this->bs->deleteAll(); + + list($req, $uId) = $this->getAuthRequest(); + $bId = $this->addBookmark( + $uId, 'http://example.org/tag1', 0, + array('unittest', 'tag1') + ); + + $data = $this->bs->getBookmarks(0, null, $uId); + $this->assertEquals(1, $data['total']); + $bookmark = $data['bookmarks'][0]; + + //send request + $res = $req->send(); + + $this->assertEquals(200, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertTag( + array( + 'tag' => 'update', + 'attributes' => array( + 'inboxnew' => '0' + ) + ), + $res->getBody(), + '', false + ); + //check time + $xml = simplexml_load_string($res->getBody()); + $this->assertTrue(isset($xml['time'])); + $this->assertEquals( + strtotime($bookmark['bDatetime']), + strtotime( + (string)$xml['time'] + ) + ); + } + +} + +if (PHPUnit_MAIN_METHOD == 'Api_PostsUpdateTest::main') { + Api_PostsUpdateTest::main(); +} +?>
\ No newline at end of file diff --git a/tests/TestBaseApi.php b/tests/TestBaseApi.php index 645ead9..eb1457f 100644 --- a/tests/TestBaseApi.php +++ b/tests/TestBaseApi.php @@ -29,6 +29,16 @@ class TestBaseApi extends TestBase protected $url; protected $urlPart = null; + /** + * @var SemanticScuttle_Service_User + */ + protected $us; + + /** + * @var SemanticScuttle_Service_Bookmark + */ + protected $bs; + protected function setUp() @@ -52,11 +62,14 @@ class TestBaseApi extends TestBase /** - * Gets a HTTP request object + * Gets a HTTP request object. + * Uses $this->url plus $urlSuffix as request URL. * * @param string $urlSuffix Suffix for the URL * * @return HTTP_Request2 HTTP request object + * + * @uses $url */ protected function getRequest($urlSuffix = null) { @@ -71,13 +84,17 @@ class TestBaseApi extends TestBase /** - * Gets a HTTP request object + * Creates a user and a HTTP request object and prepares + * the request object with authentication details, so that + * the user is logged in. * * @param string $urlSuffix Suffix for the URL * @param mixed $auth If user authentication is needed (true/false) * or array with username and password * * @return array(HTTP_Request2, integer) HTTP request object and user id + * + * @uses getRequest() */ protected function getAuthRequest($urlSuffix = null, $auth = true) { diff --git a/www/api/httpauth.inc.php b/www/api/httpauth.inc.php index 0e3a66d..ee5c7f2 100644 --- a/www/api/httpauth.inc.php +++ b/www/api/httpauth.inc.php @@ -1,10 +1,29 @@ <?php +/** + * Checks if the user is logged on and sends a HTTP basic auth + * request to the browser if not. In that case the script ends. + * If username and password are available, the user service's + * login method is used to log the user in. + * + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ require_once '../www-header.php'; -// Provides HTTP Basic authentication of a user -// and logs the user in if necessary - -function authenticate() { +/** + * Sends HTTP auth headers to the browser + */ +function authenticate() +{ header('WWW-Authenticate: Basic realm="SemanticScuttle API"'); header('HTTP/1.0 401 Unauthorized'); @@ -26,7 +45,9 @@ if (!$userservice->isLoggedOn()) { if (!isset($_SERVER['PHP_AUTH_USER'])) { authenticate(); } else { - $login = $userservice->login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); + $login = $userservice->login( + $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] + ); if ($login) { $currentUser = $userservice->getCurrentObjectUser(); } else { diff --git a/www/api/posts_add.php b/www/api/posts_add.php index 59f7dce..7f9dc59 100644 --- a/www/api/posts_add.php +++ b/www/api/posts_add.php @@ -1,67 +1,105 @@ <?php -// Implements the del.icio.us API request to add a new post. -// http://delicious.com/help/api#posts_add - -// del.icio.us behavior: -// - tags can't have spaces -// - address and description are mandatory - -// Scuttle behavior: -// - Additional 'status' variable for privacy -// - No support for 'replace' variable +/** + * API for adding a new bookmark. + * + * The following POST and GET parameters are accepted: + * @param string $url URL of the bookmark (required) + * @param string $description Bookmark title (required) + * @param string $extended Extended bookmark description (optional) + * @param string $tags Space-separated list of tags (optional) + * @param string $dt Date and time of bookmark creation (optional) + * Must be of format YYYY-MM-DDTHH:II:SSZ + * @param integer $status Visibility status (optional): + * - 2 or 'private': Bookmark is totally private + * - 1 or 'shared': People on the user's watchlist + * can see it + * - 0 or 'public': Everyone can see the bookmark + * @param string $shared "no" or "yes": Switches between private and + * public (optional) + * @param string $replace "yes" or "no" - replaces a bookmark with the + * same URL (optional) + * + * Notes: + * - tags cannot have spaces + * - URL and description (title) are mandatory + * - delicious "description" is the "title" in SemanticScuttle + * - delicious "extended" is the "description" in SemanticScuttle + * - "status" is a SemanticScuttle addition to this API method + * + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + * @link http://www.delicious.com/help/api + */ // Force HTTP authentication $httpContentType = 'text/xml'; require_once 'httpauth.inc.php'; -/* Service creation: only useful services are created */ -$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark'); +$bs = SemanticScuttle_Service_Factory::get('Bookmark'); // Get all the bookmark's passed-in information -if (isset($_REQUEST['url']) && (trim($_REQUEST['url']) != '')) +if (isset($_REQUEST['url']) && (trim($_REQUEST['url']) != '')) { $url = trim(urldecode($_REQUEST['url'])); -else - $url = NULL; +} else { + $url = null; +} -if (isset($_REQUEST['description']) && (trim($_REQUEST['description']) != '')) +if (isset($_REQUEST['description']) && (trim($_REQUEST['description']) != '')) { $description = trim($_REQUEST['description']); -else - $description = NULL; +} else { + $description = null; +} -if (isset($_REQUEST['extended']) && (trim($_REQUEST['extended']) != "")) +if (isset($_REQUEST['extended']) && (trim($_REQUEST['extended']) != '')) { $extended = trim($_REQUEST['extended']); -else - $extended = NULL; +} else { + $extended = null; +} -if (isset($_REQUEST['tags']) && (trim($_REQUEST['tags']) != '') && (trim($_REQUEST['tags']) != ',')) +if (isset($_REQUEST['tags']) && (trim($_REQUEST['tags']) != '') + && (trim($_REQUEST['tags']) != ',') +) { $tags = trim($_REQUEST['tags']); -else - $tags = NULL; +} else { + $tags = null; +} -if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != '')) +if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != '')) { $dt = trim($_REQUEST['dt']); -else - $dt = NULL; +} else { + $dt = null; +} + +$replace = isset($_REQUEST['replace']) && ($_REQUEST['replace'] == 'yes'); $status = 0; if (isset($_REQUEST['status'])) { $status_str = trim($_REQUEST['status']); if (is_numeric($status_str)) { $status = intval($status_str); - if($status < 0 || $status > 2) { + if ($status < 0 || $status > 2) { $status = 0; } } else { switch ($status_str) { - case 'private': - $status = 2; - break; - case 'shared': - $status = 1; - break; - default: - $status = 0; - break; + case 'private': + $status = 2; + break; + case 'shared': + $status = 1; + break; + default: + $status = 0; + break; } } } @@ -71,17 +109,38 @@ if (isset($_REQUEST['shared']) && (trim($_REQUEST['shared']) == 'no')) { } // Error out if there's no address or description -if (is_null($url) || is_null($description)) { - $added = false; +if (is_null($url)) { + header('HTTP/1.0 400 Bad Request'); + $msg = 'URL missing'; +} else if (is_null($description)) { + header('HTTP/1.0 400 Bad Request'); + $msg = 'Description missing'; } else { -// We're good with info; now insert it! - if ($bookmarkservice->bookmarkExists($url, $userservice->getCurrentUserId())) - $added = false; - else - $added = $bookmarkservice->addBookmark($url, $description, $extended, '', $status, $tags, null, $dt, true); + // We're good with info; now insert it! + $exists = $bs->bookmarkExists($url, $userservice->getCurrentUserId()); + if ($exists) { + if (!$replace) { + header('HTTP/1.0 409 Conflict'); + $msg = 'bookmark does already exist'; + } else { + //delete it before we re-add it + $bookmark = $bs->getBookmarkByAddress($url, false); + $bId = $bookmark['bId']; + $bs->deleteBookmark($bId); + + $exists = false; + } + } + + if (!$exists) { + $added = $bs->addBookmark( + $url, $description, $extended, '', $status, $tags, null, $dt, true + ); + $msg = 'done'; + } } // Set up the XML file and output the result. -echo '<?xml version="1.0" standalone="yes" ?'.">\r\n"; -echo '<result code="'. ($added ? 'done' : 'something went wrong') .'" />'; +echo '<?xml version="1.0" standalone="yes" ?' . ">\r\n"; +echo '<result code="' . $msg .'" />'; ?>
\ No newline at end of file diff --git a/www/api/posts_delete.php b/www/api/posts_delete.php index a63cc62..69b2429 100644 --- a/www/api/posts_delete.php +++ b/www/api/posts_delete.php @@ -1,33 +1,57 @@ <?php -// Implements the del.icio.us API request to delete a post. - -// del.icio.us behavior: -// - returns "done" even if the bookmark doesn't exist; -// - does NOT allow the hash for the url parameter; -// - doesn't set the Content-Type to text/xml (we do). +/** + * API for deleting a bookmark. + * The delicious API is implemented here. + * + * The delicious API behaves like that: + * - does NOT allow the hash for the url parameter + * - doesn't set the Content-Type to text/xml + * - we do it correctly, too + * + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + * @link http://www.delicious.com/help/api + */ // Force HTTP authentication first! $httpContentType = 'text/xml'; require_once 'httpauth.inc.php'; -/* Service creation: only useful services are created */ -$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark'); - +$bs = SemanticScuttle_Service_Factory::get('Bookmark'); +$uId = $userservice->getCurrentUserId(); -// Note that del.icio.us only errors out if no URL was passed in; there's no error on attempting -// to delete a bookmark you don't have. // Error out if there's no address -if (is_null($_REQUEST['url'])) { - $deleted = false; +if (!isset($_REQUEST['url']) + || $_REQUEST['url'] == '' +) { + $msg = 'something went wrong'; +} else if (!$bs->bookmarkExists($_REQUEST['url'], $uId)) { + //the user does not have such a bookmark + header('HTTP/1.0 404 Not Found'); + $msg = 'item not found'; } else { - $bookmark = $bookmarkservice->getBookmarkByAddress($_REQUEST['url']); - $bid = $bookmark['bId']; - $delete = $bookmarkservice->deleteBookmark($bid); - $deleted = true; + $bookmark = $bs->getBookmarkByAddress($_REQUEST['url'], false); + $bId = $bookmark['bId']; + $deleted = $bs->deleteBookmark($bId); + $msg = 'done'; + if (!$deleted) { + //something really went wrong + header('HTTP/1.0 500 Internal Server Error'); + $msg = 'something really went wrong'; + } } // Set up the XML file and output the result. -echo '<?xml version="1.0" standalone="yes" ?'.">\r\n"; -echo '<result code="'. ($deleted ? 'done' : 'something went wrong') .'" />'; +echo '<?xml version="1.0" standalone="yes" ?' . ">\r\n"; +echo '<result code="' . $msg . '" />'; ?>
\ No newline at end of file diff --git a/www/api/posts_update.php b/www/api/posts_update.php index 4aeedc3..4b080e2 100644 --- a/www/api/posts_update.php +++ b/www/api/posts_update.php @@ -1,24 +1,44 @@ <?php -// Implements the del.icio.us API request for a user's last update time and date. - -// del.icio.us behavior: -// - doesn't set the Content-Type to text/xml (we do). +/** + * API for retrieving a user's last update time. + * That is the time the user changed a bookmark lastly. + * The delicious API is implemented here. + * + * Delicious also returns "the number of new items in + * the user's inbox since it was last visited." - we do + * that too, so we are as close at the API as possible, + * not breaking delicious clients. + * + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + * @link http://www.delicious.com/help/api + */ // Force HTTP authentication first! $httpContentType = 'text/xml'; require_once 'httpauth.inc.php'; -/* Service creation: only useful services are created */ -$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark'); - - -// Get the posts relevant to the passed-in variables. -$bookmarks =& $bookmarkservice->getBookmarks(0, 1, $userservice->getCurrentUserId()); +$bs = SemanticScuttle_Service_Factory::get('Bookmark'); +$bookmarks = $bs->getBookmarks(0, 1, $userservice->getCurrentUserId()); // Set up the XML file and output all the tags. -echo '<?xml version="1.0" standalone="yes" ?'.">\r\n"; -foreach($bookmarks['bookmarks'] as $row) { - echo '<update time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) .'" />'; +echo '<?xml version="1.0" standalone="yes" ?' . ">\r\n"; +//foreach is used in case there are no bookmarks +foreach ($bookmarks['bookmarks'] as $row) { + echo '<update time="' + . gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) + . '"' + . ' inboxnew="0"' + . ' />'; } ?>
\ No newline at end of file |