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  | 
