aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/ChangeLog15
-rw-r--r--doc/developers/api10
-rw-r--r--src/SemanticScuttle/Service/Bookmark.php18
-rw-r--r--tests/AllTests.php4
-rw-r--r--tests/Api/PostsAddTest.php435
-rw-r--r--tests/Api/PostsDeleteTest.php303
-rw-r--r--tests/Api/PostsUpdateTest.php135
-rw-r--r--tests/TestBaseApi.php21
-rw-r--r--www/api/httpauth.inc.php31
-rw-r--r--www/api/posts_add.php151
-rw-r--r--www/api/posts_delete.php62
-rw-r--r--www/api/posts_update.php46
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