From b25290cef48251a31cb0f693a53677b566c1b1a4 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Tue, 16 Feb 2010 21:38:59 +0100 Subject: [PATCH 001/173] add package task --- build.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.xml b/build.xml index 9cf4de9..1915115 100644 --- a/build.xml +++ b/build.xml @@ -48,6 +48,14 @@ + + + + + + From d80fcf8de6440fd31f16bb4106fe9e373fa40308 Mon Sep 17 00:00:00 2001 From: mensonge Date: Wed, 21 Jul 2010 18:44:54 +0000 Subject: [PATCH 002/173] Change "localhost" into 127.0.0.1 because it avoids a bug in Windows Seven. http://serverfault.com/questions/4689/windows-7-localhost-name-resolution-is-handled-within-dns-itself-why git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@716 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/config.default.php | 2 +- data/config.php.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/config.default.php b/data/config.default.php index 4f45eac..bd67c7b 100644 --- a/data/config.default.php +++ b/data/config.default.php @@ -138,7 +138,7 @@ $dbtype = 'mysql4'; * * @var string */ -$dbhost = 'localhost'; +$dbhost = '127.0.0.1'; /** * Database port. diff --git a/data/config.php.dist b/data/config.php.dist index 6439478..c135e8e 100644 --- a/data/config.php.dist +++ b/data/config.php.dist @@ -88,7 +88,7 @@ $dbname = 'scuttle'; * * @var string */ -$dbhost = 'localhost'; +$dbhost = '127.0.0.1'; /*************************************************** From 95887ff823ba0470a27f0e12a6561e5969819780 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 12 Aug 2010 21:08:50 +0200 Subject: [PATCH 003/173] update readme --- doc/developers/release-new-version | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/developers/release-new-version b/doc/developers/release-new-version index 69530df..4b2540a 100644 --- a/doc/developers/release-new-version +++ b/doc/developers/release-new-version @@ -4,8 +4,8 @@ How to release a new version of SemanticScuttle 0. Run unit tests and verify that all of them pass 1. Update doc/ChangeLog 2. Update doc/UPGRADE.txt -3. Update version in data/templates/about.tpl.php - and build.xml +3. Update version in data/templates/about.tpl.php, + build.xml and doc/README.txt 4. Create a release zip file via the build script: Just type "phing". 5. Make a test installation from your zip file with a fresh From 8798309bfe5619d52fd129ab3504e13563418ea4 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 12 Aug 2010 22:01:48 +0200 Subject: [PATCH 004/173] basic package.xml generation with native pearpkg2 task - does not really work since file roles are not supported --- build.xml | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/build.xml b/build.xml index 364613a..bda5641 100644 --- a/build.xml +++ b/build.xml @@ -10,6 +10,17 @@ + + @@ -52,6 +63,59 @@ description="Creates the pear package" > + + From d51768ab404c1313f6dec702550e825c80a42395 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 00:25:54 +0200 Subject: [PATCH 005/173] nearly there; using d51pearpkg2 now --- .gitignore | 1 + build.xml | 100 +++++++++++++++++++++++++++++------------------------ 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 3895c5b..888ac2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist/ build.properties +package.xml diff --git a/build.xml b/build.xml index bda5641..0654a2b 100644 --- a/build.xml +++ b/build.xml @@ -27,6 +27,8 @@ + + @@ -63,59 +65,65 @@ description="Creates the pear package" > - - From 4d9b6950cf280e718354eb5888ae41ef04b9afb8 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 00:57:31 +0200 Subject: [PATCH 006/173] on the way making package.xml better --- build.xml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/build.xml b/build.xml index 0654a2b..6134ad7 100644 --- a/build.xml +++ b/build.xml @@ -93,12 +93,14 @@ + www data doc php test + build* dist doc/developers @@ -122,6 +124,37 @@ --> + + + + + + + + + + + + + + + + + From eaa847a93d4b9d0c77d70ef219836b9f89bd67c7 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 01:04:03 +0200 Subject: [PATCH 007/173] package.xml now looks really good --- build.xml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/build.xml b/build.xml index 6134ad7..2e1e0e8 100644 --- a/build.xml +++ b/build.xml @@ -102,9 +102,9 @@ build* - dist + dist/** doc/developers - scripts + scripts/** **/messages.po @@ -147,9 +147,25 @@ /> + + + + From aba3327b5b8beffea75687a11ccf034f5e0d0969 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 01:07:29 +0200 Subject: [PATCH 008/173] time to test the package --- build.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/build.xml b/build.xml index 2e1e0e8..389dfd4 100644 --- a/build.xml +++ b/build.xml @@ -100,12 +100,19 @@ php test - + + **/.gitignore + **/.svn build* + data/config.php + data/locales/messages.po + data/locales/*/LC_MESSAGES/messages.po dist/** - doc/developers + doc/developers/** scripts/** - **/messages.po + src/php-gettext/examples/** + src/php-gettext/bin/** - Many SQL optimizations - SemanticScuttle shows bookmarks 4 times faster now From f93aa6c7aceeb7ed9ea899cfe232b3bd097b2422 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 01:26:30 +0200 Subject: [PATCH 009/173] make files use pear directories --- build.xml | 17 +++++++++++++++++ src/SemanticScuttle/header.php | 17 ++++++++++++++--- tests/prepare.php | 8 +++++++- www/www-header.php | 8 +++++++- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/build.xml b/build.xml index 389dfd4..accae3b 100644 --- a/build.xml +++ b/build.xml @@ -113,6 +113,23 @@ scripts/** src/php-gettext/examples/** src/php-gettext/bin/** + *.tgz + + + + - Many SQL optimizations - SemanticScuttle shows bookmarks 4 times faster now diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php index ef36e83..bc60088 100644 --- a/src/SemanticScuttle/header.php +++ b/src/SemanticScuttle/header.php @@ -14,9 +14,21 @@ * @license GPL http://www.gnu.org/licenses/gpl.html * @link http://sourceforge.net/projects/semanticscuttle */ -if (!file_exists(dirname(__FILE__) .'/../../data/config.php')) { + +if ('@data_dir@' == '@' . 'data_dir@') { + //non pear-install + $datadir = dirname(__FILE__) . '/../../data/'; +} else { + //pear installation; files are in include path + $datadir = '@data_dir@'; +} + +if (!file_exists($datadir . '/config.php')) { header('HTTP/1.0 500 Internal Server Error'); - die('Please copy "config.php.dist" to "config.php" in data/ folder.'); + die( + 'Please copy "config.php.dist" to "config.php" in data/ folder.' + . "\n" + ); } set_include_path( get_include_path() . PATH_SEPARATOR @@ -24,7 +36,6 @@ set_include_path( ); // 1 // First requirements part (before debug management) -$datadir = dirname(__FILE__) . '/../../data/'; require_once $datadir . '/config.default.php'; require_once $datadir . '/config.php'; diff --git a/tests/prepare.php b/tests/prepare.php index ce9cd1c..6afc284 100644 --- a/tests/prepare.php +++ b/tests/prepare.php @@ -19,7 +19,13 @@ $_SERVER['HTTP_HOST'] = 'http://localhost/'; define('UNIT_TEST_MODE', true); -require_once dirname(__FILE__) . '/../src/SemanticScuttle/header.php'; +if ('@data_dir@' == '@' . 'data_dir@') { + //non pear-install + require_once dirname(__FILE__) . '/../src/SemanticScuttle/header.php'; +} else { + //pear installation; files are in include path + require_once 'SemanticScuttle/header.php'; +} require_once dirname(__FILE__) . '/TestBase.php'; require_once dirname(__FILE__) . '/TestBaseApi.php'; diff --git a/www/www-header.php b/www/www-header.php index 0688b71..cc5a5ae 100644 --- a/www/www-header.php +++ b/www/www-header.php @@ -15,5 +15,11 @@ * @license GPL http://www.gnu.org/licenses/gpl.html * @link http://sourceforge.net/projects/semanticscuttle */ -require_once dirname(__FILE__) . '/../src/SemanticScuttle/header.php'; +if ('@data_dir@' == '@' . 'data_dir@') { + //non pear-install + require_once dirname(__FILE__) . '/../src/SemanticScuttle/header.php'; +} else { + //pear installation; files are in include path + require_once 'SemanticScuttle/header.php'; +} ?> \ No newline at end of file From 8fd5efb7c7fecf91ab850d2f5dfeced4b07e8562 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 01:32:14 +0200 Subject: [PATCH 010/173] make test running on installed pear package work: pear run-tests -pu __uri/semanticscuttle --- src/SemanticScuttle/header.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php index bc60088..d1a5c29 100644 --- a/src/SemanticScuttle/header.php +++ b/src/SemanticScuttle/header.php @@ -20,7 +20,7 @@ if ('@data_dir@' == '@' . 'data_dir@') { $datadir = dirname(__FILE__) . '/../../data/'; } else { //pear installation; files are in include path - $datadir = '@data_dir@'; + $datadir = '@data_dir@/SemanticScuttle/'; } if (!file_exists($datadir . '/config.php')) { From b243ecd53f82b3559980ce31d84a59554eb5b8f5 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 01:33:58 +0200 Subject: [PATCH 011/173] remove unneeded code --- tests/TagsCacheTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/TagsCacheTest.php b/tests/TagsCacheTest.php index 9097bcb..84f4ec0 100644 --- a/tests/TagsCacheTest.php +++ b/tests/TagsCacheTest.php @@ -54,9 +54,6 @@ class TagsCacheTest extends PHPUnit_Framework_TestCase protected function setUp() { - global $dbhost, $dbuser, $dbpass, $dbname, $dbport, $dbpersist, $dbtype, $tableprefix, $TEMPLATES_DIR, $debugMode; - require_once dirname(__FILE__) . '/../src/SemanticScuttle/header.php'; - $this->us =SemanticScuttle_Service_Factory::get('User'); $this->bs =SemanticScuttle_Service_Factory::get('Bookmark'); $this->bs->deleteAll(); From 25baf614b8b00bb39b9d35cf6ce5ce79f0954aeb Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 22 Aug 2010 01:46:20 +0200 Subject: [PATCH 012/173] package generation is fully working now --- build.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.xml b/build.xml index accae3b..1a979dd 100644 --- a/build.xml +++ b/build.xml @@ -22,6 +22,7 @@ - Fix bug #2976593: fr_FR locale is vietnamese " /> + @@ -195,13 +196,16 @@ - + + + + - From 19bcf4933f4478ab97290501fc22440a1242cb7d Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 22 Aug 2010 11:10:15 +0000 Subject: [PATCH 013/173] make pear channel update deployment work via phing! git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@720 b3834d28-1941-0410-a4f8-b48e95affb8f --- build.properties.dist | 1 + build.xml | 55 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/build.properties.dist b/build.properties.dist index b50ad08..887f888 100644 --- a/build.properties.dist +++ b/build.properties.dist @@ -1 +1,2 @@ sfuser=FIXME +websitedir=FIXME diff --git a/build.xml b/build.xml index 1a979dd..3da2c89 100644 --- a/build.xml +++ b/build.xml @@ -21,12 +21,13 @@ - Fix bug #2960663: do not send content-type headers twice for ajax/api scripts - Fix bug #2976593: fr_FR locale is vietnamese " /> - - - - - - + + + + + + + @@ -73,7 +74,7 @@ A social bookmarking tool experimenting with new features like structured tags or collaborative descriptions of tags. - __uri + semanticscuttle.sourceforge.net GPL @@ -237,6 +238,46 @@ + + + + + + + + + + + + + + From 71aaf514b138309d71101a8f6c727beb0dd7eb65 Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 22 Aug 2010 12:45:14 +0000 Subject: [PATCH 014/173] add sourceforge logo to pirum html file git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@721 b3834d28-1941-0410-a4f8-b48e95affb8f --- build.xml | 40 +++++++++++++++++++++++++++++++++------- html.properties | 1 + 2 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 html.properties diff --git a/build.xml b/build.xml index 3da2c89..1cd70e1 100644 --- a/build.xml +++ b/build.xml @@ -7,6 +7,7 @@ tagging a release, running unit tests etc. --> + @@ -239,7 +240,7 @@ - - + />--> + + + + + + + + + + + + + + + + - + />--> diff --git a/html.properties b/html.properties new file mode 100644 index 0000000..8e25cc4 --- /dev/null +++ b/html.properties @@ -0,0 +1 @@ +html.sflogo = Get SemanticScuttle at SourceForge.net. Fast, secure and Free Open Source software downloads From 603cb2e544caa0384166544c32a4403a5cd40d3a Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 22 Aug 2010 12:45:32 +0000 Subject: [PATCH 015/173] use our own index file instead of pirum generated index git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@722 b3834d28-1941-0410-a4f8-b48e95affb8f --- build.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 1cd70e1..007885b 100644 --- a/build.xml +++ b/build.xml @@ -285,7 +285,10 @@ - + + + + Date: Sun, 22 Aug 2010 12:45:47 +0000 Subject: [PATCH 016/173] re-enable rsync to/from sourceforge git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@723 b3834d28-1941-0410-a4f8-b48e95affb8f --- build.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build.xml b/build.xml index 007885b..42999ec 100644 --- a/build.xml +++ b/build.xml @@ -256,12 +256,12 @@ 2. update channel with pirum update 3. rsync to sourceforge --> - + /> - + /> From 9b9c592774d2552fa3cc76d400164b88a1ccaa5b Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 22 Aug 2010 12:46:13 +0000 Subject: [PATCH 017/173] add package dep git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@724 b3834d28-1941-0410-a4f8-b48e95affb8f --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 42999ec..74e5212 100644 --- a/build.xml +++ b/build.xml @@ -240,7 +240,7 @@ - Date: Sun, 22 Aug 2010 12:55:51 +0000 Subject: [PATCH 018/173] ignore property files git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@732 b3834d28-1941-0410-a4f8-b48e95affb8f --- build.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/build.xml b/build.xml index 74e5212..83341e0 100644 --- a/build.xml +++ b/build.xml @@ -117,6 +117,7 @@ src/php-gettext/examples/** src/php-gettext/bin/** *.tgz + *.properties Date: Tue, 14 Sep 2010 19:20:31 +0000 Subject: [PATCH 019/173] allow debugging of connection errors git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@734 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/db/mysqli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SemanticScuttle/db/mysqli.php b/src/SemanticScuttle/db/mysqli.php index 03b36ea..9a2709a 100644 --- a/src/SemanticScuttle/db/mysqli.php +++ b/src/SemanticScuttle/db/mysqli.php @@ -50,7 +50,7 @@ class sql_db } } - return $this->sql_error(''); + return $this->sql_error(mysqli_connect_error()); } // From 930cfd784a59d95d574f87f24dfcb5fc590b5e78 Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 14 Sep 2010 19:21:19 +0000 Subject: [PATCH 020/173] changelog git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@735 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 5e24f07..b1248fd 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -4,6 +4,7 @@ ChangeLog for SemantiScuttle 0.9X.X - 2010-XX-XX ------------------- - Fix bug getTagsForBookmarks() that fetched all tags +- Show error message on mysqli connection errors 0.97.0 - 2010-06-09 From 17a1595b312e5c9d0dac6d5b33685b457647cb8d Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:30:49 +0000 Subject: [PATCH 021/173] add more countother-watchlist tests that are simpler than the previous test git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@736 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/BookmarkTest.php | 88 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/tests/BookmarkTest.php b/tests/BookmarkTest.php index 40869b2..40b40e3 100644 --- a/tests/BookmarkTest.php +++ b/tests/BookmarkTest.php @@ -1085,6 +1085,92 @@ class BookmarkTest extends TestBase + /** + * Test what countOther() returns when the user is logged in + * and a friend (people on the watchlist) has bookmarked + * and the same address with public status. + * + * @return void + */ + public function testCountOthersWatchlistPublic() + { + $uid = $this->addUser(); + $address = 'http://example.org'; + + //create other user and add main user to his watchlist + $friendPublic1 = $this->addUser(); + $this->us->setCurrentUserId($friendPublic1); + $this->us->setWatchStatus($uid); + + //create bookmarks for main user and other one + $this->addBookmark($uid, $address, 0); + $this->addBookmark($friendPublic1, $address, 0);//0 is public + + //log main user in + $this->us->setCurrentUserId($uid); + + $this->assertEquals(1, $this->bs->countOthers($address)); + } + + + + /** + * Test what countOther() returns when the user is logged in + * and a friend (people on the watchlist) has bookmarked + * and shared the same address for the watchlist. + * + * @return void + */ + public function testCountOthersWatchlistShared() + { + $uid = $this->addUser(); + $address = 'http://example.org'; + + //create other user and add main user to his watchlist + $friendPublic1 = $this->addUser(); + $this->us->setCurrentUserId($friendPublic1); + $this->us->setWatchStatus($uid); + + //create bookmarks for main user and other one + $this->addBookmark($uid, $address, 0); + $this->addBookmark($friendPublic1, $address, 1);//1 is shared + + //log main user in + $this->us->setCurrentUserId($uid); + + $this->assertEquals(1, $this->bs->countOthers($address)); + } + + + + /** + * Test what countOther() returns when the user is logged in + * and one friends (people on the watchlist) has bookmarked + * the same address but made it private. + * + * @return void + */ + public function testCountOthersWatchlistPrivate() + { + $uid = $this->addUser(); + $address = 'http://example.org'; + + //create other user and add main user to his watchlist + $friendPublic1 = $this->addUser(); + $this->us->setCurrentUserId($friendPublic1); + $this->us->setWatchStatus($uid); + + //create bookmarks for main user and other one + $this->addBookmark($uid, $address, 0); + $this->addBookmark($friendPublic1, $address, 2);//2 is private + + //log main user in + $this->us->setCurrentUserId($uid); + + $this->assertEquals(0, $this->bs->countOthers($address)); + } + + /** * Test what countOther() returns when the user is logged in * and friends (people on the watchlist) have bookmarked @@ -1092,7 +1178,7 @@ class BookmarkTest extends TestBase * * @return void */ - public function testCountOthersWatchlist() + public function testCountOthersWatchlistComplex() { $uid = $this->addUser(); $address = 'http://example.org'; From b5eee3e761e61fba11d01217fe1b8cdc2eeda0ac Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:31:10 +0000 Subject: [PATCH 022/173] finally fix the problem that watchlist count tests failed every second alltests run git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@737 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/User.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php index 281c18c..6fc3bb7 100644 --- a/src/SemanticScuttle/Service/User.php +++ b/src/SemanticScuttle/Service/User.php @@ -668,7 +668,7 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService } /** - * Delete all bookmarks. + * Delete all users and their watch states. * Mainly used in unit tests. * * @return void @@ -677,6 +677,9 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService { $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`'; $this->db->sql_query($query); + + $query = 'TRUNCATE TABLE `' . $GLOBALS['tableprefix'] . 'watched' . '`'; + $this->db->sql_query($query); } /** From 9807c70fb92e554f4ee3db4f11fc4df3ab2c323d Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:31:28 +0000 Subject: [PATCH 023/173] update TODO git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@738 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/developers/TODO | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/developers/TODO b/doc/developers/TODO index 127de1d..57805c3 100644 --- a/doc/developers/TODO +++ b/doc/developers/TODO @@ -1,14 +1,14 @@ - SemanticScuttle_Filter -> class with static filter functions to filter ids, usernames, passwords, sorting etc. -- find out the reasoning behind define(POST_|GET_) - when a user gets deleted from database, he should not be logged in anymore (name not shown on top right) - Make users inactive by default when registered newly - have to be activated by admins - Add RDFa to user profile page -- use recaptcha or alike +- use recaptcha or alike -> quickform - tutorial about sidebar +- update php-gettext Tracker items: From c7c94fec725444b88f76dab40226634a6ed09f15 Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:31:51 +0000 Subject: [PATCH 024/173] update main translation file from source code git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@739 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/locales/messages.po | 134 +++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/data/locales/messages.po b/data/locales/messages.po index 1a142a0..2f35f64 100644 --- a/data/locales/messages.po +++ b/data/locales/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-11-16 20:55+0100\n" +"POT-Creation-Date: 2010-09-15 19:15+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,50 +17,50 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: src/SemanticScuttle/functions.php:163 +#: src/SemanticScuttle/functions.php:189 msgid "message_die() was called multiple times." msgstr "" -#: src/SemanticScuttle/functions.php:175 +#: src/SemanticScuttle/functions.php:201 msgid "SQL Error" msgstr "" -#: src/SemanticScuttle/functions.php:181 +#: src/SemanticScuttle/functions.php:207 msgid "Line" msgstr "" -#: src/SemanticScuttle/functions.php:181 +#: src/SemanticScuttle/functions.php:207 #: data/templates/importDelicious.tpl.php:8 #: data/templates/importNetscape.tpl.php:9 #: data/templates/importStructure.tpl.php:10 msgid "File" msgstr "" -#: src/SemanticScuttle/functions.php:189 +#: src/SemanticScuttle/functions.php:215 msgid "Information" msgstr "" -#: src/SemanticScuttle/functions.php:194 +#: src/SemanticScuttle/functions.php:220 msgid "Critical Information" msgstr "" -#: src/SemanticScuttle/functions.php:199 +#: src/SemanticScuttle/functions.php:225 msgid "An error occured" msgstr "" -#: src/SemanticScuttle/functions.php:202 +#: src/SemanticScuttle/functions.php:228 msgid "General Error" msgstr "" -#: src/SemanticScuttle/functions.php:210 +#: src/SemanticScuttle/functions.php:236 msgid "An critical error occured" msgstr "" -#: src/SemanticScuttle/functions.php:213 +#: src/SemanticScuttle/functions.php:239 msgid "Critical Error" msgstr "" -#: src/SemanticScuttle/functions.php:222 +#: src/SemanticScuttle/functions.php:248 msgid "DEBUG MODE" msgstr "" @@ -150,7 +150,7 @@ msgstr "" msgid "Are you sure?" msgstr "" -#: data/templates/admin.tpl.php:19 data/templates/bookmarks.tpl.php:251 +#: data/templates/admin.tpl.php:19 data/templates/bookmarks.tpl.php:267 msgid "Delete" msgstr "" @@ -169,12 +169,12 @@ msgid "" msgstr "" #: data/templates/bookmarkcommondescriptionedit.tpl.php:18 -#: data/templates/bookmarks.tpl.php:137 data/templates/editbookmark.tpl.php:38 +#: data/templates/bookmarks.tpl.php:137 data/templates/editbookmark.tpl.php:43 msgid "Title" msgstr "" #: data/templates/bookmarkcommondescriptionedit.tpl.php:23 -#: data/templates/editbookmark.tpl.php:44 +#: data/templates/editbookmark.tpl.php:49 #: data/templates/editprofile.tpl.php:47 data/templates/profile.tpl.php:33 #: data/templates/tagcommondescriptionedit.tpl.php:13 #: data/templates/tagedit.tpl.php:12 @@ -193,8 +193,8 @@ msgid "Update" msgstr "" #: data/templates/bookmarkcommondescriptionedit.tpl.php:43 -#: data/templates/editbookmark.tpl.php:98 data/templates/tag2tagadd.tpl.php:24 -#: data/templates/tag2tagedit.tpl.php:38 +#: data/templates/editbookmark.tpl.php:103 +#: data/templates/tag2tagadd.tpl.php:24 data/templates/tag2tagedit.tpl.php:38 #: data/templates/tagcommondescriptionedit.tpl.php:33 #: data/templates/tagedit.tpl.php:19 data/templates/tagrename.tpl.php:25 msgid "Cancel" @@ -273,61 +273,61 @@ msgstr "" msgid "Page %d of %d" msgstr "" -#: data/templates/bookmarks.tpl.php:245 +#: data/templates/bookmarks.tpl.php:261 msgid "Tags:" msgstr "" -#: data/templates/bookmarks.tpl.php:251 +#: data/templates/bookmarks.tpl.php:267 msgid "Edit" msgstr "" -#: data/templates/bookmarks.tpl.php:255 +#: data/templates/bookmarks.tpl.php:271 msgid "Last update" msgstr "" -#: data/templates/bookmarks.tpl.php:258 +#: data/templates/bookmarks.tpl.php:274 msgid "by" msgstr "" -#: data/templates/bookmarks.tpl.php:260 +#: data/templates/bookmarks.tpl.php:276 msgid "you" msgstr "" -#: data/templates/bookmarks.tpl.php:274 +#: data/templates/bookmarks.tpl.php:290 #, php-format msgid " and %s1 other%s" msgstr "" -#: data/templates/bookmarks.tpl.php:277 +#: data/templates/bookmarks.tpl.php:293 #, php-format msgid " and %2$s%1$s others%3$s" msgstr "" -#: data/templates/bookmarks.tpl.php:288 +#: data/templates/bookmarks.tpl.php:304 msgid "Copy this bookmark to YOUR bookmarks." msgstr "" -#: data/templates/bookmarks.tpl.php:289 +#: data/templates/bookmarks.tpl.php:305 msgid "Copy" msgstr "" -#: data/templates/bookmarks.tpl.php:309 +#: data/templates/bookmarks.tpl.php:325 msgid "This bookmark is certified by an admin user." msgstr "" -#: data/templates/bookmarks.tpl.php:351 +#: data/templates/bookmarks.tpl.php:371 msgid "Private Note on this bookmark" msgstr "" -#: data/templates/bookmarks.tpl.php:363 +#: data/templates/bookmarks.tpl.php:383 msgid "Come back to the top of this page." msgstr "" -#: data/templates/bookmarks.tpl.php:363 +#: data/templates/bookmarks.tpl.php:383 msgid "Top of the page" msgstr "" -#: data/templates/bookmarks.tpl.php:369 +#: data/templates/bookmarks.tpl.php:389 msgid "No bookmarks available" msgstr "" @@ -362,133 +362,133 @@ msgstr "" msgid "Popular Tags From All Users" msgstr "" -#: data/templates/editbookmark.tpl.php:33 +#: data/templates/editbookmark.tpl.php:38 msgid "Address" msgstr "" -#: data/templates/editbookmark.tpl.php:35 #: data/templates/editbookmark.tpl.php:40 +#: data/templates/editbookmark.tpl.php:45 #: data/templates/editprofile.tpl.php:31 data/templates/tagrename.tpl.php:14 #: data/templates/tagrename.tpl.php:19 msgid "Required" msgstr "" -#: data/templates/editbookmark.tpl.php:45 +#: data/templates/editbookmark.tpl.php:50 msgid "Add Note" msgstr "" -#: data/templates/editbookmark.tpl.php:48 +#: data/templates/editbookmark.tpl.php:53 msgid "" "You can use anchors to delimite attributes. for example: [publisher]blah[/" "publisher] " msgstr "" -#: data/templates/editbookmark.tpl.php:51 +#: data/templates/editbookmark.tpl.php:56 msgid "Suggested anchors: " msgstr "" -#: data/templates/editbookmark.tpl.php:63 +#: data/templates/editbookmark.tpl.php:68 msgid "Private Note" msgstr "" -#: data/templates/editbookmark.tpl.php:65 +#: data/templates/editbookmark.tpl.php:70 msgid "Just visible by you and your contacts." msgstr "" -#: data/templates/editbookmark.tpl.php:69 data/templates/toolbar.inc.php:10 +#: data/templates/editbookmark.tpl.php:74 data/templates/toolbar.inc.php:10 #: www/tags.php:45 www/tags.php:67 msgid "Tags" msgstr "" -#: data/templates/editbookmark.tpl.php:73 +#: data/templates/editbookmark.tpl.php:78 msgid "Comma-separated" msgstr "" -#: data/templates/editbookmark.tpl.php:77 data/templates/tag2tagadd.tpl.php:9 +#: data/templates/editbookmark.tpl.php:82 data/templates/tag2tagadd.tpl.php:9 msgid "" "Note: use \">\" to include one tag in another. e.g.: europe>france>paris" msgstr "" -#: data/templates/editbookmark.tpl.php:81 data/templates/tag2tagadd.tpl.php:8 +#: data/templates/editbookmark.tpl.php:86 data/templates/tag2tagadd.tpl.php:8 msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry" msgstr "" -#: data/templates/editbookmark.tpl.php:84 +#: data/templates/editbookmark.tpl.php:89 #: data/templates/importDelicious.tpl.php:15 #: data/templates/importNetscape.tpl.php:16 msgid "Privacy" msgstr "" -#: data/templates/editbookmark.tpl.php:87 +#: data/templates/editbookmark.tpl.php:92 #: data/templates/importDelicious.tpl.php:18 #: data/templates/importNetscape.tpl.php:19 msgid "Public" msgstr "" -#: data/templates/editbookmark.tpl.php:88 +#: data/templates/editbookmark.tpl.php:93 msgid "Shared with Watch List" msgstr "" -#: data/templates/editbookmark.tpl.php:89 +#: data/templates/editbookmark.tpl.php:94 #: data/templates/importDelicious.tpl.php:20 #: data/templates/importNetscape.tpl.php:21 msgid "Private" msgstr "" -#: data/templates/editbookmark.tpl.php:102 +#: data/templates/editbookmark.tpl.php:107 msgid "Delete Bookmark" msgstr "" -#: data/templates/editbookmark.tpl.php:107 +#: data/templates/editbookmark.tpl.php:112 msgid "edit common description" msgstr "" -#: data/templates/editbookmark.tpl.php:134 +#: data/templates/editbookmark.tpl.php:139 msgid "Bookmarklet" msgstr "" -#: data/templates/editbookmark.tpl.php:140 +#: data/templates/editbookmark.tpl.php:145 #, php-format msgid "" "Click one of the following bookmarklets to add a button you can click " "whenever you want to add the page you are on to %s" msgstr "" -#: data/templates/editbookmark.tpl.php:144 +#: data/templates/editbookmark.tpl.php:149 #, php-format msgid "" "Drag one of the following bookmarklets to your browser's bookmarks and click " "it whenever you want to add the page you are on to %s" msgstr "" -#: data/templates/editbookmark.tpl.php:157 #: data/templates/editbookmark.tpl.php:162 +#: data/templates/editbookmark.tpl.php:167 #, php-format msgid "Post to %s" msgstr "" -#: data/templates/editbookmark.tpl.php:158 #: data/templates/editbookmark.tpl.php:163 +#: data/templates/editbookmark.tpl.php:168 #, php-format msgid "Post to %s (Pop-up)" msgstr "" -#: data/templates/editbookmark.tpl.php:168 +#: data/templates/editbookmark.tpl.php:173 #: data/templates/importDelicious.tpl.php:26 #: data/templates/importNetscape.tpl.php:27 #: data/templates/importStructure.tpl.php:16 msgid "Import" msgstr "" -#: data/templates/editbookmark.tpl.php:170 +#: data/templates/editbookmark.tpl.php:175 msgid "Import bookmarks from bookmark file" msgstr "" -#: data/templates/editbookmark.tpl.php:170 +#: data/templates/editbookmark.tpl.php:175 msgid "Internet Explorer, Mozilla Firefox and Netscape" msgstr "" -#: data/templates/editbookmark.tpl.php:171 +#: data/templates/editbookmark.tpl.php:176 msgid "Import bookmarks from del.icio.us" msgstr "" @@ -841,7 +841,7 @@ msgid "Edit Tag Description" msgstr "" #: data/templates/sidebar.block.tagactions.php:26 -#: www/tagcommondescriptionedit.php:64 +#: www/tagcommondescriptionedit.php:76 msgid "Edit Tag Common Description" msgstr "" @@ -1028,12 +1028,12 @@ msgstr "" msgid "Failed to delete bookmark" msgstr "" -#: www/alltags.php:49 +#: www/alltags.php:50 msgid "All Tags" msgstr "" -#: www/alltags.php:55 www/bookmarks.php:96 www/populartags.php:52 -#: www/profile.php:51 www/rss.php:67 www/search.php:109 www/watch.php:45 +#: www/alltags.php:56 www/bookmarks.php:96 www/populartags.php:52 +#: www/profile.php:51 www/rss.php:76 www/search.php:109 www/watch.php:45 #: www/watchlist.php:61 #, php-format msgid "User with username %s was not found" @@ -1041,7 +1041,7 @@ msgstr "" #: www/bookmarkcommondescriptionedit.php:51 www/tag2tagadd.php:37 #: www/tag2tagdelete.php:41 www/tag2tagedit.php:33 -#: www/tagcommondescriptionedit.php:43 www/tagedit.php:43 +#: www/tagcommondescriptionedit.php:51 www/tagedit.php:43 msgid "Permission denied." msgstr "" @@ -1179,11 +1179,11 @@ msgstr "" msgid "%s: Recent bookmarks" msgstr "" -#: www/index.php:78 +#: www/index.php:75 msgid "Store, share and tag your favourite links" msgstr "" -#: www/index.php:79 +#: www/index.php:76 msgid "All Bookmarks" msgstr "" @@ -1313,7 +1313,7 @@ msgstr "" msgid "Registration failed. Please try again." msgstr "" -#: www/rss.php:84 +#: www/rss.php:93 #, php-format msgid "Recent bookmarks posted to %s" msgstr "" @@ -1358,11 +1358,11 @@ msgstr "" msgid "Edit Link Between Tags" msgstr "" -#: www/tagcommondescriptionedit.php:55 +#: www/tagcommondescriptionedit.php:62 msgid "Tag common description updated" msgstr "" -#: www/tagcommondescriptionedit.php:58 +#: www/tagcommondescriptionedit.php:67 msgid "Failed to update the tag common description" msgstr "" From 43c7481da239093ee9362ea4cd9778f8d1bf25e0 Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:32:09 +0000 Subject: [PATCH 025/173] fix typo git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@740 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/developers/translation | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/developers/translation b/doc/developers/translation index 401089a..776b5d7 100644 --- a/doc/developers/translation +++ b/doc/developers/translation @@ -21,7 +21,7 @@ We keep one base translation file, data/locales/messages.po. This file is auto-generated via xgettext from all our php source files. In case you added a new string to the code that needs translation, update the base translation file by running -> php scripts/update-translation-base.php. +> php scripts/update-translation-base.php After that has been done, the changes to the base messages.po file need to be merged into the single language translation files, From 4b79fdc6a6a66eb3af98de86d60153067243c3d5 Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:32:27 +0000 Subject: [PATCH 026/173] update database optimization todo git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@741 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/developers/TODO | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/developers/TODO b/doc/developers/TODO index 57805c3..59dd655 100644 --- a/doc/developers/TODO +++ b/doc/developers/TODO @@ -9,6 +9,9 @@ - use recaptcha or alike -> quickform - tutorial about sidebar - update php-gettext +- index on bookmarks->modified, since created is not used in selects/sort + - how to optimize sorts, to prevent mysql filesort? -> index enough? + - how to optimize DISTINCT bHash Tracker items: From 4faa30a7e0232225660bef2f67a2ad7e7dee8686 Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:32:54 +0000 Subject: [PATCH 027/173] update french translations git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@742 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/locales/fr_CA/LC_MESSAGES/messages.po | 1927 ++++++++++++-------- data/locales/fr_FR/LC_MESSAGES/messages.po | 596 +++--- 2 files changed, 1488 insertions(+), 1035 deletions(-) diff --git a/data/locales/fr_CA/LC_MESSAGES/messages.po b/data/locales/fr_CA/LC_MESSAGES/messages.po index 4bdb4b9..41894e8 100644 --- a/data/locales/fr_CA/LC_MESSAGES/messages.po +++ b/data/locales/fr_CA/LC_MESSAGES/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Scuttle\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-04-24 19:03+0200\n" +"POT-Creation-Date: 2010-09-15 19:15+0200\n" "PO-Revision-Date: 2008-04-24 19:07+0100\n" "Last-Translator: BenjaminHKB \n" "Language-Team: fr-FR \n" @@ -19,1151 +19,1528 @@ msgstr "" "X-Poedit-Country: FRANCE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: ../../../about.php:25 -#: ../../../templates/toolbar.inc.php:16 -#: ../../../templates/toolbar.inc.php:25 -msgid "About" -msgstr "À propos" - -#: ../../../ajaxDelete.php:30 -msgid "You are not allowed to delete this bookmark" -msgstr "Vous n'êtes pas autorisés à supprimer ce signet" - -#: ../../../ajaxDelete.php:34 -#: ../../../edit.php:78 -msgid "Failed to delete bookmark" -msgstr "Erreur dans la suppression du signet" - -#: ../../../alltags.php:48 -msgid "All Tags" -msgstr "Tous les mots-clés" - -#: ../../../alltags.php:57 -#: ../../../bookmarks.php:72 -#: ../../../populartags.php:58 -#: ../../../profile.php:44 -#: ../../../rss.php:62 -#: ../../../search.php:88 -#: ../../../watch.php:34 -#: ../../../watchlist.php:61 -#, php-format -msgid "User with username %s was not found" -msgstr "L'utilisateur %s n'a pas été trouvé." - -#: ../../../bookmarkcommondescriptionedit.php:35 -#: ../../../tag2tagadd.php:31 -#: ../../../tag2tagdelete.php:31 -#: ../../../tag2tagedit.php:31 -#: ../../../tagcommondescriptionedit.php:35 -#: ../../../tagedit.php:34 -msgid "Permission denied." -msgstr "Permission non accordée." - -#: ../../../bookmarkcommondescriptionedit.php:45 -msgid "Bookmark common description updated" -msgstr "Description commune du signet mise à jour." - -#: ../../../bookmarkcommondescriptionedit.php:48 -msgid "Failed to update the bookmark common description" -msgstr "Erreur dans la mise à jour de la description du signet" - -#: ../../../bookmarkcommondescriptionedit.php:57 -msgid "Edit Bookmark Common Description" -msgstr "Editer la description commune du signet" - -#: ../../../bookmarks.php:95 -#: ../../../edit.php:44 -msgid "Your bookmark must have a title and an address" -msgstr "Votre signet doit avoir un titre et une adresse." - -#: ../../../bookmarks.php:115 -#: ../../../edit.php:58 -msgid "Bookmark saved" -msgstr "Signet enregistré." - -#: ../../../bookmarks.php:123 -#: ../../../import.php:99 -#: ../../../importNetscape.php:74 -msgid "There was an error saving your bookmark. Please try again or contact the administrator." -msgstr "Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou contacter votre administrateur." - -#: ../../../bookmarks.php:161 -#: ../../../templates/toolbar.inc.php:14 -msgid "Add a Bookmark" -msgstr "Ajouter un signet" - -#: ../../../bookmarks.php:164 -msgid "Add Bookmark" -msgstr "Ajouter signet" - -#: ../../../bookmarks.php:167 -msgid "You must be logged in before you can add bookmarks." -msgstr "Vous devez être authentifié avant de pouvoir ajouter des signets." - -#: ../../../bookmarks.php:218 -msgid "My Bookmarks" -msgstr "Mes signets" - -#: ../../../edit.php:29 -msgid "Edit Bookmark" -msgstr "Editer le signet" - -#: ../../../edit.php:34 -#, php-format -msgid "Bookmark with id %s not was not found" -msgstr "Signet %s non trouvé" - -#: ../../../edit.php:39 -msgid "You are not allowed to edit this bookmark" -msgstr "Vous n'êtes pas autorisé à éditer ce signet." - -#: ../../../edit.php:55 -msgid "Error while saving your bookmark" -msgstr "Erreur pendant l'enregistrement de votre signet." - -#: ../../../edit.php:88 -#: ../../../templates/editprofile.tpl.php:51 -msgid "Save Changes" -msgstr "Enregistrer les modifications" - -#: ../../../functions.inc.php:108 +#: src/SemanticScuttle/functions.php:189 msgid "message_die() was called multiple times." msgstr "message_die() was called multiple times. ?" -#: ../../../functions.inc.php:120 +#: src/SemanticScuttle/functions.php:201 msgid "SQL Error" msgstr "Erreur SQL" -#: ../../../functions.inc.php:126 +#: src/SemanticScuttle/functions.php:207 msgid "Line" msgstr "Ligne" -#: ../../../functions.inc.php:126 -#: ../../../templates/importDelicious.tpl.php:8 -#: ../../../templates/importNetscape.tpl.php:9 +#: src/SemanticScuttle/functions.php:207 +#: data/templates/importDelicious.tpl.php:8 +#: data/templates/importNetscape.tpl.php:9 +#: data/templates/importStructure.tpl.php:10 msgid "File" msgstr "Fichier" -#: ../../../functions.inc.php:132 +#: src/SemanticScuttle/functions.php:215 msgid "Information" msgstr "Information" -#: ../../../functions.inc.php:137 +#: src/SemanticScuttle/functions.php:220 msgid "Critical Information" msgstr "Information critique." -#: ../../../functions.inc.php:142 +#: src/SemanticScuttle/functions.php:225 msgid "An error occured" msgstr "Une erreur s'est produite." -#: ../../../functions.inc.php:145 +#: src/SemanticScuttle/functions.php:228 msgid "General Error" msgstr "Erreur générale." -#: ../../../functions.inc.php:153 +#: src/SemanticScuttle/functions.php:236 msgid "An critical error occured" msgstr "Une erreur critique s'est produite." -#: ../../../functions.inc.php:156 +#: src/SemanticScuttle/functions.php:239 msgid "Critical Error" msgstr "Erreur critique." -#: ../../../functions.inc.php:165 +#: src/SemanticScuttle/functions.php:248 msgid "DEBUG MODE" msgstr "Mode de débogage." -#: ../../../history.php:65 -msgid "History" -msgstr "Historique" - -#: ../../../history.php:66 -#, php-format -msgid "History for %s" -msgstr "Historique de %s" - -#: ../../../history.php:82 -msgid "Address was not found" -msgstr "L'adresse n'a pas été trouvée." - -#: ../../../import.php:41 -msgid "Could not open XML input" -msgstr "Impossible d'ouvrir le flux XML." - -#: ../../../import.php:45 -#, php-format -msgid "XML error: %s at line %d" -msgstr "Erreur XML: %s à la ligne %d" - -#: ../../../import.php:54 -msgid "Import Bookmarks from del.icio.us" -msgstr "Importer les signet depuis del.icio.us" - -#: ../../../import.php:86 -#: ../../../importNetscape.php:64 -msgid "You have already submitted this bookmark." -msgstr "Vous avez déjà enregistré ce signet." - -#: ../../../import.php:97 -#: ../../../importNetscape.php:72 -msgid "Bookmark imported." -msgstr "Signet importé." - -#: ../../../importNetscape.php:81 -msgid "Import Bookmarks from Browser File" -msgstr "Importer les signets depuis un fichier" - -#: ../../../index.php:32 -msgid "You have now logged out" -msgstr "Vous êtes maintenant déconnecté." - -#: ../../../index.php:39 -#, php-format -msgid "%s: Recent bookmarks" -msgstr "%s: Signets récents" - -#: ../../../index.php:73 -msgid "Store, share and tag your favourite links" -msgstr "Conservez, partagez et libellez vos liens favoris" - -#: ../../../index.php:74 -msgid "All Bookmarks" -msgstr "Tous les signets" - -#: ../../../jsScuttle.php:22 -#: ../../../templates/tag2tagadd.tpl.php:21 -#: ../../../templates/tag2tagdelete.tpl.php:13 -#: ../../../templates/tag2tagedit.tpl.php:14 -#: ../../../templates/tag2tagedit.tpl.php:35 -#: ../../../templates/tagdelete.tpl.php:6 -msgid "Are you sure?" -msgstr "Etes-vous sûr ?" - -#: ../../../jsScuttle.php:22 -#: ../../../templates/tag2tagdelete.tpl.php:15 -#: ../../../templates/tag2tagedit.tpl.php:16 -#: ../../../templates/tagdelete.tpl.php:8 -msgid "Yes" -msgstr "Oui" - -#: ../../../jsScuttle.php:22 -#: ../../../templates/tag2tagdelete.tpl.php:16 -#: ../../../templates/tag2tagedit.tpl.php:17 -#: ../../../templates/tagdelete.tpl.php:9 -msgid "No" -msgstr "Non" - -#: ../../../jsScuttle.php:68 -msgid "Available" -msgstr "Disponible" - -#: ../../../jsScuttle.php:71 -msgid "Not Available" -msgstr "Non Disponible" - -#: ../../../login.php:38 -msgid "The details you have entered are incorrect. Please try again." -msgstr "Les informations que vous avez entrées sont incorrectes. Veuillez recommencer." - -#: ../../../login.php:48 -#: ../../../templates/login.tpl.php:26 -#: ../../../templates/toolbar.inc.php:26 -msgid "Log In" -msgstr "Se connecter" - -#: ../../../password.php:31 -msgid "You must enter your username." -msgstr "Vous devez entrer votre nom d'utilisateur." - -#: ../../../password.php:35 -msgid "You must enter your e-mail address." -msgstr "Vous devez saisir une E-mail." - -#: ../../../password.php:42 -msgid "No matches found for that username." -msgstr "Rien de trouvé pour ce nom d'utilisateur." - -#: ../../../password.php:45 -#, fuzzy -msgid "No matches found for that combination of username and e-mail address." -msgstr "Nous n'avons rien trouvé pour cette combinaison de nom d'utilisateur et d'e-mail." - -#: ../../../password.php:53 -msgid "There was an error while generating your new password. Please try again." -msgstr "Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou contacter votre administrateur." - -#: ../../../password.php:57 -msgid "Your new password is:" -msgstr "Votre nouveau mot de passe est:" - -#: ../../../password.php:57 -msgid "To keep your bookmarks secure, you should change this password in your profile the next time you log in." -msgstr "Pour garder vos signets sûrs, vous devriez changer ce mot de passe dans votre profil lors de votre prochaine authentification." - -#: ../../../password.php:60 -#, php-format -msgid "%s Account Information" -msgstr "Informations du compte de %s" - -#: ../../../password.php:62 -#, php-format -msgid "New password generated and sent to %s" -msgstr "Nouveau mot de passe généré et envoyé à l'adresse %s" - -#: ../../../password.php:69 -msgid "Forgotten Password" -msgstr "Mot de passe oublié" - -#: ../../../populartags.php:49 -#: ../../../templates/dynamictags.inc.php:101 -#: ../../../templates/sidebar.block.common.php:9 -#: ../../../templates/sidebar.block.menu.php:68 -#: ../../../templates/sidebar.block.popular.php:15 -#: ../../../templates/sidebar.block.recent.php:30 -#: ../../../templates/toolbar.inc.php:24 -msgid "Popular Tags" -msgstr "Mots-clés populaires" - -#: ../../../profile.php:52 -#: ../../../watchlist.php:116 -msgid "Username was not specified" -msgstr "Le nom d'utilisateur n'a pas été spécifié." - -#: ../../../profile.php:58 -msgid "My Profile" -msgstr "Mon Profil" - -#: ../../../profile.php:60 -#: ../../../templates/toolbar.inc.php:13 -msgid "Profile" -msgstr "Profil" - -#: ../../../profile.php:78 -msgid "Password and confirmation do not match." -msgstr "Le mot de passe et sa vérification ne correspondent pas." - -#: ../../../profile.php:82 -msgid "Password must be at least 6 characters long." -msgstr "Le mot de passe doit avoir au moins 6 caractères." - -#: ../../../profile.php:86 -msgid "E-mail address is not valid." -msgstr "Adresse de courrier électronique invalide." - -#: ../../../profile.php:90 -msgid "An error occurred while saving your changes." -msgstr "Une erreur s'est produite pendant l'enregistrement de vos modifications." - -#: ../../../profile.php:92 -msgid "Changes saved." -msgstr "Modifications enregistrées." - -#: ../../../register.php:33 -msgid "You must enter a username, password and e-mail address." -msgstr "Vous devez saisir un nom d'utilisateur, un mot de passe, un nom et un e-mail" - -#: ../../../register.php:37 -msgid "This username has been reserved, please make another choice." -msgstr "Ce nom d'utilisateur existe déjà, veuillez en choisir un autre." - -#: ../../../register.php:41 -msgid "This username already exists, please make another choice." -msgstr "Ce nom d'utilisateur existe déjà, veuillez en choisir un autre." - -#: ../../../register.php:45 -msgid "E-mail address is not valid. Please try again." -msgstr "Adresse de courrier électronique invalide. Veuilez réessayer." - -#: ../../../register.php:49 -msgid "Antispam answer is not valid. Please try again." -msgstr "La réponse antispam n'est pas valide. Veuillez réessayer." - -#: ../../../register.php:58 -msgid "You have successfully registered. Enjoy!" -msgstr "Votre inscription a bien été prise en compte !" - -#: ../../../register.php:60 -msgid "Registration failed. Please try again." -msgstr "Enregistrement raté. Veuillez rééssayer." - -#: ../../../register.php:66 -#: ../../../templates/register.tpl.php:41 -#: ../../../templates/toolbar.inc.php:27 -msgid "Register" -msgstr "S'enregistrer" - -#: ../../../rss.php:79 -#, php-format -msgid "Recent bookmarks posted to %s" -msgstr "Signets ajoutés récemment à %s" - -#: ../../../search.inc.php:13 -#: ../../../search.inc.php:41 -msgid "Search" -msgstr "Chercher dans" - -#: ../../../search.inc.php:19 -msgid "this user's bookmarks" -msgstr "les signets de cet utilisateur" - -#: ../../../search.inc.php:24 -msgid "my bookmarks" -msgstr "mes signets" - -#: ../../../search.inc.php:25 -msgid "my watchlist" -msgstr "ma liste des consultés" - -#: ../../../search.inc.php:29 -msgid "all bookmarks" -msgstr "tous les signets" - -#: ../../../search.inc.php:32 -msgid "for" -msgstr "pour" - -#: ../../../search.php:59 -#: ../../../search.php:108 -msgid "Search Bookmarks" -msgstr "Recherche de signets" - -#: ../../../search.php:65 -msgid "Search Results" -msgstr "Résultats de recherche" - -#: ../../../tag2tagadd.php:44 -msgid "Tag link created" -msgstr "Lien entre mot-clé créé." - -#: ../../../tag2tagadd.php:47 -msgid "Failed to create the link" -msgstr "Impossible de créer le lien" - -#: ../../../tag2tagadd.php:58 -msgid "Add Tag Link" -msgstr "Ajout d'un lien entre mots-clés" - -#: ../../../tag2tagdelete.php:43 -#: ../../../tag2tagedit.php:43 -msgid "Tag link deleted" -msgstr "Effacement d'un lien entre mots-clés" - -#: ../../../tag2tagdelete.php:46 -#: ../../../tag2tagedit.php:46 -msgid "Failed to delete the link" -msgstr "Impossible d'effacer le lien" - -#: ../../../tag2tagdelete.php:58 -msgid "Delete Link Between Tags" -msgstr "Effacer un lien entre mots-clés" - -#: ../../../tag2tagedit.php:58 -msgid "Edit Link Between Tags" -msgstr "Editer un lien entre mots-clés" - -#: ../../../tagcommondescriptionedit.php:45 -msgid "Tag common description updated" -msgstr "Editer la description commune du mot-clé" - -#: ../../../tagcommondescriptionedit.php:48 -msgid "Failed to update the tag common description" -msgstr "Impossible de mettre à jour la description commune du mot-clé" - -#: ../../../tagcommondescriptionedit.php:55 -#: ../../../templates/sidebar.block.tagactions.php:27 -msgid "Edit Tag Common Description" -msgstr "Editer la description commune du mot-clé" - -#: ../../../tagdelete.php:33 -msgid "Tag deleted" -msgstr "Mot-clé effacé" - -#: ../../../tagdelete.php:36 -msgid "Failed to delete the tag" -msgstr "Impossible d'effacer le mot-clé" - -#: ../../../tagdelete.php:44 -#: ../../../templates/sidebar.block.tagactions.php:23 -msgid "Delete Tag" -msgstr "Supprimer le mot-clé" - -#: ../../../tagedit.php:44 -msgid "Tag description updated" -msgstr "Description du mot-clé mise à jour" - -#: ../../../tagedit.php:47 -msgid "Failed to update the tag description" -msgstr "Impossible de mettre à jour la description du mot-clé" - -#: ../../../tagedit.php:54 -#: ../../../templates/sidebar.block.tagactions.php:25 -msgid "Edit Tag Description" -msgstr "Editer la description du mot-clé" - -#: ../../../tagrename.php:50 -msgid "Tag renamed" -msgstr "Mot-clé renommé" - -#: ../../../tagrename.php:54 -msgid "Failed to rename the tag" -msgstr "Erreur dans le renommage du mot-clé" - -#: ../../../tagrename.php:61 -#: ../../../templates/sidebar.block.tagactions.php:10 -msgid "Rename Tag" -msgid_plural "Rename Tags" -msgstr[0] "Renommer le mot-clé" -msgstr[1] "Renommer les mots-clés" - -#: ../../../tags.php:38 -#: ../../../templates/editbookmark.tpl.php:44 -#: ../../../templates/toolbar.inc.php:11 -msgid "Tags" -msgstr "Mots-clés" - -#: ../../../users.php:33 -msgid "Users" -msgstr "Utilisateurs" - -#: ../../../watch.php:46 -msgid "User removed from your watchlist" -msgstr "Utilisateur enlevé de votre liste des consultés" - -#: ../../../watch.php:48 -msgid "User added to your watchlist" -msgstr "Utilisateur ajouté à la liste des consultés." - -#: ../../../watchlist.php:103 -#, fuzzy -msgid "My Watchlist" -msgstr "Liste des signets vus" - -#: ../../../watchlist.php:105 -#: ../../../templates/toolbar.inc.php:12 -#, fuzzy -msgid "Watchlist" -msgstr "Liste des signets vus" - -#: ../../../templates/about.tpl.php:6 -msgid "Store all your favourite links in one place, accessible from anywhere." -msgstr "Conservez tous vos signets au même endroit, accessibles de partout. " - -#: ../../../templates/about.tpl.php:7 -msgid "Share your bookmarks with everyone, with friends on your watchlist or just keep them private." -msgstr "Partagez vos signets avec tout le monde, avec les utilisateurs autorisés ou gardez-les pour vous." - -#: ../../../templates/about.tpl.php:8 -msgid "Tag your bookmarks with as many labels as you want, instead of wrestling with folders." -msgstr "Taggez vos signets avec autant de labels que vous le souhaitez au lieu de les hiérarchiser avec des dossiers." - -#: ../../../templates/about.tpl.php:9 +#: data/templates/about.tpl.php:6 +msgid "" +"Store all your favourite links in one place, accessible " +"from anywhere." +msgstr "" +"Conservez tous vos signets au même endroit, accessibles de " +"partout. " + +#: data/templates/about.tpl.php:7 +msgid "" +"Share your bookmarks with everyone, with friends on your " +"watchlist or just keep them private." +msgstr "" +"Partagez vos signets avec tout le monde, avec les " +"utilisateurs autorisés ou gardez-les pour vous." + +#: data/templates/about.tpl.php:8 +msgid "" +"Tag your bookmarks with as many labels as you want, instead " +"of wrestling with folders." +msgstr "" +"Taggez vos signets avec autant de labels que vous le " +"souhaitez au lieu de les hiérarchiser avec des dossiers." + +#: data/templates/about.tpl.php:9 msgid "Register now" msgstr "S'enregistrer maintenant" -#: ../../../templates/about.tpl.php:9 +#: data/templates/about.tpl.php:9 #, php-format msgid " to start using %s!" msgstr "pour commencer à employer %s !" -#: ../../../templates/about.tpl.php:12 +#: data/templates/about.tpl.php:12 msgid "Geek Stuff" msgstr "Pour les Geeks" -#: ../../../templates/about.tpl.php:14 +#: data/templates/about.tpl.php:14 msgid "is licensed under the " msgstr "est sous licence" -#: ../../../templates/about.tpl.php:14 +#: data/templates/about.tpl.php:14 msgid "you can freely host it on your own web server." msgstr "vous pouvez librement l'installer sur votre serveur Web" -#: ../../../templates/about.tpl.php:15 +#: data/templates/about.tpl.php:15 #, php-format -msgid "%1$s supports most of the del.icio.us API. Almost all of the neat tools made for that system can be modified to work with %1$s instead. If you find a tool that won't let you change the API address, ask the creator to add this setting. You never know, they might just do it." -msgstr "%1$s supporte la plupart de l'API del.icio.us." +msgid "" +"%1$s supports most of the del.icio.us " +"API. Almost all " +"of the neat tools made for that system can be modified to work with %1$s " +"instead. If you find a tool that won't let you change the API address, ask " +"the creator to add this setting. You never know, they might just do it." +msgstr "" +"%1$s supporte la plupart de l'API del.icio.us." -#: ../../../templates/bookmarkcommondescriptionedit.tpl.php:15 -#: ../../../templates/bookmarks.tpl.php:67 -#: ../../../templates/editbookmark.tpl.php:34 -msgid "Title" -msgstr "Titre" +#: data/templates/about.tpl.php:24 +msgid "Tips" +msgstr "" -#: ../../../templates/bookmarkcommondescriptionedit.tpl.php:20 -#: ../../../templates/editbookmark.tpl.php:39 -#: ../../../templates/editprofile.tpl.php:46 -#: ../../../templates/profile.tpl.php:28 -#: ../../../templates/tagcommondescriptionedit.tpl.php:13 -#: ../../../templates/tagedit.tpl.php:13 -msgid "Description" -msgstr "Description" +#: data/templates/about.tpl.php:26 +msgid "Add search plugin into your browser:" +msgstr "" -#: ../../../templates/bookmarkcommondescriptionedit.tpl.php:28 -#: ../../../templates/tagcommondescriptionedit.tpl.php:21 -msgid "Last modification:" -msgstr "Dernière modification :" +#: data/templates/about.tpl.php:27 +msgid "" +"The secret tag \"system:unfiled\" allows you to find bookmarks without tags." +msgstr "" -#: ../../../templates/bookmarkcommondescriptionedit.tpl.php:39 -#: ../../../templates/tagcommondescriptionedit.tpl.php:32 -#: ../../../templates/tagedit.tpl.php:19 -msgid "Update" -msgstr "Mettre à jour" +#: data/templates/about.tpl.php:28 +msgid "" +"The secret tag \"system:imported\" allows you to find imported bookmarks." +msgstr "" -#: ../../../templates/bookmarkcommondescriptionedit.tpl.php:40 -#: ../../../templates/tag2tagadd.tpl.php:24 -#: ../../../templates/tag2tagedit.tpl.php:38 -#: ../../../templates/tagcommondescriptionedit.tpl.php:33 -#: ../../../templates/tagedit.tpl.php:20 -#: ../../../templates/tagrename.tpl.php:25 -msgid "Cancel" -msgstr "Annuler" +#: data/templates/admin.tpl.php:5 +#, fuzzy +msgid "Users management" +msgstr "Nom d'utilisateur" -#: ../../../templates/bookmarks.tpl.php:34 -#: ../../../templates/bookmarks.tpl.php:37 -msgid "edit common description" -msgstr "éditer la description commune" +#: data/templates/admin.tpl.php:14 +msgid "Public/Shared/Private" +msgstr "" -#: ../../../templates/bookmarks.tpl.php:64 +#: data/templates/admin.tpl.php:14 data/templates/bookmarks.tpl.php:93 msgid "bookmark(s)" msgstr "signet(s)" -#: ../../../templates/bookmarks.tpl.php:65 -#: ../../../templates/tags.tpl.php:10 -#: ../../../templates/users.tpl.php:8 -msgid "Sort by:" -msgstr "Classer par :" +#: data/templates/admin.tpl.php:19 data/templates/tag2tagadd.tpl.php:21 +#: data/templates/tag2tagdelete.tpl.php:13 +#: data/templates/tag2tagedit.tpl.php:14 data/templates/tag2tagedit.tpl.php:35 +#: data/templates/tagdelete.tpl.php:6 www/jsScuttle.php:23 +msgid "Are you sure?" +msgstr "Etes-vous sûr ?" -#: ../../../templates/bookmarks.tpl.php:66 -msgid "Date" -msgstr "Date" - -#: ../../../templates/bookmarks.tpl.php:71 -msgid "URL" -msgstr "URL" - -#: ../../../templates/bookmarks.tpl.php:81 -msgid "Bookmarks from other users for this tag" -msgstr "Signets des autres utilisateurs pour ce mot-clé" - -#: ../../../templates/bookmarks.tpl.php:86 -msgid "Only your bookmarks for this tag" -msgstr "Uniquement vos signets pour ce mot-clé" - -#: ../../../templates/bookmarks.tpl.php:129 -msgid "Edit" -msgstr "Editer" - -#: ../../../templates/bookmarks.tpl.php:129 +#: data/templates/admin.tpl.php:19 data/templates/bookmarks.tpl.php:267 msgid "Delete" msgstr "Supprimer" -#: ../../../templates/bookmarks.tpl.php:135 -msgid "by" -msgstr "par" +#: data/templates/admin.tpl.php:27 +#, fuzzy +msgid "Other actions" +msgstr "Instructions" -#: ../../../templates/bookmarks.tpl.php:139 -msgid "to" -msgstr "dans" +#: data/templates/admin.tpl.php:29 +msgid "Check all URLs (May take some time)" +msgstr "" -#: ../../../templates/bookmarks.tpl.php:147 -#, fuzzy, php-format -msgid " and %s1 other%s" -msgstr " et les autres %s" +#: data/templates/bookmarkcommondescriptionedit.tpl.php:16 +msgid "" +"Collaborative description: these fields can be viewed and modified by every " +"users" +msgstr "" -#: ../../../templates/bookmarks.tpl.php:150 -#, fuzzy, php-format -msgid " and %2$s%1$s others%3$s" -msgstr " et les autres %s" +#: data/templates/bookmarkcommondescriptionedit.tpl.php:18 +#: data/templates/bookmarks.tpl.php:137 data/templates/editbookmark.tpl.php:43 +msgid "Title" +msgstr "Titre" -#: ../../../templates/bookmarks.tpl.php:159 -msgid "Copy" -msgstr "Copier" +#: data/templates/bookmarkcommondescriptionedit.tpl.php:23 +#: data/templates/editbookmark.tpl.php:49 +#: data/templates/editprofile.tpl.php:47 data/templates/profile.tpl.php:33 +#: data/templates/tagcommondescriptionedit.tpl.php:13 +#: data/templates/tagedit.tpl.php:12 +msgid "Description" +msgstr "Description" -#: ../../../templates/bookmarks.tpl.php:218 -#: ../../../templates/bookmarks.tpl.php:224 +#: data/templates/bookmarkcommondescriptionedit.tpl.php:31 +#: data/templates/tagcommondescriptionedit.tpl.php:21 +msgid "Last modification:" +msgstr "Dernière modification :" + +#: data/templates/bookmarkcommondescriptionedit.tpl.php:42 +#: data/templates/tagcommondescriptionedit.tpl.php:32 +#: data/templates/tagedit.tpl.php:18 +msgid "Update" +msgstr "Mettre à jour" + +#: data/templates/bookmarkcommondescriptionedit.tpl.php:43 +#: data/templates/editbookmark.tpl.php:103 +#: data/templates/tag2tagadd.tpl.php:24 data/templates/tag2tagedit.tpl.php:38 +#: data/templates/tagcommondescriptionedit.tpl.php:33 +#: data/templates/tagedit.tpl.php:19 data/templates/tagrename.tpl.php:25 +msgid "Cancel" +msgstr "Annuler" + +#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:24 +#, php-format +msgid "Voting %d" +msgstr "" + +#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:32 +#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:35 +msgid "Vote for" +msgstr "" + +#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:43 +#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:46 +msgid "Vote against" +msgstr "" + +#: data/templates/bookmarks.tpl.php:26 +msgid "Bookmarks on this page are managed by an admin user." +msgstr "" + +#: data/templates/bookmarks.tpl.php:51 data/templates/bookmarks.tpl.php:52 +#, fuzzy +msgid "Edit the common description of this tag" +msgstr "éditer la description commune" + +#: data/templates/bookmarks.tpl.php:55 data/templates/bookmarks.tpl.php:56 +#, fuzzy +msgid "Edit the common description of this bookmark" +msgstr "éditer la description commune" + +#: data/templates/bookmarks.tpl.php:76 data/templates/bookmarks.tpl.php:77 +msgid "Edit your personal description of this tag" +msgstr "" + +#: data/templates/bookmarks.tpl.php:93 data/templates/tags.tpl.php:10 +#: data/templates/users.tpl.php:8 +msgid "Sort by:" +msgstr "Classer par :" + +#: data/templates/bookmarks.tpl.php:135 +msgid "Date" +msgstr "Date" + +#: data/templates/bookmarks.tpl.php:140 +msgid "Voting" +msgstr "" + +#: data/templates/bookmarks.tpl.php:149 +msgid "Bookmarks from other users for this tag" +msgstr "Signets des autres utilisateurs pour ce mot-clé" + +#: data/templates/bookmarks.tpl.php:154 +msgid "Only your bookmarks for this tag" +msgstr "Uniquement vos signets pour ce mot-clé" + +#: data/templates/bookmarks.tpl.php:177 data/templates/bookmarks.tpl.php:183 msgid "First" msgstr "Première" -#: ../../../templates/bookmarks.tpl.php:219 -#: ../../../templates/bookmarks.tpl.php:225 +#: data/templates/bookmarks.tpl.php:178 data/templates/bookmarks.tpl.php:184 msgid "Previous" msgstr "Précédent" -#: ../../../templates/bookmarks.tpl.php:232 -#: ../../../templates/bookmarks.tpl.php:235 +#: data/templates/bookmarks.tpl.php:191 data/templates/bookmarks.tpl.php:194 msgid "Next" msgstr "Suivant" -#: ../../../templates/bookmarks.tpl.php:233 -#: ../../../templates/bookmarks.tpl.php:236 +#: data/templates/bookmarks.tpl.php:192 data/templates/bookmarks.tpl.php:195 msgid "Last" msgstr "Dernière" -#: ../../../templates/bookmarks.tpl.php:246 +#: data/templates/bookmarks.tpl.php:205 #, php-format msgid "Page %d of %d" msgstr "Page %d de %d" -#: ../../../templates/bookmarks.tpl.php:252 +#: data/templates/bookmarks.tpl.php:261 +#, fuzzy +msgid "Tags:" +msgstr "Mots-clés" + +#: data/templates/bookmarks.tpl.php:267 +msgid "Edit" +msgstr "Editer" + +#: data/templates/bookmarks.tpl.php:271 +msgid "Last update" +msgstr "" + +#: data/templates/bookmarks.tpl.php:274 +msgid "by" +msgstr "par" + +#: data/templates/bookmarks.tpl.php:276 +msgid "you" +msgstr "" + +#: data/templates/bookmarks.tpl.php:290 +#, fuzzy, php-format +msgid " and %s1 other%s" +msgstr " et les autres %s" + +#: data/templates/bookmarks.tpl.php:293 +#, fuzzy, php-format +msgid " and %2$s%1$s others%3$s" +msgstr " et les autres %s" + +#: data/templates/bookmarks.tpl.php:304 +#, fuzzy +msgid "Copy this bookmark to YOUR bookmarks." +msgstr "Importer les signets depuis un fichier" + +#: data/templates/bookmarks.tpl.php:305 +msgid "Copy" +msgstr "Copier" + +#: data/templates/bookmarks.tpl.php:325 +msgid "This bookmark is certified by an admin user." +msgstr "" + +#: data/templates/bookmarks.tpl.php:371 +#, fuzzy +msgid "Private Note on this bookmark" +msgstr "Vous n'êtes pas autorisé à éditer ce signet." + +#: data/templates/bookmarks.tpl.php:383 +msgid "Come back to the top of this page." +msgstr "" + +#: data/templates/bookmarks.tpl.php:383 +msgid "Top of the page" +msgstr "" + +#: data/templates/bookmarks.tpl.php:389 msgid "No bookmarks available" msgstr "Pas de signets disponibles." -#: ../../../templates/dynamictags.inc.php:108 -#: ../../../templates/sidebar.block.common.php:20 -#: ../../../templates/sidebar.block.popular.php:26 -#: ../../../templates/sidebar.block.recent.php:25 -#: ../../../templates/tags.tpl.php:19 +#: data/templates/bottom.inc.php:5 data/templates/toolbar.inc.php:15 +#: data/templates/toolbar.inc.php:28 www/about.php:23 www/about.php:24 +msgid "About" +msgstr "À propos" + +#: data/templates/bottom.inc.php:7 +msgid "Propulsed by " +msgstr "" + +#: data/templates/dynamictags.inc.php:47 +#: data/templates/sidebar.block.common.php:19 +#: data/templates/sidebar.block.popular.php:34 +#: data/templates/sidebar.block.recent.php:29 data/templates/tags.tpl.php:19 msgid "bookmark" msgid_plural "bookmarks" msgstr[0] "signet" msgstr[1] "Signets" -#: ../../../templates/editbookmark.tpl.php:29 +#: data/templates/dynamictags.inc.php:128 +#: data/templates/sidebar.block.common.php:9 +#: data/templates/sidebar.block.menu.php:74 +#: data/templates/sidebar.block.popular.php:23 +#: data/templates/sidebar.block.recent.php:34 +#: data/templates/toolbar.inc.php:27 www/populartags.php:46 +msgid "Popular Tags" +msgstr "Mots-clés populaires" + +#: data/templates/dynamictags.inc.php:131 +#, fuzzy +msgid "Popular Tags From All Users" +msgstr "Mots-clés populaires" + +#: data/templates/editbookmark.tpl.php:38 msgid "Address" msgstr "Adresse" -#: ../../../templates/editbookmark.tpl.php:31 -#: ../../../templates/editbookmark.tpl.php:36 -#: ../../../templates/editprofile.tpl.php:30 -#: ../../../templates/tagrename.tpl.php:14 -#: ../../../templates/tagrename.tpl.php:19 +#: data/templates/editbookmark.tpl.php:40 +#: data/templates/editbookmark.tpl.php:45 +#: data/templates/editprofile.tpl.php:31 data/templates/tagrename.tpl.php:14 +#: data/templates/tagrename.tpl.php:19 msgid "Required" msgstr "Requis" -#: ../../../templates/editbookmark.tpl.php:46 +#: data/templates/editbookmark.tpl.php:50 +msgid "Add Note" +msgstr "" + +#: data/templates/editbookmark.tpl.php:53 +msgid "" +"You can use anchors to delimite attributes. for example: [publisher]blah[/" +"publisher] " +msgstr "" + +#: data/templates/editbookmark.tpl.php:56 +msgid "Suggested anchors: " +msgstr "" + +#: data/templates/editbookmark.tpl.php:68 +#, fuzzy +msgid "Private Note" +msgstr "Privée" + +#: data/templates/editbookmark.tpl.php:70 +msgid "Just visible by you and your contacts." +msgstr "" + +#: data/templates/editbookmark.tpl.php:74 data/templates/toolbar.inc.php:10 +#: www/tags.php:45 www/tags.php:67 +msgid "Tags" +msgstr "Mots-clés" + +#: data/templates/editbookmark.tpl.php:78 msgid "Comma-separated" msgstr "Séparés par des virgules" -#: ../../../templates/editbookmark.tpl.php:50 -#: ../../../templates/tag2tagadd.tpl.php:9 -msgid "Note: use \">\" to include one tag in another. e.g.: europe>france>paris" -msgstr "Note: utiliser \">\" pour inclure un mot-clé dans un autre. ex: europe>france>paris" +#: data/templates/editbookmark.tpl.php:82 data/templates/tag2tagadd.tpl.php:9 +msgid "" +"Note: use \">\" to include one tag in another. e.g.: europe>france>paris" +msgstr "" +"Note: utiliser \">\" pour inclure un mot-clé dans un autre. ex: " +"europe>france>paris" -#: ../../../templates/editbookmark.tpl.php:54 -#: ../../../templates/mot-clé2tagadd.tpl.php:8 +#: data/templates/editbookmark.tpl.php:86 data/templates/tag2tagadd.tpl.php:8 msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry" -msgstr "Note : utiliser \"=\" pour rendre deux mots-clés synonymes ex: europe=eu" +msgstr "" +"Note : utiliser \"=\" pour rendre deux mots-clés synonymes ex: " +"europe=eu" -#: ../../../templates/editbookmark.tpl.php:57 -#: ../../../templates/importDelicious.tpl.php:15 -#: ../../../templates/importNetscape.tpl.php:16 +#: data/templates/editbookmark.tpl.php:89 +#: data/templates/importDelicious.tpl.php:15 +#: data/templates/importNetscape.tpl.php:16 msgid "Privacy" msgstr "Vision" -#: ../../../templates/editbookmark.tpl.php:60 -#: ../../../templates/importDelicious.tpl.php:18 -#: ../../../templates/importNetscape.tpl.php:19 +#: data/templates/editbookmark.tpl.php:92 +#: data/templates/importDelicious.tpl.php:18 +#: data/templates/importNetscape.tpl.php:19 msgid "Public" msgstr "Publique" -#: ../../../templates/editbookmark.tpl.php:61 +#: data/templates/editbookmark.tpl.php:93 msgid "Shared with Watch List" msgstr "Partagé avec liste d'accès" -#: ../../../templates/editbookmark.tpl.php:62 -#: ../../../templates/importDelicious.tpl.php:20 -#: ../../../templates/importNetscape.tpl.php:21 +#: data/templates/editbookmark.tpl.php:94 +#: data/templates/importDelicious.tpl.php:20 +#: data/templates/importNetscape.tpl.php:21 msgid "Private" msgstr "Privée" -#: ../../../templates/editbookmark.tpl.php:74 +#: data/templates/editbookmark.tpl.php:107 msgid "Delete Bookmark" msgstr "Supprimer le signet" -#: ../../../templates/editbookmark.tpl.php:101 +#: data/templates/editbookmark.tpl.php:112 +msgid "edit common description" +msgstr "éditer la description commune" + +#: data/templates/editbookmark.tpl.php:139 msgid "Bookmarklet" msgstr "Bookmarklet" -#: ../../../templates/editbookmark.tpl.php:102 -#, php-format -msgid "Drag one of the following bookmarklets to your browser's bookmarks and click it whenever you want to add the page you are on to %s" -msgstr "Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un signet pour la page courante dans %s " +#: data/templates/editbookmark.tpl.php:145 +#, fuzzy, php-format +msgid "" +"Click one of the following bookmarklets to add a button you can click " +"whenever you want to add the page you are on to %s" +msgstr "" +"Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre " +"navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un " +"signet pour la page courante dans %s " -#: ../../../templates/editbookmark.tpl.php:114 +#: data/templates/editbookmark.tpl.php:149 +#, php-format +msgid "" +"Drag one of the following bookmarklets to your browser's bookmarks and click " +"it whenever you want to add the page you are on to %s" +msgstr "" +"Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre " +"navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un " +"signet pour la page courante dans %s " + +#: data/templates/editbookmark.tpl.php:162 +#: data/templates/editbookmark.tpl.php:167 #, php-format msgid "Post to %s" msgstr "Ajouter à %s" -#: ../../../templates/editbookmark.tpl.php:115 +#: data/templates/editbookmark.tpl.php:163 +#: data/templates/editbookmark.tpl.php:168 #, php-format msgid "Post to %s (Pop-up)" msgstr "Ajouter à %s (Pop-up)" -#: ../../../templates/editbookmark.tpl.php:119 -#: ../../../templates/importDelicious.tpl.php:26 -#: ../../../templates/importNetscape.tpl.php:27 +#: data/templates/editbookmark.tpl.php:173 +#: data/templates/importDelicious.tpl.php:26 +#: data/templates/importNetscape.tpl.php:27 +#: data/templates/importStructure.tpl.php:16 msgid "Import" msgstr "Importer" -#: ../../../templates/editbookmark.tpl.php:121 +#: data/templates/editbookmark.tpl.php:175 msgid "Import bookmarks from bookmark file" msgstr "Importer les signets depuis un fichier" -#: ../../../templates/editbookmark.tpl.php:121 +#: data/templates/editbookmark.tpl.php:175 msgid "Internet Explorer, Mozilla Firefox and Netscape" msgstr "Internet Explorer, Mozilla Firefox et Netscape" -#: ../../../templates/editbookmark.tpl.php:122 +#: data/templates/editbookmark.tpl.php:176 msgid "Import bookmarks from del.icio.us" msgstr "Importer les signets depuis del.icio.us" -#: ../../../templates/editprofile.tpl.php:9 +#: data/templates/editprofile.tpl.php:10 msgid "Account Details" msgstr "Détail du compte" -#: ../../../templates/editprofile.tpl.php:13 -#: ../../../templates/login.tpl.php:15 -#: ../../../templates/password.tpl.php:10 -#: ../../../templates/profile.tpl.php:7 -#: ../../../templates/register.tpl.php:16 +#: data/templates/editprofile.tpl.php:14 data/templates/login.tpl.php:21 +#: data/templates/password.tpl.php:10 data/templates/profile.tpl.php:6 +#: data/templates/register.tpl.php:16 msgid "Username" msgstr "Nom d'utilisateur" -#: ../../../templates/editprofile.tpl.php:18 +#: data/templates/editprofile.tpl.php:19 msgid "New Password" msgstr "Nouveau mot de passe" -#: ../../../templates/editprofile.tpl.php:23 +#: data/templates/editprofile.tpl.php:24 msgid "Confirm Password" msgstr "Confirmer le mot de passe" -#: ../../../templates/editprofile.tpl.php:28 -#: ../../../templates/password.tpl.php:14 -#: ../../../templates/register.tpl.php:26 +#: data/templates/editprofile.tpl.php:29 data/templates/password.tpl.php:14 +#: data/templates/register.tpl.php:26 msgid "E-mail" msgstr "E-mail" -#: ../../../templates/editprofile.tpl.php:34 +#: data/templates/editprofile.tpl.php:35 msgid "Personal Details" msgstr "Détails personnels" -#: ../../../templates/editprofile.tpl.php:38 -#: ../../../templates/profile.tpl.php:12 +#: data/templates/editprofile.tpl.php:39 data/templates/profile.tpl.php:17 msgid "Name" msgstr "Nom" -#: ../../../templates/editprofile.tpl.php:42 -#: ../../../templates/profile.tpl.php:18 +#: data/templates/editprofile.tpl.php:43 data/templates/profile.tpl.php:23 msgid "Homepage" msgstr "Page personnelle" -#: ../../../templates/editprofile.tpl.php:54 -#: ../../../templates/sidebar.block.tagactions.php:18 -#: ../../../templates/sidebar.block.watchstatus.php:17 +#: data/templates/editprofile.tpl.php:52 www/edit.php:113 +msgid "Save Changes" +msgstr "Enregistrer les modifications" + +#: data/templates/editprofile.tpl.php:55 +#: data/templates/sidebar.block.tagactions.php:17 +#: data/templates/sidebar.block.watchstatus.php:18 #, fuzzy msgid "Actions" msgstr "Instructions" -#: ../../../templates/editprofile.tpl.php:57 +#: data/templates/editprofile.tpl.php:58 msgid "Export bookmarks" msgstr "Exporter les signets" -#: ../../../templates/editprofile.tpl.php:59 +#: data/templates/editprofile.tpl.php:60 msgid "HTML file (for browsers)" msgstr "Fichier HTML (pour navigateurs)" -#: ../../../templates/editprofile.tpl.php:60 +#: data/templates/editprofile.tpl.php:61 msgid "XML file (like del.icio.us)" msgstr "Fichier XML (comme del.icio.us)" -#: ../../../templates/error.404.tpl.php:5 +#: data/templates/editprofile.tpl.php:62 +msgid "CSV file (for spreadsheet tools)" +msgstr "" + +#: data/templates/error.404.tpl.php:5 msgid "Not Found" msgstr "Non trouvé" -#: ../../../templates/error.404.tpl.php:6 +#: data/templates/error.404.tpl.php:6 msgid "The requested URL was not found on this server" msgstr "L'URL demandée n'a pas été trouvée sur ce serveur." -#: ../../../templates/error.500.tpl.php:5 +#: data/templates/error.500.tpl.php:5 msgid "General server error" msgstr "Erreur généralisée du serveur." -#: ../../../templates/error.500.tpl.php:6 +#: data/templates/error.500.tpl.php:6 msgid "The requested URL could not be processed" msgstr "L'URL demandée n'a pas été trouvée." -#: ../../../templates/importDelicious.tpl.php:19 -#: ../../../templates/importNetscape.tpl.php:20 +#: data/templates/importDelicious.tpl.php:19 +#: data/templates/importNetscape.tpl.php:20 #, fuzzy msgid "Shared with Watchlist" msgstr "Partagé avec liste d'accès" -#: ../../../templates/importDelicious.tpl.php:31 -#: ../../../templates/importNetscape.tpl.php:32 +#: data/templates/importDelicious.tpl.php:31 +#: data/templates/importNetscape.tpl.php:32 +#: data/templates/importStructure.tpl.php:21 msgid "Instructions" msgstr "Instructions" -#: ../../../templates/importDelicious.tpl.php:33 -msgid "Log in to the export page at del.icio.us" -msgstr "Se connecter à la page d'export de del.icio.us" +#: data/templates/importDelicious.tpl.php:33 +msgid "" +"Log in to the export page at " +"del.icio.us" +msgstr "" +"Se connecter à la page d'export " +"de del.icio.us" -#: ../../../templates/importDelicious.tpl.php:34 -msgid "Save the resulting XML file to your computer" -msgstr "Enregistrer le fichier XML résultant sur votre ordinateur" +#: data/templates/importDelicious.tpl.php:34 +msgid "" +"Save the resulting XML " +"file to your computer" +msgstr "" +"Enregistrer le fichier XML " +"résultant sur votre ordinateur" -#: ../../../templates/importDelicious.tpl.php:35 -msgid "Click Browse... to find this file on your computer. The maximum size the file can be is 1MB" -msgstr "Cliquez sur Parcourir... pour trouver le fichier sur votre ordinateur. La taille maximale du fichier ne peut excèder 1Mo" +#: data/templates/importDelicious.tpl.php:35 +msgid "" +"Click Browse... to find this file on your computer. The maximum " +"size the file can be is 1MB" +msgstr "" +"Cliquez sur Parcourir... pour trouver le fichier sur votre " +"ordinateur. La taille maximale du fichier ne peut excèder 1Mo" -#: ../../../templates/importDelicious.tpl.php:36 -#: ../../../templates/importNetscape.tpl.php:43 +#: data/templates/importDelicious.tpl.php:36 +#: data/templates/importNetscape.tpl.php:43 msgid "Select the default privacy setting for your imported bookmarks" msgstr "Selectionnez la vision par défaut à appliquer à vos signets importés" -#: ../../../templates/importDelicious.tpl.php:37 -#: ../../../templates/importNetscape.tpl.php:44 -msgid "Click Import to start importing the bookmarks; it may take a minute" -msgstr "Cliquez sur Importer pour débuter l'import des signets; cette opération peut prendre quelques minutes" +#: data/templates/importDelicious.tpl.php:37 +#: data/templates/importNetscape.tpl.php:44 +msgid "" +"Click Import to start importing the bookmarks; it may take a " +"minute" +msgstr "" +"Cliquez sur Importer pour débuter l'import des signets; cette " +"opération peut prendre quelques minutes" -#: ../../../templates/importNetscape.tpl.php:35 +#: data/templates/importNetscape.tpl.php:35 msgid "Export your bookmarks from your browser to a file" msgstr "Exporter vos signets dans un fichier depuis votre navigateur" -#: ../../../templates/importNetscape.tpl.php:37 -msgid "Internet Explorer: File > Import and Export... > Export Favorites" -msgstr "Internet Explorer: Ficher > Importer et Exporter... > Exporter les favoris" +#: data/templates/importNetscape.tpl.php:37 +msgid "" +"Internet Explorer: File > Import and Export... > Export Favorites" +msgstr "" +"Internet Explorer: Ficher > Importer et Exporter... > Exporter " +"les favoris" -#: ../../../templates/importNetscape.tpl.php:38 -msgid "Mozilla Firefox: Bookmarks > Manage Bookmarks... > File > Export..." -msgstr "Mozilla Firefox: Marques-pages > Gérer les marques-pages... > Fichier > Exporter..." +#: data/templates/importNetscape.tpl.php:38 +msgid "" +"Mozilla Firefox: Bookmarks > Manage Bookmarks... > File > " +"Export..." +msgstr "" +"Mozilla Firefox: Marques-pages > Gérer les marques-pages... > " +"Fichier > Exporter..." -#: ../../../templates/importNetscape.tpl.php:39 -msgid "Netscape: Bookmarks > Manage Bookmarks... > Tools > Export..." -msgstr "Netscape: Marques-pages > Gérer les marques-pages... > Outils > Exporter..." +#: data/templates/importNetscape.tpl.php:39 +msgid "" +"Netscape: Bookmarks > Manage Bookmarks... > Tools > Export..." +msgstr "" +"Netscape: Marques-pages > Gérer les marques-pages... > Outils " +"> Exporter..." -#: ../../../templates/importNetscape.tpl.php:42 -msgid "Click Browse... to find the saved bookmark file on your computer. The maximum size the file can be is 1MB" -msgstr "Cliquez sur Parcourir... pour trouver le fichier sur votre ordinateur. La taille maximale du fichier ne peut excèder 1Mo" +#: data/templates/importNetscape.tpl.php:42 +msgid "" +"Click Browse... to find the saved bookmark file on your computer. " +"The maximum size the file can be is 1MB" +msgstr "" +"Cliquez sur Parcourir... pour trouver le fichier sur votre " +"ordinateur. La taille maximale du fichier ne peut excèder 1Mo" -#: ../../../templates/login.tpl.php:20 -#: ../../../templates/register.tpl.php:21 +#: data/templates/importStructure.tpl.php:24 +msgid "Create your structure into a simple text file and following this model:" +msgstr "" + +#: data/templates/importStructure.tpl.php:35 +msgid "" +"Then import the file. The tags and their relations will be added to your " +"profile." +msgstr "" + +#: data/templates/login.tpl.php:13 +msgid "Please activate cookies" +msgstr "" + +#: data/templates/login.tpl.php:26 data/templates/register.tpl.php:21 msgid "Password" msgstr "Mot de passe" -#: ../../../templates/login.tpl.php:22 +#: data/templates/login.tpl.php:28 msgid "Don't ask for my password for 2 weeks" msgstr "Ne pas demander mon mot de passe pendant 2 semaines" -#: ../../../templates/login.tpl.php:30 +#: data/templates/login.tpl.php:32 data/templates/toolbar.inc.php:29 +#: www/login.php:57 +msgid "Log In" +msgstr "Se connecter" + +#: data/templates/login.tpl.php:36 msgid "Forgotten your password?" msgstr "Avez-vous oublié votre mot de passe ?" -#: ../../../templates/password.tpl.php:5 +#: data/templates/password.tpl.php:5 #, php-format -msgid "If you have forgotten your password, %s can generate a new one. Enter the username and e-mail address of your account into the form below and we will e-mail your new password to you." -msgstr "Si vous avez oublié votre mot de passe, %s peut en générer un nouveau. Entrez le nom d'utilisateur et l'adresse email de votre compte dans le formulaire ci-dessous et nous vous enverrons un nouveau mot de passe." +msgid "" +"If you have forgotten your password, %s can generate a new one. Enter the " +"username and e-mail address of your account into the form below and we will " +"e-mail your new password to you." +msgstr "" +"Si vous avez oublié votre mot de passe, %s peut en générer un nouveau. " +"Entrez le nom d'utilisateur et l'adresse email de votre compte dans le " +"formulaire ci-dessous et nous vous enverrons un nouveau mot de passe." -#: ../../../templates/password.tpl.php:19 +#: data/templates/password.tpl.php:19 msgid "Generate Password" msgstr "Générer un mot de passe" -#: ../../../templates/profile.tpl.php:23 +#: data/templates/profile.tpl.php:11 +#, fuzzy +msgid "Email" +msgstr "E-mail" + +#: data/templates/profile.tpl.php:28 msgid "Member Since" msgstr "Membre depuis" -#: ../../../templates/profile.tpl.php:35 -#: ../../../templates/sidebar.block.watchlist.php:8 +#: data/templates/profile.tpl.php:40 +#: data/templates/sidebar.block.watchlist.php:30 #, fuzzy msgid "Watching" msgstr "Liste des signets vus" -#: ../../../templates/profile.tpl.php:50 +#: data/templates/profile.tpl.php:55 +#: data/templates/sidebar.block.watchlist.php:52 #, fuzzy msgid "Watched By" msgstr "Consultés" -#: ../../../templates/profile.tpl.php:63 -#: ../../../templates/toolbar.inc.php:10 +#: data/templates/profile.tpl.php:68 data/templates/toolbar.inc.php:9 msgid "Bookmarks" msgstr "Signets" -#: ../../../templates/profile.tpl.php:64 +#: data/templates/profile.tpl.php:69 msgid "Go to bookmarks" msgstr "Aller aux signets" -#: ../../../templates/register.tpl.php:11 +#: data/templates/register.tpl.php:11 #, php-format -msgid "Sign up here to create a free %s account. All the information requested below is required" -msgstr "Enregistrez-vous ici pour créer un compte gratuit %s. Toutes les informations requises ci-dessous sont nécessaires." +msgid "" +"Sign up here to create a free %s account. All the information requested " +"below is required" +msgstr "" +"Enregistrez-vous ici pour créer un compte gratuit %s. Toutes les " +"informations requises ci-dessous sont nécessaires." -#: ../../../templates/register.tpl.php:33 +#: data/templates/register.tpl.php:18 +msgid "" +" at least 5 characters, alphanumeric (no spaces, no dots or other special " +"ones)" +msgstr "" + +#: data/templates/register.tpl.php:28 +msgid " to send you your password if you forget it" +msgstr "" + +#: data/templates/register.tpl.php:33 msgid "Antispam question" msgstr "Question antispam" -#: ../../../templates/sidebar.block.linked.php:41 +#: data/templates/register.tpl.php:41 data/templates/toolbar.inc.php:31 +#: www/register.php:83 +msgid "Register" +msgstr "S'enregistrer" + +#: data/templates/search.menu.php:27 +#, fuzzy +msgid "Search..." +msgstr "Chercher dans" + +#: data/templates/search.menu.php:28 +#, fuzzy +msgid "in" +msgstr "Ligne" + +#: data/templates/search.menu.php:34 +msgid "this user's bookmarks" +msgstr "les signets de cet utilisateur" + +#: data/templates/search.menu.php:39 +msgid "my bookmarks" +msgstr "mes signets" + +#: data/templates/search.menu.php:40 +msgid "my watchlist" +msgstr "ma liste des consultés" + +#: data/templates/search.menu.php:44 +msgid "all bookmarks" +msgstr "tous les signets" + +#: data/templates/search.menu.php:54 +msgid "Search" +msgstr "Chercher dans" + +#: data/templates/sidebar.block.linked.php:51 msgid "Linked Tags" msgstr "Mots-clés structurés" -#: ../../../templates/sidebar.block.linked.php:55 -#: ../../../templates/sidebar.block.menu.php:40 +#: data/templates/sidebar.block.linked.php:62 +#: data/templates/sidebar.block.menu.php:46 msgid "Add new link" msgstr "Créer un lien" -#: ../../../templates/sidebar.block.linked.php:56 -#: ../../../templates/sidebar.block.menu.php:41 +#: data/templates/sidebar.block.linked.php:63 +#: data/templates/sidebar.block.menu.php:47 msgid "Delete link" msgstr "Supprimer un lien" -#: ../../../templates/sidebar.block.menu.php:29 +#: data/templates/sidebar.block.menu.php:35 #, php-format msgid "Tags included into the tag '%s'" msgstr "Mots-clés inclus dans le mot-clé '%s'" -#: ../../../templates/sidebar.block.menu.php:29 +#: data/templates/sidebar.block.menu.php:35 msgid "Menu Tags" msgstr "Mots-clés Menu" -#: ../../../templates/sidebar.block.menu.php:62 +#: data/templates/sidebar.block.menu.php:68 msgid "See all your tags" msgstr "Voir tous vos mots-clés" -#: ../../../templates/sidebar.block.menu.php:62 +#: data/templates/sidebar.block.menu.php:68 msgid "all your tags" msgstr "Tous vos mots-clés" -#: ../../../templates/sidebar.block.menu.php:64 +#: data/templates/sidebar.block.menu.php:70 msgid "See all tags from this user" msgstr "Voir tous les mots-clés de cet utilisateur" -#: ../../../templates/sidebar.block.menu.php:64 +#: data/templates/sidebar.block.menu.php:70 msgid "all tags from this user" msgstr "tous les mots-clés de cet utilisateur" -#: ../../../templates/sidebar.block.menu.php:68 +#: data/templates/sidebar.block.menu.php:74 #, fuzzy msgid "See popular tags" msgstr "Voir tous vos mots-clés" -#: ../../../templates/sidebar.block.recent.php:15 +#: data/templates/sidebar.block.menu2.php:25 +#, fuzzy +msgid "Featured Menu Tags" +msgstr "Mots-clés Menu" + +#: data/templates/sidebar.block.menu2.php:29 +msgid "This menu is composed of keywords (tags) organized by admins." +msgstr "" + +#: data/templates/sidebar.block.recent.php:18 msgid "Recent Tags" msgstr "Mots-clés récents" -#: ../../../templates/sidebar.block.related.php:17 +#: data/templates/sidebar.block.related.php:26 msgid "Related Tags" msgstr "Mots-clés en relation" -#: ../../../templates/sidebar.block.search.php:15 +#: data/templates/sidebar.block.search.php:15 msgid "Last Searches" msgstr "Dernières recherches" -#: ../../../templates/sidebar.block.search.php:24 +#: data/templates/sidebar.block.search.php:24 msgid "Number of bookmarks for this query" msgstr "Nombre de signets pour cette recherche" -#: ../../../templates/sidebar.block.tagactions.php:29 +#: data/templates/sidebar.block.tagactions.php:9 www/tagrename.php:72 +msgid "Rename Tag" +msgid_plural "Rename Tags" +msgstr[0] "Renommer le mot-clé" +msgstr[1] "Renommer les mots-clés" + +#: data/templates/sidebar.block.tagactions.php:22 www/tagdelete.php:54 +msgid "Delete Tag" +msgstr "Supprimer le mot-clé" + +#: data/templates/sidebar.block.tagactions.php:24 www/tagedit.php:61 +msgid "Edit Tag Description" +msgstr "Editer la description du mot-clé" + +#: data/templates/sidebar.block.tagactions.php:26 +#: www/tagcommondescriptionedit.php:76 +msgid "Edit Tag Common Description" +msgstr "Editer la description commune du mot-clé" + +#: data/templates/sidebar.block.tagactions.php:28 msgid "Create a link to another tag" msgstr "Créer un lien vers un autre mot-clé" -#: ../../../templates/sidebar.block.users.php:13 -msgid "Last Users" -msgstr "Derniers utilisateurs" +#: data/templates/sidebar.block.users.php:14 +#, fuzzy +msgid "New Users" +msgstr "Utilisateurs" -#: ../../../templates/sidebar.block.users.php:22 -#: ../../../templates/users.tpl.php:17 +#: data/templates/sidebar.block.users.php:23 data/templates/users.tpl.php:17 msgid "bookmarks" msgstr "signets" -#: ../../../templates/sidebar.block.users.php:29 +#: data/templates/sidebar.block.users.php:30 #, fuzzy msgid "See all users" msgstr "Voir tous vos mots-clés" -#: ../../../templates/sidebar.block.users.php:29 +#: data/templates/sidebar.block.users.php:30 msgid "All users" msgstr "Tous les utilisateurs" -#: ../../../templates/sidebar.block.watchstatus.php:10 +#: data/templates/sidebar.block.watchlist.php:19 +msgid "Close contacts are mutual contacts" +msgstr "" + +#: data/templates/sidebar.block.watchlist.php:19 +msgid "Close contacts" +msgstr "" + +#: data/templates/sidebar.block.watchlist.php:36 +msgid "Add a contact..." +msgstr "" + +#: data/templates/sidebar.block.watchlist.php:36 +msgid "Type a username to add it to your contacts." +msgstr "" + +#: data/templates/sidebar.block.watchlist.php:44 +#, fuzzy +msgid "Remove this contact" +msgstr "Enlever de la liste des consultés" + +#: data/templates/sidebar.block.watchstatus.php:11 msgid "Remove from Watchlist" msgstr "Enlever de la liste des consultés" -#: ../../../templates/sidebar.block.watchstatus.php:12 +#: data/templates/sidebar.block.watchstatus.php:13 msgid "Add to Watchlist" msgstr "Ajouter à la liste des consultés" -#: ../../../templates/sidebar.linkedtags.inc.php:18 +#: data/templates/sidebar.linkedtags.inc.php:18 msgid "Edit link" msgstr "Editer un lien" -#: ../../../templates/sidebar.linkedtags.inc.php:45 +#: data/templates/sidebar.linkedtags.inc.php:47 msgid "Synonyms:" msgstr "Synonymes :" -#: ../../../templates/tag2tagadd.tpl.php:12 +#: data/templates/tag2tagadd.tpl.php:12 msgid "Create new link:" msgstr "Créer un nouveau lien" -#: ../../../templates/tag2tagadd.tpl.php:19 +#: data/templates/tag2tagadd.tpl.php:19 #, php-format -msgid "Note: include a tag into '%s' tag (e.g. %s>countries) display the tag into the menu box" -msgstr "Note : inclure un mot-clé dans le mot-clé '%s' (e.g. %s>countries) affiche ce mot-clé dans la boîte de menu" +msgid "" +"Note: include a tag into '%s' tag (e.g. %s>countries) display the tag into " +"the menu box" +msgstr "" +"Note : inclure un mot-clé dans le mot-clé '%s' (e.g. %" +"s>countries) affiche ce mot-clé dans la boîte de menu" -#: ../../../templates/tag2tagadd.tpl.php:23 -#: ../../../templates/tag2tagedit.tpl.php:37 +#: data/templates/tag2tagadd.tpl.php:23 data/templates/tag2tagedit.tpl.php:37 msgid "Create" msgstr "Créer" -#: ../../../templates/tag2tagadd.tpl.php:35 -#: ../../../templates/tag2tagdelete.tpl.php:27 -#: ../../../templates/tag2tagedit.tpl.php:51 +#: data/templates/tag2tagadd.tpl.php:35 +#: data/templates/tag2tagdelete.tpl.php:27 +#: data/templates/tag2tagedit.tpl.php:51 msgid "Existing links:" msgstr "Liens existants :" -#: ../../../templates/tag2tagadd.tpl.php:53 -#: ../../../templates/tag2tagdelete.tpl.php:45 -#: ../../../templates/tag2tagedit.tpl.php:69 +#: data/templates/tag2tagadd.tpl.php:53 +#: data/templates/tag2tagdelete.tpl.php:45 +#: data/templates/tag2tagedit.tpl.php:69 msgid "No links" msgstr "Pas de liens" -#: ../../../templates/tag2tagedit.tpl.php:6 +#: data/templates/tag2tagdelete.tpl.php:15 +#: data/templates/tag2tagedit.tpl.php:16 data/templates/tagdelete.tpl.php:8 +#: www/jsScuttle.php:23 +msgid "Yes" +msgstr "Oui" + +#: data/templates/tag2tagdelete.tpl.php:16 +#: data/templates/tag2tagedit.tpl.php:17 data/templates/tagdelete.tpl.php:9 +#: www/jsScuttle.php:23 +msgid "No" +msgstr "Non" + +#: data/templates/tag2tagedit.tpl.php:6 msgid "Delete the link" msgstr "Supprimer le lien" -#: ../../../templates/tag2tagedit.tpl.php:29 +#: data/templates/tag2tagedit.tpl.php:29 msgid "Create new link" msgstr "Créer un nouveau lien" -#: ../../../templates/tagrename.tpl.php:12 +#: data/templates/tagrename.tpl.php:12 msgid "Old" msgstr "Ancien" -#: ../../../templates/tagrename.tpl.php:17 +#: data/templates/tagrename.tpl.php:17 msgid "New" msgstr "Nouveau" -#: ../../../templates/tagrename.tpl.php:24 +#: data/templates/tagrename.tpl.php:24 msgid "Rename" msgstr "Renommer" -#: ../../../templates/tags.tpl.php:11 -#: ../../../templates/users.tpl.php:9 +#: data/templates/tags.tpl.php:11 data/templates/users.tpl.php:9 msgid "Alphabet" msgstr "Alphabet" -#: ../../../templates/tags.tpl.php:12 -#: ../../../templates/users.tpl.php:10 +#: data/templates/tags.tpl.php:12 data/templates/users.tpl.php:10 msgid "Popularity" msgstr "Popularité" -#: ../../../templates/toolbar.inc.php:15 +#: data/templates/toolbar.inc.php:8 data/templates/toolbar.inc.php:26 +#, fuzzy +msgid "Home" +msgstr "Page personnelle" + +#: data/templates/toolbar.inc.php:11 www/watchlist.php:106 +#, fuzzy +msgid "Watchlist" +msgstr "Liste des signets vus" + +#: data/templates/toolbar.inc.php:12 www/profile.php:67 +msgid "Profile" +msgstr "Profil" + +#: data/templates/toolbar.inc.php:13 www/bookmarks.php:209 +msgid "Add a Bookmark" +msgstr "Ajouter un signet" + +#: data/templates/toolbar.inc.php:14 msgid "Log Out" msgstr "Quitter" -#: ../../../templates/users.tpl.php:17 +#: data/templates/toolbar.inc.php:17 +msgid "Admin" +msgstr "" + +#: data/templates/top.inc.php:49 +msgid "" +"Admins, your installation is in \"Debug Mode\" ($debugMode = true). To go in " +"\"Normal Mode\" and hide debugging messages, change $debugMode to false into " +"config.php." +msgstr "" + +#: data/templates/users.tpl.php:17 msgid "profile" msgstr "Profil" -#: ../../../templates/users.tpl.php:17 +#: data/templates/users.tpl.php:17 msgid "created in" msgstr "Créé en " +#: www/admin.php:32 +msgid "Manage users" +msgstr "" + +#: www/admin.php:68 +#, php-format +msgid "%s and all his bookmarks and tags were deleted." +msgstr "" + +#: www/admin.php:75 +msgid "Problem with " +msgstr "" + +#: www/ajaxDelete.php:37 +msgid "You are not allowed to delete this bookmark" +msgstr "Vous n'êtes pas autorisés à supprimer ce signet" + +#: www/ajaxDelete.php:41 www/edit.php:103 +msgid "Failed to delete bookmark" +msgstr "Erreur dans la suppression du signet" + +#: www/alltags.php:50 +msgid "All Tags" +msgstr "Tous les mots-clés" + +#: www/alltags.php:56 www/bookmarks.php:96 www/populartags.php:52 +#: www/profile.php:51 www/rss.php:76 www/search.php:109 www/watch.php:45 +#: www/watchlist.php:61 +#, php-format +msgid "User with username %s was not found" +msgstr "L'utilisateur %s n'a pas été trouvé." + +#: www/bookmarkcommondescriptionedit.php:51 www/tag2tagadd.php:37 +#: www/tag2tagdelete.php:41 www/tag2tagedit.php:33 +#: www/tagcommondescriptionedit.php:51 www/tagedit.php:43 +msgid "Permission denied." +msgstr "Permission non accordée." + +#: www/bookmarkcommondescriptionedit.php:60 +msgid "Bookmark common description updated" +msgstr "Description commune du signet mise à jour." + +#: www/bookmarkcommondescriptionedit.php:63 +msgid "Failed to update the bookmark common description" +msgstr "Erreur dans la mise à jour de la description du signet" + +#: www/bookmarkcommondescriptionedit.php:71 +msgid "Edit Bookmark Common Description" +msgstr "Editer la description commune du signet" + +#: www/bookmarks.php:111 www/tags.php:47 +#, fuzzy +msgid "Remove the tag from the selection" +msgstr "Voir tous les mots-clés de cet utilisateur" + +#: www/bookmarks.php:131 www/edit.php:65 +msgid "Your bookmark must have a title and an address" +msgstr "Votre signet doit avoir un titre et une adresse." + +#: www/bookmarks.php:152 www/edit.php:83 www/edit.php:86 +msgid "Bookmark saved" +msgstr "Signet enregistré." + +#: www/bookmarks.php:152 +msgid "(Come back to previous page.)" +msgstr "" + +#: www/bookmarks.php:159 www/import.php:106 www/importNetscape.php:108 +msgid "" +"There was an error saving your bookmark. Please try again or contact the " +"administrator." +msgstr "" +"Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou " +"contacter votre administrateur." + +#: www/bookmarks.php:216 +msgid "Add Bookmark" +msgstr "Ajouter signet" + +#: www/bookmarks.php:219 +msgid "You must be logged in before you can add bookmarks." +msgstr "Vous devez être authentifié avant de pouvoir ajouter des signets." + +#: www/bookmarks.php:274 www/bookmarks.php:275 +msgid "My Bookmarks" +msgstr "Mes signets" + +#: www/edit.php:44 www/edit.php:45 +msgid "Edit Bookmark" +msgstr "Editer le signet" + +#: www/edit.php:51 +#, php-format +msgid "Bookmark with id %s not was not found" +msgstr "Signet %s non trouvé" + +#: www/edit.php:57 +msgid "You are not allowed to edit this bookmark" +msgstr "Vous n'êtes pas autorisé à éditer ce signet." + +#: www/edit.php:77 +msgid "Error while saving your bookmark" +msgstr "Erreur pendant l'enregistrement de votre signet." + +#: www/history.php:61 +msgid "History" +msgstr "Historique" + +#: www/history.php:62 +#, php-format +msgid "History for %s" +msgstr "Historique de %s" + +#: www/history.php:84 +msgid "Address was not found" +msgstr "L'adresse n'a pas été trouvée." + +#: www/import.php:47 +msgid "Could not open XML input" +msgstr "Impossible d'ouvrir le flux XML." + +#: www/import.php:51 +#, php-format +msgid "XML error: %s at line %d" +msgstr "Erreur XML: %s à la ligne %d" + +#: www/import.php:60 +msgid "Import Bookmarks from del.icio.us" +msgstr "Importer les signet depuis del.icio.us" + +#: www/import.php:93 +msgid "You have already submitted this bookmark." +msgstr "Vous avez déjà enregistré ce signet." + +#: www/import.php:104 +msgid "Bookmark imported." +msgstr "Signet importé." + +#: www/importNetscape.php:95 +#, fuzzy +msgid "You have already submitted some of these bookmarks." +msgstr "Vous avez déjà enregistré ce signet." + +#: www/importNetscape.php:115 +#, fuzzy +msgid "Bookmarks found: " +msgstr "Pas de signets trouvés" + +#: www/importNetscape.php:116 +#, fuzzy +msgid "Bookmarks imported: " +msgstr "Signet importé." + +#: www/importNetscape.php:117 www/importNetscape.php:122 +msgid "Import Bookmarks from Browser File" +msgstr "Importer les signets depuis un fichier" + +#: www/importStructure.php:61 +msgid "Bad indentation" +msgstr "" + +#: www/importStructure.php:67 +#, fuzzy +msgid "New links between tags: " +msgstr "Effacer un lien entre mots-clés" + +#: www/importStructure.php:72 +msgid "Import Structure" +msgstr "" + +#: www/index.php:38 +msgid "You have now logged out" +msgstr "Vous êtes maintenant déconnecté." + +#: www/index.php:45 +#, php-format +msgid "%s: Recent bookmarks" +msgstr "%s: Signets récents" + +#: www/index.php:75 +msgid "Store, share and tag your favourite links" +msgstr "Conservez, partagez et libellez vos liens favoris" + +#: www/index.php:76 +msgid "All Bookmarks" +msgstr "Tous les signets" + +#: www/jsScuttle.php:70 +msgid "Available" +msgstr "Disponible" + +#: www/jsScuttle.php:73 +msgid "Not Available" +msgstr "Non Disponible" + +#: www/login.php:48 +msgid "The details you have entered are incorrect. Please try again." +msgstr "" +"Les informations que vous avez entrées sont incorrectes. Veuillez " +"recommencer." + +#: www/password.php:36 +msgid "You must enter your username." +msgstr "Vous devez entrer votre nom d'utilisateur." + +#: www/password.php:40 +msgid "" +"You must enter your e-mail address." +msgstr "" +"Vous devez saisir une E-mail." + +#: www/password.php:48 +msgid "No matches found for that username." +msgstr "Rien de trouvé pour ce nom d'utilisateur." + +#: www/password.php:51 +#, fuzzy +msgid "" +"No matches found for that combination of username and e-mail address." +msgstr "" +"Nous n'avons rien trouvé pour cette combinaison de nom d'utilisateur et " +"d'e-mail." + +#: www/password.php:59 +msgid "" +"There was an error while generating your new password. Please try again." +msgstr "" +"Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou " +"contacter votre administrateur." + +#: www/password.php:63 +msgid "Your new password is:" +msgstr "Votre nouveau mot de passe est:" + +#: www/password.php:63 +msgid "" +"To keep your bookmarks secure, you should change this password in your " +"profile the next time you log in." +msgstr "" +"Pour garder vos signets sûrs, vous devriez changer ce mot de passe dans " +"votre profil lors de votre prochaine authentification." + +#: www/password.php:66 +#, php-format +msgid "%s Account Information" +msgstr "Informations du compte de %s" + +#: www/password.php:68 +#, php-format +msgid "New password generated and sent to %s" +msgstr "Nouveau mot de passe généré et envoyé à l'adresse %s" + +#: www/password.php:75 +msgid "Forgotten Password" +msgstr "Mot de passe oublié" + +#: www/profile.php:59 www/watchlist.php:119 +msgid "Username was not specified" +msgstr "Le nom d'utilisateur n'a pas été spécifié." + +#: www/profile.php:65 +msgid "My Profile" +msgstr "Mon Profil" + +#: www/profile.php:89 +msgid "Invalid Token" +msgstr "" + +#: www/profile.php:94 +msgid "Password and confirmation do not match." +msgstr "Le mot de passe et sa vérification ne correspondent pas." + +#: www/profile.php:98 +msgid "Password must be at least 6 characters long." +msgstr "Le mot de passe doit avoir au moins 6 caractères." + +#: www/profile.php:102 +msgid "E-mail address is not valid." +msgstr "Adresse de courrier électronique invalide." + +#: www/profile.php:106 +msgid "An error occurred while saving your changes." +msgstr "" +"Une erreur s'est produite pendant l'enregistrement de vos modifications." + +#: www/profile.php:108 +msgid "Changes saved." +msgstr "Modifications enregistrées." + +#: www/register.php:46 +msgid "You must enter a username, password and e-mail address." +msgstr "" +"Vous devez saisir un nom d'utilisateur, un mot de passe, un nom et " +"un e-mail" + +#: www/register.php:50 +msgid "This username has been reserved, please make another choice." +msgstr "Ce nom d'utilisateur existe déjà, veuillez en choisir un autre." + +#: www/register.php:54 +msgid "This username already exists, please make another choice." +msgstr "Ce nom d'utilisateur existe déjà, veuillez en choisir un autre." + +#: www/register.php:58 +#, fuzzy +msgid "" +"This username is not valid (too short, too long, forbidden characters...), " +"please make another choice." +msgstr "Ce nom d'utilisateur existe déjà, veuillez en choisir un autre." + +#: www/register.php:62 +msgid "E-mail address is not valid. Please try again." +msgstr "Adresse de courrier électronique invalide. Veuilez réessayer." + +#: www/register.php:66 +msgid "Antispam answer is not valid. Please try again." +msgstr "La réponse antispam n'est pas valide. Veuillez réessayer." + +#: www/register.php:75 +msgid "You have successfully registered. Enjoy!" +msgstr "Votre inscription a bien été prise en compte !" + +#: www/register.php:77 +msgid "Registration failed. Please try again." +msgstr "Enregistrement raté. Veuillez rééssayer." + +#: www/rss.php:93 +#, php-format +msgid "Recent bookmarks posted to %s" +msgstr "Signets ajoutés récemment à %s" + +#: www/search.php:81 www/search.php:145 +msgid "Search Bookmarks" +msgstr "Recherche de signets" + +#: www/search.php:87 +msgid "Search Results" +msgstr "Résultats de recherche" + +#: www/search.php:135 +msgid "Unsatisfied? You can also try our " +msgstr "" + +#: www/tag2tagadd.php:50 +msgid "Tag link created" +msgstr "Lien entre mot-clé créé." + +#: www/tag2tagadd.php:53 +msgid "Failed to create the link" +msgstr "Impossible de créer le lien" + +#: www/tag2tagadd.php:65 +msgid "Add Tag Link" +msgstr "Ajout d'un lien entre mots-clés" + +#: www/tag2tagdelete.php:66 +msgid "Tag link deleted" +msgstr "Effacement d'un lien entre mots-clés" + +#: www/tag2tagdelete.php:69 +msgid "Failed to delete the link" +msgstr "Impossible d'effacer le lien" + +#: www/tag2tagdelete.php:81 +msgid "Delete Link Between Tags" +msgstr "Effacer un lien entre mots-clés" + +#: www/tag2tagedit.php:55 +msgid "Edit Link Between Tags" +msgstr "Editer un lien entre mots-clés" + +#: www/tagcommondescriptionedit.php:62 +msgid "Tag common description updated" +msgstr "Editer la description commune du mot-clé" + +#: www/tagcommondescriptionedit.php:67 +msgid "Failed to update the tag common description" +msgstr "Impossible de mettre à jour la description commune du mot-clé" + +#: www/tagdelete.php:43 +msgid "Tag deleted" +msgstr "Mot-clé effacé" + +#: www/tagdelete.php:46 +msgid "Failed to delete the tag" +msgstr "Impossible d'effacer le mot-clé" + +#: www/tagedit.php:52 +msgid "Tag description updated" +msgstr "Description du mot-clé mise à jour" + +#: www/tagedit.php:55 +msgid "Failed to update the tag description" +msgstr "Impossible de mettre à jour la description du mot-clé" + +#: www/tagrename.php:63 +msgid "Tag renamed" +msgstr "Mot-clé renommé" + +#: www/tagrename.php:66 +msgid "Failed to rename the tag" +msgstr "Erreur dans le renommage du mot-clé" + +#: www/users.php:31 +msgid "Users" +msgstr "Utilisateurs" + +#: www/watch.php:54 +msgid "User removed from your watchlist" +msgstr "Utilisateur enlevé de votre liste des consultés" + +#: www/watch.php:56 +msgid "User added to your watchlist" +msgstr "Utilisateur ajouté à la liste des consultés." + +#: www/watchlist.php:104 +#, fuzzy +msgid "My Watchlist" +msgstr "Liste des signets vus" + +#: www/api/httpauth.inc.php:11 +msgid "Use of the API calls requires authentication." +msgstr "" + +#: www/gsearch/index.php:27 +msgid "Come back to " +msgstr "" + +#: www/gsearch/index.php:32 +msgid "Admin tips: " +msgstr "" + +#: www/gsearch/index.php:33 +msgid "To refresh manually Google Custom Search Engine, goes to: " +msgstr "" + +#: www/gsearch/index.php:35 +msgid "" +"If no result appears, check that all the urls are valid in the admin section." +msgstr "" + +#~ msgid "for" +#~ msgstr "pour" + +#~ msgid "URL" +#~ msgstr "URL" + +#~ msgid "to" +#~ msgstr "dans" + +#~ msgid "Last Users" +#~ msgstr "Derniers utilisateurs" + #~ msgid "Recent Bookmarks" #~ msgstr "Signets récents" + #~ msgid "plus" #~ msgstr "plus" + #~ msgid "" #~ "Register now to start using " #~ "%s!" #~ msgstr "" #~ "Enregistrez-vous maintenant pour poster vos " #~ "propres signets sur %s !" + #~ msgid "" #~ "Semantic " #~ "Scuttle is licensed under the , sous license GNU General Public " #~ "License (vous pouvez donc l'héberger sur votre propre serveur)." + #~ msgid "edit" #~ msgstr "éditer" + #~ msgid "User with username %s not was not found" #~ msgstr "L'utilisateur %s n'a pas été trouvé." + #~ msgid "%s Bookmarks" #~ msgstr "Signets de %s" + #~ msgid "E-mail" #~ msgstr "E-mail" -#~ msgid "No bookmarks found" -#~ msgstr "Pas de signets trouvés" diff --git a/data/locales/fr_FR/LC_MESSAGES/messages.po b/data/locales/fr_FR/LC_MESSAGES/messages.po index 396c85e..0aa8be1 100644 --- a/data/locales/fr_FR/LC_MESSAGES/messages.po +++ b/data/locales/fr_FR/LC_MESSAGES/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Scuttle\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-11-16 20:55+0100\n" +"POT-Creation-Date: 2010-09-15 19:15+0200\n" "PO-Revision-Date: 2009-11-16 21:14+0100\n" "Last-Translator: BenjaminHKB \n" "Language-Team: fr-FR \n" @@ -19,64 +19,76 @@ msgstr "" "X-Poedit-Country: FRANCE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/SemanticScuttle/functions.php:163 +#: src/SemanticScuttle/functions.php:189 msgid "message_die() was called multiple times." msgstr "message_die() was called multiple times. ?" -#: src/SemanticScuttle/functions.php:175 +#: src/SemanticScuttle/functions.php:201 msgid "SQL Error" msgstr "Erreur SQL" -#: src/SemanticScuttle/functions.php:181 +#: src/SemanticScuttle/functions.php:207 msgid "Line" msgstr "Ligne" -#: src/SemanticScuttle/functions.php:181 +#: src/SemanticScuttle/functions.php:207 #: data/templates/importDelicious.tpl.php:8 #: data/templates/importNetscape.tpl.php:9 #: data/templates/importStructure.tpl.php:10 msgid "File" msgstr "Fichier" -#: src/SemanticScuttle/functions.php:189 +#: src/SemanticScuttle/functions.php:215 msgid "Information" msgstr "Information" -#: src/SemanticScuttle/functions.php:194 +#: src/SemanticScuttle/functions.php:220 msgid "Critical Information" msgstr "Information critique." -#: src/SemanticScuttle/functions.php:199 +#: src/SemanticScuttle/functions.php:225 msgid "An error occured" msgstr "Une erreur s'est produite." -#: src/SemanticScuttle/functions.php:202 +#: src/SemanticScuttle/functions.php:228 msgid "General Error" msgstr "Erreur générale." -#: src/SemanticScuttle/functions.php:210 +#: src/SemanticScuttle/functions.php:236 msgid "An critical error occured" msgstr "Une erreur critique s'est produite." -#: src/SemanticScuttle/functions.php:213 +#: src/SemanticScuttle/functions.php:239 msgid "Critical Error" msgstr "Erreur critique." -#: src/SemanticScuttle/functions.php:222 +#: src/SemanticScuttle/functions.php:248 msgid "DEBUG MODE" msgstr "Mode de débogage." #: data/templates/about.tpl.php:6 -msgid "Store all your favourite links in one place, accessible from anywhere." -msgstr "Conservez tous vos signets au même endroit, accessibles de partout. " +msgid "" +"Store all your favourite links in one place, accessible " +"from anywhere." +msgstr "" +"Conservez tous vos signets au même endroit, accessibles de " +"partout. " #: data/templates/about.tpl.php:7 -msgid "Share your bookmarks with everyone, with friends on your watchlist or just keep them private." -msgstr "Partagez vos signets avec tout le monde, avec vos contacts ou gardez-les pour vous." +msgid "" +"Share your bookmarks with everyone, with friends on your " +"watchlist or just keep them private." +msgstr "" +"Partagez vos signets avec tout le monde, avec vos contacts " +"ou gardez-les pour vous." #: data/templates/about.tpl.php:8 -msgid "Tag your bookmarks with as many labels as you want, instead of wrestling with folders." -msgstr "Taggez vos signets avec autant de labels que vous le souhaitez au lieu de les hiérarchiser avec des dossiers." +msgid "" +"Tag your bookmarks with as many labels as you want, instead " +"of wrestling with folders." +msgstr "" +"Taggez vos signets avec autant de labels que vous le " +"souhaitez au lieu de les hiérarchiser avec des dossiers." #: data/templates/about.tpl.php:9 msgid "Register now" @@ -101,8 +113,15 @@ msgstr "vous pouvez librement l'installer sur votre serveur Web" #: data/templates/about.tpl.php:15 #, php-format -msgid "%1$s supports most of the del.icio.us API. Almost all of the neat tools made for that system can be modified to work with %1$s instead. If you find a tool that won't let you change the API address, ask the creator to add this setting. You never know, they might just do it." -msgstr "%1$s supporte la plupart de l'API del.icio.us." +msgid "" +"%1$s supports most of the del.icio.us " +"API. Almost all " +"of the neat tools made for that system can be modified to work with %1$s " +"instead. If you find a tool that won't let you change the API address, ask " +"the creator to add this setting. You never know, they might just do it." +msgstr "" +"%1$s supporte la plupart de l'API del.icio.us." #: data/templates/about.tpl.php:24 msgid "Tips" @@ -113,12 +132,18 @@ msgid "Add search plugin into your browser:" msgstr "Ajouter un plugin de recherche à votre navigateur :" #: data/templates/about.tpl.php:27 -msgid "The secret tag \"system:unfiled\" allows you to find bookmarks without tags." -msgstr "Le tag secret \"system:unfiled\" vous permet de trouver les signets sans tags." +msgid "" +"The secret tag \"system:unfiled\" allows you to find bookmarks without tags." +msgstr "" +"Le tag secret \"system:unfiled\" vous permet de trouver les signets sans " +"tags." #: data/templates/about.tpl.php:28 -msgid "The secret tag \"system:imported\" allows you to find imported bookmarks." -msgstr "Le tag secret \"system:imported\" vous permet de trouver les signets importés." +msgid "" +"The secret tag \"system:imported\" allows you to find imported bookmarks." +msgstr "" +"Le tag secret \"system:imported\" vous permet de trouver les signets " +"importés." #: data/templates/admin.tpl.php:5 msgid "Users management" @@ -128,23 +153,18 @@ msgstr "Gestion des utilisateurs" msgid "Public/Shared/Private" msgstr "Publique/Partagé/Privé" -#: data/templates/admin.tpl.php:14 -#: data/templates/bookmarks.tpl.php:93 +#: data/templates/admin.tpl.php:14 data/templates/bookmarks.tpl.php:93 msgid "bookmark(s)" msgstr "signet(s)" -#: data/templates/admin.tpl.php:19 -#: data/templates/tag2tagadd.tpl.php:21 +#: data/templates/admin.tpl.php:19 data/templates/tag2tagadd.tpl.php:21 #: data/templates/tag2tagdelete.tpl.php:13 -#: data/templates/tag2tagedit.tpl.php:14 -#: data/templates/tag2tagedit.tpl.php:35 -#: data/templates/tagdelete.tpl.php:6 -#: www/jsScuttle.php:23 +#: data/templates/tag2tagedit.tpl.php:14 data/templates/tag2tagedit.tpl.php:35 +#: data/templates/tagdelete.tpl.php:6 www/jsScuttle.php:23 msgid "Are you sure?" msgstr "Etes-vous sûr ?" -#: data/templates/admin.tpl.php:19 -#: data/templates/bookmarks.tpl.php:251 +#: data/templates/admin.tpl.php:19 data/templates/bookmarks.tpl.php:267 msgid "Delete" msgstr "Supprimer" @@ -157,19 +177,21 @@ msgid "Check all URLs (May take some time)" msgstr "Vérifier toutes les URLs (Peut prendre du temps)" #: data/templates/bookmarkcommondescriptionedit.tpl.php:16 -msgid "Collaborative description: these fields can be viewed and modified by every users" -msgstr "Description collaborative : ces champs peuvent être vus et modifiés par tous les utilisateurs." +msgid "" +"Collaborative description: these fields can be viewed and modified by every " +"users" +msgstr "" +"Description collaborative : ces champs peuvent être vus et modifiés par tous " +"les utilisateurs." #: data/templates/bookmarkcommondescriptionedit.tpl.php:18 -#: data/templates/bookmarks.tpl.php:137 -#: data/templates/editbookmark.tpl.php:38 +#: data/templates/bookmarks.tpl.php:137 data/templates/editbookmark.tpl.php:43 msgid "Title" msgstr "Titre" #: data/templates/bookmarkcommondescriptionedit.tpl.php:23 -#: data/templates/editbookmark.tpl.php:44 -#: data/templates/editprofile.tpl.php:47 -#: data/templates/profile.tpl.php:33 +#: data/templates/editbookmark.tpl.php:49 +#: data/templates/editprofile.tpl.php:47 data/templates/profile.tpl.php:33 #: data/templates/tagcommondescriptionedit.tpl.php:13 #: data/templates/tagedit.tpl.php:12 msgid "Description" @@ -187,12 +209,10 @@ msgid "Update" msgstr "Mettre à jour" #: data/templates/bookmarkcommondescriptionedit.tpl.php:43 -#: data/templates/editbookmark.tpl.php:98 -#: data/templates/tag2tagadd.tpl.php:24 -#: data/templates/tag2tagedit.tpl.php:38 +#: data/templates/editbookmark.tpl.php:103 +#: data/templates/tag2tagadd.tpl.php:24 data/templates/tag2tagedit.tpl.php:38 #: data/templates/tagcommondescriptionedit.tpl.php:33 -#: data/templates/tagedit.tpl.php:19 -#: data/templates/tagrename.tpl.php:25 +#: data/templates/tagedit.tpl.php:19 data/templates/tagrename.tpl.php:25 msgid "Cancel" msgstr "Annuler" @@ -215,23 +235,19 @@ msgstr "Vote contre" msgid "Bookmarks on this page are managed by an admin user." msgstr "Les signets de cette page sont gérés par un administrateur." -#: data/templates/bookmarks.tpl.php:51 -#: data/templates/bookmarks.tpl.php:52 +#: data/templates/bookmarks.tpl.php:51 data/templates/bookmarks.tpl.php:52 msgid "Edit the common description of this tag" msgstr "Editer la description commune de ce tag" -#: data/templates/bookmarks.tpl.php:55 -#: data/templates/bookmarks.tpl.php:56 +#: data/templates/bookmarks.tpl.php:55 data/templates/bookmarks.tpl.php:56 msgid "Edit the common description of this bookmark" msgstr "Editer la description commune de ce signet" -#: data/templates/bookmarks.tpl.php:76 -#: data/templates/bookmarks.tpl.php:77 +#: data/templates/bookmarks.tpl.php:76 data/templates/bookmarks.tpl.php:77 msgid "Edit your personal description of this tag" msgstr "Editer votre description personnelle de ce tag" -#: data/templates/bookmarks.tpl.php:93 -#: data/templates/tags.tpl.php:10 +#: data/templates/bookmarks.tpl.php:93 data/templates/tags.tpl.php:10 #: data/templates/users.tpl.php:8 msgid "Sort by:" msgstr "Classer par :" @@ -252,23 +268,19 @@ msgstr "Signets des autres utilisateurs pour ce tag" msgid "Only your bookmarks for this tag" msgstr "Uniquement vos signets pour ce tag" -#: data/templates/bookmarks.tpl.php:177 -#: data/templates/bookmarks.tpl.php:183 +#: data/templates/bookmarks.tpl.php:177 data/templates/bookmarks.tpl.php:183 msgid "First" msgstr "Première" -#: data/templates/bookmarks.tpl.php:178 -#: data/templates/bookmarks.tpl.php:184 +#: data/templates/bookmarks.tpl.php:178 data/templates/bookmarks.tpl.php:184 msgid "Previous" msgstr "Précédent" -#: data/templates/bookmarks.tpl.php:191 -#: data/templates/bookmarks.tpl.php:194 +#: data/templates/bookmarks.tpl.php:191 data/templates/bookmarks.tpl.php:194 msgid "Next" msgstr "Suivant" -#: data/templates/bookmarks.tpl.php:192 -#: data/templates/bookmarks.tpl.php:195 +#: data/templates/bookmarks.tpl.php:192 data/templates/bookmarks.tpl.php:195 msgid "Last" msgstr "Dernière" @@ -277,69 +289,66 @@ msgstr "Dernière" msgid "Page %d of %d" msgstr "Page %d de %d" -#: data/templates/bookmarks.tpl.php:245 +#: data/templates/bookmarks.tpl.php:261 msgid "Tags:" msgstr "Tags:" -#: data/templates/bookmarks.tpl.php:251 +#: data/templates/bookmarks.tpl.php:267 msgid "Edit" msgstr "Editer" -#: data/templates/bookmarks.tpl.php:255 +#: data/templates/bookmarks.tpl.php:271 msgid "Last update" msgstr "Date de dernière mise à jour" -#: data/templates/bookmarks.tpl.php:258 +#: data/templates/bookmarks.tpl.php:274 msgid "by" msgstr "par" -#: data/templates/bookmarks.tpl.php:260 +#: data/templates/bookmarks.tpl.php:276 msgid "you" msgstr "vous" -#: data/templates/bookmarks.tpl.php:274 +#: data/templates/bookmarks.tpl.php:290 #, php-format msgid " and %s1 other%s" msgstr " et %s1 autre%s" -#: data/templates/bookmarks.tpl.php:277 +#: data/templates/bookmarks.tpl.php:293 #, php-format msgid " and %2$s%1$s others%3$s" msgstr " et %2$s%1$s autres%3$s" -#: data/templates/bookmarks.tpl.php:288 +#: data/templates/bookmarks.tpl.php:304 msgid "Copy this bookmark to YOUR bookmarks." msgstr "Copier ce signet dans VOS signets." -#: data/templates/bookmarks.tpl.php:289 +#: data/templates/bookmarks.tpl.php:305 msgid "Copy" msgstr "Copier" -#: data/templates/bookmarks.tpl.php:309 +#: data/templates/bookmarks.tpl.php:325 msgid "This bookmark is certified by an admin user." msgstr "Ce signet est certifié par un administrateur." -#: data/templates/bookmarks.tpl.php:351 +#: data/templates/bookmarks.tpl.php:371 msgid "Private Note on this bookmark" msgstr "Note privée sur ce signet" -#: data/templates/bookmarks.tpl.php:363 +#: data/templates/bookmarks.tpl.php:383 msgid "Come back to the top of this page." msgstr "Revenir en haut de cette page." -#: data/templates/bookmarks.tpl.php:363 +#: data/templates/bookmarks.tpl.php:383 msgid "Top of the page" msgstr "Haut de page" -#: data/templates/bookmarks.tpl.php:369 +#: data/templates/bookmarks.tpl.php:389 msgid "No bookmarks available" msgstr "Pas de signets disponibles." -#: data/templates/bottom.inc.php:5 -#: data/templates/toolbar.inc.php:15 -#: data/templates/toolbar.inc.php:28 -#: www/about.php:23 -#: www/about.php:24 +#: data/templates/bottom.inc.php:5 data/templates/toolbar.inc.php:15 +#: data/templates/toolbar.inc.php:28 www/about.php:23 www/about.php:24 msgid "About" msgstr "À propos" @@ -350,8 +359,7 @@ msgstr "Propulsé par " #: data/templates/dynamictags.inc.php:47 #: data/templates/sidebar.block.common.php:19 #: data/templates/sidebar.block.popular.php:34 -#: data/templates/sidebar.block.recent.php:29 -#: data/templates/tags.tpl.php:19 +#: data/templates/sidebar.block.recent.php:29 data/templates/tags.tpl.php:19 msgid "bookmark" msgid_plural "bookmarks" msgstr[0] "signet" @@ -362,8 +370,7 @@ msgstr[1] "Signets" #: data/templates/sidebar.block.menu.php:74 #: data/templates/sidebar.block.popular.php:23 #: data/templates/sidebar.block.recent.php:34 -#: data/templates/toolbar.inc.php:27 -#: www/populartags.php:46 +#: data/templates/toolbar.inc.php:27 www/populartags.php:46 msgid "Popular Tags" msgstr "Tags populaires" @@ -371,131 +378,143 @@ msgstr "Tags populaires" msgid "Popular Tags From All Users" msgstr "Tags populaires pour tous les utilisateurs" -#: data/templates/editbookmark.tpl.php:33 +#: data/templates/editbookmark.tpl.php:38 msgid "Address" msgstr "Adresse" -#: data/templates/editbookmark.tpl.php:35 #: data/templates/editbookmark.tpl.php:40 -#: data/templates/editprofile.tpl.php:31 -#: data/templates/tagrename.tpl.php:14 +#: data/templates/editbookmark.tpl.php:45 +#: data/templates/editprofile.tpl.php:31 data/templates/tagrename.tpl.php:14 #: data/templates/tagrename.tpl.php:19 msgid "Required" msgstr "Requis" -#: data/templates/editbookmark.tpl.php:45 +#: data/templates/editbookmark.tpl.php:50 msgid "Add Note" msgstr "Ajouter une note" -#: data/templates/editbookmark.tpl.php:48 -msgid "You can use anchors to delimite attributes. for example: [publisher]blah[/publisher] " -msgstr "Vous pouvez utiliser des balises pour délimiter des attributs. Par exemple : [publisher]blah[/publisher]" +#: data/templates/editbookmark.tpl.php:53 +msgid "" +"You can use anchors to delimite attributes. for example: [publisher]blah[/" +"publisher] " +msgstr "" +"Vous pouvez utiliser des balises pour délimiter des attributs. Par exemple : " +"[publisher]blah[/publisher]" -#: data/templates/editbookmark.tpl.php:51 +#: data/templates/editbookmark.tpl.php:56 msgid "Suggested anchors: " msgstr "Balises suggérées : " -#: data/templates/editbookmark.tpl.php:63 +#: data/templates/editbookmark.tpl.php:68 msgid "Private Note" msgstr "Note privée" -#: data/templates/editbookmark.tpl.php:65 +#: data/templates/editbookmark.tpl.php:70 msgid "Just visible by you and your contacts." msgstr "Visible uniquement par vous et vos contacts." -#: data/templates/editbookmark.tpl.php:69 -#: data/templates/toolbar.inc.php:10 -#: www/tags.php:45 -#: www/tags.php:67 +#: data/templates/editbookmark.tpl.php:74 data/templates/toolbar.inc.php:10 +#: www/tags.php:45 www/tags.php:67 msgid "Tags" msgstr "Tags" -#: data/templates/editbookmark.tpl.php:73 +#: data/templates/editbookmark.tpl.php:78 msgid "Comma-separated" msgstr "Séparés par des virgules" -#: data/templates/editbookmark.tpl.php:77 -#: data/templates/tag2tagadd.tpl.php:9 -msgid "Note: use \">\" to include one tag in another. e.g.: europe>france>paris" -msgstr "Note: utiliser \">\" pour inclure un tag dans un autre. ex: europe>france>paris" +#: data/templates/editbookmark.tpl.php:82 data/templates/tag2tagadd.tpl.php:9 +msgid "" +"Note: use \">\" to include one tag in another. e.g.: europe>france>paris" +msgstr "" +"Note: utiliser \">\" pour inclure un tag dans un autre. ex: " +"europe>france>paris" -#: data/templates/editbookmark.tpl.php:81 -#: data/templates/tag2tagadd.tpl.php:8 +#: data/templates/editbookmark.tpl.php:86 data/templates/tag2tagadd.tpl.php:8 msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry" msgstr "Note : utiliser \"=\" pour rendre deux tags synonymes ex: europe=eu" -#: data/templates/editbookmark.tpl.php:84 +#: data/templates/editbookmark.tpl.php:89 #: data/templates/importDelicious.tpl.php:15 #: data/templates/importNetscape.tpl.php:16 msgid "Privacy" msgstr "Accès" -#: data/templates/editbookmark.tpl.php:87 +#: data/templates/editbookmark.tpl.php:92 #: data/templates/importDelicious.tpl.php:18 #: data/templates/importNetscape.tpl.php:19 msgid "Public" msgstr "Publique" -#: data/templates/editbookmark.tpl.php:88 +#: data/templates/editbookmark.tpl.php:93 msgid "Shared with Watch List" msgstr "Partagé avec mes contacts" -#: data/templates/editbookmark.tpl.php:89 +#: data/templates/editbookmark.tpl.php:94 #: data/templates/importDelicious.tpl.php:20 #: data/templates/importNetscape.tpl.php:21 msgid "Private" msgstr "Privée" -#: data/templates/editbookmark.tpl.php:102 +#: data/templates/editbookmark.tpl.php:107 msgid "Delete Bookmark" msgstr "Supprimer le signet" -#: data/templates/editbookmark.tpl.php:107 +#: data/templates/editbookmark.tpl.php:112 msgid "edit common description" msgstr "éditer la description commune" -#: data/templates/editbookmark.tpl.php:134 +#: data/templates/editbookmark.tpl.php:139 msgid "Bookmarklet" msgstr "Bookmarklet" -#: data/templates/editbookmark.tpl.php:140 +#: data/templates/editbookmark.tpl.php:145 #, php-format -msgid "Click one of the following bookmarklets to add a button you can click whenever you want to add the page you are on to %s" -msgstr "Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un signet pour la page courante dans %s " +msgid "" +"Click one of the following bookmarklets to add a button you can click " +"whenever you want to add the page you are on to %s" +msgstr "" +"Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre " +"navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un " +"signet pour la page courante dans %s " -#: data/templates/editbookmark.tpl.php:144 +#: data/templates/editbookmark.tpl.php:149 #, php-format -msgid "Drag one of the following bookmarklets to your browser's bookmarks and click it whenever you want to add the page you are on to %s" -msgstr "Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un signet pour la page courante dans %s " +msgid "" +"Drag one of the following bookmarklets to your browser's bookmarks and click " +"it whenever you want to add the page you are on to %s" +msgstr "" +"Déplacez l'un des 'bookmarklet' suivants dans les marques-pages de votre " +"navigateur et cliquez dessus chaque fois que vous souhaitez ajouter un " +"signet pour la page courante dans %s " -#: data/templates/editbookmark.tpl.php:157 #: data/templates/editbookmark.tpl.php:162 +#: data/templates/editbookmark.tpl.php:167 #, php-format msgid "Post to %s" msgstr "Ajouter à %s" -#: data/templates/editbookmark.tpl.php:158 #: data/templates/editbookmark.tpl.php:163 +#: data/templates/editbookmark.tpl.php:168 #, php-format msgid "Post to %s (Pop-up)" msgstr "Ajouter à %s (Pop-up)" -#: data/templates/editbookmark.tpl.php:168 +#: data/templates/editbookmark.tpl.php:173 #: data/templates/importDelicious.tpl.php:26 #: data/templates/importNetscape.tpl.php:27 #: data/templates/importStructure.tpl.php:16 msgid "Import" msgstr "Importer" -#: data/templates/editbookmark.tpl.php:170 +#: data/templates/editbookmark.tpl.php:175 msgid "Import bookmarks from bookmark file" msgstr "Importer les signets depuis un fichier" -#: data/templates/editbookmark.tpl.php:170 +#: data/templates/editbookmark.tpl.php:175 msgid "Internet Explorer, Mozilla Firefox and Netscape" msgstr "Internet Explorer, Mozilla Firefox et Netscape" -#: data/templates/editbookmark.tpl.php:171 +#: data/templates/editbookmark.tpl.php:176 msgid "Import bookmarks from del.icio.us" msgstr "Importer les signets depuis del.icio.us" @@ -503,10 +522,8 @@ msgstr "Importer les signets depuis del.icio.us" msgid "Account Details" msgstr "Détail du compte" -#: data/templates/editprofile.tpl.php:14 -#: data/templates/login.tpl.php:21 -#: data/templates/password.tpl.php:10 -#: data/templates/profile.tpl.php:6 +#: data/templates/editprofile.tpl.php:14 data/templates/login.tpl.php:21 +#: data/templates/password.tpl.php:10 data/templates/profile.tpl.php:6 #: data/templates/register.tpl.php:16 msgid "Username" msgstr "Nom d'utilisateur" @@ -519,8 +536,7 @@ msgstr "Nouveau mot de passe" msgid "Confirm Password" msgstr "Confirmer le mot de passe" -#: data/templates/editprofile.tpl.php:29 -#: data/templates/password.tpl.php:14 +#: data/templates/editprofile.tpl.php:29 data/templates/password.tpl.php:14 #: data/templates/register.tpl.php:26 msgid "E-mail" msgstr "E-mail" @@ -529,18 +545,15 @@ msgstr "E-mail" msgid "Personal Details" msgstr "Détails personnels" -#: data/templates/editprofile.tpl.php:39 -#: data/templates/profile.tpl.php:17 +#: data/templates/editprofile.tpl.php:39 data/templates/profile.tpl.php:17 msgid "Name" msgstr "Nom" -#: data/templates/editprofile.tpl.php:43 -#: data/templates/profile.tpl.php:23 +#: data/templates/editprofile.tpl.php:43 data/templates/profile.tpl.php:23 msgid "Homepage" msgstr "Page personnelle" -#: data/templates/editprofile.tpl.php:52 -#: www/edit.php:113 +#: data/templates/editprofile.tpl.php:52 www/edit.php:113 msgid "Save Changes" msgstr "Enregistrer les modifications" @@ -594,16 +607,28 @@ msgid "Instructions" msgstr "Instructions" #: data/templates/importDelicious.tpl.php:33 -msgid "Log in to the export page at del.icio.us" -msgstr "Se connecter à la page d'export de del.icio.us" +msgid "" +"Log in to the export page at " +"del.icio.us" +msgstr "" +"Se connecter à la page d'export " +"de del.icio.us" #: data/templates/importDelicious.tpl.php:34 -msgid "Save the resulting XML file to your computer" -msgstr "Enregistrer le fichier XML résultant sur votre ordinateur" +msgid "" +"Save the resulting XML " +"file to your computer" +msgstr "" +"Enregistrer le fichier XML " +"résultant sur votre ordinateur" #: data/templates/importDelicious.tpl.php:35 -msgid "Click Browse... to find this file on your computer. The maximum size the file can be is 1MB" -msgstr "Cliquez sur Parcourir... pour trouver le fichier sur votre ordinateur. La taille maximale du fichier ne peut excèder 1Mo" +msgid "" +"Click Browse... to find this file on your computer. The maximum " +"size the file can be is 1MB" +msgstr "" +"Cliquez sur Parcourir... pour trouver le fichier sur votre " +"ordinateur. La taille maximale du fichier ne peut excèder 1Mo" #: data/templates/importDelicious.tpl.php:36 #: data/templates/importNetscape.tpl.php:43 @@ -612,43 +637,62 @@ msgstr "Selectionnez la vision par défaut à appliquer à vos signets importés #: data/templates/importDelicious.tpl.php:37 #: data/templates/importNetscape.tpl.php:44 -msgid "Click Import to start importing the bookmarks; it may take a minute" -msgstr "Cliquez sur Importer pour débuter l'import des signets; cette opération peut prendre quelques minutes" +msgid "" +"Click Import to start importing the bookmarks; it may take a " +"minute" +msgstr "" +"Cliquez sur Importer pour débuter l'import des signets; cette " +"opération peut prendre quelques minutes" #: data/templates/importNetscape.tpl.php:35 msgid "Export your bookmarks from your browser to a file" msgstr "Exporter vos signets dans un fichier depuis votre navigateur" #: data/templates/importNetscape.tpl.php:37 -msgid "Internet Explorer: File > Import and Export... > Export Favorites" -msgstr "Internet Explorer: Ficher > Importer et Exporter... > Exporter les favoris" +msgid "" +"Internet Explorer: File > Import and Export... > Export Favorites" +msgstr "" +"Internet Explorer: Ficher > Importer et Exporter... > Exporter " +"les favoris" #: data/templates/importNetscape.tpl.php:38 -msgid "Mozilla Firefox: Bookmarks > Manage Bookmarks... > File > Export..." -msgstr "Mozilla Firefox: Marques-pages > Gérer les marques-pages... > Fichier > Exporter..." +msgid "" +"Mozilla Firefox: Bookmarks > Manage Bookmarks... > File > " +"Export..." +msgstr "" +"Mozilla Firefox: Marques-pages > Gérer les marques-pages... > " +"Fichier > Exporter..." #: data/templates/importNetscape.tpl.php:39 -msgid "Netscape: Bookmarks > Manage Bookmarks... > Tools > Export..." -msgstr "Netscape: Marques-pages > Gérer les marques-pages... > Outils > Exporter..." +msgid "" +"Netscape: Bookmarks > Manage Bookmarks... > Tools > Export..." +msgstr "" +"Netscape: Marques-pages > Gérer les marques-pages... > Outils " +"> Exporter..." #: data/templates/importNetscape.tpl.php:42 -msgid "Click Browse... to find the saved bookmark file on your computer. The maximum size the file can be is 1MB" -msgstr "Cliquez sur Parcourir... pour trouver le fichier sur votre ordinateur. La taille maximale du fichier ne peut excèder 1Mo" +msgid "" +"Click Browse... to find the saved bookmark file on your computer. " +"The maximum size the file can be is 1MB" +msgstr "" +"Cliquez sur Parcourir... pour trouver le fichier sur votre " +"ordinateur. La taille maximale du fichier ne peut excèder 1Mo" #: data/templates/importStructure.tpl.php:24 msgid "Create your structure into a simple text file and following this model:" msgstr "" #: data/templates/importStructure.tpl.php:35 -msgid "Then import the file. The tags and their relations will be added to your profile." +msgid "" +"Then import the file. The tags and their relations will be added to your " +"profile." msgstr "" #: data/templates/login.tpl.php:13 msgid "Please activate cookies" msgstr "SVP activez les cookies" -#: data/templates/login.tpl.php:26 -#: data/templates/register.tpl.php:21 +#: data/templates/login.tpl.php:26 data/templates/register.tpl.php:21 msgid "Password" msgstr "Mot de passe" @@ -656,8 +700,7 @@ msgstr "Mot de passe" msgid "Don't ask for my password for 2 weeks" msgstr "Ne pas demander mon mot de passe pendant 2 semaines" -#: data/templates/login.tpl.php:32 -#: data/templates/toolbar.inc.php:29 +#: data/templates/login.tpl.php:32 data/templates/toolbar.inc.php:29 #: www/login.php:57 msgid "Log In" msgstr "Se connecter" @@ -668,8 +711,14 @@ msgstr "Avez-vous oublié votre mot de passe ?" #: data/templates/password.tpl.php:5 #, php-format -msgid "If you have forgotten your password, %s can generate a new one. Enter the username and e-mail address of your account into the form below and we will e-mail your new password to you." -msgstr "Si vous avez oublié votre mot de passe, %s peut en générer un nouveau. Entrez le nom d'utilisateur et l'adresse email de votre compte dans le formulaire ci-dessous et nous vous enverrons un nouveau mot de passe." +msgid "" +"If you have forgotten your password, %s can generate a new one. Enter the " +"username and e-mail address of your account into the form below and we will " +"e-mail your new password to you." +msgstr "" +"Si vous avez oublié votre mot de passe, %s peut en générer un nouveau. " +"Entrez le nom d'utilisateur et l'adresse email de votre compte dans le " +"formulaire ci-dessous et nous vous enverrons un nouveau mot de passe." #: data/templates/password.tpl.php:19 msgid "Generate Password" @@ -693,8 +742,7 @@ msgstr "Mes contacts" msgid "Watched By" msgstr "Dans les contacts de" -#: data/templates/profile.tpl.php:68 -#: data/templates/toolbar.inc.php:9 +#: data/templates/profile.tpl.php:68 data/templates/toolbar.inc.php:9 msgid "Bookmarks" msgstr "Signets" @@ -704,12 +752,20 @@ msgstr "Aller aux signets" #: data/templates/register.tpl.php:11 #, php-format -msgid "Sign up here to create a free %s account. All the information requested below is required" -msgstr "Enregistrez-vous ici pour créer un compte gratuit %s. Toutes les informations requises ci-dessous sont nécessaires." +msgid "" +"Sign up here to create a free %s account. All the information requested " +"below is required" +msgstr "" +"Enregistrez-vous ici pour créer un compte gratuit %s. Toutes les " +"informations requises ci-dessous sont nécessaires." #: data/templates/register.tpl.php:18 -msgid " at least 5 characters, alphanumeric (no spaces, no dots or other special ones)" -msgstr " au moins 5 caractères, alphanumériques (pas d'espaces, pas de points ou autre caractère spécial)" +msgid "" +" at least 5 characters, alphanumeric (no spaces, no dots or other special " +"ones)" +msgstr "" +" au moins 5 caractères, alphanumériques (pas d'espaces, pas de points ou " +"autre caractère spécial)" #: data/templates/register.tpl.php:28 msgid " to send you your password if you forget it" @@ -719,8 +775,7 @@ msgstr " pour vous envoyer votre mot de passe en cas de perte" msgid "Antispam question" msgstr "Question antispam" -#: data/templates/register.tpl.php:41 -#: data/templates/toolbar.inc.php:31 +#: data/templates/register.tpl.php:41 data/templates/toolbar.inc.php:31 #: www/register.php:83 msgid "Register" msgstr "S'enregistrer" @@ -802,7 +857,8 @@ msgstr "Tags Principaux" #: data/templates/sidebar.block.menu2.php:29 msgid "This menu is composed of keywords (tags) organized by admins." -msgstr "Ce menu est composé de mots-clefs (tags) organisés par les administrateurs." +msgstr "" +"Ce menu est composé de mots-clefs (tags) organisés par les administrateurs." #: data/templates/sidebar.block.recent.php:18 msgid "Recent Tags" @@ -820,25 +876,22 @@ msgstr "Dernières recherches" msgid "Number of bookmarks for this query" msgstr "Nombre de signets pour cette recherche" -#: data/templates/sidebar.block.tagactions.php:9 -#: www/tagrename.php:72 +#: data/templates/sidebar.block.tagactions.php:9 www/tagrename.php:72 msgid "Rename Tag" msgid_plural "Rename Tags" msgstr[0] "Renommer le tag" msgstr[1] "Renommer les tags" -#: data/templates/sidebar.block.tagactions.php:22 -#: www/tagdelete.php:54 +#: data/templates/sidebar.block.tagactions.php:22 www/tagdelete.php:54 msgid "Delete Tag" msgstr "Supprimer le tag" -#: data/templates/sidebar.block.tagactions.php:24 -#: www/tagedit.php:61 +#: data/templates/sidebar.block.tagactions.php:24 www/tagedit.php:61 msgid "Edit Tag Description" msgstr "Editer la description du tag" #: data/templates/sidebar.block.tagactions.php:26 -#: www/tagcommondescriptionedit.php:64 +#: www/tagcommondescriptionedit.php:76 msgid "Edit Tag Common Description" msgstr "Editer la description commune du tag" @@ -850,8 +903,7 @@ msgstr "Créer un lien vers un autre tag" msgid "New Users" msgstr "Nouveaux Utilisateurs" -#: data/templates/sidebar.block.users.php:23 -#: data/templates/users.tpl.php:17 +#: data/templates/sidebar.block.users.php:23 data/templates/users.tpl.php:17 msgid "bookmarks" msgstr "signets" @@ -905,11 +957,14 @@ msgstr "Créer un nouveau lien" #: data/templates/tag2tagadd.tpl.php:19 #, php-format -msgid "Note: include a tag into '%s' tag (e.g. %s>countries) display the tag into the menu box" -msgstr "Note : inclure un tag dans le tag '%s' (e.g. %s>countries) affiche ce tag dans la boîte de menu" +msgid "" +"Note: include a tag into '%s' tag (e.g. %s>countries) display the tag into " +"the menu box" +msgstr "" +"Note : inclure un tag dans le tag '%s' (e.g. %s>countries) affiche ce tag " +"dans la boîte de menu" -#: data/templates/tag2tagadd.tpl.php:23 -#: data/templates/tag2tagedit.tpl.php:37 +#: data/templates/tag2tagadd.tpl.php:23 data/templates/tag2tagedit.tpl.php:37 msgid "Create" msgstr "Créer" @@ -926,15 +981,13 @@ msgid "No links" msgstr "Pas de liens" #: data/templates/tag2tagdelete.tpl.php:15 -#: data/templates/tag2tagedit.tpl.php:16 -#: data/templates/tagdelete.tpl.php:8 +#: data/templates/tag2tagedit.tpl.php:16 data/templates/tagdelete.tpl.php:8 #: www/jsScuttle.php:23 msgid "Yes" msgstr "Oui" #: data/templates/tag2tagdelete.tpl.php:16 -#: data/templates/tag2tagedit.tpl.php:17 -#: data/templates/tagdelete.tpl.php:9 +#: data/templates/tag2tagedit.tpl.php:17 data/templates/tagdelete.tpl.php:9 #: www/jsScuttle.php:23 msgid "No" msgstr "Non" @@ -959,33 +1012,27 @@ msgstr "Nouveau" msgid "Rename" msgstr "Renommer" -#: data/templates/tags.tpl.php:11 -#: data/templates/users.tpl.php:9 +#: data/templates/tags.tpl.php:11 data/templates/users.tpl.php:9 msgid "Alphabet" msgstr "Alphabet" -#: data/templates/tags.tpl.php:12 -#: data/templates/users.tpl.php:10 +#: data/templates/tags.tpl.php:12 data/templates/users.tpl.php:10 msgid "Popularity" msgstr "Popularité" -#: data/templates/toolbar.inc.php:8 -#: data/templates/toolbar.inc.php:26 +#: data/templates/toolbar.inc.php:8 data/templates/toolbar.inc.php:26 msgid "Home" msgstr "Accueil" -#: data/templates/toolbar.inc.php:11 -#: www/watchlist.php:106 +#: data/templates/toolbar.inc.php:11 www/watchlist.php:106 msgid "Watchlist" msgstr "Contacts" -#: data/templates/toolbar.inc.php:12 -#: www/profile.php:67 +#: data/templates/toolbar.inc.php:12 www/profile.php:67 msgid "Profile" msgstr "Profil" -#: data/templates/toolbar.inc.php:13 -#: www/bookmarks.php:209 +#: data/templates/toolbar.inc.php:13 www/bookmarks.php:209 msgid "Add a Bookmark" msgstr "Ajouter un signet" @@ -998,8 +1045,14 @@ msgid "Admin" msgstr "Admin" #: data/templates/top.inc.php:49 -msgid "Admins, your installation is in \"Debug Mode\" ($debugMode = true). To go in \"Normal Mode\" and hide debugging messages, change $debugMode to false into config.php." -msgstr "Admins, votre installation est en \"Mode Debug\" ($debugMode = true). Pour passer en \"Mode Normal\" et cacher les messages de debuggage, changez $debugMode à false dans config.inc.php." +msgid "" +"Admins, your installation is in \"Debug Mode\" ($debugMode = true). To go in " +"\"Normal Mode\" and hide debugging messages, change $debugMode to false into " +"config.php." +msgstr "" +"Admins, votre installation est en \"Mode Debug\" ($debugMode = true). Pour " +"passer en \"Mode Normal\" et cacher les messages de debuggage, changez " +"$debugMode à false dans config.inc.php." #: data/templates/users.tpl.php:17 msgid "profile" @@ -1026,33 +1079,24 @@ msgstr "Problème avec " msgid "You are not allowed to delete this bookmark" msgstr "Vous n'êtes pas autorisés à supprimer ce signet" -#: www/ajaxDelete.php:41 -#: www/edit.php:103 +#: www/ajaxDelete.php:41 www/edit.php:103 msgid "Failed to delete bookmark" msgstr "Erreur dans la suppression du signet" -#: www/alltags.php:49 +#: www/alltags.php:50 msgid "All Tags" msgstr "Tous les tags" -#: www/alltags.php:55 -#: www/bookmarks.php:96 -#: www/populartags.php:52 -#: www/profile.php:51 -#: www/rss.php:67 -#: www/search.php:109 -#: www/watch.php:45 +#: www/alltags.php:56 www/bookmarks.php:96 www/populartags.php:52 +#: www/profile.php:51 www/rss.php:76 www/search.php:109 www/watch.php:45 #: www/watchlist.php:61 #, php-format msgid "User with username %s was not found" msgstr "L'utilisateur %s n'a pas été trouvé." -#: www/bookmarkcommondescriptionedit.php:51 -#: www/tag2tagadd.php:37 -#: www/tag2tagdelete.php:41 -#: www/tag2tagedit.php:33 -#: www/tagcommondescriptionedit.php:43 -#: www/tagedit.php:43 +#: www/bookmarkcommondescriptionedit.php:51 www/tag2tagadd.php:37 +#: www/tag2tagdelete.php:41 www/tag2tagedit.php:33 +#: www/tagcommondescriptionedit.php:51 www/tagedit.php:43 msgid "Permission denied." msgstr "Permission non accordée." @@ -1068,19 +1112,15 @@ msgstr "Erreur dans la mise à jour de la description du signet" msgid "Edit Bookmark Common Description" msgstr "Editer la description commune du signet" -#: www/bookmarks.php:111 -#: www/tags.php:47 +#: www/bookmarks.php:111 www/tags.php:47 msgid "Remove the tag from the selection" msgstr "Retirer le tag de la sélection" -#: www/bookmarks.php:131 -#: www/edit.php:65 +#: www/bookmarks.php:131 www/edit.php:65 msgid "Your bookmark must have a title and an address" msgstr "Votre signet doit avoir un titre et une adresse." -#: www/bookmarks.php:152 -#: www/edit.php:83 -#: www/edit.php:86 +#: www/bookmarks.php:152 www/edit.php:83 www/edit.php:86 msgid "Bookmark saved" msgstr "Signet enregistré." @@ -1088,11 +1128,13 @@ msgstr "Signet enregistré." msgid "(Come back to previous page.)" msgstr "(Revenir à la page précédente.)" -#: www/bookmarks.php:159 -#: www/import.php:106 -#: www/importNetscape.php:108 -msgid "There was an error saving your bookmark. Please try again or contact the administrator." -msgstr "Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou contacter votre administrateur." +#: www/bookmarks.php:159 www/import.php:106 www/importNetscape.php:108 +msgid "" +"There was an error saving your bookmark. Please try again or contact the " +"administrator." +msgstr "" +"Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou " +"contacter votre administrateur." #: www/bookmarks.php:216 msgid "Add Bookmark" @@ -1102,13 +1144,11 @@ msgstr "Ajouter signet" msgid "You must be logged in before you can add bookmarks." msgstr "Vous devez être authentifié avant de pouvoir ajouter des signets." -#: www/bookmarks.php:274 -#: www/bookmarks.php:275 +#: www/bookmarks.php:274 www/bookmarks.php:275 msgid "My Bookmarks" msgstr "Mes signets" -#: www/edit.php:44 -#: www/edit.php:45 +#: www/edit.php:44 www/edit.php:45 msgid "Edit Bookmark" msgstr "Editer le signet" @@ -1171,8 +1211,7 @@ msgstr "Signets trouvés :" msgid "Bookmarks imported: " msgstr "Signets importés :" -#: www/importNetscape.php:117 -#: www/importNetscape.php:122 +#: www/importNetscape.php:117 www/importNetscape.php:122 msgid "Import Bookmarks from Browser File" msgstr "Importer les signets depuis un fichier" @@ -1197,11 +1236,11 @@ msgstr "Vous êtes maintenant déconnecté." msgid "%s: Recent bookmarks" msgstr "%s: Signets récents" -#: www/index.php:78 +#: www/index.php:75 msgid "Store, share and tag your favourite links" msgstr "Conservez, partagez et taggez vos liens favoris" -#: www/index.php:79 +#: www/index.php:76 msgid "All Bookmarks" msgstr "Tous les signets" @@ -1215,35 +1254,51 @@ msgstr "Non Disponible" #: www/login.php:48 msgid "The details you have entered are incorrect. Please try again." -msgstr "Les informations que vous avez entrées sont incorrectes. Veuillez recommencer." +msgstr "" +"Les informations que vous avez entrées sont incorrectes. Veuillez " +"recommencer." #: www/password.php:36 msgid "You must enter your username." msgstr "Vous devez entrer votre nom d'utilisateur." #: www/password.php:40 -msgid "You must enter your e-mail address." -msgstr "Vous devez saisir une E-mail." +msgid "" +"You must enter your e-mail address." +msgstr "" +"Vous devez saisir une E-mail." #: www/password.php:48 msgid "No matches found for that username." msgstr "Rien de trouvé pour ce nom d'utilisateur." #: www/password.php:51 -msgid "No matches found for that combination of username and e-mail address." -msgstr "Pas d'entrée pour ce nom d'utilisateur et cet e-mail." +msgid "" +"No matches found for that combination of username and e-mail address." +msgstr "" +"Pas d'entrée pour ce nom d'utilisateur et cet e-" +"mail." #: www/password.php:59 -msgid "There was an error while generating your new password. Please try again." -msgstr "Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou contacter votre administrateur." +msgid "" +"There was an error while generating your new password. Please try again." +msgstr "" +"Il y a eu une erreur en enregistrant ce signet. Veuillez rééssayer ou " +"contacter votre administrateur." #: www/password.php:63 msgid "Your new password is:" msgstr "Votre nouveau mot de passe est:" #: www/password.php:63 -msgid "To keep your bookmarks secure, you should change this password in your profile the next time you log in." -msgstr "Pour garder vos signets sûrs, vous devriez changer ce mot de passe dans votre profil lors de votre prochaine authentification." +msgid "" +"To keep your bookmarks secure, you should change this password in your " +"profile the next time you log in." +msgstr "" +"Pour garder vos signets sûrs, vous devriez changer ce mot de passe dans " +"votre profil lors de votre prochaine authentification." #: www/password.php:66 #, php-format @@ -1259,8 +1314,7 @@ msgstr "Nouveau mot de passe généré et envoyé à l'adresse %s" msgid "Forgotten Password" msgstr "Mot de passe oublié" -#: www/profile.php:59 -#: www/watchlist.php:119 +#: www/profile.php:59 www/watchlist.php:119 msgid "Username was not specified" msgstr "Le nom d'utilisateur n'a pas été spécifié." @@ -1286,7 +1340,8 @@ msgstr "Adresse de courrier électronique invalide." #: www/profile.php:106 msgid "An error occurred while saving your changes." -msgstr "Une erreur s'est produite pendant l'enregistrement de vos modifications." +msgstr "" +"Une erreur s'est produite pendant l'enregistrement de vos modifications." #: www/profile.php:108 msgid "Changes saved." @@ -1294,7 +1349,9 @@ msgstr "Modifications enregistrées." #: www/register.php:46 msgid "You must enter a username, password and e-mail address." -msgstr "Vous devez saisir un nom d'utilisateur, un mot de passe, un nom et un e-mail" +msgstr "" +"Vous devez saisir un nom d'utilisateur, un mot de passe, un nom et " +"un e-mail" #: www/register.php:50 msgid "This username has been reserved, please make another choice." @@ -1305,8 +1362,12 @@ msgid "This username already exists, please make another choice." msgstr "Ce nom d'utilisateur existe déjà, veuillez en choisir un autre." #: www/register.php:58 -msgid "This username is not valid (too short, too long, forbidden characters...), please make another choice." -msgstr "Ce nom d'utilisateur n'est pas valide (trop court, trop long, caractères interdits...), Merci de faire un autre choix." +msgid "" +"This username is not valid (too short, too long, forbidden characters...), " +"please make another choice." +msgstr "" +"Ce nom d'utilisateur n'est pas valide (trop court, trop long, caractères " +"interdits...), Merci de faire un autre choix." #: www/register.php:62 msgid "E-mail address is not valid. Please try again." @@ -1324,13 +1385,12 @@ msgstr "Votre inscription a bien été prise en compte !" msgid "Registration failed. Please try again." msgstr "Enregistrement raté. Veuillez rééssayer." -#: www/rss.php:84 +#: www/rss.php:93 #, php-format msgid "Recent bookmarks posted to %s" msgstr "Signets ajoutés récemment à %s" -#: www/search.php:81 -#: www/search.php:145 +#: www/search.php:81 www/search.php:145 msgid "Search Bookmarks" msgstr "Recherche de signets" @@ -1370,11 +1430,11 @@ msgstr "Effacer un lien entre tags" msgid "Edit Link Between Tags" msgstr "Editer un lien entre tags" -#: www/tagcommondescriptionedit.php:55 +#: www/tagcommondescriptionedit.php:62 msgid "Tag common description updated" msgstr "Editer la description commune du tag" -#: www/tagcommondescriptionedit.php:58 +#: www/tagcommondescriptionedit.php:67 msgid "Failed to update the tag common description" msgstr "Impossible de mettre à jour la description commune du tag" @@ -1432,34 +1492,45 @@ msgstr "Conseil pour admin:" #: www/gsearch/index.php:33 msgid "To refresh manually Google Custom Search Engine, goes to: " -msgstr "Pour rafraîchir manuellement le moteur de Google Custom Search, allez à :" +msgstr "" +"Pour rafraîchir manuellement le moteur de Google Custom Search, allez à :" #: www/gsearch/index.php:35 -msgid "If no result appears, check that all the urls are valid in the admin section." -msgstr "Si aucun résultat apparaît, vérifier que toutes les urls sont valides dans la section admin." +msgid "" +"If no result appears, check that all the urls are valid in the admin section." +msgstr "" +"Si aucun résultat apparaît, vérifier que toutes les urls sont valides dans " +"la section admin." #~ msgid "Add this tag to the query" #~ msgstr "Ajouter ce tag à la requête" + #~ msgid "Last Users" #~ msgstr "Derniers utilisateurs" #, fuzzy #~ msgid "Watched by" #~ msgstr "Consultés" + #~ msgid "URL" #~ msgstr "URL" + #~ msgid "for" #~ msgstr "pour" + #~ msgid "Recent Bookmarks" #~ msgstr "Signets récents" + #~ msgid "plus" #~ msgstr "plus" + #~ msgid "" #~ "Register now to start using " #~ "%s!" #~ msgstr "" #~ "Enregistrez-vous maintenant pour poster vos " #~ "propres signets sur %s !" + #~ msgid "" #~ "Semantic " #~ "Scuttle is licensed under the , sous license GNU General Public " #~ "License (vous pouvez donc l'héberger sur votre propre serveur)." + #~ msgid "edit" #~ msgstr "éditer" + #~ msgid "User with username %s not was not found" #~ msgstr "L'utilisateur %s n'a pas été trouvé." + #~ msgid "%s Bookmarks" #~ msgstr "Signets de %s" + #~ msgid "E-mail" #~ msgstr "E-mail" - From e3f7ed95cd71de74f94c4333e475dee4a96805ea Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:33:15 +0000 Subject: [PATCH 028/173] implement patch 3059829: update FR_CA translation git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@743 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/locales/fr_CA/LC_MESSAGES/messages.po | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/data/locales/fr_CA/LC_MESSAGES/messages.po b/data/locales/fr_CA/LC_MESSAGES/messages.po index 41894e8..ae74c4b 100644 --- a/data/locales/fr_CA/LC_MESSAGES/messages.po +++ b/data/locales/fr_CA/LC_MESSAGES/messages.po @@ -234,7 +234,7 @@ msgstr "" #: data/templates/bookmarks.tpl.php:51 data/templates/bookmarks.tpl.php:52 #, fuzzy msgid "Edit the common description of this tag" -msgstr "éditer la description commune" +msgstr "Modifier la description public de ce mot-clé" #: data/templates/bookmarks.tpl.php:55 data/templates/bookmarks.tpl.php:56 #, fuzzy @@ -243,7 +243,7 @@ msgstr "éditer la description commune" #: data/templates/bookmarks.tpl.php:76 data/templates/bookmarks.tpl.php:77 msgid "Edit your personal description of this tag" -msgstr "" +msgstr "Modifier la description privée de ce mot-clé" #: data/templates/bookmarks.tpl.php:93 data/templates/tags.tpl.php:10 #: data/templates/users.tpl.php:8 @@ -306,7 +306,7 @@ msgstr "par" #: data/templates/bookmarks.tpl.php:276 msgid "you" -msgstr "" +msgstr "vous" #: data/templates/bookmarks.tpl.php:290 #, fuzzy, php-format @@ -581,7 +581,7 @@ msgstr "Fichier XML (comme del.icio.us)" #: data/templates/editprofile.tpl.php:62 msgid "CSV file (for spreadsheet tools)" -msgstr "" +msgstr "Fichier CSV (pour les classeurs)" #: data/templates/error.404.tpl.php:5 msgid "Not Found" @@ -742,13 +742,13 @@ msgstr "Membre depuis" #: data/templates/sidebar.block.watchlist.php:30 #, fuzzy msgid "Watching" -msgstr "Liste des signets vus" +msgstr "Éléments surveillés" #: data/templates/profile.tpl.php:55 #: data/templates/sidebar.block.watchlist.php:52 #, fuzzy msgid "Watched By" -msgstr "Consultés" +msgstr "Surveillé par" #: data/templates/profile.tpl.php:68 data/templates/toolbar.inc.php:9 msgid "Bookmarks" @@ -806,7 +806,7 @@ msgstr "mes signets" #: data/templates/search.menu.php:40 msgid "my watchlist" -msgstr "ma liste des consultés" +msgstr "ma liste de suivi" #: data/templates/search.menu.php:44 msgid "all bookmarks" @@ -1493,7 +1493,7 @@ msgstr "Utilisateur ajouté à la liste des consultés." #: www/watchlist.php:104 #, fuzzy msgid "My Watchlist" -msgstr "Liste des signets vus" +msgstr "Ma liste de suivi" #: www/api/httpauth.inc.php:11 msgid "Use of the API calls requires authentication." @@ -1522,8 +1522,8 @@ msgstr "" #~ msgid "URL" #~ msgstr "URL" -#~ msgid "to" -#~ msgstr "dans" +msgid "to" +msgstr "dans" #~ msgid "Last Users" #~ msgstr "Derniers utilisateurs" From ea4c9fa4f741007b575a9437a0c0a642d1398c1d Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:33:38 +0000 Subject: [PATCH 029/173] update changelog git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@744 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index b1248fd..52f60bc 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -5,6 +5,7 @@ ChangeLog for SemantiScuttle ------------------- - Fix bug getTagsForBookmarks() that fetched all tags - Show error message on mysqli connection errors +- Implement patch #3059829: update FR_CA translation 0.97.0 - 2010-06-09 From b17e8f940c8008b034e4db477f748a1f7e4eddb6 Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:34:05 +0000 Subject: [PATCH 030/173] Fix bug #3073215: Updating bookmark time does not work git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@745 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/ChangeLog | 1 + src/SemanticScuttle/Service/Bookmark.php | 2 +- tests/BookmarkTest.php | 32 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 52f60bc..07b1e49 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -6,6 +6,7 @@ ChangeLog for SemantiScuttle - Fix bug getTagsForBookmarks() that fetched all tags - Show error message on mysqli connection errors - Implement patch #3059829: update FR_CA translation +- Fix bug #3073215: Updating bookmark time does not work 0.97.0 - 2010-06-09 diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php index 364b1a0..d5a63a3 100644 --- a/src/SemanticScuttle/Service/Bookmark.php +++ b/src/SemanticScuttle/Service/Bookmark.php @@ -611,7 +611,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService if (!is_null($date)) { $datetime = gmdate('Y-m-d H:i:s', strtotime($date)); - $updates[] = array('bDateTime' => $datetime); + $updates['bDatetime'] = $datetime; } $sql = 'UPDATE '. $GLOBALS['tableprefix'] .'bookmarks SET '. $this->db->sql_build_array('UPDATE', $updates) .' WHERE bId = '. intval($bId); diff --git a/tests/BookmarkTest.php b/tests/BookmarkTest.php index 40b40e3..c836d5e 100644 --- a/tests/BookmarkTest.php +++ b/tests/BookmarkTest.php @@ -982,6 +982,38 @@ class BookmarkTest extends TestBase $this->assertEquals('newShortNambb', $bm['bShort']); } + /** + * Tests if updating a bookmark's date works. + * This once was a bug, see bug #3073215. + * + * @return void + * + * @link https://sourceforge.net/tracker/?func=detail&atid=1017430&aid=3073215&group_id=211356 + */ + public function testUpdateBookmarkDate() + { + $bid = $this->bs->addBookmark( + 'http://example.org', 'title', 'desc', 'priv', + 0, array(), 'myShortName' + ); + $bm = $this->bs->getBookmark($bid); + $this->assertEquals('myShortName', $bm['bShort']); + + $this->assertTrue( + $this->bs->updateBookmark( + $bid, 'http://example2.org', 'my title', 'desc', + 'priv', 0, array(), 'newShortNambb', + //we need to use zulu (GMT) time zone here + // since the dates/times are stored as that + // in the database + '2002-03-04T05:06:07Z' + ) + ); + $bm = $this->bs->getBookmark($bid); + $this->assertEquals('newShortNambb', $bm['bShort']); + $this->assertEquals('2002-03-04 05:06:07', $bm['bDatetime']); + } + /** From 6fb920199695b4b595ec15aad73039eac0feca60 Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:34:25 +0000 Subject: [PATCH 031/173] update description git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@746 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Model/User.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/SemanticScuttle/Model/User.php b/src/SemanticScuttle/Model/User.php index ed9f454..500f5b1 100644 --- a/src/SemanticScuttle/Model/User.php +++ b/src/SemanticScuttle/Model/User.php @@ -15,7 +15,7 @@ /** * SemanticScuttle user object. - * Rare fields are filled if required. + * Rarely used fields are filled if required. * * @category Bookmarking * @package SemanticScuttle @@ -133,7 +133,8 @@ class SemanticScuttle_Model_User } /** - * Returns user creation time + * Returns user creation time. + * UTC/Zulu time zone is used. * * @return string Datetime value: "YYYY-MM-DD HH:MM:SS" */ From 647a13499e5fa56f8de39504547cef8a2d89b8ff Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:34:47 +0000 Subject: [PATCH 032/173] Move user IP resolution into own class method git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@747 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Model/RemoteUser.php | 48 ++++++++++++++++++++++++ src/SemanticScuttle/Service/Bookmark.php | 21 ++--------- 2 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 src/SemanticScuttle/Model/RemoteUser.php diff --git a/src/SemanticScuttle/Model/RemoteUser.php b/src/SemanticScuttle/Model/RemoteUser.php new file mode 100644 index 0000000..6d48e3a --- /dev/null +++ b/src/SemanticScuttle/Model/RemoteUser.php @@ -0,0 +1,48 @@ + + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +/** + * Remote User helper methods. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Christian Weiske + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class SemanticScuttle_Model_RemoteUser +{ + /** + * Returns the remote user's IP. + * + * @return string IP address. NULL if not found. + */ + public static function getIp() + { + $ip = null; + if (getenv('REMOTE_ADDR')) { + $ip = getenv('REMOTE_ADDR'); + } else if (getenv('HTTP_CLIENT_IP')) { + $ip = getenv('HTTP_CLIENT_IP'); + } else if (getenv('HTTP_X_FORWARDED_FOR')) { + $ip = getenv('HTTP_X_FORWARDED_FOR'); + } + + return $ip; + } + +} + +?> \ No newline at end of file diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php index d5a63a3..4b51f35 100644 --- a/src/SemanticScuttle/Service/Bookmark.php +++ b/src/SemanticScuttle/Service/Bookmark.php @@ -12,6 +12,7 @@ * @license GPL http://www.gnu.org/licenses/gpl.html * @link http://sourceforge.net/projects/semanticscuttle */ +require_once 'SemanticScuttle/Model/RemoteUser.php'; /** * SemanticScuttle bookmark service. @@ -453,14 +454,6 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $address = $this->normalize($address); - if (getenv('HTTP_CLIENT_IP')) { - $ip = getenv('HTTP_CLIENT_IP'); - } else if (getenv('REMOTE_ADDR')) { - $ip = getenv('REMOTE_ADDR'); - } else { - $ip = getenv('HTTP_X_FORWARDED_FOR'); - } - /* * Note that if date is NULL, then it's added with a date and * time of now, and if it's present, @@ -480,7 +473,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService // Set up the SQL insert statement and execute it. $values = array( 'uId' => intval($sId), - 'bIp' => $ip, + 'bIp' => SemanticScuttle_Model_RemoteUser::getIp(), 'bDatetime' => $datetime, 'bModified' => $datetime, 'bTitle' => $title, @@ -570,15 +563,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService return false; } - // Get the client's IP address and the date; note that the date is in GMT. - if (getenv('HTTP_CLIENT_IP')) - $ip = getenv('HTTP_CLIENT_IP'); - else - if (getenv('REMOTE_ADDR')) - $ip = getenv('REMOTE_ADDR'); - else - $ip = getenv('HTTP_X_FORWARDED_FOR'); - + // Get the the date; note that the date is in GMT. $moddatetime = gmdate('Y-m-d H:i:s', time()); $address = $this->normalize($address); From a62dfd6d70b158646693e8331afa7f7624bf2dcc Mon Sep 17 00:00:00 2001 From: cweiske Date: Thu, 23 Sep 2010 07:35:11 +0000 Subject: [PATCH 033/173] Fix CS, fix docblocks git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@748 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/Bookmark.php | 111 +++++++++++++++++------ 1 file changed, 81 insertions(+), 30 deletions(-) diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php index 4b51f35..dde1df5 100644 --- a/src/SemanticScuttle/Service/Bookmark.php +++ b/src/SemanticScuttle/Service/Bookmark.php @@ -45,6 +45,13 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService + /** + * Creates a new instance. Initializes the table name. + * + * @param DB $db Database object + * + * @uses $GLOBALS['tableprefix'] + */ public function __construct($db) { $this->db = $db; @@ -169,7 +176,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService * Retrieves a bookmark with the given URL. * DOES NOT RESPECT PRIVACY SETTINGS! * - * @param string $hash URL + * @param string $address URL to get bookmarks for * * @return mixed Array with bookmark data or false in case * of an error (i.e. not found). @@ -574,7 +581,11 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService if ($bookmark['bAddress'] != $address && $this->bookmarkExists($address, $bookmark['uId']) ) { - message_die(GENERAL_ERROR, 'Could not update bookmark (URL already existing = '.$address.')', '', __LINE__, __FILE__); + message_die( + GENERAL_ERROR, + 'Could not update bookmark (URL already exists: ' . $address . ')', + '', __LINE__, __FILE__ + ); return false; } @@ -599,22 +610,30 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $updates['bDatetime'] = $datetime; } - $sql = 'UPDATE '. $GLOBALS['tableprefix'] .'bookmarks SET '. $this->db->sql_build_array('UPDATE', $updates) .' WHERE bId = '. intval($bId); + $sql = 'UPDATE '. $GLOBALS['tableprefix'] . 'bookmarks' + . ' SET '. $this->db->sql_build_array('UPDATE', $updates) + . ' WHERE bId = ' . intval($bId); $this->db->sql_transaction('begin'); if (!($dbresult = & $this->db->sql_query($sql))) { $this->db->sql_transaction('rollback'); - message_die(GENERAL_ERROR, 'Could not update bookmark', '', __LINE__, __FILE__, $sql, $this->db); + message_die( + GENERAL_ERROR, 'Could not update bookmark', + '', __LINE__, __FILE__, $sql, $this->db + ); } - $uriparts = explode('.', $address); + $uriparts = explode('.', $address); $extension = end($uriparts); unset($uriparts); $b2tservice = SemanticScuttle_Service_Factory :: get('Bookmark2Tag'); if (!$b2tservice->attachTags($bId, $categories, $fromApi, $extension)) { $this->db->sql_transaction('rollback'); - message_die(GENERAL_ERROR, 'Could not update bookmark', '', __LINE__, __FILE__, $sql, $this->db); + message_die( + GENERAL_ERROR, 'Could not update bookmark', + '', __LINE__, __FILE__, $sql, $this->db + ); } $this->db->sql_transaction('commit'); @@ -730,7 +749,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $arrWatch = $userservice->getWatchlist($user); if (count($arrWatch) > 0) { $query_3_1 = ''; - foreach($arrWatch as $row) { + foreach ($arrWatch as $row) { $query_3_1 .= 'B.uId = '. intval($row) .' OR '; } $query_3_1 = substr($query_3_1, 0, -3); @@ -741,7 +760,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService } $query_5 = ''; - if($hash == null) { + if ($hash == null) { $query_5.= ' GROUP BY B.bHash'; } @@ -789,7 +808,9 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $query_2 .= ', '. $b2tservice->getTableName() .' AS T'. $i; $query_4 .= ' AND ('; - $allLinkedTags = $tag2tagservice->getAllLinkedTags($this->db->sql_escape($tags[$i]), '>', $user); + $allLinkedTags = $tag2tagservice->getAllLinkedTags( + $this->db->sql_escape($tags[$i]), '>', $user + ); while (is_array($allLinkedTags) && count($allLinkedTags)>0) { $query_4 .= ' T'. $i .'.tag = "'. array_pop($allLinkedTags) .'"'; @@ -810,7 +831,8 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService // Search terms in tags as well when none given if (!count($tags)) { - $query_2 .= ' LEFT JOIN '. $b2tservice->getTableName() .' AS T ON B.bId = T.bId'; + $query_2 .= ' LEFT JOIN '. $b2tservice->getTableName() .' AS T' + . ' ON B.bId = T.bId'; $dotags = true; } else { $dotags = false; @@ -818,12 +840,24 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $query_4 = ''; for ($i = 0; $i < count($aTerms); $i++) { - $query_4 .= ' AND (B.bTitle LIKE "%'. $this->db->sql_escape($aTerms[$i]) .'%"'; - $query_4 .= ' OR B.bDescription LIKE "%'. $this->db->sql_escape($aTerms[$i]) .'%"'; - $query_4 .= ' OR B.bPrivateNote LIKE "'. $this->db->sql_escape($aTerms[$i]) .'%"'; //warning : search in private notes of everybody but private notes won't appear if not allowed. - $query_4 .= ' OR U.username = "'. $this->db->sql_escape($aTerms[$i]) .'"'; //exact match for username + $query_4 .= ' AND (B.bTitle LIKE "%' + . $this->db->sql_escape($aTerms[$i]) + . '%"'; + $query_4 .= ' OR B.bDescription LIKE "%' + . $this->db->sql_escape($aTerms[$i]) + . '%"'; + //warning : search in private notes of everybody + // but private notes won't appear if not allowed. + $query_4 .= ' OR B.bPrivateNote LIKE "' + . $this->db->sql_escape($aTerms[$i]) + .'%"'; + $query_4 .= ' OR U.username = "' + . $this->db->sql_escape($aTerms[$i]) + . '"'; //exact match for username if ($dotags) { - $query_4 .= ' OR T.tag LIKE "'. $this->db->sql_escape($aTerms[$i]) .'%"'; + $query_4 .= ' OR T.tag LIKE "' + . $this->db->sql_escape($aTerms[$i]) + . '%"'; } $query_4 .= ')'; } @@ -845,22 +879,35 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $query = $query_1 . $query_2 . $query_3 . $query_4 . $query_5; - if (!($dbresult = & $this->db->sql_query_limit($query, intval($perpage), intval($start)))) { - message_die(GENERAL_ERROR, 'Could not get bookmarks', '', __LINE__, __FILE__, $query, $this->db); + $dbresult = $this->db->sql_query_limit( + $query, intval($perpage), intval($start) + ); + if (!$dbresult) { + message_die( + GENERAL_ERROR, 'Could not get bookmarks', + '', __LINE__, __FILE__, $query, $this->db + ); } if (SQL_LAYER == 'mysql4') { $totalquery = 'SELECT FOUND_ROWS() AS total'; } else { if ($hash) { - $totalquery = 'SELECT COUNT(*) AS total'. $query_2 . $query_3 . $query_4; + $totalquery = 'SELECT COUNT(*) AS total'. $query_2 + . $query_3 . $query_4; } else { - $totalquery = 'SELECT COUNT(DISTINCT bAddress) AS total'. $query_2 . $query_3 . $query_4; + $totalquery = 'SELECT COUNT(DISTINCT bAddress) AS total' + . $query_2 . $query_3 . $query_4; } } - if (!($totalresult = & $this->db->sql_query($totalquery)) || (!($row = & $this->db->sql_fetchrow($totalresult)))) { - message_die(GENERAL_ERROR, 'Could not get total bookmarks', '', __LINE__, __FILE__, $totalquery, $this->db); + if (!($totalresult = $this->db->sql_query($totalquery)) + || (!($row = $this->db->sql_fetchrow($totalresult))) + ) { + message_die( + GENERAL_ERROR, 'Could not get total bookmarks', + '', __LINE__, __FILE__, $totalquery, $this->db + ); } $total = $row['total']; @@ -947,10 +994,14 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService */ public function deleteBookmarksForUser($uId) { - $query = 'DELETE FROM '. $GLOBALS['tableprefix'] .'bookmarks WHERE uId = '. intval($uId); + $query = 'DELETE FROM '. $GLOBALS['tableprefix'] . 'bookmarks' + . ' WHERE uId = '. intval($uId); - if (!($dbresult = & $this->db->sql_query($query))) { - message_die(GENERAL_ERROR, 'Could not delete bookmarks', '', __LINE__, __FILE__, $query, $this->db); + if (!($dbresult = $this->db->sql_query($query))) { + message_die( + GENERAL_ERROR, 'Could not delete bookmarks', + '', __LINE__, __FILE__, $query, $this->db + ); } return true; @@ -962,12 +1013,6 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService * Counts the number of bookmarks that have the same address * as the given address. * - * @internal - * We do support fetching counts for multiple addresses at once - * because that allows us to reduce the number of queries - * we need in the web interface when displaying i.e. - * 10 bookmarks - only one SQL query is needed then. - * * @param string|array $addresses Address/URL to look for, string * of one address or array with * multiple ones @@ -976,6 +1021,12 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService * In case $addresses was an array, key-value array * with key being the address, value said number of * bookmarks + * + * @internal + * We do support fetching counts for multiple addresses at once + * because that allows us to reduce the number of queries + * we need in the web interface when displaying i.e. + * 10 bookmarks - only one SQL query is needed then. */ public function countOthers($addresses) { From fbf28d0753338c23eaa2f44fd449911ef5bf1965 Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:05:20 +0000 Subject: [PATCH 034/173] give bookmarks.tpl.php nice looking generated html code git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@749 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/templates/bookmarks.tpl.php | 131 ++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 44 deletions(-) diff --git a/data/templates/bookmarks.tpl.php b/data/templates/bookmarks.tpl.php index a8299fd..2eed9c3 100644 --- a/data/templates/bookmarks.tpl.php +++ b/data/templates/bookmarks.tpl.php @@ -1,14 +1,30 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ /* Service creation: only useful services are created */ -$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark'); -$tagservice =SemanticScuttle_Service_Factory::get('Tag'); -$cdservice =SemanticScuttle_Service_Factory::get('CommonDescription'); +$bookmarkservice = SemanticScuttle_Service_Factory::get('Bookmark'); +$tagservice = SemanticScuttle_Service_Factory::get('Tag'); +$cdservice = SemanticScuttle_Service_Factory::get('CommonDescription'); -$pageName = isset($pageName)?$pageName:""; -$user = isset($user)?$user:""; -$currenttag = isset($currenttag)?$currenttag:""; +$pageName = isset($pageName) ? $pageName : ''; +$user = isset($user) ? $user : ''; +$currenttag = isset($currenttag) ? $currenttag : ''; $this->includeTemplate($GLOBALS['top_include']); @@ -132,23 +148,23 @@ default: break; } ?> - -/ - -/ + + / + + / - -/ + + / '; echo T_('Bookmarks from other users for this tag').''; //echo T_(' for these tags'); - } else if($userservice->isLoggedOn()){ + } else if ($userservice->isLoggedOn()){ echo ' - '; echo ''; echo T_('Only your bookmarks for this tag').''; @@ -213,10 +229,8 @@ if($currenttag!= '') { -
    0 ? ' start="'. ++$start .'"' : ''); ?> - id="bookmarks"> - - 0 ? ' start="'. ++$start .'"' : ''); ?> id="bookmarks"> + &$row) { $addresses[$row['bId']] = $row['bAddress']; @@ -253,35 +267,52 @@ if($currenttag!= '') { $tagsForCopy = ''; $tags = $row['tags']; foreach ($tags as $tkey => &$tag) { - $cats .= ', '; - $tagsForCopy.= $tag.','; + $tagcaturl = sprintf( + $cat_url, + filter($row['username'], 'url'), + filter($tag, 'url') + ); + $cats .= sprintf( + ', ', + $tagcaturl, filter($tag) + ); + $tagsForCopy .= $tag . ','; } $cats = substr($cats, 0, -2); if ($cats != '') { - $cats = ' '.T_('Tags:').' '. $cats; + $cats = T_('Tags:') . ' ' . $cats; } // Edit and delete links $edit = ''; if ($bookmarkservice->editAllowed($row)) { - $edit = ' - '. T_('Edit') .''; + $edit = ' - ' + . T_('Edit') + . '' + . ''; } // Last update - $update = ' ('. date($GLOBALS['shortdate'], strtotime($row['bModified'])). ') '; + $update = ' ('. date($GLOBALS['shortdate'], strtotime($row['bModified'])). ') '; // User attribution - $copy = ' '. T_('by'). ' '; - if($userservice->isLoggedOn() && $currentUser->getUsername() == $row['username']) { - $copy.= T_('you'); + $copy = ' ' . T_('by') . ' '; + if ($userservice->isLoggedOn() + && $currentUser->getUsername() == $row['username'] + ) { + $copy .= T_('you'); } else { - $copy.= ''. $row['username'] .''; + $copy .= '' + . $row['username'] . ''; } // Udders! if (!isset($hash)) { $others = $otherCounts[$row['bAddress']]; - $ostart = ''; + $ostart = ''; $oend = ''; switch ($others) { case 0: @@ -300,7 +331,10 @@ if($currenttag!= '') { && !$existence[$row['bAddress']] ) { $copy .= ' - ' . T_('Copy') . ''; @@ -321,11 +355,11 @@ if($currenttag!= '') { // Admin specific design if ($userservice->isAdmin($row['username']) && $GLOBALS['enableAdminColors']) { - $adminBgClass = 'class="adminBackground"'; - $adminStar = ' '; + $adminBgClass = ' class="adminBackground"'; + $adminStar = ' '; } else { $adminBgClass = ''; - $adminStar = ''; + $adminStar = ''; } // Private Note (just visible by the owner and his/her contacts) @@ -346,13 +380,16 @@ if($currenttag!= '') { } // Output - echo '
  1. '."\n"; + echo '
  2. '."\n"; include 'bookmarks-thumbnail.inc.tpl.php'; include 'bookmarks-vote.inc.tpl.php'; - echo '
    ';; + echo ' ' . "\n"; - echo '\n"; + echo ' \n"; if ($row['bDescription'] == '') { $bkDescription = $GLOBALS['blankDescription']; } else { @@ -362,17 +399,23 @@ if($currenttag!= '') { $bkDescription = preg_replace('@((http|https|ftp)://.*?)( |\r|$)@', '$1$3', $bkDescription); // make url clickable } - echo '
    '. nl2br($bkDescription) ."
    \n"; - //if(!isset($hash)) { - echo '
    ' . shortenString($oaddress) . '
    '; - //} + echo '
    '. nl2br($bkDescription) ."
    \n"; + echo '
    ' . shortenString($oaddress) . "
    \n"; - echo '
    '. $cats . $copy . $edit . $update ."
    \n"; - echo $privateNoteField!=''?'
    '.$privateNoteField."
    \n":''; + echo '
    ' + . $cats . "\n" + . $copy . "\n" + . $edit . "\n" + . $update . "\n" + . "
    \n"; + echo $privateNoteField != '' + ? '
    '.$privateNoteField."
    \n" + : ''; + echo ' '; include 'bookmarks-vote-horizontal.inc.tpl.php'; - echo '
    '; + echo " \n"; - echo "
  3. \n"; + echo " \n"; } ?> From 8b45ba9e75a5d911906e95c3980e214eff9e61b0 Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:06:31 +0000 Subject: [PATCH 035/173] escape the installation name; html code was broken when one had quotes in the name git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@750 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/templates/bookmarks.tpl.php | 5 +++- data/templates/sidebar.block.search.php | 37 ++++++++++++++++++------- data/templates/top.inc.php | 9 ++++-- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/data/templates/bookmarks.tpl.php b/data/templates/bookmarks.tpl.php index 2eed9c3..e32d3c9 100644 --- a/data/templates/bookmarks.tpl.php +++ b/data/templates/bookmarks.tpl.php @@ -215,7 +215,10 @@ if ($currenttag!= '') { $brss = ''; $size = count($rsschannels); for ($i = 0; $i < $size; $i++) { - $brss = ''. $rsschannels[$i][0] .''; + $brss = '' + . '' . htmlspecialchars($rsschannels[$i][0]) .'' + . ''; } $pagesBanner = '

    '. $bfirst .' / '. $bprev .' / '. $bnext .' / '. $blast .' / '. sprintf(T_('Page %d of %d'), $page, $totalpages) ." ". $brss ."

    \n"; diff --git a/data/templates/sidebar.block.search.php b/data/templates/sidebar.block.search.php index 64cb8ac..660ce00 100644 --- a/data/templates/sidebar.block.search.php +++ b/data/templates/sidebar.block.search.php @@ -1,13 +1,26 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ /* Service creation: only useful services are created */ -$searchhistoryservice =SemanticScuttle_Service_Factory::get('SearchHistory'); +$searchhistoryservice = SemanticScuttle_Service_Factory::get('SearchHistory'); -$logged_on_userid = $userservice->getCurrentUserId(); -if ($logged_on_userid === false) { - $logged_on_userid = NULL; -} - -$lastSearches = $searchhistoryservice->getAllSearches('all', NULL, 3, NULL, true, false); +$lastSearches = $searchhistoryservice->getAllSearches( + 'all', NULL, 3, NULL, true, false +); if ($lastSearches && count($lastSearches) > 0) { ?> @@ -18,11 +31,15 @@ if ($lastSearches && count($lastSearches) > 0) { '; - echo ''; + echo ''; echo $row['shTerms']; echo ''; - echo ' ('.$row['shNbResults'].')'; - echo ''; + echo ' (' . $row['shNbResults'] . ')'; + echo '' . "\n"; } //echo '...'; ?> diff --git a/data/templates/top.inc.php b/data/templates/top.inc.php index 830da65..b1ffa14 100644 --- a/data/templates/top.inc.php +++ b/data/templates/top.inc.php @@ -5,15 +5,18 @@ <?php echo filter($GLOBALS['sitename'] .(isset($pagetitle) ? ' » ' . $pagetitle : '')); ?> - + '; + echo ' '; } } ?> + From f52cba5cb3185adf468165bc4d53c8a64cd313ae Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:07:36 +0000 Subject: [PATCH 036/173] make html more readable git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@751 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/templates/sidebar.block.search.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/templates/sidebar.block.search.php b/data/templates/sidebar.block.search.php index 660ce00..f4e6b42 100644 --- a/data/templates/sidebar.block.search.php +++ b/data/templates/sidebar.block.search.php @@ -27,10 +27,10 @@ if ($lastSearches && count($lastSearches) > 0) {

    Date: Sun, 26 Sep 2010 22:08:44 +0000 Subject: [PATCH 037/173] reformat the code (CS) git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@752 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/SearchHistory.php | 189 ++++++++++++++---- 1 file changed, 150 insertions(+), 39 deletions(-) diff --git a/src/SemanticScuttle/Service/SearchHistory.php b/src/SemanticScuttle/Service/SearchHistory.php index df9b256..4947bf7 100644 --- a/src/SemanticScuttle/Service/SearchHistory.php +++ b/src/SemanticScuttle/Service/SearchHistory.php @@ -26,7 +26,9 @@ */ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService { - var $sizeSearchHistory; + public $sizeSearchHistory; + + /** * Returns the single service instance @@ -44,109 +46,218 @@ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService return $instance; } + + + /** + * Creates a new instance + * + * @param DB $db Database object + */ public function __construct($db) { $this->db = $db; $this->tablename = $GLOBALS['tableprefix'] .'searchhistory'; - if(isset($GLOBALS['sizeSearchHistory'])) { + if (isset($GLOBALS['sizeSearchHistory'])) { $this->sizeSearchHistory = $GLOBALS['sizeSearchHistory']; } else { $this->sizeSearchHistory = 10; } } - function addSearch($terms, $range, $nbResults, $uId=0) { - if(strlen($terms) == 0) { + + + /** + * Adds a new search to the search history + * + * @param string $terms Search terms separated by spaces + * @param string $range - 'all' - search was in all bookmarks + * - 'watchlist' - searched in watchlist + * - any username to show that the search happened + * in his own bookmarks. + * @param integer $nbResults Number of search result rows + * @param integer $uId ID of user that searched + * + * @return boolean True if it has been added, false if not + */ + public function addSearch($terms, $range, $nbResults, $uId = 0) + { + if (strlen($terms) == 0) { return false; } $datetime = gmdate('Y-m-d H:i:s', time()); //Insert values - $values = array('shTerms'=>$terms, 'shRange'=>$range, 'shDatetime'=>$datetime, 'shNbResults'=>$nbResults, 'uId'=>$uId); - $sql = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values); + $values = array( + 'shTerms' => $terms, + 'shRange' => $range, + 'shDatetime' => $datetime, + 'shNbResults' => $nbResults, + 'uId' => $uId + ); + $sql = 'INSERT INTO ' . $this->getTableName() + . ' ' . $this->db->sql_build_array('INSERT', $values); + $this->db->sql_transaction('begin'); - if (!($dbresult = & $this->db->sql_query($sql))) { + if (!($dbresult = $this->db->sql_query($sql))) { $this->db->sql_transaction('rollback'); - message_die(GENERAL_ERROR, 'Could not insert search history', '', __LINE__, __FILE__, $sql, $this->db); + message_die( + GENERAL_ERROR, 'Could not insert search history', + '', __LINE__, __FILE__, $sql, $this->db + ); return false; } - if($this->sizeSearchHistory != -1 && - $this->countSearches() > $this->sizeSearchHistory) { + if ($this->sizeSearchHistory != -1 + && $this->countSearches() > $this->sizeSearchHistory + ) { $this->deleteOldestSearch(); } + + return true; } - function getAllSearches($range = NULL, $uId = NULL, $nb = NULL, $start = NULL, $distinct = false, $withResults = false) { - $sql = 'SELECT DISTINCT(shTerms), shId, shRange, shNbResults, shDatetime, uId'; + + + /** + * Returns searches with the given features. + * + * @param string $range - 'all' - search was in all bookmarks + * - 'watchlist' - searched in watchlist + * - any username to show that the search happened + * in his own bookmarks. + * @param integer $uId Id of the user who searched. 0 for anonymous users + * @param integer $nb Number of bookmarks to retrieve (paging) + * @param integer $start Number of bookmark to begin with (paging) + * @param boolean $distinct If the search terms shall be distinct + * @param boolean $withResults Only return searches that had at least one result + * + * @return array Array of search history database rows + */ + public function getAllSearches( + $range = null, $uId = null, $nb = null, + $start = null, $distinct = false, $withResults = false + ) { + $sql = 'SELECT DISTINCT(shTerms),' + . ' shId, shRange, shNbResults, shDatetime, uId'; $sql.= ' FROM '. $this->getTableName(); $sql.= ' WHERE 1=1'; - if($range != NULL) { + if ($range != null) { $sql.= ' AND shRange = "'.$range.'"'; } else { $sql.= ' AND shRange = "all"'; } - if($uId != NULL) { + if ($uId != null) { $sql.= ' AND uId = '.$uId; } - if($withResults = true) { + if ($withResults = true) { $sql.= ' AND shNbResults > 0'; } - if($distinct) { + if ($distinct) { $sql.= ' GROUP BY shTerms'; } $sql.= ' ORDER BY shId DESC'; - if (!($dbresult = & $this->db->sql_query_limit($sql, $nb, $start))) { - message_die(GENERAL_ERROR, 'Could not get searches', '', __LINE__, __FILE__, $sql, $this->db); + if (!($dbresult = $this->db->sql_query_limit($sql, $nb, $start))) { + message_die( + GENERAL_ERROR, 'Could not get searches', + '', __LINE__, __FILE__, $sql, $this->db + ); return false; } $searches = array(); - while ($row = & $this->db->sql_fetchrow($dbresult)) { + while ($row = $this->db->sql_fetchrow($dbresult)) { $searches[] = $row; } $this->db->sql_freeresult($dbresult); return $searches; } - function countSearches() { + + + /** + * Counts the number of searches that have been made in total. + * + * @return integer Number of searches + */ + public function countSearches() + { $sql = 'SELECT COUNT(*) AS `total` FROM '. $this->getTableName(); - if (!($dbresult = & $this->db->sql_query($sql)) || (!($row = & $this->db->sql_fetchrow($dbresult)))) { - message_die(GENERAL_ERROR, 'Could not get total searches', '', __LINE__, __FILE__, $sql, $this->db); + if (!($dbresult = $this->db->sql_query($sql)) + || (!($row = & $this->db->sql_fetchrow($dbresult))) + ) { + message_die( + GENERAL_ERROR, 'Could not get total searches', + '', __LINE__, __FILE__, $sql, $this->db + ); return false; } $this->db->sql_freeresult($dbresult); return $row['total']; } - /* This function allows to limit the number of saved searches - by deleting the oldest one */ - function deleteOldestSearch() { + + + /** + * This function allows to limit the number of saved searches + * by deleting the oldest one + * + * @return boolean True when all went well, false in case of an error + */ + public function deleteOldestSearch() + { $sql = 'DELETE FROM '.$this->getTableName(); - $sql.= ' ORDER BY shId ASC LIMIT 1'; // warning: here the limit is important + // warning: here the limit is important + $sql .= ' ORDER BY shId ASC LIMIT 1'; $this->db->sql_transaction('begin'); - if (!($dbresult = & $this->db->sql_query($sql))) { + if (!($dbresult = $this->db->sql_query($sql))) { $this->db->sql_transaction('rollback'); - message_die(GENERAL_ERROR, 'Could not delete bookmarks', '', __LINE__, __FILE__, $query, $this->db); - return false; - } - } - - function deleteSearchHistoryForUser($uId) { - $query = 'DELETE FROM '. $this->getTableName() .' WHERE uId = '. intval($uId); - - if (!($dbresult = & $this->db->sql_query($query))) { - message_die(GENERAL_ERROR, 'Could not delete search history', '', - __LINE__, __FILE__, $query, $this->db); + message_die( + GENERAL_ERROR, 'Could not delete bookmarks', + '', __LINE__, __FILE__, $query, $this->db + ); return false; } return true; } - function deleteAll() { + + + /** + * Deletes all search history entries that have been made by the user + * with the given ID. + * + * @param integer $uId ID of the user + * + * @return boolean True when all went well, false in case of an error + */ + public function deleteSearchHistoryForUser($uId) + { + $query = 'DELETE FROM '. $this->getTableName() + . ' WHERE uId = ' . intval($uId); + + if (!($dbresult = $this->db->sql_query($query))) { + message_die( + GENERAL_ERROR, 'Could not delete search history', '', + __LINE__, __FILE__, $query, $this->db + ); + return false; + } + + return true; + } + + + + /** + * Deletes all search history entries. + * + * @return void + */ + public function deleteAll() + { $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`'; $this->db->sql_query($query); } From 5c65da5bffd8c464a8afb6fadbc3aa5549322880 Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:09:55 +0000 Subject: [PATCH 038/173] fix nasty bug with $withResults in getAllSearches() git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@753 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/SearchHistory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SemanticScuttle/Service/SearchHistory.php b/src/SemanticScuttle/Service/SearchHistory.php index 4947bf7..6ec240a 100644 --- a/src/SemanticScuttle/Service/SearchHistory.php +++ b/src/SemanticScuttle/Service/SearchHistory.php @@ -149,7 +149,7 @@ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService if ($uId != null) { $sql.= ' AND uId = '.$uId; } - if ($withResults = true) { + if ($withResults == true) { $sql.= ' AND shNbResults > 0'; } if ($distinct) { From 93e520165890a14ce8269b04303216458b98ad7e Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:10:54 +0000 Subject: [PATCH 039/173] add some new tests git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@754 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/SearchHistoryTest.php | 168 ++++++++++++++++++++++++++---------- 1 file changed, 122 insertions(+), 46 deletions(-) diff --git a/tests/SearchHistoryTest.php b/tests/SearchHistoryTest.php index 3716b37..f140063 100644 --- a/tests/SearchHistoryTest.php +++ b/tests/SearchHistoryTest.php @@ -55,63 +55,139 @@ class SearchHistoryTest extends TestBase + /** + * Set up all services + * + * @return void + */ protected function setUp() { - $this->us =SemanticScuttle_Service_Factory::get('User'); - $this->bs =SemanticScuttle_Service_Factory::get('Bookmark'); - $this->bs->deleteAll(); - $this->b2ts =SemanticScuttle_Service_Factory::get('Bookmark2Tag'); - $this->b2ts->deleteAll(); - $this->tts =SemanticScuttle_Service_Factory::get('Tag2Tag'); - $this->tts->deleteAll(); - $this->tsts =SemanticScuttle_Service_Factory::get('TagStat'); - $this->tsts->deleteAll(); - $this->shs =SemanticScuttle_Service_Factory::get('SearchHistory'); - $this->shs->deleteAll(); + $this->us = SemanticScuttle_Service_Factory::get('User'); + $this->bs = SemanticScuttle_Service_Factory::get('Bookmark'); + $this->bs->deleteAll(); + + $this->b2ts =SemanticScuttle_Service_Factory::get('Bookmark2Tag'); + $this->b2ts->deleteAll(); + + $this->tts = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $this->tts->deleteAll(); + + $this->tsts = SemanticScuttle_Service_Factory::get('TagStat'); + $this->tsts->deleteAll(); + + $this->shs = SemanticScuttle_Service_Factory::get('SearchHistory'); + $this->shs->deleteAll(); + } + + /** + * Tests if adding searches to the database works + */ + public function testAddSearch() + { + $this->assertEquals(0, $this->shs->countSearches()); + + $this->assertTrue( + $this->shs->addSearch('testsearchterm', 'all', 0) + ); + $this->assertEquals(1, $this->shs->countSearches()); + } + + /** + * Tests if adding a search without terms should fail + */ + public function testAddSearchNoTerms() + { + $this->assertEquals(0, $this->shs->countSearches()); + + $this->assertFalse( + $this->shs->addSearch('', 'all', 0) + ); + $this->assertEquals(0, $this->shs->countSearches()); } public function testSearchHistory() { - $shs = $this->shs; + $shs = $this->shs; - $terms = 'bbqsdkbb;,:,:q;,qddds&é"\'\\\\\(-è_çà)'; - $terms2 = '~#{|`]'; - $range = 'all'; - $nbResults = 10908; - $uId = 10; + $terms = 'bbqsdkbb;,:,:q;,qddds&é"\'\\\\\(-è_çà)'; + $terms2 = '~#{|`]'; + $range = 'all'; + $nbResults = 10908; + $uId = 10; - $shs->addSearch($terms, $range, $nbResults, $uId); - $shs->addSearch($terms2, $range, $nbResults, $uId); - $shs->addSearch('', $range, $nbResults, $uId); // A void search must not be saved + $shs->addSearch($terms, $range, $nbResults, $uId); + $shs->addSearch($terms2, $range, $nbResults, $uId); + $shs->addSearch('', $range, $nbResults, $uId); // A void search must not be saved - $searches = $shs->getAllSearches(); - $this->assertSame(2, count($searches)); - $searches = $shs->getAllSearches($range, $uId); - $this->assertEquals(2, count($searches)); - $searches = $shs->getAllSearches($range, 20); // fake userid - $this->assertEquals(0, count($searches)); - $searches = $shs->getAllSearches($range, $uId, 1); - $this->assertEquals(1, count($searches)); - $searches = $shs->getAllSearches($range, null, 1, 1); - $this->assertEquals(1, count($searches)); + $searches = $shs->getAllSearches(); + $this->assertSame(2, count($searches)); + $searches = $shs->getAllSearches($range, $uId); + $this->assertEquals(2, count($searches)); + $searches = $shs->getAllSearches($range, 20); // fake userid + $this->assertEquals(0, count($searches)); + $searches = $shs->getAllSearches($range, $uId, 1); + $this->assertEquals(1, count($searches)); + $searches = $shs->getAllSearches($range, null, 1, 1); + $this->assertEquals(1, count($searches)); - //test content of results - $searches = $shs->getAllSearches(); - $this->assertSame($terms2, $searches[0]['shTerms']); - $this->assertSame($range, $searches[0]['shRange']); - $this->assertEquals($nbResults, $searches[0]['shNbResults']); - $this->assertEquals($uId, $searches[0]['uId']); - $this->assertSame($terms, $searches[1]['shTerms']); - $this->assertSame($range, $searches[1]['shRange']); - $this->assertEquals($nbResults, $searches[1]['shNbResults']); - $this->assertEquals($uId, $searches[1]['uId']); + //test content of results + $searches = $shs->getAllSearches(); + $this->assertSame($terms2, $searches[0]['shTerms']); + $this->assertSame($range, $searches[0]['shRange']); + $this->assertEquals($nbResults, $searches[0]['shNbResults']); + $this->assertEquals($uId, $searches[0]['uId']); + $this->assertSame($terms, $searches[1]['shTerms']); + $this->assertSame($range, $searches[1]['shRange']); + $this->assertEquals($nbResults, $searches[1]['shNbResults']); + $this->assertEquals($uId, $searches[1]['uId']); - //test distinct parameter - $shs->addSearch($terms, $range, $nbResults, 30); // we repeat a search (same terms) - $searches = $shs->getAllSearches(); - $this->assertSame(3, count($searches)); - $searches = $shs->getAllSearches(NULL, NULL, NULL, NULL, true); - $this->assertSame(2, count($searches)); + //test distinct parameter + $shs->addSearch( + $terms, $range, $nbResults, 30 + ); // we repeat a search (same terms) + $searches = $shs->getAllSearches(); + $this->assertSame(3, count($searches)); + $searches = $shs->getAllSearches(null, null, null, null, true); + $this->assertSame(2, count($searches)); + } + + /** + * Deleting the oldest search without any historical searches + */ + public function testDeleteOldestSearchNone() + { + $this->assertEquals(0, $this->shs->countSearches()); + $this->assertTrue($this->shs->deleteOldestSearch()); + $this->assertEquals(0, $this->shs->countSearches()); + } + + /** + * Test deleting the oldest search + */ + public function testDeleteOldestSearchSome() + { + $this->assertEquals(0, $this->shs->countSearches()); + $this->shs->addSearch('testsearchterm1', 'all', 0); + $this->shs->addSearch('testsearchterm2', 'all', 0); + + $rows = $this->shs->getAllSearches(); + $this->assertEquals(2, count($rows)); + + $highestId = -1; + foreach ($rows as $row) { + if ($row['shId'] > $highestId) { + $highestId = $row['shId']; + } + } + + $this->shs->deleteOldestSearch(); + + $rows = $this->shs->getAllSearches(); + $this->assertEquals(1, count($rows)); + $this->assertEquals( + $highestId, + $rows[0]['shId'] + ); } } From 84224c32948fb80f64c26d0d84834a1c13ef7710 Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:28:54 +0000 Subject: [PATCH 040/173] explain $sizeSearchHistory git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@755 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/SearchHistory.php | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/SemanticScuttle/Service/SearchHistory.php b/src/SemanticScuttle/Service/SearchHistory.php index 6ec240a..f13f8f9 100644 --- a/src/SemanticScuttle/Service/SearchHistory.php +++ b/src/SemanticScuttle/Service/SearchHistory.php @@ -26,6 +26,15 @@ */ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService { + /** + * Size of the search history. + * If the number of logged searches is larger than this, + * adding a new search will delete the oldest one automatically. + * + * Use -1 to deactivate automatic deletion. + * + * @var integer + */ public $sizeSearchHistory; @@ -49,14 +58,17 @@ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService /** - * Creates a new instance + * Creates a new instance. + * + * Sets $this->sizeSearchHistory to $GLOBALS['sizeSearchHistory'] or 10 + * if the global variable is not defined. * * @param DB $db Database object */ public function __construct($db) { $this->db = $db; - $this->tablename = $GLOBALS['tableprefix'] .'searchhistory'; + $this->tablename = $GLOBALS['tableprefix'] . 'searchhistory'; if (isset($GLOBALS['sizeSearchHistory'])) { $this->sizeSearchHistory = $GLOBALS['sizeSearchHistory']; } else { @@ -67,7 +79,9 @@ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService /** - * Adds a new search to the search history + * Adds a new search to the search history. + * Automatically deletes the oldest search when the number of + * searches is larger than $sizeSearchHistory. * * @param string $terms Search terms separated by spaces * @param string $range - 'all' - search was in all bookmarks From eb105d379bc2283e2bf2ea33bf455ec9caf5c7ee Mon Sep 17 00:00:00 2001 From: cweiske Date: Sun, 26 Sep 2010 22:29:51 +0000 Subject: [PATCH 041/173] tell which test covers which method git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@756 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/SearchHistoryTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/SearchHistoryTest.php b/tests/SearchHistoryTest.php index f140063..53d0451 100644 --- a/tests/SearchHistoryTest.php +++ b/tests/SearchHistoryTest.php @@ -81,6 +81,8 @@ class SearchHistoryTest extends TestBase /** * Tests if adding searches to the database works + * + * @covers SemanticScuttle_Service_SearchHistory::addSearch */ public function testAddSearch() { @@ -94,6 +96,8 @@ class SearchHistoryTest extends TestBase /** * Tests if adding a search without terms should fail + * + * @covers SemanticScuttle_Service_SearchHistory::addSearch */ public function testAddSearchNoTerms() { @@ -153,6 +157,8 @@ class SearchHistoryTest extends TestBase /** * Deleting the oldest search without any historical searches + * + * @covers SemanticScuttle_Service_SearchHistory::deleteOldestSearch */ public function testDeleteOldestSearchNone() { @@ -163,6 +169,8 @@ class SearchHistoryTest extends TestBase /** * Test deleting the oldest search + * + * @covers SemanticScuttle_Service_SearchHistory::deleteOldestSearch */ public function testDeleteOldestSearchSome() { From f0456530b4940918efba25ea15e22532c682fb70 Mon Sep 17 00:00:00 2001 From: cweiske Date: Mon, 27 Sep 2010 20:43:35 +0000 Subject: [PATCH 042/173] Fix bug #3074816: French translation breaks edit javascript git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@757 b3834d28-1941-0410-a4f8-b48e95affb8f --- data/templates/editbookmark.tpl.php | 28 ++++++++++++++++++++-------- doc/ChangeLog | 1 + 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/data/templates/editbookmark.tpl.php b/data/templates/editbookmark.tpl.php index c58bd1b..dd6b100 100644 --- a/data/templates/editbookmark.tpl.php +++ b/data/templates/editbookmark.tpl.php @@ -140,14 +140,26 @@ if (empty($_REQUEST['popup']) && (!isset($showdelete) || !$showdelete)) {

    '; - //echo ''; - //echo ''; - //echo ''; echo ''; + */ } ?> + - From da20b90bbaafef116c7a5d439e44210b126c3522 Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:08:02 +0000 Subject: [PATCH 051/173] update php-gettext to 1.0.10 git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@763 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/php-gettext/Makefile | 14 +- src/php-gettext/README | 74 +-- .../locale/sr_CS/LC_MESSAGES/messages.mo | Bin 834 -> 829 bytes .../locale/sr_CS/LC_MESSAGES/messages.po | 3 +- src/php-gettext/examples/pigs_dropin.php | 10 +- src/php-gettext/examples/pigs_fallback.php | 10 +- src/php-gettext/examples/update | 4 +- src/php-gettext/gettext.inc | 480 +++++++++++++----- src/php-gettext/gettext.php | 179 ++++--- src/php-gettext/streams.php | 32 +- src/php-gettext/tests/LocalesTest.php | 66 +++ src/php-gettext/tests/ParsingTest.php | 43 ++ 12 files changed, 643 insertions(+), 272 deletions(-) create mode 100644 src/php-gettext/tests/LocalesTest.php create mode 100644 src/php-gettext/tests/ParsingTest.php diff --git a/src/php-gettext/Makefile b/src/php-gettext/Makefile index 2dba911..a6cce12 100644 --- a/src/php-gettext/Makefile +++ b/src/php-gettext/Makefile @@ -1,12 +1,11 @@ PACKAGE = php-gettext-$(VERSION) -VERSION = 1.0.7 +VERSION = 1.0.10 DIST_FILES = \ gettext.php \ gettext.inc \ streams.php \ AUTHORS \ - ChangeLog \ README \ COPYING \ Makefile \ @@ -17,9 +16,14 @@ DIST_FILES = \ examples/locale/sr_CS/LC_MESSAGES/messages.mo \ examples/locale/de_CH/LC_MESSAGES/messages.po \ examples/locale/de_CH/LC_MESSAGES/messages.mo \ - examples/update + examples/update \ + tests/LocalesTest.php \ + tests/ParsingTest.php -dist: +check: + phpunit --verbose tests + +dist: check if [ -d $(PACKAGE) ]; then \ rm -rf $(PACKAGE); \ fi; \ @@ -30,3 +34,5 @@ dist: rm -rf $(PACKAGE); \ fi; +clean: + rm -f $(PACKAGE).tar.gz diff --git a/src/php-gettext/README b/src/php-gettext/README index c7525e2..bca4f91 100644 --- a/src/php-gettext/README +++ b/src/php-gettext/README @@ -1,9 +1,9 @@ -PHP-gettext 1.0 +PHP-gettext 1.0 (https://launchpad.net/php-gettext) -Copyright 2003, 2006 -- Danilo "angry with PHP[1]" Segan +Copyright 2003, 2006, 2009 -- Danilo "angry with PHP[1]" Segan Licensed under GPLv2 (or any later version, see COPYING) -[1] PHP is actually cyrillic, and translates roughly to +[1] PHP is actually cyrillic, and translates roughly to "works-doesn't-work" (UTF-8: Ради-Не-Ради) @@ -50,36 +50,16 @@ Features file data, I used imaginary abstract class StreamReader to do all the input (check streams.php). For your convenience, I've already provided two classes for reading files: FileReader and - StringReader (CachedFileReader is a combination of the two: it - loads entire file contents into a string, and then works on that). - See example below for usage. You can for instance use StringReader - when you read in data from a database, or you can create your own - derivative of StreamReader for anything you like. - + StringReader (CachedFileReader is a combination of the two: it + loads entire file contents into a string, and then works on that). + See example below for usage. You can for instance use StringReader + when you read in data from a database, or you can create your own + derivative of StreamReader for anything you like. + Bugs - Plural-forms field in MO header (translation for empty string, - i.e. "") is treated according to PHP syntactic rules (it's - eval()ed). Since these should actually follow C syntax, there are - some problems. - - For instance, I'm used to using this: - Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : \ - n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; - but it fails with PHP (it sets $plural=2 instead of 0 for $n==1). - - The fix is usually simple, but I'm lazy to go into the details of - PHP operator precedence, and maybe try to fix it. In here, I had - to put everything after the first ':' in parenthesis: - Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : \ - (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); - That works, and I'm satisfied. - - Besides this one, there are probably a bunch of other bugs, since - I hate PHP (did I mention it already? no? strange), and don't - know it very well. So, feel free to fix any of those and report - them back to me at . + Report them on https://bugs.launchpad.net/php-gettext Usage @@ -94,19 +74,19 @@ Usage Then, use that as a parameter to gettext_reader constructor: $wohoo = new gettext_reader($streamer); - If you want to disable pre-loading of entire message catalog in - memory (if, for example, you have a multi-thousand message catalog - which you'll use only occasionally), use "false" for second + If you want to disable pre-loading of entire message catalog in + memory (if, for example, you have a multi-thousand message catalog + which you'll use only occasionally), use "false" for second parameter to gettext_reader constructor: $wohoo = new gettext_reader($streamer, false); From now on, you have all the benefits of gettext data at your - disposal, so may run: + disposal, so may run: print $wohoo->translate("This is a test"); print $wohoo->ngettext("%d bird", "%d birds", $birds); You might need to pass parameter "-k" to xgettext to make it - extract all the strings. In above example, try with + extract all the strings. In above example, try with xgettext -ktranslate -kngettext:1,2 file.php what should create messages.po which contains two messages for translation. @@ -118,8 +98,8 @@ Usage Usage with gettext.inc (standard gettext interfaces emulation) - Check example in examples/pig_dropin.php, basically you include - gettext.inc and use all the standard gettext interfaces as + Check example in examples/pig_dropin.php, basically you include + gettext.inc and use all the standard gettext interfaces as documented on: http://www.php.net/gettext @@ -137,20 +117,12 @@ Example There is also simple "update" script that can be used to generate POT file and to update the translation using msgmerge. -Interesting TODO: +TODO: - o Try to parse "plural-forms" header field, and to follow C syntax - rules. This won't be easy. + o Improve speed to be even more comparable to the native gettext + implementation. -Boring TODO: - - o Learn PHP and fix bugs, slowness and other stuff resulting from - my lack of knowledge (but *maybe*, it's not my knowledge that is - bad, but PHP itself ;-). - - (This is mostly done thanks to Nico Kaiser.) - - o Try to use hash tables in MO files: with pre-loading, would it + o Try to use hash tables in MO files: with pre-loading, would it be useful at all? Never-asked-questions: @@ -160,7 +132,7 @@ Never-asked-questions: Well, it's quite simple. I consider that the first released thing should be labeled "version 1" (first, right?). Zero is there to - indicate that there's zero improvement and/or change compared to + indicate that there's zero improvement and/or change compared to "version 1". I plan to use version numbers 1.0.* for small bugfixes, and to @@ -173,7 +145,7 @@ Never-asked-questions: Mozart's 40th Symphony (there is one like that, right?). o Can I...? - + Yes, you can. This is free software (as in freedom, free speech), and you might do whatever you wish with it, provided you do not limit freedom of others (GPL). diff --git a/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo index 6ffccfd52d91a1db0870a94d12472e104f9c6b2b..497c8830ca84f880f8d484df057c0bf725163edd 100644 GIT binary patch delta 44 xcmX@awwG;!iRd~;28L}63=G?WIF*TkK^{oUZuC6FIGK-0X|f@c>Ev{#+W_V;3U~kj delta 51 zcmdnXc8G0)iRflV28L}63=G?WIE#saK^{meZS*|E$fTe!nTtu8QDd?$lPRO-=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" #: pigs.php:19 msgid "" diff --git a/src/php-gettext/examples/pigs_dropin.php b/src/php-gettext/examples/pigs_dropin.php index edd2b0d..94fd850 100644 --- a/src/php-gettext/examples/pigs_dropin.php +++ b/src/php-gettext/examples/pigs_dropin.php @@ -1,6 +1,6 @@ . + Copyright (c) 2003,2004,2005,2009 Danilo Segan . Copyright (c) 2005,2006 Steven Armstrong This file is part of PHP-gettext. @@ -21,10 +21,12 @@ */ +error_reporting(E_ALL | E_STRICT); + // define constants -define(PROJECT_DIR, realpath('./')); -define(LOCALE_DIR, PROJECT_DIR .'/locale'); -define(DEFAULT_LOCALE, 'en_US'); +define('PROJECT_DIR', realpath('./')); +define('LOCALE_DIR', PROJECT_DIR .'/locale'); +define('DEFAULT_LOCALE', 'en_US'); require_once('../gettext.inc'); diff --git a/src/php-gettext/examples/pigs_fallback.php b/src/php-gettext/examples/pigs_fallback.php index b50f752..353190d 100644 --- a/src/php-gettext/examples/pigs_fallback.php +++ b/src/php-gettext/examples/pigs_fallback.php @@ -1,6 +1,6 @@ . + Copyright (c) 2003,2004,2005,2009 Danilo Segan . Copyright (c) 2005,2006 Steven Armstrong This file is part of PHP-gettext. @@ -21,10 +21,12 @@ */ +error_reporting(E_ALL | E_STRICT); + // define constants -define(PROJECT_DIR, realpath('./')); -define(LOCALE_DIR, PROJECT_DIR .'/locale'); -define(DEFAULT_LOCALE, 'en_US'); +define('PROJECT_DIR', realpath('./')); +define('LOCALE_DIR', PROJECT_DIR .'/locale'); +define('DEFAULT_LOCALE', 'en_US'); require_once('../gettext.inc'); diff --git a/src/php-gettext/examples/update b/src/php-gettext/examples/update index c8d8b61..76b4308 100644 --- a/src/php-gettext/examples/update +++ b/src/php-gettext/examples/update @@ -1,7 +1,7 @@ #!/bin/sh TEMPLATE=pigs.pot -xgettext -kT_ngettext:1,2 -kT_ -L PHP -o $TEMPLATE pigs.php -if [ x$1 == 'x-p' ]; then +xgettext -kT_ngettext:1,2 -kT_ -L PHP -o $TEMPLATE pigs_dropin.php +if [ "x$1" = "x-p" ]; then msgfmt --statistics $TEMPLATE else if [ -f $1.po ]; then diff --git a/src/php-gettext/gettext.inc b/src/php-gettext/gettext.inc index a67811f..399a0f2 100644 --- a/src/php-gettext/gettext.inc +++ b/src/php-gettext/gettext.inc @@ -1,9 +1,10 @@ - + Copyright (c) 2009 Danilo Segan + Drop in replacement for native gettext. - + This file is part of PHP-gettext. PHP-gettext is free software; you can redistribute it and/or modify @@ -22,15 +23,22 @@ */ /* -LC_CTYPE 0 -LC_NUMERIC 1 -LC_TIME 2 -LC_COLLATE 3 -LC_MONETARY 4 -LC_MESSAGES 5 -LC_ALL 6 +LC_CTYPE 0 +LC_NUMERIC 1 +LC_TIME 2 +LC_COLLATE 3 +LC_MONETARY 4 +LC_MESSAGES 5 +LC_ALL 6 */ + +// LC_MESSAGES is not available if php-gettext is not loaded +// while the other constants are already available from session extension. +if (!defined('LC_MESSAGES')) { + define('LC_MESSAGES', 5); +} + require('streams.php'); require('gettext.php'); @@ -44,29 +52,96 @@ $LC_CATEGORIES = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MO $EMULATEGETTEXT = 0; $CURRENTLOCALE = ''; +/* Class to hold a single domain included in $text_domains. */ +class domain { + var $l10n; + var $path; + var $codeset; +} // Utility functions +/** + * Return a list of locales to try for any POSIX-style locale specification. + */ +function get_list_of_locales($locale) { + /* Figure out all possible locale names and start with the most + * specific ones. I.e. for sr_CS.UTF-8@latin, look through all of + * sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr. + */ + $locale_names = array(); + $lang = NULL; + $country = NULL; + $charset = NULL; + $modifier = NULL; + if ($locale) { + if (preg_match("/^(?P[a-z]{2,3})" // language code + ."(?:_(?P[A-Z]{2}))?" // country code + ."(?:\.(?P[-A-Za-z0-9_]+))?" // charset + ."(?:@(?P[-A-Za-z0-9_]+))?$/", // @ modifier + $locale, $matches)) { + + if (isset($matches["lang"])) $lang = $matches["lang"]; + if (isset($matches["country"])) $country = $matches["country"]; + if (isset($matches["charset"])) $charset = $matches["charset"]; + if (isset($matches["modifier"])) $modifier = $matches["modifier"]; + + if ($modifier) { + if ($country) { + if ($charset) + array_push($locale_names, "${lang}_$country.$charset@$modifier"); + array_push($locale_names, "${lang}_$country@$modifier"); + } elseif ($charset) + array_push($locale_names, "${lang}.$charset@$modifier"); + array_push($locale_names, "$lang@$modifier"); + } + if ($country) { + if ($charset) + array_push($locale_names, "${lang}_$country.$charset"); + array_push($locale_names, "${lang}_$country"); + } elseif ($charset) + array_push($locale_names, "${lang}.$charset"); + array_push($locale_names, $lang); + } + + // If the locale name doesn't match POSIX style, just include it as-is. + if (!in_array($locale, $locale_names)) + array_push($locale_names, $locale); + } + return $locale_names; +} + /** * Utility function to get a StreamReader for the given text domain. */ function _get_reader($domain=null, $category=5, $enable_cache=true) { - global $text_domains, $default_domain, $LC_CATEGORIES; - if (!isset($domain)) $domain = $default_domain; - if (!isset($text_domains[$domain]->l10n)) { - // get the current locale - $locale = _setlocale(LC_MESSAGES, 0); - $p = isset($text_domains[$domain]->path) ? $text_domains[$domain]->path : './'; - $path = $p . "$locale/". $LC_CATEGORIES[$category] ."/$domain.mo"; - if (file_exists($path)) { - $input = new FileReader($path); - } - else { - $input = null; - } - $text_domains[$domain]->l10n = new gettext_reader($input, $enable_cache); - } - return $text_domains[$domain]->l10n; + global $text_domains, $default_domain, $LC_CATEGORIES; + if (!isset($domain)) $domain = $default_domain; + if (!isset($text_domains[$domain]->l10n)) { + // get the current locale + $locale = _setlocale(LC_MESSAGES, 0); + $bound_path = isset($text_domains[$domain]->path) ? + $text_domains[$domain]->path : './'; + $subpath = $LC_CATEGORIES[$category] ."/$domain.mo"; + + $locale_names = get_list_of_locales($locale); + $input = null; + foreach ($locale_names as $locale) { + $full_path = $bound_path . $locale . "/" . $subpath; + if (file_exists($full_path)) { + $input = new FileReader($full_path); + break; + } + } + + if (!array_key_exists($domain, $text_domains)) { + // Initialize an empty domain object. + $text_domains[$domain] = new domain(); + } + $text_domains[$domain]->l10n = new gettext_reader($input, + $enable_cache); + } + return $text_domains[$domain]->l10n; } /** @@ -80,8 +155,10 @@ function locale_emulation() { /** * Checks if the current locale is supported on this system. */ -function _check_locale() { +function _check_locale_and_function($function=false) { global $EMULATEGETTEXT; + if ($function and !function_exists($function)) + return false; return !$EMULATEGETTEXT; } @@ -89,56 +166,70 @@ function _check_locale() { * Get the codeset for the given domain. */ function _get_codeset($domain=null) { - global $text_domains, $default_domain, $LC_CATEGORIES; - if (!isset($domain)) $domain = $default_domain; - return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding'); + global $text_domains, $default_domain, $LC_CATEGORIES; + if (!isset($domain)) $domain = $default_domain; + return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding'); } /** * Convert the given string to the encoding set by bind_textdomain_codeset. */ function _encode($text) { - $source_encoding = mb_detect_encoding($text); - $target_encoding = _get_codeset(); - if ($source_encoding != $target_encoding) { - return mb_convert_encoding($text, $target_encoding, $source_encoding); - } - else { - return $text; - } + $source_encoding = mb_detect_encoding($text); + $target_encoding = _get_codeset(); + if ($source_encoding != $target_encoding) { + return mb_convert_encoding($text, $target_encoding, $source_encoding); + } + else { + return $text; + } } - - // Custom implementation of the standard gettext related functions +/** + * Returns passed in $locale, or environment variable $LANG if $locale == ''. + */ +function _get_default_locale($locale) { + if ($locale == '') // emulate variable support + return getenv('LANG'); + else + return $locale; +} + /** * Sets a requested locale, if needed emulates it. */ function _setlocale($category, $locale) { global $CURRENTLOCALE, $EMULATEGETTEXT; if ($locale === 0) { // use === to differentiate between string "0" - if ($CURRENTLOCALE != '') + if ($CURRENTLOCALE != '') return $CURRENTLOCALE; - else + else // obey LANG variable, maybe extend to support all of LC_* vars // even if we tried to read locale without setting it first return _setlocale($category, $CURRENTLOCALE); } else { - $ret = 0; - if (function_exists('setlocale')) // I don't know if this ever happens ;) - $ret = @setlocale($category, $locale); //the @ hides warning messages on few installations - if (($ret and $locale == '') or ($ret == $locale)) { - $EMULATEGETTEXT = 0; - $CURRENTLOCALE = $ret; - } else { - if ($locale == '') // emulate variable support - $CURRENTLOCALE = getenv('LANG'); - else - $CURRENTLOCALE = $locale; + if (function_exists('setlocale')) { + $ret = setlocale($category, $locale); + if (($locale == '' and !$ret) or // failed setting it by env + ($locale != '' and $ret != $locale)) { // failed setting it + // Failed setting it according to environment. + $CURRENTLOCALE = _get_default_locale($locale); $EMULATEGETTEXT = 1; + } else { + $CURRENTLOCALE = $ret; + $EMULATEGETTEXT = 0; + } + } else { + // No function setlocale(), emulate it all. + $CURRENTLOCALE = _get_default_locale($locale); + $EMULATEGETTEXT = 1; } + // Allow locale to be changed on the go for one translation domain. + global $text_domains, $default_domain; + unset($text_domains[$default_domain]->l10n); return $CURRENTLOCALE; } } @@ -147,135 +238,240 @@ function _setlocale($category, $locale) { * Sets the path for a domain. */ function _bindtextdomain($domain, $path) { - global $text_domains; - // ensure $path ends with a slash - if ($path[strlen($path) - 1] != '/') $path .= '/'; - elseif ($path[strlen($path) - 1] != '\\') $path .= '\\'; - $text_domains[$domain]->path = $path; + global $text_domains; + // ensure $path ends with a slash ('/' should work for both, but lets still play nice) + if (substr(php_uname(), 0, 7) == "Windows") { + if ($path[strlen($path)-1] != '\\' and $path[strlen($path)-1] != '/') + $path .= '\\'; + } else { + if ($path[strlen($path)-1] != '/') + $path .= '/'; + } + if (!array_key_exists($domain, $text_domains)) { + // Initialize an empty domain object. + $text_domains[$domain] = new domain(); + } + $text_domains[$domain]->path = $path; } /** * Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */ function _bind_textdomain_codeset($domain, $codeset) { - global $text_domains; - $text_domains[$domain]->codeset = $codeset; + global $text_domains; + $text_domains[$domain]->codeset = $codeset; } /** * Sets the default domain. */ function _textdomain($domain) { - global $default_domain; - $default_domain = $domain; + global $default_domain; + $default_domain = $domain; } /** * Lookup a message in the current domain. */ function _gettext($msgid) { - $l10n = _get_reader(); - //return $l10n->translate($msgid); - return _encode($l10n->translate($msgid)); + $l10n = _get_reader(); + return _encode($l10n->translate($msgid)); } + /** * Alias for gettext. */ function __($msgid) { - return _gettext($msgid); + return _gettext($msgid); } + /** * Plural version of gettext. */ function _ngettext($single, $plural, $number) { - $l10n = _get_reader(); - //return $l10n->ngettext($single, $plural, $number); - return _encode($l10n->ngettext($single, $plural, $number)); + $l10n = _get_reader(); + return _encode($l10n->ngettext($single, $plural, $number)); } /** * Override the current domain. */ function _dgettext($domain, $msgid) { - $l10n = _get_reader($domain); - //return $l10n->translate($msgid); - return _encode($l10n->translate($msgid)); + $l10n = _get_reader($domain); + return _encode($l10n->translate($msgid)); } + /** * Plural version of dgettext. */ function _dngettext($domain, $single, $plural, $number) { - $l10n = _get_reader($domain); - //return $l10n->ngettext($single, $plural, $number); - return _encode($l10n->ngettext($single, $plural, $number)); + $l10n = _get_reader($domain); + return _encode($l10n->ngettext($single, $plural, $number)); } /** * Overrides the domain and category for a single lookup. */ function _dcgettext($domain, $msgid, $category) { - $l10n = _get_reader($domain, $category); - //return $l10n->translate($msgid); - return _encode($l10n->translate($msgid)); + $l10n = _get_reader($domain, $category); + return _encode($l10n->translate($msgid)); } /** * Plural version of dcgettext. */ function _dcngettext($domain, $single, $plural, $number, $category) { - $l10n = _get_reader($domain, $category); - //return $l10n->ngettext($single, $plural, $number); - return _encode($l10n->ngettext($single, $plural, $number)); + $l10n = _get_reader($domain, $category); + return _encode($l10n->ngettext($single, $plural, $number)); +} + +/** + * Context version of gettext. + */ +function _pgettext($context, $msgid) { + $l10n = _get_reader(); + return _encode($l10n->pgettext($context, $msgid)); +} + +/** + * Override the current domain in a context gettext call. + */ +function _dpgettext($domain, $context, $msgid) { + $l10n = _get_reader($domain); + return _encode($l10n->pgettext($context, $msgid)); +} + +/** + * Overrides the domain and category for a single context-based lookup. + */ +function _dcpgettext($domain, $context, $msgid, $category) { + $l10n = _get_reader($domain, $category); + return _encode($l10n->pgettext($context, $msgid)); +} + +/** + * Context version of ngettext. + */ +function _npgettext($context, $singular, $plural) { + $l10n = _get_reader(); + return _encode($l10n->npgettext($context, $singular, $plural)); +} + +/** + * Override the current domain in a context ngettext call. + */ +function _dnpgettext($domain, $context, $singular, $plural) { + $l10n = _get_reader($domain); + return _encode($l10n->npgettext($context, $singular, $plural)); +} + +/** + * Overrides the domain and category for a plural context-based lookup. + */ +function _dcnpgettext($domain, $context, $singular, $plural, $category) { + $l10n = _get_reader($domain, $category); + return _encode($l10n->npgettext($context, $singular, $plural)); } -// Wrappers to use if the standard gettext functions are available, but the current locale is not supported by the system. -// Use the standard impl if the current locale is supported, use the custom impl otherwise. +// Wrappers to use if the standard gettext functions are available, +// but the current locale is not supported by the system. +// Use the standard impl if the current locale is supported, use the +// custom impl otherwise. function T_setlocale($category, $locale) { return _setlocale($category, $locale); } function T_bindtextdomain($domain, $path) { - if (_check_locale()) return bindtextdomain($domain, $path); - else return _bindtextdomain($domain, $path); + if (_check_locale_and_function()) return bindtextdomain($domain, $path); + else return _bindtextdomain($domain, $path); } function T_bind_textdomain_codeset($domain, $codeset) { // bind_textdomain_codeset is available only in PHP 4.2.0+ - if (_check_locale() and function_exists('bind_textdomain_codeset')) return bind_textdomain_codeset($domain, $codeset); - else return _bind_textdomain_codeset($domain, $codeset); + if (_check_locale_and_function('bind_textdomain_codeset')) + return bind_textdomain_codeset($domain, $codeset); + else return _bind_textdomain_codeset($domain, $codeset); } function T_textdomain($domain) { - if (_check_locale()) return textdomain($domain); - else return _textdomain($domain); + if (_check_locale_and_function()) return textdomain($domain); + else return _textdomain($domain); } function T_gettext($msgid) { - if (_check_locale()) return gettext($msgid); - else return _gettext($msgid); + if (_check_locale_and_function()) return gettext($msgid); + else return _gettext($msgid); } function T_($msgid) { - if (_check_locale()) return _($msgid); - return __($msgid); + if (_check_locale_and_function()) return _($msgid); + return __($msgid); } function T_ngettext($single, $plural, $number) { - if (_check_locale()) return ngettext($single, $plural, $number); - else return _ngettext($single, $plural, $number); + if (_check_locale_and_function()) + return ngettext($single, $plural, $number); + else return _ngettext($single, $plural, $number); } function T_dgettext($domain, $msgid) { - if (_check_locale()) return dgettext($domain, $msgid); - else return _dgettext($domain, $msgid); + if (_check_locale_and_function()) return dgettext($domain, $msgid); + else return _dgettext($domain, $msgid); } function T_dngettext($domain, $single, $plural, $number) { - if (_check_locale()) return dngettext($domain, $single, $plural, $number); - else return _dngettext($domain, $single, $plural, $number); + if (_check_locale_and_function()) + return dngettext($domain, $single, $plural, $number); + else return _dngettext($domain, $single, $plural, $number); } function T_dcgettext($domain, $msgid, $category) { - if (_check_locale()) return dcgettext($domain, $msgid, $category); - else return _dcgettext($domain, $msgid, $category); + if (_check_locale_and_function()) + return dcgettext($domain, $msgid, $category); + else return _dcgettext($domain, $msgid, $category); } function T_dcngettext($domain, $single, $plural, $number, $category) { - if (_check_locale()) return dcngettext($domain, $single, $plural, $number, $category); - else return _dcngettext($domain, $single, $plural, $number, $category); + if (_check_locale_and_function()) + return dcngettext($domain, $single, $plural, $number, $category); + else return _dcngettext($domain, $single, $plural, $number, $category); +} + +function T_pgettext($context, $msgid) { + if (_check_locale_and_function('pgettext')) + return pgettext($context, $msgid); + else + return _pgettext($context, $msgid); +} + +function T_dpgettext($domain, $context, $msgid) { + if (_check_locale_and_function('dpgettext')) + return dpgettext($domain, $context, $msgid); + else + return _dpgettext($domain, $context, $msgid); +} + +function T_dcpgettext($domain, $context, $msgid, $category) { + if (_check_locale_and_function('dcpgettext')) + return dcpgettext($domain, $context, $msgid, $category); + else + return _dcpgettext($domain, $context, $msgid, $category); +} + +function T_npgettext($context, $singular, $plural) { + if (_check_locale_and_function('npgettext')) + return npgettext($context, $single, $plural, $number); + else + return _npgettext($context, $single, $plural, $number); +} + +function T_dnpgettext($domain, $context, $singular, $plural) { + if (_check_locale_and_function('dnpgettext')) + return dnpgettext($domain, $context, $single, $plural, $number); + else + return _dnpgettext($domain, $context, $single, $plural, $number); +} + +function T_dcnpgettext($domain, $context, $singular, $plural, $category) { + if (_check_locale_and_function('dcnpgettext')) + return dcnpgettext($domain, $context, $single, + $plural, $number, $category); + else + return _dcnpgettext($domain, $context, $single, + $plural, $number, $category); } @@ -283,36 +479,56 @@ function T_dcngettext($domain, $single, $plural, $number, $category) { // Wrappers used as a drop in replacement for the standard gettext functions if (!function_exists('gettext')) { - function bindtextdomain($domain, $path) { - return _bindtextdomain($domain, $path); - } - function bind_textdomain_codeset($domain, $codeset) { - return _bind_textdomain_codeset($domain, $codeset); - } - function textdomain($domain) { - return _textdomain($domain); - } - function gettext($msgid) { - return _gettext($msgid); - } - function _($msgid) { - return __($msgid); - } - function ngettext($single, $plural, $number) { - return _ngettext($single, $plural, $number); - } - function dgettext($domain, $msgid) { - return _dgettext($domain, $msgid); - } - function dngettext($domain, $single, $plural, $number) { - return _dngettext($domain, $single, $plural, $number); - } - function dcgettext($domain, $msgid, $category) { - return _dcgettext($domain, $msgid, $category); - } - function dcngettext($domain, $single, $plural, $number, $category) { - return _dcngettext($domain, $single, $plural, $number, $category); - } + function bindtextdomain($domain, $path) { + return _bindtextdomain($domain, $path); + } + function bind_textdomain_codeset($domain, $codeset) { + return _bind_textdomain_codeset($domain, $codeset); + } + function textdomain($domain) { + return _textdomain($domain); + } + function gettext($msgid) { + return _gettext($msgid); + } + function _($msgid) { + return __($msgid); + } + function ngettext($single, $plural, $number) { + return _ngettext($single, $plural, $number); + } + function dgettext($domain, $msgid) { + return _dgettext($domain, $msgid); + } + function dngettext($domain, $single, $plural, $number) { + return _dngettext($domain, $single, $plural, $number); + } + function dcgettext($domain, $msgid, $category) { + return _dcgettext($domain, $msgid, $category); + } + function dcngettext($domain, $single, $plural, $number, $category) { + return _dcngettext($domain, $single, $plural, $number, $category); + } + function pgettext($context, $msgid) { + return _pgettext($context, $msgid); + } + function npgettext($context, $single, $plural, $number) { + return _npgettext($context, $single, $plural, $number); + } + function dpgettext($domain, $context, $msgid) { + return _dpgettext($domain, $context, $msgid); + } + function dnpgettext($domain, $context, $single, $plural, $number) { + return _dnpgettext($domain, $context, $single, $plural, $number); + } + function dcpgettext($domain, $context, $msgid, $category) { + return _dcpgettext($domain, $context, $msgid, $category); + } + function dcnpgettext($domain, $context, $single, $plural, + $number, $category) { + return _dcnpgettext($domain, $context, $single, $plural, + $number, $category); + } } ?> diff --git a/src/php-gettext/gettext.php b/src/php-gettext/gettext.php index 978794c..a121f9c 100644 --- a/src/php-gettext/gettext.php +++ b/src/php-gettext/gettext.php @@ -1,8 +1,8 @@ . + Copyright (c) 2003, 2009 Danilo Segan . Copyright (c) 2005 Nico Kaiser - + This file is part of PHP-gettext. PHP-gettext is free software; you can redistribute it and/or modify @@ -20,13 +20,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - + /** * Provides a simple gettext replacement that works independently from * the system's gettext abilities. * It can read MO files and use them for translating strings. * The files are passed to gettext_reader as a Stream (see streams.php) - * + * * This version has the ability to cache all strings and translations to * speed up the string lookup. * While the cache is enabled by default, it can be switched off with the @@ -36,7 +36,7 @@ class gettext_reader { //public: var $error = 0; // public variable that holds error code (0 if no error) - + //private: var $BYTEORDER = 0; // 0: low endian, 1: big endian var $STREAM = NULL; @@ -52,27 +52,33 @@ class gettext_reader { /* Methods */ - - + + /** * Reads a 32bit Integer from the Stream - * + * * @access private * @return Integer from the Stream */ function readint() { if ($this->BYTEORDER == 0) { // low endian - return array_shift(unpack('V', $this->STREAM->read(4))); + $input=unpack('V', $this->STREAM->read(4)); + return array_shift($input); } else { // big endian - return array_shift(unpack('N', $this->STREAM->read(4))); + $input=unpack('N', $this->STREAM->read(4)); + return array_shift($input); } } + function read($bytes) { + return $this->STREAM->read($bytes); + } + /** * Reads an array of Integers from the Stream - * + * * @param int count How many elements should be read * @return Array of Integers */ @@ -85,10 +91,10 @@ class gettext_reader { return unpack('N'.$count, $this->STREAM->read(4 * $count)); } } - + /** * Constructor - * + * * @param object Reader the StreamReader object * @param boolean enable_cache Enable or disable caching of strings (default on) */ @@ -98,39 +104,37 @@ class gettext_reader { $this->short_circuit = true; return; } - + // Caching can be turned off $this->enable_cache = $enable_cache; - // $MAGIC1 = (int)0x950412de; //bug in PHP 5 - $MAGIC1 = (int) - 1794895138; - // $MAGIC2 = (int)0xde120495; //bug - $MAGIC2 = (int) - 569244523; + $MAGIC1 = "\x95\x04\x12\xde"; + $MAGIC2 = "\xde\x12\x04\x95"; $this->STREAM = $Reader; - $magic = $this->readint(); - if ($magic == $MAGIC1 || $magic == ($MAGIC1 & 0xFFFFFFFF)) { - $this->BYTEORDER = 0; - } elseif ($magic == $MAGIC2 || $magic == ($MAGIC2 & 0xFFFFFFFF)) { + $magic = $this->read(4); + if ($magic == $MAGIC1) { $this->BYTEORDER = 1; + } elseif ($magic == $MAGIC2) { + $this->BYTEORDER = 0; } else { $this->error = 1; // not MO file return false; } - + // FIXME: Do we care about revision? We should. $revision = $this->readint(); - + $this->total = $this->readint(); $this->originals = $this->readint(); $this->translations = $this->readint(); } - + /** * Loads the translation tables from the MO file into the cache * If caching is enabled, also loads all strings into a cache * to speed up translation lookups - * + * * @access private */ function load_tables() { @@ -138,13 +142,17 @@ class gettext_reader { is_array($this->table_originals) && is_array($this->table_translations)) return; - + /* get original and translations tables */ - $this->STREAM->seekto($this->originals); - $this->table_originals = $this->readintarray($this->total * 2); - $this->STREAM->seekto($this->translations); - $this->table_translations = $this->readintarray($this->total * 2); - + if (!is_array($this->table_originals)) { + $this->STREAM->seekto($this->originals); + $this->table_originals = $this->readintarray($this->total * 2); + } + if (!is_array($this->table_translations)) { + $this->STREAM->seekto($this->translations); + $this->table_translations = $this->readintarray($this->total * 2); + } + if ($this->enable_cache) { $this->cache_translations = array (); /* read all strings in the cache */ @@ -157,10 +165,10 @@ class gettext_reader { } } } - + /** * Returns a string from the "originals" table - * + * * @access private * @param int num Offset number of original string * @return string Requested string if found, otherwise '' @@ -174,10 +182,10 @@ class gettext_reader { $data = $this->STREAM->read($length); return (string)$data; } - + /** * Returns a string from the "translations" table - * + * * @access private * @param int num Offset number of original string * @return string Requested string if found, otherwise '' @@ -191,10 +199,10 @@ class gettext_reader { $data = $this->STREAM->read($length); return (string)$data; } - + /** * Binary search for string - * + * * @access private * @param string string * @param int start (internally used in recursive function) @@ -232,10 +240,10 @@ class gettext_reader { return $this->find_string($string, $half, $end); } } - + /** * Translates a string - * + * * @access public * @param string string to be translated * @return string translated string (or original, if not found) @@ -243,8 +251,8 @@ class gettext_reader { function translate($string) { if ($this->short_circuit) return $string; - $this->load_tables(); - + $this->load_tables(); + if ($this->enable_cache) { // Caching enabled, get translated string from cache if (array_key_exists($string, $this->cache_translations)) @@ -261,17 +269,66 @@ class gettext_reader { } } + /** + * Sanitize plural form expression for use in PHP eval call. + * + * @access private + * @return string sanitized plural form expression + */ + function sanitize_plural_expression($expr) { + // Get rid of disallowed characters. + $expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr); + + // Add parenthesis for tertiary '?' operator. + $expr .= ';'; + $res = ''; + $p = 0; + for ($i = 0; $i < strlen($expr); $i++) { + $ch = $expr[$i]; + switch ($ch) { + case '?': + $res .= ' ? ('; + $p++; + break; + case ':': + $res .= ') : ('; + break; + case ';': + $res .= str_repeat( ')', $p) . ';'; + $p = 0; + break; + default: + $res .= $ch; + } + } + return $res; + } + + /** + * Parse full PO header and extract only plural forms line. + * + * @access private + * @return string verbatim plural form header field + */ + function extract_plural_forms_header_from_po_header($header) { + if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs)) + $expr = $regs[2]; + else + $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; + return $expr; + } + /** * Get possible plural forms from MO header - * + * * @access private * @return string plural form header */ function get_plural_forms() { - // lets assume message number 0 is header + // lets assume message number 0 is header // this is true, right? $this->load_tables(); - + // cache header field for plural forms if (! is_string($this->pluralheader)) { if ($this->enable_cache) { @@ -279,18 +336,15 @@ class gettext_reader { } else { $header = $this->get_translation_string(0); } - if (eregi("plural-forms: ([^\n]*)\n", $header, $regs)) - $expr = $regs[1]; - else - $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; - $this->pluralheader = $expr; + $expr = $this->extract_plural_forms_header_from_po_header($header); + $this->pluralheader = $this->sanitize_plural_expression($expr); } return $this->pluralheader; } /** * Detects which plural form to take - * + * * @access private * @param n count * @return int array index of the right plural form @@ -300,7 +354,7 @@ class gettext_reader { $string = str_replace('nplurals',"\$total",$string); $string = str_replace("n",$n,$string); $string = str_replace('plural',"\$plural",$string); - + $total = 0; $plural = 0; @@ -311,7 +365,7 @@ class gettext_reader { /** * Plural version of gettext - * + * * @access public * @param string single * @param string plural @@ -327,12 +381,12 @@ class gettext_reader { } // find out the appropriate form - $select = $this->select_string($number); - + $select = $this->select_string($number); + // this should contains all strings separated by NULLs - $key = $single.chr(0).$plural; - - + $key = $single . chr(0) . $plural; + + if ($this->enable_cache) { if (! array_key_exists($key, $this->cache_translations)) { return ($number != 1) ? $plural : $single; @@ -353,6 +407,15 @@ class gettext_reader { } } + function pgettext($context, $msgid) { + $key = $context . chr(4) . $msgid; + return $this->translate($key); + } + + function npgettext($context, $singular, $plural, $number) { + $singular = $context . chr(4) . $singular; + return $this->ngettext($singular, $plural, $number); + } } ?> diff --git a/src/php-gettext/streams.php b/src/php-gettext/streams.php index 4237de1..3cdc158 100644 --- a/src/php-gettext/streams.php +++ b/src/php-gettext/streams.php @@ -1,6 +1,6 @@ . + Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan . This file is part of PHP-gettext. @@ -21,29 +21,29 @@ */ -// Simple class to wrap file streams, string streams, etc. -// seek is essential, and it should be byte stream + // Simple class to wrap file streams, string streams, etc. + // seek is essential, and it should be byte stream class StreamReader { // should return a string [FIXME: perhaps return array of bytes?] function read($bytes) { return false; } - + // should return new position function seekto($position) { return false; } - + // returns current position function currentpos() { return false; } - + // returns length of entire stream (limit for seekto()s) function length() { return false; } -} +}; class StringReader { var $_pos; @@ -78,7 +78,7 @@ class StringReader { return strlen($this->_str); } -} +}; class FileReader { @@ -93,8 +93,8 @@ class FileReader { $this->_pos = 0; $this->_fd = fopen($filename,'rb'); if (!$this->_fd) { - $this->error = 3; // Cannot read file, probably permissions - return false; + $this->error = 3; // Cannot read file, probably permissions + return false; } } else { $this->error = 2; // File doesn't exist @@ -115,7 +115,7 @@ class FileReader { $bytes -= strlen($chunk); } $this->_pos = ftell($this->_fd); - + return $data; } else return ''; } @@ -138,9 +138,9 @@ class FileReader { fclose($this->_fd); } -} +}; -// Preloads entire file in memory first, then creates a StringReader +// Preloads entire file in memory first, then creates a StringReader // over it (it assumes knowledge of StringReader internals) class CachedFileReader extends StringReader { function CachedFileReader($filename) { @@ -150,8 +150,8 @@ class CachedFileReader extends StringReader { $fd = fopen($filename,'rb'); if (!$fd) { - $this->error = 3; // Cannot read file, probably permissions - return false; + $this->error = 3; // Cannot read file, probably permissions + return false; } $this->_str = fread($fd, $length); fclose($fd); @@ -161,7 +161,7 @@ class CachedFileReader extends StringReader { return false; } } -} +}; ?> diff --git a/src/php-gettext/tests/LocalesTest.php b/src/php-gettext/tests/LocalesTest.php new file mode 100644 index 0000000..3000286 --- /dev/null +++ b/src/php-gettext/tests/LocalesTest.php @@ -0,0 +1,66 @@ +assertEquals('sr_RS', _setlocale(LC_MESSAGES, 0)); + + // For an existing locale, it never needs emulation. + putenv("LANG=C"); + _setlocale(LC_MESSAGES, ""); + $this->assertEquals(0, locale_emulation()); + + // If we set it to a non-existent locale, it still works, but uses + // emulation. + _setlocale(LC_MESSAGES, "xxx_XXX"); + $this->assertEquals('xxx_XXX', _setlocale(LC_MESSAGES, 0)); + $this->assertEquals(1, locale_emulation()); + } + + public function test_get_list_of_locales() + { + // For a locale containing country code, we prefer + // full locale name, but if that's not found, fall back + // to the language only locale name. + $this->assertEquals(array("sr_RS", "sr"), + get_list_of_locales("sr_RS")); + + // If language code is used, it's the only thing returned. + $this->assertEquals(array("sr"), + get_list_of_locales("sr")); + + // There is support for language and charset only. + $this->assertEquals(array("sr.UTF-8", "sr"), + get_list_of_locales("sr.UTF-8")); + + // It can also split out character set from the full locale name. + $this->assertEquals(array("sr_RS.UTF-8", "sr_RS", "sr"), + get_list_of_locales("sr_RS.UTF-8")); + + // There is support for @modifier in locale names as well. + $this->assertEquals(array("sr_RS.UTF-8@latin", "sr_RS@latin", "sr@latin", + "sr_RS.UTF-8", "sr_RS", "sr"), + get_list_of_locales("sr_RS.UTF-8@latin")); + + // We can pass in only language and modifier. + $this->assertEquals(array("sr@latin", "sr"), + get_list_of_locales("sr@latin")); + + + // If locale name is not following the regular POSIX pattern, + // it's used verbatim. + $this->assertEquals(array("something"), + get_list_of_locales("something")); + + // Passing in an empty string returns an empty array. + $this->assertEquals(array(), + get_list_of_locales("")); + } +} + +?> diff --git a/src/php-gettext/tests/ParsingTest.php b/src/php-gettext/tests/ParsingTest.php new file mode 100644 index 0000000..9b350b2 --- /dev/null +++ b/src/php-gettext/tests/ParsingTest.php @@ -0,0 +1,43 @@ +assertEquals( + 'nplurals=2; plural=n == 1 ? 0 : 1;', + $parser->extract_plural_forms_header_from_po_header("")); + + // Extracting it from the middle of the header works. + $this->assertEquals( + 'nplurals=1; plural=0;', + $parser->extract_plural_forms_header_from_po_header( + "Content-type: text/html; charset=UTF-8\n" + ."Plural-Forms: nplurals=1; plural=0;\n" + ."Last-Translator: nobody\n" + )); + + // It's also case-insensitive. + $this->assertEquals( + 'nplurals=1; plural=0;', + $parser->extract_plural_forms_header_from_po_header( + "PLURAL-forms: nplurals=1; plural=0;\n" + )); + + // It falls back to default if it's not on a separate line. + $this->assertEquals( + 'nplurals=2; plural=n == 1 ? 0 : 1;', + $parser->extract_plural_forms_header_from_po_header( + "Content-type: text/html; charset=UTF-8" // note the missing \n here + ."Plural-Forms: nplurals=1; plural=0;\n" + ."Last-Translator: nobody\n" + )); + + } + +} +?> From f20135d16a0fa2bb8cd2820323052fccab8f3451 Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:08:58 +0000 Subject: [PATCH 052/173] add gettext changelog entry git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@764 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 2633fc8..e2562e4 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -6,6 +6,7 @@ ChangeLog for SemantiScuttle - Fix bug getTagsForBookmarks() that fetched all tags - 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 From b9256b14377251e6303d6abbe3cd7e7edc2583a4 Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:09:58 +0000 Subject: [PATCH 053/173] add header to httpauth.inc.php git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@765 b3834d28-1941-0410-a4f8-b48e95affb8f --- www/api/httpauth.inc.php | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) 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 @@ + * @author Christian Weiske + * @author Eric Dane + * @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 { From 6b3f1d476ea0d1be46b3e4a7df6983320865303b Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:10:53 +0000 Subject: [PATCH 054/173] update docblocks git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@766 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/TestBaseApi.php | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) 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) { From b31886b67aaa814dc0ffe48bb7d5c4863de0106e Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:11:59 +0000 Subject: [PATCH 055/173] tests for deleting bookmarks via the API. two of them fail currently because of a security issue git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@767 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/Api/PostsDeleteTest.php | 302 ++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 tests/Api/PostsDeleteTest.php diff --git a/tests/Api/PostsDeleteTest.php b/tests/Api/PostsDeleteTest.php new file mode 100644 index 0000000..705f94e --- /dev/null +++ b/tests/Api/PostsDeleteTest.php @@ -0,0 +1,302 @@ + + * @author Christian Weiske + * @author Eric Dane + * @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 + * @author Christian Weiske + * @author Eric Dane + * @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(); + + //401 - unauthorized + $this->assertEquals(401, $res->getStatus()); + //verify MIME content type + $this->assertEquals( + 'text/xml; charset=utf-8', + $res->getHeader('content-type') + ); + + //verify xml + $this->assertNotTag( + array( + 'tag' => 'result', + 'attributes' => array('code' => 'done') + ), + $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 From df8216d607a9806b57b83eea9eb55577eae7d54f Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:12:55 +0000 Subject: [PATCH 056/173] execute api tests git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@768 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/AllTests.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/AllTests.php b/tests/AllTests.php index d29de7f..61e1a57 100644 --- a/tests/AllTests.php +++ b/tests/AllTests.php @@ -64,6 +64,8 @@ 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/PostsDeleteTest.php'); return $suite; } From 22c9a01ee845d2b92fcab6b6cb10ac6ff0eec52e Mon Sep 17 00:00:00 2001 From: cweiske Date: Tue, 28 Sep 2010 22:14:31 +0000 Subject: [PATCH 057/173] rewrite api/posts/delete to be more secure and add unit tests for it git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@769 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/Bookmark.php | 18 ++++--- tests/Api/PostsDeleteTest.php | 9 ++-- www/api/posts_delete.php | 63 +++++++++++++++++------- 3 files changed, 62 insertions(+), 28 deletions(-) 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/Api/PostsDeleteTest.php b/tests/Api/PostsDeleteTest.php index 705f94e..626746f 100644 --- a/tests/Api/PostsDeleteTest.php +++ b/tests/Api/PostsDeleteTest.php @@ -202,8 +202,9 @@ class Api_PostsDeleteTest extends TestBaseApi //send request $res = $req->send(); - //401 - unauthorized - $this->assertEquals(401, $res->getStatus()); + //404 - user does not have that bookmark + $this->assertEquals(404, $res->getStatus()); + //verify MIME content type $this->assertEquals( 'text/xml; charset=utf-8', @@ -211,10 +212,10 @@ class Api_PostsDeleteTest extends TestBaseApi ); //verify xml - $this->assertNotTag( + $this->assertTag( array( 'tag' => 'result', - 'attributes' => array('code' => 'done') + 'attributes' => array('code' => 'something went wrong') ), $res->getBody(), '', false diff --git a/www/api/posts_delete.php b/www/api/posts_delete.php index a63cc62..982b686 100644 --- a/www/api/posts_delete.php +++ b/www/api/posts_delete.php @@ -1,33 +1,60 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ // 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'])) { +if (!isset($_REQUEST['url']) + || $_REQUEST['url'] == '' +) { $deleted = false; +} else if (!$bs->bookmarkExists($_REQUEST['url'], $uId)) { + //the user does not have such a bookmark + // 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. + // this sucks, and I don't care about being different but correct here. + header('HTTP/1.0 404 Not Found'); + $deleted = false; + } 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); + if (!$deleted) { + //something really went wrong + header('HTTP/1.0 500 Internal Server Error'); + } } // Set up the XML file and output the result. -echo '\r\n"; -echo ''; +echo '\r\n"; +echo ''; ?> \ No newline at end of file From 70c39a8eea7896271c0ad3f0c435ec06c64074d1 Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:49:14 +0000 Subject: [PATCH 058/173] delicious returns a proper error message when deleting non-existant items, which we do now, too git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@770 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/developers/api | 10 ++++++++++ tests/Api/PostsDeleteTest.php | 2 +- www/api/posts_delete.php | 14 +++++--------- 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 doc/developers/api 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/tests/Api/PostsDeleteTest.php b/tests/Api/PostsDeleteTest.php index 626746f..d9fb6cd 100644 --- a/tests/Api/PostsDeleteTest.php +++ b/tests/Api/PostsDeleteTest.php @@ -215,7 +215,7 @@ class Api_PostsDeleteTest extends TestBaseApi $this->assertTag( array( 'tag' => 'result', - 'attributes' => array('code' => 'something went wrong') + 'attributes' => array('code' => 'item not found') ), $res->getBody(), '', false diff --git a/www/api/posts_delete.php b/www/api/posts_delete.php index 982b686..03cc968 100644 --- a/www/api/posts_delete.php +++ b/www/api/posts_delete.php @@ -4,8 +4,6 @@ * The delicious API is implemented here. * * The delicious API behaves like that: - * - returns "done" even if the bookmark doesn't exist - * - we do it correctly * - does NOT allow the hash for the url parameter * - doesn't set the Content-Type to text/xml * - we do it correctly, too @@ -35,26 +33,24 @@ $uId = $userservice->getCurrentUserId(); if (!isset($_REQUEST['url']) || $_REQUEST['url'] == '' ) { - $deleted = false; + $msg = 'something went wrong'; } else if (!$bs->bookmarkExists($_REQUEST['url'], $uId)) { //the user does not have such a bookmark - // 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. - // this sucks, and I don't care about being different but correct here. header('HTTP/1.0 404 Not Found'); - $deleted = false; - + $msg = 'item not found'; } else { $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 '\r\n"; -echo ''; +echo ''; ?> \ No newline at end of file From 20ec8b4958dc2d6a4a08f9b0dcae27c90f0155ef Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:50:38 +0000 Subject: [PATCH 059/173] test for api/posts/update git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@771 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/AllTests.php | 1 + tests/Api/PostsUpdateTest.php | 135 ++++++++++++++++++++++++++++++++++ www/api/posts_delete.php | 1 + www/api/posts_update.php | 46 ++++++++---- 4 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 tests/Api/PostsUpdateTest.php diff --git a/tests/AllTests.php b/tests/AllTests.php index 61e1a57..799f43b 100644 --- a/tests/AllTests.php +++ b/tests/AllTests.php @@ -66,6 +66,7 @@ class AllTests extends PHPUnit_Framework_TestSuite $suite->addTestFile($tdir . '/UserTest.php'); $suite->addTestFile($tdir . '/Api/ExportCsvTest.php'); $suite->addTestFile($tdir . '/Api/PostsDeleteTest.php'); + $suite->addTestFile($tdir . '/Api/PostsUpdateTest.php'); return $suite; } 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 @@ + + * @author Christian Weiske + * @author Eric Dane + * @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 + * @author Christian Weiske + * @author Eric Dane + * @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/www/api/posts_delete.php b/www/api/posts_delete.php index 03cc968..69b2429 100644 --- a/www/api/posts_delete.php +++ b/www/api/posts_delete.php @@ -19,6 +19,7 @@ * @author Eric Dane * @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! 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 @@ + * @author Christian Weiske + * @author Eric Dane + * @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 '\r\n"; -foreach($bookmarks['bookmarks'] as $row) { - echo ''; +echo '\r\n"; +//foreach is used in case there are no bookmarks +foreach ($bookmarks['bookmarks'] as $row) { + echo ''; } ?> \ No newline at end of file From 2f3b23f96a5f595c9d0ef02abf5037f41e907390 Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:52:01 +0000 Subject: [PATCH 060/173] first tests for api/posts/add git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@772 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/AllTests.php | 1 + tests/Api/PostsAddTest.php | 197 +++++++++++++++++++++++++++++++++++++ www/api/posts_add.php | 2 + 3 files changed, 200 insertions(+) create mode 100644 tests/Api/PostsAddTest.php diff --git a/tests/AllTests.php b/tests/AllTests.php index 799f43b..ded6824 100644 --- a/tests/AllTests.php +++ b/tests/AllTests.php @@ -65,6 +65,7 @@ class AllTests extends PHPUnit_Framework_TestSuite $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..1db8af9 --- /dev/null +++ b/tests/Api/PostsAddTest.php @@ -0,0 +1,197 @@ + + * @author Christian Weiske + * @author Eric Dane + * @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 + * @author Christian Weiske + * @author Eric Dane + * @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 = <<?&\$ÄÖ'"§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)); + $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']); + } + + + + /** + * 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 = <<?&\$ÄÖ'"§special"' +characters +TXT; + + list($req, $uId) = $this->getAuthRequest( + '?url=' . urlencode($bmUrl) + . '&description=' . urlencode($bmTitle) + . '&extended=' . urlencode($bmDescription) + . '&tags=' . urlencode(implode(' ', $bmTags)) + ); + $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']); + } + +} + +if (PHPUnit_MAIN_METHOD == 'Api_PostsAddTest::main') { + Api_PostsAddTest::main(); +} +?> \ No newline at end of file diff --git a/www/api/posts_add.php b/www/api/posts_add.php index 59f7dce..b392a80 100644 --- a/www/api/posts_add.php +++ b/www/api/posts_add.php @@ -5,6 +5,8 @@ // del.icio.us behavior: // - tags can't have spaces // - address and description are mandatory +// - description == title in semanticscuttle +// - extended == description in semanticscuttle // Scuttle behavior: // - Additional 'status' variable for privacy From c9398ccd5b5208b87feef21d3996761c8be590a7 Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:52:59 +0000 Subject: [PATCH 061/173] first tests for api/posts/add git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@773 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/Api/PostsAddTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Api/PostsAddTest.php b/tests/Api/PostsAddTest.php index 1db8af9..cc9a2c9 100644 --- a/tests/Api/PostsAddTest.php +++ b/tests/Api/PostsAddTest.php @@ -101,6 +101,7 @@ TXT; $req->addPostParameter('description', $bmTitle); $req->addPostParameter('extended', $bmDescription); $req->addPostParameter('tags', implode(' ', $bmTags)); + $req->addPostParameter('dt', $bmDatetime); $res = $req->send(); //all should be well @@ -130,6 +131,10 @@ TXT; $this->assertEquals($bmTitle, $bm['bTitle']); $this->assertEquals($bmDescription, $bm['bDescription']); $this->assertEquals($bmTags, $bm['tags']); + $this->assertEquals( + strtotime($bmDatetime), + strtotime($bm['bDatetime']) + ); } From da272b5a20bec8d39c6d9cdf2fd5573de99083be Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:53:57 +0000 Subject: [PATCH 062/173] check datetime setting git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@774 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/Api/PostsAddTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/Api/PostsAddTest.php b/tests/Api/PostsAddTest.php index cc9a2c9..0769bdd 100644 --- a/tests/Api/PostsAddTest.php +++ b/tests/Api/PostsAddTest.php @@ -132,8 +132,8 @@ TXT; $this->assertEquals($bmDescription, $bm['bDescription']); $this->assertEquals($bmTags, $bm['tags']); $this->assertEquals( - strtotime($bmDatetime), - strtotime($bm['bDatetime']) + gmdate('Y-m-d H:i:s', strtotime($bmDatetime)), + $bm['bDatetime'] ); } @@ -162,6 +162,7 @@ TXT; . '&description=' . urlencode($bmTitle) . '&extended=' . urlencode($bmDescription) . '&tags=' . urlencode(implode(' ', $bmTags)) + . '&dt=' . urlencode($bmDatetime) ); $res = $req->send(); @@ -192,6 +193,10 @@ TXT; $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'] + ); } } From 3ff661c0e0180b7808b1b95f9d25cc736d710026 Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:55:14 +0000 Subject: [PATCH 063/173] send 400 status code if a parameter is missing git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@775 b3834d28-1941-0410-a4f8-b48e95affb8f --- tests/Api/PostsAddTest.php | 107 +++++++++++++++++++++++++++++++++++++ www/api/posts_add.php | 26 +++++---- 2 files changed, 124 insertions(+), 9 deletions(-) diff --git a/tests/Api/PostsAddTest.php b/tests/Api/PostsAddTest.php index 0769bdd..dea002e 100644 --- a/tests/Api/PostsAddTest.php +++ b/tests/Api/PostsAddTest.php @@ -199,6 +199,113 @@ TXT; ); } + /** + * 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']); + } } if (PHPUnit_MAIN_METHOD == 'Api_PostsAddTest::main') { diff --git a/www/api/posts_add.php b/www/api/posts_add.php index b392a80..8b63a16 100644 --- a/www/api/posts_add.php +++ b/www/api/posts_add.php @@ -73,17 +73,25 @@ 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! + if ($bookmarkservice->bookmarkExists($url, $userservice->getCurrentUserId())) { + $msg = 'something went wrong'; + } else { + $added = $bookmarkservice->addBookmark( + $url, $description, $extended, '', $status, $tags, null, $dt, true + ); + $msg = 'done'; + } } // Set up the XML file and output the result. -echo '\r\n"; -echo ''; +echo '\r\n"; +echo ''; ?> \ No newline at end of file From 66af94feaf9ef817da260c0d293577bf74e53bcf Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:56:10 +0000 Subject: [PATCH 064/173] reformat api/posts/add and document it better git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@776 b3834d28-1941-0410-a4f8-b48e95affb8f --- www/api/posts_add.php | 115 +++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 41 deletions(-) diff --git a/www/api/posts_add.php b/www/api/posts_add.php index 8b63a16..0e06d50 100644 --- a/www/api/posts_add.php +++ b/www/api/posts_add.php @@ -1,69 +1,102 @@ + * @author Christian Weiske + * @author Eric Dane + * @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; +} $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; } } } @@ -81,10 +114,10 @@ if (is_null($url)) { $msg = 'Description missing'; } else { // We're good with info; now insert it! - if ($bookmarkservice->bookmarkExists($url, $userservice->getCurrentUserId())) { + if ($bs->bookmarkExists($url, $userservice->getCurrentUserId())) { $msg = 'something went wrong'; } else { - $added = $bookmarkservice->addBookmark( + $added = $bs->addBookmark( $url, $description, $extended, '', $status, $tags, null, $dt, true ); $msg = 'done'; From 8e2b25a095ab769146ac7fa672d7a4c4eda32ab4 Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 20:57:30 +0000 Subject: [PATCH 065/173] api/posts/add respects the "replace" parameter now git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@777 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/ChangeLog | 7 ++- tests/Api/PostsAddTest.php | 121 +++++++++++++++++++++++++++++++++++++ www/api/posts_add.php | 24 ++++++-- 3 files changed, 145 insertions(+), 7 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index e2562e4..d711dd4 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -3,12 +3,13 @@ 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.0 - 2010-06-09 diff --git a/tests/Api/PostsAddTest.php b/tests/Api/PostsAddTest.php index dea002e..1f21d04 100644 --- a/tests/Api/PostsAddTest.php +++ b/tests/Api/PostsAddTest.php @@ -306,6 +306,127 @@ TXT; $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') { diff --git a/www/api/posts_add.php b/www/api/posts_add.php index 0e06d50..7f9dc59 100644 --- a/www/api/posts_add.php +++ b/www/api/posts_add.php @@ -16,6 +16,8 @@ * - 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 @@ -23,7 +25,6 @@ * - delicious "description" is the "title" in SemanticScuttle * - delicious "extended" is the "description" in SemanticScuttle * - "status" is a SemanticScuttle addition to this API method - * - SemanticScuttle currently ignores the "replace" parameter * * SemanticScuttle - your social bookmark manager. * @@ -78,6 +79,8 @@ if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != '')) { $dt = null; } +$replace = isset($_REQUEST['replace']) && ($_REQUEST['replace'] == 'yes'); + $status = 0; if (isset($_REQUEST['status'])) { $status_str = trim($_REQUEST['status']); @@ -114,9 +117,22 @@ if (is_null($url)) { $msg = 'Description missing'; } else { // We're good with info; now insert it! - if ($bs->bookmarkExists($url, $userservice->getCurrentUserId())) { - $msg = 'something went wrong'; - } else { + $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 ); From e1f9d9e3a07652d4ec668a2337d8b788e031ab9f Mon Sep 17 00:00:00 2001 From: cweiske Date: Wed, 29 Sep 2010 22:01:29 +0000 Subject: [PATCH 066/173] merge changelog from 0.97 branch git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@781 b3834d28-1941-0410-a4f8-b48e95affb8f --- doc/ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index d711dd4..0a60bff 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -12,6 +12,14 @@ ChangeLog for SemantiScuttle - 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 ------------------- - Many SQL optimizations - SemanticScuttle shows bookmarks 4 times faster now From 95c05e49a70d007f41be60f90a7ae0ed7e5996a2 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 30 Sep 2010 07:58:07 +0200 Subject: [PATCH 067/173] remove deprecate split() --- src/SemanticScuttle/Service/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php index 6fc3bb7..d74d104 100644 --- a/src/SemanticScuttle/Service/User.php +++ b/src/SemanticScuttle/Service/User.php @@ -359,7 +359,7 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService return $_SESSION[$this->getSessionKey()]; } else if (isset($_COOKIE[$this->getCookieKey()])) { - $cook = split(':', $_COOKIE[$this->getCookieKey()]); + $cook = explode(':', $_COOKIE[$this->getCookieKey()]); //cookie looks like this: 'id:md5(username+password)' $query = 'SELECT * FROM '. $this->getTableName() . ' WHERE MD5(CONCAT('.$this->getFieldName('username') . From b53b77d0dcf2ea89087d23ad037063dd8f620331 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 30 Sep 2010 19:12:49 +0200 Subject: [PATCH 068/173] make sidebar menu2 use jquery.jstree. ajax loading of subtags is still missing --- data/templates/sidebar.block.menu2.php | 18 +- www/js/jquery-1.4.2.js | 6240 ++++++++++++++++++++++++ www/js/jquery-1.4.2.min.js | 154 + www/js/jquery.jstree.js | 3510 +++++++++++++ www/js/themes/apple/bg.jpg | Bin 0 -> 331 bytes www/js/themes/apple/d.png | Bin 0 -> 7765 bytes www/js/themes/apple/dot_for_ie.gif | Bin 0 -> 43 bytes www/js/themes/apple/style.css | 60 + www/js/themes/apple/throbber.gif | Bin 0 -> 1849 bytes www/js/themes/classic/d.png | Bin 0 -> 7535 bytes www/js/themes/classic/dot_for_ie.gif | Bin 0 -> 43 bytes www/js/themes/classic/style.css | 59 + www/js/themes/classic/throbber.gif | Bin 0 -> 1849 bytes www/js/themes/default-rtl/d.gif | Bin 0 -> 2872 bytes www/js/themes/default-rtl/d.png | Bin 0 -> 7459 bytes www/js/themes/default-rtl/dots.gif | Bin 0 -> 132 bytes www/js/themes/default-rtl/style.css | 83 + www/js/themes/default-rtl/throbber.gif | Bin 0 -> 1849 bytes www/js/themes/default/d.gif | Bin 0 -> 2944 bytes www/js/themes/default/d.png | Bin 0 -> 7635 bytes www/js/themes/default/style.css | 73 + www/js/themes/default/throbber.gif | Bin 0 -> 1849 bytes www/scuttle.css | 3 +- 23 files changed, 10197 insertions(+), 3 deletions(-) create mode 100644 www/js/jquery-1.4.2.js create mode 100644 www/js/jquery-1.4.2.min.js create mode 100644 www/js/jquery.jstree.js create mode 100644 www/js/themes/apple/bg.jpg create mode 100644 www/js/themes/apple/d.png create mode 100644 www/js/themes/apple/dot_for_ie.gif create mode 100644 www/js/themes/apple/style.css create mode 100644 www/js/themes/apple/throbber.gif create mode 100644 www/js/themes/classic/d.png create mode 100644 www/js/themes/classic/dot_for_ie.gif create mode 100644 www/js/themes/classic/style.css create mode 100644 www/js/themes/classic/throbber.gif create mode 100644 www/js/themes/default-rtl/d.gif create mode 100644 www/js/themes/default-rtl/d.png create mode 100644 www/js/themes/default-rtl/dots.gif create mode 100644 www/js/themes/default-rtl/style.css create mode 100644 www/js/themes/default-rtl/throbber.gif create mode 100644 www/js/themes/default/d.gif create mode 100644 www/js/themes/default/d.png create mode 100644 www/js/themes/default/style.css create mode 100644 www/js/themes/default/throbber.gif diff --git a/data/templates/sidebar.block.menu2.php b/data/templates/sidebar.block.menu2.php index 00ad74f..81ee121 100644 --- a/data/templates/sidebar.block.menu2.php +++ b/data/templates/sidebar.block.menu2.php @@ -1,3 +1,5 @@ + + 0) { foreach ($menu2Tags as $menu2Tag) { echo '

  4. ' . sprintf( - '%s', + '%s', sprintf($cat_url, $menu2Tag), $menu2Tag ) @@ -46,10 +48,22 @@ foreach ($menu2Tags as $menu2Tag) { echo ''; */ } +//ROOT.'ajax/getadminlinkedtags.php?tag='.filter($menu2Tag, 'url') ?> - + diff --git a/www/js/jquery-1.4.2.js b/www/js/jquery-1.4.2.js new file mode 100644 index 0000000..fff6776 --- /dev/null +++ b/www/js/jquery-1.4.2.js @@ -0,0 +1,6240 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function( window, undefined ) { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, + + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // Has the ready events already been bound? + readyBound = false, + + // The functions to execute on DOM ready + readyList = [], + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwnProperty = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + indexOf = Array.prototype.indexOf; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + if ( elem ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $("TAG") + } else if ( !context && /^\w+$/.test( selector ) ) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName( selector ); + return jQuery.merge( this, selector ); + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return jQuery( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.4.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = jQuery(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // If the DOM is already ready + if ( jQuery.isReady ) { + // Execute the function immediately + fn.call( document, jQuery ); + + // Otherwise, remember the function for later + } else if ( readyList ) { + // Add the function to the wait list + readyList.push( fn ); + } + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || jQuery(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging object literal values or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { + var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src + : jQuery.isArray(copy) ? [] : {}; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // Handle when the DOM is ready + ready: function() { + // Make sure that the DOM is not already loaded + if ( !jQuery.isReady ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 13 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If there are functions bound, to execute + if ( readyList ) { + // Execute all of them + var fn, i = 0; + while ( (fn = readyList[ i++ ]) ) { + fn.call( document, jQuery ); + } + + // Reset the list of functions + readyList = null; + } + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyBound ) { + return; + } + + readyBound = true; + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + return jQuery.ready(); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", DOMContentLoaded); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwnProperty.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") + .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) { + + // Try to use the native JSON parser first + return window.JSON && window.JSON.parse ? + window.JSON.parse( data ) : + (new Function("return " + data))(); + + } else { + jQuery.error( "Invalid JSON: " + data ); + } + }, + + noop: function() {}, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && rnotwhite.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + + if ( jQuery.support.scriptEval ) { + script.appendChild( document.createTextNode( data ) ); + } else { + script.text = data; + } + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction(object); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } + } + + return object; + }, + + trim: function( text ) { + return (text || "").replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = []; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + if ( !inv !== !callback( elems[ i ], i ) ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var ret = [], value; + + // Go through the array, translating each of the items to their + // new value (or values). + for ( var i = 0, length = elems.length; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + proxy: function( fn, proxy, thisObject ) { + if ( arguments.length === 2 ) { + if ( typeof proxy === "string" ) { + thisObject = fn; + fn = thisObject[ proxy ]; + proxy = undefined; + + } else if ( proxy && !jQuery.isFunction( proxy ) ) { + thisObject = proxy; + proxy = undefined; + } + } + + if ( !proxy && fn ) { + proxy = function() { + return fn.apply( thisObject || this, arguments ); + }; + } + + // Set the guid of unique handler to the same of original handler, so it can be removed + if ( fn ) { + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + } + + // So proxy can be declared as an argument + return proxy; + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + browser: {} +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +if ( indexOf ) { + jQuery.inArray = function( elem, array ) { + return indexOf.call( array, elem ); + }; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch( error ) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +function evalScript( i, elem ) { + if ( elem.src ) { + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + } else { + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } +} + +// Mutifunctional method to get and set values to a collection +// The value/s can be optionally by executed if its a function +function access( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; +} + +function now() { + return (new Date).getTime(); +} +(function() { + + jQuery.support = {}; + + var root = document.documentElement, + script = document.createElement("script"), + div = document.createElement("div"), + id = "script" + now(); + + div.style.display = "none"; + div.innerHTML = "
    a"; + + var all = div.getElementsByTagName("*"), + a = div.getElementsByTagName("a")[0]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return; + } + + jQuery.support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText insted) + style: /red/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute("href") === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: div.getElementsByTagName("input")[0].value === "on", + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected, + + parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null, + + // Will be defined later + deleteExpando: true, + checkClone: false, + scriptEval: false, + noCloneEvent: true, + boxModel: null + }; + + script.type = "text/javascript"; + try { + script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); + } catch(e) {} + + root.insertBefore( script, root.firstChild ); + + // Make sure that the execution of code works by injecting a script + // tag with appendChild/createTextNode + // (IE doesn't support this, fails, and uses .text instead) + if ( window[ id ] ) { + jQuery.support.scriptEval = true; + delete window[ id ]; + } + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete script.test; + + } catch(e) { + jQuery.support.deleteExpando = false; + } + + root.removeChild( script ); + + if ( div.attachEvent && div.fireEvent ) { + div.attachEvent("onclick", function click() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + jQuery.support.noCloneEvent = false; + div.detachEvent("onclick", click); + }); + div.cloneNode(true).fireEvent("onclick"); + } + + div = document.createElement("div"); + div.innerHTML = ""; + + var fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; + + // Figure out if the W3C box model works as expected + // document.body must exist before we can do this + jQuery(function() { + var div = document.createElement("div"); + div.style.width = div.style.paddingLeft = "1px"; + + document.body.appendChild( div ); + jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + document.body.removeChild( div ).style.display = 'none'; + + div = null; + }); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + var eventSupported = function( eventName ) { + var el = document.createElement("div"); + eventName = "on" + eventName; + + var isSupported = (eventName in el); + if ( !isSupported ) { + el.setAttribute(eventName, "return;"); + isSupported = typeof el[eventName] === "function"; + } + el = null; + + return isSupported; + }; + + jQuery.support.submitBubbles = eventSupported("submit"); + jQuery.support.changeBubbles = eventSupported("change"); + + // release memory in IE + root = script = div = all = a = null; +})(); + +jQuery.props = { + "for": "htmlFor", + "class": "className", + readonly: "readOnly", + maxlength: "maxLength", + cellspacing: "cellSpacing", + rowspan: "rowSpan", + colspan: "colSpan", + tabindex: "tabIndex", + usemap: "useMap", + frameborder: "frameBorder" +}; +var expando = "jQuery" + now(), uuid = 0, windowData = {}; + +jQuery.extend({ + cache: {}, + + expando:expando, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + "object": true, + "applet": true + }, + + data: function( elem, name, data ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + return; + } + + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ], cache = jQuery.cache, thisCache; + + if ( !id && typeof name === "string" && data === undefined ) { + return null; + } + + // Compute a unique ID for the element + if ( !id ) { + id = ++uuid; + } + + // Avoid generating a new cache unless none exists and we + // want to manipulate it. + if ( typeof name === "object" ) { + elem[ expando ] = id; + thisCache = cache[ id ] = jQuery.extend(true, {}, name); + + } else if ( !cache[ id ] ) { + elem[ expando ] = id; + cache[ id ] = {}; + } + + thisCache = cache[ id ]; + + // Prevent overriding the named cache with undefined values + if ( data !== undefined ) { + thisCache[ name ] = data; + } + + return typeof name === "string" ? thisCache[ name ] : thisCache; + }, + + removeData: function( elem, name ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + return; + } + + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ]; + + // If we want to remove a specific section of the element's data + if ( name ) { + if ( thisCache ) { + // Remove the section of cache data + delete thisCache[ name ]; + + // If we've removed all the data, remove the element's cache + if ( jQuery.isEmptyObject(thisCache) ) { + jQuery.removeData( elem ); + } + } + + // Otherwise, we want to remove all of the element's data + } else { + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } + + // Completely remove the data cache + delete cache[ id ]; + } + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + if ( typeof key === "undefined" && this.length ) { + return jQuery.data( this[0] ); + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + } + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } else { + return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() { + jQuery.data( this, key, value ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); +jQuery.extend({ + queue: function( elem, type, data ) { + if ( !elem ) { + return; + } + + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( !data ) { + return q || []; + } + + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data) ); + + } else { + q.push( data ); + } + + return q; + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), fn = queue.shift(); + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function( i, elem ) { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + } +}); +var rclass = /[\n\t]/g, + rspace = /\s+/, + rreturn = /\r/g, + rspecialurl = /href|src|style/, + rtype = /(button|input)/i, + rfocusable = /(button|input|object|select|textarea)/i, + rclickable = /^(a|area)$/i, + rradiocheck = /radio|checkbox/; + +jQuery.fn.extend({ + attr: function( name, value ) { + return access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name, fn ) { + return this.each(function(){ + jQuery.attr( this, name, "" ); + if ( this.nodeType === 1 ) { + this.removeAttribute( name ); + } + }); + }, + + addClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.addClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspace ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; + + } else { + var className = " " + elem.className + " ", setClass = elem.className; + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.removeClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split(rspace); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this); + self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, i = 0, self = jQuery(this), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery.data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if ( jQuery.nodeName( elem, "option" ) ) { + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + } + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + } + + + // Everything else, we just grab the value + return (elem.value || "").replace(rreturn, ""); + + } + + return undefined; + } + + var isFunction = jQuery.isFunction(value); + + return this.each(function(i) { + var self = jQuery(this), val = value; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call(this, i, self.val()); + } + + // Typecast each time if the value is a Function and the appended + // value is therefore different each time. + if ( typeof val === "number" ) { + val += ""; + } + + if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { + this.checked = jQuery.inArray( self.val(), val ) >= 0; + + } else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(val); + + jQuery( "option", this ).each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + this.selectedIndex = -1; + } + + } else { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + // don't set attributes on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery(elem)[name](value); + } + + var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), + // Whether we are setting (or getting) + set = value !== undefined; + + // Try to normalize/fix the name + name = notxml && jQuery.props[ name ] || name; + + // Only do all the following if this is a node (faster for style) + if ( elem.nodeType === 1 ) { + // These attributes require special treatment + var special = rspecialurl.test( name ); + + // Safari mis-reports the default selected property of an option + // Accessing the parent's selectedIndex property fixes it + if ( name === "selected" && !jQuery.support.optSelected ) { + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + + // If applicable, access the attribute via the DOM 0 way + if ( name in elem && notxml && !special ) { + if ( set ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } + + elem[ name ] = value; + } + + // browsers index elements by id/name on forms, give priority to attributes. + if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { + return elem.getAttributeNode( name ).nodeValue; + } + + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + if ( name === "tabIndex" ) { + var attributeNode = elem.getAttributeNode( "tabIndex" ); + + return attributeNode && attributeNode.specified ? + attributeNode.value : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + + return elem[ name ]; + } + + if ( !jQuery.support.style && notxml && name === "style" ) { + if ( set ) { + elem.style.cssText = "" + value; + } + + return elem.style.cssText; + } + + if ( set ) { + // convert the value to a string (all browsers do this but IE) see #1070 + elem.setAttribute( name, "" + value ); + } + + var attr = !jQuery.support.hrefNormalized && notxml && special ? + // Some attributes require a special call on IE + elem.getAttribute( name, 2 ) : + elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return attr === null ? undefined : attr; + } + + // elem is actually elem.style ... set the style + // Using attr for specific style information is now deprecated. Use style instead. + return jQuery.style( elem, name, value ); + } +}); +var rnamespaces = /\.(.*)$/, + fcleanup = function( nm ) { + return nm.replace(/[^\w\s\.\|`]/g, function( ch ) { + return "\\" + ch; + }); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // For whatever reason, IE has trouble passing the window object + // around, causing it to be cloned in the process + if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) { + elem = window; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery.data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events = elemData.events || {}, + eventHandle = elemData.handle, eventHandle; + + if ( !eventHandle ) { + elemData.handle = eventHandle = function() { + // Handle the second event of a trigger and when + // an event is called after a page has unloaded + return typeof jQuery !== "undefined" && !jQuery.event.triggered ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + handleObj.guid = handler.guid; + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for global triggering + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)") + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( var j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( var j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem ); + } + } + }, + + // bubbling is internal + trigger: function( event, data, elem /*, bubbling */ ) { + // Event object or event type + var type = event.type || event, + bubbling = arguments[3]; + + if ( !bubbling ) { + event = typeof event === "object" ? + // jQuery.Event object + event[expando] ? event : + // Object literal + jQuery.extend( jQuery.Event(type), event ) : + // Just the event type (string) + jQuery.Event(type); + + if ( type.indexOf("!") >= 0 ) { + event.type = type = type.slice(0, -1); + event.exclusive = true; + } + + // Handle a global trigger + if ( !elem ) { + // Don't bubble custom events when global (to avoid too much overhead) + event.stopPropagation(); + + // Only trigger if we've ever bound an event for it + if ( jQuery.event.global[ type ] ) { + jQuery.each( jQuery.cache, function() { + if ( this.events && this.events[type] ) { + jQuery.event.trigger( event, data, this.handle.elem ); + } + }); + } + } + + // Handle triggering a single element + + // don't do events on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { + return undefined; + } + + // Clean up in case it is reused + event.result = undefined; + event.target = elem; + + // Clone the incoming data, if any + data = jQuery.makeArray( data ); + data.unshift( event ); + } + + event.currentTarget = elem; + + // Trigger the event, it is assumed that "handle" is a function + var handle = jQuery.data( elem, "handle" ); + if ( handle ) { + handle.apply( elem, data ); + } + + var parent = elem.parentNode || elem.ownerDocument; + + // Trigger an inline bound script + try { + if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) { + if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) { + event.result = false; + } + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (e) {} + + if ( !event.isPropagationStopped() && parent ) { + jQuery.event.trigger( event, data, parent, true ); + + } else if ( !event.isDefaultPrevented() ) { + var target = event.target, old, + isClick = jQuery.nodeName(target, "a") && type === "click", + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem, event ) === false) && + !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { + + try { + if ( target[ type ] ) { + // Make sure that we don't accidentally re-trigger the onFOO events + old = target[ "on" + type ]; + + if ( old ) { + target[ "on" + type ] = null; + } + + jQuery.event.triggered = true; + target[ type ](); + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (e) {} + + if ( old ) { + target[ "on" + type ] = old; + } + + jQuery.event.triggered = false; + } + } + }, + + handle: function( event ) { + var all, handlers, namespaces, namespace, events; + + event = arguments[0] = jQuery.event.fix( event || window.event ); + event.currentTarget = this; + + // Namespaced event handlers + all = event.type.indexOf(".") < 0 && !event.exclusive; + + if ( !all ) { + namespaces = event.type.split("."); + event.type = namespaces.shift(); + namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + var events = jQuery.data(this, "events"), handlers = events[ event.type ]; + + if ( events && handlers ) { + // Clone the handlers to prevent manipulation + handlers = handlers.slice(0); + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Filter the functions by class + if ( all || namespace.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, arguments ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + } + + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var doc = document.documentElement, body = document.body; + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) { + event.which = event.charCode || event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); + }, + + remove: function( handleObj ) { + var remove = true, + type = handleObj.origType.replace(rnamespaces, ""); + + jQuery.each( jQuery.data(this, "events").live || [], function() { + if ( type === this.origType.replace(rnamespaces, "") ) { + remove = false; + return false; + } + }); + + if ( remove ) { + jQuery.event.remove( this, handleObj.origType, liveHandler ); + } + } + + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( this.setInterval ) { + this.onbeforeunload = eventHandle; + } + + return false; + }, + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +var removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + elem.removeEventListener( type, handle, false ); + } : + function( elem, type, handle ) { + elem.detachEvent( "on" + type, handle ); + }; + +jQuery.Event = function( src ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + // Event type + } else { + this.type = src; + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = now(); + + // Mark it as fixed + this[ expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + } + // otherwise set the returnValue property of the original event to false (IE) + e.returnValue = false; + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + // Check if mouse(over|out) are still within the same parent element + var parent = event.relatedTarget; + + // Firefox sometimes assigns relatedTarget a XUL element + // which we cannot access the parentNode property of + try { + // Traverse up the tree + while ( parent && parent !== this ) { + parent = parent.parentNode; + } + + if ( parent !== this ) { + // set the correct event type + event.type = event.data; + + // handle event if we actually just moused on to a non sub-element + jQuery.event.handle.apply( this, arguments ); + } + + // assuming we've left the element since we most likely mousedover a xul element + } catch(e) { } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( this.nodeName.toLowerCase() !== "form" ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + return trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + return trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var formElems = /textarea|input|select/i, + + changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( elem.nodeName.toLowerCase() === "select" ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !formElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery.data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery.data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + return jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + click: function( e ) { + var elem = e.target, type = elem.type; + + if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { + return testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = elem.type; + + if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + return testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information/focus[in] is not needed anymore + beforeactivate: function( e ) { + var elem = e.target; + jQuery.data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return formElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return formElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; +} + +function trigger( type, elem, args ) { + args[0].type = type; + return jQuery.event.handle.apply( elem, args ); +} + +// Create "bubbling" focus and blur events +if ( document.addEventListener ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + jQuery.event.special[ fix ] = { + setup: function() { + this.addEventListener( orig, handler, true ); + }, + teardown: function() { + this.removeEventListener( orig, handler, true ); + } + }; + + function handler( e ) { + e = jQuery.event.fix( e ); + e.type = fix; + return jQuery.event.handle.call( this, e ); + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( jQuery.isFunction( data ) ) { + fn = data; + data = undefined; + } + + var handler = name === "one" ? jQuery.proxy( fn, function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }) : fn; + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + var event = jQuery.Event( type ); + event.preventDefault(); + event.stopPropagation(); + jQuery.event.trigger( event, data, this[0] ); + return event.result; + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, i = 1; + + // link all the functions, so any of them can unbind this click handler + while ( i < args.length ) { + jQuery.proxy( fn, args[ i++ ] ); + } + + return this.click( jQuery.proxy( fn, function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + })); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( jQuery.isFunction( data ) ) { + fn = data; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( type === "focus" || type === "blur" ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + context.each(function(){ + jQuery.event.add( this, liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + }); + + } else { + // unbind live handler + context.unbind( liveConvert( type, selector ), fn ); + } + } + + return this; + } +}); + +function liveHandler( event ) { + var stop, elems = [], selectors = [], args = arguments, + related, match, handleObj, elem, j, i, l, data, + events = jQuery.data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) + if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) { + return; + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( match[i].selector === handleObj.selector ) { + elem = match[i].elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) { + stop = false; + break; + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( fn ) { + return fn ? this.bind( name, fn ) : this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + +// Prevent memory leaks in IE +// Window isn't included so as not to unbind existing unload events +// More info: +// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ +if ( window.attachEvent && !window.addEventListener ) { + window.attachEvent("onunload", function() { + for ( var id in jQuery.cache ) { + if ( jQuery.cache[ id ].handle ) { + // Try/Catch is to handle iframes being unloaded, see #4280 + try { + jQuery.event.remove( jQuery.cache[ id ].handle.elem ); + } catch(e) {} + } + } + }); +} +/*! + * Sizzle CSS Selector Engine - v1.0 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function(){ + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function(selector, context, results, seed) { + results = results || []; + var origContext = context = context || document; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context), + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + var ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; + } + + if ( context ) { + var ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + var cur = parts.pop(), pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context && context.nodeType === 1 ) { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function(results){ + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } + + return results; +}; + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ + var set, match; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice(1,1); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context, isXML ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && isXML(set[0]); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var filter = Expr.filter[ type ], found, item, left = match[1]; + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + leftMatch: {}, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + attrHandle: { + href: function(elem){ + return elem.getAttribute("href"); + } + }, + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part){ + var isPartStr = typeof part === "string"; + + if ( isPartStr && !/\W/.test(part) ) { + part = part.toLowerCase(); + + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + } else { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + var nodeCheck = part = part.toLowerCase(); + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); + }, + "~": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + var nodeCheck = part = part.toLowerCase(); + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); + } + }, + find: { + ID: function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context){ + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not, isXML){ + match = " " + match[1].replace(/\\/g, "") + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + ID: function(match){ + return match[1].replace(/\\/g, ""); + }, + TAG: function(match, curLoop){ + return match[1].toLowerCase(); + }, + CHILD: function(match){ + if ( match[1] === "nth" ) { + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + ATTR: function(match, curLoop, inplace, result, not, isXML){ + var name = match[1].replace(/\\/g, ""); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + // Accessing this property makes selected-by-default + // options in Safari work properly + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return /h\d/i.test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; + }, + input: function(elem){ + return /input|select|textarea|button/i.test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 === i; + }, + eq: function(elem, i, match){ + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var i = 0, l = not.length; i < l; i++ ) { + if ( not[i] === elem ) { + return false; + } + } + + return true; + } else { + Sizzle.error( "Syntax error, unrecognized expression: " + name ); + } + }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + if ( type === "first" ) { + return true; + } + node = elem; + case 'last': + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first === 0 ) { + return diff === 0; + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + CLASS: function(elem, match){ + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + ATTR: function(elem, match){ + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){ + return "\\" + (num - 0 + 1); + })); +} + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch(e){ + makeArray = function(array, results) { + var ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var i = 0, l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( var i = 0; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.compareDocumentPosition ? -1 : 1; + } + + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + if ( !a.sourceIndex || !b.sourceIndex ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.sourceIndex ? -1 : 1; + } + + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + if ( !a.ownerDocument || !b.ownerDocument ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.ownerDocument ? -1 : 1; + } + + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.setStart(a, 0); + aRange.setEnd(a, 0); + bRange.setStart(b, 0); + bRange.setEnd(b, 0); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +function getText( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += getText( elem.childNodes ); + } + } + + return ret; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date).getTime(); + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + root = form = null; // release memory in IE +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + Expr.attrHandle.href = function(elem){ + return elem.getAttribute("href", 2); + }; + } + + div = null; // release memory in IE +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, div = document.createElement("div"); + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && context.nodeType === 9 && !isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; // release memory in IE + })(); +} + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; // release memory in IE +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +var contains = document.compareDocumentPosition ? function(a, b){ + return !!(a.compareDocumentPosition(b) & 16); +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function(selector, context){ + var tmpSet = [], later = "", match, + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = getText; +jQuery.isXMLDoc = isXML; +jQuery.contains = contains; + +return; + +window.Sizzle = Sizzle; + +})(); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + slice = Array.prototype.slice; + +// Implement the identical functionality for filter and not +var winnow = function( elements, qualifier, keep ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var ret = this.pushStack( "", "find", selector ), length = 0; + + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && jQuery.filter( selector, this ).length > 0; + }, + + closest: function( selectors, context ) { + if ( jQuery.isArray( selectors ) ) { + var ret = [], cur = this[0], match, matches = {}, selector; + + if ( cur && selectors.length ) { + for ( var i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[selector] ) { + matches[selector] = jQuery.expr.match.POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[selector]; + + if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { + ret.push({ selector: selector, elem: cur }); + delete matches[selector]; + } + } + cur = cur.parentNode; + } + } + + return ret; + } + + var pos = jQuery.expr.match.POS.test( selectors ) ? + jQuery( selectors, context || this.context ) : null; + + return this.map(function( i, cur ) { + while ( cur && cur.ownerDocument && cur !== context ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) { + return cur; + } + cur = cur.parentNode; + } + return null; + }); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context || this.context ) : + jQuery.makeArray( selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call(arguments).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], cur = elem[dir]; + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g, + rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i, + rtagName = /<([\w:]+)/, + rtbody = /"; + }, + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and getAdminLinkedTags($tag, '>'); + } + $tagData = array(); + foreach ($linkedTags as $tag) { + $tagData[] = createTagArray($tag); + } -function displayTag($tag, $uId) { - $uId = ($uId==0)?NULL:$uId; // if user is nobody, NULL allows to look for every public tags - - $tag2tagservice =SemanticScuttle_Service_Factory::get('Tag2Tag'); - $output = '{ id:'.rand().', name:\''.$tag.'\''; - - $linkedTags = $tag2tagservice->getAdminLinkedTags($tag, '>'); - if(count($linkedTags) > 0) { - $output.= ', children: ['; - foreach($linkedTags as $linkedTag) { - $output.= displayTag($linkedTag, $uId); - } - $output = substr($output, 0, -1); // remove final comma avoiding IE6 Dojo bug - $output.= "]"; - } - - $output.= '},'; - return $output; + return $tagData; } -?> +function createTagArray($tag) +{ + return array( + 'data' => $tag, + 'attr' => array('rel' => $tag), + //'children' => array('foo', 'bar'), + 'state' => 'closed' + ); +} -{ label: 'name', identifier: 'id', items: [ - -] } + +$tagData = assembleTagData( + $tag, + SemanticScuttle_Service_Factory::get('Tag2Tag') +); +//$json = substr($json, 0, -1); // remove final comma avoiding IE6 Dojo bug +echo json_encode($tagData); +?> \ No newline at end of file From bd92516e6fac1970770fbddec612f3d8da30fb3f Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 1 Oct 2010 21:34:11 +0200 Subject: [PATCH 071/173] make the admin tag menu behave correctly now and show only arrows on nodes that have children --- www/ajax/getadminlinkedtags.php | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/www/ajax/getadminlinkedtags.php b/www/ajax/getadminlinkedtags.php index a08045a..5581c43 100644 --- a/www/ajax/getadminlinkedtags.php +++ b/www/ajax/getadminlinkedtags.php @@ -1,31 +1,27 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - ***************************************************************************/ - -/* Return a json file with list of linked tags */ $httpContentType = 'application/json'; -$httpContentType='text/plain'; require_once '../www-header.php'; -$tag = isset($_GET['tag']) ? trim($_GET['tag']) : ''; - function assembleTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) { if ($tag == '') { @@ -36,27 +32,34 @@ function assembleTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) $tagData = array(); foreach ($linkedTags as $tag) { - $tagData[] = createTagArray($tag); + //FIXME: the hasChildren code is nasty, because it causes too many + // queries onto the database + $hasChildren = 0 < count($t2t->getAdminLinkedTags($tag, '>')); + $tagData[] = createTagArray($tag, $hasChildren); } return $tagData; } -function createTagArray($tag) +function createTagArray($tag, $hasChildren = true) { - return array( + $ar = array( 'data' => $tag, 'attr' => array('rel' => $tag), - //'children' => array('foo', 'bar'), - 'state' => 'closed' ); + if ($hasChildren) { + //jstree needs that to show the arrows + $ar['state'] = 'closed'; + } + + return $ar; } +$tag = isset($_GET['tag']) ? trim($_GET['tag']) : ''; $tagData = assembleTagData( $tag, SemanticScuttle_Service_Factory::get('Tag2Tag') ); -//$json = substr($json, 0, -1); // remove final comma avoiding IE6 Dojo bug echo json_encode($tagData); ?> \ No newline at end of file From df5aca497a492d2200bee2325c73b9fc5c47ba1f Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 1 Oct 2010 21:36:40 +0200 Subject: [PATCH 072/173] remove unneeded dojo code from menu2 --- data/templates/sidebar.block.menu2.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/data/templates/sidebar.block.menu2.php b/data/templates/sidebar.block.menu2.php index dec520a..41c55ee 100644 --- a/data/templates/sidebar.block.menu2.php +++ b/data/templates/sidebar.block.menu2.php @@ -38,17 +38,7 @@ foreach ($menu2Tags as $menu2Tag) { $menu2Tag ) . '
  5. '; - /* - echo '
    '; - echo '
    '; - echo ''; - echo '
    '; - */ } -//ROOT.'ajax/getadminlinkedtags.php?tag='.filter($menu2Tag, 'url') ?> From 24e344f7879a932cc9a5a3491d460a05651c08a3 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 1 Oct 2010 21:37:53 +0200 Subject: [PATCH 073/173] remove dojo css --- data/templates/top.inc.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/data/templates/top.inc.php b/data/templates/top.inc.php index b1ffa14..8be978f 100644 --- a/data/templates/top.inc.php +++ b/data/templates/top.inc.php @@ -17,17 +17,12 @@ if (isset($rsschannels)) { } ?> - - - - - + Date: Thu, 7 Oct 2010 18:59:27 +0200 Subject: [PATCH 074/173] give admin tags a link href :) --- src/SemanticScuttle/constants.php | 4 +++- www/ajax/getadminlinkedtags.php | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php index 95c4384..aebc059 100644 --- a/src/SemanticScuttle/constants.php +++ b/src/SemanticScuttle/constants.php @@ -17,7 +17,9 @@ if (!isset($GLOBALS['root'])) { $rootTmp = '/'; foreach ($pieces as $piece) { //we eliminate possible sscuttle subfolders (like gsearch for example) - if ($piece != '' && !strstr($piece, '.php') && $piece != 'gsearch') { + if ($piece != '' && !strstr($piece, '.php') + && $piece != 'gsearch' && $piece != 'ajax' + ) { $rootTmp .= $piece .'/'; } } diff --git a/www/ajax/getadminlinkedtags.php b/www/ajax/getadminlinkedtags.php index 5581c43..1004f29 100644 --- a/www/ajax/getadminlinkedtags.php +++ b/www/ajax/getadminlinkedtags.php @@ -41,11 +41,28 @@ function assembleTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) return $tagData; } +/** + * Creates an jsTree json array for the given tag + * + * @param string $tag Tag name + * @param boolean $hasChildren If the tag has subtags (children) or not + * + * @return array Array to be sent back to the browser as json + */ function createTagArray($tag, $hasChildren = true) { $ar = array( - 'data' => $tag, - 'attr' => array('rel' => $tag), + 'data' => array( + // attributes + 'title' => $tag, + 'attr' => array( + 'href' => createUrl('tags', $tag) + ) + ), + //
  6. attributes + 'attr' => array( + 'rel' => $tag,//needed for identifying the tag in html + ), ); if ($hasChildren) { //jstree needs that to show the arrows From aa6b6863b8b23aad2198b301d2b0979987f8b087 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 7 Oct 2010 19:02:06 +0200 Subject: [PATCH 075/173] CS on constants --- src/SemanticScuttle/constants.php | 70 +++++++++++++++++++------------ 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php index aebc059..c76f7b2 100644 --- a/src/SemanticScuttle/constants.php +++ b/src/SemanticScuttle/constants.php @@ -1,35 +1,49 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle */ // Debug managament -if(isset($GLOBALS['debugMode'])) { - define('DEBUG_MODE', $GLOBALS['debugMode']); - define('DEBUG_EXTRA', $GLOBALS['debugMode']); // Constant used exclusively into db/ directory +if (isset($GLOBALS['debugMode'])) { + define('DEBUG_MODE', $GLOBALS['debugMode']); + // Constant used exclusively into db/ directory + define('DEBUG_EXTRA', $GLOBALS['debugMode']); } // Determine the base URL as ROOT if (!isset($GLOBALS['root'])) { - $pieces = explode('/', $_SERVER['SCRIPT_NAME']); - - $rootTmp = '/'; - foreach ($pieces as $piece) { - //we eliminate possible sscuttle subfolders (like gsearch for example) + $pieces = explode('/', $_SERVER['SCRIPT_NAME']); + + $rootTmp = '/'; + foreach ($pieces as $piece) { + //we eliminate possible sscuttle subfolders (like gsearch for example) if ($piece != '' && !strstr($piece, '.php') && $piece != 'gsearch' && $piece != 'ajax' ) { - $rootTmp .= $piece .'/'; - } - } - if (($rootTmp != '/') && (substr($rootTmp, -1, 1) != '/')) { - $rootTmp .= '/'; - } + $rootTmp .= $piece .'/'; + } + } + if (($rootTmp != '/') && (substr($rootTmp, -1, 1) != '/')) { + $rootTmp .= '/'; + } - define('ROOT', 'http://'. $_SERVER['HTTP_HOST'] . $rootTmp); + define('ROOT', 'http://'. $_SERVER['HTTP_HOST'] . $rootTmp); } else { - define('ROOT', $GLOBALS['root']); + define('ROOT', $GLOBALS['root']); } // Error codes @@ -46,19 +60,21 @@ define('PAGE_WATCHLIST', "watchlist"); // Miscellanous -// INSTALLATION_ID is based on directory DB and used as prefix (in session and cookie) to prevent mutual login for different installations on the same host server +// INSTALLATION_ID is based on directory DB and used as prefix +// (in session and cookie) to prevent mutual login for different +// installations on the same host server define('INSTALLATION_ID', md5($GLOBALS['dbname'].$GLOBALS['tableprefix'])); // Correct bugs with PATH_INFO (maybe for Apache 1 or CGI) -- for 1&1 host... if (isset($_SERVER['PATH_INFO']) && isset($_SERVER['ORIG_PATH_INFO'])) { - if(strlen($_SERVER["PATH_INFO"]) From 20cd1969d1ccf6dd20de0bb4f96be8d2460b22bd Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 7 Oct 2010 19:46:20 +0200 Subject: [PATCH 076/173] prettier html in menu2 tags --- data/templates/sidebar.block.menu2.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/templates/sidebar.block.menu2.php b/data/templates/sidebar.block.menu2.php index 41c55ee..0c112a8 100644 --- a/data/templates/sidebar.block.menu2.php +++ b/data/templates/sidebar.block.menu2.php @@ -30,14 +30,16 @@ if (count($menu2Tags) > 0) { > From 8e3daac73e324368a414fc0e9406dcbe43ffba29 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 7 Oct 2010 19:46:55 +0200 Subject: [PATCH 077/173] we do not need that service --- data/templates/sidebar.block.menu2.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/data/templates/sidebar.block.menu2.php b/data/templates/sidebar.block.menu2.php index 0c112a8..d608821 100644 --- a/data/templates/sidebar.block.menu2.php +++ b/data/templates/sidebar.block.menu2.php @@ -1,9 +1,6 @@ Date: Fri, 8 Oct 2010 16:59:04 +0200 Subject: [PATCH 078/173] scripts to dump and restore the semanticscuttle database quickly - useful to keep the database even when running unit tests --- .gitignore | 1 + scripts/database-dump.php | 26 +++++++++++++++++++++++++ scripts/database-restore.php | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 scripts/database-dump.php create mode 100644 scripts/database-restore.php diff --git a/.gitignore b/.gitignore index 888ac2e..d84d93d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist/ build.properties package.xml +semanticscuttle-dump.sql diff --git a/scripts/database-dump.php b/scripts/database-dump.php new file mode 100644 index 0000000..f4f04ac --- /dev/null +++ b/scripts/database-dump.php @@ -0,0 +1,26 @@ + + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +require_once dirname(__FILE__) . '/../src/SemanticScuttle/header-standalone.php'; + +passthru( + 'mysqldump' + . ' -h' . escapeshellarg($GLOBALS['dbhost']) + . ' -u' . escapeshellarg($GLOBALS['dbuser']) + . ' -p' . escapeshellarg($GLOBALS['dbpass']) + . ' ' . escapeshellarg($GLOBALS['dbname']) + . ' > semanticscuttle-dump.sql' +); +?> \ No newline at end of file diff --git a/scripts/database-restore.php b/scripts/database-restore.php new file mode 100644 index 0000000..6516e71 --- /dev/null +++ b/scripts/database-restore.php @@ -0,0 +1,37 @@ + + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +if (!isset($argv[1])) { + echo "Please pass the sql file to restore\n"; + exit(1); +} +$file = $argv[1]; +if (!file_exists($file)) { + echo "The file does not exist\n"; + exit(2); +} + +require_once dirname(__FILE__) . '/../src/SemanticScuttle/header-standalone.php'; + +passthru( + 'mysql' + . ' -h' . escapeshellarg($GLOBALS['dbhost']) + . ' -u' . escapeshellarg($GLOBALS['dbuser']) + . ' -p' . escapeshellarg($GLOBALS['dbpass']) + . ' ' . escapeshellarg($GLOBALS['dbname']) + . ' < ' . escapeshellarg($file) +); +?> \ No newline at end of file From be126cc958212ed74fe916eabcb1e6ac928df5e4 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 8 Oct 2010 17:43:05 +0200 Subject: [PATCH 079/173] tests for getadminlinkedtags --- src/SemanticScuttle/Service/User.php | 18 ++- tests/TestBase.php | 28 +++++ tests/ajax/GetAdminLinkedTagsTest.php | 160 ++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 tests/ajax/GetAdminLinkedTagsTest.php diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php index d74d104..fd9d84f 100644 --- a/src/SemanticScuttle/Service/User.php +++ b/src/SemanticScuttle/Service/User.php @@ -203,18 +203,26 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService } } - /* Takes an numerical "id" or a string "username" - and returns the numerical "id" if the user exists else returns NULL */ - function getIdFromUser($user) { + /** + * Obtains the ID of the given user name. + * If a user ID is passed, it is returned. + * In case the user does not exist, NULL is returned. + * + * @param string|integer $user User name or user ID + * + * @return integer NULL if not found or the user ID + */ + public function getIdFromUser($user) + { if (is_int($user)) { return intval($user); } else { $objectUser = $this->getObjectUserByUsername($user); - if($objectUser != NULL) { + if ($objectUser != null) { return $objectUser->getId(); } } - return NULL; + return null; } /** diff --git a/tests/TestBase.php b/tests/TestBase.php index 402330b..c0acd58 100644 --- a/tests/TestBase.php +++ b/tests/TestBase.php @@ -104,6 +104,34 @@ class TestBase extends PHPUnit_Framework_TestCase return $uid; } + + + /** + * Retrieves the UID of an admin user. + * If that user does not exist in the database, it is created. + * + * @return integer UID of admin user + */ + protected function getAdminUser() + { + if (count($GLOBALS['admin_users']) == 0) { + $this->fail('No admin users configured'); + } + $adminUserName = reset($GLOBALS['admin_users']); + + $us = SemanticScuttle_Service_Factory::get('User'); + $uid = $us->getIdFromUser($adminUserName); + if ($uid === null) { + //that user does not exist in the database; create it + $uid = $us->addUser( + $adminUserName, + rand(), + 'unittest-admin-' . $adminUserName . '@example.org' + ); + } + + return $uid; + } } ?> \ No newline at end of file diff --git a/tests/ajax/GetAdminLinkedTagsTest.php b/tests/ajax/GetAdminLinkedTagsTest.php new file mode 100644 index 0000000..d8ec447 --- /dev/null +++ b/tests/ajax/GetAdminLinkedTagsTest.php @@ -0,0 +1,160 @@ + + * @author Christian Weiske + * @author Eric Dane + * @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', 'ajax_GetAdminLinkedTagsTest::main'); +} + +/** + * Unit tests for the ajax linked admin tags script + * + * @category Bookmarking + * @package SemanticScuttle + * @author Christian Weiske + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class ajax_GetAdminLinkedTagsTest extends TestBaseApi +{ + protected $urlPart = 'ajax/getadminlinkedtags.php'; + + + + /** + * 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__) + ); + } + + + + /** + * Verify that we get the configured root tags if + * we do not pass any parameters + */ + public function testRootTags() + { + $req = $this->getRequest(); + $res = $req->send(); + + $this->assertEquals(200, $res->getStatus()); + $this->assertEquals( + 'application/json; charset=utf-8', + $res->getHeader('content-type') + ); + + $data = json_decode($res->getBody()); + $this->assertType('array', $data); + + //same number of elements as the menu2Tags array + $this->assertEquals( + count($GLOBALS['menu2Tags']), + count($data) + ); + + //and the same contents + foreach ($data as $tagObj) { + $tagName = $tagObj->data->title; + $this->assertContains($tagName, $GLOBALS['menu2Tags']); + } + } + + /** + * Verify that we get subtags of a given tag + */ + public function testSubTags() + { + $t2t = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $t2t->deleteAll(); + + $menu2Tag = reset($GLOBALS['menu2Tags']); + //we have a subtag now + $this->addBookmark( + $this->getAdminUser(), + null, + 0, + $menu2Tag . '>adminsubtag' + ); + + $res = $this->getRequest('?tag=' . $menu2Tag)->send(); + $this->assertEquals(200, $res->getStatus()); + $this->assertEquals( + 'application/json; charset=utf-8', + $res->getHeader('content-type') + ); + + $data = json_decode($res->getBody()); + $this->assertType('array', $data); + + //only one subtag + $this->assertEquals(1, count($data)); + $this->assertEquals('adminsubtag', $data[0]->data->title); + } + + /** + * Verify that we only get admin tags, not tags from + * non-admin people + */ + public function testOnlyAdminTags() + { + $t2t = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $t2t->deleteAll(); + + $menu2Tag = reset($GLOBALS['menu2Tags']); + //we have a subtag now + $this->addBookmark( + $this->getAdminUser(), + null, + 0, + $menu2Tag . '>adminsubtag' + ); + //add another bookmark now, but for a normal user + $this->addBookmark( + null, + null, + 0, + $menu2Tag . '>normalsubtag' + ); + + $res = $this->getRequest('?tag=' . $menu2Tag)->send(); + $this->assertEquals(200, $res->getStatus()); + $this->assertEquals( + 'application/json; charset=utf-8', + $res->getHeader('content-type') + ); + + $data = json_decode($res->getBody()); + $this->assertType('array', $data); + + //we should have only one subtag now, the admin one + $this->assertEquals(1, count($data)); + $this->assertEquals('adminsubtag', $data[0]->data->title); + } +} + +if (PHPUnit_MAIN_METHOD == 'ajax_GetAdminLinkedTagsTest::main') { + ajax_GetAdminLinkedTagsTest::main(); +} +?> \ No newline at end of file From aaf293b18da42e41cfcaed16d13f1d25157fd7cb Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 10:18:52 +0200 Subject: [PATCH 080/173] make the bookmark page valid xhtml --- data/templates/bookmarks.tpl.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/data/templates/bookmarks.tpl.php b/data/templates/bookmarks.tpl.php index e32d3c9..29d7248 100644 --- a/data/templates/bookmarks.tpl.php +++ b/data/templates/bookmarks.tpl.php @@ -62,12 +62,16 @@ if($currenttag!= '' && $cdservice->getLastTagDescription($currenttag)) { } //common tag description edit -if($userservice->isLoggedOn()) { - if($currenttag!= '' && ($GLOBALS['enableCommonTagDescriptionEditedByAll'] || $currentUser->isAdmin())) { +if ($userservice->isLoggedOn()) { + if ($currenttag != '' + && ($GLOBALS['enableCommonTagDescriptionEditedByAll'] + || $currentUser->isAdmin() + ) + ) { echo ' '; echo !is_array($cDescription) || strlen($cDescription['cdDescription'])==0?T_('Edit the common description of this tag'):''; echo ' '; - } elseif(isset($hash)) { + } else if (isset($hash)) { echo ' ('; echo T_('Edit the common description of this bookmark').')'; } @@ -312,7 +316,7 @@ if ($currenttag!= '') { . $row['username'] . ''; } - // Udders! + // others if (!isset($hash)) { $others = $otherCounts[$row['bAddress']]; $ostart = ''; @@ -390,7 +394,7 @@ if ($currenttag!= '') { echo ' ' . "\n"; echo ' \n"; if ($row['bDescription'] == '') { From 22e46a9b45810bae480aea72b16680d715a57ff4 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 10:20:14 +0200 Subject: [PATCH 081/173] make linkedtags sidebar tree menu work with jquery/jstree now --- data/templates/sidebar.block.linked.php | 131 ++++++---------- www/ajax/getadminlinkedtags.php | 16 +- www/ajax/getlinkedtags.php | 191 +++++++++++++++++------- 3 files changed, 193 insertions(+), 145 deletions(-) diff --git a/data/templates/sidebar.block.linked.php b/data/templates/sidebar.block.linked.php index 9e91f93..db0d087 100644 --- a/data/templates/sidebar.block.linked.php +++ b/data/templates/sidebar.block.linked.php @@ -1,4 +1,11 @@ getCurrentUserId(); -if ($logged_on_userid === false) { - $logged_on_userid = NULL; -} - -$explodedTags = array(); -if (strlen($currenttag)>0) { - $explodedTags = explode('+', $currenttag); -} else { - if($summarizeLinkedTags == true) { - $orphewTags = $tag2tagservice->getOrphewTags('>', $userid, 4, "nb"); - } else { - $orphewTags = $tag2tagservice->getOrphewTags('>', $userid); - } - - foreach($orphewTags as $orphewTag) { - $explodedTags[] = $orphewTag['tag']; - } -} - +$editingMode = $logged_on_userid !== false; ?> - +

    + \ No newline at end of file diff --git a/www/ajax/getadminlinkedtags.php b/www/ajax/getadminlinkedtags.php index 1004f29..5f939a6 100644 --- a/www/ajax/getadminlinkedtags.php +++ b/www/ajax/getadminlinkedtags.php @@ -22,7 +22,16 @@ $httpContentType = 'application/json'; require_once '../www-header.php'; -function assembleTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) +/** + * Creates and returns an array of tags for the jsTree ajax loader. + * If the tag is empty, the configured menu2 (admin) main tags are used. + * + * @param string $tag Tag name to fetch subtags for + * @param SemanticScuttle_Service_Tag2Tag $t2t Tag relation service + * + * @return array Array of tag data suitable for the jsTree ajax loader + */ +function assembleAdminTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) { if ($tag == '') { $linkedTags = $GLOBALS['menu2Tags']; @@ -45,7 +54,8 @@ function assembleTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) * Creates an jsTree json array for the given tag * * @param string $tag Tag name - * @param boolean $hasChildren If the tag has subtags (children) or not + * @param boolean $hasChildren If the tag has subtags (children) or not. + * If unsure, set it to "true". * * @return array Array to be sent back to the browser as json */ @@ -74,7 +84,7 @@ function createTagArray($tag, $hasChildren = true) $tag = isset($_GET['tag']) ? trim($_GET['tag']) : ''; -$tagData = assembleTagData( +$tagData = assembleAdminTagData( $tag, SemanticScuttle_Service_Factory::get('Tag2Tag') ); diff --git a/www/ajax/getlinkedtags.php b/www/ajax/getlinkedtags.php index f412998..307ee26 100644 --- a/www/ajax/getlinkedtags.php +++ b/www/ajax/getlinkedtags.php @@ -1,64 +1,143 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ $httpContentType = 'application/json'; +#$httpContentType = 'text/plain'; require_once '../www-header.php'; -/* Service creation: only useful services are created */ -$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag'); -$bookmarkservice =SemanticScuttle_Service_Factory::get('Tag'); -$tagstatservice =SemanticScuttle_Service_Factory::get('TagStat'); +$tag = isset($_GET['tag']) ? $_GET['tag'] : null; +$uId = isset($_GET['uId']) ? (int)$_GET['uId'] : 0; +$loadParentTags = isset($_GET['parent']) ? (bool)$_GET['parent'] : false; -/* Managing all possible inputs */ -isset($_GET['tag']) ? define('GET_TAG', $_GET['tag']): define('GET_TAG', ''); -isset($_GET['uId']) ? define('GET_UID', $_GET['uId']): define('GET_UID', ''); - - -function displayTag($tag, $uId) { - $uId = ($uId==0)?NULL:$uId; // if user is nobody, NULL allows to look for every public tags - - $tag2tagservice =SemanticScuttle_Service_Factory::get('Tag2Tag'); - $output = '{ id:'.rand().', name:\''.$tag.'\''; - - $linkedTags = $tag2tagservice->getLinkedTags($tag, '>', $uId); - if(count($linkedTags) > 0) { - $output.= ', children: ['; - foreach($linkedTags as $linkedTag) { - $output.= displayTag($linkedTag, $uId); - } - $output = substr($output, 0, -1); // remove final comma avoiding IE6 Dojo bug - $output.= "]"; - } - - $output.= '},'; - return $output; +$tags = explode(' ', trim($tag)); +if (count($tags) == 1 && $tags[0] == '') { + //no tags + $tags = array(); } -?> -{ label: 'name', identifier: 'id', items: [ - -] } +function assembleLinkedTagData( + $tags, $uId, $loadParentTags, SemanticScuttle_Service_Tag2Tag $t2t +) { + $tagData = array(); + + if (count($tags) == 0) { + //no tags given -> show the 4 most used top-level tags + $orphewTags = $t2t->getOrphewTags('>', $uId, 4, 'nb'); + #$orphewTags = $t2t->getOrphewTags('>', $uId); + foreach ($orphewTags as $orphewTag) { + $tags[] = $orphewTag['tag']; + } + $loadParentTags = true; + } + + if ($loadParentTags) { + //find parent tags + append the selected tags as children afterwards + foreach ($tags as $tag) { + $parentTags = $t2t->getLinkedTags($tag, '>', $uId, true); + if (count($parentTags) > 0) { + foreach ($parentTags as $parentTag) { + $ta = createTagArray( + $parentTag, true, true, true + ); + //FIXME: find out if there are subtags + $tac = createTagArray($tag, true); + $ta['children'][] = $tac; + $tagData[] = $ta; + } + } else { + //no parent tags -> display it normally + //FIXME: find out if there are subtags + $tagData[] = createTagArray($tag, true); + } + } + } else { + //just find the linked tags + foreach ($tags as $tag) { + $linkedTags = $t2t->getLinkedTags($tag, '>', $uId); + foreach ($linkedTags as $linkedTag) { + //FIXME: find out if there are subtags + $tagData[] = createTagArray($linkedTag, true); + } + } + } + + return $tagData; +} + +/** + * Creates an jsTree json array for the given tag + * + * @param string $tag Tag name + * @param boolean $hasChildren If the tag has subtags (children) or not. + * If unsure, set it to "true". + * @param boolean $isOpen If the tag has children: Is the tree node open + * or closed? + * @param boolean $autoParent If the tag is an automatically generated parent tag + * + * @return array Array to be sent back to the browser as json + */ +function createTagArray($tag, $hasChildren = true, $isOpen = false, $autoParent = false) +{ + if ($autoParent) { + $title = '(' . $tag . ')'; + } else { + $title = $tag; + } + + $ar = array( + 'data' => array( + // attributes + 'title' => $title, + 'attr' => array( + 'href' => createUrl('tags', $tag) + ) + ), + //
  7. attributes + 'attr' => array( + 'rel' => $tag,//needed for identifying the tag in html + ), + ); + if ($hasChildren) { + //jstree needs that to show the arrows + $ar['state'] = $isOpen ? 'open' : 'closed'; + } + if ($autoParent) { + //FIXME: use css class + $ar['data']['attr']['style'] = 'color: #AAA'; + } + + return $ar; +} + + +$tagData = assembleLinkedTagData( + $tags, 0/*$uId*/, $loadParentTags, + SemanticScuttle_Service_Factory::get('Tag2Tag') +); +echo json_encode($tagData); +?> \ No newline at end of file From 48cd84921f42e409653ac34a5ba5c3d1d988c4e0 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:09:25 +0200 Subject: [PATCH 082/173] make the page valid --- data/templates/sidebar.block.search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/sidebar.block.search.php b/data/templates/sidebar.block.search.php index f4e6b42..d3cd8a5 100644 --- a/data/templates/sidebar.block.search.php +++ b/data/templates/sidebar.block.search.php @@ -34,7 +34,7 @@ foreach ($lastSearches as $row) { echo ''; - echo $row['shTerms']; + echo htmlspecialchars($row['shTerms']); echo ''; echo ' - +]]> \ No newline at end of file diff --git a/data/templates/top.inc.php b/data/templates/top.inc.php index 8be978f..c3cbc5d 100644 --- a/data/templates/top.inc.php +++ b/data/templates/top.inc.php @@ -1,7 +1,8 @@ + - + <?php echo filter($GLOBALS['sitename'] .(isset($pagetitle) ? ' » ' . $pagetitle : '')); ?> diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php index d1a5c29..3caeb35 100644 --- a/src/SemanticScuttle/header.php +++ b/src/SemanticScuttle/header.php @@ -120,7 +120,10 @@ $tplVars['userservice'] = $userservice; if (!defined('UNIT_TEST_MODE')) { //API files define that, so we need a way to support both of them if (!isset($httpContentType)) { - $httpContentType = 'text/html'; + //$httpContentType = 'text/html'; + //using that mime type makes all javascript nice in Chromium + // it also serves as test base if the pages really validate + $httpContentType = 'application/xhtml+xml'; } if ($httpContentType !== false) { header('Content-Type: ' . $httpContentType . '; charset=utf-8'); From 894011dd0fb4ba0aeee428d3460665afb71cd9d9 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:14:34 +0200 Subject: [PATCH 084/173] make the short date a valid ISO date format. --- data/config.default.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/config.default.php b/data/config.default.php index bd67c7b..3d60b91 100644 --- a/data/config.default.php +++ b/data/config.default.php @@ -327,7 +327,7 @@ $index_sidebar_blocks = array( * @var string * @link http://php.net/date */ -$shortdate = 'd-m-Y'; +$shortdate = 'Y-m-d'; /** * Format of long dates. From 2e89b28ed9d902ac208355193e708e9b76845282 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:15:11 +0200 Subject: [PATCH 085/173] embed the delete link directly in html instead of using javascript --- data/templates/bookmarks.tpl.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/data/templates/bookmarks.tpl.php b/data/templates/bookmarks.tpl.php index 29d7248..db560f2 100644 --- a/data/templates/bookmarks.tpl.php +++ b/data/templates/bookmarks.tpl.php @@ -296,10 +296,9 @@ if ($currenttag!= '') { $edit = ' - ' . T_('Edit') . '' - . ''; + .''; } // Last update From 129926f1e77404e1c210bac4fc8b4680bf6bd5db Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:43:10 +0200 Subject: [PATCH 086/173] fix jstree xhtml bug --- www/js/jquery.jstree.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/js/jquery.jstree.js b/www/js/jquery.jstree.js index 5037698..bbfc355 100644 --- a/www/js/jquery.jstree.js +++ b/www/js/jquery.jstree.js @@ -2800,7 +2800,7 @@ */ (function ($) { $.vakata.context = { - cnt : $("
    "), + cnt : $("
    "), vis : false, tgt : false, par : false, From 6c859814069e58e3b314e459879c2fb8b8463cb4 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:53:45 +0200 Subject: [PATCH 087/173] make edit profile page valid --- data/templates/editprofile.tpl.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/data/templates/editprofile.tpl.php b/data/templates/editprofile.tpl.php index b55d250..887dce3 100644 --- a/data/templates/editprofile.tpl.php +++ b/data/templates/editprofile.tpl.php @@ -3,9 +3,7 @@ $this->includeTemplate($GLOBALS['top_include']); ?>
    - - - +

    From c7a77dcb4cd355b89ec66a273e5166f8ce449046 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:54:33 +0200 Subject: [PATCH 088/173] make admin page valid --- data/templates/admin.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/admin.tpl.php b/data/templates/admin.tpl.php index c8d47e8..50680f6 100644 --- a/data/templates/admin.tpl.php +++ b/data/templates/admin.tpl.php @@ -11,7 +11,7 @@ foreach($users as $user) { echo ''; if($user->getUsername() != $currentUser->getUsername()) { From 1263a62179cca1b0bccc114d1daa7340816bcf48 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:57:17 +0200 Subject: [PATCH 089/173] make watchlist valid xhtml --- data/templates/sidebar.block.watchlist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/sidebar.block.watchlist.php b/data/templates/sidebar.block.watchlist.php index 07b7f15..88fc928 100644 --- a/data/templates/sidebar.block.watchlist.php +++ b/data/templates/sidebar.block.watchlist.php @@ -41,7 +41,7 @@ foreach($watching as $watchuser) {
  8. isLoggedOn() && $currentUser->getUsername() == $user): ?> - - x + - x
  9. From 6bf4e76495b03ccec29fb4d9262ac871dd10a221 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 13:03:55 +0200 Subject: [PATCH 090/173] make add/del tag nav links work again --- data/templates/sidebar.block.linked.php | 3 ++- www/ajax/getlinkedtags.php | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/templates/sidebar.block.linked.php b/data/templates/sidebar.block.linked.php index 54e9260..ac864dc 100644 --- a/data/templates/sidebar.block.linked.php +++ b/data/templates/sidebar.block.linked.php @@ -30,10 +30,11 @@ if ($editingMode) { echo '

    '; } ?> +