aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore13
-rw-r--r--.travis.yml15
l---------README.rst1
-rw-r--r--build.properties.dist2
-rw-r--r--build.xml471
-rw-r--r--cache/.cvsignore1
-rw-r--r--cache/.htaccess2
-rw-r--r--data/config.default.php799
-rw-r--r--data/config.php.dist139
-rw-r--r--data/locales/de_AT/LC_MESSAGES/messages.mobin0 -> 22807 bytes
-rw-r--r--data/locales/de_AT/LC_MESSAGES/messages.po1699
-rw-r--r--data/locales/de_DE/LC_MESSAGES/messages.mobin0 -> 30928 bytes
-rw-r--r--data/locales/de_DE/LC_MESSAGES/messages.po1680
-rw-r--r--data/locales/dk_DK/LC_MESSAGES/messages.mobin0 -> 14667 bytes
-rw-r--r--data/locales/dk_DK/LC_MESSAGES/messages.po1711
-rw-r--r--data/locales/en_GB/LC_MESSAGES/messages.mobin0 -> 24562 bytes
-rw-r--r--data/locales/en_GB/LC_MESSAGES/messages.po1642
-rw-r--r--data/locales/es_ES/LC_MESSAGES/messages.mobin0 -> 12669 bytes
-rw-r--r--data/locales/es_ES/LC_MESSAGES/messages.po1736
-rw-r--r--data/locales/fr_CA/LC_MESSAGES/messages.mobin0 -> 21872 bytes
-rw-r--r--data/locales/fr_CA/LC_MESSAGES/messages.po1720
-rw-r--r--data/locales/fr_FR/LC_MESSAGES/messages.mobin0 -> 27093 bytes
-rw-r--r--data/locales/fr_FR/LC_MESSAGES/messages.po1708
-rw-r--r--data/locales/hi_IN/LC_MESSAGES/messages.mobin0 -> 22499 bytes
-rw-r--r--data/locales/hi_IN/LC_MESSAGES/messages.po1706
-rw-r--r--data/locales/it_IT/LC_MESSAGES/messages.mobin0 -> 8111 bytes
-rw-r--r--data/locales/it_IT/LC_MESSAGES/messages.po1749
-rw-r--r--data/locales/ja_JP/LC_MESSAGES/messages.mobin0 -> 24129 bytes
-rw-r--r--data/locales/ja_JP/LC_MESSAGES/messages.po1670
-rw-r--r--data/locales/lt_LT/LC_MESSAGES/messages.mobin0 -> 14832 bytes
-rw-r--r--data/locales/lt_LT/LC_MESSAGES/messages.po1710
-rw-r--r--data/locales/messages.po1575
-rw-r--r--data/locales/nl_NL/LC_MESSAGES/messages.mobin0 -> 5698 bytes
-rw-r--r--data/locales/nl_NL/LC_MESSAGES/messages.po1709
-rw-r--r--data/locales/pt_BR/LC_MESSAGES/messages.mobin0 -> 13790 bytes
-rw-r--r--data/locales/pt_BR/LC_MESSAGES/messages.po1773
-rw-r--r--data/locales/ru_RU/LC_MESSAGES/messages.mobin0 -> 28905 bytes
-rw-r--r--data/locales/ru_RU/LC_MESSAGES/messages.po1664
-rwxr-xr-xdata/locales/tr_TR/LC_MESSAGES/messages.mobin0 -> 21916 bytes
-rw-r--r--data/locales/tr_TR/LC_MESSAGES/messages.po1511
-rw-r--r--data/locales/vn_VN/LC_MESSAGES/messages.mobin0 -> 30749 bytes
-rw-r--r--data/locales/vn_VN/LC_MESSAGES/messages.po1709
-rw-r--r--data/locales/zh_CN/LC_MESSAGES/messages.mobin0 -> 10272 bytes
-rw-r--r--data/locales/zh_CN/LC_MESSAGES/messages.po1690
-rw-r--r--data/schema/1.sql20
-rw-r--r--data/schema/2.sql10
-rw-r--r--data/schema/3.sql85
-rw-r--r--data/schema/4.sql11
-rw-r--r--data/schema/5.sql1
-rw-r--r--data/schema/6.sql19
-rw-r--r--data/tables-postgresql.sql259
-rw-r--r--data/tables.sql203
-rw-r--r--data/templates/default/about.tpl.php34
-rw-r--r--data/templates/default/admin.tpl.php35
-rw-r--r--data/templates/default/bookmarkcommondescriptionedit.tpl.php58
-rw-r--r--data/templates/default/bookmarklet.inc.php152
-rw-r--r--data/templates/default/bookmarks-thumbnail.inc.tpl.php18
-rw-r--r--data/templates/default/bookmarks-vote-horizontal.inc.tpl.php50
-rw-r--r--data/templates/default/bookmarks-vote.inc.tpl.php44
-rw-r--r--data/templates/default/bookmarks.tpl.php547
-rw-r--r--data/templates/default/bottom.inc.php29
-rw-r--r--data/templates/default/dynamictags.inc.php148
-rw-r--r--data/templates/default/editbookmark.tpl.php221
-rw-r--r--data/templates/default/editprofile-sslclientcerts.tpl.php60
-rw-r--r--data/templates/default/editprofile.tpl.php81
-rw-r--r--data/templates/default/error.404.tpl.php9
-rw-r--r--data/templates/default/error.500.tpl.php9
-rw-r--r--data/templates/default/importDelicious.tpl.php42
-rw-r--r--data/templates/default/importNetscape.tpl.php50
-rw-r--r--data/templates/default/importStructure.tpl.php42
-rw-r--r--data/templates/default/login.tpl.php41
-rw-r--r--data/templates/default/password.tpl.php26
-rw-r--r--data/templates/default/profile.tpl.php74
-rw-r--r--data/templates/default/register.tpl.php54
-rw-r--r--data/templates/default/rss.tpl.php30
-rw-r--r--data/templates/default/search.menu.php57
-rw-r--r--data/templates/default/sidebar.block.common.php28
-rw-r--r--data/templates/default/sidebar.block.linked.php66
-rw-r--r--data/templates/default/sidebar.block.menu.php80
-rw-r--r--data/templates/default/sidebar.block.menu2.php71
-rw-r--r--data/templates/default/sidebar.block.popular.php44
-rw-r--r--data/templates/default/sidebar.block.recent.php39
-rw-r--r--data/templates/default/sidebar.block.related.php40
-rw-r--r--data/templates/default/sidebar.block.search.php51
-rw-r--r--data/templates/default/sidebar.block.tagactions.php35
-rw-r--r--data/templates/default/sidebar.block.users.php34
-rw-r--r--data/templates/default/sidebar.block.watchlist.php60
-rw-r--r--data/templates/default/sidebar.block.watchstatus.php28
-rw-r--r--data/templates/default/sidebar.linkedtags.inc.php84
-rw-r--r--data/templates/default/sidebar.tpl.php23
-rw-r--r--data/templates/default/tag2tagadd.tpl.php57
-rw-r--r--data/templates/default/tag2tagdelete.tpl.php49
-rw-r--r--data/templates/default/tag2tagedit.tpl.php73
-rw-r--r--data/templates/default/tagcommondescriptionedit.tpl.php48
-rw-r--r--data/templates/default/tagdelete.tpl.php20
-rw-r--r--data/templates/default/tagedit.tpl.php33
-rw-r--r--data/templates/default/tagrename.tpl.php40
-rw-r--r--data/templates/default/tags.tpl.php31
-rw-r--r--data/templates/default/toolbar.inc.php37
-rw-r--r--data/templates/default/top.inc.php83
-rw-r--r--data/templates/default/users.tpl.php33
-rw-r--r--data/templates/testdummy/top.inc.php66
-rw-r--r--doc/AUTHORS37
-rw-r--r--doc/ChangeLog438
-rw-r--r--doc/INSTALL.txt148
-rw-r--r--doc/LICENSE.txt340
-rw-r--r--doc/README.rst67
-rw-r--r--doc/UPGRADE.txt172
-rw-r--r--doc/allinone.rst31
-rw-r--r--doc/authentication.rst216
-rw-r--r--doc/configuration.rst98
-rw-r--r--doc/developers/TODO.rst104
-rw-r--r--doc/developers/api.rst10
-rw-r--r--doc/developers/debugging.rst24
-rw-r--r--doc/developers/doc-TODO.rst9
-rw-r--r--doc/developers/release-new-version.rst31
-rw-r--r--doc/developers/rules.rst30
-rw-r--r--doc/developers/running-unit-tests.rst31
-rw-r--r--doc/developers/translation.rst47
-rw-r--r--doc/index.rst50
-rw-r--r--doc/ssl-client-certificates.rst181
-rw-r--r--doc/themes.rst58
-rw-r--r--doc/tools.rst44
-rw-r--r--html.properties1
-rw-r--r--kvmxfile184
-rw-r--r--puppet/.gitignore3
-rw-r--r--puppet/.mrconfig (renamed from .mrconfig)0
-rw-r--r--puppet/LICENSE (renamed from LICENSE)0
-rw-r--r--puppet/Makefile (renamed from Makefile)0
-rw-r--r--puppet/README.md (renamed from README.md)0
-rw-r--r--puppet/TODO.md (renamed from TODO.md)0
-rw-r--r--puppet/Vagrantfile (renamed from Vagrantfile)0
-rwxr-xr-xpuppet/bin/copymodules (renamed from bin/copymodules)0
-rwxr-xr-xpuppet/bin/dependencies (renamed from bin/dependencies)0
-rwxr-xr-xpuppet/bin/deploy (renamed from bin/deploy)0
-rwxr-xr-xpuppet/bin/mrconfig (renamed from bin/mrconfig)0
-rwxr-xr-xpuppet/bin/post-receive (renamed from bin/post-receive)0
-rwxr-xr-xpuppet/bin/provision (renamed from bin/provision)0
-rwxr-xr-xpuppet/bin/submodules (renamed from bin/submodules)0
-rwxr-xr-xpuppet/bin/subtrees (renamed from bin/subtrees)0
-rwxr-xr-xpuppet/bin/symlinks (renamed from bin/symlinks)0
-rwxr-xr-xpuppet/bin/syncmodules (renamed from bin/syncmodules)0
-rw-r--r--puppet/config/common.yaml (renamed from config/common.yaml)0
-rw-r--r--puppet/config/hiera.yaml (renamed from config/hiera.yaml)0
-rw-r--r--puppet/config/node/box.example.org.yaml (renamed from config/node/box.example.org.yaml)0
-rw-r--r--puppet/files/.empty (renamed from files/.empty)0
-rw-r--r--puppet/files/patches/trusty/puppet-stack-level.md (renamed from files/patches/trusty/puppet-stack-level.md)0
-rw-r--r--puppet/files/patches/trusty/puppet-stack-level.patch (renamed from files/patches/trusty/puppet-stack-level.patch)0
-rw-r--r--puppet/files/patches/wheezy/collected-resources.md (renamed from files/patches/wheezy/collected-resources.md)0
l---------puppet/hiera.yaml (renamed from hiera.yaml)0
-rw-r--r--puppet/kvmxfile98
-rw-r--r--puppet/manifests/nodes/.empty (renamed from manifests/nodes/.empty)0
-rw-r--r--puppet/manifests/nodes/default.pp (renamed from manifests/nodes/default.pp)0
-rw-r--r--puppet/modules/.empty (renamed from modules/.empty)0
l---------puppet/puppet (renamed from puppet)0
-rw-r--r--res/docs/header.tpl.html8
-rw-r--r--res/docs/style.css28
-rw-r--r--scripts/avahi-export.php107
-rw-r--r--scripts/compile-translation.php24
-rw-r--r--scripts/create-testbookmarks.php24
-rw-r--r--scripts/create-testuser.php10
-rw-r--r--scripts/database-dump.php26
-rw-r--r--scripts/database-restore.php37
-rw-r--r--scripts/fix-unfiled-tags.php35
-rw-r--r--scripts/update-translation-base.php19
-rw-r--r--scripts/update-translation.php25
-rw-r--r--src/SemanticScuttle/Config.php123
-rw-r--r--src/SemanticScuttle/DbService.php67
-rw-r--r--src/SemanticScuttle/Environment.php48
-rw-r--r--src/SemanticScuttle/Model/Bookmark.php63
-rw-r--r--src/SemanticScuttle/Model/RemoteUser.php48
-rw-r--r--src/SemanticScuttle/Model/Template.php107
-rw-r--r--src/SemanticScuttle/Model/Theme.php97
-rw-r--r--src/SemanticScuttle/Model/User.php209
-rw-r--r--src/SemanticScuttle/Model/User/SslClientCert.php148
-rw-r--r--src/SemanticScuttle/Model/UserArray.php41
-rw-r--r--src/SemanticScuttle/Service.php60
-rw-r--r--src/SemanticScuttle/Service/AuthUser.php232
-rw-r--r--src/SemanticScuttle/Service/Bookmark.php1161
-rw-r--r--src/SemanticScuttle/Service/Bookmark2Tag.php714
-rw-r--r--src/SemanticScuttle/Service/Cache.php72
-rw-r--r--src/SemanticScuttle/Service/CommonDescription.php229
-rw-r--r--src/SemanticScuttle/Service/Factory.php154
-rw-r--r--src/SemanticScuttle/Service/SearchHistory.php280
-rw-r--r--src/SemanticScuttle/Service/Tag.php160
-rw-r--r--src/SemanticScuttle/Service/Tag2Tag.php470
-rw-r--r--src/SemanticScuttle/Service/TagCache.php397
-rw-r--r--src/SemanticScuttle/Service/TagStat.php233
-rw-r--r--src/SemanticScuttle/Service/Template.php119
-rw-r--r--src/SemanticScuttle/Service/Thumbnails.php59
-rw-r--r--src/SemanticScuttle/Service/User.php1090
-rw-r--r--src/SemanticScuttle/Service/User/SslClientCert.php283
-rw-r--r--src/SemanticScuttle/Service/Vote.php337
-rw-r--r--src/SemanticScuttle/Thumbnailer/Null.php52
-rw-r--r--src/SemanticScuttle/Thumbnailer/Phancap.php92
-rw-r--r--src/SemanticScuttle/constants.php74
-rw-r--r--src/SemanticScuttle/db/db2.php417
-rw-r--r--src/SemanticScuttle/db/firebird.php527
-rw-r--r--src/SemanticScuttle/db/mssql-odbc.php576
-rw-r--r--src/SemanticScuttle/db/mssql.php551
-rw-r--r--src/SemanticScuttle/db/mysql.php552
-rw-r--r--src/SemanticScuttle/db/mysql4.php552
-rw-r--r--src/SemanticScuttle/db/mysqli.php562
-rw-r--r--src/SemanticScuttle/db/oracle.php468
-rw-r--r--src/SemanticScuttle/db/postgres.php597
-rw-r--r--src/SemanticScuttle/db/sqlite.php387
-rw-r--r--src/SemanticScuttle/functions.php292
-rw-r--r--src/SemanticScuttle/header-standalone.php19
-rw-r--r--src/SemanticScuttle/header.php174
-rw-r--r--src/SemanticScuttle/utf8.php478
-rw-r--r--src/php-gettext/AUTHORS3
-rw-r--r--src/php-gettext/COPYING340
-rw-r--r--src/php-gettext/ChangeLog152
-rw-r--r--src/php-gettext/Makefile38
-rw-r--r--src/php-gettext/README161
-rw-r--r--src/php-gettext/examples/index.php27
-rw-r--r--src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.mobin0 -> 585 bytes
-rw-r--r--src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.po30
-rw-r--r--src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mobin0 -> 829 bytes
-rw-r--r--src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po30
-rw-r--r--src/php-gettext/examples/pigs_dropin.php89
-rw-r--r--src/php-gettext/examples/pigs_fallback.php88
-rw-r--r--src/php-gettext/examples/update14
-rw-r--r--src/php-gettext/gettext.inc534
-rw-r--r--src/php-gettext/gettext.php421
-rw-r--r--src/php-gettext/streams.php167
-rw-r--r--src/php-gettext/tests/LocalesTest.php66
-rw-r--r--src/php-gettext/tests/ParsingTest.php43
-rw-r--r--tests/AllTests.php62
-rw-r--r--tests/Api/ExportCsvTest.php262
-rw-r--r--tests/Api/OpenSearchTest.php76
-rw-r--r--tests/Api/PostsAddTest.php456
-rw-r--r--tests/Api/PostsDeleteTest.php278
-rw-r--r--tests/Api/PostsUpdateTest.php110
-rw-r--r--tests/Bookmark2TagTest.php688
-rw-r--r--tests/BookmarkTest.php1380
-rw-r--r--tests/CommonDescriptionTest.php111
-rw-r--r--tests/FactoryTest.php13
-rw-r--r--tests/LAUNCH_TESTS28
-rw-r--r--tests/Model/BookmarkTest.php65
-rw-r--r--tests/SearchHistoryTest.php373
-rw-r--r--tests/SemanticScuttle/ConfigTest.php206
-rw-r--r--tests/SemanticScuttle/EnvironmentTest.php234
-rw-r--r--tests/Tag2TagTest.php537
-rw-r--r--tests/TagTest.php99
-rw-r--r--tests/TagsCacheTest.php192
-rw-r--r--tests/TestBase.php159
-rw-r--r--tests/TestBaseApi.php271
-rw-r--r--tests/UserArrayTest.php66
-rw-r--r--tests/UserTest.php527
-rw-r--r--tests/VoteTest.php536
-rw-r--r--tests/ajax/GetAdminLinkedTagsTest.php120
-rw-r--r--tests/ajax/GetAdminTagsTest.php120
-rw-r--r--tests/ajax/GetContactTagsTest.php100
-rwxr-xr-xtests/data/BookmarkTest_deliciousbookmarks.xml7
-rw-r--r--tests/data/BookmarkTest_deliciousbookmarks_longdesc_3469257.xml4
-rw-r--r--tests/data/BookmarkTest_deliciousbookmarks_private.xml1
-rwxr-xr-xtests/data/BookmarkTest_netscapebookmarks.html27
-rw-r--r--tests/dox.html1
-rw-r--r--tests/phpunit.xml15
-rw-r--r--tests/prepare.php41
-rwxr-xr-xtests/www/bookmarksTest.php156
-rwxr-xr-xtests/www/editTest.php48
-rwxr-xr-xtests/www/importNetscapeTest.php33
-rwxr-xr-xtests/www/importTest.php33
-rw-r--r--tests/www/indexTest.php58
-rw-r--r--tests/www/rssTest.php151
-rw-r--r--tests/www/searchTest.php70
-rw-r--r--www/.htaccess40
-rw-r--r--www/about.php27
-rw-r--r--www/admin.php95
-rw-r--r--www/ajax/getadminlinkedtags.php92
-rw-r--r--www/ajax/getadmintags.php47
-rw-r--r--www/ajax/getcontacttags.php47
-rw-r--r--www/ajax/getlinkedtags.php141
-rw-r--r--www/ajax/gettags.php44
-rw-r--r--www/ajaxDelete.php48
-rw-r--r--www/ajaxGetNewPrivateKey.php35
-rw-r--r--www/ajaxGetTitle.php79
-rw-r--r--www/ajaxIsAvailable.php44
-rw-r--r--www/ajaxVote.php28
-rw-r--r--www/alltags.php88
-rw-r--r--www/api/export_csv.php63
-rw-r--r--www/api/export_gcs.php67
-rw-r--r--www/api/export_html.php81
-rw-r--r--www/api/export_sioc.php90
-rw-r--r--www/api/httpauth.inc.php58
-rw-r--r--www/api/opensearch.php16
-rw-r--r--www/api/posts_add.php146
-rw-r--r--www/api/posts_all.php50
-rw-r--r--www/api/posts_dates.php60
-rw-r--r--www/api/posts_delete.php57
-rw-r--r--www/api/posts_get.php83
-rw-r--r--www/api/posts_public.php51
-rw-r--r--www/api/posts_recent.php85
-rw-r--r--www/api/posts_update.php58
-rw-r--r--www/api/tags_get.php25
-rw-r--r--www/api/tags_rename.php36
-rw-r--r--www/bookmarkcommondescriptionedit.php78
-rw-r--r--www/bookmarks.php323
-rw-r--r--www/edit.php121
-rw-r--r--www/go.php48
-rw-r--r--www/gsearch/context.php43
-rw-r--r--www/gsearch/index.php71
-rw-r--r--www/history.php93
-rw-r--r--www/import.php133
-rw-r--r--www/importNetscape.php136
-rw-r--r--www/importStructure.php76
-rw-r--r--www/index.php117
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.js612
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.min.js32
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.core.js308
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.core.min.js17
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.position.js252
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.position.min.js16
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.widget.js262
-rw-r--r--www/js/jquery-ui-1.8.11/jquery.ui.widget.min.js15
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_75_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.pngbin0 -> 120 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_dadada_1x400.pngbin0 -> 111 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.pngbin0 -> 110 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin0 -> 101 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_222222_256x240.pngbin0 -> 4369 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_454545_256x240.pngbin0 -> 4369 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_888888_256x240.pngbin0 -> 4369 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/jquery.ui.all.css11
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/jquery.ui.autocomplete.css53
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/jquery.ui.base.css2
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/jquery.ui.core.css41
-rw-r--r--www/js/jquery-ui-1.8.11/themes/base/jquery.ui.theme.css252
-rw-r--r--www/js/jstree-1.0-rc2/jquery-1.4.2.js6240
-rw-r--r--www/js/jstree-1.0-rc2/jquery-1.4.2.min.js154
-rw-r--r--www/js/jstree-1.0-rc2/jquery.jstree.js3510
-rw-r--r--www/js/jstree-1.0-rc2/jquery.jstree.min.js1
-rw-r--r--www/js/jstree-1.0-rc2/themes/apple/bg.jpgbin0 -> 331 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/apple/d.pngbin0 -> 7765 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/apple/dot_for_ie.gifbin0 -> 43 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/apple/style.css60
-rw-r--r--www/js/jstree-1.0-rc2/themes/apple/throbber.gifbin0 -> 1849 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/classic/d.pngbin0 -> 7535 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/classic/dot_for_ie.gifbin0 -> 43 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/classic/style.css59
-rw-r--r--www/js/jstree-1.0-rc2/themes/classic/throbber.gifbin0 -> 1849 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default-rtl/d.gifbin0 -> 2872 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default-rtl/d.pngbin0 -> 7459 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default-rtl/dots.gifbin0 -> 132 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default-rtl/style.css83
-rw-r--r--www/js/jstree-1.0-rc2/themes/default-rtl/throbber.gifbin0 -> 1849 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default/d.gifbin0 -> 2944 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default/d.pngbin0 -> 7635 bytes
-rw-r--r--www/js/jstree-1.0-rc2/themes/default/style.css73
-rw-r--r--www/js/jstree-1.0-rc2/themes/default/throbber.gifbin0 -> 1849 bytes
-rw-r--r--www/jsScuttle.php199
-rw-r--r--www/login.php62
-rw-r--r--www/password.php78
-rw-r--r--www/permalink.php122
-rw-r--r--www/player/error.swfbin0 -> 90 bytes
-rw-r--r--www/player/license.txt10
-rw-r--r--www/player/load.swfbin0 -> 311 bytes
-rw-r--r--www/player/musicplayer_f6.swfbin0 -> 6463 bytes
-rw-r--r--www/player/play.swfbin0 -> 73 bytes
-rw-r--r--www/player/stop.swfbin0 -> 77 bytes
-rw-r--r--www/populartags.php84
-rw-r--r--www/profile.php191
-rw-r--r--www/register.php107
-rw-r--r--www/rss.php174
-rw-r--r--www/search.php155
-rw-r--r--www/tag2tagadd.php73
-rw-r--r--www/tag2tagdelete.php85
-rw-r--r--www/tag2tagedit.php60
-rw-r--r--www/tagcommondescriptionedit.php82
-rw-r--r--www/tagdelete.php58
-rw-r--r--www/tagedit.php68
-rw-r--r--www/tagrename.php78
-rw-r--r--www/tags.php125
-rw-r--r--www/themes/default/icon.pngbin0 -> 771 bytes
-rw-r--r--www/themes/default/images/b_edit.pngbin0 -> 451 bytes
-rw-r--r--www/themes/default/images/bg_admin.pngbin0 -> 1212 bytes
-rw-r--r--www/themes/default/images/bg_bar.pngbin0 -> 873 bytes
-rw-r--r--www/themes/default/images/bg_header.pngbin0 -> 684 bytes
-rw-r--r--www/themes/default/images/bg_sidebar.pngbin0 -> 726 bytes
-rw-r--r--www/themes/default/images/loading.gifbin0 -> 1582 bytes
-rw-r--r--www/themes/default/images/logo-empty.pngbin0 -> 1639 bytes
-rw-r--r--www/themes/default/images/logo.pngbin0 -> 2973 bytes
-rw-r--r--www/themes/default/images/logo_24.gifbin0 -> 1530 bytes
-rw-r--r--www/themes/default/images/rss.gifbin0 -> 401 bytes
-rw-r--r--www/themes/default/images/thumbs_up.orig.pngbin0 -> 19257 bytes
-rw-r--r--www/themes/default/images/vote-against-voted.pngbin0 -> 419 bytes
-rw-r--r--www/themes/default/images/vote-against.pngbin0 -> 495 bytes
-rw-r--r--www/themes/default/images/vote-for-voted.pngbin0 -> 415 bytes
-rw-r--r--www/themes/default/images/vote-for.pngbin0 -> 625 bytes
-rw-r--r--www/themes/default/scuttle.css658
-rw-r--r--www/themes/mono/icon.pngbin0 -> 505 bytes
-rw-r--r--www/themes/mono/images/b_edit.pngbin0 -> 451 bytes
-rw-r--r--www/themes/mono/images/bg_admin.pngbin0 -> 1212 bytes
-rw-r--r--www/themes/mono/images/bg_bar.pngbin0 -> 873 bytes
-rw-r--r--www/themes/mono/images/bg_header.pngbin0 -> 684 bytes
-rw-r--r--www/themes/mono/images/bg_sidebar.pngbin0 -> 726 bytes
-rw-r--r--www/themes/mono/images/loading.gifbin0 -> 1582 bytes
-rw-r--r--www/themes/mono/images/logo-empty.pngbin0 -> 1639 bytes
-rw-r--r--www/themes/mono/images/logo.pngbin0 -> 2973 bytes
-rw-r--r--www/themes/mono/images/logo_24.gifbin0 -> 1530 bytes
-rw-r--r--www/themes/mono/images/rss.gifbin0 -> 401 bytes
-rw-r--r--www/themes/mono/images/thumbs_up.orig.pngbin0 -> 19257 bytes
-rw-r--r--www/themes/mono/images/vote-against-voted.pngbin0 -> 419 bytes
-rw-r--r--www/themes/mono/images/vote-against.pngbin0 -> 495 bytes
-rw-r--r--www/themes/mono/images/vote-for-voted.pngbin0 -> 415 bytes
-rw-r--r--www/themes/mono/images/vote-for.pngbin0 -> 625 bytes
-rw-r--r--www/themes/mono/scuttle.css703
-rw-r--r--www/themes/testdummy/images/logo.pngbin0 -> 2890 bytes
-rw-r--r--www/themes/testdummy/scuttle.css12
-rw-r--r--www/upgrade.php61
-rw-r--r--www/users.php62
-rw-r--r--www/vote.php68
-rw-r--r--www/watch.php61
-rw-r--r--www/watchlist.php128
-rw-r--r--www/widgetUWA.php234
-rw-r--r--www/www-header.php25
423 files changed, 80633 insertions, 23 deletions
diff --git a/.gitignore b/.gitignore
index 15a8f56..d37dac3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,10 @@
-keys
-ssl
-.vagrant
+doc/*.html
+dist/
+build.properties
+package.xml
+semanticscuttle-dump.sql
+data/config.php
+data/config.testing.php
+data/config.testing-tmp.php
+cache
+sql/dumps
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..3018418
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,15 @@
+language: php
+phps:
+ - 5.3
+script: cd tests && phpunit .
+notifications:
+ irc: "irc.freenode.org#semanticscuttle"
+
+before_script:
+ - mysql -e 'create database semanticscuttle_test;'
+ - mysql semanticscuttle_test < data/tables.sql
+ - cat data/config.php.dist | sed "s/'username'/'root'/" | sed "s/'password'/''/" | sed "s/'scuttle'/'semanticscuttle_test'/" | sed "s/'mysql4'/'mysqli'/" > data/config.php
+ - pyrus install pear/HTTP_Request2
+ - pyrus install pear/PEAR
+ - pyrus install pear/Stream_Var
+ - phpenv rehash
diff --git a/README.rst b/README.rst
new file mode 120000
index 0000000..2ad76b7
--- /dev/null
+++ b/README.rst
@@ -0,0 +1 @@
+doc/README.rst \ No newline at end of file
diff --git a/build.properties.dist b/build.properties.dist
new file mode 100644
index 0000000..887f888
--- /dev/null
+++ b/build.properties.dist
@@ -0,0 +1,2 @@
+sfuser=FIXME
+websitedir=FIXME
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..a173a66
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,471 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="SemanticScuttle" default="zip" basedir=".">
+ <!--
+ SemanticScuttle phing build file.
+ This script should provide a one-stop tool for all administrative
+ tasks around SemanticScuttle, like creating a release version,
+ tagging a release, running unit tests etc.
+ -->
+ <property file="build.properties" />
+ <property file="html.properties" />
+
+ <property name="version-m" value="0.98" />
+ <property name="version" value="0.98.5" />
+ <property name="stability" value="beta" />
+ <property name="releasenotes" value="
+- Fix bug #109: preserve privacy setting from Delicious export files
+ (Thanks to David Glenck)
+- Fix bug #110: Document solution for &quot;searching slashes&quot;
+- Fix bug #111: Comments in config.php mention *.inc.php files
+ (José María Mateos [rinze])
+- Fix bug: Do not use eregi functions anymore
+" />
+ <property name="zipfile" value="${phing.project.name}-${version}.zip" />
+ <property name="pkgfile" value="${phing.project.name}-${version}.tgz" />
+ <property name="distfile" value="dist/${zipfile}" />
+ <property name="distpkgfile" value="dist/pear/${pkgfile}" />
+ <property name="sfproject" value="SemanticScuttle" />
+ <property name="sffilepath" value="s/se/semanticscuttle/" />
+ <property name="svnpath" value="https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/" />
+
+ <fileset id="fs.zip" dir=".">
+ <include name="data/**"/>
+ <include name="doc/**"/>
+ <include name="src/**"/>
+ <include name="www/**"/>
+ <include name="scripts/avahi-export.php"/>
+ <include name="scripts/fix-unfiled-tags.php"/>
+ <exclude name="**/.gitignore/"/>
+ <exclude name="**/.svn/"/>
+ <exclude name="data/config.php"/>
+ <exclude name="data/config.testing.php"/>
+ <exclude name="data/config.testing-tmp.php"/>
+ <exclude name="data/locales/messages.po"/>
+ <exclude name="data/locales/*/LC_MESSAGES/messages.po"/>
+ <exclude name="src/php-gettext/examples/" />
+ <exclude name="src/php-gettext/tests/"/>
+ </fileset>
+
+ <fileset id="fs.doc" dir="doc">
+ <include name="ChangeLog"/>
+ <include name="**.txt"/>
+ <include name="**.rst"/>
+ <include name="**/*.rst"/>
+ <exclude name="LICENSE.txt"/>
+ <exclude name="developers/TODO.rst"/>
+ <exclude name="allinone.rst"/>
+ </fileset>
+
+ <taskdef name="rST" classname="phing.tasks.ext.rSTTask" />
+ <taskdef name="d51pearpkg2" classname="phing.tasks.ext.d51PearPkg2Task" />
+
+ <target name="zip" depends="check"
+ description="Create zip file for release"
+ >
+ <!--
+ The release file is for end users, so it is safe to
+ remove anything developer-related.
+
+ Test your zip with: unzip -l SemanticScuttle-0.95.0.zip
+ -->
+ <echo msg="Creating distribution zip for SemanticScuttle ${version}"/>
+ <delete file="${distfile}" failonerror="false"/>
+ <zip destfile="${distfile}" prefix="${phing.project.name}-${version}/">
+ <fileset refid="fs.zip"/>
+ </zip>
+ </target>
+
+
+
+ <target name="package" depends="check"
+ description="Creates the pear package"
+ >
+ <d51pearpkg2 dir="." baseinstalldir="/">
+ <name>SemanticScuttle</name>
+ <summary>A social bookmarking tool</summary>
+ <description>
+ A social bookmarking tool experimenting with new features
+ like structured tags or collaborative descriptions of tags.
+ </description>
+ <channel>semanticscuttle.sourceforge.net</channel>
+
+ <lead user="cweiske" name="Christian Weiske" email="cweiske@cweiske.de" />
+ <license>GPL</license>
+
+ <version release="${version}" api="${version}" />
+ <stability release="${stability}" api="${stability}" />
+
+ <notes>${releasenotes}</notes>
+
+ <dependencies>
+ <php minimum_version="5.2.0" />
+ <pear minimum_version="1.8.1" />
+
+ <package name="HTML_QuickForm2"
+ channel="pear.php.net"
+ minimum_version="0.4.0"
+ />
+ <!-- unit tests: -->
+ <package name="HTML_Request2"
+ channel="pear.php.net"
+ minimum_version="2.0.0"
+ />
+ <package name="Stream_Var"
+ channel="pear.php.net"
+ minimum_version="1.1.0"
+ />
+
+ </dependencies>
+
+ <!-- map directory (key) to role -->
+ <dirroles key="www">www</dirroles>
+ <dirroles key="data">data</dirroles>
+ <dirroles key="doc">doc</dirroles>
+ <dirroles key="scripts">doc</dirroles>
+ <dirroles key="src">php</dirroles>
+ <dirroles key="tests">test</dirroles>
+
+ <!-- do not add the following files to the package.
+ copied from excludes above -->
+ <ignore>**/.gitignore</ignore>
+ <ignore>**/.svn</ignore>
+ <ignore>build*</ignore>
+ <ignore>data/config.php</ignore>
+ <ignore>data/config.testing.php</ignore>
+ <ignore>data/config.testing-tmp.php</ignore>
+ <ignore>data/locales/messages.po</ignore>
+ <ignore>data/locales/*/LC_MESSAGES/messages.po</ignore>
+ <ignore>dist/**</ignore>
+ <ignore>src/php-gettext/examples/**</ignore>
+ <ignore>src/php-gettext/tests/**</ignore>
+ <ignore>*.properties</ignore>
+ <ignore>semanticscuttle-dump.sql</ignore>
+ <ignore>*.tgz</ignore>
+
+ <replacement path="src/SemanticScuttle/header.php"
+ type="pear-config" from="@data_dir@" to="data_dir"
+ />
+ <replacement path="src/SemanticScuttle/header.php"
+ type="pear-config" from="@www_dir@" to="www_dir"
+ />
+ <replacement path="src/SemanticScuttle/Config.php"
+ type="pear-config" from="@data_dir@" to="data_dir"
+ />
+ <replacement path="src/SemanticScuttle/Config.php"
+ type="pear-config" from="@www_dir@" to="www_dir"
+ />
+ <replacement path="www/www-header.php"
+ type="pear-config" from="@data_dir@" to="data_dir"
+ />
+ <replacement path="tests/prepare.php"
+ type="pear-config" from="@data_dir@" to="data_dir"
+ />
+
+ <changelog version="0.98.5" date="2013-03-20" license="GPL">
+- Fix bug #109: preserve privacy setting from Delicious export files
+ (Thanks to David Glenck)
+- Fix bug #110: Document solution for "searching slashes"
+- Fix bug #111: Comments in config.php mention *.inc.php files
+ (José María Mateos [rinze])
+- Fix bug: Do not use eregi functions anymore
+ </changelog>
+
+ <changelog version="0.98.4" date="2012-01-21" license="GPL">
+- Fix bug #3439729: URLs were escaped too often in bookmark list
+- Fix bug: Subtitle was not escaped
+- Fix bug #3388219: Incorrect URL when cancelling tag2tag-actions
+- Fix bug #3393951: Logo images missing on bookmark page
+- Fix bug #3399815: PHP error in opensearch API in 0.98.3
+- Fix bug #3407728: Can't delete users from admin page
+- Fix bug #3431742: open_basedir problems with /etc/ config files
+- Fix bug #3436624: Wrong URL for Delicious API when importing
+- Fix bug #3463481: RSS feed show warnings in feedvalidator.org
+- Fix bug #3384416: Use URL with protocol in bookmarklet
+- Fix bug: Invalid HTML when website thumbnails are activated
+- Fix bug #3413459: Thumbnails not in one line
+- Fix bug #3468293: Delicious import does not preserve private links
+- Fix bug #3396727: Title of http://lesscss.org/ not loaded
+- Fix bug #3111254: getIdFromUser() always returns an integer now (part 2)
+- Implement request #3403609: fr_CA translation update
+- Implement patch #3476011: PostgreSQL tables can not be initialized
+ (Frédéric Fauberteau [triaxx])
+ </changelog>
+
+ <!-- 0.98.3 was not needed -->
+
+ <changelog version="0.98.2" date="2011-08-08" license="GPL">
+- Fix bug #3385724: Rename tag ends with XML Parsing Error
+- Fix bug #3386178: "system:unfiled" secret tag does not work
+- Fix bug #3384416: Update documentation to explain HTTP/HTTPS root problem
+- Fix bug #3382126: Linked tags does not work for tags with spaces
+- Fix delicious API help page link
+ </changelog>
+
+ <changelog version="0.98.1" date="2011-08-01" license="GPL">
+- Fix bug #3375635: XML parsing problem in top.inc.php
+- Fix bug #3375428: Forgot to remove some old dojo files
+- Fix bug #3160512: Make SemanticScuttle work with FastCGI
+- Fix bug #3376618: Broken tag completion for private bookmarks
+ </changelog>
+
+ <changelog version="0.98.0" date="2011-07-21" license="GPL">
+- Switch to jQuery and drop dojo
+- Implement request #2928950: Private keys for RSS feeds (Mark Pemberton)
+- Implement request #3164348: Configurable default privacy (Brett Dee)
+- Implement request #1989987: Theming support
+- Implement request #3054906: Show user's full name instead of nickname
+- Implement patch #3059829: update ``FR_CA`` translation
+- Fix bug #3187177: Wrong URL / Export XML Bookmarks
+- Fix bug #3097187: Using opensearch with two tags does not work in Firefox
+- Fix bug #3251877: French translation JavaScript Bug when editing bookmarks
+- Fix bug #3168521: Title of tag-filtered RSS Feed is broken
+- Fix bug #2853627: Javascript warning
+- Fix bug in ``getTagsForBookmarks()`` that fetched all tags
+- Fix privacy issue when fetching tags of several users
+- Fix Google custom search XML
+- Show error message on mysqli connection errors
+- Update php-gettext library to 1.0.10
+- ``api/posts/add`` respects the 'replace' parameter now
+- Only URLs with an allowed protocol may be added to the database
+- Support HTTPS connections when ``$root`` is not configured
+- SQL schema version table to ease future database upgrades
+- Documentation is written with rST (reStructuredText) now
+- Support per-host configuration files
+ </changelog>
+
+ <changelog version="0.97.0" date="2010-06-09" license="GPL">
+- Many SQL optimizations - SemanticScuttle shows bookmarks 4 times faster now
+- New config option to skip "SET NAMES UTF8" call: $dbneedssetnames
+- Do not highlight admin bookmarks when $enableAdminColors is disabled
+- Add russian translation
+- Make HTML export follow the specifications a bit better
+- Fix bug #2953732: faulty error message for duplicate bookmarks
+- Fix bug #2960663: do not send content-type headers twice for ajax/api scripts
+- Fix bug #2976593: fr_FR locale is vietnamese
+ </changelog>
+
+ <!-- <dirroles key="bin">script</dirroles> -->
+ <!-- <replacement path="bin/doctrine" type="pear-config" from="@php_bin@" to="php_bin" /> -->
+ <!-- <release>
+ <install as="doctrine" name="bin/doctrine" />
+ -->
+ </d51pearpkg2>
+
+ <!-- time to fix the package.xml file since the task does not
+ allow everything we need:
+ - strip the base directory names like src, data and www
+ - remove that dumb baseinstalldir from files
+ - md5sums are generated automatically when packaging
+ -->
+ <!-- yes, we need to generate a 2nd file and move it back -->
+ <copy file="package.xml" tofile="package2.xml" overwrite="true">
+ <filterchain>
+ <replaceregexp>
+ <!-- remove md5sums -->
+ <regexp
+ pattern="md5sum=&quot;[a-z0-9]{32}&quot; "
+ replace=""
+ />
+ <!-- remove baseinstalldir for files -->
+ <regexp
+ pattern="&lt;file baseinstalldir=&quot;/&quot;"
+ replace="&lt;file"
+ />
+ <!-- install-as for different directories -->
+ <regexp
+ pattern="(&lt;file name=&quot;data/(.+?)&quot;)"
+ replace="\1 install-as=&quot;\2&quot;"
+ />
+ <regexp
+ pattern="(&lt;file name=&quot;doc/(.+?)&quot;)"
+ replace="\1 install-as=&quot;\2&quot;"
+ />
+ <regexp
+ pattern="(&lt;file name=&quot;tests/(.+?)&quot;)"
+ replace="\1 install-as=&quot;\2&quot;"
+ />
+ <regexp
+ pattern="(&lt;file name=&quot;www/(.+?)&quot;)"
+ replace="\1 install-as=&quot;SemanticScuttle/\2&quot;"
+ />
+ <regexp
+ pattern="(&lt;file name=&quot;src/(.+?)&quot;)"
+ replace="\1 install-as=&quot;\2&quot;"
+ />
+ </replaceregexp>
+ </filterchain>
+ </copy>
+ <move file="package2.xml" tofile="package.xml" overwrite="true" />
+
+ <!-- package up -->
+ <exec command="pear package" passthru="true" />
+ <move file="${pkgfile}" todir="dist/pear/" />
+
+ <delete file="package.xml" failonerror="true" />
+ </target>
+
+
+ <target name="build-docs" description="render documentation">
+ <rST format="html" uptodate="true"
+ toolparam="--stylesheet=res/docs/style.css"
+ >
+ <fileset refid="fs.doc"/>
+ <mapper type="regexp" from="^doc/(.+?)(.rst|.txt)?$" to="dist/docs/\1.html"/>
+<!--
+ <filterchain>
+ <replacetokenswithfile dir="res/docs">
+ <
+ </replacetokenswithfile>
+ </filterchain>
+-->
+ </rST>
+ </target>
+
+ <target name="clean-docs" description="delete rendered documentation files">
+ <delete dir="dist/docs" includeemptydirs="yes"/>
+ </target>
+
+
+ <target name="release" depends="check,zip,package,deploy-sf"
+ description="Release the version on sourceforge"
+ >
+ <!-- meta-target -->
+ </target>
+
+
+ <!--
+ /home/frs/project/s/se/semanticscuttle/SemanticScuttle/v0.94/
+ /home/frs/project/s/se/semanticscuttle/SemanticScuttle/v0.94/SemanticScuttle-v0.94.zip
+ /home/frs/project/s/se/semanticscuttle/SemanticScuttle/v0.94/SemanticScuttle-v0.94.1.zip
+ https://sourceforge.net/apps/trac/sourceforge/wiki/Release%20files%20for%20download
+ -->
+ <target name="deploy-sf" depends="check,zip"
+ description="Upload release to sourceforge"
+ >
+ <delete dir="dist/deploy-sf" /><!-- make empty -->
+ <mkdir dir="dist/deploy-sf" />
+ <mkdir dir="dist/deploy-sf/v${version-m}" />
+ <copy file="${distfile}" tofile="dist/deploy-sf/v${version-m}/${zipfile}"/>
+ <!-- test
+ <exec command="rsync -av dist/deploy-sf/ /tmp/test/" escape="false" />
+ -->
+ <exec
+ command="rsync -av -e ssh dist/deploy-sf/ ${sfuser},${sfproject}@frs.sourceforge.net:/home/frs/project/${sffilepath}/${sfproject}/"
+ escape="false" checkreturn="false"
+ />
+ <!-- rsync always returns code 23 on sourceforge releases, so we
+ can't check return values -->
+ </target>
+
+
+
+ <target name="deploy-sf-pear" depends="check,package"
+ description="Update PEAR channel + website on sourceforge"
+ >
+ <available file="${websitedir}"
+ type="dir" property="available.websitedir"
+ />
+ <fail unless="available.websitedir"
+ message="Website directory not set"
+ />
+ <!--
+ 1. rsync channel data from sourceforge to local, deleting
+ superfluous channel files. Need to do that so pirum knows
+ all previous releases when adding the new package
+ 2. update channel with pirum update
+ 3. rsync to sourceforge
+ -->
+ <exec
+ command="rsync --include-from=.rsync-include-files --delete -avP -e ssh ${sfuser},${sfproject}@web.sourceforge.net:htdocs/ ."
+ dir="${websitedir}"
+ escape="false" checkreturn="false"
+ passthru="true"
+ />
+
+ <exec
+ command="pirum add ${websitedir} ${distpkgfile}"
+ passthru="true"
+ />
+ <!-- fix the generated html -->
+ <!-- yes, we need to generate a 2nd file and move it back -->
+ <copy file="${websitedir}/index.html" tofile="${websitedir}/pirum.html" overwrite="true">
+ <filterchain>
+ <replaceregexp>
+ <!-- make meta links relative -->
+ <regexp
+ pattern="href=&quot;http://semanticscuttle.sourceforge.net/"
+ replace="href=&quot;"
+ />
+ <!-- add sourceforge logo -->
+ <regexp
+ pattern="powered by "
+ replace="powered by ${html.sflogo} and "
+ />
+ </replaceregexp>
+ </filterchain>
+ </copy>
+
+ <!-- overwrite pirum generated index with our own -->
+ <copy file="${websitedir}/our-index.html" tofile="${websitedir}/index.html" overwrite="true" />
+
+ <!-- add our custom css -->
+ <append
+ destFile="${websitedir}/pirum.css"
+ file="${websitedir}/pirum-custom.css"
+ />
+
+ <!-- rsync always returns code 23 on sourceforge releases, so we
+ can't check return values -->
+ <exec
+ command="rsync --include-from=.rsync-include-files -avP -e ssh . ${sfuser},${sfproject}@web.sourceforge.net:htdocs/"
+ dir="${websitedir}"
+ escape="false" checkreturn="false"
+ passthru="true"
+ />
+ </target>
+
+
+ <target name="deploy-docs" depends="build-docs"
+ description="sync docs to sourceforge website"
+ >
+ <copy todir="${websitedir}/docs">
+ <fileset dir="dist/docs">
+ <include name="**"/>
+ </fileset>
+ </copy>
+
+ <exec
+ command="rsync -avP -e ssh . ${sfuser},${sfproject}@web.sourceforge.net:htdocs/docs"
+ dir="${websitedir}/docs"
+ escape="false" checkreturn="false"
+ passthru="true"
+ />
+ </target>
+
+
+ <target name="deploy-demo"
+ description="sync demo to sourceforge website"
+ >
+ <exec
+ command="rsync -avP -e ssh . ${sfuser},${sfproject}@web.sourceforge.net:htdocs/demo"
+ dir="${websitedir}/demo"
+ escape="false" checkreturn="false"
+ passthru="true"
+ />
+ </target>
+
+
+
+
+ <target name="check"
+ description="Check variables"
+ >
+ <fail unless="version" message="Version not defined!" />
+ <fail unless="sfuser" message="Sourceforge username not defined!" />
+ <fail unless="sfproject" message="Sourceforge project name not defined!" />
+ <fail unless="sffilepath" message="Sourceforge project file path not defined!" />
+
+ <mkdir dir="dist" />
+ </target>
+
+</project>
diff --git a/cache/.cvsignore b/cache/.cvsignore
new file mode 100644
index 0000000..f05fcdc
--- /dev/null
+++ b/cache/.cvsignore
@@ -0,0 +1 @@
+*.cache \ No newline at end of file
diff --git a/cache/.htaccess b/cache/.htaccess
new file mode 100644
index 0000000..baa56e5
--- /dev/null
+++ b/cache/.htaccess
@@ -0,0 +1,2 @@
+order allow,deny
+deny from all \ No newline at end of file
diff --git a/data/config.default.php b/data/config.default.php
new file mode 100644
index 0000000..95df176
--- /dev/null
+++ b/data/config.default.php
@@ -0,0 +1,799 @@
+<?php
+/**
+ * Default configuration file for SemanticScuttle
+ *
+ * This file is included just before config.php.
+ * If there is something you want to change, copy the lines
+ * in your personal config.php file.
+ * Do not modify _this_ file!
+ *
+ * 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.
+ *
+ * @link http://sourceforge.net/projects/semanticscuttle/
+ */
+
+/**
+ * Array for defaults.
+ *
+ * @var array
+ */
+$defaults = array();
+
+
+/***************************************************
+ * HTML output configuration
+ */
+
+/**
+ * The name of this site.
+ *
+ * @var string
+ */
+$sitename = 'SemanticScuttle';
+
+/**
+ * The welcome message on the homepage.
+ *
+ * @var string
+ */
+$welcomeMessage = 'Welcome to SemanticScuttle!'
+ . ' Social bookmarking for small communities.';
+
+/**
+ * HTML message appearing at the bottom of the page.
+ * (just above SemanticScuttle credits)
+ *
+ * @var string
+ */
+$footerMessage = '';
+
+/**
+ * HTML message appearing at the top of the sidebar
+ *
+ * @var string
+ */
+$sidebarTopMessage = '';
+
+/**
+ * HTML message appearing at the bottom of the sidebar
+ *
+ * @var string
+ */
+$sidebarBottomMessage = '';
+
+/**
+ * The HTML theme to use. With themes, you can give your semanticscuttle
+ * installation a new look.
+ *
+ * Themes are the folders in data/templates/
+ *
+ * @var string
+ */
+$theme = 'default';
+
+
+/***************************************************
+ * System configuration
+ */
+
+
+/**
+ * SemanticScuttle root directory.
+ * Set to NULL to autodetect the root url of the website.
+ *
+ * If your installation is in a subdirectory like
+ * "http://www.example.com/semanticscuttle/" then
+ * replace NULL by your address (between "" and with trailing '/')
+ *
+ * The autodetection works for both HTTP and HTTPS urls.
+ * If you offer HTTP *only*, then set your root url here.
+ *
+ * @var string
+ */
+$root = null;
+
+/**
+ * Translation from locales/ folder.
+ *
+ * Examples: de_DE, en_GB, fr_FR
+ *
+ * @var string
+ */
+$locale = 'en_GB';
+
+/**
+ * If the cache shall be used (true/false)
+ *
+ * @var boolean
+ */
+$usecache = false;
+
+/**
+ * Cache directory.
+ *
+ * @var string
+ */
+$dir_cache = dirname(__FILE__) . '/cache/';
+
+/**
+ * Use clean urls without .php filenames.
+ * Requires mod_rewrite (for Apache) to be active.
+ *
+ * @var boolean
+ */
+$cleanurls = false;
+
+/**
+ * Show debug messages.
+ * This setting is recommended when setting up SemanticScuttle,
+ * and when hacking on it.
+ *
+ * @var boolean
+ */
+$debugMode = false;
+
+
+
+/***************************************************
+ * Database configuration
+ */
+
+/**
+ * Database driver
+ *
+ * available:
+ * mysql4, mysqli, mysql, oracle, postgres, sqlite, db2, firebird,
+ * mssql, mssq-odbc
+ *
+ * @var string
+ */
+$dbtype = 'mysql4';
+
+/**
+ * Database hostname/IP
+ *
+ * @var string
+ */
+$dbhost = '127.0.0.1';
+
+/**
+ * Database port.
+ *
+ * When using mysqli, leave this to null
+ * - connecting will fail otherwise.
+ *
+ * @var string|integer
+ */
+$dbport = null;
+
+/**
+ * Database username
+ *
+ * @var string
+ */
+$dbuser = 'username';
+
+/**
+ * Database password
+ *
+ * @var string
+ */
+$dbpass = 'password';
+
+
+/**
+ * Name of database
+ *
+ * @var string
+ */
+$dbname = 'scuttle';
+
+/**
+ * Database table name prefix.
+ * Do not use "-" since this is badly handled by MySQL.
+ *
+ * @var string
+ */
+$tableprefix = 'sc_';
+
+/*
+ * If the database needs to be switched to UTF8
+ * manually or not. If true, a "SET NAMES UTF8" query
+ * will be sent at the beginning. If you need performance,
+ * save this query and set it in your mysql server options.
+ *
+ * @var boolean
+ */
+$dbneedssetnames = true;
+
+
+/***************************************************
+ * Users
+ */
+
+/**
+ * Contact address for the site administrator.
+ * Used as the FROM address in password retrieval e-mails.
+ *
+ * @var string
+ */
+$adminemail = 'admin@example.org';
+
+/**
+ * Array of user names who have admin rights
+ *
+ * Example:
+ * <code>
+ * $admin_users = array('adminnickname', 'user1nick', 'user2nick');
+ * </code>
+ *
+ * @var array
+ */
+$admin_users = array();
+
+/**
+ * If admin users can edit or delete bookmarks belonging to other users.
+ *
+ * @var boolean
+ */
+$adminsCanModifyBookmarksFromOtherUsers = true;
+
+/**
+ * If tags from other admins are proposed to each admin
+ * (in add/edit a bookmark page).
+ *
+ * @var boolean
+ */
+$adminsAreAdvisedTagsFromOtherAdmins = false;
+
+/**
+ * Array of usernames that cannot be registered
+ *
+ * @var array
+ */
+$reservedusers = array('all', 'watchlist');
+
+
+
+
+/***************************************************
+ * Anti SPAM measures
+ */
+
+/**
+ * A question to avoid spam.
+ * Shown on user registration page.
+ *
+ * @var string
+ * @see $antispamAnswer
+ */
+$antispamQuestion = 'name of this application';
+
+/**
+ * The answer to the antispam question
+ * Users have to write exactly this string.
+ *
+ * @var string
+ * @see $antispamQuestion
+ */
+$antispamAnswer = 'semanticscuttle';
+
+/**
+ * Enable or disable user registration
+ *
+ * @var boolean
+ */
+$enableRegistration = true;
+
+
+
+/***************************************************
+ * Display Templates
+ */
+
+/**
+ * Directory where the template files should be loaded from.
+ * Template files are *.tpl.php
+ *
+ * @var string
+ */
+$TEMPLATES_DIR = dirname(__FILE__) . '/templates/';
+
+/**
+ * Header template file.
+ * Included before content files.
+ *
+ * @var string
+ */
+$top_include = 'top.inc.php';
+
+/**
+ * Footer template file.
+ * Included after content has been generated and output.
+ *
+ * @var string
+ */
+$bottom_include = 'bottom.inc.php';
+
+/**
+ * Ordering of sidebar blocks.
+ * See $menu2Tags for item of menu2
+ *
+ * @var array
+ * @see $menu2Tags
+ */
+$index_sidebar_blocks = array(
+ 'search',
+ 'menu2',
+ 'menu',
+ 'users',
+ 'recent'
+);
+
+
+
+/***************************************************
+ * Bookmarks
+ */
+
+/**
+ * Format for short dates.
+ * Used in date() calls
+ *
+ * @var string
+ * @link http://php.net/date
+ */
+$shortdate = 'Y-m-d';
+
+/**
+ * Format of long dates.
+ * Used in date() calls.
+ *
+ * @var string
+ * @link http://php.net/date
+ */
+$longdate = 'j F Y';
+
+/**
+ * Include rel="nofollow" attribute on bookmark links
+ *
+ * @var boolean
+ */
+$nofollow = true;
+
+/**
+ * Default number of bookmarks per page.
+ * -1 means no limit.
+ *
+ * @var integer
+ * @see $defaultPerPageForAdmins
+ */
+$defaultPerPage = 10;
+
+/**
+ * Default number of bookmarks per page for admins.
+ * -1 means no limit.
+ *
+ * @var integer
+ * @see $defaultPerPage
+ */
+$defaultPerPageForAdmins = 10;
+
+/**
+ * Number of days that bookmarks or tags are considered "recent".
+ *
+ * @var integer
+ */
+$defaultRecentDays = 14;
+
+/**
+ * Bookmark ordering
+ * (date, title, url)
+ * in order ascending or descending
+ * - date_desc - By date of entry descending.
+ * Latest entry first. (Default)
+ * - date_asc - By date of entry ascending.
+ * Earliest entry first.
+ * - title_desc - By title, descending alphabetically.
+ * - title_asc - By title, ascending alphabetically.
+ * - url_desc - By URL, descending alphabetically.
+ * - url_asc - By URL, ascending alphabetically.
+ *
+ * @var string
+ */
+$defaultOrderBy = 'date_desc';
+
+/**
+ * Database field to use when sorting by date.
+ * Options here are 'bModified' to sort after
+ * modification date, and 'bDatetime' to sort
+ * after creation date
+ *
+ * @var string
+ */
+$dateOrderField = 'bModified';
+
+/**
+ * What to show instead of a description if
+ * a bookmark has none.
+ * Default is '-'. Setting this to '' will collapse
+ * the description row for bookmarks without
+ * a description.
+ *
+ * @var string
+ */
+$blankDescription = '-';
+
+/**
+ * Number of entries that are shown in
+ * the RSS feed by default.
+ *
+ * @var integer
+ */
+$defaultRssEntries = 15;
+
+/**
+ * Number of entries the RSS puts out
+ * at maximum.
+ *
+ * @var integer
+ */
+$maxRssEntries = 100;
+
+/**
+ * Redirect all bookmarks through $url_redir to improve privacy.
+ *
+ * @var boolean
+ * @see $url_redir
+ */
+$useredir = false;
+
+/**
+ * URL prefix for bookmarks to redirect through.
+ *
+ * @var string
+ * @see $useredir
+ */
+$url_redir = 'http://www.google.com/url?sa=D&q=';
+
+/**
+ * Enable short URL service.
+ * Can be used to visit urls using http://example.org/go/shortname
+ *
+ * @var boolean
+ */
+$shorturl = true;
+
+/**
+ * Array of bookmark extensions that Scuttle should add system tags for.
+ * When adding an URL with one of the given extensions, a system
+ * tag is automatically assigned.
+ *
+ * @var array
+ */
+$filetypes = array(
+ 'audio' => array('mp3', 'ogg', 'wav'),
+ 'document' => array('doc', 'odt', 'pdf'),
+ 'image' => array('gif', 'jpeg', 'jpg', 'png'),
+ 'video' => array('avi', 'mov', 'mp4', 'mpeg', 'mpg', 'wmv')
+);
+
+/**
+ * Link protocols that are allowed for newly added bookmarks.
+ * This prevents i.e. adding javascript: links.
+ *
+ * @link http://en.wikipedia.org/wiki/URI_scheme
+ *
+ * @var array
+ */
+$allowedProtocols = array(
+ 'ftp', 'ftps',
+ 'http', 'https',
+ 'mailto', 'nntp',
+ 'xmpp'
+);
+
+/**
+ * Enable the "common bookmark description" functionality
+ *
+ * @var boolean
+ */
+$enableCommonBookmarkDescription = true;
+
+/**
+ * Enable bookmark voting system
+ *
+ * @var boolean
+ */
+$enableVoting = true;
+
+/**
+ * Voting mode:
+ * 1 - voting badge
+ * 2 - voting links: hand up/down
+ *
+ * @var integer
+ */
+$votingMode = 2;
+
+/**
+ * Hide bookmarks below a certain voting from all users.
+ * Null to deactivate it.
+ *
+ * @var integer
+ */
+$hideBelowVoting = null;
+
+/**
+ * Default privacy setting for bookmarks:
+ * 0 - Public
+ * 1 - Shared with Watchlist
+ * 2 - Private
+ *
+ * @var integer
+ */
+$defaults['privacy'] = 0;
+
+
+/****************************
+ * Website Thumbnails
+ */
+
+/**
+ * Which thumbnail service type to use.
+ *
+ * Currently supported:
+ * - null (no screenshots)
+ * - 'phancap', see http://cweiske.de/phancap.htm
+ *
+ * @var string
+ */
+$thumbnailsType = null;
+
+/**
+ * Configuration for thumbnail service.
+ *
+ * Phancap requires an array with the following keys:
+ * - url: URL to phancap's get.php file
+ * - token: user name (if access protected)
+ * - secret: password for the user (if access protected)
+ *
+ * @var array
+ */
+$thumbnailsConfig = array();
+
+
+
+/****************************
+ * Tags
+ */
+
+/**
+ * Enable common tag descriptions
+ *
+ * @var boolean
+ */
+$enableCommonTagDescription = true;
+
+/**
+ * If everybody may edit common tag description.
+ * When set to false, only admins can do it.
+ *
+ * @var boolean
+ */
+$enableCommonTagDescriptionEditedByAll = true;
+
+/**
+ * Name of the tag whose subtags will appear in the menu box.
+ *
+ * @var string
+ * @see $maxSizeMenuBlock
+ */
+$menuTag = 'menu';
+
+/**
+ * Maximum number of items (tags) appearing in menu box.
+ *
+ * @var integer
+ * @see $menuTag
+ */
+$maxSizeMenuBlock = 7;
+
+/**
+ * List of tags used by menu2 sidebar box
+ * Empty list = hidden menu2 box
+ * menu2 displays linked tags just belonging to admins.
+ *
+ * @var array
+ */
+$menu2Tags = array(
+ 'menu2', 'tags', 'configurable', 'in', 'data/config.php'
+);
+
+
+
+/****************************
+ * Search
+ */
+
+/**
+ * Number of users' searches that are saved.
+ * 10 is default, -1 means unlimited.
+ *
+ * @var integer
+ */
+$sizeSearchHistory = 10;
+
+/**
+ * Enable Google Search Engine into "gsearch/" folder.
+ *
+ * @var boolean
+ */
+$enableGoogleCustomSearch = false;
+
+
+
+
+/****************************
+ * Other
+ */
+
+/**
+ * Enables special colors on admin pages and bookmarks.
+ * Colors mark the difference to normal users.
+ *
+ * @var boolean
+ */
+$enableAdminColors = true;
+
+/**
+ * FIXME: explain better
+ *
+ * Add a possible anchor (structured content) for bookmarks description field
+ * a simple value "xxx" (like "author") automatically associates xxx with
+ * [xxx][/xxx].
+ * A complex value "xxx"=>"yyy" (like "address") directly
+ * associates xxx with yyy.
+ *
+ * @var array
+ */
+$descriptionAnchors = array(
+ 'author',
+ 'isbn',
+ 'address' => '[address][street][/street][city][/city][/address]'
+);
+
+/**
+ * GoogleAnalytics tracking code.
+ * Empty string disables analytics.
+ *
+ * @var string
+ * @link https://www.google.com/analytics/
+ */
+$googleAnalyticsCode = null;
+
+
+
+
+/****************************
+ * avahi export script
+ */
+
+/**
+ * Location of avahi service files,
+ * often /etc/avahi/services/
+ *
+ * @var string
+ */
+$avahiServiceFilePath = '/etc/avahi/services/';
+
+/**
+ * File name prefix of SemanticScuttle-generated
+ * service files
+ *
+ * @var string
+ */
+$avahiServiceFilePrefix = 'semanticscuttle-';
+
+/**
+ * Name of tag that bookmarks need to have to
+ * get exported into avahi service files.
+ *
+ * @var string
+ */
+$avahiTagName = 'zeroconf';
+
+
+
+/**
+ * Array of key value pairs to override service class names.
+ * Key is the old service name ("User"), value the new class
+ * name.
+ *
+ * @var array
+ */
+$serviceoverrides = array();
+
+
+
+
+/****************************
+ * External user authentication
+ */
+
+/**
+ * Type of external authentication via PEAR Auth
+ * To use this, you also need to set
+ * $serviceoverrides['User'] = 'SemanticScuttle_Service_AuthUser';
+ *
+ * @link http://pear.php.net/manual/en/package.authentication.auth.intro-storage.php
+ *
+ * @var string
+ */
+$authType = null;
+
+/**
+ * Options for external authentication via PEAR Auth
+ *
+ * @link http://pear.php.net/manual/en/package.authentication.auth.intro.php
+ *
+ * @var array
+ */
+$authOptions = null;
+
+/**
+ * Enable debugging for PEAR Authentication
+ *
+ * @var boolean
+ */
+$authDebug = false;
+
+/**
+ * Optional prefix to create email addresses from user names.
+ * i.e. "@example.org" to create "user@example.org" email address
+ * from "user" username.
+ *
+ * @var string
+ */
+$authEmailSuffix = null;
+
+
+
+
+/**
+ * URL unittests are being run against
+ * Has to have a trailing slash
+ *
+ * @var string
+ */
+$unittestUrl = null;
+
+/**
+ * Allow "unittestMode=1" in URLs.
+ * Should only be enabled on development systems
+ *
+ * @var boolean
+ */
+$allowUnittestMode = false;
+
+/***************************************************
+ * Caching support
+ *
+ * Use the following configuration if you want to provide a link
+ * to a cached copy of your bookmarks.
+ *
+ * Please note that these feature just provide the link if a cached
+ * copy exists.
+ *
+ * You should download SemanticScuttle bookmarks using a tool like
+ * https://git.fluxo.info/?p=httruta.git
+ */
+
+/**
+ * Set to the base public URL of you cache folder.
+ */
+$cacheUrl = null;
+
+?>
diff --git a/data/config.php.dist b/data/config.php.dist
new file mode 100644
index 0000000..5f49857
--- /dev/null
+++ b/data/config.php.dist
@@ -0,0 +1,139 @@
+<?php
+/**
+ * Configuration for SemanticScuttle.
+ *
+ * Copy this file to config.php and adjust it.
+ *
+ * See config.default.php for more options.
+ */
+
+
+/**
+ * The name of this site.
+ *
+ * @var string
+ */
+$sitename = 'SemanticScuttle';
+
+/**
+ * The welcome message on the homepage.
+ *
+ * @var string
+ */
+$welcomeMessage = 'Welcome to SemanticScuttle! Social bookmarking for small communities.';
+
+/**
+ * Translation from locales/ folder.
+ *
+ * Examples: de_DE, en_GB, fr_FR
+ *
+ * @var string
+ */
+$locale = 'en_GB';
+
+/**
+ * Use clean urls without .php filenames.
+ * Requires mod_rewrite (for Apache) to be active.
+ *
+ * @var boolean
+ */
+$cleanurls = false;
+
+/**
+ * Show debug messages.
+ * This setting is recommended when setting up SemanticScuttle,
+ * and when hacking on it.
+ *
+ * @var boolean
+ */
+$debugMode = true;
+
+
+/***************************************************
+ * Database configuration
+ */
+
+/**
+ * Database driver
+ *
+ * available:
+ * mysql4, mysqli, mysql, oracle, postgres, sqlite, db2, firebird,
+ * mssql, mssq-odbc
+ *
+ * @var string
+ */
+$dbtype = 'mysql4';
+/**
+ * Database username
+ *
+ * @var string
+ */
+$dbuser = 'username';
+
+/**
+ * Database password
+ *
+ * @var string
+ */
+$dbpass = 'password';
+
+/**
+ * Name of database
+ *
+ * @var string
+ */
+$dbname = 'scuttle';
+
+/**
+ * Database hostname/IP
+ *
+ * @var string
+ */
+$dbhost = '127.0.0.1';
+
+
+/***************************************************
+ * Users
+ */
+
+/**
+ * Contact address for the site administrator.
+ * Used as the FROM address in password retrieval e-mails.
+ *
+ * @var string
+ */
+$adminemail = 'admin@example.org';
+
+/**
+ * Array of user names who have admin rights
+ *
+ * Example:
+ * <code>
+ * $admin_users = array('adminnickname', 'user1nick', 'user2nick');
+ * </code>
+ *
+ * @var array
+ */
+$admin_users = array();
+
+
+/***************************************************
+ * Bookmarks
+ */
+
+/**
+ * Default privacy setting for bookmarks.
+ * 0 - Public
+ * 1 - Shared with Watchlist
+ * 2 - Private
+ *
+ * @var integer
+ */
+$defaults['privacy'] = 0;
+
+
+/**
+* You have completed the basic configuration!
+* More options can be found in config.default.php.
+*/
+?>
diff --git a/data/locales/de_AT/LC_MESSAGES/messages.mo b/data/locales/de_AT/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..80201d4
--- /dev/null
+++ b/data/locales/de_AT/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/de_AT/LC_MESSAGES/messages.po b/data/locales/de_AT/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..b398eea
--- /dev/null
+++ b/data/locales/de_AT/LC_MESSAGES/messages.po
@@ -0,0 +1,1699 @@
+# Copyright (C) 2008 by Elmar H. Beer
+# This file is distributed under the same license as the SemanticScuttle package.
+# Elmar H. Beer <info@ehb-world.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: SemanticScuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2008-11-18 18:46+0100\n"
+"Last-Translator: Elmar H. Beer <info@ehb-world.com>\n"
+"Language-Team: de-AT <info@ehb-world.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: German\n"
+"X-Poedit-Country: AUSTRIA\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "Die Zeile message_die() wurde öfters aufgerufen."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL - Fehler"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Zeile"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Datei"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Information"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Kritische Information"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Leider ist ein Fehler aufgetreten."
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Allgemeiner Fehler"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Leider ist ein kritischer Fehler aufgetreten."
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Kritischer Fehler"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Debugmodus"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Speichern</ strong> Sie Ihre sämtlichen Favoriten an einem Ort, von "
+"überall zugänglich."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Teilen</ strong> Sie Ihre Favoriten zugänglich für alle Anderen, mit "
+"Freunden/innen in Ihrer Beobachtungsliste oder einfach privat für Sie "
+"persönlich."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Kategorisieren</ strong> Sie Ihre Favoriten mittels unbegrenzten "
+"Schlagwörtern, anstatt Sie in unübersichtlichen Ordnern zu speichern."
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "Registrieren Sie sich jetzt!"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "um %s zu nutzen!"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Softwareinformationen"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "ist lizenziert unter der"
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+"Sie können es selbst frei auf Ihrem Webserver oder Webspace installieren"
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 unterstützt die meisten der <a href=\"http://www.delicious.com/help/api"
+"\">del.icio.us <abbr title=\"Application Programmierung Interface\">-API </ "
+"abbr> </ a>. Fast alle netten Tools, die für dieses System kreiert wurden, "
+"können für %1$s geändert und adaptiert werden. Wenn Sie eine Applikation "
+"finden, deren API-Adresse Sie nicht einfach ändern können, so fragen Sie "
+"doch einfach bei dem/der Entwickler/in nach, um dies zu adaptieren bzw. zu "
+"addieren. Man kann nie wissen, eventuell werden jene dies einfach "
+"bewerkstelligen."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Benutzername"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "Favorit(en)"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Sind Sie sicher?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Löschen"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instruktionen"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titel"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Deskription"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Letzte Änderung:"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Aktualisieren"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Inkludieren Sie eine der nachfolgenden Bookmarklets in die Favoritenliste "
+"Ihres persönlichen Webbrowsers, um jederzeit auf unkomplizierte Art und "
+"Weise einen neue Favoriten auf %s zu addieren"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Inkludieren Sie eine der nachfolgenden Bookmarklets in die Favoritenliste "
+"Ihres persönlichen Webbrowsers, um jederzeit auf unkomplizierte Art und "
+"Weise einen neue Favoriten auf %s zu addieren"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Die Favoriten mittels %s addieren."
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Die Favoriten mittels %s und einem Pop-up-Fenster addieren."
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+#, fuzzy
+msgid "Edit the common description of this tag"
+msgstr "Die gemeinsame Deskription editieren."
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+#, fuzzy
+msgid "Edit the common description of this bookmark"
+msgstr "Die gemeinsame Deskription editieren."
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Sortieren nach:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Datum"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "Favoriten anderer Benutzer/innen"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "nur meine Favoriten"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "neueste Favoriten"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "neuere Favoriten"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "ältere Favoriten"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "älteste Favoriten"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Seite %d von %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Schlagwörter"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Editieren"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "von"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr " und %s1 anderen Person%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " und %2$s%1$s anderer Person%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Die Favoriten aus einer Favoritendatei importieren."
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Kopieren"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Leider sind Sie nicht autorisiert diesen Favoriten zu editieren."
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Leider sind keine Favoriten vorhanden."
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Impressum"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+#, fuzzy
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "Favoriten"
+msgstr[1] "Favoriten"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Populäre Schlagwörter"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Populäre Schlagwörter"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Webadresse"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Erforderlich"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Privat"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Schlagwörter"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "durch Komma separiert"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+"Anmerkung: verwenden Sie \">\" um ein Schlagwort in ein hierarchisch höheres "
+"Schlagwort zu integrieren (z.B.: Europa>Frankreich>Paris)"
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+"Anmerkung: verwenden Sie \"=\" um ein Synonym für ein Schlagwort zu "
+"integrieren (z.B.: Frankreich=französisches Land)"
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Status"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Öffentlich"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Beobachtungsliste"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privat"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Favoriten löschen"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "Die gemeinsame Deskription editieren."
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importieren"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Die Favoriten aus einer Favoritendatei importieren."
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Microsoft Internet Explorer, Mozilla Firefox und Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Die Favoriten aus del.icio.us importieren."
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Name"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "E-Mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Löschen"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Kontodetails"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Benutzername"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Passwort verifizieren"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-Mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Privat"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Passwort generieren"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Persönliche Details"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Homepage"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Änderungen speichern"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Aktionen"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "Favoriten exportieren"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "HTML - Datei (für Webbrowser)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "XML - Datei (für del.icio.us etc.)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Leider nicht gefunden!"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "Leider wurde die angeforderte URL auf diesem Server nicht gefunden."
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Allgemeiner Serverfehler"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Leider konnte die angeforderte URL nicht verarbeitet werden."
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "Beobachtungsliste"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instruktionen"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Melden Sie sich <a href=\"http://del.icio.us/api/posts/all\">hier</a> bei "
+"del.icio.us an, um Ihre Favoriten zu exportieren"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Speichern Sie die resultierende <abbr title=\"Extensible Markup Language"
+"\">XML</abbr>- Datei lokal auf Ihrem Computer"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Klicken Sie auf <kbd>Durchsuchen...</kbd> um die bereits gespeicherte "
+"Favoritendatei auf Ihrem Computer zu lokalisieren und auszuwählen. Die "
+"maximale Dateigröße darf 1 MB betragen"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+"Wählen Sie die Standardeinstellung der Privatsphäre für Ihre zu "
+"importierenden Favoriten"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Klicken Sie auf <kbd>Importieren</kbd> um die bereits lokal gespeicherte "
+"Favoritendatei zu importieren. Dieser Prozess kann bis zu einer Minute "
+"andauern"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Exportieren Sie Ihre Favoriten aus dem Webbrowser in eine Datei"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Microsoft Internet Explorer: <kbd>Datei &gt; Importieren und "
+"Exportieren... &gt; Favoriten exportieren"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Favoriten &gt; Favoriten verwalten... &gt; Datei &gt; "
+"Exportieren..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Favoriten &gt; Favoriten verwalten... &gt; Werkzeuge &gt; "
+"Exportieren..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Klicken Sie auf <kbd>Durchsuchen...</kbd> um die bereits gespeicherte "
+"Favoritendatei auf Ihrem Computer zu lokalisieren und auszuwählen. Die "
+"maximale Dateigröße darf 1 MB betragen"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Passwort"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Zwei Wochen nicht nach meinem Passwort fragen"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Anmelden"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Haben Sie Ihr Passwort vergessen?"
+
+#: data/templates/default/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 ""
+"Sofern Sie Ihr Passwort vergessen haben, können Sie nun durch %s ein neues "
+"Passwort generieren lassen. Geben Sie zur Verifizierung Ihren Benutzernamen "
+"und Ihre E-Mail Adresse Ihres Accounts ein. In Kürze erhalten Sie ein neues "
+"Passwort. Verwenden Sie dieses Passwort, um sich erfolgreich anzumelden."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Passwort generieren"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Mitglied seit"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Beobachten"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Beobachtet von"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Favoriten"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Die Favoriten betrachten."
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Registrieren Sie sich hier für ein kostenloses %s Konto. Alle unten "
+"angeforderten Informationen sind für die Registrierung erforderlich"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "Sicherheitsfrage"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registrieren"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Suche"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "Zeile"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "Favoriten des/der Benutzers/in"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "meine Favoriten"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "meine Beobachtungsliste"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "alle Favoriten"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Suche"
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Ausgewählte Schlagwörter"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Einen neuen Navigationslink addieren."
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Einen Navigationslink löschen."
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+"In der Navigation inkludierte, spezielle Schlagwörter mittels dem Schlagwort "
+"'%s'."
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Spezielle Schlagwörter"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "Betrachten Sie Ihre sämtlichen Schlagwörter."
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "Ihre sämtlichen Schlagwörter."
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "Betrachten Sie sämtliche Schlagwörter dieses/r Benutzers/in."
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "Sämtliche Schlagwörter dieses/r Benutzers/in."
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "Betrachten Sie populäre Schlagwörter."
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Spezielle Schlagwörter"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Neueste Schlagwörter"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Verwandte Schlagwörter"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Neueste Suchbegriffe"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "Die Anzahl der Favoriten für diese Anfrage."
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Das Schlagwort umbenennen."
+msgstr[1] "Das Schlagwort umbenennen."
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Das Schlagwort löschen."
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Die Deskription des Schlagwortes editieren."
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Die gemeinsame Deskription des Schlagwortes editieren."
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Eine Verbindung zwischen Schlagwörtern kreieren."
+
+#: data/templates/default/sidebar.block.users.php:14
+#, fuzzy
+msgid "New Users"
+msgstr "Benutzer/innen"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "Favoriten"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "Betrachten Sie sämtliche Benutzer/innen."
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Alle Benutzer/innen"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Aus der Beobachtungsliste löschen."
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Aus der Beobachtungsliste löschen."
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Zur Beobachtungsliste addieren."
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Den Weblink editieren."
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Synonyme:"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Eine neue Verbindung kreieren:"
+
+#: data/templates/default/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 ""
+"Anmerkung: inkludieren Sie ein Schlagwort in das '%s' Schlagwort (z.B.: "
+"%s>Länder), so erscheint jenes Schlagwort in der Navigationsbox."
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Kreieren"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Bereits bestehende Navigationslinks:"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "Leider derzeit keine Weblinks."
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Ja"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Nein"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Den Weblink löschen."
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Einen neuen Weblink kreieren."
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Alt"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "Neu"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Umbenennen"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alphabet"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularität"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "Homepage"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Beobachtungsliste"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Favoriten addieren"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Abmelden"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr "Profil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "erstellt in"
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Leider sind Sie nicht autorisiert diesen Favoriten zu löschen."
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Leider ist das Löschen des Favoriten fehlgeschlagen."
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Alle Schlagwörter"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "Leider wurde der/die Benutzer/in %s nicht gefunden."
+
+#: 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 "Zugriff verweigert."
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+"Die gemeinsame Deskription des Favoriten wurde erfolgreich aktualisiert."
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr "Leider ist die gemeinsame Deskription des Favoriten fehlgeschlagen."
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr "Die gemeinsame Deskription des Favoriten editieren."
+
+#: www/bookmarks.php:114 www/tags.php:47
+#, fuzzy
+msgid "Remove the tag from the selection"
+msgstr "Betrachten Sie sämtliche Schlagwörter dieses/r Benutzers/in."
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Ihr Favorit muss einen Titel und eine URL beinhalten."
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Ihr Favorit wurde erfolgreich gespeichert."
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Leider ist ein Fehler während der Speicherung Ihres Favoriten aufgetreten. "
+"Bitte versuchen Sie es erenut oder kontaktieren Sie den/die Administrator/"
+"in. Herzlichen Dank!"
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Favoriten addieren"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr ""
+"Sie müssen sich zunächst registrieren bzw. anmelden, um einen Favoriten zu "
+"addieren."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Meine Favoriten"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Favoriten editieren"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Der Favorit mit der id %s wurde nicht gefunden."
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Leider sind Sie nicht autorisiert diesen Favoriten zu editieren."
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Ein Fehler ist während der Speicherung Ihres Favoriten aufgetreten."
+
+#: www/history.php:61
+msgid "History"
+msgstr "Verlauf"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "Verlauf von %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Leider konnte die Webadresse nicht gefunden werden."
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Leider konnte die XML Dateneingabe nicht geöffnet werden."
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML - Fehler: %s in Zeile %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Die Favoriten aus del.icio.us importieren."
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Sie haben diesen Favoriten bereits erfolgreich gespeichert."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Die Favoriten wurden erfolgreich importiert."
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Sie haben diesen Favoriten bereits erfolgreich gespeichert."
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "Favoriten"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Die Favoriten wurden erfolgreich importiert."
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Die Favoriten aus einer Favoritendatei importieren."
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+#, fuzzy
+msgid "New links between tags: "
+msgstr "Eine Verbindung zwischen Schlagwörtern löschen"
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Sie haben sich nun erfolgreich abgemeldet."
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Neue Favoriten"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Neue Favoriten"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Speichern, teilen und kategorisieren Sie Ihre Favoriten!"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Alle Favoriten"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Vorhanden."
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "Leider nicht vorhanden."
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr ""
+"Leider sind die von Ihnen eingetragenen Details nicht korrekt. Bitte "
+"versuchen Sie es erneut."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Sie müssen Ihren Benutzernamen eingeben."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Sie müssen Ihre <abbr title=\"electronic mail\">E-Mail</abbr> Adresse "
+"eingeben."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Leider keine Resultate zu diesem Benutzernamen gefunden."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Leider keine Resultate zu diesem Benutzernamen und dieser <abbr title="
+"\"electronic mail\">E-Mail</abbr> Adresse gefunden."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Leider ist ein Fehler bei der Generierung des neuen Passwortes aufgetreten. "
+"Bitte versuchen Sie es erneut."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Ihr neues Passwort lautet:"
+
+#: 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 ""
+"Um den größtmöglichen Schutz Ihrer Favoriten zu gewährleisten, sollten Sie "
+"das Passwort in Ihrem Profil bei Ihrer nächsten Anmeldung editieren."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s Account Information"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Ein neues Passwort wurde erfolgreich generiert und an %s gesandt."
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Vergessenes Passwort"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Leider wurde der Benutzername nicht angegeben."
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Mein Profil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Leider ist das Passwort und deren Verifizierung nicht identisch."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Das Passwort muss mindestens sechs Zeichen lang sein."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Leider ist die E-Mail Adresse nicht gültig."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Leider trat ein Fehler beim Speichern Ihrer Änderungen auf."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Die Änderungen wurden erfolgreich gespeichert."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "Leider ist das Löschen des Schlagwortes fehlgeschlagen."
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Sie müssen einen Benutzernamen, ein Passwort und eine <abbr title="
+"\"electronic mail\">E-Mail</abbr> Addresse eingeben."
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr ""
+"Leider wurde dieser Benutzername bereits reserviert. Bitte treffen Sie eine "
+"andere Wahl."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr ""
+"Leider existiert dieser Benutzername bereits. Bitte treffen Sie eine andere "
+"Wahl."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"Leider wurde dieser Benutzername bereits reserviert. Bitte treffen Sie eine "
+"andere Wahl."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr ""
+"Leider ist diese E-Mail Adresse nicht gültig. Bitte versuchen Sie es erneut."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr ""
+"Leider wurde die Sicherheitsfrage nicht korrekt beantwortet. Bitte versuchen "
+"Sie es erneut."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Ihre Registrierung war erfolgreich!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr ""
+"Leider ist Ihre Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Neueste Favoriten auf %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Suche Favoriten"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Suche Resultate"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Der Weblink für das Schlagwort wurde erfolgreich kreiert."
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Leider wurde der Weblink für das Schlagwort nicht kreiert."
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Eine Verbindung für das Schlagwort addieren"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Der Weblink für das Schlagwort wurde erfolgreich gelöscht."
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "Leider ist das Löschen des Weblinks fehlgeschlagen."
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr "Eine Verbindung zwischen Schlagwörtern löschen"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Einen Weblink zwischen Schlagwörtern editieren."
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+"Die gemeinsame Deskription des Schlagwortes wurde erfolgreich aktualisiert."
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Leider ist die gemeinsame Deskription des Schlagwortes fehlgeschlagen."
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Das Schlagwort wurde erfolgreich gelöscht."
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Leider ist das Löschen des Schlagwortes fehlgeschlagen."
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Die Deskription des Favoriten wurde erfolgreich aktualisiert."
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Leider ist die Deskription des Schlagwortes fehlgeschlagen."
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Das Schlagwort wurde erfolgreich umbenannt."
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Leider ist die Umbenennung des Schlagwortes fehlgeschlagen."
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Benutzer/innen"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Der/die Benutzer/in wurde aus Ihrer Beobachtungsliste entfernt."
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Der/die Benutzer/in wurde zu Ihrer Beobachtungsliste addiert."
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Meine Beobachtungsliste"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "for"
+#~ msgstr "nach"
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid "Last Users"
+#~ msgstr "Neueste Benutzer/innen"
diff --git a/data/locales/de_DE/LC_MESSAGES/messages.mo b/data/locales/de_DE/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..d1ef779
--- /dev/null
+++ b/data/locales/de_DE/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/de_DE/LC_MESSAGES/messages.po b/data/locales/de_DE/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..cba3bb3
--- /dev/null
+++ b/data/locales/de_DE/LC_MESSAGES/messages.po
@@ -0,0 +1,1680 @@
+# Copyright (C) 2008 by Elmar H. Beer
+# This file is distributed under the same license as the SemanticScuttle package.
+# Elmar H. Beer <info@ehb-world.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: SemanticScuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2011-05-11 17:57+0100\n"
+"Last-Translator: Christian Weiske <cweiske@cweiske.de>\n"
+"Language-Team: de-AT <info@ehb-world.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: German\n"
+"X-Poedit-Country: AUSTRIA\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() wurde mehrmals aufgerufen."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL-Fehler"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Zeile"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Datei"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Information"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Kritische Information"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Es ist ein Fehler aufgetreten."
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Allgemeiner Fehler"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Es ist ein kritischer Fehler aufgetreten."
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Kritischer Fehler"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Debugmodus"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Speichern</strong> Sie Ihre sämtlichen Lesezeichen an einem Ort, von "
+"überall zugänglich."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Teilen</strong> Sie Ihre Lesezeichen mit allen anderen, mit Freunden "
+"auf Ihrer Beobachtungsliste oder einfach privat für Sie persönlich."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Kategorisieren</strong> Sie Ihre Lesezeichen mittels unbegrenzten "
+"Schlagwörtern, anstatt Sie in unübersichtlichen Ordnern zu speichern."
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "Registrieren Sie sich jetzt"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "um %s zu nutzen!"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Softwareinformationen"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "ist lizenziert unter der"
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr "Sie können es selbst auf Ihrem Webserver oder Webspace installieren"
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 unterstützt die meisten der <a href=\"http://www.delicious.com/help/api"
+"\">del.icio.us-<abbr title=\"Application Programming Interface\">API</abbr></"
+"a>. Fast alle coolen Tools, die für dieses System erstellt wurden, können "
+"für %1$s geändert und adaptiert werden. Wenn Sie ein Programm finden, bei "
+"dem Sie die API-Adresse nicht ändern können, so fragen Sie doch einfach beim "
+"Entwickler nach. Man kann nie wissen, vielleicht machen sie es einfach."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr "Hinweise"
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Suchplugin zum Browser hinzufügen"
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+"Mit dem geheimen Schlagwort \"system:unfiled\" findet man Lesezeichen ohne "
+"Schlagwörter"
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+"Mit dem geheimen Schlagwort \"system:imported\" findet man importierte "
+"Lesezeichen."
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr "Benutzerverwaltung"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr "Öffentlich/Gemeinsam/Privat"
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "Lesezeichen"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Sind Sie sicher?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Löschen"
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr "Andere Aktionen"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr "Alle URLs überprüfen (Kann eine Weile dauern)"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+"Gemeinsame Beschreibung: Diese Felder können von jedem Benutzer angeschaut "
+"und verändert werden."
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titel"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Beschreibung"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Letzte Änderung:"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Aktualisieren"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Ziehen Sie eines der folgenden Bookmarklets in die Lesezeichen Ihres "
+"Webbrowsers, um jederzeit auf unkomplizierte Art und Weise einen neue "
+"Lesezeichen zu %s hinzuzufügen"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Ziehen Sie eines der folgenden Bookmarklets in die Lesezeichen Ihres "
+"Webbrowsers, um jederzeit auf unkomplizierte Art und Weise einen neue "
+"Lesezeichen zu %s hinzuzufügen"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Lesezeichen zu %s hinzufügen."
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Lesezeichen zu %s hinzufügen (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr "Stimmen: <span class=\"voting\">%d</span>"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr "Dafür"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr "Dagegen"
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr "Lesezeichen auf dieser Seite werden von einem Administrator gepflegt."
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr "Den gemeinsamen Beschreibungstext bearbeiten"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr "Den gemeinsamen Beschreibungstext bearbeiten"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "Privaten Beschreibungstext bearbeiten"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Sortieren nach:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Datum"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr "Stimmen"
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "Lesezeichen anderer Benutzer mit diesem Schlagwort"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "Nur meine Lesezeichen für dieses Schlagwort"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "neueste Lesezeichen"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "vorherige"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "weitere"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "älteste Lesezeichen"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Seite %d von %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr "Schlagwörter:"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Bearbeiten"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr "Letzte Änderung"
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "von"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "dir"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr " und %s1 anderen Person%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " und %2$s%1$s anderer Person%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Dieses Lesezeichen in DEINE Lesezeichen kopieren."
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Kopieren"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr "Dieses Lesezeichen wurde von einem Administrator verifiziert"
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr "Private Notiz zu diesem Lesezeichen"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr "Zurück zum Seitenanfang"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr "Seitenanfang"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Leider sind keine Lesezeichen vorhanden."
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Impressum"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Angetrieben von"
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "Lesezeichen"
+msgstr[1] "Lesezeichen"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Populäre Schlagwörter"
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "Populäre Schlagwörter von allen Benutzern"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Webadresse"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Erforderlich"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Notiz hinzufügen"
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+"Anker können benutzt werden, um Attribute zu begrenzen: [publisher]blah[/"
+"publisher] "
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr "Vorschläge: "
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "Privatnotiz"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr "Nur für dich und deine Kontakte sichtbar."
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Schlagwörter"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "kommagetrennt"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+"Anmerkung: verwenden Sie \">\" um ein Schlagwort in ein hierarchisch höheres "
+"Schlagwort zu integrieren (z.B.: Europa>Frankreich>Paris)"
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+"Anmerkung: verwenden Sie \"=\" um ein Synonym für ein Schlagwort zu "
+"integrieren (z.B.: Frankreich=französisches Land)"
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Status"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Öffentlich"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Beobachtungsliste"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privat"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Lesezeichen löschen"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "Die gemeinsame Beschreibung bearbeiten"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importieren"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Die Lesezeichen aus einer Lesezeichendatei importieren."
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Microsoft Internet Explorer, Mozilla Firefox und Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Lesezeichen von del.icio.us importieren."
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr "SSL-Client-Zertifikate"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr "Seriennummer"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Name"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr "E-Mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr "Herausgeber"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+msgid "delete"
+msgstr "Löschen"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr "Es sind keine Zertifikate registriert."
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr "Dein aktuelles Zertifikat ist bereits registriert."
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr "Aktuelles Zertifikat zum automatischen Einloggen registrieren."
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr "Dein Browser stellt kein Zertifikat zu Verfügung."
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Kontodetails"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Benutzername"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Passwort bestätigen"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-Mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Privatnotiz"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Passwort generieren"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Persönliche Details"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Homepage"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Änderungen speichern"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Aktionen"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "Lesezeichen exportieren"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "HTML-Datei (für Webbrowser)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "XML-Datei (für del.icio.us etc.)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr "CSV-Datei (für Tabellenkalkulationen)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Nicht gefunden!"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "Die angeforderte URL wurde auf diesem Server nicht gefunden"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Allgemeiner Serverfehler"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Die angeforderte URL konnte nicht verarbeitet werden"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "Beobachtungsliste"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instruktionen"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Melden Sie sich <a href=\"http://del.icio.us/api/posts/all\">bei del.icio.us "
+"an</a>, um Ihre Lesezeichen zu exportieren"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Speichern Sie die resultierende <abbr title=\"Extensible Markup Language"
+"\">XML</abbr>-Datei lokal auf Ihrem Computer"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Klicken Sie auf <kbd>Durchsuchen...</kbd> um die bereits gespeicherte "
+"Lesezeichendatei auszuwählen. Die Dateigröße darf maximal 1 MB betragen"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+"Wählen Sie die Standardeinstellung der Privatsphäre für Ihre zu "
+"importierenden Lesezeichen"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Klicken Sie auf <kbd>Importieren</kbd> um die bereits lokal gespeicherte "
+"Lesezeichendatei zu importieren. Dieser Prozess kann bis zu eine Minute "
+"andauern"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Exportieren Sie Ihre Lesezeichen aus dem Webbrowser in eine Datei"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Microsoft Internet Explorer: <kbd>Datei &gt; Importieren und "
+"Exportieren... &gt; Favoriten exportieren"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Lesezeichen &gt; Lesezeichen verwalten... &gt; Datei "
+"&gt; Exportieren..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Lesezeichen &gt; Lesezeichen verwalten... &gt; Werkzeuge &gt; "
+"Exportieren..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Klicken Sie auf <kbd>Durchsuchen...</kbd> um die bereits gespeicherte "
+"Lesezeichendatei auszuwählen. Die Dateigröße darf maximal 1 MB betragen"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+"Erstelle deine eigene Struktur in einer einfachen Textdatei und folge "
+"folgendem Modell:"
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+"Importiere die Datei. Die Schlagwörter und ihre Beziehungen werden deinem "
+"Profil hinzugefügt."
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr "Bitte Cookies aktivieren"
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Passwort"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Zwei Wochen nicht nach meinem Passwort fragen"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Anmelden"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Haben Sie Ihr Passwort vergessen?"
+
+#: data/templates/default/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 ""
+"Sofern Sie Ihr Passwort vergessen haben, können Sie nun durch %s ein neues "
+"Passwort erstellen lassen. Geben Sie zur Verifizierung Ihren Benutzernamen "
+"und Ihre E-Mail Adresse Ihres Accounts ein. In Kürze erhalten Sie ein neues "
+"Passwort. Verwenden Sie dieses Passwort, um sich erfolgreich anzumelden."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Passwort generieren"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Mitglied seit"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Beobachten"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Beobachtet von"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Lesezeichen"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Zu den Lesezeichen"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Registrieren Sie sich hier für ein kostenloses %s Konto. Alle unten "
+"angeforderten Informationen sind für die Registrierung erforderlich"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+" mindestens 5 alphanumerische Zeichen (keine Leerzeichen, keine Punkte oder "
+"andere Sonderzeichen)"
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr " zum Versenden des Passworts"
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "Sicherheitsfrage"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registrieren"
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr "Suche"
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr "in"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "Lesezeichen des Benutzers"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "meine Lesezeichen"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "meine Beobachtungsliste"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "alle Lesezeichen"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Suche"
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Ausgewählte Schlagwörter"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Einen neuen Navigationslink hinzufügen."
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Einen Navigationslink löschen."
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "Schlagwörter, die im Schlagwort '%s' enthalten sind."
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Spezielle Schlagwörter"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "Betrachten Sie all Ihre Schlagwörter."
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "All Ihre Schlagwörter."
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "Betrachten Sie sämtliche Schlagwörter dieses Benutzers."
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "Sämtliche Schlagwörter dieses Benutzers."
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "Betrachten Sie populäre Schlagwörter."
+
+#: data/templates/default/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr "Spezielle Schlagwörter"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+"Dieses Menü ist aus von Administratoren ausgesuchten Schlagwörtern "
+"zusammengestellt"
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Neueste Schlagwörter"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Verwandte Schlagwörter"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Neueste Suchbegriffe"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "Die Anzahl der Favoriten für diese Anfrage"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Das Schlagwort umbenennen."
+msgstr[1] "Die Schlagwörter umbenennen."
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Das Schlagwort löschen."
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Die Schlagwortbeschreibung bearbeiten"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Die gemeinsame Beschreibung des Schlagwortes bearbeiten."
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Eine Verbindung zwischen Schlagwörtern erstellen."
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "Neue Benutzer"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "Lesezeichen"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "Alle Benutzer anzeigen"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Alle Benutzer"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr "Nahe Kontakte sind gemeinsame Kontakte"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr "Nahe Kontakte"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "Kontakt hinzufügen"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "Benutzername eingeben, der den Kontakten hinzugefügt werden soll."
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "Kontakt entfernen"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Aus der Beobachtungsliste löschen"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Zur Beobachtungsliste hinzufügen"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Den Weblink bearbeiten."
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Synonyme:"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Eine neue Verbindung erstellen:"
+
+#: data/templates/default/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 ""
+"Anmerkung: Fügen Sie ein Schlagwort dem '%s' Schlagwort hinzu (z.B.: "
+"%s>Länder), so erscheint dieses Schlagwort in der Navigationsbox."
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Erstellen"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Bereits bestehende Navigationslinks:"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "Leider derzeit keine Weblinks."
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Ja"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Nein"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Den Weblink löschen."
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Einen neuen Weblink erstellen"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Alt"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "Neu"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Umbenennen"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alphabet"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularität"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr "Start"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Beobachtungsliste"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Lesezeichen hinzufügen"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Abmelden"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr "Administration"
+
+#: data/templates/default/top.inc.php:57
+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 ""
+"SemanticScuttle befindet sich im \"Debugmodus\" ($debugMode = true). Um in "
+"den normalen Modus zu wechseln und die Debugmeldungen zu verstecken, einfach "
+"$debugMode in der config.php auf \"false\" setzen."
+
+#: data/templates/default/users.tpl.php:20
+msgid "profile"
+msgstr "Profil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "erstellt in"
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "Benutzer verwalten"
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr "Benutzer %s und alle seine Lesezeichen wurden gelöscht"
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr "Problem mit "
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Sie dürfen dieses Lesezeichen nicht löschen"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Das Löschen des Lesezeichens ist fehlgeschlagen."
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Alle Schlagwörter"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "Der Benutzer %s wurde nicht gefunden."
+
+#: 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 "Zugriff verweigert."
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr "Die gemeinsame Lesezeichenbeschreibung wurde aktualisiert"
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr "Das Ändern der gemeinsamen Lesezeichenbeschreibung ist fehlgeschlagen."
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr "Die gemeinsame Lesezeichenbeschreibung bearbeiten."
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "Schlagwort aus der Auswahl entfernen"
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Ihr Lesezeichen muss einen Titel und eine URL haben."
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr "Diese Lesezeichen-URL darf nicht hinzugefügt werden."
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Ihr Lesezeichen wurde erfolgreich gespeichert."
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr "Zur vorherigen Seite zurück"
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Beim Speichern des Lesezeichens ist ein Fehler aufgetreten. Bitte versuchen "
+"Sie es erneut oder kontaktieren Sie den Administrator."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Lesezeichen hinzufügen"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Sie müssen angemeldet sein, um ein Lesezeichen hinzuzufügen."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Meine Lesezeichen"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Lesezeichen bearbeiten"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Das Lesezeichen mit der ID %s wurde nicht gefunden."
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Sie dürfen dieses Lesezeichen nicht bearbeiten"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Beim Speichern des Lesezeichens ist ein Fehler aufgetreten"
+
+#: www/history.php:61
+msgid "History"
+msgstr "Verlauf"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "Verlauf von %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Die Adresse wurde nicht gefunden."
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Die XML-Daten konnten nicht geöffnet werden."
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML-Fehler: %s in Zeile %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Lesezeichen von del.icio.us importieren"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Sie haben dieses Lesezeichen bereits gespeichert."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Lesezeichen importiert."
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr "Sie haben einige dieser Lesezeichen bereits gespeichert."
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "Lesezeichen gefunden: "
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "Importierte Lesezeichen: "
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Lesezeichen aus Browser-Lesezeichendatei importieren"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr "Falsche Einrückung"
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr "Neue Verbindung zwischen Schlagwörtern: "
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "Struktur importieren"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Sie sind jetzt abgemeldet."
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Neue Lesezeichen"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Neue Lesezeichen"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Speichern, teilen und kategorisieren Sie Ihre Lesezeichen!"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Alle Lesezeichen"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Vorhanden"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "Nicht vorhanden"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr ""
+"Die von Ihnen angegebenen Daten sind nicht korrekt. Bitte versuchen Sie es "
+"erneut."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Sie müssen Ihren Benutzernamen eingeben."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Sie müssen Ihre <abbr title=\"electronic mail\">E-Mail</abbr>adresse "
+"eingeben."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Keine Ergebnisse für diesen Benutzernamen gefunden."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Keine Ergebnisse für diesen Benutzernamen und diese <abbr title=\"electronic "
+"mail\">E-Mail</abbr>adresse gefunden."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Bei der Generierung des neuen Passwortes ist ein Fehler aufgetreten. Bitte "
+"versuchen Sie es erneut."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Ihr neues Passwort lautet:"
+
+#: 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 ""
+"Um den größtmöglichen Schutz Ihrer Lesezeichen zu gewährleisten, sollten Sie "
+"das Passwort in Ihrem Profil bei Ihrer nächsten Anmeldung editieren."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s Zugangsinformationen"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Ein neues Passwort wurde erstellt und an %s gesandt."
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Vergessenes Passwort"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Der Benutzername wurde nicht angegeben."
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Mein Profil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr "Unzulässiges Token"
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Das Passwort und dessen Bestätigung ist nicht identisch."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Das Passwort muss mindestens sechs Zeichen lang sein."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Die E-Mailadresse ist nicht gültig."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Beim Speichern Ihrer Änderungen trat ein Fehler auf."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Die Änderungen wurden gespeichert."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr "Du hast kein gültiges SSL-Client-Zertifikat."
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr "Das Zertifikat ist bereits registriert."
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr "Fehler beim registrieren des SSL-Client-Zertifikats."
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr "SSL-Client-Zertifikat wurde registriert."
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr "Zertifikat nicht gefunden."
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr "Dieses Zertifikat gehört nicht zu dir."
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr "Das Löschen des SSL-Client-Zertifikats ist fehlgeschlagen."
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr "SSL-Client-Zertifikat gelöscht."
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Sie müssen einen Benutzernamen, ein Passwort und eine <abbr title="
+"\"electronic mail\">E-Mail</abbr>addresse eingeben."
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr ""
+"Dieser Benutzername ist bereits reserviert. Bitte treffen Sie eine andere "
+"Wahl."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr ""
+"Dieser Benutzername existiert bereits. Bitte treffen Sie eine andere Wahl."
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"Dieser Benutzername ist unzulässig (zu kurz, zu lang, nicht erlaubte "
+"Zeichen, ...). Bitte treffen Sie eine andere Wahl."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Diese E-Mailadresse ist nicht gültig. Bitte versuchen Sie es erneut."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr ""
+"Die Sicherheitsfrage wurde nicht korrekt beantwortet. Bitte versuchen Sie es "
+"erneut."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Ihre Registrierung war erfolgreich!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Ihre Registrierung ist fehlgeschlagen. Bitte versuchen Sie es erneut."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Neueste Lesezeichen in %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Suche Lesezeichen"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Suche Ergebnisse"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr "Nicht zufriegen? Probier unser "
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Der Weblink für das Schlagwort wurde erfolgreich erstellt."
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Der Weblink für das Schlagwort konnte nicht erstellt werden."
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Eine Verbindung für das Schlagwort hinzufügen"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Der Weblink für das Schlagwort wurde erfolgreich gelöscht."
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "Das Löschen des Weblinks ist fehlgeschlagen."
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr "Eine Verbindung zwischen Schlagwörtern löschen"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Einen Weblink zwischen Schlagwörtern editieren"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+"Die gemeinsame Beschreibung des Schlagwortes wurde erfolgreich aktualisiert."
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+"Ändern der gemeinsame Beschreibung des Schlagwortes ist fehlgeschlagen."
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Das Schlagwort wurde erfolgreich gelöscht."
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Das Löschen des Schlagwortes ist fehlgeschlagen."
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Die Schlagwortbeschreibung wurde aktualisiert."
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Das Aktualisieren der Schlagwortbeschreibung ist fehlgeschlagen"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Das Schlagwort wurde umbenannt."
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Die Umbenennung des Schlagwortes ist fehlgeschlagen."
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Benutzer"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Der Benutzer wurde aus Ihrer Beobachtungsliste entfernt."
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Der Benutzer wurde zu Ihrer Beobachtungsliste hinzugefügt."
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Meine Beobachtungsliste"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr "Die API kann nur nach Authentifizierung benutzt werden."
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr "Zurück zu "
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr "Administratorhinweise: "
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr "Um die Google Custom Search Engine manuell zu aktualsieren, gehe zu "
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+"Wenn keine Ergebnisse gefunden werden, sollten alle URLs im "
+"Administrationsbereich geprüft werden."
+
+#~ msgid "for"
+#~ msgstr "nach"
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid "Last Users"
+#~ msgstr "Neueste Benutzer"
diff --git a/data/locales/dk_DK/LC_MESSAGES/messages.mo b/data/locales/dk_DK/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..94c98ac
--- /dev/null
+++ b/data/locales/dk_DK/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/dk_DK/LC_MESSAGES/messages.po b/data/locales/dk_DK/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..98f5f6d
--- /dev/null
+++ b/data/locales/dk_DK/LC_MESSAGES/messages.po
@@ -0,0 +1,1711 @@
+# Scuttle dk-DK Translation
+# Copyright (C) 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Nicki Brøchner <nicki.brochner@gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-04-16 22:04-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: dk-DK <Nicki.brochner@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Danish\n"
+"X-Poedit-Country: DENMARK\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() blev kaldt flere gange"
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL fejl"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Line"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Filer"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Information"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Kritisk information"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "En fejl opstod"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "General fejl"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "En kritisk fejl opstod"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Kritisk fejl"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "DEBUG MODE"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Gem</strong> alle dine favorit links et sted, tilgængelig fra hvor "
+"som helst"
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"Del dine bogmærker med alle, med vennerne på din overvågningsliste eller "
+"behold dem private."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Marker</strong> dine bogmærker med så mange mærkater som du ønsker, "
+"i stedet for at kæmpe med mapper."
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "Registrere"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Nørd Ting"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 understøtter de fleste <a href=\"http://www.delicious.com/help/api"
+"\">del.icio.us <abbr title=\"Forbindelsesflade imellem forskellige "
+"programmer \">API</abbr></a>. Næsten alle de værktøjer udviklet til det "
+"system kan modificeres til at fungere med %1$s i stedet for. Hvis du finder "
+"et værktøj som ikke tillader at du fortager ændringer i API adresse, kunne "
+"du jo spørge udviklerne. Man ved aldrig det kunne jo være de ville."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Brugernavn"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "bogmærke"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Er du sikker?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Slet"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instruktioner"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titel"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bogmærkelet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Træk en af de følgende bogmærkeletter til din browsers bogmærker, og tryk på "
+"den hver gang du ønsker at tilføje den side du er på til %s"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Træk en af de følgende bogmærkeletter til din browsers bogmærker, og tryk på "
+"den hver gang du ønsker at tilføje den side du er på til %s"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Send til %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Send til %s (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Sortere efter:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Dato"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "Første"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Forrige"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Næste"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "Sidste"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Side %d af %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Mærkater"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Redigere"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "af"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr "og %s1 andre%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " og %2$s%1$s andre%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Importere bogmærker fra bogmærke fil"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Kopi"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Du har ikke tilladelse til at redigere dette bogmærke"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Igen bogmærker tilgængelig"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Om programmet"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "bogmærke"
+msgstr[1] "bogmærker"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Populære mærkater"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Populære mærkater"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Adresse"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Påkrævet"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Private"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Mærkater"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Komma adskillelse"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Privat"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Offentligt"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Del med Overvågningslisten"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Private"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Slet bogmærker"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importere"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importere bogmærker fra bogmærke fil"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox og Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importere bogmærker fra del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Navn"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Slet"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Konto oplysninger"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Brugernavn"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nyt kodeord"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "bekræft kodeord"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Private"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Generere kodeord"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Personlige detaljer"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Hjemmeside"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Gem ændringer"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Aktion"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "mine bogmærker"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Ikke fundet"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "Den forespurgte adresse kunne ikke findes på denne server"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "General server fejl"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Den forespurgte adresse kunne ikke afvikles"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Del med Overvågningslisten"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instruktioner"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Log ind på <a href=\"http://del.icio.us/api/posts/all\">del.icio.us' eksport "
+"side</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Gem <abbr title=\"Extensible Markup Language\">XML</abbr> fillen på din "
+"computer"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Tryk <kbd>Gennemse...</kbd> for at finde denne fil på din computer. "
+"Filstørrelsen må maksimum være på 1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+"Vælg den ønskede standart privatindstilling for dine importerede bogmærker"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Tryk Import for at starte med at importere dine bogmærker; dette kan tage et "
+"par minutter."
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Eksportere dine bogmærker fra din browser til en fil"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>Filer &gt; Importer og eksporter... &gt; Eksporter "
+"foretrukne emner"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Bogmærker &gt; Arranger bogmærker... &gt; Filer &gt; "
+"Eksporter..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bogmærker &gt; Arranger bogmærker... &gt; Værktøjer &gt; "
+"Eksporter..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Tryk <kbd>Gennemse...</kbd> for at finde den gemte bogmærke fil på din "
+"computer. Filstørrelsen må maksimum være på 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Kodeord"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Søg ikke efter mit kodeord i 2 uger"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Log ind"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Glemt dit kodeord?"
+
+#: data/templates/default/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 ""
+"Hvis du har glemte dit kodeord, kan %s generere et nyt et. Skriv "
+"brugernavnet og e-mail adressen på din konto i formularen nedenfor og vi vil "
+"sende dit nye kodeord til dig via e-mail."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Generere kodeord"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Medlem siden"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Overvåger"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Overvågede af"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Bogmærker"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "mine bogmærker"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Tilmeld dig her for at oprette en gratis %s konto. Alle forspurgte "
+"informationerne neden for er påkrævet."
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registrere"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Søg"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "Line"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "denne brugers bogmærker"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "mine bogmærker"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "Min Overvågningsliste"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "alle bogmærkerne"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Søg"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "Lignede mærker"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "Slet"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "Nylig mærker"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "Populære mærkater"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Lignede mærker"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Nylig mærker"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Lignede mærker"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "Søg"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "Igen bogmærker fundet"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Lignede mærker"
+msgstr[1] "Lignede mærker"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "Slet"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "Beskrivelse"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "bogmærke"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Fjern fra Overvågningslisten"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Fjern fra Overvågningslisten"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Tilføj til Overvågningslisten"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "Redigere"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Ja"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Nej"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "Brugernavn"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alfabet"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularitet"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "Hjemmeside"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Overvågningsliste"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Tilføj et bogmærke"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Log ud"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "Profil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Du har ikke tilladelse til at slette dette bogmærke"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Alle mærker"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "Bruger med brugernavn %s kunne ikke findes"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Dit bogmærke skal have både en titel og en adresse"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Bogmærke gemt"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Der opstod en fejl, ved forsøg på at gemme dit bogmærke. Prøv venligst igen "
+"eller kontakt Administrator"
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Tilføj bogmærke"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Du skal være logget ind før du kan tilføje bogmærker"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Mine bogmærker"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Rediger bogmærker"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Bogmærke med id %s kunne ikke findes"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Du har ikke tilladelse til at redigere dette bogmærke"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Der opstod en fejl, ved forsøg på at gemme dit bogmærke."
+
+#: www/history.php:61
+msgid "History"
+msgstr "Historie"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "Historie for %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Adressen kunne ikke findes"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Kunne ikke åbne XML input"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XLM fejl: %s i line %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Importere bogmærker fra del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Du har allerede tilføjet dette bogmærke"
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Bogmærke importeret"
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Du har allerede tilføjet dette bogmærke"
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "Igen bogmærker fundet"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Bogmærke importeret"
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Importere bogmærker fra browser"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Du er nu logget ud"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Seneste bogmærker"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Seneste bogmærker"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Gem, del og mærk dine favorit links"
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "alle bogmærkerne"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Tilgængelig"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "Ikke tilgængelig"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "Detaljerne du har indtastet er forkerte. Prøv venligst igen."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Du skal indtaste dit brugernavn"
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Du skal indtaste din <abbr title=\"Elektronisk post\">E-mail</abbr> adresse."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Søgning gav igen resultat på det brugernavn."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Søgning gav igen resultat på den kombination af brugernavn og <abbr title="
+"\"elektronisk post\">e-mail</abbr> adresse."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Der opstod en fejl under genereringen af dit nye kodeord. Prøv venligst igen."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Dit nye kodeord er:"
+
+#: 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 ""
+"FFor at bevare dine bogmærker sikrere, bør du skifte dette kodeord i din "
+"profil næste gang du logger ind."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s Konto oplysninger"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Nyt kodeord er blevet genereret og sendt til %s"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Glemt kodeord"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Brugernavn var ikke specificerede"
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Min profil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Kodeord og bekræftelse matcher ikke."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Kodeordet skal mindst være på 6 karakterer."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "E-mail adressen er ikke gyldig."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "En fejl opstod ved forsøg på at gemme dine ændringer."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Ændringer gemt."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr "Du <em>skal</em> indtaste et brugernavn, kodeord og e-mail adresse."
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr "Brugernavnet eksistere allerede, vælg venligst et andet."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Brugernavnet eksistere allerede, vælg venligst et andet."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr "Brugernavnet eksistere allerede, vælg venligst et andet."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "E-mail addressen er ikke gyldig. Prøv venligst igen."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "E-mail addressen er ikke gyldig. Prøv venligst igen."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Du er nu succesfuldt registeret. God fornøjelse!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Registreringen fejlede. Prøv venligst igen."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Seneste bogmærker sendt til %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Søge i bogmærker"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Søge resultat"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "Kunne ikke slette bogmærket"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "Brugernavn"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Bruger fjernet fra din overvågningsliste"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Bruger tilføjet til din overvågningsliste "
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Min Overvågningsliste "
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "Bruger med brugernavn %s kunne ikke findes"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "Seneste bogmærker"
+
+#~ msgid "for"
+#~ msgstr "for"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%s Bogmærker"
+
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<strong><a href=\"register.php\">Register nu</a></strong> for at starte "
+#~ "med at bruge %s!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "%s er baseret på <a href=\"http://sourceforge.net/projects/scuttle/\">et "
+#~ "Ã¥bent-kode projekt</a> licenseret under <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. Dette betyder at du kan hoste systemet på din "
+#~ "egen server gratis, lige meget om det er på Internet, et privat netværk "
+#~ "eller bare på din egen computer."
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Elektronisk post\">E-mail</abbr>"
diff --git a/data/locales/en_GB/LC_MESSAGES/messages.mo b/data/locales/en_GB/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..e877f98
--- /dev/null
+++ b/data/locales/en_GB/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/en_GB/LC_MESSAGES/messages.po b/data/locales/en_GB/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..b5bee79
--- /dev/null
+++ b/data/locales/en_GB/LC_MESSAGES/messages.po
@@ -0,0 +1,1642 @@
+# SemanticScuttle en-GB Translation
+# Copyright (C) 2005 - 2009 SemanticScuttle project
+# This file is distributed under the same license as the SemanticScuttle package.
+# Ogee <ogee@ogee.de>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Semantic Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2009-06-01 02:29+0100\n"
+"Last-Translator: Jan Seifert <seif1301@users.sourceforge.net>\n"
+"Language-Team: Ogee <ogee@ogee.de>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-Language: English\n"
+"X-Poedit-Country: UNITED KINGDOM\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() was called multiple times."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL Error"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Line"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "File"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Information"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Critical Information"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "An error has occurred"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "General Error"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "A critical error has occurred"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Critical Error"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "DEBUG MODE"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "Register now"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr " to start using %s!"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Geek Stuff"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "is licensed under the "
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr "you can freely host it on your own web server."
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr "Tips"
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Add search plugin into your browser:"
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr "User management"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr "Public/Shared/Private"
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "bookmark(s)"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Are you sure?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Delete"
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr "Other actions"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr "Check all URLs (may take some time)"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Title"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Description"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Last modification:"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Update"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Cancel"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"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"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"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"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Post to %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Post to %s (pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr "Bookmarks on this page are managed by an admin user."
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr "Edit the common description of this tag"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr "Edit the common description of this bookmark"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "Edit your personal description of this tag"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Sort by:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Date"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "Bookmarks from other users for this tag"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "Only your bookmarks for this tag"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "First"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Previous"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Next"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "Last"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Page %d of %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr "Tags:"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Edit"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr "Last update"
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "by"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "you"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr " and %s1 other%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " and %2$s%1$s others%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Copy this bookmark to YOUR bookmarks."
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copy"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr "This bookmark is certified by an admin user."
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr "Private note on this bookmark"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr "Come back to the top of this page."
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr "Top of the page"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "No bookmarks available"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "About"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Propulsed by "
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "bookmark"
+msgstr[1] "bookmarks"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Popular Tags"
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "Popular tags from all users"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Address"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Required"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Add Note"
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+"You can use anchors to delimite attributes, e.g. [publisher]blah[/publisher] "
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr "Suggested anchors: "
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "Private Note"
+
+#: data/templates/default/editbookmark.tpl.php:78
+#, fuzzy
+msgid "Just visible by you and your contacts."
+msgstr "Just visible by you and your friends."
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Tags"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Comma-separated"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+"Note: use \">\" to include one tag in another. e.g.: europe>britain>edinburgh"
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr "Note: use \"=\" to make synonym two tags. e.g.: britain=united kingdom"
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Privacy"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Public"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Shared with Watchlist"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Private"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Delete Bookmark"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "edit common description"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Import"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Import bookmarks from bookmark file"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox and Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Import bookmarks from del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Name"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr "Email"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Delete"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Account Details"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Username"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "New Password"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Confirm Password"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Private Note"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Generate Password"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Personal Details"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Homepage"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Save Changes"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Actions"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "Export bookmarks"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "HTML file (for browsers)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "XML file (like del.icio.us)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr "CSV file (for spreadsheet tools)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Not Found"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "The requested URL was not found on this server"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "General server error"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "The requested URL could not be processed"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "Shared with Watchlist"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instructions"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Log in to the <a href=\"http://del.icio.us/api/posts/all\">export page at "
+"del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "Select the default privacy setting for your imported bookmarks"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Export your bookmarks from your browser to a file"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+"Create your structure into a simple text file and following this model:"
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Password"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Don't ask for my password for 2 weeks"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Log In"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Forgotten your password?"
+
+#: data/templates/default/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 ""
+"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."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Generate Password"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Member Since"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Watching"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Watched By"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Bookmarks"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Go to bookmarks"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr " to send you your password if you forget it"
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "Antispam question"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Register"
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr "Search..."
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr "in"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "this user's bookmarks"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "my bookmarks"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "my watchlist"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "all bookmarks"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Search"
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Linked Tags"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Add new link"
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Delete link"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "Tags included into the tag '%s'"
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Menu Tags"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "See all your tags"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "all your tags"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "See all tags from this user"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "all tags from this user"
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "See popular tags"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr "Featured Menu Tags"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr "This menu is composed of keywords (tags) organized by admins."
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Recent Tags"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Related Tags"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Last searches"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "Number of bookmarks for this query"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Rename tag"
+msgstr[1] "Rename tags"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Delete tag"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Edit tag description"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Edit Tag common description"
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Create a link to another tag"
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "New users"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "bookmarks"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "See all users"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "All users"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr "Close contacts are mutual contacts"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr "Close contacts"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "Add a contact..."
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "Type a username to add it to your contacts."
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "Remove this contact"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Remove from Watchlist"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Add to Watchlist"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Edit link"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Synonyms:"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Create new link:"
+
+#: data/templates/default/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: include a tag into '%s' tag (e.g. %s>countries) display the tag into "
+"the menu box"
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Create"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Existing links:"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "No links"
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Yes"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "No"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Delete the link"
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Create new link"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Old"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "New"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Rename"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alphabet"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularity"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr "Home"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Watchlist"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profile"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Add a Bookmark"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Log Out"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr "Admin"
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr "Profile"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "created in"
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "Manage users"
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr "%s and all his bookmarks and tags were deleted."
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr "Problem with "
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "You are not allowed to delete this bookmark"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Failed to delete bookmark"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "All Tags"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "User with username %s was not found"
+
+#: 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 denied."
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr "Bookmark common description updated"
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr "Failed to update the bookmark common description"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr "Edit Bookmark common description"
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "Remove the tag from the selection"
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Your bookmark must have a title and an address"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Bookmark saved"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr "(Go back to previous page.)"
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Add Bookmark"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "You must be logged in before you can add bookmarks."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "My Bookmarks"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Edit Bookmark"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Bookmark with id %s was not found"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "You are not allowed to edit this bookmark"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Error while saving your bookmark"
+
+#: www/history.php:61
+msgid "History"
+msgstr "History"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "History for %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Address was not found"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Could not open XML input"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML error: %s at line %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Import bookmarks from del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "You have already submitted this bookmark."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Bookmark imported."
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr "You have already submitted some of these bookmarks."
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "Bookmarks found: "
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "Bookmarks imported: "
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Import Bookmarks from browser file"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr "Bad indentation"
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr "New links between tags: "
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "Import Structure"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "You have now logged out"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Recent bookmarks"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Recent bookmarks"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Store, share and tag your favourite links"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "All Bookmarks"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Available"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "Not available"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "The details you have entered are incorrect. Please try again."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "You must enter your username."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "No matches found for that username."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"There was an error while generating your new password. Please try again."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Your new password is:"
+
+#: 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 ""
+"To keep your bookmarks secure, you should change this password in your "
+"profile the next time you log in."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s Account Information"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "New password generated and sent to %s"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Forgotten Password"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Username was not specified"
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "My Profile"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr "Invalid token"
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Password and confirmation do not match."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Password must be at least 6 characters long."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "E-mail address is not valid."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "An error occurred while saving your changes."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Changes saved."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "Failed to delete the tag"
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr "You <em>must</em> enter a username, password and e-mail address."
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr "This username has been reserved, please make another choice."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "This username already exists, please make another choice."
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "E-mail address is not valid. Please try again."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Antispam answer is not valid. Please try again."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "You have successfully registered. Enjoy!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Registration failed. Please try again."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Recent bookmarks posted to %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Search Bookmarks"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Search Results"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr "Unsatisfied? You can also try our "
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Tag link created"
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Failed to create the link"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Add Tag Link"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Tag link deleted"
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "Failed to delete the link"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr "Delete Link between Tags"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Edit Link between Tags"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr "Tag common description updated"
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Failed to update the tag common description"
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Tag deleted"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Failed to delete the tag"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Tag description updated"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Failed to update the tag description"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Tag renamed"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Failed to rename the tag"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Users"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "User removed from your watchlist"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "User added to your watchlist"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "My Watchlist"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+#, fuzzy
+msgid "Come back to "
+msgstr "(Go back to previous page.)"
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
diff --git a/data/locales/es_ES/LC_MESSAGES/messages.mo b/data/locales/es_ES/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..c72a054
--- /dev/null
+++ b/data/locales/es_ES/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/es_ES/LC_MESSAGES/messages.po b/data/locales/es_ES/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..576391f
--- /dev/null
+++ b/data/locales/es_ES/LC_MESSAGES/messages.po
@@ -0,0 +1,1736 @@
+# Scuttle es-ES Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Francisco Portero <fportero@serbinario.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-03-26 14:28-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: es-ES <fportero@serbinario.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() requerido en varios ocasiones."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Error de SQL"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "L&iacute;nea"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Archivo"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Informaci&oacute;n"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Informaci&oacute;n Critica"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Ha habido un error"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Error General"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Ha habido un error critico"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Error Critico"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "MODO DEPURACION"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Guarde</strong> todos sus enlaces favoritos en un lugar y acceda "
+"desde donde quiera."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Comparte</strong> tus enlaces, solo con los amigos de la lista de "
+"consultados o mant&eacute;ngalos privados.."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Etiquete</strong> sus enlaces con tantas etiquetas como usted desea, "
+"en lugar de pellearse con las carpetas."
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "Registrarse"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Adelante"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 esta basado en la <a href=\"http://www.delicious.com/help/api\"><abbr "
+"title=\"Application Programming Interface\">API</abbr></a> de <a href="
+"\"http://www.delicious.com/help/api\">del.icio.us <abbr title=\"Application "
+"Programming Interface\">API</abbr></a>. La mayor&iacute;a de las "
+"herramientas utilizadas para construir este sistema se pueden modificar para "
+"trabajar con %1$s. Si usted encuentra una herramienta en la que no puede "
+"modificar la direccion de la API, pregunte al creador si puede cambiar esta "
+"configuraci&oacute;n. Nunca se sabe, puede que ya se haya cambiado esa "
+"configuraci&oacute;n."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Usuario"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "enlace"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Esta usted seguro?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Borrar"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instrucciones"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "T&iacute;tulo"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Descripci&oacute;n"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Marcadores"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Arrastre los enlaces a los marcadores de su navegador y pulselos siempre que "
+"usted desee agregar la pagina que esta viendo a %s"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Arrastre los enlaces a los marcadores de su navegador y pulselos siempre que "
+"usted desee agregar la pagina que esta viendo a %s"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Agregar a %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Agregar a %s (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Por:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Fecha"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "Primera"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Anterior"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Siguiente"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "&Uacute;ltima"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "P&aacute;gina %d de %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Etiquetas"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Editar"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "por"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, fuzzy, php-format
+msgid " and %s1 other%s"
+msgstr " y %s mas"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " y %s mas"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Improtar enlaces de un archivo"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copiar"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Usted no tiene permisos para editar este enlace"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "No hay enlaces disponibles"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Acerca de"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+#, fuzzy
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "enlace"
+msgstr[1] "enlaces"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "M&aacute;s visitadas"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "M&aacute;s visitadas"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Direcci&oacute;n"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Requerido"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Privado"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Etiquetas"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Con comas"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Permisos"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "P&uacute;blico"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Compartido en Consultados"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privado"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Borrar enlace"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importar"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Improtar enlaces de un archivo"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox y Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importar los enlaces de del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Nombre"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "Correo"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Borrar"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Detalles de usuario"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Usuario"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nueva clave"
+
+#: data/templates/default/editprofile.tpl.php:22
+#, fuzzy
+msgid "Confirm Password"
+msgstr "Confirmar la clave"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "Correo"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Privado"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Nueva clave"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Datos del usuario"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "P&aacute;gina web"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Guardar Cambios"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "Instrucciones"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "mis enlaces"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "No funciona"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "No se encontr&oacute; la URL solicitada"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Error general del servidor"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "La URL solicitada no puede ser encontrada"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Compartido en Consultados"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instrucciones"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Registrese para <a href=\"http://del.icio.us/api/posts/all\">exportar la "
+"pagina a del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Guardar los resultados en un archivo <abbr title=\"Extensible Markup Language"
+"\">XML</abbr> de su ordenador"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Pulse <kbd>Examinar...</kbd> para buscar el archivo en su ordenador. El "
+"tamaño maximo no puede ser superior a 1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "Elija los permisos para los enlaces importados"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Pulse <kbd>Importar</kbd> para comenzar a importar los enlaces, esto puede "
+"tartar algunos minutos"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Expote sus enlaces a su navegador a un archivo"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>Archivo &gt; Importar y Exportar... &gt; Exportar "
+"Favoritos"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Enlaces &gt; Manejar Enlaces... &gt; Archivo &gt; "
+"Exportar..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Enlaces &gt; Manejar Enlaces... &gt; Utilidades &gt; "
+"Exportar..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Pulse <kbd>Examinar...</kbd> para buscar los enlaces guardados en un archivo "
+"de us ordenador. El tamaño maximo no puede ser superior a 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Clave"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Recordar mi clave 2 semanas"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Acceder"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Olvido su clave?"
+
+#: data/templates/default/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 olvido su clave, %s puede crearle una nueva. Introduzca su direccion de "
+"correo electr&oacute;nico de su perfil en el formulario siguiente y se le "
+"enviara una nueva clave"
+
+#: data/templates/default/password.tpl.php:19
+#, fuzzy
+msgid "Generate Password"
+msgstr "Nueva clave"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Miembro desde"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+#, fuzzy
+msgid "Watching"
+msgstr "Consultados"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Consultado de"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+#, fuzzy
+msgid "Bookmarks"
+msgstr "Enlaces"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "mis enlaces"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Inserte los datos para crear una cuenta gratis en %s. Es cesaria toda la "
+"informacion requerida"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registrarse"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Buscar"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "L&iacute;nea"
+
+#: data/templates/default/search.menu.php:34
+#, fuzzy
+msgid "this user's bookmarks"
+msgstr "enlaces de %s"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "mis enlaces"
+
+#: data/templates/default/search.menu.php:40
+#, fuzzy
+msgid "my watchlist"
+msgstr "mis consultados"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "todos"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Buscar"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "Etiquetas mencionadas"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "Borrar"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "Recientes"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "M&aacute;s visitadas"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Etiquetas mencionadas"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Recientes"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Etiquetas mencionadas"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "Buscar"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "No se han encontrado enlaces"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Etiquetas mencionadas"
+msgstr[1] "Etiquetas mencionadas"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "Borrar"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "Descripci&oacute;n"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "enlace"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Borrar de Consultados"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Borrar de Consultados"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Agregar a Consultados"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "Editar"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Si"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "No"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "Usuario"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alfab&eacute;tico"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+#, fuzzy
+msgid "Popularity"
+msgstr "Mas visitadas"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "P&aacute;gina web"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+#, fuzzy
+msgid "Watchlist"
+msgstr "Consultados"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "P&eacute;rfil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Agregar"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Salir"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "P&eacute;rfil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "No puede borrar estos enlaces"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Error al borrar los enlaces"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Todas"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, fuzzy, php-format
+msgid "User with username %s was not found"
+msgstr "No se encontro el usuario %s"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "Fallo al borrar el enlace"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Su enlace debe tener un t&iacute;tulo y una direcci&oacute;n"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Enlace salvado"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Hay un error al salvar su enlace. Por favor intentelo de nuevo o contacte "
+"con el administrador."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Agregar Enlace"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr ""
+"Debe introducir su usuario y contrase&ntilde;a para poder agregar enlaces"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Mis Enlaces"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Editar enlace"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "El enlace con id %s no funciona"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Usted no tiene permisos para editar este enlace"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Se ha producido un error al salvar su enlace"
+
+#: www/history.php:61
+msgid "History"
+msgstr "Historial"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "Historial de %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "La direcci&oacute;n no existe"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "No se pude abrir la entrada XML"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "Errores: %s en la linea %d del XML"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Importar enlaces de del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Usted ya ha agregado este enlace"
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Enlaces importados."
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Usted ya ha agregado este enlace"
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "No se han encontrado enlaces"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Enlaces importados."
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Importar enlaces de un Navegador"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Usted esta ahora desconectado"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Enlaces recientes"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Enlaces recientes"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Almacene, navege y etiquete sus enlaces favoritos"
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "todos"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Disponible"
+
+#: www/jsScuttle.php:74
+#, fuzzy
+msgid "Not Available"
+msgstr "No hay enlaces disponibles"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "Los datos introducidos son incorrectos. Por favor intentelo de nuevo."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Introduzca su nombre de usuario"
+
+#: www/password.php:40
+#, fuzzy
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Debe <em>especificar</em> usuario, clave, nombre y <abbr title=\"electronic "
+"mail\">correo electr&oacute;nico</abbr>."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "No se encuentra ning&uacute;n usuario con ese nombre"
+
+#: www/password.php:51
+#, fuzzy
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Debe <em>especificar</em> usuario, clave, nombre y <abbr title=\"electronic "
+"mail\">correolectr&oacute;nico</abbr>."
+
+#: www/password.php:59
+#, fuzzy
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Hay un error al salvar su enlace. Por favor intentelo de nuevo o contacte "
+"con el administrador."
+
+#: www/password.php:63
+#, fuzzy
+msgid "Your new password is:"
+msgstr "Confirmar la nueva clave"
+
+#: 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 ""
+"Para que sean seguros sus enlaces, debe cambiar la clave de su usuario en el "
+"siguiente acceso"
+
+#: www/password.php:66
+#, fuzzy, php-format
+msgid "%s Account Information"
+msgstr "Informaci&oacute;n"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Se ha creado una nueva clave y se ha enviado a %s"
+
+#: www/password.php:75
+#, fuzzy
+msgid "Forgotten Password"
+msgstr "Nueva clave"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Usuario no introducido"
+
+#: www/profile.php:70
+#, fuzzy
+msgid "My Profile"
+msgstr "Mi P&eacute;rfil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "La clave y su confirmaci&oacute;n no es correcta."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "La clave debe tener al menos 6 acracteres."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Correo incorrecto."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Ha habido un error al guardar los cambios."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Cambios guardados."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+#, fuzzy
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Debe <em>especificar</em> usuario, clave, nombre y <abbr title=\"electronic "
+"mail\">correo electr&oacute;nico</abbr>."
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr "Este usuario ya existe, por favor introduzca otro."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Este usuario ya existe, por favor introduzca otro."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr "Este usuario ya existe, por favor introduzca otro."
+
+#: www/register.php:68
+#, fuzzy
+msgid "E-mail address is not valid. Please try again."
+msgstr "Correo incorrecto."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Correo incorrecto."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "El registro ha sido correcto. Puede continuar!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Registro erroneo. Por favor intentelo de nuevo."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, fuzzy, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Enlaces recientes agregados a %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Buscar Enlaces"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Buscar Resultados"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "Fallo al borrar el enlace"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "Fallo al borrar el enlace"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "Fallo al borrar el enlace"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "Fallo al borrar el enlace"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "Fallo al borrar el enlace"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "Usuario"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Usuario borrado de consultados"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Usuario agregado a consultados"
+
+#: www/watchlist.php:104
+#, fuzzy
+msgid "My Watchlist"
+msgstr "Mis Consultados"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "No se encontro el usuario %s"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "Enlaces Recientes"
+
+#~ msgid "for"
+#~ msgstr "por"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "Enlace de %s"
+
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<strong><a href=\"register.php\">Registrese ahora</a></strong> para usar "
+#~ "%s!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "%s esta basado en <a href=\"http://sourceforge.net/projects/scuttle/\">un "
+#~ "proyecto de codigo abierto</a> licenciado bajo <a href=\"http://www.gnu."
+#~ "org/copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. Esto quiere decir que usted puede instalarlo "
+#~ "en su propio web server para uso libre, en una red privada o en su propia "
+#~ "computadora."
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Electronic mail\">Correo</abbr>"
diff --git a/data/locales/fr_CA/LC_MESSAGES/messages.mo b/data/locales/fr_CA/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..cd57b64
--- /dev/null
+++ b/data/locales/fr_CA/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/fr_CA/LC_MESSAGES/messages.po b/data/locales/fr_CA/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..ff8beef
--- /dev/null
+++ b/data/locales/fr_CA/LC_MESSAGES/messages.po
@@ -0,0 +1,1720 @@
+# Scuttle fr-FR Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# <toony.sf@chezouam.net>
+# nitram <nitram@no-log.org>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2008-04-24 19:07+0100\n"
+"Last-Translator: BenjaminHKB <benjamin.huynh-kim-bang@loria.fr>\n"
+"Language-Team: fr-FR <toony.sf@chezouam.net>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: French\n"
+"X-Poedit-Country: FRANCE\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() was called multiple times. ?"
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Erreur SQL"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Ligne"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Fichier"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Information"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Information critique."
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Une erreur s'est produite."
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Erreur générale."
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Une erreur critique s'est produite."
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Erreur critique."
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Mode de débogage."
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Conservez</strong> tous vos signets au même endroit, accessibles de "
+"partout. "
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Partagez</strong> vos signets avec tout le monde, avec les "
+"utilisateurs autorisés ou gardez-les pour vous."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Marquez</strong> vos signets avec autant de labels que vous le "
+"souhaitez au lieu de les hiérarchiser avec des dossiers."
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "S'enregistrer maintenant"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "pour commencer à employer %s !"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Pour les Geeks"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "est sous licence"
+
+#: data/templates/default/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"
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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'<a href=\"http://www.delicious.com/help/api"
+"\"><abbr title=\"Application Programming Interface\">API</abbr> del.icio.us</"
+"a>."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr "Astuces"
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Ajoutez le module de recherche à votre navigateur web:"
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+"Le mot-clé caché <i>system:unfiled</i> vous permet de repérerles signets "
+"sans mot-clé"
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+"Le mot-clé caché <i>system:imported</i> vous permet de repérerles signets "
+"importés"
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Nom d'utilisateur"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "signet(s)"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Etes-vous sûr ?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Supprimer"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instructions"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titre"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Description"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Dernière modification :"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Mettre à jour"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Annuler"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 "
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Ajouter à %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Ajouter à %s (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+#, fuzzy
+msgid "Edit the common description of this tag"
+msgstr "Modifier la description public de ce mot-cl&eacute;"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+#, fuzzy
+msgid "Edit the common description of this bookmark"
+msgstr "éditer la description commune"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "Modifier la description priv&eacute;e de ce mot-cl&eacute;"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Classer par :"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Date"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "Signets des autres utilisateurs pour ce mot-cl&eacute;"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "Uniquement vos signets pour ce mot-cl&eacute;"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "Première"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Précédent"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Suivant"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "Dernière"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Page %d de %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Mots-clés"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Editer"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "par"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "vous"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, fuzzy, php-format
+msgid " and %s1 other%s"
+msgstr " et les autres %s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " et les autres %s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Importer les signets depuis un fichier"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copier"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Vous n'êtes pas autorisé à éditer ce signet."
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr "Revenir en haut de la page"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Pas de signets disponibles."
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "À propos"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Propulsé par"
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "signet"
+msgstr[1] "Signets"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Mots-cl&eacute;s populaires"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Mots-cl&eacute;s populaires"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Adresse"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Requis"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Ajouter un commentaire"
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+"Vous pouvez utilisez des ancres pour spécifiez certains champs. Par exemble: "
+"[publisher]blah[/publisher¸] "
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr "Ancres suggéres: "
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Privée"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Mots-clés"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Séparés par des virgules"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/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&eacute; dans un autre. ex: "
+"europe>france>paris"
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+"Note : utiliser \"=\" pour rendre deux mots-cl&eacute;s synonymes ex: "
+"europe=eu"
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Vision"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Publique"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Partagé avec liste d'accès"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privée"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Supprimer le signet"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "éditer la description commune"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importer"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importer les signets depuis un fichier"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox et Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importer les signets depuis del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr "Certificats SSL"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Nom"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Supprimer"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr "Aucun certificat enregistré"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr "Votre navigateur n'a pas octroyé de certificat"
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Détail du compte"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nouveau mot de passe"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Confirmer le mot de passe"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+msgid "Private RSS Feed"
+msgstr "Fil RSS privé"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr "Activer"
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Générer une nouvelle clé"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Détails personnels"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Page personnelle"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Enregistrer les modifications"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "Instructions"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "Exporter les signets"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "Fichier HTML (pour navigateurs)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "Fichier XML (comme del.icio.us)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr "Fichier CSV (pour les classeurs)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Non trouvé"
+
+#: data/templates/default/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."
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Erreur généralisée du serveur."
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "L'URL demandée n'a pas été trouvée."
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Partagé avec liste d'accès"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instructions"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Se connecter à la <a href=\"http://del.icio.us/api/posts/all\">page d'export "
+"de del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Enregistrer le fichier <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"résultant sur votre ordinateur"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Cliquez sur <kbd>Parcourir...</kbd> pour trouver le fichier sur votre "
+"ordinateur. La taille maximale du fichier ne peut excèder 1Mo"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/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"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Cliquez sur <kbd>Importer</kbd> pour débuter l'import des signets; cette "
+"opération peut prendre quelques minutes"
+
+#: data/templates/default/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/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>Ficher &gt; Importer et Exporter... &gt; Exporter "
+"les favoris"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Marques-pages &gt; Gérer les marques-pages... &gt; "
+"Fichier &gt; Exporter..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Marques-pages &gt; Gérer les marques-pages... &gt; Outils "
+"&gt; Exporter..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Cliquez sur <kbd>Parcourir...</kbd> pour trouver le fichier sur votre "
+"ordinateur. La taille maximale du fichier ne peut excèder 1Mo"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Mot de passe"
+
+#: data/templates/default/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"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Se connecter"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Avez-vous oublié votre mot de passe ?"
+
+#: data/templates/default/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."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Générer un mot de passe"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Membre depuis"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+#, fuzzy
+msgid "Watching"
+msgstr "Éléments surveillés"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+#, fuzzy
+msgid "Watched By"
+msgstr "Surveillé par"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Signets"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Aller aux signets"
+
+#: data/templates/default/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."
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "Question antispam"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "S'enregistrer"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Chercher..."
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr "dans"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "les signets de cet utilisateur"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "mes signets"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "ma liste de suivi"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "tous les signets"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Chercher"
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Mots-cl&eacute;s structurés"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Créer un lien"
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Supprimer un lien"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "Mots-cl&eacute;s inclus dans le mot-cl&eacute; '%s'"
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Mots-cl&eacute;s Menu"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "Voir tous vos mots-cl&eacute;s"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "Tous vos mots-cl&eacute;s"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "Voir tous les mots-cl&eacute;s de cet utilisateur"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "tous les mots-cl&eacute;s de cet utilisateur"
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "Voir tous vos mots-cl&eacute;s"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Menu de mot-clé"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Mots-cl&eacute;s récents"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Mots-cl&eacute;s en relation"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Dernières recherches"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "Nombre de signets pour cette recherche"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Renommer le mot-cl&eacute;"
+msgstr[1] "Renommer les mots-cl&eacute;s"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Supprimer le mot-cl&eacute;"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Editer la description du mot-cl&eacute;"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Editer la description commune du mot-cl&eacute;"
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Créer un lien vers un autre mot-cl&eacute;"
+
+#: data/templates/default/sidebar.block.users.php:14
+#, fuzzy
+msgid "New Users"
+msgstr "Nouveaux utilisateurs"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "signets"
+
+#: data/templates/default/sidebar.block.users.php:30
+#, fuzzy
+msgid "See all users"
+msgstr "Voir tous vos mots-cl&eacute;s"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Tous les utilisateurs"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Enlever de la liste des consultés"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Enlever de la liste des consultés"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Ajouter à la liste des consultés"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Editer un lien"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Synonymes :"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Créer un nouveau lien"
+
+#: data/templates/default/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&eacute; dans le mot-cl&eacute; '%s' (e.g. "
+"%s>countries) affiche ce mot-cl&eacute; dans la boîte de menu"
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Créer"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Liens existants :"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "Pas de liens"
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Oui"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Non"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Supprimer le lien"
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Créer un nouveau lien"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Ancien"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "Nouveau"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Renommer"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alphabet"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularité"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "Accueil"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "À surveiller"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Ajouter un signet"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Quitter"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr "Profil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "Créé en "
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+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&eacute;s"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 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:114 www/tags.php:47
+#, fuzzy
+msgid "Remove the tag from the selection"
+msgstr "Voir tous les mots-cl&eacute;s de cet utilisateur"
+
+#: www/bookmarks.php:134 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:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Signet enregistré."
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 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:222
+msgid "Add Bookmark"
+msgstr "Ajouter signet"
+
+#: www/bookmarks.php:225
+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:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+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&eacute;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:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Signets récents"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Signets récents"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Conservez, partagez et libellez vos liens favoris"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Tous les signets"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Disponible"
+
+#: www/jsScuttle.php:74
+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 <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Vous <em>devez</em> saisir une <abbr title=\"adresse électronique\">E-mail</"
+"abbr>."
+
+#: 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 <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Nous n'avons rien trouvé pour cette combinaison de nom d'utilisateur et "
+"d'<abbr title=\"adresse mail\">e-mail</abbr>."
+
+#: 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:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Le nom d'utilisateur n'a pas été spécifié."
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Mon Profil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Le mot de passe et sa vérification ne correspondent pas."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Le mot de passe doit avoir au moins 6 caractères."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Adresse de courrier électronique invalide."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr ""
+"Une erreur s'est produite pendant l'enregistrement de vos modifications."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Modifications enregistrées."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "Impossible d'effacer le mot-cl&eacute;"
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Vous <em>devez</em> saisir un nom d'utilisateur, un mot de passe, un nom et "
+"un <abbr title=\"adresse électronique\">e-mail</abbr>"
+
+#: www/register.php:56
+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:60
+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:64
+#, 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:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Adresse de courrier électronique invalide. Veuilez réessayer."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "La réponse antispam n'est pas valide. Veuillez réessayer."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Votre inscription a bien été prise en compte !"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Enregistrement raté. Veuillez rééssayer."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Signets ajoutés récemment à %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Recherche de signets"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Résultats de recherche"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Lien entre mot-cl&eacute; créé."
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Impossible de créer le lien"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Ajout d'un lien entre mots-cl&eacute;s"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Effacement d'un lien entre mots-cl&eacute;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&eacute;s"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Editer un lien entre mots-cl&eacute;s"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr "Editer la description commune du mot-cl&eacute;"
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Impossible de mettre à jour la description commune du mot-cl&eacute;"
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Mot-cl&eacute; effacé"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Impossible d'effacer le mot-cl&eacute;"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Description du mot-cl&eacute; mise à jour"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Impossible de mettre à jour la description du mot-cl&eacute;"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Mot-cl&eacute; renommé"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Erreur dans le renommage du mot-cl&eacute;"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: 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
+msgid "My Watchlist"
+msgstr "Mes signets surveillés"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+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 ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<a href=\"register.php\">Enregistrez-vous maintenant</a> pour poster vos "
+#~ "propres signets sur %s !"
+
+#~ msgid ""
+#~ "<a href=\"http://sourceforge.net/projects/semanticscuttle/\">Semantic "
+#~ "Scuttle</a> is licensed under the <a href=\"http://www.gnu.org/copyleft/"
+#~ "gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> General Public "
+#~ "License</a> (you can host it on your own web server)."
+#~ msgstr ""
+#~ "<a href=\"http://sourceforge.net/projects/semanticscuttle/\">Semantic "
+#~ "Scuttle</a>, sous license <a href=\"http://www.gnu.org/copyleft/gpl.html"
+#~ "\"><acronym title=\"GNU's Not Unix\">GNU</acronym> General Public "
+#~ "License</a> (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 "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Adresse électronique\">E-mail</abbr>"
diff --git a/data/locales/fr_FR/LC_MESSAGES/messages.mo b/data/locales/fr_FR/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..e0ea52a
--- /dev/null
+++ b/data/locales/fr_FR/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/fr_FR/LC_MESSAGES/messages.po b/data/locales/fr_FR/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..ab2286f
--- /dev/null
+++ b/data/locales/fr_FR/LC_MESSAGES/messages.po
@@ -0,0 +1,1708 @@
+# Scuttle fr-FR Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# <toony.sf@chezouam.net>
+# nitram <nitram@no-log.org>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2009-11-16 21:14+0100\n"
+"Last-Translator: BenjaminHKB <benjamin.huynh-kim-bang@loria.fr>\n"
+"Language-Team: fr-FR <toony.sf@chezouam.net>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: French\n"
+"X-Poedit-Country: FRANCE\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() was called multiple times. ?"
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Erreur SQL"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Ligne"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Fichier"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Information"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Information critique."
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Une erreur s'est produite."
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Erreur générale."
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Une erreur critique s'est produite."
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Erreur critique."
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Mode de débogage."
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Conservez</strong> tous vos signets au même endroit, accessibles de "
+"partout. "
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Partagez</strong> vos signets avec tout le monde, avec vos contacts "
+"ou gardez-les pour vous."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Taggez</strong> vos signets avec autant de labels que vous le "
+"souhaitez au lieu de les hiérarchiser avec des dossiers."
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "S'enregistrer maintenant"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "pour commencer à employer %s !"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Pour les Geeks"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "est sous licence"
+
+#: data/templates/default/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"
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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'<a href=\"http://www.delicious.com/help/api"
+"\"><abbr title=\"Application Programming Interface\">API</abbr> del.icio.us</"
+"a>."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr "Astuces"
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Ajouter un plugin de recherche à votre navigateur :"
+
+#: data/templates/default/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."
+
+#: data/templates/default/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."
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr "Gestion des utilisateurs"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr "Publique/Partagé/Privé"
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "signet(s)"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Etes-vous sûr ?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Supprimer"
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr "Autres actions"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr "Vérifier toutes les URLs (Peut prendre du temps)"
+
+#: data/templates/default/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."
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titre"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Description"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Dernière modification :"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Mettre à jour"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Annuler"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 "
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Ajouter à %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Ajouter à %s (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr "Vote <span class=\"voting\">%d</span>"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr "Vote pour"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr "Vote contre"
+
+#: data/templates/default/bookmarks.tpl.php:43
+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/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr "Editer la description commune de ce tag"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr "Editer la description commune de ce signet"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "Editer votre description personnelle de ce tag"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Classer par :"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Date"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr "Vote"
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "Signets des autres utilisateurs pour ce tag"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "Uniquement vos signets pour ce tag"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "Première"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Précédent"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Suivant"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "Dernière"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Page %d de %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr "Tags:"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Editer"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr "Date de dernière mise à jour"
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "par"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "vous"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr " et %s1 autre%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " et %2$s%1$s autres%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Copier ce signet dans VOS signets."
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copier"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr "Ce signet est certifié par un administrateur."
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr "Note privée sur ce signet"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr "Revenir en haut de cette page."
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr "Haut de page"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Pas de signets disponibles."
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "À propos"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Propulsé par "
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "signet"
+msgstr[1] "Signets"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Tags populaires"
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "Tags populaires pour tous les utilisateurs"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Adresse"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Requis"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Ajouter une note"
+
+#: data/templates/default/editbookmark.tpl.php:61
+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/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr "Balises suggérées : "
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "Note privée"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr "Visible uniquement par vous et vos contacts."
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Tags"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Séparés par des virgules"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/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/default/editbookmark.tpl.php:94
+#: data/templates/default/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/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Accès"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Publique"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Partagé avec mes contacts"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privée"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Supprimer le signet"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "éditer la description commune"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importer"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importer les signets depuis un fichier"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox et Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importer les signets depuis del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Nom"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Supprimer"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Détail du compte"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nouveau mot de passe"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Confirmer le mot de passe"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Note privée"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Générer un mot de passe"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Détails personnels"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Page personnelle"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Enregistrer les modifications"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Actions"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "Exporter les signets"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "Fichier HTML (pour navigateurs)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "Fichier XML (comme del.icio.us)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr "Fichier CSV (pour tableurs)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Non trouvé"
+
+#: data/templates/default/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."
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Erreur généralisée du serveur."
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "L'URL demandée n'a pas été trouvée."
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "Partagé avec mes contacts"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instructions"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Se connecter à la <a href=\"http://del.icio.us/api/posts/all\">page d'export "
+"de del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Enregistrer le fichier <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"résultant sur votre ordinateur"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Cliquez sur <kbd>Parcourir...</kbd> pour trouver le fichier sur votre "
+"ordinateur. La taille maximale du fichier ne peut excèder 1Mo"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/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"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Cliquez sur <kbd>Importer</kbd> pour débuter l'import des signets; cette "
+"opération peut prendre quelques minutes"
+
+#: data/templates/default/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/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>Ficher &gt; Importer et Exporter... &gt; Exporter "
+"les favoris"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Marques-pages &gt; Gérer les marques-pages... &gt; "
+"Fichier &gt; Exporter..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Marques-pages &gt; Gérer les marques-pages... &gt; Outils "
+"&gt; Exporter..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Cliquez sur <kbd>Parcourir...</kbd> pour trouver le fichier sur votre "
+"ordinateur. La taille maximale du fichier ne peut excèder 1Mo"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr "SVP activez les cookies"
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Mot de passe"
+
+#: data/templates/default/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"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Se connecter"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Avez-vous oublié votre mot de passe ?"
+
+#: data/templates/default/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."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Générer un mot de passe"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Membre depuis"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Mes contacts"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Dans les contacts de"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Signets"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Aller aux signets"
+
+#: data/templates/default/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."
+
+#: data/templates/default/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)"
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr " pour vous envoyer votre mot de passe en cas de perte"
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "Question antispam"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "S'enregistrer"
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr "Chercher..."
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr "dans"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "les signets de cet utilisateur"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "mes signets"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "mes contacts"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "tous les signets"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Chercher"
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Tags structurés"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Créer un lien"
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Supprimer un lien"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "Tags inclus dans le tag '%s'"
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Tags Menu"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "Voir tous vos tags"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "Tous vos tags"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "Voir tous les tags de cet utilisateur"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "tous les tags de cet utilisateur"
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "Voir les tags populaires"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr "Tags Principaux"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+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."
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Tags récents"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Tags en relation"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Dernières recherches"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "Nombre de signets pour cette recherche"
+
+#: data/templates/default/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/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Supprimer le tag"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Editer la description du tag"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Editer la description commune du tag"
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Créer un lien vers un autre tag"
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "Nouveaux Utilisateurs"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "signets"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "Voir tous les utilisateurs"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Tous les utilisateurs"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr "Les contacts proches sont des contacts mutuels"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr "Contacts proches"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "Ajouter un contact..."
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "Entrer un nom d'utilisateur pour l'ajouter à vos contacts."
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "Enlever ce contact"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Enlever des contacts"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Ajouter aux contacts"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Editer un lien"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Synonymes :"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Créer un nouveau lien"
+
+#: data/templates/default/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"
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Créer"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Liens existants :"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "Pas de liens"
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Oui"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Non"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Supprimer le lien"
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Créer un nouveau lien"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Ancien"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "Nouveau"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Renommer"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alphabet"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularité"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr "Accueil"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Contacts"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Ajouter un signet"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Quitter"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr "Admin"
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr "Profil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "Créé en "
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "Gérer les utilisateurs"
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr "%s, ses signets et ses tags ont été effacés."
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr "Problème avec "
+
+#: 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 tags"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 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:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "Retirer le tag de la sélection"
+
+#: www/bookmarks.php:134 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:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Signet enregistré."
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr "(Revenir à la page précédente.)"
+
+#: www/bookmarks.php:165 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:222
+msgid "Add Bookmark"
+msgstr "Ajouter signet"
+
+#: www/bookmarks.php:225
+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:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+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
+msgid "You have already submitted some of these bookmarks."
+msgstr "Vous avez déjà enregistré certains de ces signets."
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "Signets trouvés :"
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "Signets importés :"
+
+#: 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 "Mauvaise indentation"
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr "Nouveau lien entre tags :"
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "Importer une structure"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Vous êtes maintenant déconnecté."
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Signets récents"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Signets récents"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Conservez, partagez et taggez vos liens favoris"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Tous les signets"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Disponible"
+
+#: www/jsScuttle.php:74
+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 <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Vous <em>devez</em> saisir une <abbr title=\"adresse électronique\">E-mail</"
+"abbr>."
+
+#: 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 <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Pas d'entrée pour ce nom d'utilisateur et cet <abbr title=\"adresse mail\">e-"
+"mail</abbr>."
+
+#: 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:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Le nom d'utilisateur n'a pas été spécifié."
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Mon Profil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr "Token invalide"
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Le mot de passe et sa vérification ne correspondent pas."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Le mot de passe doit avoir au moins 6 caractères."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Adresse de courrier électronique invalide."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr ""
+"Une erreur s'est produite pendant l'enregistrement de vos modifications."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Modifications enregistrées."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "Impossible d'effacer le tag"
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Vous <em>devez</em> saisir un nom d'utilisateur, un mot de passe, un nom et "
+"un <abbr title=\"adresse électronique\">e-mail</abbr>"
+
+#: www/register.php:56
+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:60
+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:64
+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:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Adresse de courrier électronique invalide. Veuillez réessayer."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "La réponse antispam n'est pas valide. Veuillez réessayer."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Votre inscription a bien été prise en compte !"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Enregistrement raté. Veuillez rééssayer."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Signets ajoutés récemment à %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Recherche de signets"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Résultats de recherche"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr "Insatisfait ? Vous pouvez aussi essayer notre "
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Lien entre tags créé."
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Impossible de créer le lien"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Ajout d'un lien entre tags"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Effacement d'un lien entre tags"
+
+#: 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 tags"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Editer un lien entre tags"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr "Editer la description commune du tag"
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Impossible de mettre à jour la description commune du tag"
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Tag effacé"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Impossible d'effacer le tag"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Description du tag mise à jour"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Impossible de mettre à jour la description du tag"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Tag renommé"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Erreur dans le renommage du tag"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Utilisateur enlevé de vos contacts"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Utilisateur ajouté à vos contacts"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Mes Contacts"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr "L'utilisation de l'API requiert une authentification."
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr "Revenir à"
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr "Conseil pour admin:"
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+"Pour rafraîchir manuellement le moteur de Google Custom Search, allez à :"
+
+#: www/gsearch/index.php:61
+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 ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<a href=\"register.php\">Enregistrez-vous maintenant</a> pour poster vos "
+#~ "propres signets sur %s !"
+
+#~ msgid ""
+#~ "<a href=\"http://sourceforge.net/projects/semanticscuttle/\">Semantic "
+#~ "Scuttle</a> is licensed under the <a href=\"http://www.gnu.org/copyleft/"
+#~ "gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> General Public "
+#~ "License</a> (you can host it on your own web server)."
+#~ msgstr ""
+#~ "<a href=\"http://sourceforge.net/projects/semanticscuttle/\">Semantic "
+#~ "Scuttle</a>, sous license <a href=\"http://www.gnu.org/copyleft/gpl.html"
+#~ "\"><acronym title=\"GNU's Not Unix\">GNU</acronym> General Public "
+#~ "License</a> (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 "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Adresse électronique\">E-mail</abbr>"
diff --git a/data/locales/hi_IN/LC_MESSAGES/messages.mo b/data/locales/hi_IN/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..d8972bd
--- /dev/null
+++ b/data/locales/hi_IN/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/hi_IN/LC_MESSAGES/messages.po b/data/locales/hi_IN/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..e12e5f0
--- /dev/null
+++ b/data/locales/hi_IN/LC_MESSAGES/messages.po
@@ -0,0 +1,1706 @@
+# Scuttle hi-IN Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Debashish Chakrabarty <debashish @ gmail.com>
+# Ravishankar Shrivastava <raviratlami@yahoo.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-04-16 21:56-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: hi-IN Debashish Chakrabarty <debashish @ gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Hindi\n"
+"X-Poedit-Country: India\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() को कई मरà¥à¤¤à¤¬à¤¾ काल किया गया."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "à¤à¤¸à¤•à¥à¤¯à¥‚à¤à¤² तà¥à¤°à¥à¤Ÿà¤¿"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "पंकà¥à¤¤à¤¿"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "फ़ाइल"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "जानकारी"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "जरूरी जानकारी"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "à¤à¤• तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "सामानà¥à¤¯ तà¥à¤°à¥à¤Ÿà¤¿"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "à¤à¤• गंभीर तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "गंभीर तà¥à¤°à¥à¤Ÿà¤¿"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "डिबग मोड"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"अपनी पसंद की कड़ियों को कहीं से भी पहà¥à¤à¤š योगà¥à¤¯ बनाने के लिठà¤à¤• ही सà¥à¤¥à¤² पर<strong>संचित</"
+"strong>करें."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"अपने पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ कड़ियों को सबके साथ, अपने निगरानी सूची के मितà¥à¤°à¥‹à¤‚ के साथ <strong>साà¤à¤¾</"
+"strong> करें या निजी इसà¥à¤¤à¥‡à¤®à¤¾à¤² करें"
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"अपनी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ कड़ियों को किसी फोलà¥à¤¡à¤° में ठूंसने के बजाठचाहे जितने लेबलों के "
+"साथ<strong>टैग</strong> कर सकते हैं."
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "पंजीकरण"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "विदà¥à¤µà¤¾à¤¨à¥‹à¤‚ का खजाना"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 में पà¥à¤°à¤¾à¤¯à¤ƒ सभी <a href=\"http://www.delicious.com/help/api\">del.icio.us "
+"<abbr title=\"Application Programming Interface\">à¤à¤ªà¥€à¤†à¤ˆ</abbr></a> का समरà¥à¤¥à¤¨ "
+"मिलता है. उस तंतà¥à¤° के लिठबनाठगठपà¥à¤°à¤¾à¤¯à¤ƒ सभी औजारों को %1$s के लिठकारà¥à¤¯ करने लायक "
+"परिवरà¥à¤§à¤¿à¤¤ किया जा सकता है. यदि आपको कोई औजार à¤à¤¸à¤¾ मिलता है जो आपको à¤à¤ªà¥€à¤†à¤ˆ पता "
+"बदलने नहीं देता है तो उसके सृजक को इस विनà¥à¤¯à¤¾à¤¸ को जोड़ने के लिठकहें. कौन जाने वे à¤à¤¸à¤¾ भी कर "
+"लें."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "उपयोकà¥à¤¤à¤¾ नाम"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "पकà¥à¤•à¥€ बात ना?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "मिटाà¤à¤"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "निरà¥à¤¦à¥‡à¤¶"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "शीरà¥à¤·à¤•"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "वरà¥à¤£à¤¨"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ चिहà¥à¤¨à¤•"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"निमà¥à¤¨ पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ चिहà¥à¤¨à¤•à¥‹à¤‚ में से किसी à¤à¤• को अपने बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° के पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ में खींच लाà¤à¤ व जब भी "
+"आप %s पर हों व कोई पृषà¥à¤  जोड़ना चाहेठतो उस पर कà¥à¤²à¤¿à¤• करें"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"निमà¥à¤¨ पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ चिहà¥à¤¨à¤•à¥‹à¤‚ में से किसी à¤à¤• को अपने बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° के पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ में खींच लाà¤à¤ व जब भी "
+"आप %s पर हों व कोई पृषà¥à¤  जोड़ना चाहेठतो उस पर कà¥à¤²à¤¿à¤• करें"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "%s को पोसà¥à¤Ÿ करें"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "%s को पोसà¥à¤Ÿ करें (पॉप-अप)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "इस आधार पर छांटें:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "तारीख़"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "पà¥à¤°à¤¥à¤®"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "पिछला"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "अगला"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "अंतिम"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "%d में से पृषà¥à¤  %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "टैग"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "संपादन"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "दà¥à¤µà¤¾à¤°à¤¾"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr " तथा %s1 अनà¥à¤¯%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " तथा %2$s%1$s अनà¥à¤¯%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ फ़ाइल से आयात करें"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "नक़ल करें"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "आप इस पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को संपादित नही कर सकते"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "कोई पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ उपलबà¥à¤§ नहीं"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "के बारे में"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿"
+msgstr[1] "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "लोकपà¥à¤°à¤¿à¤¯ टैग"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "लोकपà¥à¤°à¤¿à¤¯ टैग"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "पता"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "आवशà¥à¤¯à¤•"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "निजी"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "टैग"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "अलà¥à¤ªà¤µà¤¿à¤°à¤¾à¤® से अलग की गई"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "गोपनीयता"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "सारà¥à¤µà¤œà¤¨à¤¿à¤•"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "निगरानी सूची के साथ साà¤à¤¾"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "निजी"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाà¤à¤"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "आयात"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ फ़ाइल से आयात करें"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "इंटरनेट à¤à¤•à¥à¤¸à¤ªà¥à¤²à¥‹à¤°à¤°, मॉज़िलà¥à¤²à¤¾ फ़ॉयरफ़ॉकà¥à¤¸ तथा नेटसà¥à¤•à¥‡à¤ª"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "del.icio.us से पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ आयात करें"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "नाम"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "ईमेल"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "मिटाà¤à¤"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "खाते की जानकारी"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "उपयोकà¥à¤¤à¤¾ नाम"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "नया कूटशबà¥à¤¦"
+
+#: data/templates/default/editprofile.tpl.php:22
+#, fuzzy
+msgid "Confirm Password"
+msgstr "नया कूटशबà¥à¤¦ सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ करें"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "ईमेल"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "निजी"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "नया कूटशबà¥à¤¦ बनायें"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त जानकारी"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "गृहपृषà¥à¤ "
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "परिवरà¥à¤¤à¤¨ सहेजें"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "निरà¥à¤¦à¥‡à¤¶"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "मेरी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "नहीं मिला"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "इस सरà¥à¤µà¤° पर निवेदित यूआरà¤à¤² नहीं मिला"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "सामानà¥à¤¯ सरà¥à¤µà¤° तà¥à¤°à¥à¤Ÿà¤¿"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "निवेदित यूआरà¤à¤² को पà¥à¤°à¥‹à¤¸à¥‡à¤¸ नहीं किया जा सका"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "निगरानी सूची के साथ साà¤à¤¾"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "निरà¥à¤¦à¥‡à¤¶"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"<a href=\"http://del.icio.us/api/posts/all\">del.icio.us के निरà¥à¤¯à¤¾à¤¤ पृषà¥à¤  पर</a> "
+"लॉगइन करें"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"परिणाम <abbr title=\"Extensible Markup Language\">à¤à¤•à¥à¤¸à¤à¤®à¤à¤²</abbr> को अपने "
+"कमà¥à¤ªà¥à¤¯à¥‚टर पर सहेजें"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"इस फ़ाइल को अपने कमà¥à¤ªà¥à¤¯à¥‚टर पर ढूंढने के लिठ<kbd>बà¥à¤°à¤¾à¤‰à¤œà¤¼...</kbd> पर कà¥à¤²à¤¿à¤• करें. फ़ाइल का "
+"अधिकतम आकार 1 मे.बा. तक हो सकता है"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "आपके आयातित पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ के डिफ़ॉलà¥à¤Ÿ गोपनीयता विनà¥à¤¯à¤¾à¤¸ चà¥à¤¨à¥‡à¤‚"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ आयात चालू करने के लिठ<kbd>आयात</kbd> पर कà¥à¤²à¤¿à¤• करें, इसमें कà¥à¤› समय लग सकता है"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "अपने बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° से अपने पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को à¤à¤• फ़ाइल में निरà¥à¤¯à¤¾à¤¤ करें"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"इंटरनेट à¤à¤•à¥à¤¸à¤ªà¥à¤²à¥‹à¤°à¤°: <kbd>फ़ाइल &gt; आयात व निरà¥à¤¯à¤¾à¤¤... &gt; पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ निरà¥à¤¯à¤¾à¤¤ करें"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"मॉज़िलà¥à¤²à¤¾ फ़ॉयरफ़ॉकà¥à¤¸: <kbd>पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ &gt; पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¬à¤‚धन... &gt; फ़ाइल &gt; "
+"निरà¥à¤¯à¤¾à¤¤..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr "नेटसà¥à¤•à¥‡à¤ª: <kbd>पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ &gt; पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¬à¤‚धन... &gt; औजार &gt; निरà¥à¤¯à¤¾à¤¤..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"अपने कमà¥à¤ªà¥à¤¯à¥‚टर में सहेजे गठपृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ फ़ाइलों को ढूंढने के लिठ<kbd>बà¥à¤°à¤¾à¤‰à¤œà¤¼...</kbd> पर कà¥à¤²à¤¿à¤• "
+"करें. फ़ाइल का अधिकतम आकार 1मे.बा. तक हो सकता है."
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "कूटशबà¥à¤¦"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "कूटशबà¥à¤¦ के लिठमà¥à¤à¤¸à¥‡ 2 सपà¥à¤¤à¤¾à¤¹ तक नहीं पूछें"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "लॉग इन"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "अपना कूटशबà¥à¤¦ भूल गये हैं?"
+
+#: data/templates/default/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 ""
+"अगर आप अपना कूटशबà¥à¤¦ भूल गये हों तो %s आपके à¤à¤• नया कूटशबà¥à¤¦ बना सकता है। खाते से संबदà¥à¤§ "
+"अपना उपयोकà¥à¤¤à¤¾ नाम और ईमेल पता नीचे पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करें और हम आपको à¤à¤• नया कूटशबà¥à¤¦ ईमेल कर देंगे."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "नया कूटशबà¥à¤¦ बनायें"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "सदसà¥à¤¯à¤¤à¤¾ अवधि"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "निगरानी में"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "दà¥à¤µà¤¾à¤°à¤¾ देखा गया"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "मेरी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr "मà¥à¤«à¤¼à¥à¤¤ %s खाता बनाने के लिठयहाठसाइन करें. नीचे निवेदित सभी जानकारियाठआवशà¥à¤¯à¤• हैं"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "पंजीकरण"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "ढूंढें"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "पंकà¥à¤¤à¤¿"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "इस उपयोकà¥à¤¤à¤¾ की पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "मेरी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "मेरी निगरानी सूची"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "सभी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "ढूंढें"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "समà¥à¤¬à¤‚धित टैग"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "मिटाà¤à¤"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "हालिया टैग"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "लोकपà¥à¤°à¤¿à¤¯ टैग"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "समà¥à¤¬à¤‚धित टैग"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "हालिया टैग"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "समà¥à¤¬à¤‚धित टैग"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "ढूंढें"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "कोई पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ नहीं मिली"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "समà¥à¤¬à¤‚धित टैग"
+msgstr[1] "समà¥à¤¬à¤‚धित टैग"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "मिटाà¤à¤"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "वरà¥à¤£à¤¨"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "निगरानी सूची से मिटाà¤à¤"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "निगरानी सूची से मिटाà¤à¤"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "निगरानी सूची में जोड़ें"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "संपादन"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "हाà¤"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "नहीं"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "उपयोकà¥à¤¤à¤¾ नाम"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "वरà¥à¤£"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "लोकपà¥à¤°à¤¿à¤¯à¤¤à¤¾"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "गृहपृषà¥à¤ "
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+#, fuzzy
+msgid "Watchlist"
+msgstr "निगरानी सूची"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "पà¥à¤°à¥‹à¤«à¤¼à¤¾à¤‡à¤²"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "à¤à¤• पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ जोड़ें"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "लॉग आउट"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "पà¥à¤°à¥‹à¤«à¤¼à¤¾à¤‡à¤²"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "आपको इस पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को मिटाने की अनà¥à¤®à¤¤à¤¿ नहीं है."
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "सभी टैग"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, fuzzy, php-format
+msgid "User with username %s was not found"
+msgstr "%s नाम का कोई भी उपयोकà¥à¤¤à¤¾ नहीं मिला"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "आपकी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ के लिठà¤à¤• शीरà¥à¤·à¤• तथा à¤à¤• पता होना आवशà¥à¤¯à¤• है"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ सहेजी गई"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"आपकी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को सहेजने के दौरान à¤à¤• तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ. कृपया फिर से कोशिश करें या पà¥à¤°à¤¶à¤¾à¤¸à¤• से "
+"समà¥à¤ªà¤°à¥à¤• साधें."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ जोड़ें"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ जोड़ने से पहले आपका लॉगइन होना आवशà¥à¤¯à¤• है."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "मेरी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ संपादन"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "पहचान चिहà¥à¤¨ %s यà¥à¤•à¥à¤¤ पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ नहीं मिला"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "आप इस पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को संपादित नही कर सकते"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ सहेजने के दौरान तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ"
+
+#: www/history.php:61
+msgid "History"
+msgstr "इतिहास"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "%s का इतिहास"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "पता नहीं मिला"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "à¤à¤•à¥à¤¸à¤à¤®à¤à¤² इनपà¥à¤Ÿ खोल नहीं सकता"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "à¤à¤•à¥à¤¸à¤à¤®à¤à¤² तà¥à¤°à¥à¤Ÿà¤¿: %s में पंकà¥à¤¤à¤¿ %d पर"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "del.icio.us से पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ आयात करें"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "आप इस पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को पहले ही पेश कर चà¥à¤•à¥‡ हैं"
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ आयातित."
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "आप इस पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को पहले ही पेश कर चà¥à¤•à¥‡ हैं"
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "कोई पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ नहीं मिली"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ आयातित."
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° फ़ाइल से पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ को आयात करें"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "अब आप लॉग आउट हैं"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: हालिया पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: हालिया पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "अपनी मनपसंद पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ सहेजें, साà¤à¤¾ करें और टैग करें"
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "सभी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "उपलबà¥à¤§"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "उपलबà¥à¤§ नहीं"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "आपने जो विवरण भरे हैं वे गलत हैं. कृपया पà¥à¤¨à¤ƒ पà¥à¤°à¤¯à¤¾à¤¸ करें."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "आपका उपयोकà¥à¤¤à¤¾ नाम पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करना ज़रूरी है."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr "<abbr title=\"electronic mail\">ईमेल</abbr> पता भरना आवशà¥à¤¯à¤• है."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "इस से मेल खाता कोई उपयोकà¥à¤¤à¤¾ नाम नहीं मिला."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"इस उपयोकà¥à¤¤à¤¾ तथा <abbr title=\"electronic mail\">ईमेल</abbr> पते से मेल खाता कोई "
+"जोड़ नहीं मिला."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr "आपका नया कूटशबà¥à¤¦ बनाने समय कोई तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ है। कृपया पà¥à¤¨à¤ƒ पà¥à¤°à¤¯à¤¾à¤¸ करें।"
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "आपका नया कूटशबà¥à¤¦ हैः"
+
+#: 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 ""
+"अपनी पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रखने के लिये, जब आप अगली बार लॉगिन करें तो अपने पà¥à¤°à¥‹à¤«à¤¾à¤ˆà¤² में "
+"जाकर अपना कूटशबà¥à¤¦ अवशà¥à¤¯ बदल लें."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s खाते की जानकारी"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "नया कूटशबà¥à¤¦ बना कर %s को भेज दिया गया है."
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "भूला कूटशबà¥à¤¦"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "उपयोकà¥à¤¤à¤¾ नाम निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ नहीं है"
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "मेरा पà¥à¤°à¥‹à¤«à¤¼à¤¾à¤‡à¤²"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "कूटशबà¥à¤¦ तथा उसका पà¥à¤·à¥à¤Ÿà¤¿à¤•à¤°à¤£ मेल नहीं खाते"
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "कूटशबà¥à¤¦ को कम से कम 6 अकà¥à¤·à¤° लंबा होना चाहिà¤"
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "ईमेल पता वैध नहीं है."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "आपके दà¥à¤µà¤¾à¤°à¤¾ किठगठपरिवरà¥à¤¤à¤¨à¥‹à¤‚ को सहेजने के दौरान तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "परिवरà¥à¤¤à¤¨à¥‹à¤‚ को सहेजा गया."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"आपको उपयोकà¥à¤¤à¤¾ नाम, कूटशबà¥à¤¦ तथा <abbr title=\"electronic mail\">ईमेल</abbr> पता "
+"भरना <em>आवशà¥à¤¯à¤•</em> है"
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr "यह उपयोकà¥à¤¤à¤¾ नाम पहले ही मौजूद है, कृपया कोई दूसरा चà¥à¤¨à¥‡à¤‚."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "यह उपयोकà¥à¤¤à¤¾ नाम पहले ही मौजूद है, कृपया कोई दूसरा चà¥à¤¨à¥‡à¤‚."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr "यह उपयोकà¥à¤¤à¤¾ नाम पहले ही मौजूद है, कृपया कोई दूसरा चà¥à¤¨à¥‡à¤‚."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "<abbr title=\"electronic mail\">ईमेल</abbr> पता वैध नहीं है."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "<abbr title=\"electronic mail\">ईमेल</abbr> पता वैध नहीं है."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "आप सफलता पूरà¥à¤µà¤• पंजीकृत हो गठहैं. बधाई!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "पंजीकरण असफल. कृपया फिर से कोशिश करें."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "%s को पोसà¥à¤Ÿ की गईं हालिया पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ "
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤ ढूंढें"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "खोज परिणाम"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿ मिटाने में असफल"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "उपयोकà¥à¤¤à¤¾ नाम"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "आपकी निगरानी सूची से उपयोकà¥à¤¤à¤¾ को मिटाया गया"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "उपयोकà¥à¤¤à¤¾ को आपकी निगरानी सूची में जोड़ा"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "मेरी निगरानी सूची"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "%s नाम का कोई भी उपयोकà¥à¤¤à¤¾ नहीं मिला"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "हालिया पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#~ msgid "for"
+#~ msgstr "के लिà¤"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%s पृषà¥à¤ -सà¥à¤®à¥ƒà¤¤à¤¿à¤¯à¤¾à¤"
+
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<strong><a href=\"register.php\">तà¥à¤°à¤‚त पंजीकृत हों</a></strong> %s का इसà¥à¤¤à¥‡à¤®à¤¾à¤² "
+#~ "करने के लिà¤!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "%s à¤à¤• <a href=\"http://sourceforge.net/projects/scuttle/\">मà¥à¤•à¥à¤¤ सà¥à¤°à¥‹à¤¤ "
+#~ "परियोजना</a> पर आधारित है तथा इसे<a href=\"http://www.gnu.org/copyleft/gpl."
+#~ "html\"><acronym title=\"GNU's Not Unix\">गà¥à¤¨à¥‚</acronym> जनरल पबà¥à¤²à¤¿à¤• लाइसेंस</"
+#~ "a> के तहत जारी किया गया है. इसका अरà¥à¤¥ है कि आप इसे अपने सà¥à¤µà¤¯à¤‚ के वेब सरà¥à¤µà¤° पर चाहे "
+#~ "इंटरनेट पर हो या निजी नेटवरà¥à¤• पर या आपके वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त कमà¥à¤ªà¥à¤¯à¥‚टर पर, होसà¥à¤Ÿ कर सकते हैं."
+
+#~ msgid "URL"
+#~ msgstr "यूआरà¤à¤²"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"इलेकà¥à¤Ÿà¥à¤°à¤¾à¤¨à¤¿à¤• मेल\">ईमेल</abbr>"
diff --git a/data/locales/it_IT/LC_MESSAGES/messages.mo b/data/locales/it_IT/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..9d4baa0
--- /dev/null
+++ b/data/locales/it_IT/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/it_IT/LC_MESSAGES/messages.po b/data/locales/it_IT/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..8a43739
--- /dev/null
+++ b/data/locales/it_IT/LC_MESSAGES/messages.po
@@ -0,0 +1,1749 @@
+# Scuttle it-IT Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# <upcome@gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-03-18 21:51-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: it-IT <upcome@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Italian\n"
+"X-Poedit-Country: ITALY\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() è stato richiamato più volte."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Errore SQL"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Linea"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "File"
+
+#: src/SemanticScuttle/functions.php:215
+#, fuzzy
+msgid "Information"
+msgstr "Informazioni"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Informazioni Importanti"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Si è verificato un errore"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Errore Generale"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Si è verificato un errore critico"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Errore Critico"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "MODO DEBUG"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Archivia</strong> i tuoi Preferiti in un unico luogo, accessibile "
+"ovunque tu sia. "
+
+#: data/templates/default/about.tpl.php:7
+#, fuzzy
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Condividi</strong> i tuoi Preferiti con gli altri o mantinili "
+"privati. Potrai restare aggiornato sui Preferiti aggiunti da altri utenti."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Tagga</strong> i tuoi Preferiti con quante etichette vuoi, anziché "
+"perdere tempo con le cartelle."
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "Registrati"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "(Geek Stuff)"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 supporta la maggioranza delle <a href=\"http://www.delicious.com/help/"
+"api\">del.icio.us <abbr title=\"Application Programming Interface\">API</"
+"abbr></a>. Praticamente tutti i tools progettati per de.icio.us possono "
+"essere modificati per funzionare con %1$s. Se trovi un tool che non lascia "
+"variare indirizzo API, chiedi a chiedi a chi lo ha realizzato di aggiungere "
+"questo settaggio."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Username"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "preferito"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Elimina"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Istruzioni"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titolo"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Descrizione"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Trascina uno dei seguenti bookmarklets tra i collegamenti del tuo browser e "
+"cliccalo ogni volta che vuoi aggiungere la pagina in cui ti trovi al tuo "
+"account %s "
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Trascina uno dei seguenti bookmarklets tra i collegamenti del tuo browser e "
+"cliccalo ogni volta che vuoi aggiungere la pagina in cui ti trovi al tuo "
+"account %s "
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Aggiungi a %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Aggiungi a %s (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Ordinato per:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Data"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Precedente"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Seguente"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Pagina %d di %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Etichette"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Modifica"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, fuzzy, php-format
+msgid " and %s1 other%s"
+msgstr " e %s altri"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " e %s altri"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Importa preferiti da un file"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copia"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Non hai il permesso per modificare questo preferito"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Nessun preferito disponibile"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Informazioni"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+#, fuzzy
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "preferito"
+msgstr[1] "preferiti"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Tags Più Utilizzati"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Tags Più Utilizzati"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Indirizzo"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Obbligatorio"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Privato"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Etichette"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Saparati da virgola"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Privacy"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Pubblico"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Condiviso con chi tieni sotto osservazione"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privato"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Elimina Preferito"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importa"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importa preferiti da un file"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox e Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importa preferiti da del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Nome"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Elimina"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Username"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nuova Password"
+
+#: data/templates/default/editprofile.tpl.php:22
+#, fuzzy
+msgid "Confirm Password"
+msgstr "Conferma Nuova Password"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Privato"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Nuova Password"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Homepage"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Salva Modifiche"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "Istruzioni"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "i miei preferiti"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+#, fuzzy
+msgid "Not Found"
+msgstr "Non Trovato"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "URL non trovata"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Errore generale del server"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "URL non eseguibile"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Condiviso con chi tieni sotto osservazione"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Istruzioni"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Fai il Log-in e vai alla <a href=\"http://del.icio.us/api/posts/all\">export "
+"page di del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Salva il file <abbr title=\"Extensible Markup Language\">XML</abbr> nel tuo "
+"computer"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Clicca <kbd>Sfoglia...</kbd> per trovare il file nel tuo computer. La "
+"dimensione massima accettata è 1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "Seleziona il livello di privacy predefinito per i preferiti importati"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Clicca <kbd>Importa</kbd> per cominciare ad importare i preferiti; questa "
+"operazione può richiedere qualche minuto"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Esporta i tuoi preferiti dal tuo browser ad un file"
+
+#: data/templates/default/importNetscape.tpl.php:37
+#, fuzzy
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>File > Importa ed Esporta... > Esporta Preferiti"
+
+#: data/templates/default/importNetscape.tpl.php:38
+#, fuzzy
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Segnalibri > Gestione Segnalibri... > File > Esporta..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+#, fuzzy
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bookmarks > Gestione Bookmarks... > Strumenti > Esporta..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Clicca <kbd>Sfoglia...</kbd> per trovare il file salvato sul tuo computer. "
+"La dimensione massima consentita è 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Password"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Non chiedermi la password per 2 settimane"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Entra"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/password.tpl.php:19
+#, fuzzy
+msgid "Generate Password"
+msgstr "Nuova Password"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Iscritto Dal"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+#, fuzzy
+msgid "Watching"
+msgstr "Lista Sotto Osservazione"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+#, fuzzy
+msgid "Watched By"
+msgstr "Sotto Osservazione Di"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+#, fuzzy
+msgid "Bookmarks"
+msgstr "%s Preferiti"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "i miei preferiti"
+
+#: data/templates/default/register.tpl.php:11
+#, fuzzy, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Registrati per creare un account gratuito con %s. Tutte le informazioni "
+"richieste sono obbligatorie."
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registrati"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Cerca"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "Linea"
+
+#: data/templates/default/search.menu.php:34
+#, fuzzy
+msgid "this user's bookmarks"
+msgstr "%s preferiti"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "i miei preferiti"
+
+#: data/templates/default/search.menu.php:40
+#, fuzzy
+msgid "my watchlist"
+msgstr "Metti Sotto Osservazione"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "tutti i preferiti"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Cerca"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "Tags Correlati"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "Elimina"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "Tags Recenti"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "Tags Più Utilizzati"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Tags Correlati"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+#, fuzzy
+msgid "Recent Tags"
+msgstr "Tags Recenti"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Tags Correlati"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "Cerca"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "Nessun preferito trovato"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Tags Correlati"
+msgstr[1] "Tags Correlati"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "Elimina"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "Descrizione"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "preferito"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Rimuovi da Sotto Osservazione"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Rimuovi da Sotto Osservazione"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Metti Sotto Osservazione"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "Modifica"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "Username"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr ""
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+#, fuzzy
+msgid "Popularity"
+msgstr "Tags Più Utilizzati"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "Homepage"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+#, fuzzy
+msgid "Watchlist"
+msgstr "Lista Sotto Osservazione"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profilo"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Aggiungi un preferito"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Esci"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "Profilo"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+#, fuzzy
+msgid "You are not allowed to delete this bookmark"
+msgstr "Non hai il permesso di eliminare questo preferito"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+#, fuzzy
+msgid "Failed to delete bookmark"
+msgstr "Eliminazione del preferito fallita"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Tutti i Tags"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, fuzzy, php-format
+msgid "User with username %s was not found"
+msgstr "L'utente %s non è stato trovato"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "Eliminazione preferito fallita"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Il preferito deve avere un titolo ed un indirizzo"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Preferito salvato"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Si è verificato un errore salvando il tuo preferito. Riprova o contatta "
+"l'amministratore"
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Aggiungi preferito"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Devi essere loggato per poter aggiungere dei preferiti"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+#, fuzzy
+msgid "My Bookmarks"
+msgstr "I Miei Preferiti"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Modifica Preferito"
+
+#: www/edit.php:51
+#, fuzzy, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Il preferito con id %s non è stato trovato"
+
+#: www/edit.php:57
+#, fuzzy
+msgid "You are not allowed to edit this bookmark"
+msgstr "Non hai il permesso per modificare questo preferito"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Errore salvando il preferito"
+
+#: www/history.php:61
+msgid "History"
+msgstr ""
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr ""
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr ""
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Non posso aprire la fonte XML"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "Errore XML: %s alla linea %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Importa Preferiti da del.icio.us"
+
+#: www/import.php:93
+#, fuzzy
+msgid "You have already submitted this bookmark."
+msgstr "Hai già inserito questo preferito"
+
+#: www/import.php:104
+#, fuzzy
+msgid "Bookmark imported."
+msgstr "Preferito importato"
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Hai già inserito questo preferito"
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "Nessun preferito trovato"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Preferito importato"
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+#, fuzzy
+msgid "Import Bookmarks from Browser File"
+msgstr "Importa Preferiti da un File del Browser"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Sei uscito dal sistema"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Preferiti recenti"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Preferiti recenti"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr ""
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "tutti i preferiti"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr ""
+
+#: www/jsScuttle.php:74
+#, fuzzy
+msgid "Not Available"
+msgstr "Nessun preferito disponibile"
+
+#: www/login.php:48
+#, fuzzy
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "Le informazioni che hai fornito non sono corrette. Riprova."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr ""
+
+#: www/password.php:40
+#, fuzzy
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"<em>Devi</em> inserire un nome utente, una password, un nome ed un indirizzo "
+"<abbr title=\"posta elettronica\">e-mail</abbr>."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr ""
+
+#: www/password.php:51
+#, fuzzy
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"<em>Devi</em> inserire un nome utente, una password, un nome ed un indirizzo "
+"<abbr title=\"posta elettronica\">e-mail</abbr>."
+
+#: www/password.php:59
+#, fuzzy
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Si è verificato un errore salvando il tuo preferito. Riprova o contatta "
+"l'amministratore"
+
+#: www/password.php:63
+#, fuzzy
+msgid "Your new password is:"
+msgstr "Conferma Nuova Password"
+
+#: 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 ""
+
+#: www/password.php:66
+#, fuzzy, php-format
+msgid "%s Account Information"
+msgstr "Informazioni"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr ""
+
+#: www/password.php:75
+#, fuzzy
+msgid "Forgotten Password"
+msgstr "Nuova Password"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Nome utente non specificato"
+
+#: www/profile.php:70
+#, fuzzy
+msgid "My Profile"
+msgstr "Profilo"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+#, fuzzy
+msgid "Password and confirmation do not match."
+msgstr "La password e la sua conferma non coincidono."
+
+#: www/profile.php:119
+#, fuzzy
+msgid "Password must be at least 6 characters long."
+msgstr "La password deve essere lunga almeno 6 caratteri."
+
+#: www/profile.php:123
+#, fuzzy
+msgid "E-mail address is not valid."
+msgstr "Indirizzo e-mail non valido."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Si è verificato un errore salvando le tue modifiche."
+
+#: www/profile.php:129
+#, fuzzy
+msgid "Changes saved."
+msgstr "Modifiche salvate."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+#, fuzzy
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"<em>Devi</em> inserire un nome utente, una password, un nome ed un indirizzo "
+"<abbr title=\"posta elettronica\">e-mail</abbr>."
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr "Questo nome utente esiste già, scegline un altro."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Questo nome utente esiste già, scegline un altro."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr "Questo nome utente esiste già, scegline un altro."
+
+#: www/register.php:68
+#, fuzzy
+msgid "E-mail address is not valid. Please try again."
+msgstr "Indirizzo e-mail non valido."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Indirizzo e-mail non valido."
+
+#: www/register.php:90
+#, fuzzy
+msgid "You have successfully registered. Enjoy!"
+msgstr "Ti sei registrato con successo. Buon divertimento!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Registrazione fallita. Riprova."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, fuzzy, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Preferiti inseriti di recente"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Cerca Preferiti"
+
+#: www/search.php:89
+#, fuzzy
+msgid "Search Results"
+msgstr "Risultati della Ricerca"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "Eliminazione preferito fallita"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "Eliminazione preferito fallita"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "Eliminazione preferito fallita"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "Eliminazione preferito fallita"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "Eliminazione preferito fallita"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "Username"
+
+#: www/watch.php:54
+#, fuzzy
+msgid "User removed from your watchlist"
+msgstr "Utente rimosso dalla tua watchlist"
+
+#: www/watch.php:56
+#, fuzzy
+msgid "User added to your watchlist"
+msgstr "Utente aggiunto alla tua watchlist"
+
+#: www/watchlist.php:104
+#, fuzzy
+msgid "My Watchlist"
+msgstr "Lista Sotto Osservazione"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "L'utente %s non è stato trovato"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "Preferiti Recenti"
+
+#~ msgid "for"
+#~ msgstr "per"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%s Preferiti"
+
+#, fuzzy
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<strong><a href=\"register.php\">Registrati ora</a></strong> per iniziare "
+#~ "ad utilizzare %s!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "%s è basato su <a href=\"http://sourceforge.net/projects/scuttle/\">un "
+#~ "progetto open-source</a>."
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Posta elettronica\">E-mail</abbr>"
diff --git a/data/locales/ja_JP/LC_MESSAGES/messages.mo b/data/locales/ja_JP/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..37f3d74
--- /dev/null
+++ b/data/locales/ja_JP/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/ja_JP/LC_MESSAGES/messages.po b/data/locales/ja_JP/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..0356a37
--- /dev/null
+++ b/data/locales/ja_JP/LC_MESSAGES/messages.po
@@ -0,0 +1,1670 @@
+# Scuttle ja-JP Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Tadashi Jokagi <elf2000@users.sourceforge.net>, 2005-2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: semanticscuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2009-05-30 02:29+0900\n"
+"Last-Translator: Tadashi Jokagi <elf2000@users.sourceforge.net>\n"
+"Language-Team: Japanese <http://oss.poyo.jp/bbs/>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"X-Poedit-Language: Japanese\n"
+"X-Poedit-Country: JAPAN\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() ã¯è¤‡æ•°å›žå‘¼ã°ã‚Œã¾ã—ãŸã€‚"
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL エラー"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "行"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "ファイル"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "情報"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "é‡è¦ãªæƒ…å ±"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "一般エラー"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "é‡å¤§ãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "é‡å¤§ãªã‚¨ãƒ©ãƒ¼"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "デãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"ã„ã‹ãªã‚‹å ´æ‰€ã‹ã‚‰ã‚‚アクセスå¯èƒ½ãª 1 ã¤ã®å ´æ‰€ã«ã€å¥½ããªãƒªãƒ³ã‚¯ã‚’ã™ã¹ã¦<strong>ä¿"
+"管</strong>ã—ã¾ã™ã€‚"
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"ブックマークをプライベートã§ã€æ³¨ç›®ä¸€è¦§ã§å‹é”ã¨ã€ã™ã¹ã¦ã®äººã¨<strong>共有</"
+"strong>ã—ã¾ã™ã€‚"
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"フォルダーã¨æ ¼é—˜ã™ã‚‹ä»£ã‚ã‚Šã«ã€æœ›ã‚€ãªã‚‰ã„ãã¤ã‹ã®ãƒ©ãƒ™ãƒ«ã¨ã¨ã—ã¦ãƒ–ックマークã®"
+"<strong>ã‚¿ã‚°</strong>を付ã‘ã¦ãã ã•ã„。"
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "今ã™ã登録ã™ã‚‹"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "㧠%s ã®åˆ©ç”¨ã‚’始ã‚られã¾ã™!"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "ギークスタッフ"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "ã¯æ¬¡ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã§åˆ©ç”¨ã§ãã¾ã™: "
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr "ã‚ãªãŸã®ã‚¦ã‚§ãƒ–サーãƒãƒ¼ã§è‡ªç”±ã«é‹å–¶ã§ãã¾ã™ã€‚"
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 ã¯ã»ã¨ã‚“ã©ã® <a href=\"http://www.delicious.com/help/api\">del.icio.us "
+"<abbr title=\"Application Programming Interface\">API</abbr></a> をサãƒãƒ¼ãƒˆã—"
+"ã¾ã™ã€‚ãã®ã‚·ã‚¹ãƒ†ãƒ ã®ãŸã‚ã«ä½œæˆã•ã‚ŒãŸé©åˆ‡ãªãƒ„ールã®ã»ã¼å…¨ã¦ã¯ã€ä»£ã‚ã‚Šã«AAAAã§"
+"動作ã™ã‚‹ã‚ˆã†ã«ä¿®æ­£ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã‚ãªãŸãŒ API アドレスを変更ã™ã‚‹ã“ã¨ãŒã§"
+"ããªã„ツールを見ã¤ã‘ãŸã‚‰ã€è£½ä½œè€…ã«ã“ã®è¨­å®šã‚’加ãˆã‚‹ã‚ˆã†é ¼ã‚“ã§ãã ã•ã„。ã©ã†ãª"
+"ã‚‹ã‹ã‚ã‹ã‚Šã¾ã›ã‚“ãŒã€å½¼ã‚‰ã¯å¯¾å¿œã™ã‚‹ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。"
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr "ユーザー管ç†"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr "公開/共有/プライベート"
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "ブックマーク"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "本当ã«ã—ã¾ã™ã‹?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "削除ã™ã‚‹"
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr "ãã®ä»–ã®æ“作"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "é¡Œå"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "説明"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "最終更新:"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "æ›´æ–°"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "å–り消ã—"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "ブックマークレット"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"ブラウザーã®ãƒ–ックマークã«æ¬¡ã®ãƒ–ックマークレットã®ã†ã¡ã® 1 ã¤ã‚’ドラッグã—ã¾"
+"ã™ã€‚%s ã«ãƒšãƒ¼ã‚¸ã‚’追加ã—ãŸã„å ´åˆã€å¸¸ã«ãれをクリックã—ã¦ãã ã•ã„。"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"ブラウザーã®ãƒ–ックマークã«æ¬¡ã®ãƒ–ックマークレットã®ã†ã¡ã® 1 ã¤ã‚’ドラッグã—ã¾"
+"ã™ã€‚%s ã«ãƒšãƒ¼ã‚¸ã‚’追加ã—ãŸã„å ´åˆã€å¸¸ã«ãれをクリックã—ã¦ãã ã•ã„。"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "%s ã«æŠ•ç¨¿ã™ã‚‹"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "%s ã«æŠ•ç¨¿ã™ã‚‹ (ãƒãƒƒãƒ—アップ)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr "ã“ã®ã‚¿ã‚°ã®å…±é€šèª¬æ˜Žã‚’編集ã™ã‚‹"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr "ã“ã®ãƒ–ックマークã®å…±é€šèª¬æ˜Žã‚’編集ã™ã‚‹"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "ã“ã®ã‚¿ã‚°ã®å€‹äººçš„ã«èª¬æ˜Žã‚’編集ã™ã‚‹"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "並ã¹æ›¿ãˆã®åŸºæº–:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "日付"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "ã“ã®ã‚¿ã‚°ã®ãã®ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ–ックマーク"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "ã“ã®ã‚¿ã‚°ã¯ã‚ãªãŸã®ãƒ–ックマークã®ã¿ã§ã™"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "最åˆã¸"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "å‰ã¸"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "次ã¸"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "最後ã¸"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "ページ (%d/%d)"
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr "ã‚¿ã‚°:"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "編集ã™ã‚‹"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "登録者:"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "ã‚ãªãŸ"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr "ã¨ãã®ä»–%sã²ã¨ã‚Š%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr "ã¨ãã®ä»– %2$s%1$s 人%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "ã‚ãªãŸã®ãƒ–ックマークã«ã“ã®ãƒ–ックマークをコピーã—ã¾ã™"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "コピーã™ã‚‹"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr "ã“ã®ãƒ–ックマークã®ãƒ—ライベートノート"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr "ページã®å…ˆé ­"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "ブックマークã«ç™»éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "ã“ã®ã‚µã‚¤ãƒˆã«ã¤ã„ã¦"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "ブックマーク"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "人気ã®ã‚¿ã‚°"
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§äººæ°—ã®ã‚¿ã‚°"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "アドレス"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "å¿…é ˆ"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "ノートã®è¿½åŠ "
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "プライベートノート"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "ã‚¿ã‚°"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "カンマ区切り"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "プライãƒã‚·ãƒ¼"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "公開"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "注目リストã§å…±æœ‰"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "プライベート"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "ブックマークを削除ã™ã‚‹"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "共通説明ã®ç·¨é›†"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "インãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "ブックマークファイルã‹ã‚‰ãƒ–ックマークをインãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorerã€Mozilla Firefox 㨠Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "del.icio.us ã‹ã‚‰ãƒ–ックマークをインãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "åå‰"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr "é›»å­ãƒ¡ãƒ¼ãƒ«"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "削除ã™ã‚‹"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "アカウントã®è©³ç´°"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "ユーザーå"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "æ–°è¦ãƒ‘スワード"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "パスワード(確èª)"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "é›»å­ãƒ¡ãƒ¼ãƒ«"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "プライベートノート"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "パスワードを生æˆã™ã‚‹"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "個人ã®è©³ç´°"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "ホームページ"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "変更をä¿å­˜ã™ã‚‹"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "æ“作"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "ブックマークをエクスãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "HTML ファイル (ブラウザー用)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "XML ファイル (del.icio.us ã®ã‚ˆã†ãª)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr "CSV ファイル (スプレッドシート ツール用)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "見ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "è¦æ±‚ã•ã‚ŒãŸ URL ã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "サーãƒãƒ¼å…¨ä½“ã®ã‚¨ãƒ©ãƒ¼"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "è¦æ±‚ã•ã‚ŒãŸ URL ã¯å‡¦ç†ã§ãã¾ã›ã‚“"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "注目リストã§å…±æœ‰"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "手順"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"<a href=\"http://del.icio.us/api/posts/all\">del.icio.us ã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒšãƒ¼ã‚¸"
+"</a> ã«ãƒ­ã‚°ã‚¤ãƒ³ã—ã¾ã™ã€‚"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"çµæžœã® <abbr title=\"Extensible Markup Language\">XML</abbr> ファイルをコン"
+"ピューターã«ä¿å­˜ã—ã¾ã™ã€‚"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"コンピューター上ã®ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’見ã¤ã‘ã‚‹ãŸã‚ã« <kbd>å‚ç…§...</kbd> をクリック"
+"ã—ã¾ã™ã€‚ファイルã®æœ€å¤§ã‚µã‚¤ã‚ºã¯ 1MB ã§ã™ã€‚"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "インãƒãƒ¼ãƒˆã—ãŸãƒ–ックマークã®æ¨™æº–プライãƒã‚·ãƒ¼è¨­å®šã‚’é¸æŠžã—ã¾ã™ã€‚"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"<kbd>インãƒãƒ¼ãƒˆã™ã‚‹</kbd> をクリックã™ã‚‹ã¨ãƒ–ックマークã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’開始ã—ã¾"
+"ã™ã€‚ã—ã°ã‚‰ãã‹ã‹ã‚‹ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "ブラウザーã‹ã‚‰ãƒ•ã‚¡ã‚¤ãƒ«ã«ãƒ–ックマークをエクスãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>ファイル &gt; インãƒãƒ¼ãƒˆã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ... &gt; ãŠæ°—ã«"
+"入りã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>ブックマーク &gt; ブックマークã®ç®¡ç†... &gt; ファイル "
+"&gt; Export..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>ブックマーク &gt; ブックマークã®ç®¡ç†... &gt; ツール &gt; エク"
+"スãƒãƒ¼ãƒˆ..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"コンピューター上ã«ä¿å­˜ã•ã‚ŒãŸãƒ–ックマークを見ã¤ã‘ã‚‹ãŸã‚ã« <kbd>å‚ç…§...</kbd> "
+"をクリックã—ã¾ã™ã€‚ファイルã®æœ€å¤§ã‚µã‚¤ã‚ºã¯ 1MB ã§ã™ã€‚"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "パスワード"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "2 週間自分ã®ãƒ‘スワードをå•ã„åˆã‚ã›ã¾ã›ã‚“"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "ログイン"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "パスワードを忘れã¾ã—ãŸã‹?"
+
+#: data/templates/default/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 ""
+"パスワードを忘れã¦ã—ã¾ã£ãŸå ´åˆã€%s ã¯æ–°ã—ã„パスワードを生æˆã§ãã¾ã™ã€‚アカウン"
+"トã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¨é›»å­ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’下ã®ãƒ•ã‚©ãƒ¼ãƒ ã«å…¥åŠ›ã—ã¦ãã ã•ã„。ãã†ã™ã‚Œ"
+"ã°ã€æ–°ã—ã„パスワードを電å­ãƒ¡ãƒ¼ãƒ«ã§é€ä¿¡ã™ã‚‹ã§ã—ょã†ã€‚"
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "パスワードを生æˆã™ã‚‹"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "メンãƒãƒ¼ç™»éŒ²æ—¥"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "注目一覧"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "注目ã—ã¦ã„る人:"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "ブックマーク"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "ブックマークã«ç§»å‹•ã™ã‚‹"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"ã“ã“ã§ã‚µã‚¤ãƒ³ã‚¢ãƒƒãƒ—ã—ã€%s ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’自由ã«ä½œæˆã§ãã¾ã™ã€‚下記ã§è¦æ±‚ã•ã‚ŒãŸæƒ…"
+"å ±ã¯ã™ã¹ã¦å¿…é ˆã§ã™ã€‚"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "スパム対策ã®è³ªå•"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "登録ã™ã‚‹"
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr "検索..."
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr ""
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ–ックマーク"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "自分ã®ãƒ–ックマーク"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "自分ã®æ³¨ç›®ä¸€è¦§"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "ã™ã¹ã¦ã®ãƒ–ックマーク"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "検索"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "関連ã™ã‚‹ã‚¿ã‚°"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "æ–°è¦ãƒªãƒ³ã‚¯ã‚’追加ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "リンクを削除ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "ã‚¿ã‚°ã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "ã™ã¹ã¦ã®ã‚¿ã‚°ã‚’見る"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "ã‚ãªãŸã®ã™ã¹ã¦ã®ã‚¿ã‚°"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã™ã¹ã¦ã®ã‚¿ã‚°ã‚’見る"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã»ã‹ã®ã‚¿ã‚°"
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "人気ã®ã‚¿ã‚°ã‚’見る"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "最近ã®ã‚¿ã‚°"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "関連ã™ã‚‹ã‚¿ã‚°"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "最後ã®æ¤œç´¢"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "ã“ã®å•ã„åˆã‚ã›ã®ãƒ–ックマーク数"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "タグを変更ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "タグを削除ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "ã‚¿ã‚°ã®èª¬æ˜Žã‚’編集ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "別ã®ã‚¿ã‚°ã¸ã®ãƒªãƒ³ã‚¯ã‚’作æˆã™ã‚‹"
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "ブックマーク"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’見る"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr "連絡簿を閉ã˜ã‚‹"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "連絡簿ã«è¿½åŠ ã™ã‚‹..."
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "連絡簿ã«è¿½åŠ ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼åを入力ã—ã¾ã™ã€‚"
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "連絡簿ã‹ã‚‰å‰Šé™¤ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "注目一覧ã‹ã‚‰å‰Šé™¤ã™ã‚‹"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "注目一覧ã«è¿½åŠ "
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "リンクを編集ã™ã‚‹"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "æ–°ã—ã„リンクã®ä½œæˆ:"
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "作æˆ"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "既存ã®ãƒªãƒ³ã‚¯:"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "リンクãªã—"
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "ã¯ã„"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "ã„ã„ãˆ"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "リンクを作æˆã™ã‚‹"
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "æ–°è¦ãƒªãƒ³ã‚¯ã‚’作æˆã™ã‚‹"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "æ—§"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "æ–°"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "åå‰ã‚’変更ã™ã‚‹"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "アルファベット"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "人気"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr "ホーム"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "注目一覧"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "プロフィール"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "ブックマーク追加"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "ログアウト"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr "管ç†"
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr "プロフィール"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "ユーザー管ç†"
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr "%s ã¨ã™ã¹ã¦ã®ãƒ–ックマークを削除ã—ã¾ã—ãŸã€‚"
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "ã“ã®ãƒ–ックマークã®å‰Šé™¤ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "ブックマークã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "ã™ã¹ã¦ã®ã‚¿ã‚°"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "ユーザーåãŒã€Œ%sã€ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯è¦‹ã¤ã‘られã¾ã›ã‚“ã§ã—ãŸ"
+
+#: 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 "権é™ãŒã‚ã‚Šã¾ã›ã‚“。"
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr "ブックマーク共通ã®èª¬æ˜Žã‚’æ›´æ–°ã—ã¾ã—ãŸ"
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr "ブックマーク共通ã®èª¬æ˜Žã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr "ブックマーク共通ã®èª¬æ˜Žã®ç·¨é›†"
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "é¸æŠžã‹ã‚‰ã‚¿ã‚°ã‚’削除ã™ã‚‹"
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "ブックマークã¯é¡Œåã¨ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "ブックマークをä¿å­˜ã—ã¾ã—ãŸ"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"ブックマークã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ã§ã™ã€‚å†åº¦è©¦ã™ã‹ã€ç®¡ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "ブックマークを追加ã™ã‚‹"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "ブックマークを追加ã™ã‚‹å‰ã«ãƒ­ã‚°ã‚¤ãƒ³ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "自分ã®ãƒ–ックマーク"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "ブックマークを編集ã™ã‚‹"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "ID「%sã€ã¨ãƒ–ックマークã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "ã“ã®ãƒ–ックマークã®ç·¨é›†ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "ブックマークã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼"
+
+#: www/history.php:61
+msgid "History"
+msgstr "履歴"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "%s ã®å±¥æ­´"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "アドレスã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "入力㮠XML ã‚’é–‹ã‘ã¾ã›ã‚“"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML エラー: %s 㮠%d 行目"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "del.icio.us ã‹ã‚‰ãƒ–ックマークをインãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "ã“ã®ãƒ–ックマークã¯æ—¢ã«é€ä¿¡ã—ã¾ã—ãŸã€‚"
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "ブックマークをインãƒãƒ¼ãƒˆã—ã¾ã—ãŸã€‚"
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr "ブックマークã®ã†ã¡ã„ãã¤ã‹ã¯æ—¢ã«ç™»éŒ²æ¸ˆã¿ã§ã™ã€‚"
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "ブックマーク発見: "
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "ブックマークをインãƒãƒ¼ãƒˆã—ã¾ã—ãŸ: "
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "ブラウザーã®ãƒ•ã‚¡ã‚¤ãƒ«ã‹ã‚‰ãƒ–ックマークをインãƒãƒ¼ãƒˆã™ã‚‹"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "構造ã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "ログアウトã—ã¾ã—ãŸ"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: 最近ã®ãƒ–ックマーク"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: 最近ã®ãƒ–ックマーク"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "ãŠæ°—ã«å…¥ã‚Šã®ãƒªãƒ³ã‚¯ã®ä¿å­˜ã€å…±æœ‰ã€ã‚¿ã‚°ä»˜ã‘ã‚’ã—ã¾ã™ã€‚"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "ã™ã¹ã¦ã®ãƒ–ックマーク"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "利用ã§ãã¾ã™"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "利用ã§ãã¾ã›ã‚“"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "入力ã—ãŸè©³ç´°ã¯æ­£ã—ãã‚ã‚Šã¾ã›ã‚“。å†åº¦è©¦ã—ã¦ãã ã•ã„。"
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "ユーザーåを入力ã—ã¦ãã ã•ã„。"
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"<abbr title=\"é›»å­ãƒ¡ãƒ¼ãƒ«\">é›»å­ãƒ¡ãƒ¼ãƒ«</abbr>アドレスを入力ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›"
+"ん。"
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "一致ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼åãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"一致ã™ã‚‹<abbr title=\"é›»å­ãƒ¡ãƒ¼ãƒ«\">é›»å­ãƒ¡ãƒ¼ãƒ«</abbr>アドレスã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼åã®çµ„"
+"ã¿åˆã‚ã›ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr "æ–°è¦ãƒ‘スワードã®ç”Ÿæˆä¸­ã«ã‚¨ãƒ©ãƒ¼ã§ã™ã€‚å†åº¦è©¦ã—ã¦ãã ã•ã„。"
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "æ–°ã—ã„パスワード:"
+
+#: 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 ""
+"ブックマークã®å®‰å…¨æ€§ã‚’維æŒã™ã‚‹ãŸã‚ã«ã€æ¬¡å›žã®ãƒ­ã‚°ã‚¤ãƒ³æ™‚ã«ãƒ—ロフィールã®ã“ã®ãƒ‘"
+"スワードを変ãˆã‚‹ã¹ãã§ã™ã€‚"
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%sアカウント情報"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "æ–°è¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’生æˆã—ã€%s ã«é€ä¿¡ã—ã¾ã—ãŸ"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "パスワード忘れ"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "ユーザーåãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "自分ã®ãƒ—ロフィール"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "パスワードã¨ç¢ºèªãƒ‘スワードãŒä¸€è‡´ã—ã¾ã›ã‚“。"
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "パスワードã¯å°‘ãªãã¨ã‚‚ 6 文字以上ã§ã™ã€‚"
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "é›»å­ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒæœ‰åŠ¹ã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "変更ã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ã§ã™ã€‚"
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "変更をä¿å­˜ã—ã¾ã—ãŸã€‚"
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "ã‚¿ã‚°ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"ユーザーåã€ãƒ‘スワードã€åå‰ã¨é›»å­ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯äºˆç´„ã•ã‚Œã¦ã„ã¾ã™ã€‚ã»ã‹ã®åå‰ã‚’é¸ã‚“ã§ãã ã•ã„。"
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚ã»ã‹ã®åå‰ã‚’é¸ã‚“ã§ãã ã•ã„。"
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯æ­£ã—ãã‚ã‚Šã¾ã›ã‚“ (短ã™ãŽã‚‹ã€é•·ã™ãŽã‚‹ã€ç¦æ­¢æ–‡å­—...)。他をé¸ã‚“"
+"ã§ãã ã•ã„。"
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "é›»å­ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯æœ‰åŠ¹ã§ã¯ã‚ã‚Šã¾ã›ã‚“。å†åº¦è©¦ã—ã¦ãã ã•ã„。"
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "スパム対策ã®å›žç­”ãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“。もã†ä¸€åº¦è©¦ã—ã¦ãã ã•ã„。"
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "登録ã«æˆåŠŸã—ã¾ã—ãŸã€‚楽ã—ã‚“ã§ãã ã•ã„!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "登録ã«å¤±æ•—ã—ã¾ã—ãŸã€‚å†åº¦è©¦ã—ã¦ãã ã•ã„。"
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "最近ã®ãƒ–ックマークを %s ã«æŠ•ç¨¿ã—ã¾ã—ãŸ"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "ブックマークを検索ã™ã‚‹"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "検索çµæžœ"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "リンクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "リンクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "タグを削除ã—ã¾ã—ãŸ"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "ã‚¿ã‚°ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "ã‚¿ã‚°ã®èª¬æ˜Žã‚’æ›´æ–°ã—ã¾ã—ãŸ"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "ã‚¿ã‚°ã®èª¬æ˜Žã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "ã‚¿ã‚°åを変更ã—ã¾ã—ãŸ"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "ã‚¿ã‚°åã®å¤‰æ›´ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "ユーザー"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "注目一覧ã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’削除ã—ã¾ã—ãŸ"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "注目一覧ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’追加ã—ã¾ã—ãŸ"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "自分ã®æ³¨ç›®ä¸€è¦§"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "Last Users"
+#~ msgstr "最後ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+#~ msgid "for"
+#~ msgstr "キーワード"
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "ユーザーåãŒã€Œ%sã€ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯è¦‹ã¤ã‘られã¾ã›ã‚“ã§ã—ãŸ"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "最近ã®ãƒ–ックマーク"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%sブックマーク"
+
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "%s を使ã„始ã‚ã‚‹ã«ã¯<strong><a href=\"register.php\">今ã™ã登録</a>ã—ã¦ãã "
+#~ "ã•ã„</strong>!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "%s 㯠<a href=\"http://sourceforge.net/projects/scuttle/\">オープンソース"
+#~ "</a>ã«åŸºã¥ã„ãŸã€<a href=\"http://www.gnu.org/copyleft/gpl.html\"><acronym "
+#~ "title=\"GNU's Not Unix\">GNU</acronym>General Public License</a> ã®å…ƒã§ãƒ©"
+#~ "イセンスã•ã‚Œã¦ã„ã¾ã™ã€‚ã“ã‚Œã¯ã€ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã‚„個人ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã€ã‚ã‚‹ã„ã¯"
+#~ "自分ã®ã‚³ãƒ³ãƒ”ュータやã€è‡ªåˆ†ã®ã‚¦ã‚§ãƒ–サーãƒã§ç„¡æ–™ã§é‹å–¶ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã“ã¨ã‚’"
+#~ "æ„味ã—ã¾ã™ã€‚"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"é›»å­ãƒ¡ãƒ¼ãƒ«\">é›»å­ãƒ¡ãƒ¼ãƒ«</abbr>"
diff --git a/data/locales/lt_LT/LC_MESSAGES/messages.mo b/data/locales/lt_LT/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..7b351f8
--- /dev/null
+++ b/data/locales/lt_LT/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/lt_LT/LC_MESSAGES/messages.po b/data/locales/lt_LT/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..023772f
--- /dev/null
+++ b/data/locales/lt_LT/LC_MESSAGES/messages.po
@@ -0,0 +1,1710 @@
+# Scuttle lt-LT Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Audrius RadzeviÄius <audrius@gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-03-26 14:26-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: lt-LT <audrius@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Lithuanian\n"
+"X-Poedit-Country: LITHUANIA\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() buvo iškviesta daug kartų."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "message_die() buvo iškviesta daug kartų."
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "EilutÄ—"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Failas"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Informacija"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "KritinÄ— informacija"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Įvyko klaida"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Bendro pobūdžio klaida"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Įvyko kritinė klaida"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "KritinÄ— klaida"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Derinimo rėžimas"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Kaupkite</strong> Jus dominanÄias žymes vienoje, bet kur "
+"pasiekiamoje vietoje."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>DalinkitÄ—s</strong> savo žymÄ—mis su visais ar į tik biÄiulių sÄ…raÅ¡Ä… "
+"įtrauktais draugais arba laikykite jas asmeniniam naudojimui."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Grupuokite</strong> savo žymes gairių pagalba. Jų galite sukurti "
+"tiek, kiek Jums reikia. PamirÅ¡kite nelanksÄius katalogus!"
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "Registracija"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "TechninÄ— informacija"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 ""
+"<strong>%1$s</strong> palaiko <a href=\"http://www.delicious.com/help/api"
+"\">del.icio.us <abbr title=\"Application Programming Interface\">API</abbr></"
+"a>. Daugumą šiai sistemai skirtų įrankių galiama pakoreguoti taip, kad jie "
+"veiktų su <strong>%1$s</strong>. Jei radote įrankį, kuriame negalima "
+"pakeisti API adreso, paprašykite jo kūrėjų tai padaryti. Jie tikrai "
+"neatsilaikys Jūsų kerams."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Vartotojas"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "žymė"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Ar tikrai?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Trinti"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instrukcija"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "pavadinimÄ…"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Aprašas"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Žymeklis"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Perkelkite šį žymeklįį Jūsų naršyklėje esantį žymių katalogą ir naudokite, "
+"kai norėsite pridėti naują puslapį į <strong>%s</strong>"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Perkelkite šį žymeklįį Jūsų naršyklėje esantį žymių katalogą ir naudokite, "
+"kai norėsite pridėti naują puslapį į <strong>%s</strong>"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Siųsti į <strong>%s</strong>"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Siųsti į <strong>%s</strong> (atskirame lange)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "RÅ«Å¡iuoti pagal:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "datÄ…"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "Pirmas"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Ankstesnis"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Kitas"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "Paskutinis"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "%d iš %d puslapių"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "GairÄ—s"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Keisti"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr " įrašė"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr " bei dar %s1 vartotojas%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " bei dar %2$s%1$s vartotojai%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Importuoti žymes iš žymių failo"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Kopijuoti"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Jūs neturite teisės keisti šią žymę"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Žymių nėra"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Apie"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "žymė"
+msgstr[1] "žymės"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Populiarios gairÄ—s"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Populiarios gairÄ—s"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Adresas"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Privaloma"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Privati"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "GairÄ—s"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Skiriamos kableliais"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Privatumas"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Vieša"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Tik biÄiuliams"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privati"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Trinti žymę"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importas"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importuoti žymes iš žymių failo"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox ir Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importuoti žymes iš del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Vardas"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "El. paštas"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Trinti"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Paskyris informacija"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Vartotojas"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Naujas slaptažodis"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Pakartoti slaptažodį"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "El. paštas"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Privati"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Generuoti slaptažodį"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Asmeniniai duomenys"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "SvetainÄ—"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "IÅ¡saugoti pakeitimus"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Veiksmai"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "mano žymėse"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "NÄ—ra"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "Užklausto saito darbo stotyje nėra"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Bendro pobūdžio darbo stoties klaida"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Užklausa negali būti įvykdyta"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Tik biÄiuliams"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instrukcija"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Prisijunkite prie <a href=\"http://del.icio.us/api/posts/all\">del.icio.us "
+"eksporto puslapio</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Išsaugoti <abbr title=\"Extensible Markup Language\">XML</abbr> failą Jūsų "
+"kompiuteryje"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Paspauskite <kbd>Browse...</kbd> ir raskite šį failą kompiuteryje. "
+"Maksimalus failo dydis - 1 MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "Pasirinkite bazines privatumo nuostatas importuojamoms žymėms"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Norėdami importuoti žymes, spauskite <kbd>Importuoti</kbd>; tai gali šiek "
+"tiek užtrukti"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Eksportuokite savo žymes iš naršyklės į failą"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Paspauskite <kbd>Browse...</kbd> ir raskite žymes savo kompiuteryje. "
+"Maksimalus failo dydis - 1MB."
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Slaptažodis"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Neklausti slaptažodžio dvi savaites"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Prisijungti"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Pamiršote slaptažodį?"
+
+#: data/templates/default/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 ""
+"Jei pamiršote savo slaptažodį, <strong>%s</strong> gali sukurti Jums naują. "
+"Įrašykite savo paskyros vartotojo vardą ir mums pateikto el. pašto adresą ir "
+"mes Jums persiųsime naująjį slaptažodį."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Generuoti slaptažodį"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Dalyvauja nuo"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Stebi"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Yra stebimas"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Žymės"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "mano žymėse"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"NorÄ—dami prisiregistruoti <strong>%s</strong> užlipdykite žemiau esanÄiÄ… "
+"anketÄ…"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registracija"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Ieškoti"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "EilutÄ—"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "šio vartotojo žymėse"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "mano žymėse"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "biÄiulių žymÄ—se"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "visose žymėse"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Ieškoti"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "SusijÄ™ gairÄ—s"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "Trinti"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "Naujos gairÄ—s"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "Populiarios gairÄ—s"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "SusijÄ™ gairÄ—s"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Naujos gairÄ—s"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "SusijÄ™ gairÄ—s"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "Ieškoti"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "Žymių nėra"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "SusijÄ™ gairÄ—s"
+msgstr[1] "SusijÄ™ gairÄ—s"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "Trinti"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "Aprašas"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "žymė"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "PaÅ¡alinti iÅ¡ biÄiulių sÄ…raÅ¡o"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "PaÅ¡alinti iÅ¡ biÄiulių sÄ…raÅ¡o"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Ä®traukti į biÄiulių sÄ…raÅ¡Ä…"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "Keisti"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Taip"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Ne"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "Vartotojas"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "AbÄ—cÄ—lÄ—"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Populiarumas"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "SvetainÄ—"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "BiÄiuliai"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profilis"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Pridėti žymę"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Atsijungti"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "Profilis"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Jums neturite teisės ištrinti šį žymę"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Nepavyko ištrinti žymės"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Visos gairÄ—s"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "Nepavyko rasti vartotojo vardu %s"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "Žymės ištrinti nepavyko"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Įrašoma žymė privalo turėti pavadinimą ir adresą"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Žymė išsaugota"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Saugant žymę įvyko klaida. Bandykite dar kartą arba kreipkitės į "
+"administratorių."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Pridėti žymę"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Jei norite pridėti naujas žymes, privalote prisijungti."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Mano žymės"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Keisti žymę"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Žymės su id %s nepavyko rasti"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Jūs neturite teisės keisti šią žymę"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Saugant žymę įvyko klaida"
+
+#: www/history.php:61
+msgid "History"
+msgstr "Istorija"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "%s istorija"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Adreso nepavyko rasti"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Nepavyko atidaryti XML įvesties"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML klaida: %s eilutÄ—je %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Importuoti žymes iš del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Jūs jau išsaugojote šią žymę."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Žymė importuota."
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Jūs jau išsaugojote šią žymę."
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "Žymių nėra"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Žymė importuota."
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Importuoti žymes iš naryklės failo"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "JÅ«s dabar atsijungÄ—te"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: naujos žymės"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: naujos žymės"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Kaupkite, dalinkites ir aprašinėkite savo svarbiausias interneto žymes"
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "visose žymėse"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Yra"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "NÄ—ra"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "Jūsų įrašyti duomenys neteisingi. Bandykite dar kartą."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Privalote įrašyti vartotojo vardą."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Privalote įrašyti savo <abbr title=\"elektroninis paštas\">el. pašto</abbr> "
+"adresÄ…."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Nepavyko rasti įrašų, susijusių su šiuo vartotoju."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Nepavyko rasti įrašų, susijusių su šiuo vartotojo vardu ir <abbr title="
+"\"elektroninis paštas\">el. pašto</abbr> adresu."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr "Generuojant naują Jūsų slaptažodį įvyko klaida. Bandykite dar kartą."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Jūsų naujasis slaptažodis yra:"
+
+#: 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 ""
+"Vardan Jūsų kaupiamos informacijos saugumo, rekomenduojame pasikeisti "
+"slaptažodį, kai kitą kartą prisijungsite prie vartotojo profilio."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s paskyros informacija"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Naujas slaptažodis sukurtas ir išsiųstas %s"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Užmirštas slaptažodis"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Nenurodytas vartotojo vardas"
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Mano profilis"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Įrašyti slaptažodžio variantai nesutampa."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Slaptažodis turi būti bent 6 simbolių ilgio."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Neteisingas el. pašto adresas."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Saugant pakeitimus įvyko klaida."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Pakeitimai išsaugoti."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"<em>Privalote</em> įrašyti vartotojo vardą, slaptažodį, vardą ir <abbr title="
+"\"elektroninis paštas\">el. pašto</abbr> adresą."
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr "Toks vartotojas jau yra, prašom pasirinkti kitą."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Toks vartotojas jau yra, prašom pasirinkti kitą."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr "Toks vartotojas jau yra, prašom pasirinkti kitą."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "El. pašto adresas neteisingas. Bandykite dar kartą."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "El. pašto adresas neteisingas. Bandykite dar kartą."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Sveikiname sėkmingai užsiregistravus!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Registracija nepavyko. Bandykite dar kartÄ…."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Naujos, %s išsiųstos gairės"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Ieškoti žymėse"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Paieškos rezultatai"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "Žymės ištrinti nepavyko"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "Žymės ištrinti nepavyko"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "Žymės ištrinti nepavyko"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "Žymės ištrinti nepavyko"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "Žymės ištrinti nepavyko"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "Vartotojas"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Vartotojas iÅ¡brauktas iÅ¡ biÄiulių sÄ…raÅ¡o"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Vartotojas įtrauktas į biÄiulių sÄ…raÅ¡Ä…"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Mano biÄiuliai"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "Nepavyko rasti vartotojo vardu %s"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "Naujos žymės"
+
+#~ msgid "for"
+#~ msgstr "teksto"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%s žymės"
+
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<strong><a href=\"register.php\">RegistruokitÄ—s dabar</a></strong> ir "
+#~ "pradÄ—kite darbÄ… su <strong>%s</strong>!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "<strong>%s</strong> veikia pagal <a href=\"http://www.gnu.org/copyleft/"
+#~ "gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> GPL</a> "
+#~ "licencijÄ… sukurto <a href=\"http://sourceforge.net/projects/scuttle/"
+#~ "\">atvirojo kodo projekto</a> pagrindu. Tai reiškia, kad Jūs galite šią "
+#~ "sistemÄ… nemokamai naudoti ir talpinti internete, vidaus tinkle ar savo "
+#~ "kompiuteryje."
+
+#~ msgid "URL"
+#~ msgstr "adresÄ…"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Elektroninis paštas\">El. paštas</abbr>"
diff --git a/data/locales/messages.po b/data/locales/messages.po
new file mode 100644
index 0000000..6276a8b
--- /dev/null
+++ b/data/locales/messages.po
@@ -0,0 +1,1575 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 ""
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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/default/bookmarklet.inc.php:20
+#, 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/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr ""
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr ""
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+msgid "delete"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:32
+msgid "Private RSS Feed"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+msgid "Generate New Key"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr ""
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr ""
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:33
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr ""
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr ""
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr ""
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr ""
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr ""
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr ""
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr ""
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr ""
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr ""
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr ""
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr ""
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr ""
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr ""
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr ""
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] ""
+msgstr[1] ""
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr ""
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr ""
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr ""
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr ""
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr ""
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr ""
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr ""
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr ""
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr ""
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr ""
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr ""
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr ""
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr ""
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr ""
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr ""
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr ""
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr ""
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr ""
+
+#: www/history.php:61
+msgid "History"
+msgstr ""
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr ""
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr ""
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr ""
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr ""
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr ""
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr ""
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr ""
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr ""
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr ""
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr ""
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr ""
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr ""
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr ""
+
+#: www/index.php:58
+#, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr ""
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr ""
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr ""
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr ""
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr ""
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr ""
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr ""
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr ""
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr ""
+
+#: 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 ""
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr ""
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr ""
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr ""
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr ""
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr ""
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr ""
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr ""
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr ""
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr ""
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr ""
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr ""
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr ""
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr ""
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr ""
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr ""
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr ""
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr ""
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr ""
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr ""
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr ""
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr ""
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr ""
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr ""
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr ""
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr ""
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr ""
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr ""
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr ""
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
diff --git a/data/locales/nl_NL/LC_MESSAGES/messages.mo b/data/locales/nl_NL/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..34b8e34
--- /dev/null
+++ b/data/locales/nl_NL/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/nl_NL/LC_MESSAGES/messages.po b/data/locales/nl_NL/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..d84bfbc
--- /dev/null
+++ b/data/locales/nl_NL/LC_MESSAGES/messages.po
@@ -0,0 +1,1709 @@
+# Scuttle nl-NL Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# <alley@ilikeu2.nl>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-03-18 21:46-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: nl-NL <alley@ilikeu2.nl>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Dutch\n"
+"X-Poedit-Country: NETHERLANDS\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Bestand"
+
+#: src/SemanticScuttle/functions.php:215
+#, fuzzy
+msgid "Information"
+msgstr "Instructies"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr ""
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:7
+#, fuzzy
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"Deel je bookmarks met iedereen, met gebruikers op jouw 'watch-list' of hou "
+"ze gewoon voor jezelf"
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 ""
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Gebruikersnaam"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "%s Bookmarks"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Verwijderen"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instructies"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Titel"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Omschrijving"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Sleep een van de volgende 'bookmarklets' naar de link-balk van je browser, "
+"en klik er vervolgens op als je je op een pagina bevind die je aan %s wilt "
+"toevoegen"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Sleep een van de volgende 'bookmarklets' naar de link-balk van je browser, "
+"en klik er vervolgens op als je je op een pagina bevind die je aan %s wilt "
+"toevoegen"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Plaats op %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Plaats op %s (pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Datum"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Pagina %d van %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Tags"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Bewerken"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Importeer bookmarks uit een bookmark bestand"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Kopi&euml;ren"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Geen bookmarks beschikbaar"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Over ons"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+#, fuzzy
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "%s Bookmarks"
+msgstr[1] "%s Bookmarks"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Populaire Tags"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Populaire Tags"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Adres"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Verplicht"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Priv&egrav;"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Tags"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Door comma's gescheiden"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Privacy"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Publiekelijk"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Gedeeld met 'watch list'"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Priv&egrav;"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Bookmark Verwijderen"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importeren"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importeer bookmarks uit een bookmark bestand"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox en Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importeer van del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Naam"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "Email"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Verwijderen"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Gebruikersnaam"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nieuw wachtwoord"
+
+#: data/templates/default/editprofile.tpl.php:22
+#, fuzzy
+msgid "Confirm Password"
+msgstr "Bevestiging nieuw wachtwoord"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "Email"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Priv&egrav;"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Nieuw wachtwoord"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Homepage"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Veranderingen opslaan"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "Instructies"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "Mijn bookmarks"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+#, fuzzy
+msgid "Not Found"
+msgstr "Gebruiker werd niet gevonden"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr ""
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr ""
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr ""
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Gedeeld met 'watch list'"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instructies"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Log in op de <a href=\"http://del.icio.us/api/posts/all\">export pagina op "
+"del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Sla het resulterende <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"bestand op, op de locale computer"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Klik vervolgens op <kbd>Bladeren...</kbd> om dit opgeslagen bestand op de "
+"computer te vinden. De maximum toegelaten bestandsgrootte op onze server is "
+"1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+"Selecteer welke privacy instelling moet worden toegepast op de ge&iuml;"
+"mporteerde bookmarks"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Klik op <kbd>Importeren</kbd> om het importeren te starten. Dit kan enkele "
+"minuten duren."
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Bookmarks exporteren vanuit een browser"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Klik vervolgens op <kbd>Bladeren...</kbd> om het opgeslagen bookmark bestand "
+"op de computer te vinden. De maximum toegelaten bestandsgrootte op onze "
+"server is 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Wachtwoord"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Wachtwoord de komende 2 weken niet nog een keer vragen"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Inloggen"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/password.tpl.php:19
+#, fuzzy
+msgid "Generate Password"
+msgstr "Nieuw wachtwoord"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Lid sinds"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+#, fuzzy
+msgid "Watching"
+msgstr "Watch List"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+#, fuzzy
+msgid "Watched By"
+msgstr "Watched"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+#, fuzzy
+msgid "Bookmarks"
+msgstr "%s Bookmarks"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "Mijn bookmarks"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr ""
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr ""
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr ""
+
+#: data/templates/default/search.menu.php:34
+#, fuzzy
+msgid "this user's bookmarks"
+msgstr "%s Bookmarks"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "Mijn bookmarks"
+
+#: data/templates/default/search.menu.php:40
+#, fuzzy
+msgid "my watchlist"
+msgstr "Voeg toe aan 'watch-list'"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "Alle bookmarks"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "Gerelateerde Tags"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "Verwijderen"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "Gerelateerde Tags"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "Populaire Tags"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Gerelateerde Tags"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+#, fuzzy
+msgid "Recent Tags"
+msgstr "Gerelateerde Tags"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Gerelateerde Tags"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr ""
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "Geen bookmarks beschikbaar"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Gerelateerde Tags"
+msgstr[1] "Gerelateerde Tags"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "Verwijderen"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "Omschrijving"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "%s Bookmarks"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Van 'watch-list' verwijderen"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Van 'watch-list' verwijderen"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Voeg toe aan 'watch-list'"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "Bewerken"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "Gebruikersnaam"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr ""
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+#, fuzzy
+msgid "Popularity"
+msgstr "Populaire Tags"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "Homepage"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+#, fuzzy
+msgid "Watchlist"
+msgstr "Watch List"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Profiel"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Voeg een bookmark toe"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr ""
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "Profiel"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+#, fuzzy
+msgid "You are not allowed to delete this bookmark"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+#, fuzzy
+msgid "Failed to delete bookmark"
+msgstr "Bookmark Verwijderen"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Alle tags"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, fuzzy, php-format
+msgid "User with username %s was not found"
+msgstr "Bookmark werd niet gevonden"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Je bookmarks moeten een titel en adres hebben"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Bookmark opgeslagen"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Bookmark toevoegen"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Je moet ingelogd zijn om bookmarks toe te kunnen voegen"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+#, fuzzy
+msgid "My Bookmarks"
+msgstr "%s Bookmarks"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Bookmark Bewerken"
+
+#: www/edit.php:51
+#, fuzzy, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Bookmark werd niet gevonden"
+
+#: www/edit.php:57
+#, fuzzy
+msgid "You are not allowed to edit this bookmark"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr ""
+
+#: www/history.php:61
+msgid "History"
+msgstr ""
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr ""
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr ""
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "XML bestand kon niet geopend worden"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML fout: %s op regel %d"
+
+#: www/import.php:60
+#, fuzzy
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Importeer van del.icio.us"
+
+#: www/import.php:93
+#, fuzzy
+msgid "You have already submitted this bookmark."
+msgstr "Je hebt deze bookmark al opgenomen"
+
+#: www/import.php:104
+#, fuzzy
+msgid "Bookmark imported."
+msgstr "Bookmark opgeslagen"
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Je hebt deze bookmark al opgenomen"
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "Geen bookmarks beschikbaar"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Bookmark opgeslagen"
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+#, fuzzy
+msgid "Import Bookmarks from Browser File"
+msgstr "Importeer bookmarks uit een bookmark bestand"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Je bent uitgelogd"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Recente bookmarks"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Recente bookmarks"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr ""
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "Alle bookmarks"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr ""
+
+#: www/jsScuttle.php:74
+#, fuzzy
+msgid "Not Available"
+msgstr "Geen bookmarks beschikbaar"
+
+#: www/login.php:48
+#, fuzzy
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "De ingevoerde details zijn niet correct. Probeer het nog eens."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr ""
+
+#: www/password.php:40
+#, fuzzy
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Een gebruikersnaam, wachtwoord, naam en <abbr title=\"Electronic mail"
+"\">email</abbr> adres <em>moeten</em> worden opgegeven"
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr ""
+
+#: www/password.php:51
+#, fuzzy
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Een gebruikersnaam, wachtwoord, naam en <abbr title=\"Electronic mail"
+"\">email</abbr> adres <em>moeten</em> worden opgegeven"
+
+#: www/password.php:59
+#, fuzzy
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr "De ingevoerde details zijn niet correct. Probeer het nog eens."
+
+#: www/password.php:63
+#, fuzzy
+msgid "Your new password is:"
+msgstr "Bevestiging nieuw wachtwoord"
+
+#: 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 ""
+
+#: www/password.php:66
+#, fuzzy, php-format
+msgid "%s Account Information"
+msgstr "Instructies"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr ""
+
+#: www/password.php:75
+#, fuzzy
+msgid "Forgotten Password"
+msgstr "Nieuw wachtwoord"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr ""
+
+#: www/profile.php:70
+#, fuzzy
+msgid "My Profile"
+msgstr "Profiel"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+#, fuzzy
+msgid "Password and confirmation do not match."
+msgstr "Wachtwoord en wachtwoordbevestiging komen niet overen"
+
+#: www/profile.php:119
+#, fuzzy
+msgid "Password must be at least 6 characters long."
+msgstr "Wachtwoord dient minimaal 6 karakters lang te zijn"
+
+#: www/profile.php:123
+#, fuzzy
+msgid "E-mail address is not valid."
+msgstr "Email adres is ongeldig"
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr ""
+
+#: www/profile.php:129
+#, fuzzy
+msgid "Changes saved."
+msgstr "Wijzigingen opgeslagen"
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+#, fuzzy
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Een gebruikersnaam, wachtwoord, naam en <abbr title=\"Electronic mail"
+"\">email</abbr> adres <em>moeten</em> worden opgegeven"
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr ""
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr ""
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+
+#: www/register.php:68
+#, fuzzy
+msgid "E-mail address is not valid. Please try again."
+msgstr "Email adres is ongeldig"
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Email adres is ongeldig"
+
+#: www/register.php:90
+#, fuzzy
+msgid "You have successfully registered. Enjoy!"
+msgstr "De bookmark is succesvol verwijderd"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr ""
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, fuzzy, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Recente bookmarks"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Bookmarks zoeken"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr ""
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "De bookmark kon niet worden verwijderd"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "Gebruikersnaam"
+
+#: www/watch.php:54
+#, fuzzy
+msgid "User removed from your watchlist"
+msgstr "Van 'watch-list' verwijderen"
+
+#: www/watch.php:56
+#, fuzzy
+msgid "User added to your watchlist"
+msgstr "Voeg toe aan 'watch-list'"
+
+#: www/watchlist.php:104
+#, fuzzy
+msgid "My Watchlist"
+msgstr "Watch List"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "Recente bookmarks"
+
+#~ msgid "for"
+#~ msgstr "voor"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%s Bookmarks"
+
+#, fuzzy
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<a href=\"register.php\">Registreer nu</a> om je eigen bookmarks op te "
+#~ "slaan"
diff --git a/data/locales/pt_BR/LC_MESSAGES/messages.mo b/data/locales/pt_BR/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..5a380cf
--- /dev/null
+++ b/data/locales/pt_BR/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/pt_BR/LC_MESSAGES/messages.po b/data/locales/pt_BR/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..7962ccb
--- /dev/null
+++ b/data/locales/pt_BR/LC_MESSAGES/messages.po
@@ -0,0 +1,1773 @@
+# Scuttle pt-BR Translation
+# Copyright (C) 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Marcelo Jorge Vieira (metal) <metal@alucinados.com>, 2006.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-04-06 17:26-0300\n"
+"Last-Translator: Daniel duende Carvalho <daniel.carvalho@gmail.com>\n"
+"Language-Team: Portuguese/Brazil\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() foi chamada múltiplas vezes"
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Erro de SQL"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Linha"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Arquivo"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Informações"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Informações Importantes"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Um erro ocorreu"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Erro Geral"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Um erro crítico ocorreu"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Erro Crítico"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "MODO DEBUG"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Agregue</strong> todos os seus links favoritos em um só local, "
+"acessível de qualquer lugar."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Compartilhe</strong> seus bookmarks com todo o mundo, com amigos em "
+"sua lista de observação ou guarde-os apenas para você."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Identifique</strong> seus bookmarks com tantos tags quanto quiser, "
+"em vez de lutar contra pastas e diretórios."
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "Registre-se"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Coisas de Geek"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 ""
+"O %1$s suporta a maior parte do <a href=\"http://www.delicious.com/help/api"
+"\"><abbr title=\"Application Programming Interface\">API</abbr> do del.icio."
+"us</a>. Quase todas as adoráveis ferramentas desenvolvidas para aquele "
+"sistema podem ser modificadas para funcionar com o %1$s. Se você encontrar "
+"uma ferramenta que não permita a mudança de seu endereço de API, solicite a "
+"seu criador para que adicione esta configuração. Nunca se sabe. Ele pode "
+"simplesmente atender a seu pedido."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "Nome de Usuário"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "bookmark"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Você tem certeza disso!?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Apagar"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "Instruções"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Título"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Descrição"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Gerador de Bookmarks"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Arraste um dos seguintes Geradores de Bookmark para a área de favoridos de "
+"seu navegador. Clique nele toda vez que quiser adicionar a página na qual "
+"você se encontra ao %s"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Arraste um dos seguintes Geradores de Bookmark para a área de favoridos de "
+"seu navegador. Clique nele toda vez que quiser adicionar a página na qual "
+"você se encontra ao %s"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Postar para %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Postar para %s (versão Pop-Up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Ordenar por:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Data"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "Anterior"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Próximo"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Página %d de %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "Tags (Rótulos)"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Editar"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, fuzzy, php-format
+msgid " and %s1 other%s"
+msgstr "e %s outros"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr "e %s outros"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Importar bookmarks do arquivo de bookmarks"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copiar"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "Você não tem permissão para editar este bookmark"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Nenhum bookmark disponível"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Sobre"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+#, fuzzy
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "bookmark"
+msgstr[1] "bookmark"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Tags populares"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "Tags populares"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Endereço"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Requerido"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "Privado"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Tags (Rótulos)"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Separados por vírgula"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Privacidade"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Público"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Partilhado com a sua Lista de Observação"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Privado"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Apagar Bookmark"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Importar"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Importar bookmarks do arquivo de bookmarks"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox and Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Importar bookmarks do del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Nome"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Apagar"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Nome de Usuário"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Nova Senha"
+
+#: data/templates/default/editprofile.tpl.php:22
+#, fuzzy
+msgid "Confirm Password"
+msgstr "Confirme a Nova Senha"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Privado"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Gerar Senha"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Página na Web"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Salva Alterações"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "Instruções"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "Seus Bookmarks"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Não Encontrado"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "A URL requisitada não foi encontrada neste servidor"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Erro Geral de servidor"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "A URL requisitada não pôde ser processada"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "Partilhado com a sua Lista de Observação"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Instruções"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Entrar na <a href=\"http://del.icio.us/api/posts/all\">página de exportação "
+"do del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Salvar o arquivo <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"resultante em seu computador."
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Clique em <kbd>Navegar...</kbd>para encontrar este arquivo no seu "
+"computador. O tamanho máximo que o arquivo pode ter é 1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+"Selecione o status de privacidade padrão para os seus bookmarks importados"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Clique em <kbd>Importar</kbd> para começar a importar os bookmarks. Isso "
+"pode levar um minutinho..."
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Exporte seus bookmarks de seu navegador para um arquivo"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>Arquivo &gt; Importar e Exportar... &gt; Exportar "
+"Favoritos"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Gerenciar Bookmarks... &gt; Arquivo "
+"&gt; Exportar..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bookmarks &gt; Gerenciar Bookmarks... &gt; Ferramentas &gt; "
+"Exportar..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Clique <kbd>Procurar...</kbd> para encontrar o arquivo de bookmarks gravado "
+"em seu computador. O tamanho máximo permitido para o arquivo é 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Senha"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Não pergunte minha senha por duas semanas"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Entrar no sistema"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Esqueceu a sua senha?"
+
+#: data/templates/default/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 ""
+"Se você esqueceu a sua senha, o %s pode gerar uma nova para você. Digite o "
+"seu nome de usuário e o seu endereço de e-mail no formulário abaixo e nós "
+"enviaremos a nova senha para seu e-mail."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Gerar Senha"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Membro Desde"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+#, fuzzy
+msgid "Watching"
+msgstr "Lista de Observação"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Observado Por"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+#, fuzzy
+msgid "Bookmarks"
+msgstr "%s Bookmarks"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "%s bookmarks"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Cadastre-se aqui para criar uma conta gratuita no %s. Todas as informações "
+"solicitadas abaixo são necessárias."
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Registre-se"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "Busca"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "Linha"
+
+#: data/templates/default/search.menu.php:34
+#, fuzzy
+msgid "this user's bookmarks"
+msgstr "%s bookmarks"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "meus bookmarks"
+
+#: data/templates/default/search.menu.php:40
+#, fuzzy
+msgid "my watchlist"
+msgstr "Adicionar à Lista de Observação"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "todos os bookmarks"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Busca"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "Tags Relacionadas"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "Apagar"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "Minhas Tags"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "Tags Populares Gerais"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Tags Relacionadas"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Tags Recentes"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Tags Relacionadas"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "Busca"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "Nenhum bookmark encontrado"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Tags Relacionadas"
+msgstr[1] "Tags Relacionadas"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "Apagar"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "Descrição"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "bookmark"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "Remover da Lista de Observação"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Remover da Lista de Observação"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Adicionar à Lista de Observação"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "Editar"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Sim"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Não"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "Nome de Usuário"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Alfabeto"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popularidade"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "Página na Web"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+#, fuzzy
+msgid "Watchlist"
+msgstr "Lista de Observação"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Perfil"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Adicione um Bookmark"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Sair do Sistema"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "Perfil"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Você não tem permissão para apagar este bookmark"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Falha ao apagar bookmark"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Todos os Tags"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, fuzzy, php-format
+msgid "User with username %s was not found"
+msgstr "O Nome de Usuário %s não foi encontrado"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "Falha ao apagar o bookmark"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "O seu bookmark deve possuir um título e um endereço"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Bookmark gravado"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Ocorreu um erro na gravação do seu bookmark. Por favor tente novamente ou "
+"entre em contato com o administrador."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Adicionar Bookmark"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Você deve dar entrada no sistema para poder adicionar bookmarks"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Meus Bookmarks"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Editar Bookmark"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "O Bookmark com a identificação %s não foi encontrado"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Você não tem permissão para editar este bookmark"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Erro durante a gravação do seu bookmark"
+
+#: www/history.php:61
+msgid "History"
+msgstr ""
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr ""
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr ""
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Não foi possível abrir a entrada XML"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "Erro de XML: %s na linha %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Importar Bookmarks do del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Você já submeteu este Bookmark."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Bookmark importado."
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "Você já submeteu este Bookmark."
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "Nenhum bookmark encontrado"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "Bookmark importado."
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Importar Bookmarks (Favoritos) do Arquivo do Navegador"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Você saiu do sistema"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Bookmarks recentes"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Bookmarks recentes"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr ""
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Todos os Bookmarks"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr ""
+
+#: www/jsScuttle.php:74
+#, fuzzy
+msgid "Not Available"
+msgstr "Nenhum bookmark disponível"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr ""
+"Os detalhes submetidos por você estão incorretos. Por favor tente novamente."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Você tem que digitar o seu nome de usuário"
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Você tem que digitar o seu endereço de <abbr title=\"electronic mail\">e-"
+"mail</abbr>."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Nenhum resultado encontrado pra este nome de usuário"
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Nenhum resultado encontrado para esta combinação de nome de usuário e "
+"endereço de <abbr title=\"electronic mail\">e-mail</abbr> "
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Ocorreu um erro durante a geração de sua nova senha. Por favor tente "
+"novamente."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "A sua nova senha é:"
+
+#: 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 ""
+"Para manter seguros os seus bookmarks, você deveria alterar esta senha (em "
+"seu perfil) na próxima vez que entrar no sistema."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "Informações da Conta de %s"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Nova senha gerada e enviada para %s"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Esquecí a Senha"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "O nome de usuário não foi especificado"
+
+#: www/profile.php:70
+#, fuzzy
+msgid "My Profile"
+msgstr "Perfil"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr ""
+"A senha digitada não é idêntica àquela digitada no campo de confirmação de "
+"senha."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "A senha deve ter um mínimo de 6 caracteres."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "O endereço de e-mail fornecido não é válido."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Um erro ocorreu durante a gravação das suas alterações."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Alterações gravadas."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+#, fuzzy
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Você <em>deve</em> digitar um nome de usuário, uma senha, seu nome e "
+"endereço de <abbr title=\"electronic mail\">e-mail</abbr>."
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr ""
+"Este nome de usuário já existe. Por favor, escolha outro nome de usuário."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr ""
+"Este nome de usuário já existe. Por favor, escolha outro nome de usuário."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"Este nome de usuário já existe. Por favor, escolha outro nome de usuário."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Endereço de email inválido. Por favor tente novamente."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Endereço de email inválido. Por favor tente novamente."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Você completou o seu registro. Agora divirta-se!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Falha ao registrar-se. Por favor tente novamente."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Bookmarks recentemente postados para %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Busca Bookmarks"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Resultados da Busca"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "Falha ao apagar o bookmark"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "Falha ao apagar o bookmark"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "Falha ao apagar o bookmark"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "Falha ao apagar o bookmark"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "Falha ao apagar o bookmark"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "Nome de Usuário"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Usuário removido da sua lista de observação"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Usuário adicionado à sua lista de observação"
+
+#: www/watchlist.php:104
+#, fuzzy
+msgid "My Watchlist"
+msgstr "Meus Observados"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "Online Bookmarks Manager"
+#~ msgstr "Gerenciador Online de Bookmarks"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "Bookmarks Recentes"
+
+#~ msgid "You are already logged in."
+#~ msgstr "Você já entrou no sistema."
+
+#~ msgid "%s Profile"
+#~ msgstr "Perfil de %s"
+
+#~ msgid "Your name and e-mail address are required and must be completed."
+#~ msgstr ""
+#~ "Seu nome e endereço de email são requeridos e devem ser preenchidos."
+
+#~ msgid "my watched bookmarks"
+#~ msgstr "meus bookmarks observados"
+
+#~ msgid "for"
+#~ msgstr "por"
+
+#~ msgid "Watched Bookmarks"
+#~ msgstr "Bookmarks Observados"
+
+#~ msgid "%s Watched Bookmarks"
+#~ msgstr "%s Bookmarks Observados"
+
+#~ msgid "Your"
+#~ msgstr "Seu"
+
+#~ msgid ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<strong><a href=\"register.php\">Registre-se agora</a></strong> para "
+#~ "começar a usar o %s!"
+
+#~ msgid ""
+#~ "%s is based on <a href=\"http://sourceforge.net/projects/scuttle/\">an "
+#~ "open-source project</a> licensed under the <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public License</a>. This means you can host it on your own web "
+#~ "server for free, whether it is on the Internet, a private network or just "
+#~ "your own computer."
+#~ msgstr ""
+#~ "O %s é baseado em <a href=\"http://sourceforge.net/projects/scuttle/\">um "
+#~ "projeto open source</a> licenciado sob a <a href=\"http://www.gnu.org/"
+#~ "copyleft/gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> "
+#~ "General Public Licence</a>. Isso significa que você pode hospedá-lo em "
+#~ "seu próprio servidor de rede gratuitamente, esteja ele na Internet, em "
+#~ "uma rede privada ou apenas em seu computador."
+
+#~ msgid "URL"
+#~ msgstr "URL"
+
+#~ msgid " and 1 other"
+#~ msgstr "e 1 outro"
+
+#~ msgid "Input the default tags you want added to your imported bookmarks"
+#~ msgstr ""
+#~ "Digite as tags padrão que você deseja que sejam adicionadas aos bookmarks "
+#~ "importados"
+
+#~ msgid "Show"
+#~ msgstr "Mostrar"
+
+#~ msgid "My Popular Tags"
+#~ msgstr "Minhas Tags Populares"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Correio Eletrônico\">E-mail</abbr>"
+
+#~ msgid "Watched"
+#~ msgstr "Observado"
+
+#~ msgid "All Recent"
+#~ msgstr "Todos os Recentes"
diff --git a/data/locales/ru_RU/LC_MESSAGES/messages.mo b/data/locales/ru_RU/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..8a76aa0
--- /dev/null
+++ b/data/locales/ru_RU/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/ru_RU/LC_MESSAGES/messages.po b/data/locales/ru_RU/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..d3f47af
--- /dev/null
+++ b/data/locales/ru_RU/LC_MESSAGES/messages.po
@@ -0,0 +1,1664 @@
+# SemanticScuttle ru_RU Translation
+# Copyright (C) 2005 - 2009 SemanticScuttle project
+# This file is distributed under the same license as the SemanticScuttle package.
+# Sergey Ulitin <serge.ulitin@gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Semantic Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2010-04-30 02:35+0400\n"
+"Last-Translator: Sergey Ulitin <serge.ulitin@gmail.com>\n"
+"Language-Team: Sergey Ulitin <serge.ulitin@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-Language: Russian\n"
+"X-Poedit-Country: Russia\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() было вызвано неÑколько раз."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Ошибка SQL"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Line"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "Файл"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "ИнформациÑ"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Ð’Ð°Ð¶Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Произошла ошибка"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "ÐžÐ±Ñ‰Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Произошла критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "КритичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Режим DEBUG"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>Храните</strong> Ваши любимые ÑÑылки в одной меÑте, доÑтупном "
+"отовÑюду."
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>ДелитеÑÑŒ</strong> ÑÑылками Ñо вÑем миром, Ñ Ð’Ð°ÑˆÐ¸Ð¼Ð¸ друзьÑми или "
+"оÑтавьте их приватными."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Помечайте Ñ‚Ñгами</strong> Ваши ÑÑылки Ñ Ñ‚Ð°ÐºÐ¸Ð¼ количеÑтвом меток "
+"Ñколько хотите, вмеÑто того чтобы боротьÑÑ Ñо Ñвалкой ÑÑылок."
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "ЗарегиÑтрируйтеÑÑŒ ÑейчаÑ"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr " чтобы начать пользоватьÑÑ %s!"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Geek Stuff"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "лицензировано по"
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr "Ð’Ñ‹ можете Ñвободно уÑтанавливать его на Ñвой веб-Ñервер."
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 поддерживает большинÑтво <a href=\"http://www.delicious.com/help/api"
+"\">del.icio.us <abbr title=\"Application Programming Interface\">API</abbr></"
+"a>. БольшинÑтво инÑтрументов Ñделаных Ð´Ð»Ñ Ñтой ÑиÑтемы, могут быть изменены "
+"Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ %1$s. ЕÑли Ð’Ñ‹ нашли инÑтрумент, который не позволÑет изменить "
+"Ð°Ð´Ñ€ÐµÑ API, попроÑите авторов добавить Ñту наÑтройку. Возможно они Ñто "
+"Ñделают."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr "ПодÑказки"
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Добавить плагин поиÑка в браузер:"
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+"Секретный Ñ‚Ñг \"system:unfiled\" позволÑет Вам находить закладки без Ñ‚Ñгов."
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+"Секретный Ñ‚Ñг \"system:imported\" позволÑет Вам находить импортированные "
+"закладки."
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr "Управление пользователем"
+
+#: data/templates/default/admin.tpl.php:14
+#, fuzzy
+msgid "Public/Shared/Private"
+msgstr "Публичный/Общий/Приватный"
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "закладка(и)"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Вы уверены?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Удалить"
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr "Другие дейÑтвиÑ"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr "Проверить вÑе ÑÑылки (может занÑÑ‚ÑŒ некоторое времÑ)"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+#, fuzzy
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+"СовмеÑтное опиÑание: Ñти Ð¿Ð¾Ð»Ñ Ð¼Ð¾Ð³ÑƒÑ‚ быть проÑмотрены и изменены любым "
+"пользователем"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Ðазвание"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "ОпиÑание"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "ПоÑледнее изменение:"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Имзенить"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Отмена"
+
+#: data/templates/default/bookmarklet.inc.php:1
+#, fuzzy
+msgid "Bookmarklet"
+msgstr "Закладочник"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Кликните на один из букмарклетов чтобы добавить кнопку, на которую Вы можете "
+"нажать в любой момент когда захотите добавить Ñтраницу на которой "
+"находитеÑÑŒ в %s"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, fuzzy, 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 ""
+"ПеретÑните мышкой bookmarklets в закладки Вашего браузера и нажмите на него, "
+"в любой момент когда захотите добавить Ñтраницу на которой находитеÑÑŒ в %s"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Добавить в %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Добавить в %s (pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+#, fuzzy
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr "Закладки на Ñтой Ñтранице управлÑÑŽÑ‚ÑÑ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором."
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+#, fuzzy
+msgid "Edit the common description of this tag"
+msgstr "Изменить опиÑание Ñтого Ñ‚Ñга"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr "Изменить опиÑание Ñтой закладки"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "Изменить личное опиÑание Ñтой закладки"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Сортировать по:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Дате"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "СÑылки других пользователей Ñ Ñтим Ñ‚Ñгом"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "Только Ваши ÑÑылки Ñ Ñтим Ñ‚Ñгом"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "ПерваÑ"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "ПредыдущаÑ"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "СледующаÑ"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "ПоÑледнÑÑ"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Страница%d из %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr "ТÑги:"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Изменить"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr "ПоÑледнее изменение"
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr " добавлено"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "Вами"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, fuzzy, php-format
+msgid " and %s1 other%s"
+msgstr " и %s1 другие%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " и %2$s%1$s другими%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Скопировать Ñту ÑÑылку закладку в Ð’ÐШИ закладки"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Копировать"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr "Эта закладка заверена админиÑтрацией."
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr "Ð›Ð¸Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ° к Ñтой закладке"
+
+#: data/templates/default/bookmarks.tpl.php:444
+#, fuzzy
+msgid "Come back to the top of this page."
+msgstr "ВернутьÑÑ Ð½Ð°Ð²ÐµÑ€Ñ… Ñтой Ñтраницы."
+
+#: data/templates/default/bookmarks.tpl.php:444
+#, fuzzy
+msgid "Top of the page"
+msgstr "Верх Ñтраницы"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Ðи одной закладки недоÑтупно"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "О Ñайте"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Propulsed by "
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "закладка"
+msgstr[1] "закладки"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "ПопулÑрные Ñ‚Ñги"
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "ПопулÑрные Ñ‚Ñги вÑех пользователей"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "ÐдреÑ"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+#, fuzzy
+msgid "Required"
+msgstr "ОбÑзательно"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Добавить заметку"
+
+#: data/templates/default/editbookmark.tpl.php:61
+#, fuzzy
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+"Ð’Ñ‹ можете иÑпользовать \"анкоры\" чтобы разделать атрибуты, например: "
+"[publisher]blah[/publisher] "
+
+#: data/templates/default/editbookmark.tpl.php:64
+#, fuzzy
+msgid "Suggested anchors: "
+msgstr "Предлагаемые анкоры: "
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "Ð›Ð¸Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr "Видимо только Ð´Ð»Ñ Ð’Ð°Ñ Ð¸ Ваших контактов."
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "ТÑги"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Разделенный запÑтыми"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+"Внимание: иÑпользуйте \">\" чтобы включить один Ñ‚Ñг в другой. например: "
+"европа>роÑÑиÑ>моÑква"
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+"Внимание: иÑпользуйте \"=\" чтобы Ñделать два Ñ‚Ñга Ñинонимами. например: "
+"роÑÑиÑ=рф"
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "КонфиденциальноÑÑ‚ÑŒ"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Публичный"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Shared with Watchlist"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Личный"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Удалить закладку"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "изменит опиÑание"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Импорт"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Импортировать закладки из файла закладок"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox и Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Импортировать закладки из del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "ИмÑ"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr "Email"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Удалить"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Данные аккаунта"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Ðовый пароль"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Подтвердите пароль"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Ð›Ð¸Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Сгенерировать пароль"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Личные данные"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "ДомашнÑÑ Ñтраница"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Сохранить изменениÑ"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "ДейÑтвиÑ"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "ЭкÑпорт закладок"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "HTML файл (Ð´Ð»Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð²)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "XML файл (как del.icio.us)"
+
+#: data/templates/default/editprofile.tpl.php:70
+#, fuzzy
+msgid "CSV file (for spreadsheet tools)"
+msgstr "CSV файл (Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ‡Ð½Ñ‹Ñ… процеÑÑоров)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Ðе найдено"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "Запрашиваемый Ð°Ð´Ñ€ÐµÑ Ð½Ðµ найден на Ñервере"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Ошибка Ñервера"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Запрашиваемый Ð°Ð´Ñ€ÐµÑ Ð½Ðµ может быть обработан"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "Открыть Ð´Ð»Ñ Ð»Ð¸Ñта наблюдениÑ"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "ИнÑтрукции"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Войдите на <a href=\"http://del.icio.us/api/posts/all\">Ñтраницу ÑкÑпорта на "
+"del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Сохраните полученный<abbr title=\"Extensible Markup Language\">XML</abbr> "
+"файл на Ваш компьютер"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Yf;vbnt <kbd>ПроÑмотр...</kbd> чтобы выбрать файл на Вашем компьютере. "
+"МакÑимальный размер файла - 1MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+"Выберете уровень доÑтупа, который будет применÑÑ‚ÑŒÑÑ Ð¿Ð¾ умолчанию к "
+"импортируемый закладкам"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Ðажмите <kbd>Импорт</kbd> Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° импорта закладок; Ñто может занÑÑ‚ÑŒ "
+"минуту"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "ЭкÑпортировать Ваши закладки из браузера в файл"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Ðажмите <kbd>ПроÑмотр...</kbd> чтобы выбрать Ñохраненный файл закладок на "
+"Вашем компьютере. МакÑимальный размер файла - 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+#, fuzzy
+msgid "Create your structure into a simple text file and following this model:"
+msgstr "Создайте Ñтруктуру в проÑтом текÑтовом файле иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñту модель:"
+
+#: data/templates/default/importStructure.tpl.php:35
+#, fuzzy
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+"Затем импортируйте файл. ТÑги и их Ð¾Ñ‚Ð½Ð¾ÑˆÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ добавлены в Ваш профиль."
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Пароль"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Ðе запрашивать мой пароль 2 недели"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Вход"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Забыли пароль?"
+
+#: data/templates/default/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 ""
+"ЕÑли Ð’Ñ‹ забыли пароль, %s может Ñоздать новый. Введите Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸ "
+"Ð°Ð´Ñ€ÐµÑ e-mail Вашего аккаунта в форме ниже и мы вышлем Вам новый пароль."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Сгенерировать пароль"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "УчаÑтник Ñ"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Watching"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "ÐаблюдаетÑÑ"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Закладки"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Перейти к закладкам"
+
+#: data/templates/default/register.tpl.php:11
+#, fuzzy, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"ЗарегиÑтрируйтеÑÑŒ здеÑÑŒ, чтобы Ñоздать беÑплатный %s аккаунт. Ð’ÑÑ Ð¿Ð¾Ð»Ñ "
+"обÑзательны Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+" как минимум 5 Ñимволов, буквенно-цифровой (без пробелов, без точек или "
+"других ÑпецÑимволов)"
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr " чтобы выÑлать Вам Вамш пароль, еÑли Ð’Ñ‹ его забудете"
+
+#: data/templates/default/register.tpl.php:33
+#, fuzzy
+msgid "Antispam question"
+msgstr "ÐнтиÑпам вопроÑ"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "РегиÑтрациÑ"
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr "ПоиÑк..."
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr "в"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "закладки Ñтого пользователÑ"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "мои закладки"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "мой лиÑÑ‚ наблюдениÑ"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "вÑе закладки"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "ПоиÑк"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "СвÑзанные Ñ‚Ñги"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Добавить новую ÑÑылку"
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Удалить ÑÑылку"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "ТÑги включенные в Ñ‚Ñг '%s'"
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Меню Ñ‚Ñгов"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "ПроÑмотреть вÑе Ñвои Ñ‚Ñги"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "вÑе Ваши Ñ‚Ñги"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "ПроÑмотреть вÑе Ñ‚Ñги Ñтого пользователÑ"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "вÑе Ñ‚Ñги Ñтого пользователÑ"
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "ПроÑмотреть популÑрные Ñ‚Ñги"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "Рекомендуемые ТÑги"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+#, fuzzy
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr "Это меню ÑоÑтоит из ключевых Ñлов (Ñ‚Ñгов) управлÑемых админиÑтраицей."
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "ПоÑледние ТÑги"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "СвÑзанные ТÑги"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "Что иÑкали недавно"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "КоличеÑтво закладок Ð´Ð»Ñ Ñтого запроÑа"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Переименовать Ñ‚Ñг"
+msgstr[1] "Переименовать Ñ‚Ñги"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Удалить Ñ‚Ñг"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Изменить опиÑание Ñ‚Ñга"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Edit Tag common description"
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Создать ÑÑылку на другой Ñ‚Ñг"
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "Ðовые Пользователи"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "закладки"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "ПроÑмотреть вÑех пользователей"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Ð’Ñе пользователи"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+#, fuzzy
+msgid "Close contacts are mutual contacts"
+msgstr "Близкие контакты взаимных контактов"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr "Близкие контакты"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "Добавить контакт..."
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "Введите Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ‡Ñ‚Ð¾Ð±Ñ‹ добавить его в контакты."
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "Удалить Ñтот контакт"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Удалить из ÑпиÑка наблюдениÑ"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Добавить в ÑпиÑок наблюдениÑ"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Изменить ÑÑылку"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Синонимы:"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Создать новую ÑÑылку:"
+
+#: data/templates/default/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 ""
+"Внимание: включение Ñ‚Ñга в '%s' Ñ‚Ñг (например: %s>Ñтраны) выведет Ñ‚Ñг в "
+"ÑпиÑке меню"
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Создать"
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "СущеÑтвующие ÑÑылки:"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "СÑылок нет"
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Да"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Ðет"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Удалить ÑÑылку"
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Создать новую ÑÑылку"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Старый"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "Ðовый"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Переименовать"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Ðлфавиту"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "ПопулÑрноÑти"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr "Домой"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "СпиÑок наблюдениÑ"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Профиль"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Добавить закладку"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Выйти"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr "Ðдмин"
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+msgid "profile"
+msgstr "Профиль"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "Ñоздано в"
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "Управление пользователÑми"
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr "%s и вÑего закладки и Ñ‚Ñги удалены."
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr "Возникла проблема Ñ"
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "У Ð’Ð°Ñ Ð½ÐµÑ‚ прав на удаление Ñтой закладки"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Ошибка при удалении закладки"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Ð’Ñе Ñ‚Ñги"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "Пользователь Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %s не найден"
+
+#: 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 "ДоÑтуп запрещен."
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr "ОпиÑание закладки обновлено"
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr "Ошибка при изменении опиÑÐ°Ð½Ð¸Ñ Ð·Ð°ÐºÐ»Ð°Ð´ÐºÐ¸"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr "Изменить опиÑание закладки"
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "Remove the tag from the selection"
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "Закладка должна иметь название и адреÑ"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Закладка Ñохранена"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr "(ВернутьÑÑ Ð½Ð° предыдущую Ñтраницу.)"
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Произошла ошибка при Ñохранении закладки. Попробуйте еще раз или ÑвÑжитеÑÑŒ Ñ "
+"админиÑтрацией."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Добавить закладку"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Ð’Ñ‹ должны войти в ÑиÑтему, прежде чем добавлÑÑ‚ÑŒ закладки."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Мои закладки"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Изменить закладку"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Закладка # %s не найдена"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "У Ð’Ð°Ñ Ð½ÐµÑ‚ доÑтупа Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтой закладки"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Ошибка во Ð²Ñ€ÐµÐ¼Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð’Ð°ÑˆÐµÐ¹ закладки"
+
+#: www/history.php:61
+msgid "History"
+msgstr "ИÑториÑ"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð´Ð»Ñ %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "ÐÐ´Ñ€ÐµÑ Ð½Ðµ найден"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Ðевозможно открыть XML"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "Ошибка XML: %s в Ñтроке %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Импортировать закладки из del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Ð’Ñ‹ уже добавили Ñту закладку."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Закладка импортирована."
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr "Ð’Ñ‹ уже Ñохранили некоторые из Ñтих закладок."
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "Ðайденные закладки: "
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "Закладки загружены: "
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Импортировать закладки из файла"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr "Ошибка в отÑтупе"
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr "Ðовые ÑÑылки между Ñ‚Ñгов: "
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "Импорт Ñтруктуры"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Вы вышли"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Ðедавно добавленные закладки"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Ðедавно добавленные закладки"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "СохранÑйте, делитеÑÑŒ и отмечайте Ваши любимые ÑÑылки"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Ð’Ñе закладки"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "ДоÑтупно"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "ÐедоÑтупно"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "Вы ввели некорректные данные. Попробуйте еще раз."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Ð’Ñ‹ должны ввеÑти Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr "Ð’Ñ‹ должны ввеÑти <abbr title=\"electronic mail\">e-mail</abbr>."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Ð”Ð»Ñ Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½Ð¸Ñ‡ÐµÐ³Ð¾ не найдено."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Ðичего не найдено Ð´Ð»Ñ Ñтого имени Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸ адреÑа <abbr title="
+"\"electronic mail\">e-mail</abbr>."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ имени пользователÑ. Попробуйте еще раз."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Ваш новый пароль:"
+
+#: 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 ""
+"Ð”Ð»Ñ Ð¾Ð±ÐµÑÐ¿ÐµÑ‡ÐµÐ½Ð¸Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°ÑноÑти, Ð’Ñ‹ должны изменить пароль в профиле, Ñледующий "
+"раз когда войдете в ÑиÑтему."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s проÑмотр информации об аккаунте"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Ðовый пароль отправлен по адреÑу %s"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Забытый пароль"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½Ðµ введено"
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Мой профиль"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr "Ðеверный Ñимвол"
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Пароль и подтверждение Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð½Ðµ Ñовпадают."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Пароль должен быть минимум 6 Ñимволов длиной."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Ðекорректный Ð°Ð´Ñ€ÐµÑ E-mail."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Ошибка при Ñохранении изменений."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñохранены."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñ‚Ñга"
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr "Ð’Ñ‹ <em>должны</em> ввеÑти Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ, пароль и Ð°Ð´Ñ€ÐµÑ e-mail."
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr "Это Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð·Ð°Ð½Ñто, пожалуйÑта выберете другое."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Это Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÑ ÑƒÐ¶Ðµ ÑущеÑтвует, пожалуйÑта выберете другое."
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"Ðекорректное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (Ñлишком короткое, Ñлишком длинное или "
+"запрещенные Ñимволы...), пожалуйÑта выберете другое."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Ðекорректный Ð°Ð´Ñ€ÐµÑ e-mail. Попробуйте еще раз."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Ответ на Ð²Ð¾Ð¿Ñ€Ð¾Ñ Ñпам-фильтра не верен. Попробуйте еще раз."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "ПоздравлÑем! Ð’Ñ‹ уÑпешно зарегиÑтрировалиÑÑŒ!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Ошибка региÑтрации. ПожалуйÑта попробуйте еще раз."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "ПоÑледние закладки добавленные в %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "ПоиÑк закладок"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Результаты поиÑка"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr "Ðеудовлетворены? Ð’Ñ‹ еще можете поробовать наш "
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Tag link created"
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÑÑылки"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Добавить Ñ‚Ñг к ÑÑылке"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "ТÑг ÑÑылки удален"
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÑÑылки"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr "Delete Link between Tags"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Edit Link between Tags"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr "Tag common description updated"
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Failed to update the tag common description"
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "ТÑг удален"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñ‚Ñга"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "ОпиÑание Ñ‚Ñга обновлено"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ñ‚Ñга"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "ТÑг переименован"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Ошибка Ð¿ÐµÑ€ÐµÐ¸Ð¼ÐµÐ½Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ‚Ñга"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Пользователи"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Пользователь удален из ÑпиÑка наблюдениÑ"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Пользователь добавлен в ÑпиÑок наблюдениÑ"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Мой ÑпиÑок наблюдениÑ"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+#, fuzzy
+msgid "Come back to "
+msgstr "(ВернутьÑÑ Ð½Ð° предыдущую Ñтраницу.)"
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
diff --git a/data/locales/tr_TR/LC_MESSAGES/messages.mo b/data/locales/tr_TR/LC_MESSAGES/messages.mo
new file mode 100755
index 0000000..8b2a0f1
--- /dev/null
+++ b/data/locales/tr_TR/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/tr_TR/LC_MESSAGES/messages.po b/data/locales/tr_TR/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..b2da3e7
--- /dev/null
+++ b/data/locales/tr_TR/LC_MESSAGES/messages.po
@@ -0,0 +1,1511 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Semantic Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-05-11 17:51+0200\n"
+"PO-Revision-Date: 2011-07-27 14:38+0200\n"
+"Last-Translator: Yalçın Can <ylcncn@gmail.com>\n"
+"Language-Team: <ylcncn@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n>1)\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() fonksiyonu birden fazla çağrıldı."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL Hatası"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "Satır"
+
+#: 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 "Dosya"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Bilgi"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Önemli Bilgi"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Bir hata oluÅŸtu"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Genel Hata"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Önemli bir hata oluştu"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Kritik Hata"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "GELÄ°ÅžTÄ°RÄ°CÄ° KÄ°PÄ°"
+
+#: data/templates/about.tpl.php:6
+msgid "<strong>Store</strong> all your favourite links in one place, accessible from anywhere."
+msgstr "Tüm beğendiğiniz bağlantıları tek yerde ve her yerden erişilebilecek bir şekilde <strong>saklayın</strong>"
+
+#: data/templates/about.tpl.php:7
+msgid "<strong>Share</strong> your bookmarks with everyone, with friends on your watchlist or just keep them private."
+msgstr "Yerimlerinizi arkadaşlarınızla, izleme listenizle isterseniz herkesle <strong>paylaşın</strong>, isterseniz gizli tutun."
+
+#: data/templates/about.tpl.php:8
+msgid "<strong>Tag</strong> your bookmarks with as many labels as you want, instead of wrestling with folders."
+msgstr "Yerimlerinizi dosyalara bölmenize gerek yok, yerimlerinizi istediğiniz kadar <strong>etiket</strong>le etiketleyin."
+
+#: data/templates/about.tpl.php:9
+msgid "Register now"
+msgstr "Åžimdi kaydoldun"
+
+#: data/templates/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "%s'i kullanmaya başlamak için!"
+
+#: data/templates/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Geek Åžeyler"
+
+#: data/templates/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "koruma latın olduğu lisans"
+
+#: data/templates/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr "kendi sunucunuzda özgürce kullanabilirsiniz."
+
+#: data/templates/about.tpl.php:15
+#, php-format
+msgid "%1$s supports most of the <a href=\"http://del.icio.us/doc/api\">del.icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. 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 birçok <a href=\"http://del.icio.us/doc/api\">del.icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>'sini desteklemektedir. Delicious için yapılmış tüm güzel uygulamalar düzenlenerek %1$s ile çalışması sağlanabilir. API adresini değiştirecek bir araç bulamadıysanız, programın yaratıcısından yardım isteyin. Kim bilir, hemen ekleyebilirler."
+
+#: data/templates/about.tpl.php:24
+msgid "Tips"
+msgstr "İpuçları"
+
+#: data/templates/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Arama eklentisini tarayıcınıza yükleyin:"
+
+#: data/templates/about.tpl.php:27
+msgid "The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr "\"system:unfiled\" gizli etiketi etiketsiz olan yerimlerinizi bulmanızda yardımcı olur."
+
+#: data/templates/about.tpl.php:28
+msgid "The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr "\"system:imported\" gizli etiketi içe aktarılmış olan yerimlerinizi bulmanızda yardımcı olur."
+
+#: data/templates/admin.tpl.php:5
+msgid "Users management"
+msgstr "Kullanıcı Yönetimi"
+
+#: data/templates/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr "Genel/Paylaşılmış/Gizli"
+
+#: data/templates/admin.tpl.php:14
+#: data/templates/bookmarks.tpl.php:113
+msgid "bookmark(s)"
+msgstr "yer imi"
+
+#: 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 "Emini misiniz?"
+
+#: data/templates/admin.tpl.php:19
+#: data/templates/bookmarks.tpl.php:300
+msgid "Delete"
+msgstr "Sil"
+
+#: data/templates/admin.tpl.php:27
+msgid "Other actions"
+msgstr "DiÄŸer eylemler"
+
+#: data/templates/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr "Tüm bağlantıları kontrol edin (Uzun sürebilir)"
+
+#: data/templates/bookmarkcommondescriptionedit.tpl.php:16
+msgid "Collaborative description: these fields can be viewed and modified by every users"
+msgstr "İmece tanım: Bu alanlar tüm kullancılar tarafından görülebilir ve değiştirilebilir."
+
+#: data/templates/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/bookmarks.tpl.php:157
+#: data/templates/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Başlık"
+
+#: data/templates/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/editbookmark.tpl.php:57
+#: data/templates/editprofile.tpl.php:45
+#: data/templates/profile.tpl.php:33
+#: data/templates/tagcommondescriptionedit.tpl.php:13
+#: data/templates/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Tanım"
+
+#: data/templates/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Son deÄŸiÅŸtirilme:"
+
+#: data/templates/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/tagcommondescriptionedit.tpl.php:33
+#: data/templates/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Güncelle"
+
+#: data/templates/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/editbookmark.tpl.php:111
+#: data/templates/tag2tagadd.tpl.php:24
+#: data/templates/tag2tagedit.tpl.php:38
+#: data/templates/tagcommondescriptionedit.tpl.php:34
+#: data/templates/tagedit.tpl.php:19
+#: data/templates/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Ä°ptal"
+
+#: data/templates/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/bookmarklet.inc.php:11
+#, 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 "Aşağıdaki bookmarklet'lerden birine tıklayarak tarayıcınızda %s'e link eklemenizi kolaylaştıracak bir buton oluşturun."
+
+#: data/templates/bookmarklet.inc.php:20
+#, 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/bookmarklet.inc.php:67
+#: data/templates/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "%s'e gönder"
+
+#: data/templates/bookmarklet.inc.php:93
+#: data/templates/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "%s'e gönder (Açılır pencere)"
+
+#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr "Oy <span class=\"voting\">%d</span>"
+
+#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr "BeÄŸen"
+
+#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr "Kötüle"
+
+#: data/templates/bookmarks.tpl.php:42
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr "Bu sayfadaki yerimleri bir yönetici tarafından yönetilebilir. "
+
+#: data/templates/bookmarks.tpl.php:71
+#: data/templates/bookmarks.tpl.php:72
+msgid "Edit the common description of this tag"
+msgstr "Bu etiketin genel tanımını düzenleyin"
+
+#: data/templates/bookmarks.tpl.php:75
+#: data/templates/bookmarks.tpl.php:76
+msgid "Edit the common description of this bookmark"
+msgstr "Bu yeriminin genel tanımını düzenleyin"
+
+#: data/templates/bookmarks.tpl.php:96
+#: data/templates/bookmarks.tpl.php:97
+msgid "Edit your personal description of this tag"
+msgstr "Bu etiketin kişisel tanımını değiştirin"
+
+#: data/templates/bookmarks.tpl.php:113
+#: data/templates/tags.tpl.php:10
+#: data/templates/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Sırala:"
+
+#: data/templates/bookmarks.tpl.php:155
+msgid "Date"
+msgstr "Tarih"
+
+#: data/templates/bookmarks.tpl.php:160
+msgid "Voting"
+msgstr "Oy"
+
+#: data/templates/bookmarks.tpl.php:169
+msgid "Bookmarks from other users for this tag"
+msgstr "Bu etiketteki diğer kullanıcıların yerimleri"
+
+#: data/templates/bookmarks.tpl.php:174
+msgid "Only your bookmarks for this tag"
+msgstr "Bu etiketteki yerimleriniz"
+
+#: data/templates/bookmarks.tpl.php:197
+#: data/templates/bookmarks.tpl.php:203
+msgid "First"
+msgstr "Ä°lk"
+
+#: data/templates/bookmarks.tpl.php:198
+#: data/templates/bookmarks.tpl.php:204
+msgid "Previous"
+msgstr "Önceki"
+
+#: data/templates/bookmarks.tpl.php:211
+#: data/templates/bookmarks.tpl.php:214
+msgid "Next"
+msgstr "Sonraki"
+
+#: data/templates/bookmarks.tpl.php:212
+#: data/templates/bookmarks.tpl.php:215
+msgid "Last"
+msgstr "Son"
+
+#: data/templates/bookmarks.tpl.php:228
+#, php-format
+msgid "Page %d of %d"
+msgstr "Sayfa %d / %d"
+
+#: data/templates/bookmarks.tpl.php:290
+msgid "Tags:"
+msgstr "Etiketler:"
+
+#: data/templates/bookmarks.tpl.php:297
+msgid "Edit"
+msgstr "Düzenle"
+
+#: data/templates/bookmarks.tpl.php:305
+msgid "Last update"
+msgstr "Son güncelleme"
+
+#: data/templates/bookmarks.tpl.php:308
+msgid "by"
+msgstr "tarafından"
+
+#: data/templates/bookmarks.tpl.php:312
+msgid "you"
+msgstr "siz"
+
+#: data/templates/bookmarks.tpl.php:328
+#, php-format
+msgid " and %s1 other%s"
+msgstr "ve %s1 diÄŸer%s"
+
+#: data/templates/bookmarks.tpl.php:331
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr "Tüm Diğerleri"
+
+#: data/templates/bookmarks.tpl.php:345
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Bu yer imini kendi yer imlerinize kopyalayın."
+
+#: data/templates/bookmarks.tpl.php:346
+msgid "Copy"
+msgstr "Kopyala"
+
+#: data/templates/bookmarks.tpl.php:366
+msgid "This bookmark is certified by an admin user."
+msgstr "Bu yer imi bir yönetici tarafından onaylanmıştır."
+
+#: data/templates/bookmarks.tpl.php:419
+msgid "Private Note on this bookmark"
+msgstr "Bu yer imindeki özel notunuz"
+
+#: data/templates/bookmarks.tpl.php:433
+msgid "Come back to the top of this page."
+msgstr "Bu sayfanın en başına gidin."
+
+#: data/templates/bookmarks.tpl.php:433
+msgid "Top of the page"
+msgstr "Sayfanın üstü"
+
+#: data/templates/bookmarks.tpl.php:439
+msgid "No bookmarks available"
+msgstr "Yerimi mevcut deÄŸil"
+
+#: 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 "Hakkında"
+
+#: data/templates/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Altyapı:"
+
+#: data/templates/dynamictags.inc.php:56
+#: 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] "yer imi"
+msgstr[1] "yerimi"
+
+#: data/templates/dynamictags.inc.php:105
+#: 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 "Popüler Etiketler"
+
+#: data/templates/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "Tüm kullanıcıların En Popüler Etiketleri"
+
+#: data/templates/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Adres"
+
+#: data/templates/editbookmark.tpl.php:48
+#: data/templates/editbookmark.tpl.php:53
+#: data/templates/editprofile.tpl.php:29
+#: data/templates/tagrename.tpl.php:14
+#: data/templates/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Gerekli"
+
+#: data/templates/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Not Ekle"
+
+#: data/templates/editbookmark.tpl.php:61
+msgid "You can use anchors to delimite attributes. for example: [publisher]blah[/publisher] "
+msgstr ""
+
+#: data/templates/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr "Önerilen çapalar(anchor):"
+
+#: data/templates/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "Gizli Not"
+
+#: data/templates/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr "Sadece siz ve arkadaşlarınız tarafından görülebilir."
+
+#: data/templates/editbookmark.tpl.php:82
+#: data/templates/toolbar.inc.php:10
+#: www/tags.php:45
+#: www/tags.php:67
+msgid "Tags"
+msgstr "Etiketler"
+
+#: data/templates/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Virgülle ayrılmış"
+
+#: data/templates/editbookmark.tpl.php:90
+#: 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:94
+#: data/templates/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/editbookmark.tpl.php:97
+#: data/templates/importDelicious.tpl.php:15
+#: data/templates/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Gizlilik"
+
+#: data/templates/editbookmark.tpl.php:100
+#: data/templates/importDelicious.tpl.php:18
+#: data/templates/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Açık"
+
+#: data/templates/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "İzleme listesiyle paylaşılanlar"
+
+#: data/templates/editbookmark.tpl.php:102
+#: data/templates/importDelicious.tpl.php:20
+#: data/templates/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "Gizli"
+
+#: data/templates/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Yerimini Sil"
+
+#: data/templates/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "genel tanımı düzenle"
+
+#: data/templates/editbookmark.tpl.php:212
+#: data/templates/importDelicious.tpl.php:26
+#: data/templates/importNetscape.tpl.php:27
+#: data/templates/importStructure.tpl.php:16
+msgid "Import"
+msgstr "İçeri Aktar"
+
+#: data/templates/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Bağlantılarınızı bir yerimi dosyasından içeri aktarın"
+
+#: data/templates/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox ve Netscape"
+
+#: data/templates/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Bağlantılarınızı del.icio.us'dan içeri aktarın"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr "SSL istemci sertifikaları"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr "Seri No"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/editprofile.tpl.php:37
+#: data/templates/profile.tpl.php:17
+msgid "Name"
+msgstr "Ä°sim"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/profile.tpl.php:11
+msgid "Email"
+msgstr "Eposta"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:30
+msgid "delete"
+msgstr "sil"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr "Hİçbir sertifika kaydedilmedi"
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr "Geçerli sertifika zaten hesabınıza kaydolmuş durumda."
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr "Sertifikayı kaydederek otomatik bir şekilde giriş yapabilirsiniz."
+
+#: data/templates/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr "Tarayıcınız bir sertifika sunmuyor."
+
+#: data/templates/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Hesap Ayrıntıları"
+
+#: data/templates/editprofile.tpl.php:12
+#: 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 "Kullanıcı Adı"
+
+#: data/templates/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Yeni Parola"
+
+#: data/templates/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Parolayı tekrarla"
+
+#: data/templates/editprofile.tpl.php:27
+#: data/templates/password.tpl.php:14
+#: data/templates/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-posta"
+
+#: data/templates/editprofile.tpl.php:33
+msgid "Personal Details"
+msgstr "Kullanıcı Ayrıntıları"
+
+#: data/templates/editprofile.tpl.php:41
+#: data/templates/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Ana Sayfa"
+
+#: data/templates/editprofile.tpl.php:50
+#: www/edit.php:113
+msgid "Save Changes"
+msgstr "DeÄŸiÅŸiklikleri Kaydet"
+
+#: data/templates/editprofile.tpl.php:55
+#: data/templates/sidebar.block.tagactions.php:17
+#: data/templates/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Eylemler"
+
+#: data/templates/editprofile.tpl.php:58
+msgid "Export bookmarks"
+msgstr "Yer İmlerini dışarı aktar"
+
+#: data/templates/editprofile.tpl.php:60
+msgid "HTML file (for browsers)"
+msgstr "HTML dosyası (tarayıcılar için)"
+
+#: data/templates/editprofile.tpl.php:61
+msgid "XML file (like del.icio.us)"
+msgstr "XML dosyası (del.icio.us'ın sağladığı gibi)"
+
+#: data/templates/editprofile.tpl.php:62
+msgid "CSV file (for spreadsheet tools)"
+msgstr "CSV dosyası (excel programları için)"
+
+#: data/templates/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Bulunamadı"
+
+#: data/templates/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "İstenilen adres bu sunucuda bulunamadı"
+
+#: data/templates/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Genel sunucu hatası"
+
+#: data/templates/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Ä°stenen adres iÅŸlenemedi"
+
+#: data/templates/importDelicious.tpl.php:19
+#: data/templates/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "İzleme listesiyle Paylaşılanlar"
+
+#: data/templates/importDelicious.tpl.php:31
+#: data/templates/importNetscape.tpl.php:32
+#: data/templates/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Talimatlar"
+
+#: data/templates/importDelicious.tpl.php:33
+msgid "Log in to the <a href=\"http://del.icio.us/api/posts/all\">export page at del.icio.us</a>"
+msgstr "del.icio.us'a giriş yapıp <a href=\"http://del.icio.us/api/posts/all\">dışarı aktar sayfasına girin</a>"
+
+#: data/templates/importDelicious.tpl.php:34
+msgid "Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> file to your computer"
+msgstr "Sonuç <abbr title=\"Extensible Markup Language\">XML</abbr> dosyasını bilgisayarınıza kaydedin"
+
+#: data/templates/importDelicious.tpl.php:35
+msgid "Click <kbd>Browse...</kbd> to find this file on your computer. The maximum size the file can be is 1MB"
+msgstr "<kbd>Gözat...</kbd>'a basarak bilgisayarınızdan bir dosya seçin. Dosya boyutu en fazla 1 MB olabilir."
+
+#: data/templates/importDelicious.tpl.php:36
+#: data/templates/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr ""
+
+#: data/templates/importDelicious.tpl.php:37
+#: data/templates/importNetscape.tpl.php:44
+msgid "Click <kbd>Import</kbd> to start importing the bookmarks; it may take a minute"
+msgstr ""
+
+#: data/templates/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr ""
+
+#: data/templates/importNetscape.tpl.php:37
+msgid "Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr "Internet Explorer: <kbd> DOsya &gt; İçe Aktar ve Dışa Aktar... &gt; Favorileri Dışarı Aktar"
+
+#: data/templates/importNetscape.tpl.php:38
+msgid "Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; Export..."
+msgstr "Mozilla Firefox: <kbd>Yerimleri &gt; Yerimlerini Yönet... &gt; Dosya &gt; Dışarı Aktar..."
+
+#: data/templates/importNetscape.tpl.php:39
+msgid "Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+
+#: data/templates/importNetscape.tpl.php:42
+msgid "Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. The maximum size the file can be is 1MB"
+msgstr ""
+
+#: 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 "Lütfen çerezleri etkinleştirin"
+
+#: data/templates/login.tpl.php:26
+#: data/templates/register.tpl.php:21
+msgid "Password"
+msgstr "Åžifre"
+
+#: data/templates/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Åžifremi 2 hafta boyunca sorma"
+
+#: data/templates/login.tpl.php:32
+#: data/templates/toolbar.inc.php:29
+#: www/login.php:57
+msgid "Log In"
+msgstr "GiriÅŸ"
+
+#: data/templates/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Parolanızı mı unuttunuz?"
+
+#: 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 ""
+
+#: data/templates/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Rastgele bir ÅŸifre oluÅŸtur"
+
+#: data/templates/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Ãœyelik Tarihi:"
+
+#: data/templates/profile.tpl.php:40
+#: data/templates/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Ä°zleniyor"
+
+#: data/templates/profile.tpl.php:55
+#: data/templates/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Ä°zleyenler"
+
+#: data/templates/profile.tpl.php:68
+#: data/templates/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Yer imleri"
+
+#: data/templates/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Yer imlerine git"
+
+#: 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 ""
+
+#: 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 "Spam belirleyici soru"
+
+#: data/templates/register.tpl.php:41
+#: data/templates/toolbar.inc.php:31
+#: www/register.php:99
+msgid "Register"
+msgstr "Kaydol"
+
+#: data/templates/search.menu.php:27
+msgid "Search..."
+msgstr "Ara..."
+
+#: data/templates/search.menu.php:28
+msgid "in"
+msgstr "ÅŸurada"
+
+#: data/templates/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "bu kullanıcının yerimleri"
+
+#: data/templates/search.menu.php:39
+msgid "my bookmarks"
+msgstr "yer imlerim"
+
+#: data/templates/search.menu.php:40
+msgid "my watchlist"
+msgstr "izleme listem"
+
+#: data/templates/search.menu.php:44
+msgid "all bookmarks"
+msgstr "tüm yer imleri"
+
+#: data/templates/search.menu.php:54
+msgid "Search"
+msgstr "Ara"
+
+#: data/templates/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Bağlantılı Etiketler"
+
+#: data/templates/sidebar.block.linked.php:28
+#: data/templates/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Yeni bağlantı ekle"
+
+#: data/templates/sidebar.block.linked.php:29
+#: data/templates/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Bağlantıyı sil"
+
+#: data/templates/sidebar.block.menu.php:35
+#, fuzzy, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "Geany etiket dosyaları (*.tags)"
+
+#: data/templates/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Vitrin Etiketleri"
+
+#: data/templates/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "Tüm etiketlerinizi görün"
+
+#: data/templates/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "tüm etiketleriniz"
+
+#: data/templates/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "Bu kullanıcının tüm etiketlerine bak"
+
+#: data/templates/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "bu kullanıcının tüm etiketleri"
+
+#: data/templates/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "Popüler etiketlere bak"
+
+#: data/templates/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr "Vitrin Etiketleri"
+
+#: data/templates/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Son Etiketler"
+
+#: data/templates/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Ä°lgili Etiketler"
+
+#: data/templates/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Son Aramalar"
+
+#: data/templates/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "Bu albüme yüklenebilecek maksimum resim sayısı:"
+
+#: data/templates/sidebar.block.tagactions.php:9
+#: www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Etiketi Düzenle"
+msgstr[1] "Etiketi Düzenle"
+
+#: data/templates/sidebar.block.tagactions.php:22
+#: www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Etiketi sil"
+
+#: data/templates/sidebar.block.tagactions.php:24
+#: www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Etiket Tanımını Düzenle"
+
+#: data/templates/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Genel Etiket Tanımını Düzenleyin"
+
+#: data/templates/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Başka bir etikete bağlantı yarat"
+
+#: data/templates/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "Yeni Kullanıcılar"
+
+#: data/templates/sidebar.block.users.php:23
+#: data/templates/users.tpl.php:24
+msgid "bookmarks"
+msgstr "yer imleri"
+
+#: data/templates/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "TÜm kullanıcıları gör"
+
+#: data/templates/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Tüm Kullanıcılar"
+
+#: 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 "Yakın kişiler"
+
+#: data/templates/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "Bir kiÅŸi ekle..."
+
+#: data/templates/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "Kişilerinize eklemek için bir kullanıcı adı yazın."
+
+#: data/templates/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "Bu kiÅŸiyi sil"
+
+#: data/templates/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Ä°zleme listesinden sil"
+
+#: data/templates/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Ä°zleme listesine ekle"
+
+#: data/templates/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Bağlantıyı düzenle"
+
+#: data/templates/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Eş anlamlılar"
+
+#: data/templates/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Yeni bağlantı ekle:"
+
+#: 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 ""
+
+#: data/templates/tag2tagadd.tpl.php:23
+#: data/templates/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "OluÅŸtur"
+
+#: data/templates/tag2tagadd.tpl.php:35
+#: data/templates/tag2tagdelete.tpl.php:27
+#: data/templates/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Geçmiş Bağlantılar"
+
+#: data/templates/tag2tagadd.tpl.php:53
+#: data/templates/tag2tagdelete.tpl.php:45
+#: data/templates/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "Yer imi yok"
+
+#: 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 "Evet"
+
+#: 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 "Hayır"
+
+#: data/templates/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Bağlantıyı sil"
+
+#: data/templates/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Yeni bağlantı ekle"
+
+#: data/templates/tagrename.tpl.php:12
+msgid "Old"
+msgstr "Eski"
+
+#: data/templates/tagrename.tpl.php:17
+msgid "New"
+msgstr "Yeni"
+
+#: data/templates/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Yeniden Adlandır"
+
+#: data/templates/tags.tpl.php:11
+#: data/templates/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Abece"
+
+#: data/templates/tags.tpl.php:12
+#: data/templates/users.tpl.php:10
+msgid "Popularity"
+msgstr "Popülarite"
+
+#: data/templates/toolbar.inc.php:8
+#: data/templates/toolbar.inc.php:26
+msgid "Home"
+msgstr "Başlangıç"
+
+#: data/templates/toolbar.inc.php:11
+#: www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Ä°zleme listesi"
+
+#: data/templates/toolbar.inc.php:12
+#: www/profile.php:67
+msgid "Profile"
+msgstr "Profil"
+
+#: data/templates/toolbar.inc.php:13
+#: www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Yer imi ekle"
+
+#: data/templates/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Çıkış"
+
+#: data/templates/toolbar.inc.php:17
+msgid "Admin"
+msgstr "Yönetici"
+
+#: data/templates/top.inc.php:55
+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:20
+msgid "profile"
+msgstr "profil"
+
+#: data/templates/users.tpl.php:21
+#, fuzzy
+msgid "created in"
+msgstr " (paket içinde):"
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "Kullanıcıları yönet"
+
+#: 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 "Åžurada problem:"
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Bu yerimini silmek için yetkiniz yok."
+
+#: www/ajaxDelete.php:41
+#: www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Yer iminin silinmesi başarısız oldu"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Tüm etiketler"
+
+#: www/alltags.php:56
+#: www/bookmarks.php:99
+#: www/populartags.php:52
+#: www/profile.php:51
+#: www/rss.php:82
+#: www/search.php:111
+#: www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr ""
+
+#: 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 "Ä°zin verilmedi."
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114
+#: www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "Seçili öğeden etiketi çıkar"
+
+#: www/bookmarks.php:134
+#: www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr ""
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr "Bu yerimi adresi eklenmeyebilir"
+
+#: www/bookmarks.php:158
+#: www/edit.php:83
+#: www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Yer imi kaydedildi"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr "(Önceki sayfaya geri git.)"
+
+#: www/bookmarks.php:165
+#: www/import.php:106
+#: www/importNetscape.php:108
+msgid "There was an error saving your bookmark. Please try again or contact the administrator."
+msgstr ""
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Yer imi ekle"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr ""
+
+#: www/bookmarks.php:280
+#: www/bookmarks.php:281
+msgid "My Bookmarks"
+msgstr "Yer Ä°mlerim"
+
+#: www/edit.php:44
+#: www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Yer İmini Düzenle"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr ""
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Bu yer imini düzenlemek için yetkiniz yok."
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Yeriminizi kaydederken bir sorun yaşandı"
+
+#: www/history.php:61
+msgid "History"
+msgstr "Geçmiş"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "%s için geçmiş"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Adres bulunamadı"
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "XML girdisi açılamadı"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML hatası: %s, %d satırında"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Bağlantılarınızı del.icio.us'dan içeri aktarın"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Bu yer imini önceden kaydetmişsiniz."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Yer imi içeri aktarıldı"
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr ""
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "Yerimleri bulundu:"
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "Yerimleri içe aktarıldı:"
+
+#: www/importNetscape.php:117
+#: www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Yer imlerinizi Tarayıcı Dosyasından İçeri Aktarın"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr "Geçersiz Girinti"
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr "Etiketler arasında yeni bağlantılar:"
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "Yapıyı İçeri Aktar"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Çıkış yapmış bulunuyorsunuz"
+
+#: www/index.php:45
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: En son yer imleri"
+
+#: www/index.php:75
+msgid "Store, share and tag your favourite links"
+msgstr "Favori yer imlerinizi saklayın, paylaşın ve etiketleyin"
+
+#: www/index.php:76
+msgid "All Bookmarks"
+msgstr "Tüm yer imleri"
+
+#: www/jsScuttle.php:70
+msgid "Available"
+msgstr "Uygun"
+
+#: www/jsScuttle.php:73
+msgid "Not Available"
+msgstr "Uygun DeÄŸil"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr ""
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Kullanıcı adınızı girmeniz gerekiyor."
+
+#: www/password.php:40
+msgid "You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr ""
+
+#: www/password.php:51
+msgid "No matches found for that combination of username and <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+
+#: www/password.php:59
+msgid "There was an error while generating your new password. Please try again."
+msgstr ""
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Yeni parolanız:"
+
+#: 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 ""
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "%s Hesap Bilgileri"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr ""
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Åžifremi Unuttum"
+
+#: www/profile.php:59
+#: www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Kullanıcı adı belirtilmedi"
+
+#: www/profile.php:65
+msgid "My Profile"
+msgstr "Profilim"
+
+#: www/profile.php:89
+msgid "Invalid Token"
+msgstr "Geçersiz belirteç"
+
+#: www/profile.php:94
+msgid "Password and confirmation do not match."
+msgstr ""
+
+#: www/profile.php:98
+msgid "Password must be at least 6 characters long."
+msgstr "Şifre en az 6 karakter uzunluğunda olmalıdır."
+
+#: www/profile.php:102
+msgid "E-mail address is not valid."
+msgstr "Eposta adresi geçersiz."
+
+#: www/profile.php:106
+msgid "An error occurred while saving your changes."
+msgstr "Ayarlarınız kaydedilirken bir hata oluştu."
+
+#: www/profile.php:108
+msgid "Changes saved."
+msgstr "DeÄŸiÅŸiklikler kaydedildi."
+
+#: www/profile.php:121
+msgid "You do not have a valid SSL client certificate"
+msgstr "Geçerli bir SSL istemci sertifikasına sahip değilsiniz"
+
+#: www/profile.php:123
+msgid "This certificate is already registered"
+msgstr "Bu sertifika zaten yüklü"
+
+#: www/profile.php:125
+msgid "Failed to register SSL client certificate."
+msgstr "SSL istemci sertifikasının kaydedilmesi başarısız oldu."
+
+#: www/profile.php:127
+msgid "SSL client certificate registered."
+msgstr "SSL istemci sertifikası kaydedildi."
+
+#: www/profile.php:136
+msgid "Certificate not found."
+msgstr "Sertifika bulunamadı."
+
+#: www/profile.php:138
+msgid "The certificate does not belong to you."
+msgstr "Sertifika size ait deÄŸil."
+
+#: www/profile.php:140
+msgid "Failed to delete SSL client certificate."
+msgstr "SSL istemci sertifikasının silinmesi başarısız oldu"
+
+#: www/profile.php:142
+msgid "SSL client certificate deleted."
+msgstr "SSL istemci sertifikası silindi."
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr "Bir kullanıcı adı, şifre ve eposta adresi girmeniz <em>zorunludur</em>."
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr "Bu kullanıcı adı rezerve edilmiş, lütfen başka bir seçim yapın."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Bu kullanıcı adı önceden alınmış, lütfen başka bir seçim yapın."
+
+#: www/register.php:64
+msgid "This username is not valid (too short, too long, forbidden characters...), please make another choice."
+msgstr "Bu kullanıcı adı geçerli değil (çok kısa, çok uzun, yasaklı karakterler...), lütfen farklı bir ad seçin."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Eposta adresi geçerli değil. Lütfen tekrar deneyin."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Çöp ileti engelleyici cevabı geçersiz."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Başarılı bir şekilde kaydoldunuz. İyi eğlenceler!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Kaydınız başarısız oldu. Lütfen tekrar deneyin."
+
+#: www/rss.php:99
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "%s'e son kaydedilen yerimleri"
+
+#: www/search.php:83
+#: www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Yer Ä°mlerinde Ara "
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Sonuçlarda Ara"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr "Memnun kalmadınız mı? Şunu da deneyebilirsiniz:"
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Etiket bağlantısı oluşturuldu"
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Bağlantı oluşturulamadı"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Etiket Bağlantısı Oluştur"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Etiket bağlantısı silindi"
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "Bağlantının silinmesi başarısız"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr "Etiketleri Arasındaki Bağlantıyı Sil"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Etiketler arasındaki bağlantıyı düzenle"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr "Etiketin genel tanımı güncellendi"
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Etiketin genel tanımının güncellenmesi başarısız oldu"
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Etiket silindi"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Etiketin silinemedi"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Etiket tanımı güncellendi"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Etiket tanımının güncellenmesi başarısız oldu"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Etiket yeniden adlandırıldı"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Etiket yeniden adlandırılamadı"
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Kullanıcılar"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Kullancıı izleme listenizden silindi"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Kullancıı izleme listenize eklendi"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Ä°zleme Listem"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:27
+msgid "Come back to "
+msgstr "Geri dön"
+
+#: www/gsearch/index.php:32
+msgid "Admin tips: "
+msgstr "Yönetici ipuçları:"
+
+#: www/gsearch/index.php:33
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:37
+msgid "If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
diff --git a/data/locales/vn_VN/LC_MESSAGES/messages.mo b/data/locales/vn_VN/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..e0d5cfb
--- /dev/null
+++ b/data/locales/vn_VN/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/vn_VN/LC_MESSAGES/messages.po b/data/locales/vn_VN/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..1279c9c
--- /dev/null
+++ b/data/locales/vn_VN/LC_MESSAGES/messages.po
@@ -0,0 +1,1709 @@
+# Scuttle fr-FR Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# <toony.sf@chezouam.net>
+# nitram <nitram@no-log.org>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2009-11-18 15:42+0100\n"
+"Last-Translator: Nguyen Thi Hong Thai <thaichv@yahoo.com>\n"
+"Language-Team: fr-FR <toony.sf@chezouam.net>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: French\n"
+"X-Poedit-Country: FRANCE\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die() bị gá»i nhiá»u lần."
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "Lỗi cấu trúc truy vấn"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "mạng"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "File"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "Thông tin"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "Thông tin nguy cấp"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "Một lỗi đã xảy ra"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "Lá»—i thông thÆ°á»ng"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "Một lỗi nguy cấp đã xảy ra"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "Lỗi nguy cấp"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "Chế độ gỡ rối"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+"<strong>LÆ°u trữ</strong>tất cả các Ä‘Æ°á»ng links ở má»™t nÆ¡i, có thể truy cập "
+"tại má»i nÆ¡i"
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"<strong>Chia xẻ</strong> các trang ghi nhá»› của bạn vá»›i tất cả má»i ngÆ°á»ii, "
+"bạn có thể chia xẻ với bạn bè trong danh sách hoặc giữ cá nhân."
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"<strong>Gắn từ khoá/strong> những trang ghi nhớ của bạn với bất kì tên nào "
+"mà bạn muốn mà không phải làm việc với các thư mục"
+
+#: data/templates/default/about.tpl.php:9
+msgid "Register now"
+msgstr "Äăng kí ngay bây giá»"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr "Bắt đầu sử dụng %s !"
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "Pour les Geeks"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr "phân dÆ°á»›i quyá»n"
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr "Bạn có thể thoải mái cài đặt nó trên Web server của bạn"
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 hỗ trợ phần lớn từ'<a href=\"http://www.delicious.com/help/api\"><abbr "
+"title=\"Ứng dụng lập trình giao diện\">API</abbr> del.icio.us</a>."
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr "Mẹo"
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr "Thêm một nút tìm kiếm vào trong trình duyệt của bạn:"
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+"Thẻ bí mật \"system:unfiled\" cho phép bạn tìm thấy những trang ghi nhớ "
+"không có thẻ."
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+"Từ khoá bí mật \"system:imported\" cho phép bạn tìm thấy những trang ghi nhớ "
+"quan trá»ng."
+
+#: data/templates/default/admin.tpl.php:5
+msgid "Users management"
+msgstr "Quản lí những ngÆ°á»i dùng"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr "Công cộng/Chia xẻ/Cá nhân"
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+msgid "bookmark(s)"
+msgstr "Những trang ghi nhớ"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr "Bạn có chắc chắn không ?"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "Xoá"
+
+#: data/templates/default/admin.tpl.php:27
+msgid "Other actions"
+msgstr "Những hành động khác"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr "Kiểm tra tất cả Ä‘Æ°á»ng link (Có thể mất ít thá»i gian)"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+"Sá»± mô tả cá»™ng tác : những trÆ°á»ng này có thể được nhìn thấy và được sá»­a đổi "
+"bởi tất cả những ngÆ°á»i dùng."
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "Tiêu Ä‘á»"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "Mô tả"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr "Những thay đổi cuối:"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr "Cập nhật"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr "Huá»· bá»"
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Click theo một trong những bookmarklets để thêm một nút, bạn có thể click "
+"bất cứ khi nào bạn muốn thêm một trang cho trang hiện tại %s "
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Click theo một trong những bookmarklets vào trình duyệt bookmark của bạn và "
+"click nó bất cứ khi nào bạn muốn thêm một trang cho trang hiện tại %s "
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "Thêm vào %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "Thêm vào %s (Pop-up)"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr "Sự ủng hộ <span class=\"voting\">%d</span>"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr "Ủng hộ cho"
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr "Không ủng hộ"
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+"Những trang ghi nhá»› trên trang này được quản lí bởi má»™t ngÆ°á»i quản trị."
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr "Sửa mô tả chung của từ khoá này"
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr "Sửa mô tả chung của trang ghi nhớ này"
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr "Sửa mô tả cá nhân của bạn của thẻ này"
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "Sắp xếp bởi :"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "Ngày"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr "Sự bầu cử"
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr "Ghi nhá»› trang từ những ngÆ°á»i dùng khác cho thẻ này"
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr "Chỉ có những trang ghi nhớ của bạn"
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr "Äầu tiên"
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "TrÆ°á»›c"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "Sau"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr "Cuối cùng"
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "Trang %d của %d"
+
+#: data/templates/default/bookmarks.tpl.php:295
+msgid "Tags:"
+msgstr "Những từ khoá:"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "Sá»­a"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr "Lần cập nhật cuối"
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr "bởi"
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr "bạn"
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, php-format
+msgid " and %s1 other%s"
+msgstr "và %s1 khác%s"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr " và %2$s%1$s những cái khác%3$s"
+
+#: data/templates/default/bookmarks.tpl.php:350
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr "Copy trang ghi nhớ này vào những trang ghi nhớ của bạn."
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "Copy"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr "Trang ghi nhá»› này được bảo đảm bởi má»™t ngÆ°á»i quản trị."
+
+#: data/templates/default/bookmarks.tpl.php:430
+msgid "Private Note on this bookmark"
+msgstr "Ghi chú cá nhân trên trang ghi nhớ này"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr "Quay trở lại đầu trang này."
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr "Äầu trang"
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "Không có trang ghi nhớ nào có sẵn."
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "Vá»"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr "Äã đẩy bởi"
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "Trang ghi nhá»›"
+msgstr[1] "Signets"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "Những thẻ phổ biến"
+
+#: data/templates/default/dynamictags.inc.php:108
+msgid "Popular Tags From All Users"
+msgstr "Những thẻ phổ biến cho tất cả những ngÆ°á»i dùng"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "Äịa chỉ"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "Yêu cầu"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr "Thêm vào ghi chú"
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+"Bạn có thể sử dụng những nguồn tin cậy để xác định phạm vi những thuộc tính. "
+"Ví dụ : [publisher]blah[/publisher]"
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr "Những gợi ý tin cậy : "
+
+#: data/templates/default/editbookmark.tpl.php:76
+msgid "Private Note"
+msgstr "Ghi chú cá nhân"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr "Chỉ được xem bới bạn và danh sách liên lạc của bạn"
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "Thẻ"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "Phân tách bởi những dấu phẩy"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+"Chú ý: sử dụng \">\" để cho một mô tả mối liên hệ giữa các thẻ. Ví dụ: "
+"Chau_Au>Phap>paris"
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+"Chú ý : sử dụng \"=\" để diễn tả hai thẻ tương đương nhau. Ví dụ: europe=eu"
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "Riêng tư"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "Công cộng"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "Chia xẻ với danh sách liên hệ của tôi"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "cá nhân"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "Xoá trang ghi nhớ này"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr "Sửa mô tả chung"
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "Nhập "
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr "Nạp những trang ghi nhớ từ một file "
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox et Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "Nạp những trang ghi nhớ từ del.icio.us"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "Tên"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+msgid "Email"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "Xoá"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr "Tài khoản cụ thể"
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "Tên ngÆ°á»i dùng"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "Mật khẩu mới"
+
+#: data/templates/default/editprofile.tpl.php:22
+msgid "Confirm Password"
+msgstr "Xác nhận mật khẩu"
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "E-mail"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "Ghi chú cá nhân"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "Phát sinh một mật khẩu"
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr "Chi tiết cá nhân"
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "Trang chủ"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "Lưu lại sự thay đổi"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+msgid "Actions"
+msgstr "Những thao tác"
+
+#: data/templates/default/editprofile.tpl.php:66
+msgid "Export bookmarks"
+msgstr "Xuất những trang ghi nhớ"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr "file HTML (để duyệt)"
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr "file XML (nhÆ° del.icio.us)"
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr "file CSV (cho những công cụ lan truyá»n)"
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "Không tìm thấy"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "Yêu cầu Ä‘Æ°á»ng link đã không được tìm thấy trên server này."
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "Lỗi tác độgn từ serveur."
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "Yêu cầu Ä‘Æ°á»ng link không thể tìm thấy trên server này"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+msgid "Shared with Watchlist"
+msgstr "Chia xẻ với danh sách liên lạc"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "Giới thiệu"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"Äăng nhập vào <a href=\"http://del.icio.us/api/posts/all\">trang trích xuất "
+"từ del.icio.us</a>"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"Lưu file kết quả <abbr title=\"Extensible Markup Language\">XML</abbr> trên "
+"máy tính của bạn"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr ""
+"Click <kbd>Browse...</kbd> để tìm thấy file trên máy tính của bạn. Kích cỡ "
+"tối đa của file không được vượt quá 1 MB"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "Lá»±a chá»n cài đặt mặc định cho những trang ghi nhá»› được nạp vào"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr ""
+"Click vào <kbd>Importer</kbd> để bắt đầu nạp những trang ghi nhớ; thao tác "
+"này có thể mất vài phút"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "Xuất những trang ghi nhớ ra một file từ danh sách duyệt của bạn"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr ""
+"Internet Explorer: <kbd>File &gt; Nạp và ÄÆ°a ra... &gt; ÄÆ°a ra những cái "
+"được ưa chuộng"
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>Những trang đánh dấu &gt;Quản lí những trang được đánh "
+"dấu... &gt;File &gt; ÄÆ°a rar..."
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr ""
+"Netscape: <kbd>Những trang đánh dấu &gt; Quản lí những trang đánh dấu... "
+"&gt; Những công cụ &gt; ÄÆ°a ra..."
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr ""
+"Click <kbd>Browse...</kbd> để tìm thấy file chứa những trang ghi nhớ đã được "
+"lưu trên máy tính của bạn. Kích thước tối đa của file không vượt quá 1MB"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr "Tạo cấu trúc của bạn vào một file văn bản đơn giản theo mô hình sau"
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+"Sau khi nạp vào file này. Những thẻ này và mối liên hệ giữa chúng sẽ được "
+"thêm vào thông tin của bạn"
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr "Hãy kích hoạt việc xác nhận sử dụng dịch vụ"
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "Mật khẩu"
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "Äừng há»i mật khẩu của tôi trong 2 tuần"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "Äăng nhập"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr "Có phải bạn đã quên mật khẩu ?"
+
+#: data/templates/default/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 ""
+"Nếu bạn đã quên mật khẩu, %s có thể tạo ra một mật khẩu mới. Nhập vào tên "
+"đăng nhập và địa chỉ email của bạn trong mẫu dưới đây và chúng tôi sẽ gửi "
+"cho bạn một mật khẩu mới."
+
+#: data/templates/default/password.tpl.php:19
+msgid "Generate Password"
+msgstr "Phát sinh một mật khẩu"
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "Thành viên trước đây"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+msgid "Watching"
+msgstr "Những danh sách liên lạc của tôi"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "Äược xem bởi"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+msgid "Bookmarks"
+msgstr "Những trang ghi nhớ"
+
+#: data/templates/default/profile.tpl.php:69
+msgid "Go to bookmarks"
+msgstr "Vào những trang ghi nhớ"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr ""
+"Bạn đăng kí ở đây để tạo ra một tài khoản miến phí %s. Tất cả những thông "
+"tin yêu cầu dưới đây là cần thiết."
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+" ít nhất 5 kí tự, chứa chữ và số (không khoảng trống, không dấu chấm hoặc "
+"kí tự đặc biệt khác)"
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr "để gửi cho bạn mật khẩu của bạn nếu bạn quên nó"
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr "Câu há»i chống thÆ° rác"
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "Äăng kí"
+
+#: data/templates/default/search.menu.php:27
+msgid "Search..."
+msgstr "Tìm kiếm..."
+
+#: data/templates/default/search.menu.php:28
+msgid "in"
+msgstr "trong"
+
+#: data/templates/default/search.menu.php:34
+msgid "this user's bookmarks"
+msgstr "những trang ghi nhá»› của ngÆ°á»i dùng"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "những trang ghi nhớ của tôi"
+
+#: data/templates/default/search.menu.php:40
+msgid "my watchlist"
+msgstr "danh sách liên lạc của tôi"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "tất cả những trang được ghi nhớ "
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "Tìm kiếm"
+
+#: data/templates/default/sidebar.block.linked.php:23
+msgid "Linked Tags"
+msgstr "Liên kết những từ khoá"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr "Tạo một liên kết"
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+msgid "Delete link"
+msgstr "Xoá một liên kết"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr "Những từ khoá chứa trong từ khoá '%s'"
+
+#: data/templates/default/sidebar.block.menu.php:35
+msgid "Menu Tags"
+msgstr "Danh sách từ khoá"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr "Nhìn thấy tất cả thẻ của bạn"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr "Tất cả thẻ của bạn"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr "Nhìn thấy tất cả từ khoá của ngÆ°á»i dùng này"
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr "tất cả từ khoá từ ngÆ°á»i dùng này"
+
+#: data/templates/default/sidebar.block.menu.php:74
+msgid "See popular tags"
+msgstr "Nhìn thấy những từ khoá thông thÆ°á»ng"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+msgid "Featured Menu Tags"
+msgstr "Danh sách những từ khoá chính"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+"Danh mục này đã được tạo từ các từ khoá (thẻ) và được sắp xếp bởi những "
+"ngÆ°á»i quản trị."
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "Những từ khoá gần đây"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "Quan hệ các từ khoá"
+
+#: data/templates/default/sidebar.block.search.php:28
+msgid "Last Searches"
+msgstr "Những tìm kiếm trước"
+
+#: data/templates/default/sidebar.block.search.php:40
+msgid "Number of bookmarks for this query"
+msgstr "Tổng số trang ghi nhớ tìm thấy cho truy vấn này"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "Thay đổi tên từ khoá"
+msgstr[1] "Renommer les tags"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+msgid "Delete Tag"
+msgstr "Xoá từ khoá"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+msgid "Edit Tag Description"
+msgstr "Sửa mô tả của từ khoá"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr "Sửa mô tả chung của từ khoá"
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr "Tạo một liên kết với một từ khoá khác"
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr "Những ngÆ°á»i dùng má»›i"
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+msgid "bookmarks"
+msgstr "Danh mục những trang ghi nhớ"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr "Nhìn thấy tất cả ngÆ°á»i dùng"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr "Tất cả ngÆ°á»i dùng"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+"Những danh sách liên lạc gần nhau là những danh sách liên lạc tương hỗ với "
+"nhau"
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr "Danh sách liên lạc gần nhau"
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr "Thêm một liên lạc..."
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr "Nhập vào tên truy cập để thêm vào danh sách liên lạc của bạn."
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+msgid "Remove this contact"
+msgstr "BỠliên lạc này"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "Rá»i bá» từ danh sách liên lạc"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "Thêm vào danh sách liên lạc"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+msgid "Edit link"
+msgstr "Sửa một liên kết"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr "Äồng nghÄ©a :"
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr "Tạo một mối liên kết mới"
+
+#: data/templates/default/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 ""
+"Chú ý : gồm có một từ khoá chứa trong trong từ khoá'%s' (e.g. %s>Những lĩnh "
+"vực) để hiển thị từ khoá này trong hộp danh mục"
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr "Tạo "
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr "Những liên kết hiện có :"
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr "Không có liên kết nào"
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr "Äúng"
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr "Không"
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr "Xoá liên kết này"
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr "Tạo một liên kết mới"
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr "CÅ©"
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr "Má»›i"
+
+#: data/templates/default/tagrename.tpl.php:24
+msgid "Rename"
+msgstr "Äổi tên"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr "Bảng chữ cái"
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+msgid "Popularity"
+msgstr "Phổ biến"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+msgid "Home"
+msgstr "Chủ"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+msgid "Watchlist"
+msgstr "Danh mục liên lạc"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "Mô tả cá nhân"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "Thêm một trang ghi nhớ"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "Thoát"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr "Quản trị"
+
+#: data/templates/default/top.inc.php:57
+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 ""
+"Quản trị, sá»± cài đặt của bạn o trong \"Mode Debug\" ($debugMode = true). Äể "
+"đến \"Mode Normal\" và ẩn những thông báo gỡ rối, thay đổi $debugMode "
+"thành false ở trong config.inc.php."
+
+#: data/templates/default/users.tpl.php:20
+msgid "profile"
+msgstr "thông tin cá nhân"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr "Tạo trong"
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr "Quản lí những ngÆ°á»i dùng"
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr "%s, tất cả những trang ghi nhớ và những thẻ bị xoá."
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr "Có vấn đỠvới "
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "Bạn đã không cho phép xoá trang ghi nhớ này"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "Có lỗi trong việc xoá trang ghi nhớ này"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "Tất cả từ khoá"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, php-format
+msgid "User with username %s was not found"
+msgstr "Tên đăng nhập %s đã không tìm thấy."
+
+#: 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 "Cho phép từ chối."
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr "Cập nhật mô tả trang ghi nhớ."
+
+#: www/bookmarkcommondescriptionedit.php:63
+msgid "Failed to update the bookmark common description"
+msgstr "Có lỗi trong việc cập nhật mô tả trang ghi nhớ"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr "Sửa mô tả trang ghi nhớ"
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr "Huá»· bá» từ khoá đã chá»n"
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr ""
+"Trang ghi nhớ của bạn phải có một tiêu đỠtóm tắt và một địa chỉ của trang "
+"đó."
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "Lưu lại trang ghi nhớ này."
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr "(Quay lại trang trước đó.)"
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr ""
+"Có lỗi trong việc lưu lại trang ghi nhớ này. Hãy thử lại hoặc liên lạc với "
+"ngÆ°á»i quản trị."
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "Thêm một trang ghi nhớ"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "Äể thêm má»™t trang ghi nhá»› bạn phải đăng nhập."
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "Những trang ghi nhớ của tôi"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "Sá»­a trang ghi nhá»›"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "Trang ghi nhớ với tên %s đã không tìm thấy"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "Bạn đã không cho phép sửa trang ghi nhớ này."
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "Có lỗi trong khi lưu lại trang ghi nhớ của bạn."
+
+#: www/history.php:61
+msgid "History"
+msgstr "Lưu vết quá trình"
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr "Lưu vết quá trình cho %s"
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr "Äịa chỉ này đã không tìm thấy."
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "Không thể mở dữ liệu XML."
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "Lá»—i XML: %s tại Ä‘Æ°á»ng truyá»n %d"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "Nạp vào những trang ghi nhớ từ del.icio.us"
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "Bạn vừa nhập trang ghi nhớ này."
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "Trang ghi nhớ này đã nạp."
+
+#: www/importNetscape.php:95
+msgid "You have already submitted some of these bookmarks."
+msgstr "Bạn vừa nhập một trong số những trang ghi nhớ này."
+
+#: www/importNetscape.php:115
+msgid "Bookmarks found: "
+msgstr "Trang ghi nhớ này đã được tìm thấy :"
+
+#: www/importNetscape.php:116
+msgid "Bookmarks imported: "
+msgstr "Những trang ghi nhớ đã nạp :"
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr "Nạp những trang ghi nhớ này từ một file trong danh sách duyệt"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr "Xác định kém"
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr "Liên kết mới giữa các từ khoá :"
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr "Nạp một cấu trúc"
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "Bạn vừa thoátt khá»i"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: Những trang ghi nhớ gần đây"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: Những trang ghi nhớ gần đây"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr "Lưu trữ, chia xẻ và gán từ khoá cho những liên kết yêu thích của bạn"
+
+#: www/index.php:97
+msgid "All Bookmarks"
+msgstr "Tất cả các trang ghi nhớ"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr "Có giá trị"
+
+#: www/jsScuttle.php:74
+msgid "Not Available"
+msgstr "Không có giá trị"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "Những thông tin bạn đã nhập không chính xác. Hãy thử lại."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr "Bạn phải nhập vào tên truy cập."
+
+#: www/password.php:40
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr "Bạn <em>phải</em> đưa ra một <abbr title=\"địa chỉ \">E-mail</abbr>."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr "Không có tên truy cập này."
+
+#: www/password.php:51
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"Không phù hợp giữa tên ngÆ°á»i dùng này và <abbr title=\"địachỉ email nàyl"
+"\">e-mail</abbr>."
+
+#: www/password.php:59
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr ""
+"Äã có lá»—i trong khi nhập trang ghi nhá»› này. Hãy thá»­ lại hoặc liên lạc vá»›i "
+"ngÆ°á»i quản trị."
+
+#: www/password.php:63
+msgid "Your new password is:"
+msgstr "Mật khẩu mới của bạn là:"
+
+#: 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 ""
+"Äể giữ an toàn và bảo mật cho những trang ghi nhá»› của bạn, bạn nên thay đổi "
+"mật khẩu trong mô tả cá nhân cho lần đăng nhập sau."
+
+#: www/password.php:66
+#, php-format
+msgid "%s Account Information"
+msgstr "Thông tin tài khoản %s"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr "Mật khẩu mới sẽ được gửi đến địa chỉ email %s"
+
+#: www/password.php:75
+msgid "Forgotten Password"
+msgstr "Quên mật khẩu"
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "Tên truy cập đã không chỉ rõ ."
+
+#: www/profile.php:70
+msgid "My Profile"
+msgstr "Hồ sơ cá nhân"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr "Token không có hiệu lực"
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "Mật khẩu không tương thích với tên truy cập."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "Mật khẩu phải chứa ít nhất 6 kí tự."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Äịa chỉ email không có giá trị."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "Có một lỗi xuất hiện trong khi lưu lại những sửa đổi của bạn."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "Lưu lại những thay đổi."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+#, fuzzy
+msgid "Failed to delete SSL client certificate."
+msgstr "Không thể xoá từ khoá này"
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"Bạn <em>phải</em> đưa ra một tên truy cập, một mật khẩu, một <abbr title="
+"\"điạ chỉ email\">e-mail</abbr>"
+
+#: www/register.php:56
+msgid "This username has been reserved, please make another choice."
+msgstr "Tên truy cập này đã được đăng kí, hãy chá»n tên khác."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "Tên truy cập này đã tồn tại, hãy chá»n má»™t tên khác."
+
+#: www/register.php:64
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr ""
+"Tên truy cập này không được xác nhận (quá ngắn, quá dài, kí tự cấm...), hãy "
+"tạo tên khác."
+
+#: www/register.php:68
+msgid "E-mail address is not valid. Please try again."
+msgstr "Äịa chỉ email không được xác nhận. Hãy thá»­ lại."
+
+#: www/register.php:72
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Câu trả lá»i chống thÆ° rác không được xác nhận. hãy thá»­ lại."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "Bạn đã đăng kí thành công!"
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "Äăng kí thất bại. hãy thá»­ làm lại."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "Những trang ghi nhớ gần đây %s"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "Tìm kiếm trang ghi nhớ"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "Kêt quả tìm kiếm"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr "Có phải bạn không vừa lòng? Bạn có thể thử của chúng tôi "
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr "Liên kết giữa các từ khoá được tạo."
+
+#: www/tag2tagadd.php:57
+msgid "Failed to create the link"
+msgstr "Không thể tạo liên kết này"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr "Thêm một liên kết giữa các từ khoá"
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr "Xoá một liên kết từ khoá"
+
+#: www/tag2tagdelete.php:69
+msgid "Failed to delete the link"
+msgstr "Không thể xoá liên kết này"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr "Xoá một liên kết giữa các từ khoá"
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr "Sửa một liên kết giữa các từ khoá"
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr "Sửa mô tả chung của từ khoá"
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr "Không thể cập nhật mô tả chung của từ khoá"
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr "Từ khoá đã được xoá"
+
+#: www/tagdelete.php:46
+msgid "Failed to delete the tag"
+msgstr "Không thể xoá từ khoá này"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr "Mô tả của từ khoá đã được cập nhật"
+
+#: www/tagedit.php:55
+msgid "Failed to update the tag description"
+msgstr "Không thể cập nhật mô tả của từ khoá"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr "Từ khoá đã được đổi tên"
+
+#: www/tagrename.php:66
+msgid "Failed to rename the tag"
+msgstr "Lỗi trong việc đổi tên từ khoá"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+msgid "Users"
+msgstr "Những ngÆ°á»i tham gia"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "Tên truy cập đã dá»i khá»i danh sách liên lạc của bạn"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "Tên truy cập đã được thêm vào danh sách liên lạc của bạn"
+
+#: www/watchlist.php:104
+msgid "My Watchlist"
+msgstr "Danh sách liên lạc của tôi"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr "Sá»­ dụng API để gá»i yêu cầu chính thức."
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr "Quay lại"
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr "Lá»i khuyên của quản trị:"
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+"Äể khởi Ä‘á»™ng lại Ä‘iá»u khiển bằng tay của máy tìm kiếm Google, hãy đến :"
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+"Nếu không có kết quả nào xuất hiệnt, hãy kiểm tra lại tất cả các Ä‘Æ°á»ng link "
+"được xác nhận trong phần quản lí."
+
+#~ 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 ""
+#~ "<strong><a href=\"register.php\">Register now</a></strong> to start using "
+#~ "%s!"
+#~ msgstr ""
+#~ "<a href=\"register.php\">Enregistrez-vous maintenant</a> pour poster vos "
+#~ "propres signets sur %s !"
+
+#~ msgid ""
+#~ "<a href=\"http://sourceforge.net/projects/semanticscuttle/\">Semantic "
+#~ "Scuttle</a> is licensed under the <a href=\"http://www.gnu.org/copyleft/"
+#~ "gpl.html\"><acronym title=\"GNU's Not Unix\">GNU</acronym> General Public "
+#~ "License</a> (you can host it on your own web server)."
+#~ msgstr ""
+#~ "<a href=\"http://sourceforge.net/projects/semanticscuttle/\">Semantic "
+#~ "Scuttle</a>, sous license <a href=\"http://www.gnu.org/copyleft/gpl.html"
+#~ "\"><acronym title=\"GNU's Not Unix\">GNU</acronym> General Public "
+#~ "License</a> (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 "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"Adresse électronique\">E-mail</abbr>"
diff --git a/data/locales/zh_CN/LC_MESSAGES/messages.mo b/data/locales/zh_CN/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..7c38205
--- /dev/null
+++ b/data/locales/zh_CN/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/data/locales/zh_CN/LC_MESSAGES/messages.po b/data/locales/zh_CN/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..9906096
--- /dev/null
+++ b/data/locales/zh_CN/LC_MESSAGES/messages.po
@@ -0,0 +1,1690 @@
+# Scuttle zh-CN Translation
+# Copyright (C) 2005 - 2006 Scuttle project
+# This file is distributed under the same license as the Scuttle package.
+# Yanni Zheng <ynzheng@gmail.com>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Scuttle\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-01-20 13:58+0100\n"
+"PO-Revision-Date: 2006-03-18 21:45-0800\n"
+"Last-Translator: Marcus Campbell <marcus.campbell@gmail.com>\n"
+"Language-Team: zh-CN <ynzheng@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Chinese\n"
+"X-Poedit-Country: CHINA\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/SemanticScuttle/functions.php:189
+msgid "message_die() was called multiple times."
+msgstr "message_die()被调用了多次"
+
+#: src/SemanticScuttle/functions.php:201
+msgid "SQL Error"
+msgstr "SQL出错"
+
+#: src/SemanticScuttle/functions.php:207
+msgid "Line"
+msgstr "行"
+
+#: src/SemanticScuttle/functions.php:207
+#: data/templates/default/importDelicious.tpl.php:8
+#: data/templates/default/importNetscape.tpl.php:9
+#: data/templates/default/importStructure.tpl.php:10
+msgid "File"
+msgstr "文件"
+
+#: src/SemanticScuttle/functions.php:215
+msgid "Information"
+msgstr "ä¿¡æ¯"
+
+#: src/SemanticScuttle/functions.php:220
+msgid "Critical Information"
+msgstr "关键性信æ¯"
+
+#: src/SemanticScuttle/functions.php:225
+msgid "An error occured"
+msgstr "有错误å‘生"
+
+#: src/SemanticScuttle/functions.php:228
+msgid "General Error"
+msgstr "一般性错误"
+
+#: src/SemanticScuttle/functions.php:236
+msgid "An critical error occured"
+msgstr "有严é‡é”™è¯¯å‘生"
+
+#: src/SemanticScuttle/functions.php:239
+msgid "Critical Error"
+msgstr "严é‡é”™è¯¯"
+
+#: src/SemanticScuttle/functions.php:248
+msgid "DEBUG MODE"
+msgstr "调试模å¼"
+
+#: data/templates/default/about.tpl.php:6
+msgid ""
+"<strong>Store</strong> all your favourite links in one place, accessible "
+"from anywhere."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:7
+msgid ""
+"<strong>Share</strong> your bookmarks with everyone, with friends on your "
+"watchlist or just keep them private."
+msgstr ""
+"您å¯ä»¥å°†æ‚¨æ”¶è—的书签资æºä¸Žæ‰€æœ‰æœ‹å‹åˆ†äº«ï¼Œä¹Ÿè®¸å› æ­¤å‘现与您志åŒé“åˆçš„朋å‹ã€‚或者"
+"ä¿å­˜ä¸ºåªèƒ½æ‚¨è‡ªå·±æŸ¥çœ‹çš„ç§æœ‰çŠ¶æ€"
+
+#: data/templates/default/about.tpl.php:8
+msgid ""
+"<strong>Tag</strong> your bookmarks with as many labels as you want, instead "
+"of wrestling with folders."
+msgstr ""
+"您å¯ä»¥å°†æ‚¨è®¤ä¸ºä¼˜ç§€çš„站点用标签(Tag)定义,这是一ç§æ›´ä¸ºçµæ´»çš„分类形å¼ï¼Œæ–¹ä¾¿æ‚¨"
+"çš„å‘散记忆和管ç†ã€‚"
+
+#: data/templates/default/about.tpl.php:9
+#, fuzzy
+msgid "Register now"
+msgstr "注册"
+
+#: data/templates/default/about.tpl.php:9
+#, php-format
+msgid " to start using %s!"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:12
+msgid "Geek Stuff"
+msgstr "致谢"
+
+#: data/templates/default/about.tpl.php:14
+msgid "is licensed under the "
+msgstr ""
+
+#: data/templates/default/about.tpl.php:14
+msgid "you can freely host it on your own web server."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:15
+#, php-format
+msgid ""
+"%1$s supports most of the <a href=\"http://www.delicious.com/help/api\">del."
+"icio.us <abbr title=\"Application Programming Interface\">API</abbr></a>. "
+"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 ""
+
+#: data/templates/default/about.tpl.php:24
+msgid "Tips"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:26
+msgid "Add search plugin into your browser:"
+msgstr ""
+
+#: data/templates/default/about.tpl.php:27
+msgid ""
+"The secret tag \"system:unfiled\" allows you to find bookmarks without tags."
+msgstr ""
+
+#: data/templates/default/about.tpl.php:28
+msgid ""
+"The secret tag \"system:imported\" allows you to find imported bookmarks."
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:5
+#, fuzzy
+msgid "Users management"
+msgstr "用户å¸å·"
+
+#: data/templates/default/admin.tpl.php:14
+msgid "Public/Shared/Private"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:14
+#: data/templates/default/bookmarks.tpl.php:115
+#, fuzzy
+msgid "bookmark(s)"
+msgstr "书签"
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/tag2tagadd.tpl.php:21
+#: data/templates/default/tag2tagdelete.tpl.php:13
+#: data/templates/default/tag2tagedit.tpl.php:14
+#: data/templates/default/tag2tagedit.tpl.php:35
+#: data/templates/default/tagdelete.tpl.php:6 www/jsScuttle.php:24
+msgid "Are you sure?"
+msgstr ""
+
+#: data/templates/default/admin.tpl.php:19
+#: data/templates/default/bookmarks.tpl.php:305
+msgid "Delete"
+msgstr "删除"
+
+#: data/templates/default/admin.tpl.php:27
+#, fuzzy
+msgid "Other actions"
+msgstr "步骤和导入方法"
+
+#: data/templates/default/admin.tpl.php:29
+msgid "Check all URLs (May take some time)"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:16
+msgid ""
+"Collaborative description: these fields can be viewed and modified by every "
+"users"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:18
+#: data/templates/default/bookmarks.tpl.php:159
+#: data/templates/default/editbookmark.tpl.php:51
+msgid "Title"
+msgstr "书签标题"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:23
+#: data/templates/default/editbookmark.tpl.php:57
+#: data/templates/default/editprofile.tpl.php:53
+#: data/templates/default/profile.tpl.php:33
+#: data/templates/default/tagcommondescriptionedit.tpl.php:13
+#: data/templates/default/tagedit.tpl.php:12
+msgid "Description"
+msgstr "æè¿°"
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:31
+#: data/templates/default/tagcommondescriptionedit.tpl.php:21
+msgid "Last modification:"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:43
+#: data/templates/default/tagcommondescriptionedit.tpl.php:33
+#: data/templates/default/tagedit.tpl.php:18
+msgid "Update"
+msgstr ""
+
+#: data/templates/default/bookmarkcommondescriptionedit.tpl.php:44
+#: data/templates/default/editbookmark.tpl.php:111
+#: data/templates/default/tag2tagadd.tpl.php:24
+#: data/templates/default/tag2tagedit.tpl.php:38
+#: data/templates/default/tagcommondescriptionedit.tpl.php:34
+#: data/templates/default/tagedit.tpl.php:19
+#: data/templates/default/tagrename.tpl.php:25
+msgid "Cancel"
+msgstr ""
+
+#: data/templates/default/bookmarklet.inc.php:1
+msgid "Bookmarklet"
+msgstr "Bookmarklet"
+
+#: data/templates/default/bookmarklet.inc.php:11
+#, 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 ""
+"Bookmarklet是一个内å«ç®€å•JavaScript代ç çš„书签,使用的方法和您的æµè§ˆå™¨ä¸­çš„书签"
+"一样,ä¸åŒçš„地方是:书签是用æ¥è®°å½•æµè§ˆçš„ä½ç½®ï¼Œè€ŒBookmarklet则是记录æŸäº›å¯¹æ‚¨æ­£"
+"在æµè§ˆçš„页é¢è¿›è¡Œå¤„ç†çš„程åºå’Œæ–¹æ³•ã€‚<br>使用Bookmarklet之å‰ï¼Œæ‚¨æ— éœ€å¦å¤–安装或下"
+"载程å¼ï¼Œåªè¦ç®€å•çš„将如下链接加入您的æµè§ˆå™¨ä¹¦ç­¾æˆ–是收è—夹之中。 <br>选择如下"
+"Bookmarklet之一,并将其加入到您的æµè§ˆå™¨ä¹¦ç­¾ä¸­,当您æµè§ˆå¹¶å¸Œæœ›æ”¶è—其他网站页é¢"
+"æ—¶,åªéœ€ç‚¹å‡»æ‚¨çš„æµè§ˆå™¨ä¸­ Bookmarklet 书签,就会将您希望收è—的页é¢å‘布到 %s"
+
+#: data/templates/default/bookmarklet.inc.php:20
+#, 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 ""
+"Bookmarklet是一个内å«ç®€å•JavaScript代ç çš„书签,使用的方法和您的æµè§ˆå™¨ä¸­çš„书签"
+"一样,ä¸åŒçš„地方是:书签是用æ¥è®°å½•æµè§ˆçš„ä½ç½®ï¼Œè€ŒBookmarklet则是记录æŸäº›å¯¹æ‚¨æ­£"
+"在æµè§ˆçš„页é¢è¿›è¡Œå¤„ç†çš„程åºå’Œæ–¹æ³•ã€‚<br>使用Bookmarklet之å‰ï¼Œæ‚¨æ— éœ€å¦å¤–安装或下"
+"载程å¼ï¼Œåªè¦ç®€å•çš„将如下链接加入您的æµè§ˆå™¨ä¹¦ç­¾æˆ–是收è—夹之中。 <br>选择如下"
+"Bookmarklet之一,并将其加入到您的æµè§ˆå™¨ä¹¦ç­¾ä¸­,当您æµè§ˆå¹¶å¸Œæœ›æ”¶è—其他网站页é¢"
+"æ—¶,åªéœ€ç‚¹å‡»æ‚¨çš„æµè§ˆå™¨ä¸­ Bookmarklet 书签,就会将您希望收è—的页é¢å‘布到 %s"
+
+#: data/templates/default/bookmarklet.inc.php:67
+#: data/templates/default/bookmarklet.inc.php:100
+#, php-format
+msgid "Post to %s"
+msgstr "收è—到 %s"
+
+#: data/templates/default/bookmarklet.inc.php:93
+#: data/templates/default/bookmarklet.inc.php:110
+#, php-format
+msgid "Post to %s (Pop-up)"
+msgstr "收è—到 %s(弹出窗å£ï¼‰ "
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:24
+#, php-format
+msgid "Voting <span class=\"voting\">%d</span>"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:32
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:35
+msgid "Vote for"
+msgstr ""
+
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:43
+#: data/templates/default/bookmarks-vote-horizontal.inc.tpl.php:46
+msgid "Vote against"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:43
+msgid "Bookmarks on this page are managed by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:73
+#: data/templates/default/bookmarks.tpl.php:74
+msgid "Edit the common description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:77
+#: data/templates/default/bookmarks.tpl.php:78
+msgid "Edit the common description of this bookmark"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:98
+#: data/templates/default/bookmarks.tpl.php:99
+msgid "Edit your personal description of this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:115
+#: data/templates/default/tags.tpl.php:10
+#: data/templates/default/users.tpl.php:8
+msgid "Sort by:"
+msgstr "排åºæ–¹å¼:"
+
+#: data/templates/default/bookmarks.tpl.php:157
+msgid "Date"
+msgstr "å‘布时间"
+
+#: data/templates/default/bookmarks.tpl.php:162
+msgid "Voting"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:171
+msgid "Bookmarks from other users for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:176
+msgid "Only your bookmarks for this tag"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:199
+#: data/templates/default/bookmarks.tpl.php:205
+msgid "First"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:200
+#: data/templates/default/bookmarks.tpl.php:206
+msgid "Previous"
+msgstr "上一页"
+
+#: data/templates/default/bookmarks.tpl.php:213
+#: data/templates/default/bookmarks.tpl.php:216
+msgid "Next"
+msgstr "下一页"
+
+#: data/templates/default/bookmarks.tpl.php:214
+#: data/templates/default/bookmarks.tpl.php:217
+msgid "Last"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:233
+#, php-format
+msgid "Page %d of %d"
+msgstr "第 %d 页 / 共 %d 页"
+
+#: data/templates/default/bookmarks.tpl.php:295
+#, fuzzy
+msgid "Tags:"
+msgstr "标签"
+
+#: data/templates/default/bookmarks.tpl.php:302
+msgid "Edit"
+msgstr "编辑"
+
+#: data/templates/default/bookmarks.tpl.php:310
+msgid "Last update"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:313
+msgid "by"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:317
+msgid "you"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:333
+#, fuzzy, php-format
+msgid " and %s1 other%s"
+msgstr "åŒæ—¶è¢«å…¶ä»– %s 个用户收è—"
+
+#: data/templates/default/bookmarks.tpl.php:336
+#, fuzzy, php-format
+msgid " and %2$s%1$s others%3$s"
+msgstr "åŒæ—¶è¢«å…¶ä»– %s 个用户收è—"
+
+#: data/templates/default/bookmarks.tpl.php:350
+#, fuzzy
+msgid "Copy this bookmark to YOUR bookmarks."
+msgstr ""
+"导入(Internet Explorer, Mozilla Firefox and Netscape)æµè§ˆå™¨çš„书签文件"
+
+#: data/templates/default/bookmarks.tpl.php:351
+msgid "Copy"
+msgstr "å¤åˆ¶"
+
+#: data/templates/default/bookmarks.tpl.php:376
+msgid "This bookmark is certified by an admin user."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:430
+#, fuzzy
+msgid "Private Note on this bookmark"
+msgstr "您没有æƒé™ç¼–辑此æ¡ä¹¦ç­¾"
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Come back to the top of this page."
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:444
+msgid "Top of the page"
+msgstr ""
+
+#: data/templates/default/bookmarks.tpl.php:450
+msgid "No bookmarks available"
+msgstr "尚未有书签列入"
+
+#: data/templates/default/bottom.inc.php:5
+#: data/templates/default/toolbar.inc.php:15
+#: data/templates/default/toolbar.inc.php:28 www/about.php:23 www/about.php:24
+msgid "About"
+msgstr "关于"
+
+#: data/templates/default/bottom.inc.php:7
+msgid "Propulsed by "
+msgstr ""
+
+#: data/templates/default/dynamictags.inc.php:56
+#: data/templates/default/sidebar.block.common.php:19
+#: data/templates/default/sidebar.block.popular.php:34
+#: data/templates/default/sidebar.block.recent.php:29
+#: data/templates/default/tags.tpl.php:19
+msgid "bookmark"
+msgid_plural "bookmarks"
+msgstr[0] "书签"
+msgstr[1] "书签"
+
+#: data/templates/default/dynamictags.inc.php:105
+#: data/templates/default/sidebar.block.common.php:9
+#: data/templates/default/sidebar.block.menu.php:74
+#: data/templates/default/sidebar.block.popular.php:23
+#: data/templates/default/sidebar.block.recent.php:34
+#: data/templates/default/toolbar.inc.php:27 www/populartags.php:46
+msgid "Popular Tags"
+msgstr "热门标签"
+
+#: data/templates/default/dynamictags.inc.php:108
+#, fuzzy
+msgid "Popular Tags From All Users"
+msgstr "热门标签"
+
+#: data/templates/default/editbookmark.tpl.php:46
+msgid "Address"
+msgstr "网å€"
+
+#: data/templates/default/editbookmark.tpl.php:48
+#: data/templates/default/editbookmark.tpl.php:53
+#: data/templates/default/editprofile.tpl.php:29
+#: data/templates/default/tagrename.tpl.php:14
+#: data/templates/default/tagrename.tpl.php:19
+msgid "Required"
+msgstr "必填项目"
+
+#: data/templates/default/editbookmark.tpl.php:58
+msgid "Add Note"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:61
+msgid ""
+"You can use anchors to delimite attributes. for example: [publisher]blah[/"
+"publisher] "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:64
+msgid "Suggested anchors: "
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:76
+#, fuzzy
+msgid "Private Note"
+msgstr "仅本用户"
+
+#: data/templates/default/editbookmark.tpl.php:78
+msgid "Just visible by you and your contacts."
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:82
+#: data/templates/default/toolbar.inc.php:10 www/tags.php:45 www/tags.php:67
+msgid "Tags"
+msgstr "标签"
+
+#: data/templates/default/editbookmark.tpl.php:86
+msgid "Comma-separated"
+msgstr "以å•å­—节逗å·åˆ†å¼€"
+
+#: data/templates/default/editbookmark.tpl.php:90
+#: data/templates/default/tag2tagadd.tpl.php:9
+msgid ""
+"Note: use \">\" to include one tag in another. e.g.: europe>france>paris"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:94
+#: data/templates/default/tag2tagadd.tpl.php:8
+msgid "Note: use \"=\" to make synonym two tags. e.g.: france=frenchcountry"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:97
+#: data/templates/default/importDelicious.tpl.php:15
+#: data/templates/default/importNetscape.tpl.php:16
+msgid "Privacy"
+msgstr "ç§å¯†"
+
+#: data/templates/default/editbookmark.tpl.php:100
+#: data/templates/default/importDelicious.tpl.php:18
+#: data/templates/default/importNetscape.tpl.php:19
+msgid "Public"
+msgstr "公开å‘布"
+
+#: data/templates/default/editbookmark.tpl.php:101
+msgid "Shared with Watch List"
+msgstr "仅关注者"
+
+#: data/templates/default/editbookmark.tpl.php:102
+#: data/templates/default/importDelicious.tpl.php:20
+#: data/templates/default/importNetscape.tpl.php:21
+msgid "Private"
+msgstr "仅本用户"
+
+#: data/templates/default/editbookmark.tpl.php:115
+msgid "Delete Bookmark"
+msgstr "删除书签"
+
+#: data/templates/default/editbookmark.tpl.php:120
+msgid "edit common description"
+msgstr ""
+
+#: data/templates/default/editbookmark.tpl.php:212
+#: data/templates/default/importDelicious.tpl.php:26
+#: data/templates/default/importNetscape.tpl.php:27
+#: data/templates/default/importStructure.tpl.php:16
+msgid "Import"
+msgstr "导入书签"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Import bookmarks from bookmark file"
+msgstr ""
+"导入(Internet Explorer, Mozilla Firefox and Netscape)æµè§ˆå™¨çš„书签文件"
+
+#: data/templates/default/editbookmark.tpl.php:214
+msgid "Internet Explorer, Mozilla Firefox and Netscape"
+msgstr "Internet Explorer, Mozilla Firefox and Netscape"
+
+#: data/templates/default/editbookmark.tpl.php:215
+msgid "Import bookmarks from del.icio.us"
+msgstr "导入del.icio.us的书签文件"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:11
+msgid "SSL client certificates"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:17
+msgid "Serial"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:18
+#: data/templates/default/editprofile.tpl.php:45
+#: data/templates/default/profile.tpl.php:17
+msgid "Name"
+msgstr "用户全å"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:19
+#: data/templates/default/profile.tpl.php:11
+#, fuzzy
+msgid "Email"
+msgstr "用户Email"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:20
+msgid "Issuer"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:30
+#, fuzzy
+msgid "delete"
+msgstr "删除"
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:43
+msgid "No certificates registered"
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:48
+msgid "Your current certificate is already registered with your account."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:53
+msgid "Register current certificate to automatically login."
+msgstr ""
+
+#: data/templates/default/editprofile-sslclientcerts.tpl.php:59
+msgid "Your browser does not provide a certificate."
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:8
+msgid "Account Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:12
+#: data/templates/default/login.tpl.php:21
+#: data/templates/default/password.tpl.php:10
+#: data/templates/default/profile.tpl.php:6
+#: data/templates/default/register.tpl.php:16
+msgid "Username"
+msgstr "用户å¸å·"
+
+#: data/templates/default/editprofile.tpl.php:17
+msgid "New Password"
+msgstr "新的密ç "
+
+#: data/templates/default/editprofile.tpl.php:22
+#, fuzzy
+msgid "Confirm Password"
+msgstr "确认密ç "
+
+#: data/templates/default/editprofile.tpl.php:27
+#: data/templates/default/password.tpl.php:14
+#: data/templates/default/register.tpl.php:26
+msgid "E-mail"
+msgstr "用户Email"
+
+#: data/templates/default/editprofile.tpl.php:32
+#, fuzzy
+msgid "Private RSS Feed"
+msgstr "仅本用户"
+
+#: data/templates/default/editprofile.tpl.php:34
+msgid "Enable"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:36
+#, fuzzy
+msgid "Generate New Key"
+msgstr "新的密ç "
+
+#: data/templates/default/editprofile.tpl.php:41
+msgid "Personal Details"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:49
+#: data/templates/default/profile.tpl.php:23
+msgid "Homepage"
+msgstr "用户网站"
+
+#: data/templates/default/editprofile.tpl.php:58 www/edit.php:113
+msgid "Save Changes"
+msgstr "ä¿å­˜ä¿®æ”¹"
+
+#: data/templates/default/editprofile.tpl.php:63
+#: data/templates/default/sidebar.block.tagactions.php:17
+#: data/templates/default/sidebar.block.watchstatus.php:18
+#, fuzzy
+msgid "Actions"
+msgstr "步骤和导入方法"
+
+#: data/templates/default/editprofile.tpl.php:66
+#, fuzzy
+msgid "Export bookmarks"
+msgstr "您的书签"
+
+#: data/templates/default/editprofile.tpl.php:68
+msgid "HTML file (for browsers)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:69
+msgid "XML file (like del.icio.us)"
+msgstr ""
+
+#: data/templates/default/editprofile.tpl.php:70
+msgid "CSV file (for spreadsheet tools)"
+msgstr ""
+
+#: data/templates/default/error.404.tpl.php:5
+msgid "Not Found"
+msgstr "文件ä¸å­˜åœ¨"
+
+#: data/templates/default/error.404.tpl.php:6
+msgid "The requested URL was not found on this server"
+msgstr "您指定的文件ä¸å­˜åœ¨ã€‚"
+
+#: data/templates/default/error.500.tpl.php:5
+msgid "General server error"
+msgstr "一般性æœåŠ¡å™¨é”™è¯¯"
+
+#: data/templates/default/error.500.tpl.php:6
+msgid "The requested URL could not be processed"
+msgstr "无法对您指定的文件进行导入æ“作。"
+
+#: data/templates/default/importDelicious.tpl.php:19
+#: data/templates/default/importNetscape.tpl.php:20
+#, fuzzy
+msgid "Shared with Watchlist"
+msgstr "仅关注者"
+
+#: data/templates/default/importDelicious.tpl.php:31
+#: data/templates/default/importNetscape.tpl.php:32
+#: data/templates/default/importStructure.tpl.php:21
+msgid "Instructions"
+msgstr "步骤和导入方法"
+
+#: data/templates/default/importDelicious.tpl.php:33
+#, fuzzy
+msgid ""
+"Log in to the <a href=\"https://api.del.icio.us/v1/posts/all\">export page "
+"at del.icio.us</a>"
+msgstr ""
+"登录<a href=\"http://del.icio.us/api/posts/all\">del.icio.us</a>导出书签数æ®"
+
+#: data/templates/default/importDelicious.tpl.php:34
+msgid ""
+"Save the resulting <abbr title=\"Extensible Markup Language\">XML</abbr> "
+"file to your computer"
+msgstr ""
+"ä¿å­˜<abbr title=\"Extensible Markup Language\">XML</abbr>文件到您的电脑中。"
+
+#: data/templates/default/importDelicious.tpl.php:35
+msgid ""
+"Click <kbd>Browse...</kbd> to find this file on your computer. The maximum "
+"size the file can be is 1MB"
+msgstr "点击<kbd>æµè§ˆ...</kbd>在您的电脑中找到书签文件,最大å…许导入1Mbæ•°æ®"
+
+#: data/templates/default/importDelicious.tpl.php:36
+#: data/templates/default/importNetscape.tpl.php:43
+msgid "Select the default privacy setting for your imported bookmarks"
+msgstr "为导入的书签选择默认ç§å¯†è®¾ç½®"
+
+#: data/templates/default/importDelicious.tpl.php:37
+#: data/templates/default/importNetscape.tpl.php:44
+msgid ""
+"Click <kbd>Import</kbd> to start importing the bookmarks; it may take a "
+"minute"
+msgstr "点击<kbd>导入</kbd>书签开始导入,耗时1分钟左å³ã€‚"
+
+#: data/templates/default/importNetscape.tpl.php:35
+msgid "Export your bookmarks from your browser to a file"
+msgstr "将您的æµè§ˆå™¨ä¹¦ç­¾æ•°æ®å¯¼å‡ºä¸ºä¸€ä¸ªæ–‡ä»¶"
+
+#: data/templates/default/importNetscape.tpl.php:37
+msgid ""
+"Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites"
+msgstr "Internet Explorer: <kbd>文件 &gt; 导入和导出... &gt; 导出收è—夹 "
+
+#: data/templates/default/importNetscape.tpl.php:38
+msgid ""
+"Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; "
+"Export..."
+msgstr ""
+"Mozilla Firefox: <kbd>书签 &gt; 管ç†ä¹¦ç­¾... &gt; 文件 &gt; 导出书签... "
+
+#: data/templates/default/importNetscape.tpl.php:39
+msgid ""
+"Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export..."
+msgstr "Netscape: <kbd>书签 &gt; 管ç†ä¹¦ç­¾... &gt; 工具 &gt;> 导出书签... "
+
+#: data/templates/default/importNetscape.tpl.php:42
+msgid ""
+"Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. "
+"The maximum size the file can be is 1MB"
+msgstr "点击<kbd>æµè§ˆ...</kbd> 在您的电脑中找到书签文件,最大å…许导入1Mbæ•°æ®"
+
+#: data/templates/default/importStructure.tpl.php:24
+msgid "Create your structure into a simple text file and following this model:"
+msgstr ""
+
+#: data/templates/default/importStructure.tpl.php:35
+msgid ""
+"Then import the file. The tags and their relations will be added to your "
+"profile."
+msgstr ""
+
+#: data/templates/default/login.tpl.php:13
+msgid "Please activate cookies"
+msgstr ""
+
+#: data/templates/default/login.tpl.php:26
+#: data/templates/default/register.tpl.php:21
+msgid "Password"
+msgstr "密ç "
+
+#: data/templates/default/login.tpl.php:28
+msgid "Don't ask for my password for 2 weeks"
+msgstr "存储å¸å·å¯†ç ä¿¡æ¯2周"
+
+#: data/templates/default/login.tpl.php:32
+#: data/templates/default/toolbar.inc.php:29 www/login.php:57
+msgid "Log In"
+msgstr "登陆"
+
+#: data/templates/default/login.tpl.php:36
+msgid "Forgotten your password?"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/password.tpl.php:19
+#, fuzzy
+msgid "Generate Password"
+msgstr "新的密ç "
+
+#: data/templates/default/profile.tpl.php:28
+msgid "Member Since"
+msgstr "注册时间"
+
+#: data/templates/default/profile.tpl.php:40
+#: data/templates/default/sidebar.block.watchlist.php:30
+#, fuzzy
+msgid "Watching"
+msgstr "我关注的其他用户"
+
+#: data/templates/default/profile.tpl.php:55
+#: data/templates/default/sidebar.block.watchlist.php:52
+msgid "Watched By"
+msgstr "关注该用户的其他用户"
+
+#: data/templates/default/profile.tpl.php:68
+#: data/templates/default/toolbar.inc.php:9
+#, fuzzy
+msgid "Bookmarks"
+msgstr "%s 书签"
+
+#: data/templates/default/profile.tpl.php:69
+#, fuzzy
+msgid "Go to bookmarks"
+msgstr "您的书签"
+
+#: data/templates/default/register.tpl.php:11
+#, php-format
+msgid ""
+"Sign up here to create a free %s account. All the information requested "
+"below is required"
+msgstr "您å¯ä»¥å…费注册æˆä¸º %s 的用户,请填写如下表å•:"
+
+#: data/templates/default/register.tpl.php:18
+msgid ""
+" at least 5 characters, alphanumeric (no spaces, no dots or other special "
+"ones)"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:28
+msgid " to send you your password if you forget it"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:33
+msgid "Antispam question"
+msgstr ""
+
+#: data/templates/default/register.tpl.php:41
+#: data/templates/default/toolbar.inc.php:31 www/register.php:99
+msgid "Register"
+msgstr "注册"
+
+#: data/templates/default/search.menu.php:27
+#, fuzzy
+msgid "Search..."
+msgstr "æœç´¢"
+
+#: data/templates/default/search.menu.php:28
+#, fuzzy
+msgid "in"
+msgstr "行"
+
+#: data/templates/default/search.menu.php:34
+#, fuzzy
+msgid "this user's bookmarks"
+msgstr "%s 书签"
+
+#: data/templates/default/search.menu.php:39
+msgid "my bookmarks"
+msgstr "您的书签"
+
+#: data/templates/default/search.menu.php:40
+#, fuzzy
+msgid "my watchlist"
+msgstr "将用户加入到您的关注列表"
+
+#: data/templates/default/search.menu.php:44
+msgid "all bookmarks"
+msgstr "所有书签"
+
+#: data/templates/default/search.menu.php:54
+msgid "Search"
+msgstr "æœç´¢"
+
+#: data/templates/default/sidebar.block.linked.php:23
+#, fuzzy
+msgid "Linked Tags"
+msgstr "å…³è”标签"
+
+#: data/templates/default/sidebar.block.linked.php:28
+#: data/templates/default/sidebar.block.menu.php:46
+msgid "Add new link"
+msgstr ""
+
+#: data/templates/default/sidebar.block.linked.php:29
+#: data/templates/default/sidebar.block.menu.php:47
+#, fuzzy
+msgid "Delete link"
+msgstr "删除"
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, php-format
+msgid "Tags included into the tag '%s'"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:35
+#, fuzzy
+msgid "Menu Tags"
+msgstr "最新标签"
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "See all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:68
+msgid "all your tags"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "See all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:70
+msgid "all tags from this user"
+msgstr ""
+
+#: data/templates/default/sidebar.block.menu.php:74
+#, fuzzy
+msgid "See popular tags"
+msgstr "热门标签"
+
+#: data/templates/default/sidebar.block.menu2.php:21
+#, fuzzy
+msgid "Featured Menu Tags"
+msgstr "å…³è”标签"
+
+#: data/templates/default/sidebar.block.menu2.php:25
+msgid "This menu is composed of keywords (tags) organized by admins."
+msgstr ""
+
+#: data/templates/default/sidebar.block.recent.php:18
+msgid "Recent Tags"
+msgstr "最新标签"
+
+#: data/templates/default/sidebar.block.related.php:26
+msgid "Related Tags"
+msgstr "å…³è”标签"
+
+#: data/templates/default/sidebar.block.search.php:28
+#, fuzzy
+msgid "Last Searches"
+msgstr "æœç´¢"
+
+#: data/templates/default/sidebar.block.search.php:40
+#, fuzzy
+msgid "Number of bookmarks for this query"
+msgstr "尚无书签"
+
+#: data/templates/default/sidebar.block.tagactions.php:9 www/tagrename.php:72
+#, fuzzy
+msgid "Rename Tag"
+msgid_plural "Rename Tags"
+msgstr[0] "å…³è”标签"
+msgstr[1] "å…³è”标签"
+
+#: data/templates/default/sidebar.block.tagactions.php:22 www/tagdelete.php:54
+#, fuzzy
+msgid "Delete Tag"
+msgstr "删除"
+
+#: data/templates/default/sidebar.block.tagactions.php:24 www/tagedit.php:61
+#, fuzzy
+msgid "Edit Tag Description"
+msgstr "æè¿°"
+
+#: data/templates/default/sidebar.block.tagactions.php:26
+#: www/tagcommondescriptionedit.php:76
+msgid "Edit Tag Common Description"
+msgstr ""
+
+#: data/templates/default/sidebar.block.tagactions.php:28
+msgid "Create a link to another tag"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:14
+msgid "New Users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:23
+#: data/templates/default/users.tpl.php:24
+#, fuzzy
+msgid "bookmarks"
+msgstr "书签"
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "See all users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.users.php:30
+msgid "All users"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts are mutual contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:19
+msgid "Close contacts"
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Add a contact..."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:36
+msgid "Type a username to add it to your contacts."
+msgstr ""
+
+#: data/templates/default/sidebar.block.watchlist.php:44
+#, fuzzy
+msgid "Remove this contact"
+msgstr "将用户从您的关注列表中删除"
+
+#: data/templates/default/sidebar.block.watchstatus.php:11
+msgid "Remove from Watchlist"
+msgstr "将用户从您的关注列表中删除"
+
+#: data/templates/default/sidebar.block.watchstatus.php:13
+msgid "Add to Watchlist"
+msgstr "将用户加入到您的关注列表"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:18
+#, fuzzy
+msgid "Edit link"
+msgstr "编辑"
+
+#: data/templates/default/sidebar.linkedtags.inc.php:47
+msgid "Synonyms:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:12
+msgid "Create new link:"
+msgstr ""
+
+#: data/templates/default/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 ""
+
+#: data/templates/default/tag2tagadd.tpl.php:23
+#: data/templates/default/tag2tagedit.tpl.php:37
+msgid "Create"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:35
+#: data/templates/default/tag2tagdelete.tpl.php:27
+#: data/templates/default/tag2tagedit.tpl.php:51
+msgid "Existing links:"
+msgstr ""
+
+#: data/templates/default/tag2tagadd.tpl.php:53
+#: data/templates/default/tag2tagdelete.tpl.php:45
+#: data/templates/default/tag2tagedit.tpl.php:69
+msgid "No links"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:15
+#: data/templates/default/tag2tagedit.tpl.php:16
+#: data/templates/default/tagdelete.tpl.php:8 www/jsScuttle.php:24
+msgid "Yes"
+msgstr ""
+
+#: data/templates/default/tag2tagdelete.tpl.php:16
+#: data/templates/default/tag2tagedit.tpl.php:17
+#: data/templates/default/tagdelete.tpl.php:9 www/jsScuttle.php:24
+msgid "No"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:6
+msgid "Delete the link"
+msgstr ""
+
+#: data/templates/default/tag2tagedit.tpl.php:29
+msgid "Create new link"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:12
+msgid "Old"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:17
+msgid "New"
+msgstr ""
+
+#: data/templates/default/tagrename.tpl.php:24
+#, fuzzy
+msgid "Rename"
+msgstr "用户å¸å·"
+
+#: data/templates/default/tags.tpl.php:11
+#: data/templates/default/users.tpl.php:9
+msgid "Alphabet"
+msgstr ""
+
+#: data/templates/default/tags.tpl.php:12
+#: data/templates/default/users.tpl.php:10
+#, fuzzy
+msgid "Popularity"
+msgstr "热门标签"
+
+#: data/templates/default/toolbar.inc.php:8
+#: data/templates/default/toolbar.inc.php:26
+#, fuzzy
+msgid "Home"
+msgstr "用户网站"
+
+#: data/templates/default/toolbar.inc.php:11 www/watchlist.php:106
+#, fuzzy
+msgid "Watchlist"
+msgstr "我关注的其他用户"
+
+#: data/templates/default/toolbar.inc.php:12 www/profile.php:79
+msgid "Profile"
+msgstr "个人资料"
+
+#: data/templates/default/toolbar.inc.php:13 www/bookmarks.php:215
+msgid "Add a Bookmark"
+msgstr "添加书签"
+
+#: data/templates/default/toolbar.inc.php:14
+msgid "Log Out"
+msgstr "登出"
+
+#: data/templates/default/toolbar.inc.php:17
+msgid "Admin"
+msgstr ""
+
+#: data/templates/default/top.inc.php:57
+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/default/users.tpl.php:20
+#, fuzzy
+msgid "profile"
+msgstr "个人资料"
+
+#: data/templates/default/users.tpl.php:21
+msgid "created in"
+msgstr ""
+
+#: www/admin.php:32
+msgid "Manage users"
+msgstr ""
+
+#: www/admin.php:69
+#, php-format
+msgid "%s and all his bookmarks and tags were deleted."
+msgstr ""
+
+#: www/admin.php:76
+msgid "Problem with "
+msgstr ""
+
+#: www/ajaxDelete.php:37
+msgid "You are not allowed to delete this bookmark"
+msgstr "您没有æƒé™åˆ é™¤æ­¤æ¡ä¹¦ç­¾"
+
+#: www/ajaxDelete.php:41 www/edit.php:103
+msgid "Failed to delete bookmark"
+msgstr "删除书签失败"
+
+#: www/alltags.php:50
+msgid "All Tags"
+msgstr "所有标签"
+
+#: www/alltags.php:56 www/bookmarks.php:99 www/populartags.php:52
+#: www/profile.php:55 www/rss.php:100 www/search.php:111 www/watch.php:45
+#: www/watchlist.php:61
+#, fuzzy, php-format
+msgid "User with username %s was not found"
+msgstr "未å‘现以 %s 为用户å的用户"
+
+#: 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 ""
+
+#: www/bookmarkcommondescriptionedit.php:60
+msgid "Bookmark common description updated"
+msgstr ""
+
+#: www/bookmarkcommondescriptionedit.php:63
+#, fuzzy
+msgid "Failed to update the bookmark common description"
+msgstr "删除书签失败"
+
+#: www/bookmarkcommondescriptionedit.php:71
+msgid "Edit Bookmark Common Description"
+msgstr ""
+
+#: www/bookmarks.php:114 www/tags.php:47
+msgid "Remove the tag from the selection"
+msgstr ""
+
+#: www/bookmarks.php:134 www/edit.php:65
+msgid "Your bookmark must have a title and an address"
+msgstr "æ¯æ¡ä¹¦ç­¾å¿…须包å«æ ‡é¢˜å’Œç½‘å€"
+
+#: www/bookmarks.php:139
+msgid "This bookmark URL may not be added"
+msgstr ""
+
+#: www/bookmarks.php:158 www/edit.php:83 www/edit.php:86
+msgid "Bookmark saved"
+msgstr "录入书签已被æˆåŠŸä¿å­˜"
+
+#: www/bookmarks.php:158
+msgid "(Come back to previous page.)"
+msgstr ""
+
+#: www/bookmarks.php:165 www/import.php:106 www/importNetscape.php:108
+msgid ""
+"There was an error saving your bookmark. Please try again or contact the "
+"administrator."
+msgstr "ä¿å­˜ä¹¦ç­¾æ—¶å‡ºé”™ï¼Œè¯·å†ä¿å­˜é‡è¯•ä¸€æ¬¡æˆ–者è”系管ç†å‘˜"
+
+#: www/bookmarks.php:222
+msgid "Add Bookmark"
+msgstr "添加书签"
+
+#: www/bookmarks.php:225
+msgid "You must be logged in before you can add bookmarks."
+msgstr "您必须登陆åŽæ‰èƒ½æ·»åŠ ä¹¦ç­¾"
+
+#: www/bookmarks.php:270
+#, php-format
+msgid "%s: %s"
+msgstr ""
+
+#: www/bookmarks.php:283
+#, php-format
+msgid "%s: %s (+private %s)"
+msgstr ""
+
+#: www/bookmarks.php:305 www/bookmarks.php:306
+msgid "My Bookmarks"
+msgstr "您的书签"
+
+#: www/edit.php:44 www/edit.php:45
+msgid "Edit Bookmark"
+msgstr "编辑书签"
+
+#: www/edit.php:51
+#, php-format
+msgid "Bookmark with id %s not was not found"
+msgstr "id为 %s 的书签ä¸å­˜åœ¨"
+
+#: www/edit.php:57
+msgid "You are not allowed to edit this bookmark"
+msgstr "您没有æƒé™ç¼–辑此æ¡ä¹¦ç­¾"
+
+#: www/edit.php:77
+msgid "Error while saving your bookmark"
+msgstr "在ä¿å­˜ä¹¦ç­¾æ—¶å‡ºé”™"
+
+#: www/history.php:61
+msgid "History"
+msgstr ""
+
+#: www/history.php:62
+#, php-format
+msgid "History for %s"
+msgstr ""
+
+#: www/history.php:84
+msgid "Address was not found"
+msgstr ""
+
+#: www/import.php:47
+msgid "Could not open XML input"
+msgstr "无法打开XML输入"
+
+#: www/import.php:51
+#, php-format
+msgid "XML error: %s at line %d"
+msgstr "XML错误: %s å‘生在 %d è¡Œ"
+
+#: www/import.php:60
+msgid "Import Bookmarks from del.icio.us"
+msgstr "将del.icio.us的书签文件导入 "
+
+#: www/import.php:93
+msgid "You have already submitted this bookmark."
+msgstr "您过去已ç»å½•å…¥è¿‡è¿™æ¡ä¹¦ç­¾"
+
+#: www/import.php:104
+msgid "Bookmark imported."
+msgstr "书签导入完毕."
+
+#: www/importNetscape.php:95
+#, fuzzy
+msgid "You have already submitted some of these bookmarks."
+msgstr "您过去已ç»å½•å…¥è¿‡è¿™æ¡ä¹¦ç­¾"
+
+#: www/importNetscape.php:115
+#, fuzzy
+msgid "Bookmarks found: "
+msgstr "尚无书签"
+
+#: www/importNetscape.php:116
+#, fuzzy
+msgid "Bookmarks imported: "
+msgstr "书签导入完毕."
+
+#: www/importNetscape.php:117 www/importNetscape.php:122
+msgid "Import Bookmarks from Browser File"
+msgstr ""
+"å°†æµè§ˆå™¨çš„书签文件导入(Internet Explorer, Mozilla Firefox and Netscape)"
+
+#: www/importStructure.php:61
+msgid "Bad indentation"
+msgstr ""
+
+#: www/importStructure.php:67
+msgid "New links between tags: "
+msgstr ""
+
+#: www/importStructure.php:72
+msgid "Import Structure"
+msgstr ""
+
+#: www/index.php:38
+msgid "You have now logged out"
+msgstr "您已ç»æˆåŠŸç™»å‡º"
+
+#: www/index.php:46
+#, php-format
+msgid "%s: Recent bookmarks"
+msgstr "%s: 最近书签"
+
+#: www/index.php:58
+#, fuzzy, php-format
+msgid "%s: Recent bookmarks (+private %s)"
+msgstr "%s: 最近书签"
+
+#: www/index.php:96
+msgid "Store, share and tag your favourite links"
+msgstr ""
+
+#: www/index.php:97
+#, fuzzy
+msgid "All Bookmarks"
+msgstr "所有书签"
+
+#: www/jsScuttle.php:71
+msgid "Available"
+msgstr ""
+
+#: www/jsScuttle.php:74
+#, fuzzy
+msgid "Not Available"
+msgstr "尚未有书签列入"
+
+#: www/login.php:48
+msgid "The details you have entered are incorrect. Please try again."
+msgstr "您输入的详细信æ¯ä¸æ­£ç¡®ï¼Œè¯·é‡è¯•."
+
+#: www/password.php:36
+msgid "You must enter your username."
+msgstr ""
+
+#: www/password.php:40
+#, fuzzy
+msgid ""
+"You must enter your <abbr title=\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"您 <em>å¿…é¡»</em> 输入用户å,密ç ,姓åå’Œ <abbr title=\"电å­é‚®ä»¶\">e-mail</"
+"abbr> 地å€."
+
+#: www/password.php:48
+msgid "No matches found for that username."
+msgstr ""
+
+#: www/password.php:51
+#, fuzzy
+msgid ""
+"No matches found for that combination of username and <abbr title="
+"\"electronic mail\">e-mail</abbr> address."
+msgstr ""
+"您 <em>å¿…é¡»</em> 输入用户å,密ç ,姓åå’Œ <abbr title=\"电å­é‚®ä»¶\">e-mail</"
+"abbr> 地å€."
+
+#: www/password.php:59
+#, fuzzy
+msgid ""
+"There was an error while generating your new password. Please try again."
+msgstr "ä¿å­˜ä¹¦ç­¾æ—¶å‡ºé”™ï¼Œè¯·å†ä¿å­˜é‡è¯•ä¸€æ¬¡æˆ–者è”系管ç†å‘˜"
+
+#: www/password.php:63
+#, fuzzy
+msgid "Your new password is:"
+msgstr "确认密ç "
+
+#: 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 ""
+
+#: www/password.php:66
+#, fuzzy, php-format
+msgid "%s Account Information"
+msgstr "ä¿¡æ¯"
+
+#: www/password.php:68
+#, php-format
+msgid "New password generated and sent to %s"
+msgstr ""
+
+#: www/password.php:75
+#, fuzzy
+msgid "Forgotten Password"
+msgstr "新的密ç "
+
+#: www/profile.php:63 www/watchlist.php:119
+msgid "Username was not specified"
+msgstr "没有æ供用户å"
+
+#: www/profile.php:70
+#, fuzzy
+msgid "My Profile"
+msgstr "个人资料"
+
+#: www/profile.php:110
+msgid "Invalid Token"
+msgstr ""
+
+#: www/profile.php:115
+msgid "Password and confirmation do not match."
+msgstr "密ç å’Œç¡®è®¤å¯†ç ä¸ä¸€è‡´."
+
+#: www/profile.php:119
+msgid "Password must be at least 6 characters long."
+msgstr "密ç è‡³å°‘6ä½."
+
+#: www/profile.php:123
+msgid "E-mail address is not valid."
+msgstr "Email地å€ä¸åˆæ³•."
+
+#: www/profile.php:127
+msgid "An error occurred while saving your changes."
+msgstr "ä¿å­˜ä¿®æ”¹æ—¶å‡ºé”™."
+
+#: www/profile.php:129
+msgid "Changes saved."
+msgstr "ä¿å­˜ä¿®æ”¹æˆåŠŸ."
+
+#: www/profile.php:148
+msgid "You do not have a valid SSL client certificate"
+msgstr ""
+
+#: www/profile.php:150
+msgid "This certificate is already registered"
+msgstr ""
+
+#: www/profile.php:152
+msgid "Failed to register SSL client certificate."
+msgstr ""
+
+#: www/profile.php:154
+msgid "SSL client certificate registered."
+msgstr ""
+
+#: www/profile.php:163
+msgid "Certificate not found."
+msgstr ""
+
+#: www/profile.php:165
+msgid "The certificate does not belong to you."
+msgstr ""
+
+#: www/profile.php:167
+msgid "Failed to delete SSL client certificate."
+msgstr ""
+
+#: www/profile.php:169
+msgid "SSL client certificate deleted."
+msgstr ""
+
+#: www/register.php:52
+#, fuzzy
+msgid "You <em>must</em> enter a username, password and e-mail address."
+msgstr ""
+"您 <em>å¿…é¡»</em> 输入用户å,密ç ,姓åå’Œ <abbr title=\"电å­é‚®ä»¶\">e-mail</"
+"abbr> 地å€."
+
+#: www/register.php:56
+#, fuzzy
+msgid "This username has been reserved, please make another choice."
+msgstr "用户åå·²ç»å­˜åœ¨ï¼Œè¯·é€‰æ‹©å…¶ä»–用户å."
+
+#: www/register.php:60
+msgid "This username already exists, please make another choice."
+msgstr "用户åå·²ç»å­˜åœ¨ï¼Œè¯·é€‰æ‹©å…¶ä»–用户å."
+
+#: www/register.php:64
+#, fuzzy
+msgid ""
+"This username is not valid (too short, too long, forbidden characters...), "
+"please make another choice."
+msgstr "用户åå·²ç»å­˜åœ¨ï¼Œè¯·é€‰æ‹©å…¶ä»–用户å."
+
+#: www/register.php:68
+#, fuzzy
+msgid "E-mail address is not valid. Please try again."
+msgstr "Email地å€ä¸åˆæ³•."
+
+#: www/register.php:72
+#, fuzzy
+msgid "Antispam answer is not valid. Please try again."
+msgstr "Email地å€ä¸åˆæ³•."
+
+#: www/register.php:90
+msgid "You have successfully registered. Enjoy!"
+msgstr "您已ç»æ³¨å†ŒæˆåŠŸ."
+
+#: www/register.php:92
+msgid "Registration failed. Please try again."
+msgstr "注册失败,请é‡è¯•."
+
+#: www/rss.php:90 www/rss.php:111
+#, php-format
+msgid "Failed to Autenticate User with username %s using private key"
+msgstr ""
+
+#: www/rss.php:128
+#, php-format
+msgid "Recent bookmarks posted to %s"
+msgstr "最新å‘布到%s的书签"
+
+#: www/search.php:83 www/search.php:147
+msgid "Search Bookmarks"
+msgstr "æœç´¢ä¹¦ç­¾"
+
+#: www/search.php:89
+msgid "Search Results"
+msgstr "æœç´¢ç»“æžœ"
+
+#: www/search.php:137
+msgid "Unsatisfied? You can also try our "
+msgstr ""
+
+#: www/tag2tagadd.php:54
+msgid "Tag link created"
+msgstr ""
+
+#: www/tag2tagadd.php:57
+#, fuzzy
+msgid "Failed to create the link"
+msgstr "删除书签失败"
+
+#: www/tag2tagadd.php:69
+msgid "Add Tag Link"
+msgstr ""
+
+#: www/tag2tagdelete.php:66
+msgid "Tag link deleted"
+msgstr ""
+
+#: www/tag2tagdelete.php:69
+#, fuzzy
+msgid "Failed to delete the link"
+msgstr "删除书签失败"
+
+#: www/tag2tagdelete.php:81
+msgid "Delete Link Between Tags"
+msgstr ""
+
+#: www/tag2tagedit.php:55
+msgid "Edit Link Between Tags"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:62
+msgid "Tag common description updated"
+msgstr ""
+
+#: www/tagcommondescriptionedit.php:67
+msgid "Failed to update the tag common description"
+msgstr ""
+
+#: www/tagdelete.php:43
+msgid "Tag deleted"
+msgstr ""
+
+#: www/tagdelete.php:46
+#, fuzzy
+msgid "Failed to delete the tag"
+msgstr "删除书签失败"
+
+#: www/tagedit.php:52
+msgid "Tag description updated"
+msgstr ""
+
+#: www/tagedit.php:55
+#, fuzzy
+msgid "Failed to update the tag description"
+msgstr "删除书签失败"
+
+#: www/tagrename.php:63
+msgid "Tag renamed"
+msgstr ""
+
+#: www/tagrename.php:66
+#, fuzzy
+msgid "Failed to rename the tag"
+msgstr "删除书签失败"
+
+#: www/tags.php:71
+#, php-format
+msgid "%s: tagged with \"%s\""
+msgstr ""
+
+#: www/tags.php:84
+#, php-format
+msgid "%s: tagged with \"%s\" (+private %s)"
+msgstr ""
+
+#: www/users.php:31
+#, fuzzy
+msgid "Users"
+msgstr "用户å¸å·"
+
+#: www/watch.php:54
+msgid "User removed from your watchlist"
+msgstr "已将用户从您的关注列表中删除"
+
+#: www/watch.php:56
+msgid "User added to your watchlist"
+msgstr "已将用户加入到您的关注列表中"
+
+#: www/watchlist.php:104
+#, fuzzy
+msgid "My Watchlist"
+msgstr "我关注的其他用户"
+
+#: www/api/httpauth.inc.php:30
+msgid "Use of the API calls requires authentication."
+msgstr ""
+
+#: www/gsearch/index.php:51
+msgid "Come back to "
+msgstr ""
+
+#: www/gsearch/index.php:56
+msgid "Admin tips: "
+msgstr ""
+
+#: www/gsearch/index.php:57
+msgid "To refresh manually Google Custom Search Engine, goes to: "
+msgstr ""
+
+#: www/gsearch/index.php:61
+msgid ""
+"If no result appears, check that all the urls are valid in the admin section."
+msgstr ""
+
+#~ msgid "User with username %s not was not found"
+#~ msgstr "未å‘现以 %s 为用户å的用户"
+
+#~ msgid "Recent Bookmarks"
+#~ msgstr "最近书签"
+
+#~ msgid "for"
+#~ msgstr "关于"
+
+#~ msgid "%s Bookmarks"
+#~ msgstr "%s 书签"
+
+#~ msgid "URL"
+#~ msgstr "URL网å€"
+
+#~ msgid "<abbr title=\"Electronic mail\">E-mail</abbr>"
+#~ msgstr "<abbr title=\"电å­é‚®ä»¶l\">E-mail</abbr>"
diff --git a/data/schema/1.sql b/data/schema/1.sql
new file mode 100644
index 0000000..e09a210
--- /dev/null
+++ b/data/schema/1.sql
@@ -0,0 +1,20 @@
+RENAME TABLE `sc_tags` TO `sc_bookmarks2tags` ;
+
+ CREATE TABLE `sc_searchhistory` (
+ `shId` int(11) NOT NULL auto_increment,
+ `shTerms` varchar(255) NOT NULL default '',
+ `shRange` varchar(32) NOT NULL default '',
+ `shDatetime` datetime NOT NULL default '0000-00-00 00:00:00',
+ `shNbResults` int(6) NOT NULL default '0',
+ `uId` int(11) NOT NULL default '0',
+ PRIMARY KEY (`shId`)
+ );
+
+ CREATE TABLE `sc_tags` (
+ `tId` int(11) NOT NULL auto_increment,
+ `tag` varchar(32) NOT NULL default '',
+ `uId` int(11) NOT NULL default '0',
+ `tDescription` varchar(255) default NULL,
+ PRIMARY KEY (`tId`),
+ UNIQUE KEY `sc_tags_tag_uId` (`tag`, `uId`)
+ );
diff --git a/data/schema/2.sql b/data/schema/2.sql
new file mode 100644
index 0000000..171e011
--- /dev/null
+++ b/data/schema/2.sql
@@ -0,0 +1,10 @@
+ALTER TABLE `sc_bookmarks` CHANGE `bDescription` `bDescription` VARCHAR( 1500 );
+CREATE TABLE `sc_tagscache` (
+ `tcId` int(11) NOT NULL auto_increment,
+ `tag1` varchar(100) NOT NULL default '',
+ `tag2` varchar(100) NOT NULL default '',
+ `relationType` varchar(32) NOT NULL default '',
+ `uId` int(11) NOT NULL default '0',
+ PRIMARY KEY (`tcId`),
+ UNIQUE KEY `sc_tagscache_tag1_tag2_type_uId` (`tag1`,`tag2`,`relationType`,`uId`)
+);
diff --git a/data/schema/3.sql b/data/schema/3.sql
new file mode 100644
index 0000000..32431b9
--- /dev/null
+++ b/data/schema/3.sql
@@ -0,0 +1,85 @@
+/* modify and add fields */
+ALTER TABLE `sc_bookmarks` MODIFY `bAddress` varchar(1500) NOT NULL;
+ALTER TABLE `sc_bookmarks` MODIFY `bDescription` TEXT default NULL;
+ALTER TABLE `sc_bookmarks` ADD `bPrivateNote` TEXT NULL AFTER `bDescription` ;
+ALTER TABLE `sc_tags` MODIFY `tDescription` TEXT default NULL;
+ALTER TABLE `sc_commondescription` MODIFY `cdDescription` TEXT default NULL;
+
+/* convert to UTF-8 if your table is ISO-something (through BLOB: tips provided by MYSQL documentation)*/
+/* first need to remove index keys because of BLOB constraints*/
+ALTER TABLE `sc_tags` DROP INDEX `sc_tags_tag_uId`;
+ALTER TABLE `sc_bookmarks2tags` DROP INDEX `sc_bookmarks2tags_tag_bId`;
+ALTER TABLE `sc_bookmarks2tags` DROP INDEX `sc_bookmarks2tags_bId`;
+ALTER TABLE `sc_tags2tags` DROP INDEX `sc_tags2tags_tag1_tag2_uId`;
+ALTER TABLE `sc_commondescription` DROP INDEX `sc_commondescription_tag_datetime`;
+ALTER TABLE `sc_tagscache` DROP INDEX `sc_tagscache_tag1_tag2_type_uId`;
+ALTER TABLE `sc_tagsstats` DROP INDEX `sc_tagsstats_tag1_type_uId`;
+
+/* secondly convert through BLOB type */
+ALTER TABLE `sc_bookmarks` CHANGE `bTitle` `bTitle` BLOB;
+ALTER TABLE `sc_bookmarks` CHANGE `bTitle` `bTitle` varchar(255) CHARACTER SET utf8;
+ALTER TABLE `sc_bookmarks` CHANGE `bAddress` `bAddress` BLOB;
+ALTER TABLE `sc_bookmarks` CHANGE `bAddress` `bAddress` varchar(1500) CHARACTER SET utf8;
+ALTER TABLE `sc_bookmarks` CHANGE `bDescription` `bDescription` BLOB;
+ALTER TABLE `sc_bookmarks` CHANGE `bDescription` `bDescription` text CHARACTER SET utf8;
+ALTER TABLE `sc_bookmarks` CHANGE `bPrivateNote` `bPrivateNote` BLOB;
+ALTER TABLE `sc_bookmarks` CHANGE `bPrivateNote` `bPrivateNote` text CHARACTER SET utf8;
+
+ALTER TABLE `sc_tags` CHANGE `tag` `tag` BLOB;
+ALTER TABLE `sc_tags` CHANGE `tag` `tag` varchar(100) CHARACTER SET utf8;
+ALTER TABLE `sc_tags` CHANGE `tDescription` `tDescription` BLOB;
+ALTER TABLE `sc_tags` CHANGE `tDescription` `tDescription` text CHARACTER SET utf8;
+
+ALTER TABLE `sc_bookmarks2tags` CHANGE `tag` `tag` BLOB;
+ALTER TABLE `sc_bookmarks2tags` CHANGE `tag` `tag` varchar(100) CHARACTER SET utf8;
+
+ALTER TABLE `sc_users` CHANGE `name` `name` BLOB;
+ALTER TABLE `sc_users` CHANGE `name` `name` varchar(50) CHARACTER SET utf8;
+ALTER TABLE `sc_users` CHANGE `uContent` `uContent` BLOB;
+ALTER TABLE `sc_users` CHANGE `uContent` `uContent` text CHARACTER SET utf8;
+
+ALTER TABLE `sc_tags2tags` CHANGE `tag1` `tag1` BLOB;
+ALTER TABLE `sc_tags2tags` CHANGE `tag1` `tag1` varchar(100) CHARACTER SET utf8;
+ALTER TABLE `sc_tags2tags` CHANGE `tag2` `tag2` BLOB;
+ALTER TABLE `sc_tags2tags` CHANGE `tag2` `tag2` varchar(100) CHARACTER SET utf8;
+
+ALTER TABLE `sc_tagsstats` CHANGE `tag1` `tag1` BLOB;
+ALTER TABLE `sc_tagsstats` CHANGE `tag1` `tag1` varchar(100) CHARACTER SET utf8;
+
+ALTER TABLE `sc_tagscache` CHANGE `tag1` `tag1` BLOB;
+ALTER TABLE `sc_tagscache` CHANGE `tag1` `tag1` varchar(100) CHARACTER SET utf8;
+ALTER TABLE `sc_tagscache` CHANGE `tag2` `tag2` BLOB;
+ALTER TABLE `sc_tagscache` CHANGE `tag2` `tag2` varchar(100) CHARACTER SET utf8;
+
+ALTER TABLE `sc_commondescription` CHANGE `tag` `tag` BLOB;
+ALTER TABLE `sc_commondescription` CHANGE `tag` `tag` varchar(100) CHARACTER SET utf8;
+ALTER TABLE `sc_commondescription` CHANGE `cdTitle` `cdTitle` BLOB;
+ALTER TABLE `sc_commondescription` CHANGE `cdTitle` `cdTitle` varchar(255) CHARACTER SET utf8;
+ALTER TABLE `sc_commondescription` CHANGE `cdDescription` `cdDescription` BLOB;
+ALTER TABLE `sc_commondescription` CHANGE `cdDescription` `cdDescription` text CHARACTER SET utf8;
+
+ALTER TABLE `sc_searchhistory` CHANGE `shTerms` `shTerms` BLOB;
+ALTER TABLE `sc_searchhistory` CHANGE `shTerms` `shTerms` varchar(255) CHARACTER SET utf8;
+ALTER TABLE `sc_searchhistory` CHANGE `shRange` `shRange` BLOB;
+ALTER TABLE `sc_searchhistory` CHANGE `shRange` `shRange` varchar(32) CHARACTER SET utf8;
+
+/* Thirdly re-add index keys */
+ALTER TABLE `sc_tags` ADD UNIQUE KEY `sc_tags_tag_uId` (`tag`, `uId`);
+ALTER TABLE `sc_bookmarks2tags` ADD UNIQUE KEY `sc_bookmarks2tags_tag_bId` (`tag`,`bId`);
+ALTER TABLE `sc_bookmarks2tags` ADD KEY `sc_bookmarks2tags_bId` (`bId`);
+ALTER TABLE `sc_tags2tags` ADD UNIQUE KEY `sc_tags2tags_tag1_tag2_uId` (`tag1`,`tag2`,`relationType`,`uId`);
+ALTER TABLE `sc_commondescription` ADD UNIQUE KEY `sc_commondescription_tag_datetime` (`tag`,`cdDatetime`);
+ALTER TABLE `sc_tagscache` ADD UNIQUE KEY `sc_tagscache_tag1_tag2_type_uId` (`tag1`,`tag2`,`relationType`,`uId`);
+ALTER TABLE `sc_tagsstats` ADD UNIQUE KEY `sc_tagsstats_tag1_type_uId` (`tag1`,`relationType`,`uId`);
+
+/* Change tables to utf-8 charset */
+ALTER TABLE `sc_bookmarks` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_tags` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_bookmarks2tags` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_users` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_watched` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_tags2tags` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_tagsstats` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_tagscache` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_commondescription` CHARACTER SET utf8 COLLATE utf8_general_ci;
+ALTER TABLE `sc_searchhistory` CHARACTER SET utf8 COLLATE utf8_general_ci;
diff --git a/data/schema/4.sql b/data/schema/4.sql
new file mode 100644
index 0000000..022aaf0
--- /dev/null
+++ b/data/schema/4.sql
@@ -0,0 +1,11 @@
+ALTER TABLE `sc_bookmarks` ADD `bVoting` INT NOT NULL;
+ALTER TABLE `sc_bookmarks` ADD `bVotes` INT NOT NULL;
+
+CREATE TABLE `sc_votes` (
+ `bId` INT NOT NULL ,
+ `uId` INT NOT NULL ,
+ `vote` INT( 2 ) NOT NULL ,
+ UNIQUE KEY `bid_2` (`bId`,`uId`),
+ KEY `bid` (`bId`),
+ KEY `uid` (`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
diff --git a/data/schema/5.sql b/data/schema/5.sql
new file mode 100644
index 0000000..d4c9afc
--- /dev/null
+++ b/data/schema/5.sql
@@ -0,0 +1 @@
+ALTER TABLE `sc_bookmarks` ADD `bShort` VARCHAR(16) NULL DEFAULT NULL;
diff --git a/data/schema/6.sql b/data/schema/6.sql
new file mode 100644
index 0000000..cf9dd37
--- /dev/null
+++ b/data/schema/6.sql
@@ -0,0 +1,19 @@
+CREATE TABLE `sc_version` (
+ `schema_version` int(11) NOT NULL
+) DEFAULT CHARSET=utf8;
+INSERT INTO `sc_version` (`schema_version`) VALUES ('6');
+
+CREATE TABLE `sc_users_sslclientcerts` (
+ `id` INT NOT NULL AUTO_INCREMENT ,
+ `uId` INT NOT NULL ,
+ `sslSerial` VARCHAR( 32 ) NOT NULL ,
+ `sslClientIssuerDn` VARCHAR( 1024 ) NOT NULL ,
+ `sslName` VARCHAR( 64 ) NOT NULL ,
+ `sslEmail` VARCHAR( 64 ) NOT NULL ,
+ PRIMARY KEY ( `id` ) ,
+ UNIQUE (`id`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+ALTER TABLE `sc_users` ADD `privateKey` VARCHAR(33) NULL;
+CREATE UNIQUE INDEX `privateKey` ON `sc_users` (`privateKey`);
+
diff --git a/data/tables-postgresql.sql b/data/tables-postgresql.sql
new file mode 100644
index 0000000..b0ada11
--- /dev/null
+++ b/data/tables-postgresql.sql
@@ -0,0 +1,259 @@
+-- Semantic Scuttle - Tables creation SQL script
+-- ! Dont forget to change table names according to $tableprefix defined in config.php !
+
+--
+-- Table structure for table "sc_bookmarks"
+--
+
+CREATE SEQUENCE bIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_bookmarks (
+ bId integer DEFAULT nextval('bIds'::text) PRIMARY KEY,
+ uId integer NOT NULL,
+ bIp varchar(40) DEFAULT NULL,
+ bStatus smallint NOT NULL,
+ bDatetime timestamp with time zone DEFAULT now() NOT NULL,
+ bModified timestamp with time zone DEFAULT now() NOT NULL,
+ bTitle varchar(255) DEFAULT '' NOT NULL,
+ bAddress varchar(1500) DEFAULT '' NOT NULL,
+ bDescription text,
+ bPrivateNote text,
+ bHash varchar(32) DEFAULT '' NOT NULL,
+ bVotes integer NOT NULL,
+ bVoting integer NOT NULL,
+ bShort varchar(16) DEFAULT NULL
+);
+
+CREATE INDEX sc_bookmarks_usd ON sc_bookmarks (uId, bStatus, bDatetime);
+CREATE INDEX sc_bookmarks_hui ON sc_bookmarks (bHash, uId, bId);
+CREATE INDEX sc_bookmarks_du ON sc_bookmarks (bDatetime, uId);
+
+--
+-- Table structure for table "sc_bookmarks2tags"
+--
+
+CREATE SEQUENCE b2tIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_bookmarks2tags (
+ id integer DEFAULT nextval('b2tIds'::text) PRIMARY KEY,
+ bId integer NOT NULL,
+ tag varchar(100) DEFAULT '' NOT NULL
+);
+
+CREATE UNIQUE INDEX sc_bookmarks2tags_tag_bId on sc_bookmarks2tags (tag, bId);
+CREATE INDEX sc_bookmarks2tags_bId on sc_bookmarks2tags (bId);
+
+--
+-- Table structure for table "sc_commondescription"
+--
+
+CREATE SEQUENCE cdIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_commondescription (
+ cdId integer DEFAULT nextval('cdIds'::text) PRIMARY KEY,
+ uId integer NOT NULL,
+ tag varchar(100) DEFAULT '' NOT NULL,
+ bHash varchar(32) DEFAULT '' NOT NULL,
+ cdTitle varchar(255) DEFAULT '' NOT NULL,
+ cdDescription text,
+ cdDatetime timestamp with time zone DEFAULT now() NOT NULL
+);
+
+CREATE UNIQUE INDEX sc_commondescription_tag_timestamp on sc_commondescription (tag, cdDatetime);
+CREATE UNIQUE INDEX sc_commondescription_bookmark_timestamp on sc_commondescription (bHash, cdDatetime);
+
+--
+-- Table structure for table "sc_searchhistory"
+--
+
+CREATE SEQUENCE shIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_searchhistory (
+ shId integer DEFAULT nextval('shIds'::text) PRIMARY KEY,
+ shTerms varchar(255) NOT NULL DEFAULT '',
+ shRange varchar(32) NOT NULL DEFAULT '',
+ shDatetime timestamp with time zone DEFAULT now() NOT NULL,
+ shNbResults integer NOT NULL,
+ uId integer NOT NULL
+);
+
+--
+-- Table structure for table "sc_tags"
+--
+
+CREATE SEQUENCE tIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_tags (
+ tId integer DEFAULT nextval('tIds'::text) PRIMARY KEY,
+ tag varchar(100) NOT NULL DEFAULT '',
+ uId integer NOT NULL,
+ tDescription text
+);
+
+CREATE UNIQUE INDEX sc_tags_tag_uId on sc_tags (tag, uId);
+
+--
+-- Table structure for table "sc_tags2tags"
+--
+
+CREATE SEQUENCE ttIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_tags2tags (
+ ttId integer DEFAULT nextval('ttIds'::text) PRIMARY KEY,
+ tag1 varchar(100) NOT NULL DEFAULT '',
+ tag2 varchar(100) NOT NULL DEFAULT '',
+ relationType varchar(32) NOT NULL DEFAULT '',
+ uId integer NOT NULL
+);
+
+CREATE UNIQUE INDEX sc_tags2tags_tag1_tag2_uId on sc_tags2tags (tag1, tag2, relationType, uId);
+
+--
+-- Table structure for table "sc_tagscache"
+--
+
+CREATE SEQUENCE tcIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_tagscache (
+ tcId integer DEFAULT nextval('tcIds'::text) PRIMARY KEY,
+ tag1 varchar(100) NOT NULL DEFAULT '',
+ tag2 varchar(100) NOT NULL DEFAULT '',
+ relationType varchar(32) NOT NULL DEFAULT '',
+ uId integer NOT NULL DEFAULT '0'
+);
+
+CREATE UNIQUE INDEX sc_tagscache_tag1_tag2_type_uId on sc_tagscache (tag1, tag2, relationType, uId);
+
+--
+-- Table structure for table "sc_tagsstats"
+--
+
+CREATE SEQUENCE tstIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_tagsstats (
+ tstId integer DEFAULT nextval('tstIds'::text) PRIMARY KEY,
+ tag1 varchar(100) NOT NULL DEFAULT '',
+ relationType varchar(32) NOT NULL DEFAULT '',
+ uId integer NOT NULL,
+ nb integer NOT NULL,
+ depth integer NOT NULL,
+ nbupdate integer NOT NULL
+);
+
+CREATE UNIQUE INDEX sc_tagsstats_tag1_type_uId on sc_tagsstats (tag1, relationType, uId);
+
+--
+-- Table structure for table "sc_users"
+--
+
+CREATE SEQUENCE uIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_users (
+ uId integer DEFAULT nextval('uIds'::text) PRIMARY KEY,
+ username varchar(25) NOT NULL DEFAULT '',
+ password varchar(40) NOT NULL DEFAULT '',
+ uDatetime timestamp with time zone DEFAULT now() NOT NULL,
+ uModified timestamp with time zone DEFAULT now() NOT NULL,
+ name varchar(50) DEFAULT NULL,
+ email varchar(50) NOT NULL DEFAULT '',
+ homepage varchar(255) DEFAULT NULL,
+ uContent text,
+ privateKey varchar(33) DEFAULT NULL
+);
+
+CREATE UNIQUE INDEX privateKey on sc_users (privateKey);
+
+--
+-- Table structure for table "sc_users_sslclientcerts"
+--
+
+CREATE SEQUENCE ids
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_users_sslclientcerts (
+ id integer DEFAULT nextval('ids'::text) PRIMARY KEY,
+ uId integer NOT NULL,
+ sslSerial varchar(32) DEFAULT '' NOT NULL,
+ sslClientIssuerDn varchar(1024) DEFAULT '' NOT NULL,
+ sslName varchar(64) DEFAULT '' NOT NULL,
+ sslEmail varchar(64) DEFAULT '' NOT NULL
+);
+
+--
+-- Table structure for table "sc_version"
+--
+
+CREATE TABLE sc_version (
+ schema_version integer NOT NULL
+);
+
+--
+-- Table structure for table "sc_votes"
+--
+
+CREATE TABLE sc_votes (
+ bId integer NOT NULL,
+ uId integer NOT NULL,
+ vote integer NOT NULL
+);
+
+CREATE UNIQUE INDEX bid_2 on sc_votes (bId, uId);
+CREATE INDEX bid on sc_votes (bId);
+CREATE INDEX uid on sc_votes (uId);
+
+--
+-- Table structure for table "sc_watched"
+--
+
+CREATE SEQUENCE wIds
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+CREATE TABLE sc_watched (
+ wId integer DEFAULT nextval('wIds'::text) PRIMARY KEY,
+ uId integer NOT NULL,
+ watched integer NOT NULL
+);
+
+CREATE INDEX sc_watched_uId on sc_watched (uId);
diff --git a/data/tables.sql b/data/tables.sql
new file mode 100644
index 0000000..68d5ba9
--- /dev/null
+++ b/data/tables.sql
@@ -0,0 +1,203 @@
+-- Semantic Scuttle - Tables creation SQL script
+-- ! Dont forget to change table names according to $tableprefix defined in config.php !
+
+--
+-- Table structure for table `sc_bookmarks`
+--
+
+CREATE TABLE `sc_bookmarks` (
+ `bId` int(11) NOT NULL auto_increment,
+ `uId` int(11) NOT NULL default '0',
+ `bIp` varchar(40) default NULL,
+ `bStatus` tinyint(1) NOT NULL default '0',
+ `bDatetime` datetime NOT NULL default '0000-00-00 00:00:00',
+ `bModified` datetime NOT NULL default '0000-00-00 00:00:00',
+ `bTitle` varchar(255) NOT NULL default '',
+ `bAddress` varchar(1500) NOT NULL,
+ `bDescription` text default NULL,
+ `bPrivateNote` text default NULL,
+ `bHash` varchar(32) NOT NULL default '',
+ `bVotes` int(11) NOT NULL,
+ `bVoting` int(11) NOT NULL,
+ `bShort` varchar(16) default NULL,
+ PRIMARY KEY (`bId`),
+ KEY `sc_bookmarks_usd` (`uId`,`bStatus`,`bDatetime`),
+ KEY `sc_bookmarks_hui` (`bHash`,`uId`,`bId`),
+ KEY `sc_bookmarks_du` (`bDatetime`,`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_tags`
+--
+
+CREATE TABLE `sc_tags` (
+ `tId` int(11) NOT NULL auto_increment,
+ `tag` varchar(100) NOT NULL default '',
+ `uId` int(11) NOT NULL default '0',
+ `tDescription` text default NULL,
+ PRIMARY KEY (`tId`),
+ UNIQUE KEY `sc_tags_tag_uId` (`tag`, `uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_bookmarks2tags`
+--
+
+CREATE TABLE `sc_bookmarks2tags` (
+ `id` int(11) NOT NULL auto_increment,
+ `bId` int(11) NOT NULL default '0',
+ `tag` varchar(100) NOT NULL default '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `sc_bookmarks2tags_tag_bId` (`tag`,`bId`),
+ KEY `sc_bookmarks2tags_bId` (`bId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_users`
+--
+
+CREATE TABLE `sc_users` (
+ `uId` int(11) NOT NULL auto_increment,
+ `username` varchar(25) NOT NULL default '',
+ `password` varchar(40) NOT NULL default '',
+ `uDatetime` datetime NOT NULL default '0000-00-00 00:00:00',
+ `uModified` datetime NOT NULL default '0000-00-00 00:00:00',
+ `name` varchar(50) default NULL,
+ `email` varchar(50) NOT NULL default '',
+ `homepage` varchar(255) default NULL,
+ `uContent` text,
+ `privateKey` varchar(33) default NULL,
+ PRIMARY KEY (`uId`),
+ UNIQUE KEY `privateKey` (`privateKey`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+CREATE TABLE `sc_users_sslclientcerts` (
+ `id` INT NOT NULL AUTO_INCREMENT ,
+ `uId` INT NOT NULL ,
+ `sslSerial` VARCHAR( 32 ) NOT NULL ,
+ `sslClientIssuerDn` VARCHAR( 1024 ) NOT NULL ,
+ `sslName` VARCHAR( 64 ) NOT NULL ,
+ `sslEmail` VARCHAR( 64 ) NOT NULL ,
+ PRIMARY KEY ( `id` )
+) CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+--
+-- Table structure for table `sc_watched`
+--
+
+CREATE TABLE `sc_watched` (
+ `wId` int(11) NOT NULL auto_increment,
+ `uId` int(11) NOT NULL default '0',
+ `watched` int(11) NOT NULL default '0',
+ PRIMARY KEY (`wId`),
+ KEY `sc_watched_uId` (`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_tags2tags`
+--
+
+CREATE TABLE `sc_tags2tags` (
+ `ttId` int(11) NOT NULL auto_increment,
+ `tag1` varchar(100) NOT NULL default '',
+ `tag2` varchar(100) NOT NULL default '',
+ `relationType` varchar(32) NOT NULL default '',
+ `uId` int(11) NOT NULL default '0',
+ PRIMARY KEY (`ttId`),
+ UNIQUE KEY `sc_tags2tags_tag1_tag2_uId` (`tag1`,`tag2`,`relationType`,`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_tagsstats`
+--
+
+CREATE TABLE `sc_tagsstats` (
+ `tstId` int(11) NOT NULL auto_increment,
+ `tag1` varchar(100) NOT NULL default '',
+ `relationType` varchar(32) NOT NULL default '',
+ `uId` int(11) NOT NULL default '0',
+ `nb` int(11) NOT NULL default '0',
+ `depth` int(11) NOT NULL default '0',
+ `nbupdate` int(11) NOT NULL default '0',
+ PRIMARY KEY (`tstId`),
+ UNIQUE KEY `sc_tagsstats_tag1_type_uId` (`tag1`,`relationType`,`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_tagscache`
+--
+
+CREATE TABLE `sc_tagscache` (
+ `tcId` int(11) NOT NULL auto_increment,
+ `tag1` varchar(100) NOT NULL default '',
+ `tag2` varchar(100) NOT NULL default '',
+ `relationType` varchar(32) NOT NULL default '',
+ `uId` int(11) NOT NULL default '0',
+ PRIMARY KEY (`tcId`),
+ UNIQUE KEY `sc_tagscache_tag1_tag2_type_uId` (`tag1`,`tag2`,`relationType`,`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_commondescription`
+--
+
+CREATE TABLE `sc_commondescription` (
+ `cdId` int(11) NOT NULL auto_increment,
+ `uId` int(11) NOT NULL default '0',
+ `tag` varchar(100) NOT NULL default '',
+ `bHash` varchar(32) NOT NULL default '',
+ `cdTitle` varchar(255) NOT NULL default '',
+ `cdDescription` text default NULL,
+ `cdDatetime` datetime NOT NULL default '0000-00-00 00:00:00',
+ PRIMARY KEY (`cdId`),
+ UNIQUE KEY `sc_commondescription_tag_datetime` (`tag`,`cdDatetime`),
+ UNIQUE KEY `sc_commondescription_bookmark_datetime` (`bHash`,`cdDatetime`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `sc_searchhistory`
+--
+
+CREATE TABLE `sc_searchhistory` (
+ `shId` int(11) NOT NULL auto_increment,
+ `shTerms` varchar(255) NOT NULL default '',
+ `shRange` varchar(32) NOT NULL default '',
+ `shDatetime` datetime NOT NULL default '0000-00-00 00:00:00',
+ `shNbResults` int(6) NOT NULL default '0',
+ `uId` int(11) NOT NULL default '0',
+ PRIMARY KEY (`shId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+
+CREATE TABLE `sc_votes` (
+ `bId` INT NOT NULL ,
+ `uId` INT NOT NULL ,
+ `vote` INT( 2 ) NOT NULL ,
+ UNIQUE KEY `bid_2` (`bId`,`uId`),
+ KEY `bid` (`bId`),
+ KEY `uid` (`uId`)
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+
+CREATE TABLE `sc_version` (
+ `schema_version` int(11) NOT NULL
+) DEFAULT CHARSET=utf8;
+INSERT INTO `sc_version` (`schema_version`) VALUES ('6');
diff --git a/data/templates/default/about.tpl.php b/data/templates/default/about.tpl.php
new file mode 100644
index 0000000..843a31f
--- /dev/null
+++ b/data/templates/default/about.tpl.php
@@ -0,0 +1,34 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<ul>
+<li><?php echo T_('<strong>Store</strong> all your favourite links in one place, accessible from anywhere.'); ?></li>
+<li><?php echo T_('<strong>Share</strong> your bookmarks with everyone, with friends on your watchlist or just keep them private.') ;?></li>
+<li><?php echo T_('<strong>Tag</strong> your bookmarks with as many labels as you want, instead of wrestling with folders.'); ?></li>
+<li><?php echo sprintf('<strong><a href="'.createURL('register').'">'.T_('Register now').'</a> </strong>'.T_(' to start using %s!'), $GLOBALS['sitename']); ?></li>
+</ul>
+
+<h3><?php echo T_('Geek Stuff'); ?></h3>
+<ul>
+<li><a href="http://sourceforge.net/projects/semanticscuttle/">Semantic Scuttle</a> <?php echo T_('is licensed under the ');?> <a href="http://www.gnu.org/copyleft/gpl.html"><acronym title="GNU\'s Not Unix">GNU</acronym> General Public License</a> (<?php echo T_('you can freely host it on your own web server.'); ?>)</li>
+<li><?php echo sprintf(T_('%1$s supports most of the <a href="http://www.delicious.com/help/api">del.icio.us <abbr title="Application Programming Interface">API</abbr></a>. 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.'), $GLOBALS['sitename']); ?></li>
+
+
+
+<?php if(!is_null($currentUser) && $currentUser->isAdmin()): ?>
+<li>SemanticScuttle v0.98.5</li>
+<?php endif ?>
+</ul>
+
+<h3><?php echo T_('Tips'); ?></h3>
+<ul>
+<li><?php echo T_('Add search plugin into your browser:'); ?> <a href="#" onclick="window.external.AddSearchProvider('<?php echo ROOT?>api/opensearch.php');">opensearch</a></li>
+<li><?php echo T_('The secret tag "system:unfiled" allows you to find bookmarks without tags.'); ?></li>
+<li><?php echo T_('The secret tag "system:imported" allows you to find imported bookmarks.'); ?></li>
+</ul>
+
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/admin.tpl.php b/data/templates/default/admin.tpl.php
new file mode 100644
index 0000000..50680f6
--- /dev/null
+++ b/data/templates/default/admin.tpl.php
@@ -0,0 +1,35 @@
+<?php
+
+$this->includeTemplate($GLOBALS['top_include']);
+
+echo '<h3>'.T_('Users management').'</h3>';
+
+echo '<ol id="bookmarks">';
+
+foreach($users as $user) {
+ echo '<li class="xfolkentry">'."\n";
+
+ echo '<div class="link">';
+ echo '<a href="'.createURL('profile', $user->getUsername()).'">'.$user->getUsername().'</a>';
+ echo ' - <span title="'. T_('Public/Shared/Private') .'">'. $user->getNbBookmarks('public') .' / '. $user->getNbBookmarks('shared') .' / '. $user->getNbBookmarks('private') .' '. T_('bookmark(s)') .'</span>';
+ echo '</div>';
+
+ if($user->getUsername() != $currentUser->getUsername()) {
+ echo '<div class="meta">';
+ echo '<a href="'.createURL('admin','delete/'.$user->getUsername()).'" onclick="return confirm(\''.T_('Are you sure?').'\');">'.T_('Delete').'</a>';
+ echo '</div>';
+ }
+
+ echo '</li>'."\n";
+}
+echo '</ol>';
+?>
+<h3><?php echo T_('Other actions')?></h3>
+<p>
+<a href="<?php echo createURL('admin','checkUrl/') ?>"> <?php echo T_('Check all URLs (May take some time)') ?></a>
+</p>
+<?php
+$this->includeTemplate('sidebar.tpl');
+$this->includeTemplate($GLOBALS['bottom_include']);
+
+?>
diff --git a/data/templates/default/bookmarkcommondescriptionedit.tpl.php b/data/templates/default/bookmarkcommondescriptionedit.tpl.php
new file mode 100644
index 0000000..807c58b
--- /dev/null
+++ b/data/templates/default/bookmarkcommondescriptionedit.tpl.php
@@ -0,0 +1,58 @@
+<?php
+
+$this->includeTemplate($GLOBALS['top_include']);
+
+list ($url, $hash) = explode('/', $_SERVER['PATH_INFO']);
+
+
+?>
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementById("title").focus();
+}
+</script>
+
+<form action="<?php echo $formaction; ?>" method="post">
+<table title="<?php echo T_('Collaborative description: these fields can be viewed and modified by every users') ?>">
+<tr>
+ <th align="left"><?php echo T_('Title'); ?></th>
+ <td><input type="text" id="title" name="title" size="75" maxlength="255" value="<?php echo $description['cdTitle']; ?>" onkeypress="this.style.backgroundImage = 'none';" /></td>
+ <td></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Description'); ?></th>
+ <td><textarea name="description" cols="75" rows="10"><?php echo $description['cdDescription']; ?></textarea></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <?php
+ if(strlen($description['cdDatetime'])>0) {
+ echo T_('Last modification:').' '.$description['cdDatetime'].', ';
+ $lastUser = $userservice->getUser($description['uId']);
+ echo '<a href="'.createURL('profile', $lastUser['username']).'">'
+ . SemanticScuttle_Model_UserArray::getName($lastUser) . '</a>';
+ }
+ ?>
+ </td>
+ <td></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <input type="submit" name="confirm" value="<?php echo T_('Update'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('Cancel'); ?>" />
+ </td>
+ <td></td>
+</tr>
+</table>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+<div><input type="hidden" name="hash" value="<?php echo $hash; ?>" /></div>
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/bookmarklet.inc.php b/data/templates/default/bookmarklet.inc.php
new file mode 100644
index 0000000..b203735
--- /dev/null
+++ b/data/templates/default/bookmarklet.inc.php
@@ -0,0 +1,152 @@
+<h3><?php echo T_('Bookmarklet'); ?></h3>
+<p id="bookmarklet"></p>
+<script type="text/javascript">
+//<![CDATA[
+var browser = navigator.appName;
+jQuery(function($) {
+if (browser == "Opera") {
+ $('#bookmarklet').append(
+ <?php echo json_encode(
+ sprintf(
+ T_("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") . ':',
+ $GLOBALS['sitename']
+ )
+ ); ?>
+ );
+} else {
+ $('#bookmarklet').append(
+ <?php echo json_encode(
+ sprintf(
+ T_("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") . ':',
+ $GLOBALS['sitename']
+ )
+ );
+ ?>
+ );
+}
+});
+//]]>
+</script>
+<script type="text/javascript">
+//<![CDATA[
+var selection = '';
+if (window.getSelection) {
+ selection = 'window.getSelection()';
+} else if (document.getSelection) {
+ selection = 'document.getSelection()';
+} else if (document.selection) {
+ selection = 'document.selection.createRange().text';
+}
+if (browser == "Opera") {
+ $('#bookmarklet').append(
+ '<ul>'
+ + '<li>'
+ + '<a class="bookmarklet" href="'
+ + '<?php
+$popupLink = 'javascript:'
+ . "location.href='"
+ . addProtocolToUrl(createURL('bookmarks', $GLOBALS['user']))
+ . '?action=add'
+ . "&address='+encodeURIComponent(document.location.href)+'"
+ . "&title='+encodeURIComponent(document.title)+'"
+ . "&description='+encodeURIComponent(SELECTION)"
+ . ";";
+$link = 'opera:/button/'
+ //Opera command
+ . 'Go to page'
+ //command parameter 1
+ . ',"' . rawurlencode($popupLink) . '"'
+ //command parameter 2
+ . ','
+ //button title
+ . ',"Post to ' . fixOperaButtonName($GLOBALS['sitename']) . '"'
+ //button icon name
+ . ',"Scuttle"';
+echo jsEscTitle(htmlspecialchars($link));
+?>'.replace('SELECTION', selection)
+ + '"><?php echo jsEscTitle(sprintf(T_('Post to %s'), $GLOBALS['sitename'])); ?></a>'
+ + '</li>'
+ + '<li>'
+ + '<a class="bookmarklet" href="'
+ + '<?php
+$popupLink = 'javascript:'
+ . 'open('
+ . "'" . addProtocolToUrl(createURL('bookmarks', $GLOBALS['user']))
+ . '?action=add'
+ . '&popup=1'
+ . "&address='+encodeURIComponent(document.location.href)+'"
+ . "&title='+encodeURIComponent(document.title)+'"
+ . "&description='+encodeURIComponent(SELECTION)"
+ . ","
+ . "'" . htmlspecialchars(jsEscTitle($GLOBALS['sitename'])) . "',"
+ . "'modal=1,status=0,scrollbars=1,toolbar=0,resizable=1,width=790,height=465"
+ . ",left='+(screen.width-790)/2+',top='+(screen.height-425)/2"
+ . ");void 0";
+$link = 'opera:/button/'
+ . 'Go to page'
+ . ',"' . rawurlencode($popupLink) . '"'
+ . ','
+ . ',"Post to ' . fixOperaButtonName($GLOBALS['sitename']) . ' (Pop-up)"'
+ . ',"Scuttle"';
+echo jsEscTitle(htmlspecialchars($link));
+?>'.replace('SELECTION', selection)
+ + '"><?php echo jsEscTitle(sprintf(T_('Post to %s (Pop-up)'), $GLOBALS['sitename'])); ?></a>'
+ + '</li>'
+ + '</ul>'
+ );
+} else {
+ $('#bookmarklet').append(
+ '<ul>'
+ + '<li><a class="bookmarklet" href="javascript:x=document;a=encodeURIComponent(x.location.href);t=encodeURIComponent(x.title);d=encodeURIComponent('+selection+');location.href=\'<?php echo addProtocolToUrl(createURL('bookmarks', $GLOBALS['user'])); ?>?action=add&amp;address=\'+a+\'&amp;title=\'+t+\'&amp;description=\'+d;void 0;"><?php echo jsEscTitle(sprintf(T_('Post to %s'), $GLOBALS['sitename'])); ?><\/a><\/li>'
+ + '<li>'
+ + '<a class="bookmarklet" href="'
+ + 'javascript:x=document;'
+ + 'a=encodeURIComponent(x.location.href);'
+ + 't=encodeURIComponent(x.title);'
+ + 'd=encodeURIComponent('+selection+');'
+ + 'open('
+ + '\'<?php echo addProtocolToUrl(createURL('bookmarks', $GLOBALS['user'])); ?>?action=add&amp;popup=1&amp;address=\'+a+\'&amp;title=\'+t+\'&amp;description=\'+d,\'<?php echo htmlspecialchars(jsEscTitleDouble($GLOBALS['sitename'])); ?>\',\'modal=1,status=0,scrollbars=1,toolbar=0,resizable=1,width=790,height=465,left=\'+(screen.width-790)/2+\',top=\'+(screen.height-425)/2'
+ + ');void 0;">'
+ + '<?php echo jsEscTitle(sprintf(T_('Post to %s (Pop-up)'), $GLOBALS['sitename'])); ?>'
+ + '</a>'
+ + '</li>'
+ + '</ul>'
+ );
+}
+//]]>
+</script>
+
+
+<script type="text/javascript">
+function activateSocialApi(node) {
+ var baseurl = <?php echo json_encode(addProtocolToUrl(createURL())); ?>;
+ var socialApiData = {
+ // currently required
+ "name": <?php echo json_encode($GLOBALS['sitename']); ?>,
+ "iconURL": baseurl + "themes/default/images/logo.png",
+ "icon32URL": baseurl + "themes/default/images/logo.png",
+ "icon64URL": baseurl + "themes/default/images/logo.png",
+
+ "markURL": "<?php echo addProtocolToUrl(createURL('bookmarks', $GLOBALS['user'])); ?>?action=add&amp;popup=1&amp;width=800&amp;height=470&amp;address=%{url}&amp;title=%{title}&amp;description=%{description}%{text}",
+ "markedIcon": baseurl + "themes/default/images/logo.png",
+ "unmarkedIcon": baseurl + "themes/default/images/logo-empty.png",
+
+ // should be available for display purposes
+ "description": "Self-hosted bookmark manager",
+ "author": "Christian Weiske",
+ "homepageURL": "http://semanticscuttle.sf.net/",
+
+ // optional
+ "version": "0.0.3"
+ };
+
+ var event = new CustomEvent("ActivateSocialFeature");
+ node.setAttribute("data-service", JSON.stringify(socialApiData));
+ node.dispatchEvent(event);
+}
+</script>
+<p>
+ <button onclick="activateSocialApi(this)" title="activate semanticscuttle in firefox">
+ Add SemanticScuttle to Firefox
+ </button>
+</p>
diff --git a/data/templates/default/bookmarks-thumbnail.inc.tpl.php b/data/templates/default/bookmarks-thumbnail.inc.tpl.php
new file mode 100644
index 0000000..b8770dc
--- /dev/null
+++ b/data/templates/default/bookmarks-thumbnail.inc.tpl.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Bookmark thumbnail image
+ * Shows the website thumbnail for the bookmark.
+ *
+ * Expects a $row variable with bookmark data.
+ */
+
+$thumbnailer = SemanticScuttle_Service_Factory::get('Thumbnails')->getThumbnailer();
+$imgUrl = $thumbnailer->getThumbnailUrl($address, 120, 90);
+if ($imgUrl !== false) {
+ echo '<a href="' . htmlspecialchars($address) . '">'
+ . '<img class="thumbnail" width="120" height="90" src="'
+ . htmlspecialchars($imgUrl).
+ '" />'
+ . '</a>';
+}
+?> \ No newline at end of file
diff --git a/data/templates/default/bookmarks-vote-horizontal.inc.tpl.php b/data/templates/default/bookmarks-vote-horizontal.inc.tpl.php
new file mode 100644
index 0000000..c4a9f8e
--- /dev/null
+++ b/data/templates/default/bookmarks-vote-horizontal.inc.tpl.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Bookmark voting badge.
+ * Shows the number of votes and buttons to vote for or
+ * against a bookmark.
+ * Expects a $row variable with bookmark data
+ */
+if (!$GLOBALS['enableVoting'] || $GLOBALS['votingMode'] != 2) {
+ return;
+}
+if (!isset($row['hasVoted'])) {
+ $classes = 'vote-horiz vote-horiz-inactive';
+} else if (isset($row['vote'])) {
+ $classes = 'vote-horiz '
+ . ($row['vote'] == 1
+ ? 'vote-horiz-for'
+ : 'vote-horiz-against'
+ );
+} else {
+ $classes = 'vote-horiz';
+}
+echo '<div class="' . $classes . '" id="bmv-' . $row['bId'] . '">';
+echo sprintf(
+ T_('Voting <span class="voting">%d</span>'), $row['bVoting']
+) . ' ';
+
+if (isset($row['hasVoted'])) {
+ if ($row['vote'] != 1) {
+ echo '<a class="vote-for" rel="nofollow" href="'
+ . createVoteURL(true, $row['bId']) . '"'
+ . ' onclick="javascript:vote(' . $row['bId'] . ',1); return false;"'
+ . '>' . T_('Vote for') . '</a> ';
+ } else {
+ echo '<span class="vote-for-inactive">'
+ . T_('Vote for') . '</span> ';
+ }
+
+
+ if ($row['vote'] != -1) {
+ echo '<a class="vote-against" rel="nofollow" href="'
+ . createVoteURL(false, $row['bId']) . '"'
+ . ' onclick="vote(' . $row['bId'] . ',-1); return false;"'
+ . '>' . T_('Vote against') . '</a>';
+ } else {
+ echo '<span class="vote-against-inactive">'
+ . T_('Vote against') . '</span>';
+ }
+}
+echo '</div>';
+?> \ No newline at end of file
diff --git a/data/templates/default/bookmarks-vote.inc.tpl.php b/data/templates/default/bookmarks-vote.inc.tpl.php
new file mode 100644
index 0000000..41572d5
--- /dev/null
+++ b/data/templates/default/bookmarks-vote.inc.tpl.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Bookmark voting badge.
+ * Shows the number of votes and buttons to vote for or
+ * against a bookmark.
+ * Expects a $row variable with bookmark data
+ */
+if (!$GLOBALS['enableVoting'] || $GLOBALS['votingMode'] != 1) {
+ return;
+}
+if (isset($row['hasVoted']) && !$row['hasVoted']) {
+ $classes = 'vote-badge vote-badge-inactive';
+} else if (isset($row['vote'])) {
+ $classes = 'vote-badge '
+ . ($row['vote'] == 1
+ ? 'vote-badge-for'
+ : 'vote-badge-against'
+ );
+} else {
+ $classes = 'vote-badge';
+}
+echo '<span class="' . $classes . '" id="bmv-' . $row['bId'] . '">';
+
+if (isset($row['hasVoted']) && $row['vote'] != 1) {
+ echo '<a class="vote-for" rel="nofollow" href="'
+ . createVoteURL(true, $row['bId']) . '"'
+ . ' onclick="javascript:vote(' . $row['bId'] . ',1); return false;"'
+ . '>+</a>';
+} else {
+ echo '<span class="vote-for vote-for-inactive">+</span>';
+}
+
+echo '<span class="voting">' . $row['bVoting'] . '</span>';
+
+if (isset($row['hasVoted']) && $row['vote'] != -1) {
+ echo '<a class="vote-against" rel="nofollow" href="'
+ . createVoteURL(false, $row['bId']) . '"'
+ . ' onclick="vote(' . $row['bId'] . ',-1); return false;"'
+ . '>-</a>';
+} else {
+ echo '<span class="vote-against vote-against-inactive">-</span>';
+}
+echo '</span>';
+?> \ No newline at end of file
diff --git a/data/templates/default/bookmarks.tpl.php b/data/templates/default/bookmarks.tpl.php
new file mode 100644
index 0000000..20b5336
--- /dev/null
+++ b/data/templates/default/bookmarks.tpl.php
@@ -0,0 +1,547 @@
+<?php
+/**
+ * Show a list of bookmarks.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @subcategory Templates
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/* 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');
+
+
+$pageName = isset($pageName) ? $pageName : '';
+$user = isset($user) ? $user : '';
+$currenttag = isset($currenttag) ? $currenttag : '';
+
+
+$this->includeTemplate($GLOBALS['top_include']);
+
+include('search.menu.php');
+?>
+
+<?php if($pageName == PAGE_INDEX && $GLOBALS['welcomeMessage']):?>
+<p id="welcome"><?php echo $GLOBALS['welcomeMessage'];?></p>
+<?php endif?>
+
+
+<?php if($GLOBALS['enableAdminColors']!=false && isset($userid) && $userservice->isAdmin($userid) && $pageName != PAGE_WATCHLIST) : ?>
+<div style="width:70%;text-align:center;">
+ <img src="<?php echo $theme->resource('images/logo_24.gif'); ?>" width="12px"/>
+ <?php echo T_('Bookmarks on this page are managed by an admin user.'); ?>
+ <img src="<?php echo $theme->resource('images/logo_24.gif'); ?>" width="12px"/>
+</div>
+<?php endif?>
+
+
+<?php
+// common tag description
+if(($currenttag!= '' && $GLOBALS['enableCommonTagDescription'])
+|| (isset($hash) && $GLOBALS['enableCommonBookmarkDescription'])):?>
+
+
+<p class="commondescription"><?php
+$cDescription = '';
+if($currenttag!= '' && $cdservice->getLastTagDescription($currenttag)) {
+ $cDescription = $cdservice->getLastTagDescription($currenttag);
+ echo nl2br(filter($cDescription['cdDescription']));
+} elseif(isset($hash) && $cdservice->getLastBookmarkDescription($hash)) {
+ $cDescription = $cdservice->getLastBookmarkDescription($hash);
+ echo nl2br(filter($cDescription['cdTitle'])). "<br/>";
+ echo nl2br(filter($cDescription['cdDescription'])). "<br/>";
+}
+
+//common tag description edit
+if ($userservice->isLoggedOn()) {
+ if ($currenttag != ''
+ && ($GLOBALS['enableCommonTagDescriptionEditedByAll']
+ || $currentUser->isAdmin()
+ )
+ ) {
+ echo ' <a href="'. createURL('tagcommondescriptionedit', $currenttag).'" title="'.T_('Edit the common description of this tag').'">';
+ echo !is_array($cDescription) || strlen($cDescription['cdDescription'])==0?T_('Edit the common description of this tag'):'';
+ echo ' <img src="' . $theme->resource('images/b_edit.png') . '" /></a>';
+ } else if (isset($hash)) {
+ echo ' (<a href="'.createURL('bookmarkcommondescriptionedit', $hash).'" title="'.T_('Edit the common description of this bookmark').'">';
+ echo T_('Edit the common description of this bookmark').'</a>)';
+ }
+}
+?></p>
+<?php endif ?>
+
+
+<?php
+/* personal tag description */
+if($currenttag!= '' && $user!='') {
+ $userObject = $userservice->getUserByUsername($user);
+ if($tagservice->getDescription($currenttag, $userObject['uId'])) { ?>
+
+<p class="commondescription"><?php
+$pDescription = $tagservice->getDescription($currenttag, $userObject['uId']);
+echo nl2br(filter($pDescription['tDescription']));
+
+//personal tag description edit
+if($userservice->isLoggedOn()) {
+ if($currenttag!= '') {
+ echo ' <a href="'. createURL('tagedit', $currenttag).'" title="'.T_('Edit your personal description of this tag').'" >';
+ echo strlen($pDescription['tDescription'])==0?T_('Edit your personal description of this tag'):'';
+ echo ' <img src="' . $theme->resource('images/b_edit.png') . '" /></a>';
+ }
+}
+?></p>
+
+<?php
+ }
+}
+?>
+
+<?php if (isset($bookmarks) && count($bookmarks) > 0) { ?>
+<script type="text/javascript">
+window.onload = playerLoad;
+</script>
+
+<?php if (count($bookmarks) > 1) { ?>
+<p id="sort"><?php echo $total.' '.T_("bookmark(s)"); ?> - <?php echo T_("Sort by:"); ?>
+ <?php
+$titleArrow = '';
+$dateArrow = '';
+$votingArrow = '';
+$dateSort = 'date_desc';
+$titleSort = 'title_asc';
+$votingSort = 'voting_desc';
+
+switch(getSortOrder()) {
+case 'date_asc':
+ $dateArrow = ' ↑';
+ $dateSort = 'date_desc';
+ break;
+
+case 'title_asc':
+ $titleArrow = ' ↑';
+ $titleSort = 'title_desc';
+ break;
+
+case 'title_desc':
+ $titleArrow = ' ↓';
+ $titleSort = 'title_asc';
+ break;
+
+case 'voting_asc':
+ $votingArrow = ' ↑';
+ $votingSort = 'voting_desc';
+ break;
+
+case 'voting_desc':
+ $votingArrow = ' ↓';
+ $votingSort = 'voting_asc';
+ break;
+
+case 'date_desc':
+default:
+ $dateArrow = ' ↓';
+ $dateSort = 'date_asc';
+ break;
+}
+?>
+ <a href="?sort=<?php echo $dateSort ?>"><?php echo T_("Date").$dateArrow; ?></a>
+ <span>/</span>
+ <a href="?sort=<?php echo $titleSort ?>"><?php echo T_("Title").$titleArrow; ?></a>
+ <span>/</span>
+<?php if ($GLOBALS['enableVoting']) { ?>
+ <a href="?sort=<?php echo $votingSort ?>"><?php echo T_("Voting").$votingArrow; ?></a>
+ <span>/</span>
+<?php } ?>
+<?php } ?>
+
+<?php
+if ($currenttag!= '') {
+ if ($user!= '') {
+ echo ' - ';
+ echo '<a href="'. createURL('tags', $currenttag) .'">';
+ echo T_('Bookmarks from other users for this tag').'</a>';
+ //echo T_(' for these tags');
+ } else if ($userservice->isLoggedOn()){
+ echo ' - ';
+ echo '<a href="'. createURL('bookmarks', $currentUser->getUsername().'/'.$currenttag) .'">';
+ echo T_('Only your bookmarks for this tag').'</a>';
+ //echo T_(' for these tags');
+ }
+}
+?></p>
+
+<?php
+ // PAGINATION
+
+ // Ordering
+ $sortOrder = '';
+ if (GET_SORT != '') {
+ $sortOrder = 'sort=' . getSortOrder();
+ }
+
+ $sortAmp = (($sortOrder) ? '&amp;'. $sortOrder : '');
+ $sortQue = (($sortOrder) ? '?'. $sortOrder : '');
+
+ // Previous
+ $perpage = getPerPageCount($currentUser);
+ if (!$page || $page < 2) {
+ $page = 1;
+ $start = 0;
+ $bfirst = '<span class="disable">'. T_('First') .'</span>';
+ $bprev = '<span class="disable">'. T_('Previous') .'</span>';
+ } else {
+ $prev = $page - 1;
+ $prev = 'page='. $prev;
+ $start = ($page - 1) * $perpage;
+ $bfirst= '<a href="'. sprintf($nav_url, $user, $currenttag, '') . $sortQue .'">'. T_('First') .'</a>';
+ $bprev = '<a href="'. sprintf($nav_url, $user, $currenttag, '?') . $prev . $sortAmp .'">'. T_('Previous') .'</a>';
+ }
+
+ // Next
+ $next = $page + 1;
+ $totalpages = ceil($total / $perpage);
+ if (count($bookmarks) < $perpage || $perpage * $page == $total) {
+ $bnext = '<span class="disable">'. T_('Next') .'</span>';
+ $blast = '<span class="disable">'. T_('Last') ."</span>\n";
+ } else {
+ $bnext = '<a href="'. sprintf($nav_url, $user, $currenttag, '?page=') . $next . $sortAmp .'">'. T_('Next') .'</a>';
+ $blast = '<a href="'. sprintf($nav_url, $user, $currenttag, '?page=') . $totalpages . $sortAmp .'">'. T_('Last') ."</a>\n";
+ }
+
+ // RSS
+ $brss = '';
+ $size = count($rsschannels);
+ for ($i = 0; $i < $size; $i++) {
+ $brss = '<a style="background:#FFFFFF"'
+ . ' href="'. htmlspecialchars($rsschannels[$i][1]) . '"'
+ . ' title="' . htmlspecialchars($rsschannels[$i][0]) . '">'
+ . '<img src="' . $theme->resource('images/rss.gif') . '"'
+ . ' width="16" height="16"'
+ . ' alt="' . htmlspecialchars($rsschannels[$i][0]) .'"/>'
+ . '</a>';
+ }
+
+ $pagesBanner = '<p class="paging">'. $bfirst .'<span> / </span>'. $bprev .'<span> / </span>'. $bnext .'<span> / </span>'. $blast .'<span> / </span>'. sprintf(T_('Page %d of %d'), $page, $totalpages) ." ". $brss ." </p>\n";
+
+ if (getPerPageCount($currentUser) > 10) {
+ echo $pagesBanner; // display a page banner if too many bookmarks to manage
+ }
+
+
+?>
+
+
+
+<ol<?php echo ($start > 0 ? ' start="'. ++$start .'"' : ''); ?> id="bookmarks">
+<?php
+ $addresses = array();
+ foreach ($bookmarks as $key => &$row) {
+ $addresses[$row['bId']] = $row['bAddress'];
+ }
+ $otherCounts = $bookmarkservice->countOthers($addresses);
+ if ($userservice->isLoggedOn()) {
+ $existence = $bookmarkservice->bookmarksExist(
+ $addresses, $currentUser->getId()
+ );
+ }
+
+ if ($userservice->isLoggedOn()) {
+ $watchedNames = $userservice->getWatchNames(
+ $currentUser->getId(), true
+ );
+ } else {
+ $watchedNames = null;
+ }
+
+ foreach ($bookmarks as $key => &$row) {
+ switch ($row['bStatus']) {
+ case 0:
+ $access = '';
+ break;
+ case 1:
+ $access = ' shared';
+ break;
+ case 2:
+ $access = ' private';
+ break;
+ }
+
+ // Add username in case bookmark was loaded using getBookmark()
+ if (!isset($row['username']) && isset($row['uId'])) {
+ $userinfo = $userservice->getObjectUser($row['uId']);
+ $row['username'] = $userinfo->username;
+ }
+
+ $cats = '';
+ $tagsForCopy = '';
+ $tags = $row['tags'];
+ foreach ($tags as $tkey => &$tag) {
+ $tagcaturl = sprintf(
+ $cat_url,
+ filter($row['username'], 'url'),
+ filter($tag, 'url')
+ );
+ $cats .= sprintf(
+ '<a href="%s" rel="tag">%s</a>, ',
+ $tagcaturl, filter($tag)
+ );
+ $tagsForCopy .= $tag . ',';
+ }
+ $cats = substr($cats, 0, -2);
+ if ($cats != '') {
+ $cats = T_('Tags:') . ' ' . $cats;
+ }
+
+ // Edit and delete links
+ $edit = '';
+ if ($bookmarkservice->editAllowed($row)) {
+ $edit = ' - <a href="' . createURL('edit', $row['bId']) . '">'
+ . T_('Edit')
+ . '</a>'
+ . ' <a href="#" onclick="deleteBookmark(this, '. $row['bId'] .'); return false;">'
+ . T_('Delete')
+ .'</a>';
+ }
+
+ // Last update
+ $update = ' <small title="'. T_('Last update') .'">('. date($GLOBALS['shortdate'], strtotime($row['bModified'])). ') </small>';
+
+ // User attribution
+ if ($userservice->isLoggedOn()
+ && $currentUser->getUsername() == $row['username']
+ ) {
+ $copy = ' ' . T_('by') . ' ';
+ $copy .= T_('you');
+ } else if (isset($row['username'])) {
+ $copy = ' ' . T_('by') . ' ';
+ $copy .= '<a href="' . createURL('bookmarks', $row['username']) . '">'
+ . SemanticScuttle_Model_UserArray::getName($row)
+ . '</a>';
+ }
+
+ // others
+ if (!isset($hash)) {
+ $others = $otherCounts[$row['bAddress']];
+ $ostart = '<a href="' . createURL('history', $row['bHash']) . '">';
+ $oend = '</a>';
+ switch ($others) {
+ case 0:
+ break;
+ case 1:
+ $copy .= sprintf(T_(' and %s1 other%s'), $ostart, $oend);
+ break;
+ default:
+ $copy .= sprintf(T_(' and %2$s%1$s others%3$s'), $others, $ostart, $oend);
+ }
+ }
+
+ // Local cache
+ $cacheInfo = $GLOBALS['dir_cache'] .'/urls';
+ $cacheLink = null;
+ if ($GLOBALS['cacheUrl'] != null) {
+ // Hashing discussion at http://linuxprocess.free.fr/MHonArc/Oct-2005/msg00016.html
+ $assetHash = sha1($row['bAddress'] . "\n");
+ $assetHash = substr($assetHash, 0, 2) . '/' . substr($assetHash, 2, 2) . '/' . $assetHash;
+ $assetBase = $GLOBALS['cacheUrl'] . '/' . $assetHash;
+ $assetLink = $assetBase;
+ $assetPdf = $assetBase.'/screenshot.pdf';
+ $assetPng = $assetBase .'/screenshot.png';
+
+ // Handle PDF links
+ $assetFile = basename(parse_url($row['bAddress'])['path']);
+ $assetExt = strtolower(pathinfo($assetFile, PATHINFO_EXTENSION));
+ if ($assetExt == 'pdf') {
+ $assetLink = $GLOBALS['cacheUrl'] . '/' . $assetHash . '/' . $assetFile;
+ }
+
+ // Check if the link exists
+ if (file_exists($cacheInfo .'/'. $assetHash .'/hascache')) {
+ $cacheLink = "| <a href=\"$assetLink\">Cache</a>";
+
+ if (file_exists($cacheInfo .'/'. $assetHash .'/haspdf')) {
+ $cacheLink .= " | <a href=\"$assetPdf\">PDF</a>";
+ }
+
+ if (file_exists($cacheInfo .'/'. $assetHash .'/haspng')) {
+ $cacheLink .= " | <a href=\"$assetPng\">PNG</a>";
+ }
+ }
+ else if ($fp = curl_init($assetBase)) {
+ curl_setopt($fp, CURLOPT_NOBODY, true);
+ curl_exec($fp);
+
+ $retcode = curl_getinfo($fp, CURLINFO_HTTP_CODE);
+
+ if ($retcode != 404) {
+ if (!file_exists($cacheInfo .'/'. $assetHash)) {
+ mkdir($cacheInfo .'/'. $assetHash, 0755, true);
+ }
+
+ touch($cacheInfo .'/'. $assetHash .'/hascache');
+ $cacheLink = "| <a href=\"$assetLink\">Cache</a>";
+
+ // Check if PDF is available
+ if ($fp = curl_init($assetPdf)) {
+ curl_setopt($fp, CURLOPT_NOBODY, true);
+ curl_exec($fp);
+
+ $retcode = curl_getinfo($fp, CURLINFO_HTTP_CODE);
+
+ if ($retcode != 404) {
+ touch($cacheInfo .'/'. $assetHash .'/haspdf');
+ $cacheLink .= " | <a href=\"$assetPdf\">PDF</a>";
+ }
+
+ curl_close($fp);
+ }
+
+ // Check if PNG is available
+ if ($fp = curl_init($assetPng)) {
+ curl_setopt($fp, CURLOPT_NOBODY, true);
+ curl_exec($fp);
+
+ $retcode = curl_getinfo($fp, CURLINFO_HTTP_CODE);
+
+ if ($retcode != 404) {
+ touch($cacheInfo .'/'. $assetHash .'/haspng');
+ $cacheLink .= " | <a href=\"$assetPng\">PNG</a>";
+ }
+
+ curl_close($fp);
+ }
+ }
+
+ curl_close($fp);
+ }
+ }
+
+ // Copy link
+ if ($userservice->isLoggedOn()
+ && ($currentUser->getId() != $row['uId'])
+ && !$existence[$row['bAddress']]
+ ) {
+ $copy .= ' - <a href="'
+ . createURL(
+ 'bookmarks',
+ $currentUser->getUsername()
+ . '?action=add&amp;copyOf=' . $row['bId'])
+ . '" title="'.T_('Copy this bookmark to YOUR bookmarks.').'">'
+ . T_('Copy')
+ . '</a>';
+ }
+
+ // Nofollow option
+ $rel = '';
+ if ($GLOBALS['nofollow']) {
+ $rel = ' rel="nofollow"';
+ }
+
+ $address = $row['bAddress'];
+ $oaddress = $address;
+ // Redirection option
+ if ($GLOBALS['useredir']) {
+ $address = $GLOBALS['url_redir'] . $address;
+ }
+
+ // Admin specific design
+ if ($userservice->isAdmin($row['username'])
+ && $GLOBALS['enableAdminColors']
+ ) {
+ $adminBgClass = ' class="adminBackground"';
+ $adminStar = ' <img'
+ . ' src="' . $theme->resource('images/logo_24.gif') . '"'
+ . ' width="12px"'
+ . ' title="' . T_('This bookmark is certified by an admin user.') . '"'
+ . '/>';
+ } else {
+ $adminBgClass = '';
+ $adminStar = '';
+ }
+
+ // Private Note (just visible by the owner and his/her contacts)
+ if ($watchedNames !== null
+ && ($currentUser->getId() == $row['uId']
+ || in_array($row['username'], $watchedNames)
+ )
+ ) {
+ $privateNoteField = $row['bPrivateNote'];
+ } else {
+ $privateNoteField = '';
+ }
+
+ if ($GLOBALS['enableVoting'] && $GLOBALS['hideBelowVoting'] !== null
+ && $row['bVoting'] < $GLOBALS['hideBelowVoting']
+ ) {
+ $access .= ' below-threshold';
+ }
+
+ // Output
+ echo ' <li class="xfolkentry'. $access .'">'."\n";
+ include 'bookmarks-thumbnail.inc.tpl.php';
+ include 'bookmarks-vote.inc.tpl.php';
+
+ echo ' <div' . $adminBgClass . '>' . "\n";
+
+ echo ' <div class="link">'
+ . '<a href="'. htmlspecialchars($address) .'"'. $rel .' class="taggedlink">'
+ . filter($row['bTitle'])
+ . '</a>' . $adminStar . "</div>\n";
+ if ($row['bDescription'] == '') {
+ $bkDescription = $GLOBALS['blankDescription'];
+ } else {
+ // Improve description display (anchors, links, ...)
+ $bkDescription = preg_replace('|\[\/.*?\]|', '', filter($row['bDescription'])); // remove final anchor
+ $bkDescription = preg_replace('|\[(.*?)\]|', ' <span class="anchorBookmark">$1</span> » ', $bkDescription); // highlight starting anchor
+ $bkDescription = preg_replace('@((http|https|ftp)://.*?)( |\r|$)@', '<a href="$1" rel="nofollow">$1</a>$3', $bkDescription); // make url clickable
+
+ }
+ echo ' <div class="description">'. nl2br($bkDescription) ."</div>\n";
+ echo ' <div class="address">' . htmlspecialchars(shortenString($oaddress)) . "</div>\n";
+
+ echo ' <div class="meta">'
+ . $cats . "\n"
+ . $copy . "\n"
+ . $edit . "\n"
+ . $update . "\n"
+ . $cacheLink ."\n"
+ . ' | <a href="/permalink/'. $row['bId'] . '">Permalink</a>' ."\n"
+ . " </div>\n";
+ echo $privateNoteField != ''
+ ? ' <div class="privateNote" title="'. T_('Private Note on this bookmark') .'">'.$privateNoteField."</div>\n"
+ : '';
+ echo ' ';
+ include 'bookmarks-vote-horizontal.inc.tpl.php';
+ echo " </div>\n";
+
+ echo " </li>\n";
+ }
+ ?>
+
+</ol>
+
+ <?php
+ if(getPerPageCount($currentUser)>7) {
+ echo '<p class="backToTop"><a href="#header" title="'.T_('Come back to the top of this page.').'">'.T_('Top of the page').'</a></p>';
+ }
+
+ if (isset($bookmarks) && count($bookmarks) > 1) {
+ echo $pagesBanner; // display previous and next links pages + RSS link
+ }
+
+} else {
+ echo '<p class="error">'.T_('No bookmarks available').'</p>';
+}
+$this->includeTemplate('sidebar.tpl');
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/bottom.inc.php b/data/templates/default/bottom.inc.php
new file mode 100644
index 0000000..9e4014c
--- /dev/null
+++ b/data/templates/default/bottom.inc.php
@@ -0,0 +1,29 @@
+<!--following code is generated by templates/bottom.inc.php-->
+<div id="bottom">
+<?php echo $GLOBALS['footerMessage'].' ';?>
+<?php
+echo '<a href="'.createURL('about').'">'.T_('About').'</a>';
+echo ' - ';
+echo T_("Propulsed by ");
+echo " <a href=\"https://sourceforge.net/projects/semanticscuttle/\">SemanticScuttle</a>";
+?>
+
+</div>
+
+
+<?php if(isset($GLOBALS['googleAnalyticsCode']) && $GLOBALS['googleAnalyticsCode']!= ''):?>
+
+<script type="text/javascript">
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+try {
+var pageTracker = _gat._getTracker("<?php echo $GLOBALS['googleAnalyticsCode']?>");
+pageTracker._trackPageview();
+} catch(err) {}</script>
+
+<?php endif;?>
+
+</body>
+</html>
diff --git a/data/templates/default/dynamictags.inc.php b/data/templates/default/dynamictags.inc.php
new file mode 100644
index 0000000..c2ab6d4
--- /dev/null
+++ b/data/templates/default/dynamictags.inc.php
@@ -0,0 +1,148 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2005 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+/* Service creation: only useful services are created */
+$b2tservice = SemanticScuttle_Service_Factory :: get('Bookmark2Tag');
+
+$logged_on_userid = $userservice->getCurrentUserId();
+
+//tags from current user
+$userPopularTags = $b2tservice->getPopularTags($logged_on_userid, 25, $logged_on_userid);
+$userPopularTagsCloud = $b2tservice->tagCloud($userPopularTags, 5, 90, 175);
+$userPopularTagsCount = count($userPopularTags);
+
+//tags from all users
+$allPopularTags = $b2tservice->getPopularTags(null, 5, $logged_on_userid);
+$allPopularTagsCloud = $b2tservice->tagCloud($allPopularTags, 5, 90, 175);
+$allPopularTagsCount = count($allPopularTags);
+
+
+// function printing the cloud
+function writeTagsProposition($tagsCloud, $title)
+{
+ static $id = 0;
+ ++$id;
+
+ echo <<<JS
+ $('.edit-tagclouds')
+ .append(
+'<div class="collapsible" id="edit-tagcloud-$id">'
++ ' <h3>$title</h3>'
++ ' <p class="popularTags tags"></p>'
++ '</div>');
+JS;
+
+ $taglist = '';
+ foreach (array_keys($tagsCloud) as $key) {
+ $row = $tagsCloud[$key];
+ $entries = T_ngettext('bookmark', 'bookmarks', $row['bCount']);
+ $taglist .= '<span'
+ . ' title="'. $row['bCount'] . ' ' . $entries . '"'
+ . ' style="font-size:' . $row['size'] . '"'
+ . ' onclick="addTag(this)">'
+ . filter($row['tag'])
+ . '</span> ';
+ }
+ echo '$(\'#edit-tagcloud-' . $id . ' p\').append('
+ . json_encode($taglist)
+ . ");\n";
+}
+
+
+if ($allPopularTagsCount > 0 || $userPopularTagsCount > 0 ) { ?>
+<script type="text/javascript">
+//<![CDATA[
+Array.prototype.contains = function (ele) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] == ele) {
+ return true;
+ }
+ }
+ return false;
+};
+
+Array.prototype.remove = function (ele) {
+ var arr = new Array();
+ var count = 0;
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] != ele) {
+ arr[count] = this[i];
+ count++;
+ }
+ }
+ return arr;
+};
+
+function addonload(addition) {
+ //var existing = window.onload;
+ window.onload = function () {
+ //existing();
+ addition();
+ }
+}
+
+jQuery(function($) {
+<?php
+if ($userPopularTagsCount > 0) {
+ writeTagsProposition($userPopularTagsCloud, T_('Popular Tags'));
+}
+if ($allPopularTagsCount > 0) {
+ writeTagsProposition($allPopularTagsCloud, T_('Popular Tags From All Users'));
+}
+?>
+ var taglist = $('#tags');
+ var tags = taglist.val().split(', ');
+
+ var populartags = $('.edit-tagclouds span');
+
+ for (var i = 0; i < populartags.length; i++) {
+ if (tags.contains(populartags[i].innerHTML)) {
+ populartags[i].className = 'selected';
+ }
+ }
+});
+
+function addTag(ele) {
+ var thisTag = ele.innerHTML;
+ var taglist = document.getElementById('tags');
+ var tags = taglist.value.split(', ');
+
+ // If tag is already listed, remove it
+ if (tags.contains(thisTag)) {
+ tags = tags.remove(thisTag);
+ ele.className = 'unselected';
+
+ // Otherwise add it
+ } else {
+ tags.splice(0, 0, thisTag);
+ ele.className = 'selected';
+ }
+
+ taglist.value = tags.join(', ');
+
+ document.getElementById('tags').focus();
+}
+//]]>
+</script>
+<div class="edit-tagclouds"></div>
+<?php
+}
+?>
diff --git a/data/templates/default/editbookmark.tpl.php b/data/templates/default/editbookmark.tpl.php
new file mode 100644
index 0000000..8b71230
--- /dev/null
+++ b/data/templates/default/editbookmark.tpl.php
@@ -0,0 +1,221 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+
+$accessPublic = '';
+$accessShared = '';
+$accessPrivate = '';
+switch ($row['bStatus']) {
+ case 0 :
+ $accessPublic = ' selected="selected"';
+ break;
+ case 1 :
+ $accessShared = ' selected="selected"';
+ break;
+ case 2 :
+ $accessPrivate = ' selected="selected"';
+ break;
+}
+
+function jsEscTitle($title)
+{
+ return addcslashes($title, "'");
+}
+function jsEscTitleDouble($title)
+{
+ return addcslashes(addcslashes($title, "'"), "'\\");
+}
+function fixOperaButtonName($name) {
+ //yes, opera has problems with double quotes in button names
+ return str_replace('"', "''", $name);
+}
+
+if (is_array($row['tags'])) {
+ $row['tags'] = implode(', ', $row['tags']);
+}
+
+$ajaxUrl = ROOT . 'ajax/'
+ . (
+ ($GLOBALS['adminsAreAdvisedTagsFromOtherAdmins'] && $currentUser->isAdmin())
+ ? 'getadmintags'
+ : 'getcontacttags'
+ ) . '.php';
+?>
+<form action="<?php echo $formaction; ?>" method="post">
+<table>
+<tr>
+ <th align="left"><?php echo T_('Address'); ?></th>
+ <td><input type="text" id="address" name="address" size="75" maxlength="65535" value="<?php echo filter($row['bAddress'], 'xml'); ?>" onblur="useAddress(this)" /></td>
+ <td>↠<?php echo T_('Required'); ?></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Title'); ?></th>
+ <td><input type="text" id="titleField" name="title" size="75" maxlength="255" value="<?php echo filter($row['bTitle'], 'xml'); ?>" onkeypress="this.style.backgroundImage = 'none';" /></td>
+ <td>↠<?php echo T_('Required'); ?></td>
+</tr>
+<tr>
+ <th align="left">
+ <?php echo T_('Description'); ?>
+ <a onclick="var nz = document.getElementById('privateNoteZone'); nz.style.display='';this.style.display='none';"><?php echo T_("Add Note"); ?></a>
+ </th>
+ <td><textarea name="description" id="description" rows="5" cols="63" ><?php echo filter($row['bDescription'], 'xml'); ?></textarea></td>
+ <td>↠<?php echo T_('You can use anchors to delimite attributes. for example: [publisher]blah[/publisher] '); ?>
+ <?php if(count($GLOBALS['descriptionAnchors'])>0): ?>
+ <br /><br />
+ <?php echo T_('Suggested anchors: '); ?>
+ <?php foreach($GLOBALS['descriptionAnchors'] as $anchorName => $anchorValue): ?>
+ <?php if(is_numeric($anchorName)) {
+ $anchorName = $anchorValue;
+ $anchorValue = '['.$anchorValue.']'.'[/'.$anchorValue.']';
+ } ?>
+ <span class="anchor" title="<?php echo $anchorValue ?>" onclick="addAnchor('<?php echo $anchorValue ?>', 'description')"><?php echo $anchorName ?></span>
+ <?php endforeach; ?>
+ <?php endif; ?>
+ </td>
+</tr>
+<tr id="privateNoteZone" <?php if(strlen($row['bPrivateNote'])==0):?>style="display:none"<?php endif; ?>>
+ <th align="left"><?php echo T_('Private Note'); ?></th>
+ <td><textarea name="privateNote" id="privateNote" rows="1" cols="63" ><?php echo filter($row['bPrivateNote'], 'xml'); ?></textarea></td>
+ <td>↠<?php echo T_('Just visible by you and your contacts.'); ?>
+ </td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Tags'); ?></th>
+ <td class="scuttletheme">
+ <input type="text" id="tags" name="tags" size="75" value="<?php echo filter($row['tags'], 'xml'); ?>"/>
+ </td>
+ <td>↠<?php echo T_('Comma-separated'); ?></td>
+</tr>
+<tr>
+ <th></th>
+ <td align="right"><small><?php echo htmlspecialchars(T_('Note: use ">" to include one tag in another. e.g.: europe>france>paris'))?></small></td>
+</tr>
+<tr>
+ <th></th>
+ <td align="right"><small><?php echo T_('Note: use "=" to make synonym two tags. e.g.: france=frenchcountry')?></small></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Privacy'); ?></th>
+ <td>
+ <select name="status">
+ <option value="0"<?php echo $accessPublic ?>><?php echo T_('Public'); ?></option>
+ <option value="1"<?php echo $accessShared ?>><?php echo T_('Shared with Watch List'); ?></option>
+ <option value="2"<?php echo $accessPrivate ?>><?php echo T_('Private'); ?></option>
+ </select>
+ </td>
+ <td></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <input type="submit" name="submitted" value="<?php echo $btnsubmit; ?>" />
+ <input type="button" name="cancel" value="<?php echo T_('Cancel') ?>" onclick="<?php echo $popup?'window.close();':'javascript: history.go(-1)'; ?>" />
+ <?php
+ if (isset($showdelete) && $showdelete) {
+ ?>
+ <input type="submit" name="delete" value="<?php echo T_('Delete Bookmark'); ?>" />
+ <?php
+ }
+ if (isset($showdelete) && $showdelete) {
+ echo ' (<a href="'.createURL('bookmarkcommondescriptionedit', $row['bHash']).'">';
+ echo T_('edit common description').'</a>)';
+ }
+
+ if ($popup) {
+ ?>
+ <input type="hidden" name="popup" value="1" />
+ <?php
+ } elseif (isset($referrer)) {
+ ?>
+ <input type="hidden" name="referrer" value="<?php echo $referrer; ?>" />
+ <?php
+ }
+ ?>
+ </td>
+ <td></td>
+ </tr>
+ </table>
+</form>
+
+<link href="<?php echo ROOT ?>js/jquery-ui-1.8.11/themes/base/jquery.ui.all.css" rel="stylesheet" type="text/css"/>
+
+<script type="text/javascript" src="<?php echo ROOT ?>js/jquery-ui-1.8.11/jquery.ui.core.js"></script>
+<script type="text/javascript" src="<?php echo ROOT ?>js/jquery-ui-1.8.11/jquery.ui.widget.js"></script>
+<script type="text/javascript" src="<?php echo ROOT ?>js/jquery-ui-1.8.11/jquery.ui.position.js"></script>
+<script type="text/javascript" src="<?php echo ROOT ?>js/jquery-ui-1.8.11/jquery.ui.autocomplete.js"></script>
+<script type="text/javascript">
+//<![CDATA[
+jQuery(document).ready(function() {
+ function split(val)
+ {
+ return val.split(/[,=><]\s*/);
+ }
+
+ function extractLast(term)
+ {
+ return split(term).pop();
+ }
+ //var availableTags = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby"];
+
+ jQuery("input#tags").autocomplete({
+ autoFocus: true,
+ minLength: 1,
+
+ source: function(request, response) {
+ // delegate back to autocomplete, but extract the last term
+ var term = extractLast(request.term);
+ if (term.length < this.options.minLength) {
+ return;
+ }
+ response(
+ /*
+ $.ui.autocomplete.filter(
+ availableTags, extractLast(request.term)
+ )
+ */
+ $.getJSON(
+ "<?php echo $ajaxUrl; ?>",
+ { beginsWith: term },
+ response
+ )
+ );
+ },
+
+ focus: function() {
+ // prevent value inserted on focus
+ return false;
+ },
+ select: function(event, ui) {
+ var terms = split(this.value);
+ // remove the current input
+ terms.pop();
+ // add the selected item
+ terms.push(ui.item.value);
+ // add placeholder to get the comma-and-space at the end
+ terms.push("");
+ this.value = terms.join(", ");
+ return false;
+ }
+ });
+});
+//]]>
+</script>
+
+<?php
+// Dynamic tag selection
+$this->includeTemplate('dynamictags.inc');
+
+// Bookmarklets and import links
+if (empty($_REQUEST['popup']) && (!isset($showdelete) || !$showdelete)) {
+
+$this->includeTemplate('bookmarklet.inc.php');
+?>
+<h3><?php echo T_('Import'); ?></h3>
+<ul>
+ <li><a href="<?php echo createURL('importNetscape'); ?>"><?php echo T_('Import bookmarks from bookmark file'); ?></a> (<?php echo T_('Internet Explorer, Mozilla Firefox and Netscape'); ?>)</li>
+ <li><a href="<?php echo createURL('import'); ?>"><?php echo T_('Import bookmarks from del.icio.us'); ?></a></li>
+</ul>
+
+<?php
+}
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/editprofile-sslclientcerts.tpl.php b/data/templates/default/editprofile-sslclientcerts.tpl.php
new file mode 100644
index 0000000..900c193
--- /dev/null
+++ b/data/templates/default/editprofile-sslclientcerts.tpl.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * User's own profile page: SSL client certificate settings
+ *
+ * @param array $sslClientCerts Array of SSL client certificate objects
+ * @param string $formaction URL where to send the forms to
+ * @param SemanticScuttle_Model_User_SslClientCert
+ * $currentCert Current SSL client certificate (may be null)
+ */
+?>
+<h3><?php echo T_('SSL client certificates'); ?></h3>
+<?php if (count($sslClientCerts)) { ?>
+<table>
+ <thead>
+ <tr>
+ <th>Options</th>
+ <th><?php echo T_('Serial'); ?></th>
+ <th><?php echo T_('Name'); ?></th>
+ <th><?php echo T_('Email'); ?></th>
+ <th><?php echo T_('Issuer'); ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach($sslClientCerts as $cert) { ?>
+ <tr <?php if ($cert->isCurrent()) { echo 'class="ssl-current"'; } ?>>
+ <td>
+ <form method="post" action="<?php echo $formaction; ?>">
+ <input type="hidden" name="certId" value="<?php echo $cert->id; ?>"/>
+ <button type="submit" name="action" value="deleteClientCert">
+ <?php echo T_('delete'); ?>
+ </button>
+ </form>
+ </td>
+ <td><?php echo htmlspecialchars($cert->sslSerial); ?></td>
+ <td><?php echo htmlspecialchars($cert->sslName); ?></td>
+ <td><?php echo htmlspecialchars($cert->sslEmail); ?></td>
+ <td><?php echo htmlspecialchars($cert->sslClientIssuerDn); ?></td>
+ </tr>
+ <?php } ?>
+ </tbody>
+</table>
+<?php } else { ?>
+ <p><?php echo T_('No certificates registered'); ?></p>
+<?php } ?>
+
+<?php if ($currentCert) { ?>
+ <?php if ($currentCert->isRegistered($sslClientCerts)) { ?>
+ <p><?php echo T_('Your current certificate is already registered with your account.'); ?></p>
+ <?php } else { ?>
+ <p>
+ <form method="post" action="<?php echo $formaction; ?>">
+ <button type="submit" name="action" value="registerCurrentCert">
+ <?php echo T_('Register current certificate to automatically login.'); ?>
+ </button>
+ </form>
+ </p>
+ <?php } ?>
+<?php } else { ?>
+ <p><?php echo T_('Your browser does not provide a certificate.'); ?></p>
+<?php } ?>
diff --git a/data/templates/default/editprofile.tpl.php b/data/templates/default/editprofile.tpl.php
new file mode 100644
index 0000000..25dc3a4
--- /dev/null
+++ b/data/templates/default/editprofile.tpl.php
@@ -0,0 +1,81 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<form action="<?php echo $formaction; ?>" method="post">
+<input type="hidden" name="token" value="<?php echo $token; ?>"/>
+
+<h3><?php echo T_('Account Details'); ?></h3>
+
+<table class="profile">
+<tr>
+ <th align="left"><?php echo T_('Username'); ?></th>
+ <td><?php echo $user; ?></td>
+ <td></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('New Password'); ?></th>
+ <td><input type="password" name="pPass" size="20" /></td>
+ <td></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Confirm Password'); ?></th>
+ <td><input type="password" name="pPassConf" size="20" /></td>
+ <td></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('E-mail'); ?></th>
+ <td><input type="text" name="pMail" size="75" value="<?php echo filter($objectUser->getEmail(), 'xml'); ?>" /></td>
+ <td>↠<?php echo T_('Required'); ?></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Private RSS Feed'); ?></th>
+ <td><input type="checkbox" id="pEnablePrivateKey" name="pEnablePrivateKey" value="true" <?php echo $privateKeyIsEnabled;?> />
+ <label for="pEnablePrivateKey"><?php echo T_('Enable'); ?></label>&nbsp;&nbsp;&nbsp;
+ <input type="text" id="pPrivateKey" name="pPrivateKey" size="40" value="<?php echo $privateKey;?>" readonly="readonly" />
+ <a onclick="getNewPrivateKey(this); return false;"><button type="submit" name="submittedPK" value="1"><?php echo T_('Generate New Key'); ?></button></a>
+ </td>
+</tr>
+</table>
+
+<h3><?php echo T_('Personal Details'); ?></h3>
+
+<table class="profile">
+<tr>
+ <th align="left"><?php echo T_('Name'); ?></th>
+ <td><input type="text" name="pName" size="75" value="<?php echo filter($objectUser->getName(), 'xml'); ?>" /></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Homepage'); ?></th>
+ <td><input type="text" name="pPage" size="75" value="<?php echo filter($objectUser->getHomepage()); ?>" /></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('Description'); ?></th>
+ <td><textarea name="pDesc" cols="75" rows="10"><?php echo $objectUser->getContent(); ?></textarea></td>
+</tr>
+<tr>
+ <th></th>
+ <td><input type="submit" name="submitted" value="<?php echo T_('Save Changes'); ?>" /></td>
+</tr>
+</table>
+
+<?php include 'editprofile-sslclientcerts.tpl.php'; ?>
+<h3><?php echo T_('Actions'); ?></h3>
+<table class="profile">
+<tr>
+ <th align="left"><?php echo T_('Export bookmarks'); ?></th>
+ <td>
+ <a href="../api/export_html.php"><?php echo T_('HTML file (for browsers)')?></a> /
+ <a href="../api/posts_all.php"><?php echo T_('XML file (like del.icio.us)')?></a> /
+ <a href="../api/export_csv.php"><?php echo T_('CSV file (for spreadsheet tools)')?></a>
+ </td>
+</tr>
+<tr><th> </th><td> </td></tr>
+<tr><th> </th><td> </td></tr>
+</table>
+
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/error.404.tpl.php b/data/templates/default/error.404.tpl.php
new file mode 100644
index 0000000..fe9401d
--- /dev/null
+++ b/data/templates/default/error.404.tpl.php
@@ -0,0 +1,9 @@
+<?php
+header('HTTP/1.x 404 Not Found');
+$this->includeTemplate($GLOBALS['top_include']);
+if (!$error) {
+ echo '<h1>'. T_('Not Found') .'</h1>';
+ echo '<p>'. T_('The requested URL was not found on this server') .'</p>';
+}
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/error.500.tpl.php b/data/templates/default/error.500.tpl.php
new file mode 100644
index 0000000..1d7f7ae
--- /dev/null
+++ b/data/templates/default/error.500.tpl.php
@@ -0,0 +1,9 @@
+<?php
+header('HTTP/1.x 500 Server error');
+$this->includeTemplate($GLOBALS['top_include']);
+if (!$error) {
+ echo '<h1>'. T_('General server error') .'</h1>';
+ echo '<p>'. T_('The requested URL could not be processed') .'</p>';
+}
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/importDelicious.tpl.php b/data/templates/default/importDelicious.tpl.php
new file mode 100644
index 0000000..0f53cde
--- /dev/null
+++ b/data/templates/default/importDelicious.tpl.php
@@ -0,0 +1,42 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<form id="import" enctype="multipart/form-data" action="<?php echo $formaction; ?>" method="post">
+<table>
+<tr valign="top">
+ <th align="left"><?php echo T_('File'); ?></th>
+ <td>
+ <input type="hidden" name="MAX_FILE_SIZE" value="1024000" />
+ <input type="file" name="userfile" size="50" />
+ </td>
+</tr>
+<tr valign="top">
+ <th align="left"><?php echo T_('Privacy'); ?></th>
+ <td>
+ <select name="status">
+ <option value="0"><?php echo T_('Public'); ?></option>
+ <option value="1"><?php echo T_('Shared with Watchlist'); ?></option>
+ <option value="2"><?php echo T_('Private'); ?></option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td />
+ <td><input type="submit" value="<?php echo T_('Import'); ?>" /></td>
+</tr>
+</table>
+</form>
+
+<h3><?php echo T_('Instructions'); ?></h3>
+<ol>
+ <li><?php echo T_('Log in to the <a href="https://api.del.icio.us/v1/posts/all">export page at del.icio.us</a>'); ?>.</li>
+ <li><?php echo T_('Save the resulting <abbr title="Extensible Markup Language">XML</abbr> file to your computer'); ?>.</li>
+ <li><?php echo T_('Click <kbd>Browse...</kbd> to find this file on your computer. The maximum size the file can be is 1MB'); ?>.</li>
+ <li><?php echo T_('Select the default privacy setting for your imported bookmarks'); ?>.</li>
+ <li><?php echo T_('Click <kbd>Import</kbd> to start importing the bookmarks; it may take a minute'); ?>.</li>
+</ol>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/importNetscape.tpl.php b/data/templates/default/importNetscape.tpl.php
new file mode 100644
index 0000000..627a5af
--- /dev/null
+++ b/data/templates/default/importNetscape.tpl.php
@@ -0,0 +1,50 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<div id="bookmarks">
+ <form id="import" enctype="multipart/form-data" action="<?php echo $formaction; ?>" method="post">
+ <table>
+ <tr valign="top">
+ <th align="left"><?php echo T_('File'); ?></th>
+ <td>
+ <input type="hidden" name="MAX_FILE_SIZE" value="1024000" />
+ <input type="file" name="userfile" size="50" />
+ </td>
+ </tr>
+ <tr valign="top">
+ <th align="left"><?php echo T_('Privacy'); ?></th>
+ <td>
+ <select name="status">
+ <option value="0"><?php echo T_('Public'); ?></option>
+ <option value="1"><?php echo T_('Shared with Watchlist'); ?></option>
+ <option value="2"><?php echo T_('Private'); ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td />
+ <td><input type="submit" value="<?php echo T_('Import'); ?>" /></td>
+ </tr>
+ </table>
+ </form>
+
+ <h3><?php echo T_('Instructions'); ?></h3>
+ <ol>
+ <li>
+ <p><?php echo T_('Export your bookmarks from your browser to a file'); ?>:</p>
+ <ul>
+ <li><?php echo T_('Internet Explorer: <kbd>File &gt; Import and Export... &gt; Export Favorites'); ?></kbd></li>
+ <li><?php echo T_('Mozilla Firefox: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; File &gt; Export...'); ?></kbd></li>
+ <li><?php echo T_('Netscape: <kbd>Bookmarks &gt; Manage Bookmarks... &gt; Tools &gt; Export...'); ?></kbd></li>
+ </ul>
+ </li>
+ <li><?php echo T_('Click <kbd>Browse...</kbd> to find the saved bookmark file on your computer. The maximum size the file can be is 1MB'); ?>.</li>
+ <li><?php echo T_('Select the default privacy setting for your imported bookmarks'); ?>.</li>
+ <li><?php echo T_('Click <kbd>Import</kbd> to start importing the bookmarks; it may take a minute'); ?>.</li>
+ </ol>
+</div>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/importStructure.tpl.php b/data/templates/default/importStructure.tpl.php
new file mode 100644
index 0000000..9c54612
--- /dev/null
+++ b/data/templates/default/importStructure.tpl.php
@@ -0,0 +1,42 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<div id="bookmarks">
+<form id="import" enctype="multipart/form-data"
+ action="<?php echo $formaction; ?>" method="post">
+<table>
+ <tr valign="top">
+ <th align="left"><?php echo T_('File'); ?></th>
+ <td><input type="hidden" name="MAX_FILE_SIZE" value="1024000" /> <input
+ type="file" name="userfile" size="50" /></td>
+ </tr>
+ <tr>
+ <td />
+ <td><input type="submit" value="<?php echo T_('Import'); ?>" /></td>
+ </tr>
+</table>
+</form>
+
+<h3><?php echo T_('Instructions'); ?></h3>
+<ol>
+ <li>
+ <p><?php echo T_('Create your structure into a simple text file and following this model:');?></p>
+ <ul>
+ <li>firstTagOfLevel1</li>
+ <li>&nbsp;&nbsp;&nbsp;&nbsp;firstTagOfLevel2 <i>(the line starts with two spaces)</i></li>
+ <li>&nbsp;&nbsp;&nbsp;&nbsp;secondTagOfLevel2</li>
+ <li>&nbsp;&nbsp;&nbsp;&nbsp;thirdTagOfLevel2</li>
+ <li>secondTagOfLevel1</li>
+ <li>&nbsp;&nbsp;&nbsp;&nbsp;fourthTagOfLevel2 <i>(included into secondTagOfLevel1)</i></li>
+ </ul>
+ </li>
+ <li>
+ <p><?php echo T_('Then import the file. The tags and their relations will be added to your profile.'); ?></p>
+ </li>
+</ol>
+</div>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/login.tpl.php b/data/templates/default/login.tpl.php
new file mode 100644
index 0000000..e67808d
--- /dev/null
+++ b/data/templates/default/login.tpl.php
@@ -0,0 +1,41 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementById("username").focus();
+}
+</script>
+
+<?php
+if (!$userservice->isSessionStable()) {
+ echo '<p class="error">'.T_('Please activate cookies').'</p>';
+}
+?>
+
+<form action="<?php echo $formaction; ?>" method="post">
+ <div><input type="hidden" name="query" value="<?php echo $querystring; ?>" /></div>
+ <table>
+ <tr>
+ <th align="left"><label for="username"><?php echo T_('Username'); ?></label></th>
+ <td><input type="text" id="username" name="username" size="20" /></td>
+ <td></td>
+ </tr>
+ <tr>
+ <th align="left"><label for="password"><?php echo T_('Password'); ?></label></th>
+ <td><input type="password" id="password" name="password" size="20" /></td>
+ <td><input type="checkbox" name="keeppass" id="keeppass" value="yes" /> <label for="keeppass"><?php echo T_("Don't ask for my password for 2 weeks"); ?>.</label></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><input type="submit" name="submitted" value="<?php echo T_('Log In'); ?>" /></td>
+ <td></td>
+ </tr>
+ </table>
+ <p>&raquo; <a href="<?php echo ROOT ?>password.php"><?php echo T_('Forgotten your password?') ?></a></p>
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/password.tpl.php b/data/templates/default/password.tpl.php
new file mode 100644
index 0000000..55dbed6
--- /dev/null
+++ b/data/templates/default/password.tpl.php
@@ -0,0 +1,26 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<p><?php echo sprintf(T_('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.'), $GLOBALS['sitename']); ?></p>
+
+<form action="<?php echo $formaction; ?>" method="post">
+ <table>
+ <tr>
+ <th align="left"><label for="username"><?php echo T_('Username'); ?></label></th>
+ <td><input type="text" id="username" name="username" size="20" class="required" /></td>
+ </tr>
+ <tr>
+ <th align="left"><label for="email"><?php echo T_('E-mail'); ?></label></th>
+ <td><input type="text" id="email" name="email" size="40" class="required" /></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><input type="submit" name="submitted" value="<?php echo T_('Generate Password'); ?>" /></td>
+ </tr>
+ </table>
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/profile.tpl.php b/data/templates/default/profile.tpl.php
new file mode 100644
index 0000000..1e2003a
--- /dev/null
+++ b/data/templates/default/profile.tpl.php
@@ -0,0 +1,74 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<dl id="profile">
+<dt><?php echo T_('Username'); ?></dt>
+ <dd><?php echo $user; ?></dd>
+<?php
+if ($userservice->isLoggedOn() && $currentUser->isAdmin()) {
+?>
+<dt><?php echo T_('Email'); ?></dt>
+ <dd><?php echo filter($objectUser->getEmail()) ?></dd>
+<?php
+}
+if ($objectUser->getName() != "") {
+?>
+<dt><?php echo T_('Name'); ?></dt>
+ <dd><?php echo filter($objectUser->getName()); ?></dd>
+<?php
+}
+if ($objectUser->getHomepage() != "") {
+?>
+<dt><?php echo T_('Homepage'); ?></dt>
+ <dd><a href="<?php echo filter($objectUser->getHomepage()); ?>"><?php echo filter($objectUser->getHomepage()); ?></a></dd>
+<?php
+}
+?>
+<dt><?php echo T_('Member Since'); ?></dt>
+ <dd><?php echo date($GLOBALS['longdate'], strtotime($objectUser->getDatetime())); ?></dd>
+<?php
+if ($objectUser->getContent() != "") {
+?>
+<dt><?php echo T_('Description'); ?></dt>
+ <dd><?php echo $objectUser->getContent(); ?></dd>
+<?php
+}
+$watching = $userservice->getWatchNames($userid);
+if ($watching) {
+?>
+ <dt><?php echo T_('Watching'); ?></dt>
+ <dd>
+ <?php
+ $list = '';
+ foreach($watching as $watchuser) {
+ $list .= '<a href="'. createURL('bookmarks', $watchuser) .'">'. $watchuser .'</a>, ';
+ }
+ echo substr($list, 0, -2);
+ ?>
+ </dd>
+<?php
+}
+$watchnames = $userservice->getWatchNames($userid, true);
+if ($watchnames) {
+?>
+ <dt><?php echo T_('Watched By'); ?></dt>
+ <dd>
+ <?php
+ $list = '';
+ foreach($watchnames as $watchuser) {
+ $list .= '<a href="'. createURL('bookmarks', $watchuser) .'">'. $watchuser .'</a>, ';
+ }
+ echo substr($list, 0, -2);
+ ?>
+ </dd>
+<?php
+}
+?>
+<dt><?php echo T_('Bookmarks'); ?></dt>
+ <dd><a href="<?php echo createURL('bookmarks', $user) ?>"><?php echo T_('Go to bookmarks')?> >></a></dd>
+</dl>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/register.tpl.php b/data/templates/default/register.tpl.php
new file mode 100644
index 0000000..6cfddd7
--- /dev/null
+++ b/data/templates/default/register.tpl.php
@@ -0,0 +1,54 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementById("username").focus();
+}
+</script>
+
+<p><?php echo sprintf(T_('Sign up here to create a free %s account. All the information requested below is required'), $GLOBALS['sitename']); ?>.</p>
+
+<form action="<?php echo $formaction; ?>" method="post">
+<table>
+<tr>
+ <th align="left"><label for="username"><?php echo T_('Username'); ?></label></th>
+ <td><input type="text" id="username" name="username" size="20" class="required" onkeyup="isAvailable(this, '')" /> </td>
+ <td id="availability"><?php echo 'â†'.T_(' at least 5 characters, alphanumeric (no spaces, no dots or other special ones)') ?></td>
+</tr>
+<tr>
+ <th align="left"><label for="password"><?php echo T_('Password'); ?></label></th>
+ <td><input type="password" id="password" name="password" size="20" class="required" /></td>
+ <td></td>
+</tr>
+<tr>
+ <th align="left"><label for="password2"><?php echo T_('Repeat Password'); ?></label></th>
+ <td><input type="password" id="password2" name="password2" size="20" class="required" /></td>
+ <td></td>
+</tr>
+<tr>
+ <th align="left"><label for="email"><?php echo T_('E-mail'); ?></label></th>
+ <td><input type="text" id="email" name="email" size="40" class="required" value="<?php echo htmlspecialchars(POST_MAIL); ?>" /></td>
+ <td><?php echo 'â†'.T_(' to send you your password if you forget it')?></td>
+</tr>
+
+<?php if(strlen($antispamQuestion)>0) {?>
+<tr>
+ <th align="left"><label for="antispamAnswer"><?php echo T_('Antispam question'); ?></label></th>
+ <td><input type="text" id="antispamAnswer" name="antispamAnswer" size="40" class="required" value="<?php echo $antispamQuestion; ?>" onfocus="if (this.value == '<?php echo $antispamQuestion; ?>') this.value = '';" onblur="if (this.value == '') this.value = '<?php echo $antispamQuestion; ?>';"/></td>
+ <td></td>
+</tr>
+<?php } ?>
+
+<tr>
+ <td></td>
+ <td><input type="submit" name="submitted" value="<?php echo T_('Register'); ?>" /></td>
+ <td></td>
+</tr>
+</table>
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/rss.tpl.php b/data/templates/default/rss.tpl.php
new file mode 100644
index 0000000..4aba277
--- /dev/null
+++ b/data/templates/default/rss.tpl.php
@@ -0,0 +1,30 @@
+<?php
+echo '<' . '?xml version="1.0" encoding="utf-8" ?' . ">\n";
+?>
+<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+>
+<channel>
+ <title><?php echo $feedtitle; ?></title>
+ <link><?php echo htmlspecialchars($pagelink); ?></link>
+ <atom:link rel="self" type="application/rss+xml" href="<?php echo htmlspecialchars($feedlink); ?>"/>
+ <description><?php echo htmlspecialchars($feeddescription); ?></description>
+ <pubDate><?php echo date('r'); ?></pubDate>
+ <lastBuildDate><?php echo $feedlastupdate ?></lastBuildDate>
+ <ttl>60</ttl>
+
+<?php foreach($bookmarks as $bookmark): ?>
+ <item>
+ <title><?php echo htmlspecialchars($bookmark['title']); ?></title>
+ <link><?php echo htmlspecialchars($bookmark['link']); ?></link>
+ <guid><?php echo $bookmark['guid']; ?></guid>
+ <description><?php echo htmlspecialchars($bookmark['description']); ?></description>
+ <dc:creator><?php echo htmlspecialchars($bookmark['creator']); ?></dc:creator>
+ <pubDate><?php echo $bookmark['pubdate']; ?></pubDate>
+<?php foreach($bookmark['tags'] as $tag): ?>
+ <category><?php echo htmlspecialchars($tag); ?></category>
+<?php endforeach; ?>
+ </item>
+<?php endforeach; ?>
+</channel>
+</rss>
diff --git a/data/templates/default/search.menu.php b/data/templates/default/search.menu.php
new file mode 100644
index 0000000..5a8f027
--- /dev/null
+++ b/data/templates/default/search.menu.php
@@ -0,0 +1,57 @@
+<?php
+
+
+/* Managing all possible inputs */
+$select_watchlist = isset($select_watchlist) ? $select_watchlist : '';
+$select_all = isset($select_all) ? $select_all : '';
+
+$selected = ' selected="selected"';
+if (!isset($range)) {
+ $range = '';
+}
+?>
+
+
+<form id="search" action="<?php echo createURL('search'); ?>" method="post">
+ <table>
+ <tr>
+ <?php
+ $currentUser = $currentUsername = null;
+ if ($userservice->isLoggedOn()) {
+ $currentUser = $userservice->getCurrentObjectUser();
+ $currentUsername = $currentUser->getUsername();
+ }
+ if ($userservice->isLoggedOn() || isset($user)) {
+ ?>
+
+ <td><input type="text" name="terms" size="30" value="<?php $terms=!isset($terms)?T_('Search...'):$terms; echo filter($terms); ?>" onfocus="if (this.value == '<?php echo T_('Search...') ?>') this.value = '';" onblur="if (this.value == '') this.value = '<?php echo T_('Search...') ?>';"/></td>
+ <td><?php echo T_('in') ?></td>
+ <td>
+ <select name="range">
+ <?php
+ if ($range == 'user' && $user!=$currentUsername) {
+ ?>
+ <option value="<?php echo $user ?>"><?php echo T_("this user's bookmarks"); ?></option>
+ <?php
+ }
+ if ($userservice->isLoggedOn()) {
+ ?>
+ <option value="<?php echo $currentUsername; ?>"><?php echo T_('my bookmarks'); ?></option>
+ <option value="watchlist" <?php echo ($range == 'watchlist')?$selected:''?> ><?php echo T_('my watchlist'); ?></option>
+ <?php
+ }
+ ?>
+ <option value="all" <?php echo ($range == 'all' || $range == '')?$selected:'' ?> ><?php echo T_('all bookmarks'); ?></option>
+ </select>
+ </td>
+ <?php
+ } else {
+ ?>
+ <td><input type="hidden" name="range" value="all" /></td>
+ <?php
+ }
+ ?>
+ <td><input type="submit" value="<?php echo T_('Search' /* Submit button */); ?>" /></td>
+ </tr>
+ </table>
+</form>
diff --git a/data/templates/default/sidebar.block.common.php b/data/templates/default/sidebar.block.common.php
new file mode 100644
index 0000000..dfc83ef
--- /dev/null
+++ b/data/templates/default/sidebar.block.common.php
@@ -0,0 +1,28 @@
+<?php
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$commonTags = $b2tservice->getRelatedTagsByHash($hash);
+$commonTags = $b2tservice->tagCloud($commonTags, 5, 90, 225, 'alphabet_asc');
+
+if ($commonTags && count($commonTags) > 0) {
+ ?>
+
+<h2><?php echo T_('Popular Tags'); ?></h2>
+<div id="common">
+<p class="tags"><?php
+$contents = '';
+
+if(strlen($user)==0) {
+ $cat_url = createURL('tags', '%2$s');
+}
+
+foreach ($commonTags as $row) {
+ $entries = T_ngettext('bookmark', 'bookmarks', $row['bCount']);
+ $contents .= '<a href="'. sprintf($cat_url, $user, filter($row['tag'], 'url')) .'" title="'. $row['bCount'] .' '. $entries .'" rel="tag" style="font-size:'. $row['size'] .'">'. filter($row['tag']) .'</a> ';
+}
+echo $contents ."\n";
+?></p>
+</div>
+
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.linked.php b/data/templates/default/sidebar.block.linked.php
new file mode 100644
index 0000000..d3a25a5
--- /dev/null
+++ b/data/templates/default/sidebar.block.linked.php
@@ -0,0 +1,66 @@
+<?php
+/*
+ * Used in:
+ * - populartags.php
+ * - bookmarks.php
+ * - alltags.php
+ * - tags.php
+ */
+/* Service creation: only useful services are created */
+$tag2tagservice =SemanticScuttle_Service_Factory::get('Tag2Tag');
+
+require_once('sidebar.linkedtags.inc.php');
+
+/* Manage input */
+$user = isset($user)?$user:'';
+$userid = isset($userid)?$userid:0;
+$currenttag = isset($currenttag) ? str_replace('+', ',', $currenttag) : '';
+//$summarizeLinkedTags = isset($summarizeLinkedTags)?$summarizeLinkedTags:false;
+
+$logged_on_userid = $userservice->getCurrentUserId();
+$editingMode = $logged_on_userid !== false;
+?>
+<h2><?php echo T_('Linked Tags'); ?></h2>
+<div id="related">
+<?php
+if ($editingMode) {
+ echo '<p style="margin-bottom: 13px;text-align:center;">';
+ echo ' (<a href="'. createURL('tag2tagadd','') .'" rel="tag">'.T_('Add new link').'</a>) ';
+ echo ' (<a href="'. createURL('tag2tagdelete','') .'" rel="tag">'.T_('Delete link').'</a>)';
+ echo '</p>';
+}
+?>
+ <div id="related-content"></div>
+ <script type="text/javascript">//<![CDATA[
+jQuery("#related-content")
+.jstree({
+ "themes" : {
+ "theme": "default",
+ "dots": false,
+ "icons": true,
+ "url": '<?php echo ROOT_JS ?>themes/default/style.css'
+ },
+ "json_data" : {
+ "ajax" : {
+ "url": function(node) {
+ //-1 is root
+ parentparam = "";
+ if (node == -1 ) {
+ node = <?php echo json_encode($currenttag); ?>;
+ parentparam = "&parent=true";
+ } else if (node.attr('rel')) {
+ node = node.attr('rel');
+ } else {
+ return;
+ }
+
+ return "<?php echo ROOT ?>ajax/getlinkedtags.php?tag=" + node
+ + parentparam;
+ }
+ }
+ },
+ plugins : [ "themes", "json_data"]
+});
+//]]>
+ </script>
+</div> \ No newline at end of file
diff --git a/data/templates/default/sidebar.block.menu.php b/data/templates/default/sidebar.block.menu.php
new file mode 100644
index 0000000..94a9fa2
--- /dev/null
+++ b/data/templates/default/sidebar.block.menu.php
@@ -0,0 +1,80 @@
+<?php
+/* Service creation: only useful services are created */
+$tag2tagservice =SemanticScuttle_Service_Factory::get('Tag2Tag');
+
+
+require_once('sidebar.linkedtags.inc.php');
+
+/* Manage input */
+$userid = isset($userid)?$userid:0;
+$user = isset($user)?$user:null;
+
+
+$logged_on_userid = $userservice->getCurrentUserId();
+if ($logged_on_userid === false) {
+ $logged_on_userid = NULL;
+}
+
+// editing mode
+if(($logged_on_userid != null) && ($userid === $logged_on_userid)) {
+ $editingMode = true;
+} else {
+ $editingMode = false;
+}
+
+if(!isset($user) || $user == '') {
+ $cat_url = createURL('tags', '%2$s');
+}
+
+$menuTags = $tag2tagservice->getMenuTags($userid);
+if (sizeof($menuTags) > 0 || ($userid != 0 && $userid === $logged_on_userid)) {
+?>
+
+<h2>
+<?php
+ echo '<span title="'.sprintf(T_('Tags included into the tag \'%s\''), $GLOBALS['menuTag']).'">'.T_('Menu Tags').'</span> ';
+ //}
+?>
+</h2>
+
+
+<div id="related">
+<table>
+<?php
+ if($editingMode) {
+ echo '<tr><td></td><td>';
+ echo ' (<a href="'. createURL('tag2tagadd','menu') .'" rel="tag">'.T_('Add new link').'</a>) ';
+ echo ' (<a href="'. createURL('tag2tagdelete','menu') .'" rel="tag">'.T_('Delete link').'</a>)';
+ echo '</td></tr>';
+ }
+
+ $stopList = array();
+ foreach($menuTags as $menuTag) {
+ $tag = $menuTag['tag'];
+ if(!in_array($tag, $stopList)) {
+ $displayLinkedTags = displayLinkedTags($tag, '>', $userid, $cat_url, $user, $editingMode, null, 1);
+ echo $displayLinkedTags['output'];
+ if(is_array($displayLinkedTags['stopList'])) {
+ $stopList = array_merge($stopList, $displayLinkedTags['stopList']);
+ }
+ }
+ }
+?>
+</table>
+
+<?php $cUser = $userservice->getUser($userid); ?>
+<?php if($userid>0): ?>
+<?php if($userid==$logged_on_userid): ?>
+<p style="text-align:right"><a href="<?php echo createURL('alltags', $cUser['username']); ?>" title="<?php echo T_('See all your tags')?>"><?php echo T_('all your tags'); ?></a> →</p>
+<?php else: ?>
+<p style="text-align:right"><a href="<?php echo createURL('alltags', $cUser['username']); ?>" title="<?php echo T_('See all tags from this user')?>"><?php echo T_('all tags from this user'); ?></a> →</p>
+<?php endif; ?>
+
+<?php else : ?>
+<p style="text-align:right"><a href="<?php echo createURL('populartags', $cUser['username']); ?>" title="<?php echo T_('See popular tags')?>"><?php echo T_('Popular Tags'); ?></a> →</p>
+<?php endif; ?>
+</div>
+
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.menu2.php b/data/templates/default/sidebar.block.menu2.php
new file mode 100644
index 0000000..1c177a5
--- /dev/null
+++ b/data/templates/default/sidebar.block.menu2.php
@@ -0,0 +1,71 @@
+<?php
+require_once('sidebar.linkedtags.inc.php');
+
+/* Manage input */
+$userid = isset($userid)?$userid:0;
+$user = isset($user)?$user:null;
+
+
+$logged_on_userid = $userservice->getCurrentUserId();
+if ($logged_on_userid === false) {
+ $logged_on_userid = NULL;
+}
+
+
+$cat_url = createURL('tags', '%s');
+$menu2Tags = $GLOBALS['menu2Tags'];
+
+if (count($menu2Tags) > 0) {
+?>
+
+<h2><?php echo T_('Featured Menu Tags');?></h2>
+
+
+<div id="maintagsmenu"
+<?php echo 'title="'.T_('This menu is composed of keywords (tags) organized by admins.').'"'?>>
+ <ul>
+<?php
+//this is unneeded and replaced by the ajax tree anyway. we keep it for
+// non-js browsers
+foreach ($menu2Tags as $menu2Tag) {
+ echo ' <li>'
+ . sprintf(
+ '<a href="%s">%s</a>',
+ sprintf($cat_url, $menu2Tag),
+ $menu2Tag
+ )
+ . '</li>' . "\n";
+}
+?>
+ </ul>
+</div>
+<script type="text/javascript">
+jQuery("#maintagsmenu")
+.jstree({
+ "themes" : {
+ "theme": "default",
+ "dots": false,
+ "icons": true,
+ "url": '<?php echo ROOT_JS ?>themes/default/style.css'
+ },
+ "json_data" : {
+ "ajax" : {
+ "url": function(node) {
+ //-1 is root
+ if (node == -1 ) {
+ node = "";
+ } else if (node.attr('rel')) {
+ node = node.attr('rel');
+ } else {
+ return;
+ }
+ return "<?php echo ROOT ?>ajax/getadminlinkedtags.php?tag=" + node;
+ }
+ }
+ },
+ plugins : [ "themes", "json_data"]
+});
+</script>
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.popular.php b/data/templates/default/sidebar.block.popular.php
new file mode 100644
index 0000000..842aa60
--- /dev/null
+++ b/data/templates/default/sidebar.block.popular.php
@@ -0,0 +1,44 @@
+<?php
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+
+
+if(!isset($user)) {
+ $user = '';
+}
+if(!isset($userid)) {
+ $userid = NULL;
+}
+
+$logged_on_userid = $userservice->getCurrentUserId();
+if ($logged_on_userid === false) {
+ $logged_on_userid = NULL;
+}
+$popularTags = $b2tservice->getPopularTags($userid, $popCount, $logged_on_userid);
+$popularTags = $b2tservice->tagCloud($popularTags, 5, 90, 225, 'alphabet_asc');
+
+if ($popularTags && count($popularTags) > 0) {
+?>
+
+<h2><?php echo T_('Popular Tags'); ?></h2>
+<div id="popular">
+ <p class="tags">
+ <?php
+ $contents = '';
+
+ if(strlen($user)==0) {
+ $cat_url = createURL('tags', '%2$s');
+ }
+
+ foreach ($popularTags as $row) {
+ $entries = T_ngettext('bookmark', 'bookmarks', $row['bCount']);
+ $contents .= '<a href="'. sprintf($cat_url, $user, filter($row['tag'], 'url')) .'" title="'. $row['bCount'] .' '. $entries .'" rel="tag" style="font-size:'. $row['size'] .'">'. filter($row['tag']) .'</a> ';
+ }
+ echo $contents ."\n";
+ ?>
+ </p>
+</div>
+
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.recent.php b/data/templates/default/sidebar.block.recent.php
new file mode 100644
index 0000000..80ae71c
--- /dev/null
+++ b/data/templates/default/sidebar.block.recent.php
@@ -0,0 +1,39 @@
+<?php
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+
+/* Manage input */
+$userid = isset($userid)?$userid:NULL;
+
+$logged_on_userid = $userservice->getCurrentUserId();
+if ($logged_on_userid === false) {
+ $logged_on_userid = NULL;
+}
+$recentTags = $b2tservice->getPopularTags($userid, $popCount, $logged_on_userid, $GLOBALS['defaultRecentDays']);
+$recentTags = $b2tservice->tagCloud($recentTags, 5, 90, 225, 'alphabet_asc');
+
+if ($recentTags && count($recentTags) > 0) {
+?>
+
+<h2><?php echo T_('Recent Tags'); ?></h2>
+<div id="recent">
+ <?php
+ $contents = '<p class="tags">';
+
+ if(!isset($user) || $user == '') {
+ $user = '';
+ $cat_url = createURL('tags', '%2$s');
+ }
+
+ foreach ($recentTags as $row) {
+ $entries = T_ngettext('bookmark', 'bookmarks', $row['bCount']);
+ $contents .= '<a href="'. sprintf($cat_url, $user, filter($row['tag'], 'url')) .'" title="'. $row['bCount'] .' '. $entries .'" rel="tag" style="font-size:'. $row['size'] .'">'. filter($row['tag']) .'</a> ';
+ }
+ echo $contents ."</p>\n";
+ ?>
+ <p style="text-align:right"><a href="<?php echo createURL('populartags'); ?>"><?php echo T_('Popular Tags'); ?></a> →</p>
+</div>
+
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.related.php b/data/templates/default/sidebar.block.related.php
new file mode 100644
index 0000000..60ee486
--- /dev/null
+++ b/data/templates/default/sidebar.block.related.php
@@ -0,0 +1,40 @@
+<?php
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+
+
+if(!isset($user)) $user="";
+
+
+$logged_on_userid = $userservice->getCurrentUserId();
+if ($logged_on_userid === false) {
+ $logged_on_userid = NULL;
+}
+
+if(!isset($userid)) {
+ $userid = NULL;
+}
+
+if(isset($user) && strlen($user)==0) {
+ $cat_url = createURL('tags', '%2$s');
+}
+if ($currenttag) {
+ $relatedTags = $b2tservice->getRelatedTags($currenttag, $userid, $logged_on_userid);
+ if (sizeof($relatedTags) > 0) {
+?>
+
+<h2><?php echo T_('Related Tags'); ?></h2>
+<div id="related">
+ <table>
+ <?php foreach($relatedTags as $row): ?>
+ <tr>
+ <td><a href="<?php echo sprintf($cat_url, filter($user, 'url'), filter($row['tag'], 'url')); ?>" rel="tag"><?php echo filter($row['tag']); ?></a> <a href="<?php echo sprintf($cat_url, filter($user, 'url'), filter($currenttag, 'url') .'+'. filter($row['tag'], 'url')); ?>" title="<?php echo $currenttag . '+' . filter($row['tag']); ?>" class="crossTags">+</a></td>
+ </tr>
+ <?php endforeach; ?>
+ </table>
+</div>
+
+<?php
+ }
+}
+?>
diff --git a/data/templates/default/sidebar.block.search.php b/data/templates/default/sidebar.block.search.php
new file mode 100644
index 0000000..d3cd8a5
--- /dev/null
+++ b/data/templates/default/sidebar.block.search.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Show a list of the last searches.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @subcategory Templates
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+/* Service creation: only useful services are created */
+$searchhistoryservice = SemanticScuttle_Service_Factory::get('SearchHistory');
+
+$lastSearches = $searchhistoryservice->getAllSearches(
+ 'all', NULL, 3, NULL, true, false
+);
+
+if ($lastSearches && count($lastSearches) > 0) {
+?>
+
+<h2><?php echo T_('Last Searches'); ?></h2>
+<div id="searches">
+ <table>
+<?php
+foreach ($lastSearches as $row) {
+ echo ' <tr><td>';
+ echo '<a href="'
+ . htmlspecialchars(createURL('search', $range.'/'.$row['shTerms']))
+ . '">';
+ echo htmlspecialchars($row['shTerms']);
+ echo '</a>';
+ echo ' <span title="'
+ . T_('Number of bookmarks for this query')
+ . '">(' . $row['shNbResults'] . ')</span>';
+ echo '</td></tr>' . "\n";
+}
+//echo '<tr><td><a href="'.createURL('users').'">...</a></td></tr>';
+?>
+
+ </table>
+</div>
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.tagactions.php b/data/templates/default/sidebar.block.tagactions.php
new file mode 100644
index 0000000..3351866
--- /dev/null
+++ b/data/templates/default/sidebar.block.tagactions.php
@@ -0,0 +1,35 @@
+<?php
+/* Service creation: only useful services are created */
+//No specific services
+
+if ($userservice->isLoggedOn()) {
+
+ if ($currentUser->getUsername() == $user) {
+ $tags = explode('+', $currenttag);
+ $renametext = T_ngettext('Rename Tag', 'Rename Tags', count($tags));
+ $renamelink = createURL('tagrename', $currenttag);
+ $deletelink = createURL('tagdelete', $currenttag);
+ $tagdesclink = createURL('tagedit', $currenttag);
+ $commondesclink = createURL('tagcommondescriptionedit', $currenttag);
+ $addtag2taglinklink = createURL('tag2tagadd', $currenttag);
+?>
+
+<h2><?php echo T_('Actions'); ?></h2>
+<div id="tagactions">
+ <ul>
+ <li><a href="<?php echo $renamelink; ?>"><?php echo $renametext ?></a></li>
+ <?php if (count($tags) == 1): ?>
+ <li><a href="<?php echo $deletelink; ?>"><?php echo T_('Delete Tag') ?></a></li>
+ <?php endif; ?>
+ <li><a href="<?php echo $tagdesclink; ?>"><?php echo T_('Edit Tag Description') ?></a></li>
+ <?php if ($GLOBALS['enableCommonTagDescription'] && ($GLOBALS['enableCommonTagDescriptionEditedByAll'] || $currentUser->isAdmin() )): ?>
+ <li><a href="<?php echo $commondesclink; ?>"><?php echo T_('Edit Tag Common Description') ?></a></li>
+ <?php endif; ?>
+ <li><a href="<?php echo $addtag2taglinklink; ?>"><?php echo T_('Create a link to another tag') ?></a></li>
+ </ul>
+</div>
+
+<?php
+ }
+}
+?>
diff --git a/data/templates/default/sidebar.block.users.php b/data/templates/default/sidebar.block.users.php
new file mode 100644
index 0000000..58fdfb7
--- /dev/null
+++ b/data/templates/default/sidebar.block.users.php
@@ -0,0 +1,34 @@
+<?php
+/* Service creation: only useful services are created */
+//No specific services
+
+$logged_on_userid = $userservice->getCurrentUserId();
+if ($logged_on_userid === false) {
+ $logged_on_userid = NULL;
+}
+$lastUsers = $userservice->getUsers(3);
+
+if ($lastUsers && count($lastUsers) > 0) {
+?>
+
+<h2><?php echo T_('New Users'); ?></h2>
+<div id="users">
+<table>
+<?php
+foreach ($lastUsers as $row) {
+ echo '<tr><td>';
+ echo '<a href="'.createURL('profile', $row['username']).'">';
+ echo SemanticScuttle_Model_UserArray::getName($row);
+ echo '</a>';
+ echo ' (<a href="'.createURL('bookmarks', $row['username']).'">'.T_('bookmarks').'</a>)';
+ echo '</td></tr>';
+}
+//echo '<tr><td><a href="'.createURL('users').'">...</a></td></tr>';
+?>
+
+</table>
+<p style="text-align:right"><a href="<?php echo createURL('users'); ?>" title="<?php echo T_('See all users')?>"><?php echo T_('All users'); ?></a> →</p>
+</div>
+<?php
+}
+?>
diff --git a/data/templates/default/sidebar.block.watchlist.php b/data/templates/default/sidebar.block.watchlist.php
new file mode 100644
index 0000000..3af9c5a
--- /dev/null
+++ b/data/templates/default/sidebar.block.watchlist.php
@@ -0,0 +1,60 @@
+<?php
+/* Service creation: only useful services are created */
+//No specific services
+
+$watching = $userservice->getWatchNames($userid);
+$watchedBy = $userservice->getWatchNames($userid, true);
+
+
+$closeContacts = array(); // people in my contacts list and who I am also in the contacts list
+foreach($watching as $watchuser) {
+ if(in_array($watchuser, $watchedBy)) {
+ $closeContacts[] = $watchuser;
+ }
+}
+
+?>
+
+<?php if(count($closeContacts)>0):?>
+<h2 title="<?php echo T_('Close contacts are mutual contacts');?>"><?php echo ' ↔ '. T_('Close contacts'); ?></h2>
+<div id="watching">
+ <ul>
+ <?php foreach($closeContacts as $watchuser): ?>
+ <li><a href="<?php echo createURL('bookmarks', $watchuser); ?>"><?php echo $watchuser; ?></a> </li>
+ <?php endforeach; ?>
+ </ul>
+</div>
+<?php endif; ?>
+
+
+<h2><?php echo ' → '. T_('Watching'); ?></h2>
+<div id="watching">
+ <ul>
+ <?php if($userservice->isLoggedOn() && $currentUser->getUsername() == $user): ?>
+ <li>
+ <form action="<?php echo createURL('watch', '');?>" method="post">
+ <input type="text" name="contact" value="<?php echo T_('Add a contact...');?>" onfocus="if (this.value == '<?php echo T_('Add a contact...');?>') this.value = '';" onblur="if (this.value == '') this.value = '<?php echo T_('Add a contact...');?>';" title="<?php echo T_('Type a username to add it to your contacts.') ?>" />
+ </form>
+ </li>
+ <?php endif; ?>
+
+ <?php foreach($watching as $watchuser): ?>
+ <li><a href="<?php echo createURL('bookmarks', $watchuser); ?>"><?php echo $watchuser; ?></a>
+ <?php if($userservice->isLoggedOn() && $currentUser->getUsername() == $user): ?>
+ - <a href="<?php echo createUrl('watch','?contact='.$watchuser); ?>" title="<?php echo T_('Remove this contact'); ?>">x</a>
+ </li>
+ <?php endif; ?>
+ <?php endforeach; ?>
+
+ </ul>
+</div>
+
+<h2><?php echo ' ↠'. T_('Watched By'); ?></h2>
+<div id="watching">
+ <ul>
+ <?php foreach($watchedBy as $watchuser): ?>
+ <li><a href="<?php echo createURL('bookmarks', $watchuser); ?>"><?php echo $watchuser; ?></a> </li>
+ <?php endforeach; ?>
+ </ul>
+
+</div>
diff --git a/data/templates/default/sidebar.block.watchstatus.php b/data/templates/default/sidebar.block.watchstatus.php
new file mode 100644
index 0000000..99574aa
--- /dev/null
+++ b/data/templates/default/sidebar.block.watchstatus.php
@@ -0,0 +1,28 @@
+<?php
+/* Service creation: only useful services are created */
+//No specific services
+
+
+if ($userservice->isLoggedOn()) {
+
+ if ($currentUser->getUsername() != $user) {
+ $result = $userservice->getWatchStatus($userid, $userservice->getCurrentUserId());
+ if ($result) {
+ $linkText = T_('Remove from Watchlist');
+ } else {
+ $linkText = T_('Add to Watchlist');
+ }
+ $linkAddress = createURL('watch', $user);
+?>
+
+<h2><?php echo T_('Actions'); ?></h2>
+<div id="watchlist">
+ <ul>
+ <li><a href="<?php echo $linkAddress ?>"><?php echo $linkText ?></a></li>
+ </ul>
+</div>
+
+<?php
+ }
+}
+?> \ No newline at end of file
diff --git a/data/templates/default/sidebar.linkedtags.inc.php b/data/templates/default/sidebar.linkedtags.inc.php
new file mode 100644
index 0000000..020d0f0
--- /dev/null
+++ b/data/templates/default/sidebar.linkedtags.inc.php
@@ -0,0 +1,84 @@
+<?php
+/*
+To be inserted into blocks where structured tags must be displayed in a tree format.
+*/
+
+function displayLinkedTags($tag, $linkType, $uId, $cat_url, $user, $editingMode =false, $precedentTag =null, $level=0, $stopList=array()) {
+
+ if(in_array($tag, $stopList)) {
+ return array('output' => '', 'stoplist' => $stopList);
+ }
+
+ $tag2tagservice =SemanticScuttle_Service_Factory::get('Tag2Tag');
+ $tagstatservice =SemanticScuttle_Service_Factory::get('TagStat');
+
+ // link '>'
+ if($level>1) {
+ if($editingMode) {
+ $link = '<small><a href="'.createURL('tag2tagedit', $precedentTag.'/'.$tag).'" title="'._('Edit link').'">></a> </small>';
+ } else {
+ $link = '> ';
+ }
+ } else {
+ $link = '';
+ }
+
+ $output = '';
+ $output.= '<tr>';
+ $output.= '<td></td>';
+ $output.= '<td>';
+ $output.= $level == 1?'<b>':'';
+ $output.= str_repeat('&nbsp;', $level*2) .$link.'<a href="'. sprintf($cat_url, filter($user, 'url'), filter($tag, 'url')) .'" rel="tag">'. filter($tag) .'</a>';
+ $output.= $level == 1?'</b>':'';
+ //$output.= ' - '. $tagstatservice->getMaxDepth($tag, $linkType, $uId);
+
+ $synonymTags = $tag2tagservice->getAllLinkedTags($tag, '=', $uId);
+ $synonymTags = is_array($synonymTags)?$synonymTags:array($synonymTags);
+ sort($synonymTags);
+ $synonymList = '';
+ foreach($synonymTags as $synonymTag) {
+ //$output.= ", ".$synonymTag;
+ $synonymList.= $synonymTag.' ';
+ }
+ if(count($synonymTags)>0) {
+ $output.= ', '.$synonymTags[0];
+ }
+ if(count($synonymTags)>1) {
+ $output.= '<span title="'.T_('Synonyms:').' '.$synonymList.'">, etc</span>';
+ }
+
+ /*if($editingMode) {
+ $output.= ' (';
+ $output.= '<a href="'.createURL('tag2tagadd', $tag).'" title="'._('Add a subtag').'">+</a>';
+ if(1) {
+ $output.= ' - ';
+ $output.= '<a href="'.createURL('tag2tagdelete', $tag).'">-</a>';
+ }
+ $output.= ')';
+ }*/
+ $output.= '</td>';
+ $output.= '</tr>';
+
+ $tags = array($tag);
+ $tags = array_merge($tags, $synonymTags);
+ foreach($tags as $tag) {
+
+ if(!in_array($tag, $stopList)) {
+ $linkedTags = $tag2tagservice->getLinkedTags($tag, '>', $uId);
+ $precedentTag = $tag;
+ $stopList[] = $tag;
+ foreach($linkedTags as $linkedTag) {
+ $displayLinkedTags = displayLinkedTags($linkedTag, $linkType, $uId, $cat_url, $user, $editingMode, $precedentTag, $level + 1, $stopList);
+ $output.= $displayLinkedTags['output'];
+ }
+ if(isset($displayLinkedTags) && is_array($displayLinkedTags['stopList'])) {
+ $stopList = array_merge($stopList, $displayLinkedTags['stopList']);
+ $stopList = array_unique($stopList);
+ }
+ }
+
+ }
+ return array('output' => $output, 'stopList' => $stopList);
+}
+
+?>
diff --git a/data/templates/default/sidebar.tpl.php b/data/templates/default/sidebar.tpl.php
new file mode 100644
index 0000000..b4fac96
--- /dev/null
+++ b/data/templates/default/sidebar.tpl.php
@@ -0,0 +1,23 @@
+<?php if ($GLOBALS['enableAdminColors'] != false
+ && isset($userid) && $userservice->isAdmin($userid)
+): ?>
+<div id="sidebar" class="adminBackground">
+<?php else: ?>
+<div id="sidebar">
+<?php endif ?>
+
+
+<?php
+echo $GLOBALS['sidebarTopMessage'].' ';
+
+if (isset($sidebar_blocks) && count($sidebar_blocks)) {
+ $size = count($sidebar_blocks);
+ for ($i = 0; $i < $size; $i++) {
+ $this->includeTemplate('sidebar.block.'. $sidebar_blocks[$i]);
+ }
+}
+
+echo $GLOBALS['sidebarBottomMessage'];
+?>
+
+</div>
diff --git a/data/templates/default/tag2tagadd.tpl.php b/data/templates/default/tag2tagadd.tpl.php
new file mode 100644
index 0000000..9482007
--- /dev/null
+++ b/data/templates/default/tag2tagadd.tpl.php
@@ -0,0 +1,57 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<form action="<?php echo $formaction; ?>" method="post">
+
+<p align="right" style="float:right">
+<small style="text-align:right"><?php echo T_('Note: use "=" to make synonym two tags. e.g.: france=frenchcountry')?></small><br/>
+<small style="text-align:right"><?php echo T_('Note: use ">" to include one tag in another. e.g.: europe>france>paris')?></small><br/>
+</p>
+
+<p><?php echo T_('Create new link:')?></p>
+<p>
+<input type="text" name="tag1" value="<?php echo $tag1 ?>"/>
+<input type="text" name="linkType" value=">" size="1" maxlength="1"/>
+<input type="text" name="tag2" />
+</p>
+<p>
+<small style="text-align:right"><?php echo sprintf(T_('Note: include a tag into \'%s\' tag (e.g. %s>countries) display the tag into the menu box'), $GLOBALS['menuTag'], $GLOBALS['menuTag'])?></small>
+</p>
+<!--p><?php echo T_('Are you sure?'); ?></p-->
+<p>
+ <input type="submit" name="confirm" value="<?php echo T_('Create'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('Cancel'); ?>" />
+</p>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+
+</form>
+
+<?php
+if(count($links)>0) {
+echo T_("Existing links:");
+foreach($links as $link) {
+ echo '<span style="white-space:nowrap;margin-left:25px;">';
+ if($link['tag1'] == $tag1 || $link['tag1'] == $tag2) {
+ $textTag1 = '<b>'.$tag1.'</b>';
+ } else {
+ $textTag1 = $link['tag1'];
+ }
+ if($link['tag2'] == $tag1 || $link['tag2'] == $tag2) {
+ $textTag2 = '<b>'.$tag2.'</b>';
+ } else {
+ $textTag2 = $link['tag2'];
+ }
+
+ echo $textTag1.' '.$link['relationType'].' '.$textTag2;
+ echo "</span> ";
+}
+} else {
+ echo T_('No links');
+}
+
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/tag2tagdelete.tpl.php b/data/templates/default/tag2tagdelete.tpl.php
new file mode 100644
index 0000000..8018374
--- /dev/null
+++ b/data/templates/default/tag2tagdelete.tpl.php
@@ -0,0 +1,49 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<form action="<?php echo $formaction; ?>" method="post">
+<!--input type="hidden" name="tag1" value="<?php echo $tag1 ?>" />
+<input type="hidden" name="tag2" value="<?php echo $tag2 ?>" /-->
+<p>
+<input type="text" name="tag1" value="<?php echo $tag1 ?>"/>
+<input type="text" name="linkType" value=">" size="1" maxlength="1"/>
+<input type="text" name="tag2" value="<?php echo $tag2 ?>"/>
+</p>
+<p><?php echo T_('Are you sure?'); ?></p>
+<p>
+ <input type="submit" name="confirm" value="<?php echo T_('Yes'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('No'); ?>" />
+</p>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+
+</form>
+
+<?php
+if(count($links)>0) {
+echo T_("Existing links:");
+foreach($links as $link) {
+ echo '<span style="white-space:nowrap;margin-left:25px;">';
+ if($link['tag1'] == $tag1 || $link['tag1'] == $tag2) {
+ $textTag1 = '<b>'.$tag1.'</b>';
+ } else {
+ $textTag1 = $link['tag1'];
+ }
+ if($link['tag2'] == $tag1 || $link['tag2'] == $tag2) {
+ $textTag2 = '<b>'.$tag2.'</b>';
+ } else {
+ $textTag2 = $link['tag2'];
+ }
+
+ echo $textTag1.' '.$link['relationType'].' '.$textTag2;
+ echo "</span> ";
+}
+} else {
+ echo T_('No links');
+}
+
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/tag2tagedit.tpl.php b/data/templates/default/tag2tagedit.tpl.php
new file mode 100644
index 0000000..4745ad2
--- /dev/null
+++ b/data/templates/default/tag2tagedit.tpl.php
@@ -0,0 +1,73 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<form action="<?php echo $formdeleteaction; ?>" method="post">
+<p><?php echo T_('Delete the link')?></p>
+<!--input type="hidden" name="tag1" value="<?php echo $tag1 ?>" />
+<input type="hidden" name="tag2" value="<?php echo $tag2 ?>" /-->
+<p>
+<input type="text" name="tag1" value="<?php echo $tag1 ?>"/>
+<input type="text" name="linkType" value=">" size="1" maxlength="1"/>
+<input type="text" name="tag2" value="<?php echo $tag2 ?>"/>
+</p>
+<p><?php echo T_('Are you sure?'); ?></p>
+<p>
+ <input type="submit" name="confirm" value="<?php echo T_('Yes'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('No'); ?>" />
+</p>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+
+</form>
+
+<hr />
+
+<form action="<?php echo $formaddaction; ?>" method="post">
+<p><?php echo T_('Create new link')?></p>
+<p>
+<input type="text" name="tag1" value="<?php echo $tag1 ?>"/>
+<input type="text" name="linkType" value=">" size="1" maxlength="1"/>
+<input type="text" name="tag2" />
+</p>
+<!--p><?php echo T_('Are you sure?'); ?></p-->
+<p>
+ <input type="submit" name="confirm" value="<?php echo T_('Create'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('Cancel'); ?>" />
+</p>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+
+</form>
+
+<hr />
+
+<?php
+if(count($links)>0) {
+echo T_("Existing links:");
+foreach($links as $link) {
+ echo '<span style="white-space:nowrap;margin-left:25px;">';
+ if($link['tag1'] == $tag1 || $link['tag1'] == $tag2) {
+ $textTag1 = '<b>'.$tag1.'</b>';
+ } else {
+ $textTag1 = $link['tag1'];
+ }
+ if($link['tag2'] == $tag1 || $link['tag2'] == $tag2) {
+ $textTag2 = '<b>'.$tag2.'</b>';
+ } else {
+ $textTag2 = $link['tag2'];
+ }
+
+ echo $textTag1.' '.$link['relationType'].' '.$textTag2;
+ echo "</span> ";
+}
+} else {
+ echo T_('No links');
+}
+
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/tagcommondescriptionedit.tpl.php b/data/templates/default/tagcommondescriptionedit.tpl.php
new file mode 100644
index 0000000..207cfd2
--- /dev/null
+++ b/data/templates/default/tagcommondescriptionedit.tpl.php
@@ -0,0 +1,48 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+
+?>
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementById("description").focus();
+}
+</script>
+<form action="<?php echo $formaction; ?>" method="post">
+<table>
+<tr>
+ <th align="left"><?php echo T_('Description'); ?></th>
+ <td><textarea name="description" cols="75" rows="10"><?php echo $description['cdDescription']; ?></textarea></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <?php
+ if(strlen($description['cdDatetime'])>0) {
+ echo T_('Last modification:').' '.$description['cdDatetime'].', ';
+ $lastUser = $userservice->getUser($description['uId']);
+ echo '<a href="' . createURL('profile', $lastUser['username']) . '">'
+ . SemanticScuttle_Model_UserArray::getName($lastUser) . '</a>';
+ }
+ ?>
+ </td>
+ <td></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <input type="submit" name="confirm" value="<?php echo T_('Update'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('Cancel'); ?>" />
+ </td>
+ <td></td>
+</tr>
+</table>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+<div><input type="hidden" name="tag" value="<?php echo $tag; ?>" /></div>
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/tagdelete.tpl.php b/data/templates/default/tagdelete.tpl.php
new file mode 100644
index 0000000..e787ff5
--- /dev/null
+++ b/data/templates/default/tagdelete.tpl.php
@@ -0,0 +1,20 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<form action="<?php echo $formaction; ?>" method="post">
+<p><?php echo T_('Are you sure?'); ?></p>
+<p>
+ <input type="submit" name="confirm" value="<?php echo T_('Yes'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('No'); ?>" />
+</p>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/tagedit.tpl.php b/data/templates/default/tagedit.tpl.php
new file mode 100644
index 0000000..860c933
--- /dev/null
+++ b/data/templates/default/tagedit.tpl.php
@@ -0,0 +1,33 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementById("description").focus();
+}
+</script>
+<form action="<?php echo $formaction; ?>" method="post">
+<table>
+<tr>
+ <th align="left"><?php echo T_('Description'); ?></th>
+ <td><textarea name="description" cols="75" rows="10"><?php echo $description['tDescription']; ?></textarea></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <input type="submit" name="confirm" value="<?php echo T_('Update'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('Cancel'); ?>" />
+ </td>
+ <td></td>
+</tr>
+</table>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+<div><input type="hidden" name="tag" value="<?php echo $tag; ?>" /></div>
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/tagrename.tpl.php b/data/templates/default/tagrename.tpl.php
new file mode 100644
index 0000000..e3d0467
--- /dev/null
+++ b/data/templates/default/tagrename.tpl.php
@@ -0,0 +1,40 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementById("new").focus();
+}
+</script>
+<form action="<?php echo $formaction; ?>" method="post">
+<table>
+<tr>
+ <th align="left"><?php echo T_('Old'); ?></th>
+ <td><input type="text" name="old" id="old" value="<?php echo $old; ?>" /></td>
+ <td>↠<?php echo T_('Required'); ?></td>
+</tr>
+<tr>
+ <th align="left"><?php echo T_('New'); ?></th>
+ <td><input type="text" name="new" id="new" value="" /></td>
+ <td>↠<?php echo T_('Required'); ?></td>
+</tr>
+<tr>
+ <td></td>
+ <td>
+ <input type="submit" name="confirm" value="<?php echo T_('Rename'); ?>" />
+ <input type="submit" name="cancel" value="<?php echo T_('Cancel'); ?>" />
+ </td>
+ <td></td>
+</tr>
+
+</table>
+
+<?php if (isset($referrer)): ?>
+<div><input type="hidden" name="referrer" value="<?php echo $referrer; ?>" /></div>
+<?php endif; ?>
+
+</form>
+
+<?php
+$this->includeTemplate($GLOBALS['bottom_include']);
+?> \ No newline at end of file
diff --git a/data/templates/default/tags.tpl.php b/data/templates/default/tags.tpl.php
new file mode 100644
index 0000000..d6259cc
--- /dev/null
+++ b/data/templates/default/tags.tpl.php
@@ -0,0 +1,31 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+?>
+
+<?php
+if ($tags && count($tags) > 0) {
+?>
+
+<p id="sort">
+ <?php echo T_("Sort by:"); ?>
+ <a href="?sort=alphabet_asc"><?php echo T_("Alphabet"); ?></a><span> / </span>
+ <a href="?sort=popularity_asc"><?php echo T_("Popularity"); ?></a>
+</p>
+<p class="tags">
+
+<?php
+$contents = '';
+foreach ($tags as $row) {
+ $entries = T_ngettext('bookmark', 'bookmarks', $row['bCount']);
+ $contents .= '<a href="'. sprintf($cat_url, $user, filter($row['tag'], 'url')) .'" title="'. $row['bCount'] .' '. $entries .'" rel="tag" style="font-size:'. $row['size'] .'">'. filter($row['tag']) .'</a> ';
+}
+echo $contents ."\n";
+?>
+
+</p>
+
+<?php
+}
+$this->includeTemplate('sidebar.tpl');
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/default/toolbar.inc.php b/data/templates/default/toolbar.inc.php
new file mode 100644
index 0000000..fb6638d
--- /dev/null
+++ b/data/templates/default/toolbar.inc.php
@@ -0,0 +1,37 @@
+<?php
+if ($userservice->isLoggedOn() && is_object($currentUser)) {
+ $cUserId = $userservice->getCurrentUserId();
+ $cUsername = $currentUser->getUsername();
+?>
+
+ <ul id="navigation">
+ <li><a href="<?php echo createURL(''); ?>"><?php echo T_('Home'); ?></a></li>
+ <li><a href="<?php echo createURL('bookmarks', $cUsername); ?>"><?php echo T_('Bookmarks'); ?></a></li>
+ <li><a href="<?php echo createURL('alltags', $cUsername); ?>"><?php echo T_('Tags'); ?></a></li>
+ <li><a href="<?php echo createURL('watchlist', $cUsername); ?>"><?php echo T_('Watchlist'); ?></a></li>
+ <li><a href="<?php echo $userservice->getProfileUrl($cUserId, $cUsername); ?>"><?php echo T_('Profile'); ?></a></li>
+ <li><a href="<?php echo createURL('bookmarks', $cUsername . '?action=add'); ?>"><?php echo T_('Add a Bookmark'); ?></a></li>
+ <li class="access"><?php echo $cUsername?><a href="<?php echo ROOT ?>?action=logout">(<?php echo T_('Log Out'); ?>)</a></li>
+ <li><a href="<?php echo createURL('about'); ?>"><?php echo T_('About'); ?></a></li>
+ <?php if($currentUser->isAdmin()): ?>
+ <li><a href="<?php echo createURL('admin', ''); ?>"><?php echo '['.T_('Admin').']'; ?></a></li>
+ <?php endif; ?>
+
+ </ul>
+
+<?php
+} else {
+?>
+ <ul id="navigation">
+ <li><a href="<?php echo createURL(''); ?>"><?php echo T_('Home'); ?></a></li>
+ <li><a href="<?php echo createURL('populartags'); ?>"><?php echo T_('Popular Tags'); ?></a></li>
+ <li><a href="<?php echo createURL('about'); ?>"><?php echo T_('About'); ?></a></li>
+ <li class="access"><a href="<?php echo createURL('login'); ?>"><?php echo T_('Log In'); ?></a></li>
+ <?php if ($GLOBALS['enableRegistration']) { ?>
+ <li class="access"><a href="<?php echo createURL('register'); ?>"><?php echo T_('Register'); ?></a></li>
+ <?php } ?>
+ </ul>
+
+<?php
+}
+?>
diff --git a/data/templates/default/top.inc.php b/data/templates/default/top.inc.php
new file mode 100644
index 0000000..79cc2e2
--- /dev/null
+++ b/data/templates/default/top.inc.php
@@ -0,0 +1,83 @@
+<?php echo '<'; ?>?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <title><?php echo filter($GLOBALS['sitename'] .(isset($pagetitle) ? ' » ' . $pagetitle : '')); ?></title>
+ <link rel="icon" type="image/png" href="<?php echo $theme->resource('icon.png');?>" />
+ <link rel="stylesheet" type="text/css" href="<?php echo $theme->resource('scuttle.css');?>" />
+ <link rel="search" type="application/opensearchdescription+xml" href="<?php echo ROOT ?>api/opensearch.php" title="<?php echo htmlspecialchars($GLOBALS['sitename']) ?>"/>
+<?php
+if (isset($rsschannels)) {
+ $size = count($rsschannels);
+ for ($i = 0; $i < $size; $i++) {
+ echo ' <link rel="alternate" type="application/rss+xml" title="'
+ . htmlspecialchars($rsschannels[$i][0]) . '"'
+ . ' href="'. htmlspecialchars($rsschannels[$i][1]) .'" />' . "\n";
+ }
+}
+?>
+
+<?php if (isset($loadjs)) :?>
+<?php if (DEBUG_MODE) : ?>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery-1.4.2.js"></script>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery.jstree.js"></script>
+<?php else: ?>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery.jstree.min.js"></script>
+<?php endif ?>
+ <script type="text/javascript" src="<?php echo ROOT ?>jsScuttle.php"></script>
+<?php endif ?>
+
+ </head>
+<?php
+$bodystyle = '';
+if (isset($_GET['popup'])) {
+ if (isset($_GET['height'])) {
+ $bodystyle .= 'height:' . intval($_GET['height']) . 'px;';
+ }
+ if (isset($_GET['width'])) {
+ $bodystyle .= 'width:' . intval($_GET['width']) . 'px;';
+ }
+ if ($bodystyle != '') {
+ $bodystyle = ' style="' . $bodystyle . '"';
+ }
+}
+?>
+ <body<?php echo $bodystyle; ?>>
+
+<?php
+$headerstyle = '';
+if (isset($_GET['popup'])) {
+ $headerstyle = ' class="popup"';
+}
+?>
+
+<div id="header" <?php echo $headerstyle; ?>>
+<h1><a href="<?php echo ROOT ?>"><?php echo $GLOBALS['sitename']; ?></a></h1>
+<?php
+if(!isset($_GET['popup'])) {
+ $this->includeTemplate('toolbar.inc');
+}
+?></div>
+
+<?php
+if (isset($subtitlehtml)) {
+ echo '<h2>' . $subtitlehtml . "</h2>\n";
+} else if (isset($subtitle)) {
+ echo '<h2>' . htmlspecialchars($subtitle) . "</h2>\n";
+}
+if(DEBUG_MODE) {
+ echo '<p class="error">'. T_('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.') ."</p>\n";
+}
+if (isset($error) && $error!='') {
+ echo '<p class="error">'. $error ."</p>\n";
+}
+if (isset($msg) && $msg!='') {
+ echo '<p class="success">'. $msg ."</p>\n";
+}
+if (isset($tipMsg) && $tipMsg!='') {
+ echo '<p class="tipMsg">'. $tipMsg ."</p>\n";
+}
+?>
diff --git a/data/templates/default/users.tpl.php b/data/templates/default/users.tpl.php
new file mode 100644
index 0000000..fa92bef
--- /dev/null
+++ b/data/templates/default/users.tpl.php
@@ -0,0 +1,33 @@
+<?php
+$this->includeTemplate($GLOBALS['top_include']);
+
+if ($users && count($users) > 0) {
+?>
+
+<!--p id="sort">
+ <?php echo T_("Sort by:"); ?>
+ <a href="?sort=alphabet_asc"><?php echo T_("Alphabet"); ?></a><span> / </span>
+ <a href="?sort=popularity_asc"><?php echo T_("Popularity"); ?></a>
+</p-->
+<p class="users">
+<ul>
+<?php
+$contents = '<';
+foreach ($users as $row) {
+ echo '<li><strong>'
+ . SemanticScuttle_Model_UserArray::getName($row) . '</strong>'
+ . ' (<a href="' . createURL('profile', $row['username']) . '">'
+ . T_('profile') . '</a> '
+ . T_('created in') . ' '
+ . date('M Y', strtotime($row['uDatetime'])) . ')'
+ . ' : <a href="' . createURL('bookmarks', $row['username']).'">'
+ . T_('bookmarks') . '</a></li>';
+}
+?>
+</ul>
+</p>
+
+<?php
+}
+$this->includeTemplate($GLOBALS['bottom_include']);
+?>
diff --git a/data/templates/testdummy/top.inc.php b/data/templates/testdummy/top.inc.php
new file mode 100644
index 0000000..b85a1ae
--- /dev/null
+++ b/data/templates/testdummy/top.inc.php
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
+ <title><?php echo filter($GLOBALS['sitename'] .(isset($pagetitle) ? ' » ' . $pagetitle : '')); ?></title>
+ <link rel="icon" type="image/png" href="<?php echo $theme->resource('icon.png');?>" />
+ <link rel="stylesheet" type="text/css" href="<?php echo $theme->resource('scuttle.css');?>" />
+ <link rel="search" type="application/opensearchdescription+xml" href="<?php echo ROOT ?>api/opensearch.php" title="<?php echo htmlspecialchars($GLOBALS['sitename']) ?>"/>
+<?php
+if (isset($rsschannels)) {
+ $size = count($rsschannels);
+ for ($i = 0; $i < $size; $i++) {
+ echo ' <link rel="alternate" type="application/rss+xml" title="'
+ . $rsschannels[$i][0] . '"'
+ . ' href="'. $rsschannels[$i][1] .'" />';
+ }
+}
+?>
+
+<?php if (isset($loadjs)) :?>
+<?php if (DEBUG_MODE) : ?>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery-1.4.2.js"></script>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery.jstree.js"></script>
+<?php else: ?>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="<?php echo ROOT_JS ?>jquery.jstree.min.js"></script>
+<?php endif ?>
+ <script type="text/javascript" src="<?php echo ROOT ?>jsScuttle.php"></script>
+<?php endif ?>
+
+ </head>
+ <body>
+
+<?php
+$headerstyle = '';
+if(isset($_GET['popup'])) {
+ $headerstyle = ' class="popup"';
+}
+?>
+
+<div id="header" <?php echo $headerstyle; ?>>
+<h1><a href="<?php echo ROOT ?>">.</a></h1>
+<?php
+if(!isset($_GET['popup'])) {
+ $this->includeTemplate('toolbar.inc');
+}
+?></div>
+
+<?php
+if (isset($subtitle)) {
+ echo '<h2>'. $subtitle ."</h2>\n";
+}
+if(DEBUG_MODE) {
+ echo '<p class="error">'. T_('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.') ."</p>\n";
+}
+if (isset($error) && $error!='') {
+ echo '<p class="error">'. $error ."</p>\n";
+}
+if (isset($msg) && $msg!='') {
+ echo '<p class="success">'. $msg ."</p>\n";
+}
+if (isset($tipMsg) && $tipMsg!='') {
+ echo '<p class="tipMsg">'. $tipMsg ."</p>\n";
+}
+?>
diff --git a/doc/AUTHORS b/doc/AUTHORS
new file mode 100644
index 0000000..633c49c
--- /dev/null
+++ b/doc/AUTHORS
@@ -0,0 +1,37 @@
+SemanticScuttle developers:
+ Eric Dane <ericdane@users.sourceforge.net>
+ Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ Christian Weiske <cweiske@cweiske.de>
+
+
+SemanticScuttle is a fork of Scuttle.
+ http://sourceforge.net/projects/scuttle/
+
+Scuttle and SemanticScuttle contain code from the following applications:
+
+------------
+GPL Licenced
+------------
+
+phpBB2 (database abstraction layer)
+ http://www.phpbb.com/
+
+php-gettext
+ Danilo Segan <danilo@kvota.net>
+ http://savannah.nongnu.org/projects/php-gettext/
+
+UTF8 Helper Functions
+ Andreas Gohr <andi@splitbrain.org>
+
+XSPF Web Music Player (Flash)
+ http://musicplayer.sourceforge.net/
+
+
+----
+
+Patches were realised by :
+
+* Simple Admin-Interface for deleting unwanted users
+ Idea of the patch from Andreas Jaggi, for details visit:
+ http://sourceforge.net/tracker/index.php?func=detail&aid=1543065&group_id=134378&atid=729862
+ Adaptation by Andreas Keller (aka Arakel) http://akeller.eu for Takuya Misawa.
diff --git a/doc/ChangeLog b/doc/ChangeLog
new file mode 100644
index 0000000..52d3322
--- /dev/null
+++ b/doc/ChangeLog
@@ -0,0 +1,438 @@
+ChangeLog for SemanticScuttle
+=============================
+
+.. contents::
+
+0.98.6 - 2013-XX-XX
+-------------------
+- Fix bug #161: URLs broken on 1&1 server
+
+
+0.98.5 - 2013-03-20
+-------------------
+- Fix bug #109: preserve privacy setting from Delicious export files
+ (Thanks to David Glenck)
+- Fix bug #110: Document solution for "searching slashes"
+- Fix bug #111: Comments in config.php mention \*.inc.php files
+ (José María Mateos [rinze])
+- Fix bug: Do not use eregi functions anymore
+
+
+0.98.4 - 2012-01-21
+-------------------
+- Fix bug #3439729: URLs were escaped too often in bookmark list
+- Fix bug: Subtitle was not escaped
+- Fix bug #3388219: Incorrect URL when cancelling tag2tag-actions
+- Fix bug #3393951: Logo images missing on bookmark page
+- Fix bug #3399815: PHP error in opensearch API in 0.98.3
+- Fix bug #3407728: Can't delete users from admin page
+- Fix bug #3431742: open_basedir problems with /etc/ config files
+- Fix bug #3436624: Wrong URL for Delicious API when importing
+- Fix bug #3463481: RSS feed show warnings in feedvalidator.org
+- Fix bug #3384416: Use URL with protocol in bookmarklet
+- Fix bug: Invalid HTML when website thumbnails are activated
+- Fix bug #3413459: Thumbnails not in one line
+- Fix bug #3468293: Delicious import does not preserve private links
+- Fix bug #3396727: Title of http://lesscss.org/ not loaded
+- Fix bug #3111254: getIdFromUser() always returns an integer now (part 2)
+- Implement request #3403609: fr_CA translation update
+- Implement patch #3476011: PostgreSQL tables can not be initialized
+ (Frédéric Fauberteau [triaxx])
+
+
+0.98.3 - 2011-08-09
+-------------------
+- Fix bug #3388456: Missing scripts/fix-unfiled-tags.php
+
+The script was only missing in the .zip file; the PEAR package
+contained it - thus no 0.98.3 PEAR package.
+
+
+0.98.2 - 2011-08-08
+-------------------
+- Fix bug #3385724: Rename tag ends with XML Parsing Error
+- Fix bug #3386178: "system:unfiled" secret tag does not work
+- Fix bug #3384416: Update documentation to explain HTTP/HTTPS root problem
+- Fix bug #3382126: Linked tags does not work for tags with spaces
+- Fix delicious API help page link
+
+Run ``scripts/fix-unfiled-tags.php`` to fix old bookmarks that miss the
+``system:unfiled`` tags.
+
+
+0.98.1 - 2011-08-01
+-------------------
+- Fix bug #3375635: XML parsing problem in top.inc.php
+- Fix bug #3375428: Forgot to remove some old dojo files
+- Fix bug #3160512: Make SemanticScuttle work with FastCGI
+- Fix bug #3376618: Broken tag completion for private bookmarks
+
+
+0.98.0 - 2011-07-21
+-------------------
+- Switch to jQuery and drop dojo
+- Implement request #2928950: Private keys for RSS feeds (Mark Pemberton)
+- Implement request #3164348: Configurable default privacy (Brett Dee)
+- Implement request #1989987: Theming support
+- Implement request #3054906: Show user's full name instead of nickname
+- Implement patch #3059829: update ``FR_CA`` translation
+- Fix bug #3187177: Wrong URL / Export XML Bookmarks
+- Fix bug #3097187: Using opensearch with two tags does not work in Firefox
+- Fix bug #3251877: French translation JavaScript Bug when editing bookmarks
+- Fix bug #3168521: Title of tag-filtered RSS Feed is broken
+- Fix bug #2853627: Javascript warning
+- Fix bug in ``getTagsForBookmarks()`` that fetched all tags
+- Fix privacy issue when fetching tags of several users
+- Fix Google custom search XML
+- Show error message on mysqli connection errors
+- Update php-gettext library to 1.0.10
+- ``api/posts/add`` respects the "replace" parameter now
+- Only URLs with an allowed protocol may be added to the database
+- Support HTTPS connections when ``$root`` is not configured
+- SQL schema version table to ease future database upgrades
+- Documentation is written with rST (reStructuredText) now
+- Support per-host configuration files
+
+
+0.97.2 - 2011-02-17
+-------------------
+- Fix bug #3178597: Broken link to context in gsearch admin index page
+- Fix bug #3074816: French translation breaks edit javascript
+- Fix bug #3111254: Search in my_watchlist results in error
+- Fix bug #3073215: Updating bookmark time does not work
+- Fix bug #3065284: AjaxVote problem with Webkit browsers
+
+
+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
+- New config option to skip "SET NAMES UTF8" call: $dbneedssetnames
+- Do not highlight admin bookmarks when $enableAdminColors is disabled
+- Add russian translation
+- Make HTML export follow the specifications a bit better
+- Fix bug #2953732: faulty error message for duplicate bookmarks
+- Fix bug #2960663: do not send content-type headers twice for ajax/api scripts
+- Fix bug #2976593: fr_FR locale is vietnamese
+
+
+0.96.1 - 2010-02-09
+-------------------
+- Fix bug #2948410: API is broken in 0.96.0
+
+
+0.96.0 - 2010-02-08
+-------------------
+- Fix bug #2843523: ArtViper thumbnail license change
+ reflected by using the javascript backlink
+- Fix bug #2887063: Common tag combination description feels broken
+- Fix bug #2928905: API was broken when no user was logged in
+- Fix bug #2934891: RSS XML was sometimes invalid because
+ special characters did not get escaped.
+- Fix bug #2936833: api fix: shared=no
+ Patch by fnorder@users.sourceforge.net
+- Fix bug #2899515: Bookmarklet broken when site title contains quote
+- Fix several SQL injection possibilities
+- Fix E_NOTICE when calling alltags.php without any parameter
+- Implement part of request #2928950:
+
+ - User adjustable entry count: rss.php?count=20
+ - Fix HTTP content type header for RSS
+ - Add config option to allow sorting by bookmark creation date
+ instead of modification date
+
+ - Implement request #2833793: Export to avahi config files
+ - we support zeroconf networking!
+- Implement request #2934868: Do not display full redirection URL
+ Patch by fnorder@users.sourceforge.net
+- Implement request #2934872: Option to set the "no description"
+ description. Patch by fnorder@users.sourceforge.net
+- Implement request #2830234: Support for external authentication
+ services via PEAR's Auth package
+- Add special header file for shell scripts (header-standalone.php)
+- Fix some problems when using mysqli database driver.
+- Make it easy to have www/ in any location one wants it to have
+- Send HTTP status code 500 when site has not been configured yet.
+
+
+0.95.2 - 2010-01-16
+-------------------
+This is a security release! We do highly recommend to update
+your SemanticScuttle installations!
+
+- Fix several SQL injection possibilities related to tagging
+
+
+0.94.2 - 2010-01-16
+-------------------
+This is a security release! We do highly recommend to update
+your SemanticScuttle installations!
+
+- Fix several SQL injection possibilities related to tagging
+
+
+0.95.1 - 2009-11-16
+-------------------
+- Fix bug: admin tags were not shown because javascript include was broken.
+ Thanks, Jean-Luc!
+
+
+0.95 - 2009-11-10
+-----------------
+- Implement request #2878169: Voting system with two layouts
+- Fix bug #2881886: PHP notice when viewing tags
+- Configuration option to disable registration
+- Implement request #2874049: "Feeling lucky" button
+ just add "?lucky=1" as parameter to search page
+- Hide bookmarks with vote < $threshold from users (not admins)
+- Fix many more unfreed mysql results, but there are still many left
+- Improve documentation of many service methods
+- All unit tests run now
+- Update German translation to 100%
+- Helper scripts for translators and translation documentation
+- Beginning of developer documentation
+- Fix gettext/translation on 64bit systems
+- Fix OpenSearch file
+- Re-add de_AT which is a rather ugly "official-style" german
+ translation. Choose the translation you like most.
+
+
+0.94.1 - 2009-10-29
+-------------------
+This is a security release! We do highly recommend to update
+your SemanticScuttle installations!
+
+- Fix several XSS possibilities
+
+
+0.94 - 2009-10-02
+-----------------
+- Fix bug #2834070: unfreed sql results
+- Fix bug #2834077: .htaccess on virtual masshosted apaches
+- Fix bug #2834097: Errors when using search without terms
+- Fix bug #2834293: E_NOTICE when referer is missing
+- Fix bug #2834118: Undefined variable in search.inc.php when not logged on
+- Fix bug #2847580: short open tag <? in import.php
+- Fix bug #2674961: editAllowed optimization - make less queries for each bookmark
+- Fix bug #2829642: PATH_INFO undefined
+- Fix bug: mysqli did not work at all
+- Fix bug: 404 - api/posts/all not found
+- Fix bug: PATH_INFO used when not needed
+- Fix bug: htmlentities not always escaped
+- Fix bug: Not all pages are valid XHTML
+- Fix bug: Hide welcome message if it is empty
+- Fix bug: allow deleting links between tags when deleting a user.
+- Fix bug: some parse errors
+- Interface fix: put linkedtags box on bookmarks page.
+
+- Implement request #2834111: Detect if cookies are enabled
+- Translation updates
+- Updated German translation (de_DE), removed de_AT
+- New configuration file system
+- Add INSTALL.txt and ChangeLog
+
+
+0.93 - 2009-05-26
+-----------------
+
+
+0.92 - 2009-02-11
+-----------------
+- Database stores data in UTF-8 now, leading to better
+ search results for accented characters
+- Several Internet Explorer bugs fixed
+- clean URLs are back
+- Improved page titles
+
+
+0.91 - 2008-11-14
+-----------------
+- Major Refactoring: add a tag cache improving query processing
+ (knowledge inference on tag relations) [DB modified]
+- Interface fix: allows to remove a tag from the selection into
+ subtitle bar thanks to a \*.
+- New Feature: authorizes anchors with brackets into descriptions
+ of bookmark. For example: "[city]Paris[/city]".
+ This text between anchors will be highlighted when the
+ corresponding bookmark will be displayed.
+- Interface Fix: transforms description field into textarea
+- Interface fix: make longer description field [DB modified]
+- Interface fix: takes dModified field instead of bDatetime one
+ (to sort bookmarks)
+- Interface fix: add link to gsearch/ forlder on normal search
+ page and correct gsearch feature.
+- New Translation: SemanticScuttle is now available in German,
+ formal and gender-neutral version (thanks Elmar H. Beer)
+- New Feature: admin can check urls validity (in his/her admin page)
+- Bug fix: correct ROOT declaration
+- Bug fix: correct DOJO completion in tags edition
+- Interface fix: add tags cloud from all users into add bookmark
+ window (orouk proposition)
+- New Feature: possibility to import a structure of
+ hierarchical tags from a file
+- Minor fix: improve import from Netscape files
+ (now takes tags into account)
+- Interface fix: add an hyperlink from bookmark edition
+ page to collaborative edition of the same bookmark
+- Bug fix: correct admin CSRF vulnerability (thank you Etienne Z.)
+- Major refactoring: transform users row into object, define
+ page parameters (POST and GET) as constants, simplify
+ debugging ... [Config file modified: $debugMode]
+- New feature: Dynamic tree of tags using Dojo toolkit
+ (used by a menu2 sidebar box) [Config file modified: $menu2Tags]
+- Bug fix: prevent from CSRF vulnerabilities on profile page
+ (thank you Etienne Z.)
+- Bug fix: correct AJAX deleting of bookmarks (thank you Ricco)
+
+
+0.90 - 2008-06-05
+-----------------
+The main improvements of this new version are:
+
+- menu tags (tags which are included into the "menu" tag and
+ thus which appear on the main page)
+- connexion with Google Custom Search Engine which allows to
+ use the Google Engine to look into your SemanticScuttle
+ bookmarks (http://www.google.com/coop/cse/).
+
+
+All the changes:
+
+- New feature: add Google Custom Search Engine into
+ gsearch/ folder and looking into all bookmarks [Config modified]
+- Interface design: antispam question is hidden when
+ focussed (Thanks to Lifty)
+- Interface fix: move welcome message to index page
+- Interface fix: displays an error message if the
+ config.inc.php file has not been created
+- Interface design: improve tags presentation, menu box,
+ popup size, redirection after adding a bookmark, RSS link position
+- Interface design: hide searchs with no results into search history box
+- New feature: private descriptions for tags [DB modified]
+- Refactoring: rename sc_tags into sc_bookmarks2tags
+ (and services, codes...) [DB modified]
+- Interface design: shorten too long URLs in the displayed bookmarks
+- Bug fix: correct mutual logins for several installations on the same host
+- New feature: menu box which displays tags included into the
+ special tag "menu" [Config modified]
+- New feature: export URL for Google Custom Search (api/export/gcs)
+- New feature: add admins profile [Config modified: $admin_users]
+- New feature: template sidebar customized in configuration
+ file [Config modified]
+- New feature: export bookmarks into html file for import into
+ browsers (Thanks to warenhaus)
+- Interface design: add export option through API into the profile page
+- Bug fix: del.icio.us bookmarks (in .xml format) import 0K
+- Bug fix: correct authentification for API with CGI
+- Interface fix: correct few links and translations on the about page
+- Interface design: copy description and tags (when creating a
+ bookmark from another one)
+- Feature improvement: make rename tags more consistent
+ (with regards to linked tags)
+- Interface design fix: improve visualisation of structuredtags box
+- Interface design: improve tag2tag link edition (add item in
+ Actions menu and improve tag2tagedit page)
+- New feature: Save search history: what, by who, when, number of
+ results and display "last searches" box [DB & config modified]
+- Bug fix: correct counting result for history page
+- Bug fix: correct counting for unified bookmarks
+- Interface fix: improve visualisation in history if no descriptions
+ for bookmarks
+- Interface design: display number of resources in results
+- Interface design: improve navigation between tags
+ (clickable ">")
+- Bug fix: delete thumbnail when ajax delete
+
+
+0.88 - 2008-03-12
+-----------------
+The main change of this release is the possibility to preview websites
+through thumbnails (thanks to artviper.net).
+
+All the changes:
+
+- Interface design: display SemanticScuttle version number
+ on "about" page
+- Bug fix: allow clean urls thanks to .htaccess
+- New feature: add website preview/thumbnail (based on
+ artviper.net) [Config file modified]
+- Interface fix: add About link for logged users
+- Interface fix: hide "structured box" when no linked tags
+- Interface fix: make more consistent the general navigation between
+ personal and common spaces
+- Interface fix: add a link to last editor profile in common description.
+ Moreover, don't count a change if a user saves with no modifications.
+- Bug fixes: prevent from adding special characters
+ (like " or ') into tags
+- Upgrade: from Scuttle 0.7.2 to SemanticScuttle 0.87.
+
+
+0.87 - 2008-02-14
+-----------------
+This new version brings two major features to SemanticScuttle:
+
+1) You can now create a synonym link between tags.
+ For example, by linking the tags "monument" and "monuments",
+ you can find resources tagged with the first one when you navigate
+ through the second one. More important, this link works with
+ the inclusion link already implemented.
+2) Users can create commune descriptions for tags and bookmarks.
+ In a small community, the idea is to bring gradually people to work
+ together (if they want it). From a research perspective, we explore
+ the bridge between individual actions (easy to do) and collaborative
+ ones (more difficult but more powerful).
+
+All the changes:
+
+- Interface design: allow to access to users' bookmarks from last
+ user sidebox. (Currently, we can just access to their profiles).
+- Interface design: hide multiple URLs repeated into history page
+- Interface design: hide About link for logged users
+- New feature: Collaborative spaces (like wiki) for descriptions
+ of tags and bookmarks
+- Interface fix: improve the creation of structured links
+- New feature: Allow the creation of synonym links between tags
+
+
+0.86 - 2008-01-24
+-----------------
+- Bug fix: correct antispam question feature
+- Interface design: normalize URL when adding
+ (remove final "/")
+- Interface design: group similar URL
+- Interface bug fix: improve linked relations into structured links box
+- Interface bug fix: improve display of structured tags into alltags
+ and populartags page (problem of div size)
+
+
+0.85 - 2008-01-22
+-----------------
+- New feature: Add stats (nb children, nb descendants, depth,
+ nb update) to structured tags
+
+ - Allow to visualize structured tags according to stats
+ - Add admin page to update stats from the existing structured
+ tags in the database
+
+- Translation: improve French translation (add missing translations,
+ transform "labels" into "tags")
+- New feature: List all users in a users page and users block on
+ the front page
+- Interface design: Transform recent bookmarks into last bookmarks
+ on front page (to avoid blank front page)
+- Interface design: Add structured tags in a right box on the front page
+- New feature: Antispam question to register
+- New feature: Allowing a subtitle message, managable in the
+ configuration file.
+- Interface design: Redesign menu (search box, sort buttons, top
+ and right menu)
+- Interface design: Make URLs appear under bookmarks
+- Debug: correcting a problem to log out (bugs from Scuttle).
+
diff --git a/doc/INSTALL.txt b/doc/INSTALL.txt
new file mode 100644
index 0000000..c911740
--- /dev/null
+++ b/doc/INSTALL.txt
@@ -0,0 +1,148 @@
+============================
+SemanticScuttle installation
+============================
+
+.. contents::
+
+Prerequisites
+=============
+To run SemanticScuttle, you need:
+
+- PHP5 with filter functions enabled
+- A web server, for example Apache
+
+
+Standard installation instructions
+==================================
+If you've downloaded a ``SemanticScuttle-x.y.z.zip`` file from SourceForge,
+then this instructions are for you.
+
+1. Create a new MySQL/PostgreSQL database
+2. Import ``data/tables.sql`` into that database, i.e.
+ run ::
+
+ $ mysql -umyusername -p mydatabasename < data/tables.sql
+
+ on the shell.
+
+ If you run PostgreSQL, use ``tables-postgresql.sql``::
+
+ $ psql mydatabasename myusername < data/tables-postgresql.sql
+
+3. Copy ``data/config.php.dist`` to ``data/config.php`` and modify it as
+ necessary. See configuration_ for more information.
+4. Make the cache directory writable by your web server.
+ For example, run ::
+
+ $ chmod 0777 cache
+
+ on the shell.
+5. Set the ``www/`` directory as document root in your web server,
+ restart the web server.
+6. That's all! Visit your SemanticScuttle installation web site now
+ with your browser.
+7. Register a user and add bookmarks.
+
+
+.. _configuration: configuration.html
+
+
+Ugly www directory in URLs
+--------------------------
+In case point 5 of the installation instructions cannot be put into
+practice by you because you are not able to change the web server
+configuration, you are not lost! There is a way to get rid of
+``www/`` in your URL!
+
+Imagine following directory layout: ::
+
+ /home/customer123/
+ www/
+ subdomain1/
+ subdomain2/
+ subdomain3/
+
+Create a SemanticScuttle directory somewhere outside ``www`` if possible
+and put all directories except ``www/`` in there. Move all files and
+directories from ``www/`` into your subdomain directory. Then modify
+``subdomain/www-header.php`` to include the correct file path.
+
+The new directory layout should look that way: ::
+
+ /home/customer123/
+ semanticscuttle/
+ doc/
+ data/
+ src/
+ www/
+ subdomain1/
+ subdomain2/ (this is the semanticscuttle subdomain)
+ about.php
+ admin.php
+ ..
+ www-header.php
+ subdomain3/
+
+Now open www-header.php and replace ::
+
+ require_once '../src/SemanticScuttle/header.php';
+
+with ::
+
+ require_once '../../semanticscuttle/src/SemanticScuttle/header.php';
+
+
+
+PEAR package installation
+=========================
+Apart from the normal installation, you may install the SemanticScuttle PEAR
+package - it gives you the ability to upgrade to the next version with a single
+command, without manually unzipping files.
+
+0. The SemanticScuttle PEAR channel is available at
+ http://semanticscuttle.sourceforge.net/pirum.html
+
+1. Make sure you have the PEAR installer (or Pyrus) available and working.
+ You can check that by running ::
+
+ $ pear version
+ PEAR Version: 1.9.2
+ PHP Version: 5.3.6-13ubuntu3.2
+ Zend Engine Version: 2.3.0
+ Running on: Linux bogo 3.0.0-12-generic #20-Ubuntu SMP Fri Oct 7 14:56:25 UTC 2011 x86_64
+
+2. Discover the channel if you haven't done so yet::
+
+ $ pear channel-discover semanticscuttle.sourceforge.net
+
+ Verify that the channel has been added with::
+
+ $ pear list-channels
+ Registered Channels:
+ ====================
+ Channel Alias Summary
+ ...
+ semanticscuttle.sourceforge.net sc SemanticScuttle PEAR channel
+ ...
+
+3. Install SemanticScuttle::
+
+ $ pear install sc/semanticscuttle-beta
+
+4. Find out where it got installed to::
+
+ $ pear list-files sc/semanticscuttle
+
+5. Generally, the directory you need to point your web server's ``DocumentRoot`` to
+ is the *PEAR www directory* + ``/SemanticScuttle/``. Find the www directory with::
+
+ $ pear config-get www_dir
+ /usr/share/php/htdocs
+
+6. The configuration file is located in *PEAR's data directory* + ``/SemanticScuttle/``::
+
+ $ pear config-get data_dir
+ /usr/share/php/data
+
+ In ``$data_dir/SemanticScuttle/``, copy ``config.php.dist`` to
+ ``config.php`` and continue with configuration_.
diff --git a/doc/LICENSE.txt b/doc/LICENSE.txt
new file mode 100644
index 0000000..72188c3
--- /dev/null
+++ b/doc/LICENSE.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License. \ No newline at end of file
diff --git a/doc/README.rst b/doc/README.rst
new file mode 100644
index 0000000..ed41a38
--- /dev/null
+++ b/doc/README.rst
@@ -0,0 +1,67 @@
+======================
+SemanticScuttle 0.98.5
+======================
+A social bookmarking tool experimenting with new features
+like structured tags or collaborative descriptions of tags.
+
+- Home page: http://semanticscuttle.sourceforge.net/
+- Project page: https://sourceforge.net/projects/semanticscuttle/
+- Demo: http://semanticscuttle.sourceforge.net/demo/
+
+Available under the GNU General Public License
+
+
+Installation
+============
+See `INSTALL.rst`__
+
+
+__ INSTALL.html
+
+
+Upgrading from a previous version
+=================================
+See `UPGRADE.txt`__
+
+__ UPGRADE.html
+
+
+Public API
+==========
+SemanticScuttle supports most of the `del.icio.us API`__.
+Almost all of the neat tools made for that system can be modified
+to work with your SemanticScuttle installation. 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.
+
+__ http://del.icio.us/doc/api
+
+
+
+Links
+-----
+- `further documentation`__
+- `support and help questions`__
+- `development mailing list instructions`__
+- `suggestions`_ for SemanticScuttle
+- `bug reports`_
+- `feature requests`_
+
+__ http://semanticscuttle.wiki.sourceforge.net/
+__ http://sourceforge.net/forum/forum.php?forum_id=759510
+__ https://sourceforge.net/mailarchive/forum.php?forum_name=semanticscuttle-devel
+.. _suggestions: http://sourceforge.net/forum/forum.php?forum_id=759511
+.. _bug reports: http://sourceforge.net/tracker/?group_id=211356&atid=1017430
+.. _feature requests: https://sourceforge.net/tracker/?group_id=211356&atid=1017433
+
+
+
+
+Known issues
+============
+
+Number of bookmarks always 0: "0 bookmark(s)"
+---------------------------------------------
+This issue occurs when debug mode is enabled.
+Technically, this is because the database layers ``DEBUG_EXTRA`` gets
+enabled through debug mode.
diff --git a/doc/UPGRADE.txt b/doc/UPGRADE.txt
new file mode 100644
index 0000000..511748e
--- /dev/null
+++ b/doc/UPGRADE.txt
@@ -0,0 +1,172 @@
+=================================================
+Upgrading SemanticScuttle from a previous version
+=================================================
+
+.. contents::
+
+From version 0.94-0.98.1 to 0.98.3
+==================================
+Run ``scripts/fix-unfiled-tags.php`` to fix old bookmarks that miss the
+``system:unfiled`` tags.
+
+
+From version 0.97 to 0.98
+=========================
+Database updates
+----------------
+Apply ``data/schema/6.sql``
+
+ ALTER TABLE `sc_users` ADD `privateKey` VARCHAR(33) NULL;
+ CREATE UNIQUE INDEX `privateKey` ON `sc_users` (`privateKey`);
+
+
+From version 0.96 to 0.97
+=========================
+No database changes necessary.
+
+
+From version 0.95 to 0.96
+=========================
+Database updates
+----------------
+Apply ``data/schema/5.sql``
+
+API
+---
+The method signatures of ``addBookmark()`` and ``updateBookmark()``
+changed due to the addition of the ``$short`` parameter.
+
+We got complaints about the changed file structure, and people told
+us that they just cannot set the document root to ``www/``, because they
+are not admins on their HTTP server. This is a valid point, and
+with 0.96.0 you can easily change it. See `INSTALL.txt`_ for more information
+about moving ``www/``.
+
+.. _INSTALL.txt: INSTALL.html
+
+
+From version 0.94 to 0.95
+=========================
+The file structure completely changed in 0.95.0 compared
+to previous versions. We recommend that you start with a
+fresh installation, just copying over your ``config.php`` file.
+Set your web server document root directory to ``www/``.
+
+Yes, we kind of lost the ability to run SemanticScuttle
+in a subdirectory of a hostname. This functionality will
+be back in one of the next releases, but for now, you have
+to live with it.
+
+Update your database
+--------------------
+Apply ``data/schema/4.sql``.
+
+Currently, only MySQL can be used as database backend.
+All other DBMS (database management systems) have not been
+tested except for PostgreSQL, and SemanticScuttle fails there.
+
+Translation
+-----------
+The ``de_AT`` translation has been re-added. This is because
+``de_AT`` provides a rather ugly "official German" style,
+while the normal ``de_DE`` is friendlier. Choose what you like.
+
+
+From version 0.93 to 0.94
+=========================
+
+- Nothing changed except for the default configuration file.
+ It is recommended to start with a fresh config file,
+ but not neccesary. Old config files still work.
+- If you used translation ``de_AT``, please switch to ``de_DE``.
+ ``de_AT`` was moved to ``de_DE`` and ``de_AT`` has been removed.
+
+
+From version 0.92 to 0.93
+=========================
+
+- Backup your database
+- Make a copy from your SemanticScuttle Web directory
+- Upgrade your current configuration file (``config.inc.php``) with respect to ``config.inc.php.example`` ::
+
+ $footerMessage = ''; #HTML message appearing at the bottom of the page (just above SemanticScuttle credits)
+ $sidebarTopMessage = ''; #HTML message appearing at the top of the sidebar
+ $sidebarBottomMessage = ''; #HTML message appearing at the bottom of the sidebar
+ $adminsCanModifyBookmarksFromOtherUsers = true; # 'true' if admin users can edit or delete bookmarks belonging to other users. Else 'false'.
+ $adminsAreAdvisedTagsFromOtherAdmins = false; # 'true' if tags from other admins are proposed to each admin (in add/edit a bookmark page). Else 'false'.
+ $defaultPerPageForAdmins = 20; # default number of bookmarks per page for admins (-1 means no limit)
+
+
+From version 0.91 to 0.92
+=========================
+Message: this version modifies the database to UTF-8 charset. The idea is to convert the content (through BLOB type) and then to change the tables' charsets.
+
+- Backup your database
+- Make a copy from your SemanticScuttle Web directory
+- Upgrade your database by applying ``data/schema/3.sql``
+- Upgrade your current configuration file (``config.inc.php``) with respect to ``config.inc.php.example``
+
+ - Add variable ::
+
+ $descriptionAnchors = array("author", "isbn", "address"=>"[address][street][/street][city][/city][/address]"); #add a possible anchor (structured content) for bookmarks' description field
+
+ - Add variable ::
+
+ $enableCommonTagDescriptionEditedByAll = true; #true mean everybody can edit common description. Else just the admins can do it.
+ - Add variable ::
+
+ $googleAnalyticsCode = ''; #Allow GoogleAnalytics tracker https://www.google.com/analytics/
+
+
+From version 0.90 to 0.91
+=========================
+- Backup you database
+- Make a copy from your SemanticScuttle Web directory
+- Upgrade your database by applying ``data/schema/2.sql``
+- Upgrade your current configuration file (``config.inc.php``) with respect to ``config.inc.php.example``
+
+ - Delete last line ::
+
+ include_once('debug.inc.php');
+
+ - Add variable::
+
+ $menu2Tags = array('example', 'of', 'menu', 'tags');
+
+ - Add variable::
+
+ $debugMode = true; # if true, show debug messages
+
+
+From version 0.89 to 0.90
+=========================
+
+- Backup you database
+- Make a copy from your SemanticScuttle Web directory
+
+- Upgrade your current configuration file (config.inc.php) with respect to config.inc.php.example
+
+ add these lines under ``$enableWebsiteThumbnails = false; # enableWebsiteThumbnails {true|false}``::
+
+ $thumbnailsUserId = '';
+ $thumbnailsKey = '';
+
+
+From version 0.88 to 0.89
+=========================
+
+- Backup you database
+- Make a copy from your SemanticScuttle Web directory
+- Upgrade your database by applying ``data/schema/1.sql``
+- Upgrade your current configuration file (``config.inc.php``) with respect to ``config.inc.php.example``
+
+ - add line::
+
+ $sizeSearchHistory = 10;
+
+ - add sidebar block index line::
+
+ $index_sidebar_blocks = array('search','menu','users','popular');
+
+ - add line::
+ $enableGoogleCustomSearch = true;
diff --git a/doc/allinone.rst b/doc/allinone.rst
new file mode 100644
index 0000000..e5ca3c7
--- /dev/null
+++ b/doc/allinone.rst
@@ -0,0 +1,31 @@
+=============================
+SemanticScuttle documentation
+=============================
+
+.. contents::
+
+First reads
+===========
+.. include:: README.rst
+.. include:: INSTALL.txt
+.. include:: UPGRADE.txt
+
+
+
+Features
+========
+.. include:: authentication.rst
+.. include:: ssl-client-certificates.rst
+.. include:: themes.rst
+
+
+
+Developer documentation
+=======================
+.. include:: developers/rules.rst
+.. include:: developers/api.rst
+.. include:: developers/debugging.rst
+.. include:: developers/release-new-version.rst
+.. include:: developers/running-unit-tests.rst
+.. include:: developers/translation.rst
+.. include:: ChangeLog
diff --git a/doc/authentication.rst b/doc/authentication.rst
new file mode 100644
index 0000000..d9d5066
--- /dev/null
+++ b/doc/authentication.rst
@@ -0,0 +1,216 @@
+============================================
+External authentication with SemanticScuttle
+============================================
+
+Most times, one piece of software is only a part in the big puzzle
+that makes the software landscape of a company or organization.
+SemanticScuttle is not different and should integrate as nicely as
+possible with all other systems.
+
+One of the basic tasks of integration is user authentication against
+a central database - be it a central user database, an LDAP or a
+active directory server.
+
+Since version 0.96, SemanticScuttle supports user authentication against
+external systems. To provide a wide range of supported systems, we chose
+to utilize PEAR's `Authentication package`__.
+It does this by providing different "`authentication containers`__",
+for example Database, IMAP, LDAP, POP3, RADIUS, SAP and SOAP.
+
+Please be aware of the fact that, after successful authentication, the user
+and his scrambled password are stored in the SemanticScuttle database.
+This is required for proper functioning of the software. It does not mean
+that you will be able to login if your external authentication provider
+is offline - you won't, execpt you switch it off in the SemanticScuttle
+configuration.
+
+
+__ http://pear.php.net/package/Auth
+__ http://pear.php.net/manual/en/package.authentication.auth.intro-storage.php
+
+
+Basic configuration
+===================
+The default configuration file ``data/config.default.php`` has an own section
+on auth options and an explanation of the single entries.
+
+To utilize the external authentication, you need to install the
+PEAR Auth package: ::
+
+ $ pear install auth
+
+If you do not have a PEAR installation available, you can try to manually
+install the files in the src/ directory. If you choose to do that, the
+src/ directory should look similar to that: ::
+
+ src/
+ Auth.php
+ Auth/
+ Anonymous.php
+ Container.php
+ Container/
+ ..
+ SemanticScuttle/
+ header.php
+ ..
+
+After that, modify your ``data/config.php`` file. The most important change
+is to use ::
+
+ $serviceoverrides['User'] = 'SemanticScuttle_Service_AuthUser';
+
+which tells SemanticScuttle to switch to the special authentication service.
+
+Now that's done, you can configure the single auth options:
+
+``$authType = 'MDB2';``
+ selects the authentication container.
+
+``$authOptions``
+ is an array of options specific to the authentication container. Please
+ consult the PEAR Auth documentation for more information.
+
+``$authDebug = true;``
+ should be used when setup fails, since it may give important hints
+ where it fails.
+
+ Please note that login will seem to fail with
+ debugging activated. Going back to the main page after that will
+ show that you are logged in.
+
+
+
+Authentication examples
+=======================
+
+General database authentification
+---------------------------------
+Here you also need the PEAR `MDB2 package`_.
+The "``new_link``" option is important!
+
+``config.php`` settings: ::
+
+ $serviceoverrides['User'] = 'SemanticScuttle_Service_AuthUser';
+ $authType = 'MDB2';
+ $authOptions = array(
+ 'dsn' => array(
+ 'phptype' => 'mysql',
+ 'hostspec' => 'FIXME',
+ 'username' => 'FIXME',
+ 'password' => 'FIXME',
+ 'database' => 'FIXME',
+ 'new_link' => true,
+ ),
+ 'table' => 'usersFIXME',
+ 'usernamecol' => 'usernameFIXME',
+ 'passwordcol' => 'passwordFIXME',
+ 'cryptType' => 'md5',
+ );
+
+
+Mantis Bugtracker
+-----------------
+Here you also need the PEAR `MDB2 package`_.
+
+``config.php`` settings: ::
+
+ $serviceoverrides['User'] = 'SemanticScuttle_Service_AuthUser';
+ $authType = 'MDB2';
+ $authOptions = array(
+ 'dsn' => array(
+ 'phptype' => 'mysql',
+ 'hostspec' => 'FIXME',
+ 'username' => 'FIXME',
+ 'password' => 'FIXME',
+ 'database' => 'FIXME',
+ 'new_link' => true,
+ ),
+ 'table' => 'mantis_user_table',
+ 'usernamecol' => 'username',
+ 'passwordcol' => 'password',
+ 'cryptType' => 'md5',
+ );
+
+.. _MDB2 package: http://pear.php.net/package/MDB2
+
+
+MediaWiki
+---------
+Unfortunately, the password column does not contain a simple hashed
+password - for good reasons as described on
+http://www.mediawiki.org/wiki/Manual_talk:User_table#user_password_column
+
+If you configure your MediaWiki_ to use passwords without salt, you
+can make it work nevertheless:
+
+MediaWiki ``LocalSettings.php``: ::
+
+ $wgPasswordSalt = false;
+
+\- after that, users need to change/update their passwords to get them
+unsalted in the database. You can verify if the passwords are unhashed
+if you do ::
+
+ SELECT CAST( user_password AS CHAR ) FROM user
+
+on your MediaWiki database. Passwords prefixed with "``:A:``" can be used.
+
+Another problem is that mediawiki user names begin with an uppercase letter.
+You need to modify ``www/login.php`` and remove the "``utf8_strtolower``" function
+call: ::
+
+ $posteduser = trim(utf8_strtolower(POST_USERNAME));
+
+becomes ::
+
+ $posteduser = trim(POST_USERNAME);
+
+
+``config.php`` settings: ::
+
+ $serviceoverrides['User'] = 'SemanticScuttle_Service_AuthUser';
+ $authType = 'MDB2';
+ $authOptions = array(
+ 'dsn' => array(
+ 'phptype' => 'mysql',
+ 'hostspec' => 'FIXME',
+ 'username' => 'FIXME',
+ 'password' => 'FIXME',
+ 'database' => 'FIXME',
+ 'new_link' => true,
+ ),
+ 'table' => 'user',
+ 'usernamecol' => 'user_name',
+ 'passwordcol' => 'user_password',
+ 'cryptType' => 'md5_mediawiki',
+ );
+ function md5_mediawiki($password) {
+ return ':A:' . md5($password);
+ }
+
+
+.. _MediaWiki: http://www.mediawiki.org/wiki/MediaWiki
+
+Active Directory / LDAP
+-----------------------
+Here we authenticate against an active directory server.
+
+``config.php`` settings: ::
+
+ $serviceoverrides['User'] = 'SemanticScuttle_Service_AuthUser';
+ $authType = 'LDAP';
+ $authOptions = array(
+ 'host' => '192.168.1.4',
+ 'version' => 3,
+ 'basedn' => 'DC=EXAMPLE,DC=ORG',
+ 'binddn' => 'readuser',
+ 'bindpw' => 'readuser',
+ 'userattr' => 'sAMAccountName',
+ 'userfilter' => '(objectClass=user)',
+ 'attributes' => array(''),
+ );
+ $authEmailSuffix = '@example.org';
+
+More LDAP options can be found in the `PEAR manual`__.
+
+__ http://pear.php.net/manual/en/package.authentication.auth.storage.ldap.php
diff --git a/doc/configuration.rst b/doc/configuration.rst
new file mode 100644
index 0000000..dd0fb5e
--- /dev/null
+++ b/doc/configuration.rst
@@ -0,0 +1,98 @@
+===================
+Configuration files
+===================
+
+.. contents::
+
+SemanticScuttle uses at least two configuration files:
+
+1. Default configuration file ``config.default.php``
+2. Custom configuration file ``config.php``
+
+
+The **default configuration** file contains sensible defaults for most users
+that do not need to be changed to get started.
+
+Never change it - it will get overwritten with the next update.
+If you want to change values in it, copy them into your personal
+``config.php`` file - updates to SemanticScuttle will not change that one.
+
+The **custom configuration** file, ``config.php`` is created by copying the
+shipped ``config.php.dist`` file and modifying the values in there.
+
+It consists of the configuration directives that should be set on every
+fresh installation.
+
+
+
+Configuration scenarios
+=======================
+
+Simple installation
+-------------------
+Put your configuration file in ``data/config.php``.
+If you installed SemanticScuttle's PEAR package, use::
+
+ $ pear config-get data_dir
+ /usr/share/php/data
+
+to find the data directory location and append ``SemanticScuttle/`` to it.
+In this case, the configuration file has to be in::
+
+ /usr/share/php/data/SemanticScuttle/config.php
+
+
+The configuration file may also be saved into::
+
+ /etc/semanticscuttle/config.php
+
+
+Multiple SemanticScuttle instances
+----------------------------------
+The files of one single SemanticScuttle installation may be shared
+for several SemanticScuttle instances.
+
+To be able to configure them differently, SemanticScuttle supports
+per-host configuration files:
+
+- ``data/config.$hostname.php``
+- ``/etc/semanticscuttle/config.$hostname.php``
+
+
+
+Configuration options
+=====================
+``$root`` URL
+-------------
+Normally, this configuration setting is detected automatically and will
+work for both HTTP and HTTPS installations.
+
+If your installation is available on **HTTP only**, then you need to configure
+it.
+
+The value is the full URL to your installation, including a trailing
+slash::
+
+ $root = "http://homepage.example.org/semanticscuttle/";
+
+or::
+
+ $root = "http://bookmarks.example.org/";
+
+
+Common problems
+===============
+Searching for words with slashes "/" does not work
+--------------------------------------------------
+When searching for a phrase with a slash in it, like "foo/bar", you
+may get a 404 error.
+
+In that case, you need to enable AllowEncodedSlashes__ in your Apache
+virtual host configuration::
+
+ AllowEncodedSlashes NoDecode
+
+Restart apache after changing the vhost config file.
+Searching will work now.
+
+__ http://httpd.apache.org/docs/2.2/mod/core.html#allowencodedslashes
diff --git a/doc/developers/TODO.rst b/doc/developers/TODO.rst
new file mode 100644
index 0000000..59dd655
--- /dev/null
+++ b/doc/developers/TODO.rst
@@ -0,0 +1,104 @@
+- SemanticScuttle_Filter
+ -> class with static filter functions to filter
+ ids, usernames, passwords, sorting etc.
+- 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 -> 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:
+#1908041 - klick counter
+ a counter how many people klicked one link
+#1964906 - Page numbers list / alternative pagination
+ I would love to have page numbers like for example google has for terms
+ with many hits. You can have a look at what exactly I'm talking about here:
+ http://www.jenst.se/wp-content/uploads/2008/04/wp-page-numbers-themes.gif .
+ With this you can more easily browse through the pages by clicking the
+ numbers and not those 'next' and 'previous' buttons.
+#1989984 - Branding Improvements
+ Create an ability to brand the installed site on several levels:
+ Level 1 - add a logo and text to the header
+ Level 2 - add a logo and text to the top header and add html (logos, text,
+ flickr widgets, youtube widgets, etc.) to the left and right side bars
+ through an interface similar to the theme editor on Wordpress. Also
+ include easy interface to Google AdWords.
+ Level 3 - add the items in Level 2 plus a banner ad manager.
+ Requirements:
+ - add comments to the CSS to allow for the novice to edit the CSS based on
+ known widget or image size. This is an important addition.
+#1969682 - private bookmarks
+ I think the handling of private bookmarks could be improved. Now it is so
+ that you just see a smaller amount of bookmarks as a visitor as you would
+ see when you are logged in as a member (and if had added some private
+ bookmarks to the site).
+ There should be a separate count of this private bookmarks to indicate that
+ there are some. Something like '80 public/total bookmark(s) - 5 private
+ bookmarks(s)'. It also should be easier to find them. Maybe the above
+ mentioned line counting the private bookmarks could be a link to a page
+ listing them.
+#2035563 - Delete bookmarks based on user votes
+ I'm using Semantic Scuttle for ONE topic based bookmarks sharing. Sometimes
+ I, as admin is not sure if the bookmark submitted is right for or not.
+ I'l like to propose a function, where 5 (numbers can be selected)of top
+ users (or sub admins if they can be created) of the site marks the bookmark
+ not applicable to the site, then that bookmark is deleted.
+#2862548 - Disable email TLD verification
+ It should be possible to disable the top level domain verification in the
+ email verification for new users. Use cases are
+ a) new top level domains (TLD) that are created in the future
+ b) company-internal non-standard TLDs
+#2830219 - Edit by other people
+ We use SemanticScruttle to share bookmarks in the company I work for.
+ It'd be helpful if there was a way all people could edit public bookmarks.
+#1969705 - adjustable thumbnail height and width in config.inc.php
+ I put two vars in the config file to adjust the size of the thumbnails and
+ accordingly changed the bookmarks.tpl.php file:
+ bookmarks.tpl.php
+ $websiteThumbnailsWidth = 90; # width of thumbnails in pixel, max value
+ 240
+ $websiteThumbnailsHeight = 68; # height of thumbnails in pixel, max value
+ 180
+#1933227 - custom maximum number of items in RSS-feeds
+ for Mozilla Firefox's Live Bookmarks feature, the current 15 items
+ delivered by RSS may not be enough.
+ note: by changing getPerPageCount() in functions.inc.php in a similar
+ manner, it should be possible to make the max-entries-per-page
+ customizable
+#1926991 - Admin Approval of New Users
+ Create a way for admins to select if they have to approve all new users.
+ Then have the following methods for new user approval:
+ - by email to selected admin on a per instance basis
+ - through an admin panel on a per instance basis
+ - through an admin panel on a batch basis
+ - through an admin panel by setting up rules for approval (for example:
+ user admin must have a specific domain - helpful for big companies)
+ This would help reduce the amount of spam for some users.
+#1932109 - tag counting: count each URL only once
+ I don't know if it's only me, but I have would prefer another way of
+ calculating a tag's weight:
+ current behaviour:
+ existing tags are currently counted by summing up the entries in the system
+ that have that tag.
+ example:
+ one user added youtube.com with the tag "videos".
+ one user added video.google.com with the tag "videos".
+ ten users added amazon.com with the tag "books".
+ the tags are now weighed like this: 2 videos, 10 books.
+ although there are more unique URLs in the system with the tag "videos".
+ preferred behaviour:
+ for tag-weighing, count the unique URLs, instead of the entries.
+ in the example above: count 2 for "videos", one for "books".
+#2830224 - Shorturl/tinyurl service
+ It would be cool if SemanticScruttle could be used as shorturl service with
+ configurable short urls.
+ So I'd define "freddy" as short url name in the bookmark, and anyone could
+ access it e.g. via our-bookmarks.com/s/freddy and get redirected to the
+ real url. Useful to get permanent URLs to moving targets.
diff --git a/doc/developers/api.rst b/doc/developers/api.rst
new file mode 100644
index 0000000..efa05fe
--- /dev/null
+++ b/doc/developers/api.rst
@@ -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/doc/developers/debugging.rst b/doc/developers/debugging.rst
new file mode 100644
index 0000000..6bee309
--- /dev/null
+++ b/doc/developers/debugging.rst
@@ -0,0 +1,24 @@
+How to debug SemanticScuttle
+============================
+
+
+Database queries
+----------------
+
+In ``data/config.php``, enable ``debugMode``.
+Further, add the following afterwards: ::
+
+ register_shutdown_function(
+ create_function('', <<<FNC
+ \$GLOBALS['db'] = SemanticScuttle_Service_Factory::getDb();
+ \$GLOBALS['db']->sql_report('display');
+ FNC
+ )
+ );
+
+
+To see database queries in SemanticScuttle, add ::
+
+ ?explain=1
+
+to your URL.
diff --git a/doc/developers/doc-TODO.rst b/doc/developers/doc-TODO.rst
new file mode 100644
index 0000000..4fac4ab
--- /dev/null
+++ b/doc/developers/doc-TODO.rst
@@ -0,0 +1,9 @@
+- Bookmarklets: Text selection is used as description
+- Tag nesting: Paris > France > World
+- Tag alias: Deutschland = Germany
+
+
+- Which fields are searched?
+ title, description, private note, username, tags
+
+- What are [isbn] and so for?
diff --git a/doc/developers/release-new-version.rst b/doc/developers/release-new-version.rst
new file mode 100644
index 0000000..de2c4e3
--- /dev/null
+++ b/doc/developers/release-new-version.rst
@@ -0,0 +1,31 @@
+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/default/about.tpl.php``,
+ ``build.xml`` and ``doc/README.rst``
+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
+ database. Register a user, add bookmarks etc.
+6. When all is fine, it's time to release.
+ The build script takes care for most of the
+ tasks.
+ Run "``phing release``", and it will upload the release to
+ sourceforge.
+7. Create a git tag and push it
+8. Create the PEAR package and test it:
+ Run ``phing package``, then
+ ``pear upgrade dist/pear/SemanticScuttle-0.xx.tgz``
+9. Publish the PEAR package: ``phing deploy-sf-pear``
+10. Publish the documentation: ``phing deploy-docs``
+11. Write announcement mail to the SemanticScuttle mailing list
+ semanticscuttle-devel@lists.sourceforge.net
+12. Announce the new release in the sourceforge project news
+ https://sourceforge.net/news/submit.php?group_id=211356
+ Help about the process is available in
+ https://sourceforge.net/apps/trac/sourceforge/wiki/News
+13. Set the default file in
+ https://sourceforge.net/projects/semanticscuttle/files/SemanticScuttle/
diff --git a/doc/developers/rules.rst b/doc/developers/rules.rst
new file mode 100644
index 0000000..701a215
--- /dev/null
+++ b/doc/developers/rules.rst
@@ -0,0 +1,30 @@
+Rules for developers
+====================
+
+1. Coding style
+---------------
+SemanticScuttle uses the PEAR Coding Standards.
+While quite some parts still do not follow them, all of the
+code will be coverted to them. When developing new code,
+adhere to it.
+
+A helpful tool to check your coding style is PHP CodeSniffer,
+http://pear.php.net/package/PHP_CodeSniffer
+
+
+2. Unit tests
+-------------
+At least the service and model classes have unit tests.
+If you fix things in there, make sure you
+a) do not break the tests or
+b) fix the tests if the old behavior was broken
+
+
+3. Keep security in mind
+------------------------
+As a web application, there are several attack vectors to SemanticScuttle.
+When processing user input (form variables, URL parameters)
+be sure to convert and validate them. If you expect a bookmark id,
+there is no reason not to cast the variable to (int).
+
+Filter input, escape output.
diff --git a/doc/developers/running-unit-tests.rst b/doc/developers/running-unit-tests.rst
new file mode 100644
index 0000000..f75cb81
--- /dev/null
+++ b/doc/developers/running-unit-tests.rst
@@ -0,0 +1,31 @@
+Running unit tests
+==================
+The tests are dependent on the pear packages HTTP_Request2 and Stream_Var.
+
+Install them with: ::
+ $ pear install HTTP_Request2
+ $ pear install Stream_Var
+
+Go to the SemanticScuttle ``tests`` directory and run ``phpunit``::
+
+ $ cd tests
+ $ phpunit .
+
+also remember the ``--verbose`` parameter to PHPUnit.
+
+If you want to run a specific test class only: ::
+
+ $ cd tests
+ $ phpunit BookmarksTest.php
+
+If you need to test one method only: ::
+
+ $ cd tests
+ $ phpunit --filter BookmarkTest::testUnificationOfBookmarks tests/BookmarkTest.php
+
+
+Caveats
+-------
+Having debugging enabled and database driver "``mysql4``" activated
+will lead to failing tests because of ``FOUND_ROWS()`` usage, which
+does not work nicely with database debugging.
diff --git a/doc/developers/translation.rst b/doc/developers/translation.rst
new file mode 100644
index 0000000..008f66e
--- /dev/null
+++ b/doc/developers/translation.rst
@@ -0,0 +1,47 @@
+===========================
+Translating SemanticScuttle
+===========================
+
+SemanticScuttle uses gnu gettext for translation. It does not
+rely on the php extension but ships with a pure php implementation,
+php-gettext_.
+
+Using gettext from within the code is really easy:
+Enclose the string you want to translate in a "``T_``" function call.
+
+For example, to translate::
+
+ echo "Vote for";
+
+just write ::
+
+ echo T_("Vote for");
+
+.. _php-gettext: https://launchpad.net/php-gettext/
+
+Translation basics
+==================
+
+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
+
+After that has been done, the changes to the base ``messages.po`` file
+need to be merged into the single language translation files,
+for example ``data/locales/de_DE/LC_MESSAGES/messages.po``.
+
+Updating them from the master file is as easy as running::
+
+ $ php scripts/update-translation.php de_DE
+
+When the translation is ready, the ``.po`` file needs to be compiled
+in a machine-readable ``.mo`` file. Use ::
+
+ $ php scripts/compile-translation.php de_DE
+
+to achieve that.
+
+
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..678c102
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,50 @@
+=============================
+SemanticScuttle documentation
+=============================
+
+
+First reads
+===========
+- README_
+- `installation guide`_
+- `upgrade instructions`_
+
+.. _README: README.html
+.. _installation guide: INSTALL.html
+.. _upgrade instructions: UPGRADE.html
+
+
+
+Features
+========
+- `Custom user authentication`__
+- `SSL Client certificates`__
+- Themes__
+- `Configuration files`__
+- Tools__
+
+__ authentication.html
+__ ssl-client-certificates.html
+__ themes.html
+__ configuration.html
+__ tools.html
+
+
+
+Developer documentation
+=======================
+- `General development rules`__
+- `Delicious API`__
+- `Debugging HowTo`__
+- `How to release a new version`__
+- `Running unit testes`__
+- `How to translate SemanticScuttle`__
+- `ChangeLog`__
+
+__ developers/rules.html
+__ developers/api.html
+__ developers/debugging.html
+__ developers/release-new-version.html
+__ developers/running-unit-tests.html
+__ developers/translation.html
+__ ChangeLog.html
diff --git a/doc/ssl-client-certificates.rst b/doc/ssl-client-certificates.rst
new file mode 100644
index 0000000..7abf17a
--- /dev/null
+++ b/doc/ssl-client-certificates.rst
@@ -0,0 +1,181 @@
+=======================
+SSL client certificates
+=======================
+
+By using SSL client certificates, you get automatically logged into
+SemanticScuttle without using a password or submitting a login form.
+
+Any number of certificates can be registered with a user account,
+making it easy to login to the same installation from home and from
+work - without risking to use the same certificate on both machines.
+
+
+Usage scenarios
+===============
+The scenarios assume that the web server is configured_ properly
+to request client certificates.
+
+.. _configured: `Configuring your web server`_
+
+
+Registering a certificate with an existing account
+--------------------------------------------------
+You already have an account and want to associate a SSL client certificate
+with it.
+
+1. Visit your profile page
+2. Click "Register current certificate to automatically login."
+
+That's it. Now logout and click "Home". You will be logged in again
+automatically.
+
+
+Registering a certificate with a new account
+--------------------------------------------
+When you do not have an user account yet, just visit the registration
+page. Your email address will already be filled in, using the information
+from the SSL certificate.
+
+Provide the rest of the data and submit the form.
+The certificate will automatically be associated to your account,
+and the user name will also be set for you.
+
+
+
+Configuring your web server
+===========================
+
+Getting SSL certificates
+------------------------
+You need both a server certificate for normal HTTPS mode, as well as a client
+certificate in your browser.
+
+CAcert.org is a good place to obtain both.
+You are of course free to generate your own certificate with i.e. openssl
+or buy a certificate from another certificate authority, but this is out
+of this document's scope.
+
+Server certificate
+''''''''''''''''''
+First, generate a Certificate Signing Request with the `CSR generator`__.
+Store the key file under ::
+
+ /etc/ssl/private/bookmarks.cweiske.de.key
+
+Use the the .csr file and the CAcert web interface to generate a signed
+certificate. Store it as ::
+
+ /etc/ssl/private/bookmarks.cweiske.de-cacert.pem
+
+Now fetch both official CAcert certificates (root and class 3) and put both
+together into ::
+
+ /etc/ssl/private/cacert-1and3.crt
+
+.. _CSR: http://wiki.cacert.org/CSRGenerator
+__ CSR_
+
+
+Client certificate
+''''''''''''''''''
+CAcert also offers to create client certificates. You do not need a
+certificate sign request but just can create it on their web page.
+
+After creation, you can simply install it in your browser by clicking
+on the appropriate link on the CAcert page.
+
+Once you got the certificate installed in your browser, you can transfer
+it to other browsers by exporting it in the `PKCS #12` format
+(with private key included) and importing it in any other browsers
+you use.
+
+
+
+Apache configuration
+--------------------
+To make use of SSL client certificates, you need to deliver SemanticScuttle
+via HTTPS.
+
+Note that you can equip several virtual hosts with SSL certificates
+and keep them on the same, standard SSL port by using SNI -
+`Server Name Indication`__.
+
+.. _SNI: http://wiki.apache.org/httpd/NameBasedSSLVHostsWithSNI
+__ SNI_
+
+A basic virtual host configuration with SSL looks like this:
+
+::
+
+ <VirtualHost *:443>
+ ServerName bookmarks.cweiske.de
+
+ LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
+ CustomLog /var/log/apache2/access_log vcommon
+
+ VirtualDocumentRoot /home/cweiske/Dev/html/hosts/bookmarks.cweiske.de
+ <Directory "/home/cweiske/Dev/html/hosts/bookmarks.cweiske.de">
+ AllowOverride all
+ </Directory>
+
+ SSLEngine On
+ SSLCertificateFile /etc/ssl/private/bookmarks.cweiske.de-cacert.pem
+ SSLCertificateKeyFile /etc/ssl/private/bookmarks.cweiske.de.key
+
+ SSLCACertificateFile /etc/ssl/private/cacert-1and3.crt
+ </VirtualHost>
+
+Apart from that, you might need to enable the SSL module in your webserver,
+i.e. by executing ::
+
+ $ a2enmod ssl
+
+
+Now that SSL works, you can configure your web server to request client
+certificates.
+
+::
+
+ ...
+ </Directory>
+
+ SSLVerifyClient optional
+ SSLVerifyDepth 1
+ SSLOptions +StdEnvVars
+ </VirtualHost>
+
+There are several options you need to set:
+
+
+``SSLVerifyClient optional``
+ You may choose ``optional`` or ``required`` here.
+ ``optional`` asks the browser for a client certificate but accepts
+ if the browser (the user) does choose not to send any certificate.
+ This is the best option if you want to be able to login with and
+ without a certificate.
+
+ Setting ``required`` makes the web server terminate the connection
+ when no client certificate is sent by the browser.
+ This option may be used when all users have their client certificate set.
+
+``SSLVerifyDepth 1``
+ Your client certificate is signed by a certificate authority (CA),
+ and your web server trusts the CA specified in ``SSLCACertificateFile``.
+ CA certificates itself may be signed by another authority, i.e. like ::
+
+ CAcert >> your own CA >> your client certificate
+
+ In this case, you have a higher depth. For most cases, 1 is enough.
+
+``SSLOptions +StdEnvVars``
+ This makes your web server pass the SSL environment variables to PHP,
+ so that SemanticScuttle can detect that a client certificate is available
+ and read its data.
+
+ In case you need the complete certificate
+ \- which SemanticScuttle does *not* need - you have to add ``+ExportCertData``
+ to the line.
+
+
+That's it. Restart your web server and visit your SemanticScuttle installation.
+Continue reading the `Usage scenarios`_.
diff --git a/doc/themes.rst b/doc/themes.rst
new file mode 100644
index 0000000..895f1a0
--- /dev/null
+++ b/doc/themes.rst
@@ -0,0 +1,58 @@
+======================
+SemanticScuttle Themes
+======================
+SemanticScuttle may be changed visually by supplying custom "themes" (skins)
+that modify the visual appearance.
+
+
+Changing the current theme
+==========================
+In ``data/config.php``, set your theme like this: ::
+
+ $theme = 'darkmood';
+
+The available themes are the folders in ``www/themes/``.
+By default, SemanticScuttle ships only one usable theme ("default") and one
+to demonstrate how to create your own theme ("testdummy").
+
+
+Creating your own theme
+=======================
+Have a look at the "testdummy" theme in ``www/themes/testdummy/``.
+
+CSS and image files
+-------------------
+Since both file types need to be accessible via the web server directly,
+they are located in the ``www/`` folder: ::
+
+ www/themes/$themename/
+
+The main CSS file that automatically gets included is ::
+
+ www/themes/$themename/scuttle.css
+
+Several template files in SemanticScuttle include image files. If they do not
+exist in your theme, the default ones are used automatically.
+Note that this is not true for images that are specified in the CSS files.
+
+
+Template files
+--------------
+The templates of the default file are located in ::
+
+ data/templates/default/
+
+You may put your theme template files into ::
+
+ data/templates/$themename/
+
+
+Existing themes
+===============
+
+* `Flat`__ by Roman Lehnhof. See the `introduction blog post`__
+* `sscuttlizr`__
+
+__ https://github.com/rlehnhof/flat
+__ http://lehnhof.net/2013/08/semantic-scuttle-new-flat-interface-theme-template/
+__ https://github.com/jonrandoem/sscuttlizr
diff --git a/doc/tools.rst b/doc/tools.rst
new file mode 100644
index 0000000..3c3ad04
--- /dev/null
+++ b/doc/tools.rst
@@ -0,0 +1,44 @@
+=================================
+Tools to use with SemanticScuttle
+=================================
+
+Scuttleoid
+==========
+Scuttloid is an android client for managing your bookmarks that are
+stored on a Semantic Scuttle server.
+It allows to list/search your personal bookmarks, add and edit existing
+bookmarks, and share them to other applications.
+
+Installable via `f-droid`__.
+Also see the `source code`__.
+
+__ https://f-droid.org/repository/browse/?fdid=gr.ndre.scuttloid
+__ https://github.com/ilesinge/scuttloid
+
+
+Scuttle for Android
+===================
+Scuttle for Android is a client for the Scuttle bookmarking software.
+Enter your Scuttle login information and you will presented with a list of your
+bookmarks.
+Touching a bookmark will load that web page in the browser.
+Use the browser's "Share" menu option to add bookmarks to your Scuttle.
+
+See the homepage__ and `source code`__.
+
+__ http://slideme.org/application/scuttle-android
+__ https://github.com/shadybrooksoftware/Scuttle-For-Android
+
+
+ttrss2scuttle
+=============
+Announced in a `blog post`__, the plugin for the feed reader `Tiny Tiny RSS`__
+allows you to bookmark any of the articles in your own SemanticScuttle
+instance.
+
+Source code and installation instructions are on the
+`ttrss2scuttle github page`__.
+
+__ http://www.versvs.net/anotacion/plugin-tiny-tiny-rss-semanticscuttle
+__ http://tt-rss.org/
+__ https://github.com/versvs/ttrss2scuttle
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 = <a href="http://sourceforge.net/projects/semanticscuttle"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=211356&amp;type=10" width="80" height="15" alt="Get SemanticScuttle at SourceForge.net. Fast, secure and Free Open Source software downloads" /></a>
diff --git a/kvmxfile b/kvmxfile
index 7ab8cef..2a0cdcb 100644
--- a/kvmxfile
+++ b/kvmxfile
@@ -2,30 +2,90 @@
# Sample kvmx file - https://kvmx.fluxo.info
#
+# Hostname
+hostname="links"
+
# Which base box you should use. Leave unconfigured to use kvmx-create instead.
-#basebox="stretch"
+#basebox="buster"
+basebox="dev"
# First user name
-#user="vagrant"
user="user"
# First user password
-#password="vagrant"
-password="user"
+password="`head -c 40 /dev/urandom | base64`"
+
+# Networking
+#net="tap"
+net="user"
+
+# DNS config
+#net_dns="192.168.1.1"
+#net_dns="host" # copy /etc/resolv.conf from host when creating the box
+
+# Networking: tap config only
+# This setting is used during virtual machine bootstrapping by kvmx-create.
+#net_ip="10.1.1.2"
+#net_mask="255.255.255.0"
+#net_gateway="10.1.1.1"
-# Set this is you want to be able to share folders between host and guest.
+# Set this is you want to be able to share a single folder between host and guest.
+# Needs ssh_support set to "y" and a workable SSH connection to the guest.
shared_folder="."
+#shared_folder_mountpoint="/home/$user/code/$VM"
+#shared_folder="$HOME/temp/shared/$VM"
+#shared_folder_mountpoint="/home/$user/temp/shared/$VM"
+shared_folder_mountpoint="/srv/shared"
+#shared_folder_mountpoint="/srv/kvmx"
#shared_folder_mountpoint="/vagrant"
-shared_folder_mountpoint="/srv/kvmx"
-# Folder to sync during provisioning in the format "/host/folder /guest/folder".
-provision_rsync="puppet /etc/puppet"
+# Set this is you want to be able to share multiple folders between host and guest using 9p.
+# Needs ssh_support set to "y" and a workable SSH connection to the guest.
+# Format: <id1>:<host-folder1>:<guest-mountpoint1>,<id2>:<host-folder2>:<guest-mountpoint2>[,...]
+#shared_folders="shared1:.:/home/$user/code/$VM,shared2:$HOME/.local/share/app:/home/$user/.local/share/app"
+
+# Maximum packet size including any headers for shared folders using 9p
+# See https://forums.lime-technology.com/topic/34691-9p-sharing-speed-not-what-i-expected/
+# https://github.com/clearcontainers/hyperstart/pull/25
+#shared_folders_msize="524288"
+
+# Shared folders caching
+# See https://www.kernel.org/doc/Documentation/filesystems/9p.txt
+#shared_folders_cache="none"
+
+# Set this is you want to be able to share multiple folders between host and guest using SSHFS.
+# Needs ssh_support set to "y" and a workable SSH connection to the guest.
+# Format: <id1>:<guest-folder1>:<host-mountpoint1>,<id2>:<guest-folder2>:<host-mountpoint2>[,...]
+#shared_folders_sshfs="shared1:/home/$user/code/$VM:.,shared2:/home/$user/.local/share/app:$HOME/.local/share/app"
+
+# Folder to sync during provisioning in the format "/host/folder1 /guest/folder1,/host/folder2 /guest/folder2[,...]".
+# Needs ssh_support set to "y" and a workable SSH connection to the guest.
+#provision_rsync="$KVMX_BASE/share/provision/ /usr/local/share/kvmx/provision/"
+#provision_rsync="puppet/ /etc/puppet/"
# Options for provision_rsync
-provision_rsync_opts="--exclude=.git --exclude=keys --exclude=config/secrets"
+#provision_rsync_opts="--exclude=somefolder"
# Absolute path for a provision script located inside the guest.
-provision_command="/etc/puppet/bin/provision && /etc/puppet/bin/deploy"
+# Needs ssh_support set to "y" and a workable SSH connection to the guest.
+#provision_command="sudo apt-get update && sudo apt-get dist-upgrade -y && sudo apt-get autoremove -y && sudo apt-get clean"
+#provision_command="/usr/local/share/kvmx/provision/development && /etc/puppet/bin/provision && /etc/puppet/bin/deploy"
+#provision_command="/usr/local/share/kvmx/provision/development && /etc/puppet/bin/deploy"
+#provision_command="/usr/local/share/kvmx/provision/development && /home/$user/code/$VM/bin/custom-provisioner"
+provision_command="/usr/local/share/kvmx/provision/development"
+#provision_command="/usr/local/share/kvmx/provision/trashman"
+#provision_command="/usr/local/share/kvmx/provision/desktop-basic"
+
+# Startup command
+#startup_command="/path/to/custom/command"
+#startup_command="hydractl aperiodic-upgrade"
+
+# Pre-poweroff command
+#poweroff_pre_command="/path/to/custom/command"
+
+# Rsync commands to be run on startup or poweroff, useful to keep files synced between host and guest
+#startup_rsync_to_guest="data:data:/var/data,code:$HOME/code:/home/user/code"
+#poweroff_rsync_from_guest="data:/var/data:data,code:/home/user/code:$HOME/code"
# Graphics
# See https://wiki.archlinux.org/index.php/QEMU#Graphics
@@ -36,18 +96,60 @@ graphics="-vga qxl"
#vnc_client="xtightvncviewer"
#vnc_client="xvnc4viewer"
#vnc_client="xvncviewer"
+#vnc_client="vncviewer"
vnc_client="virt-viewer"
-# Set this if you want to automatically attach an spice client when the machine
-# boots.
+# SPICE support
+spice="1"
+
+# Set this if you want to attach an spice client when the machine boots.
run_spice_client="0"
+# SPICE client
+#spice_client="spicec"
+#spice_client="virt-viewer"
+spice_client="spicy"
+
+# Set this if you want to start an xpra session when the machine boots.
+run_xpra="0"
+
+# Set this if you want to start an xephyr session when the machine boots.
+run_xephyr="0"
+
+# Set this if you want kvmx to redimension the guest screen according to host's screen dimension
+# Requires ssh_support and xrandr installed on both host and guest
+xrandr="0"
+
+# Xrandr device
+#xrandr_device="qxl-0"
+#xrandr_device="Virtual-0"
+
+# Set screen resolution
+#resolution="1280x785"
+
+# Sound
+#sound="0"
+#sound="ac97"
+
# Set additional hostfwd mappings
#port_mapping="hostfwd=tcp:127.0.0.1:8080-:80,hostfwd=tcp:127.0.0.1:8443-:443"
# Where the guest image is stored
#image="$HOME/.local/share/kvmx/$VM/box.img"
+# Use basebox image as a backing file for overlay images
+# See https://wiki.archlinux.org/index.php/QEMU#Overlay_storage_images
+#backing_file="1"
+
+# Where datafiles are stored: just set this if you know what you're doing
+#datadir="$HOME/.local/share/kvmx/$VM"
+
+# Drive type: use this if you want to run a Live CD/DVD/USB
+#image_drive="cdrom"
+
+# Image type: file or device (kvmx-create only)
+image_type="file"
+
# Image size
size="10G"
@@ -57,15 +159,9 @@ format="qcow2"
# Image compression (qcow2 only)
qcow2_compression="1"
-# Memory
-memory="512"
-
# Bootstrap method: custom or vmdeboostrap
method="custom"
-# Hostname
-hostname="puppet"
-
# Domain
domain="example.org"
@@ -73,10 +169,13 @@ domain="example.org"
arch="amd64"
# Box distribution when bootstraping a new image
-version="stretch"
+version="buster"
# Debian mirror
-mirror="http://http.debian.net/debian/"
+mirror="https://deb.debian.org/debian/"
+
+# Memory
+memory="2048"
# Enables remote administration using SSH. With this configuration enabled,
# kvmx will be able to administer a running virtual machine using SSH access
@@ -94,5 +193,50 @@ ssh_support="y"
# This setting is used during virtual machine bootstrapping by kvmx-create.
ssh_custom="y"
+# Use this config if you want kvmx-create to include a specif SSH pubkey
+# It might be a path for an existing pubkey file or the public key itself.
+#
+# Make sure to have this key available when trying to SSH into the guest using kvmx
+#ssh_custom_pubkey="/home/myhostuser/.ssh/id_rsa.pub"
+#ssh_custom_pubkey="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDVFmJ2UDn2dK3nHHAkHzt8F4AfrXTFiGa+guem92S/pztMGAUDjEZBYEW3mZ8ATyo4GoPZ92tkjmra0Fgv6ETIox+SPWbzzjhzbv2CQUTWvF6PEVwJbT1PTzaIVRiDYf+yX7e3Y8HcmkAi60Cxs5Xr3HLkqdi2jYKFofCm58R+HGnRv1WSurPnf7C7KQBSW7E1S2CafW9VFHhGCzezyThjN+n3bJjgYFzPxdTlWyfW1T7Yv61/fqfuara0kpZx1l5pblpgbCTT7WKRIhwj1x0QTo/qDQ6k52tffiCVyMGJKvires9yp5qT5Y+ldssBKDa8muRF/dh7/UCyxvcm3HTDjWG24Sr4r9JWbhkqF89UePlOw5j73qw4gzT7YQ38tzz2XI5weAL1OXM6qhCbOwfPXwYbB5xM4g62WZugtcCLan6Iy8hvoiRIJ1MU2ar73wunghQQ84oNIrEVezJsuZxwxVbe45ulnM7x4Hqeu6jmOutWFkdkAHsqd1E3zTOS1RURwi0TpnD+iWwD7FOA9c8B6AWP4i9XVW6BLi1waARrS3bVnOh3djc20fVsClfEDDXFg2KiTeQaAWfjLguyUmxysSiUC2pnibd1bEDtdfPlkA2jaE2nAn6Tw7Vp5zd8P1d1trLMx3YkRq5uQSPqnfQDKoYH5FPMlNTbMINLC56ijw== user@kvmx"
+
# Bootloader (used only during bootstrapping by kvmx-create).
bootloader="grub"
+
+# Drive interface
+#drive_interface="ide" # Needed by some systems like NetBSD and FreeBSD
+#drive_interface="virtio,cache=none,index=0,format=raw" # For raw devices
+
+# See http://www.reactos.org/wiki/QEMU#Setting_up_network
+#nic_model="ne2k_pci"
+
+# USB support
+#
+# Allowed values:
+#
+# 0 - No USB support
+# 1 - Support for USB 1.0
+# 2 - Support for USB 1.0 and 2.0
+# 3 - Support for USB 1.0, 2.0 and 3.0
+#
+#usb="0"
+
+# Additional qemu opts
+# Example: http://www.linux-kvm.org/page/USB_Host_Device_Assigned_to_Guest
+# See also: https://qemu.readthedocs.io/en/latest/system/usb.html
+# https://wiki.gentoo.org/wiki/QEMU/Windows_guest
+#qemu_opts="-device usb-host,hostbus=2,hostaddr=3" # Automatically attach an specific USB device
+#qemu_opts=""
+
+# Number of CPUs
+#smp="4"
+
+# Enviroment passed to SSH commands
+#ssh_env="TERM=xterm"
+
+# Whether to be managed by kvmx-supervise
+#supervise_manage="1"
+
+# VirtIO RNG parameters
+# See https://wiki.qemu.org/Features/VirtIORNG
+#virtio_rng="max-bytes=128,period=1000"
diff --git a/puppet/.gitignore b/puppet/.gitignore
new file mode 100644
index 0000000..15a8f56
--- /dev/null
+++ b/puppet/.gitignore
@@ -0,0 +1,3 @@
+keys
+ssl
+.vagrant
diff --git a/.mrconfig b/puppet/.mrconfig
index 06755dd..06755dd 100644
--- a/.mrconfig
+++ b/puppet/.mrconfig
diff --git a/LICENSE b/puppet/LICENSE
index dba13ed..dba13ed 100644
--- a/LICENSE
+++ b/puppet/LICENSE
diff --git a/Makefile b/puppet/Makefile
index 8d77330..8d77330 100644
--- a/Makefile
+++ b/puppet/Makefile
diff --git a/README.md b/puppet/README.md
index 9ab8d84..9ab8d84 100644
--- a/README.md
+++ b/puppet/README.md
diff --git a/TODO.md b/puppet/TODO.md
index 650dcf9..650dcf9 100644
--- a/TODO.md
+++ b/puppet/TODO.md
diff --git a/Vagrantfile b/puppet/Vagrantfile
index b5cd7f6..b5cd7f6 100644
--- a/Vagrantfile
+++ b/puppet/Vagrantfile
diff --git a/bin/copymodules b/puppet/bin/copymodules
index ab5b405..ab5b405 100755
--- a/bin/copymodules
+++ b/puppet/bin/copymodules
diff --git a/bin/dependencies b/puppet/bin/dependencies
index 3a47a8e..3a47a8e 100755
--- a/bin/dependencies
+++ b/puppet/bin/dependencies
diff --git a/bin/deploy b/puppet/bin/deploy
index f23bc42..f23bc42 100755
--- a/bin/deploy
+++ b/puppet/bin/deploy
diff --git a/bin/mrconfig b/puppet/bin/mrconfig
index 41be968..41be968 100755
--- a/bin/mrconfig
+++ b/puppet/bin/mrconfig
diff --git a/bin/post-receive b/puppet/bin/post-receive
index e6baa07..e6baa07 100755
--- a/bin/post-receive
+++ b/puppet/bin/post-receive
diff --git a/bin/provision b/puppet/bin/provision
index 99cb862..99cb862 100755
--- a/bin/provision
+++ b/puppet/bin/provision
diff --git a/bin/submodules b/puppet/bin/submodules
index 960d290..960d290 100755
--- a/bin/submodules
+++ b/puppet/bin/submodules
diff --git a/bin/subtrees b/puppet/bin/subtrees
index 19211f1..19211f1 100755
--- a/bin/subtrees
+++ b/puppet/bin/subtrees
diff --git a/bin/symlinks b/puppet/bin/symlinks
index a2f27cf..a2f27cf 100755
--- a/bin/symlinks
+++ b/puppet/bin/symlinks
diff --git a/bin/syncmodules b/puppet/bin/syncmodules
index 1585aa4..1585aa4 100755
--- a/bin/syncmodules
+++ b/puppet/bin/syncmodules
diff --git a/config/common.yaml b/puppet/config/common.yaml
index 936420d..936420d 100644
--- a/config/common.yaml
+++ b/puppet/config/common.yaml
diff --git a/config/hiera.yaml b/puppet/config/hiera.yaml
index 14e393d..14e393d 100644
--- a/config/hiera.yaml
+++ b/puppet/config/hiera.yaml
diff --git a/config/node/box.example.org.yaml b/puppet/config/node/box.example.org.yaml
index 657bce1..657bce1 100644
--- a/config/node/box.example.org.yaml
+++ b/puppet/config/node/box.example.org.yaml
diff --git a/files/.empty b/puppet/files/.empty
index e69de29..e69de29 100644
--- a/files/.empty
+++ b/puppet/files/.empty
diff --git a/files/patches/trusty/puppet-stack-level.md b/puppet/files/patches/trusty/puppet-stack-level.md
index 9a3f4d7..9a3f4d7 100644
--- a/files/patches/trusty/puppet-stack-level.md
+++ b/puppet/files/patches/trusty/puppet-stack-level.md
diff --git a/files/patches/trusty/puppet-stack-level.patch b/puppet/files/patches/trusty/puppet-stack-level.patch
index 1d112f7..1d112f7 100644
--- a/files/patches/trusty/puppet-stack-level.patch
+++ b/puppet/files/patches/trusty/puppet-stack-level.patch
diff --git a/files/patches/wheezy/collected-resources.md b/puppet/files/patches/wheezy/collected-resources.md
index b2ce77d..b2ce77d 100644
--- a/files/patches/wheezy/collected-resources.md
+++ b/puppet/files/patches/wheezy/collected-resources.md
diff --git a/hiera.yaml b/puppet/hiera.yaml
index 31df718..31df718 120000
--- a/hiera.yaml
+++ b/puppet/hiera.yaml
diff --git a/puppet/kvmxfile b/puppet/kvmxfile
new file mode 100644
index 0000000..7ab8cef
--- /dev/null
+++ b/puppet/kvmxfile
@@ -0,0 +1,98 @@
+#
+# Sample kvmx file - https://kvmx.fluxo.info
+#
+
+# Which base box you should use. Leave unconfigured to use kvmx-create instead.
+#basebox="stretch"
+
+# First user name
+#user="vagrant"
+user="user"
+
+# First user password
+#password="vagrant"
+password="user"
+
+# Set this is you want to be able to share folders between host and guest.
+shared_folder="."
+#shared_folder_mountpoint="/vagrant"
+shared_folder_mountpoint="/srv/kvmx"
+
+# Folder to sync during provisioning in the format "/host/folder /guest/folder".
+provision_rsync="puppet /etc/puppet"
+
+# Options for provision_rsync
+provision_rsync_opts="--exclude=.git --exclude=keys --exclude=config/secrets"
+
+# Absolute path for a provision script located inside the guest.
+provision_command="/etc/puppet/bin/provision && /etc/puppet/bin/deploy"
+
+# Graphics
+# See https://wiki.archlinux.org/index.php/QEMU#Graphics
+#graphics="-vga std -nographic -vnc :$GUEST_DISPLAY"
+graphics="-vga qxl"
+
+# VNC Client
+#vnc_client="xtightvncviewer"
+#vnc_client="xvnc4viewer"
+#vnc_client="xvncviewer"
+vnc_client="virt-viewer"
+
+# Set this if you want to automatically attach an spice client when the machine
+# boots.
+run_spice_client="0"
+
+# Set additional hostfwd mappings
+#port_mapping="hostfwd=tcp:127.0.0.1:8080-:80,hostfwd=tcp:127.0.0.1:8443-:443"
+
+# Where the guest image is stored
+#image="$HOME/.local/share/kvmx/$VM/box.img"
+
+# Image size
+size="10G"
+
+# Image format: raw or qcow2
+format="qcow2"
+
+# Image compression (qcow2 only)
+qcow2_compression="1"
+
+# Memory
+memory="512"
+
+# Bootstrap method: custom or vmdeboostrap
+method="custom"
+
+# Hostname
+hostname="puppet"
+
+# Domain
+domain="example.org"
+
+# System arch
+arch="amd64"
+
+# Box distribution when bootstraping a new image
+version="stretch"
+
+# Debian mirror
+mirror="http://http.debian.net/debian/"
+
+# Enables remote administration using SSH. With this configuration enabled,
+# kvmx will be able to administer a running virtual machine using SSH access
+# inside the virtual machine.
+ssh_support="y"
+
+# Use a custom, per-virtual-machine generated SSH keypair. If you disable this
+# configuration but still want guest administration using SSH, the default
+# insecure keypair will be used.
+#
+# Please note that this setting won't take effect if you're using a basebox.
+# In that case the basebox keypair will be used if it exists, otherwise kvmx
+# fallsback to the default insecure keypair.
+#
+# This setting is used during virtual machine bootstrapping by kvmx-create.
+ssh_custom="y"
+
+# Bootloader (used only during bootstrapping by kvmx-create).
+bootloader="grub"
diff --git a/manifests/nodes/.empty b/puppet/manifests/nodes/.empty
index e69de29..e69de29 100644
--- a/manifests/nodes/.empty
+++ b/puppet/manifests/nodes/.empty
diff --git a/manifests/nodes/default.pp b/puppet/manifests/nodes/default.pp
index 5ebbf90..5ebbf90 100644
--- a/manifests/nodes/default.pp
+++ b/puppet/manifests/nodes/default.pp
diff --git a/modules/.empty b/puppet/modules/.empty
index e69de29..e69de29 100644
--- a/modules/.empty
+++ b/puppet/modules/.empty
diff --git a/puppet b/puppet/puppet
index 945c9b4..945c9b4 120000
--- a/puppet
+++ b/puppet/puppet
diff --git a/res/docs/header.tpl.html b/res/docs/header.tpl.html
new file mode 100644
index 0000000..ee0650f
--- /dev/null
+++ b/res/docs/header.tpl.html
@@ -0,0 +1,8 @@
+<ul class="menu">
+ <li><a href="index.html">Index</a></li>
+ <li><a href="INSTALL.html">Installation</a></li>
+ <li><a href="UPGRADE.html">Upgrade</a></li>
+ <li><a href="ChangeLog.html">ChangeLog</a></li>
+ <li><a href="http://semanticscuttle.sourceforget.net/">Homepage</a></li>
+ <li><a href="https://sourceforget.net/projects/semanticscuttle">Project page</a></li>
+</ul>
diff --git a/res/docs/style.css b/res/docs/style.css
new file mode 100644
index 0000000..90a3fbc
--- /dev/null
+++ b/res/docs/style.css
@@ -0,0 +1,28 @@
+/* SemanticScuttle improvements */
+.document {
+ max-width: 100ex;
+}
+h1.title {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAsvSURBVGiBxZprjCRXdcd/p6r6Nd0979mHZ+3sCkfx4AdJsAhGm2BMYhAbrAgsEimKjGIlxELk8SkrQoQiSGgrCSJZh9hECsQ8hJCQlRDjYDsY4RAvRF6b9drN2t7HzOzOzmtnpl/V1fW4Jx+qu6emZ3pmbI/kI7Wq+9Ste///87j33FstqspeS7kkR4HPAAeAx4G/mDqu9T0fCJC9JlAuyXuB72Ymbklnxt9K/fxjmFblf4A7p45rc08HA6y97KxcksPAI5mJW9JDN99DbvI2Rt7+CSSVPwo8tJdjdWRPCQAPWZmhYuHn70LwyeQFOzvC4C/cDfB75ZL85h6Pt3cEyiW5B7gzf+T9ABRGM+SHHFIZQ3p8isz4TajyYLkkQ3s1JuwRgXJJ9gGfT49NkSpeQzYvpDMKxic/kkLUp3D9MaxUbhL4u70YsyN75YH7xcmN5q75FUQCCqNp0BaYFraE5IdTiJUmf+ROVLm3XJLb9mjcN06gXJJ3Avdk9/0iqDK0L4slAZgA1Af1yeaFVMaQGXsrqeKkGOWBckn2xHhvqJNySQQ4YWVHxRk8RCYvZHICptX1ACYmURhLo8Zj4OfegyXyy8C9bzoB4GPArZmxKYg8hvbn2lYP2sCDLgnHMQwMOtjZETLjN2IMnyuXZORNI1AuyU3A553CJOJkKY5ncZxog9W7V/XBtOKExid78Fbs9MCYUf71TSHQBv8tSeVzTnESJxVSnMgkgLfWwyhBxCJgcF8WEPJxKP1WuSSfLZdk8PUS2FUpUS7JAeCdwG3AMeBGsbOkhg5jpXNM3nQd6YEsiAU48VVsoH0VC7Db323WrtRorHr4Ky/TWnwejVot4HvAD4BngOemjmvrdRMol+Qg8OvAbwC/ChwGASuFlcphpQqIk0XEZuL6/QzuH2kD7AVub6lXtVi6sELgGRAhbMwTVGeJGgsYvwrgA6eAp4iLwf+dOq7+tgTKJbkF+Ggb9E3xbQssG7EcxE6D2IjYWKkU+fFBhg+MkhvO94DcgUj7atSisdKkWfXbRGzEstEoIGouE7qLhLU5ouYSQAP4IfBd4OGp41rtEnjpc+SBh4EPbe0kC7EtcsMFsoMFBkYK5EeKWI5DMiz6hctu9IGvePWAoBkReBFhAGK125mAsD5PULtEsHYOjVoV4ONTx/XrHQL/DPzRBrdYwsDwAPmRPAOjA2QLWcSSra2btHI//WsIL8TGRELLDWm5AX4jJAxBxAYUb/4U3sIpBd4+dVyfc4APdIAXxguMXjtKbjiHZW01QUWg7Y9YoB0gUft7r5UT+k7bThttA07q220tyyZXsMgVB0AswgBa9YD6VZfMvrfhLZwS4H3Acw5wBbgOIFvMkh/Nbx1JG8SAGiBMAEiAwtqlPtqsJ6mPDeA4NtagTW2xhb86DcBilaWp+ElOdGAtX1ymsdLYBYFer/QuWtuvBxv0Znf6lZkrBG4Fb/5ZWgGLv/sgMyIybgHfaIWcBEDh8guX8Ztbzli7INIuHbpg2wWdWS8pNul7wXafXddXrizjXl2jefkkGrp8v8wDl1fxgWtFVbnvDvmlj93B4xmHcYBMPsPhWw9jpTKQmoD0frAH25177U8LWpdiXV/pJKnD5tlod/pGxWPp3ApB5SJhbZZXF/jWXV/gi+0BPAfgwac4m0nxl39wO/9oWU4qdeiDVPQwwweuRTqdqqGbxGisswvgnoX6s+DP9/FKlIjpZPJaINH66r2FvtWIWHplkdBdIazNslLn2d/+Ig8mBlhxAFTVFZHv33CQv3/3DeHxsDKNHjpKo+pQGMkCJgathvUENhDWYw9N3A2RC9WT4L60BZH2MxomQDqsz1JRj/UjAs9w5cV5Qq9BUJnGC7jyx1/jr5o+Zt06TCfnyvP3/RuPlOf4qjf/f9TPPUqr5uKu1duDR20gHasaEI0BRY04pIbfCxO/E3tmS9GYxIaE3Vy1Rn6TuTMXCRprBJUZwkgbJ57gk6emqSY6e0VV/S4BVQ2BMx8+wZcurfBfjYtP0Jw7ibtap9VorpPoeiFK6NqhEC6DpODgH0L+5j4kkuGVTPo4adW0mDtzkVa1QtiYx6gJvvljPvnlp7mQePiSqi5ATzmtqi5Q/tAJ/na5xo9rZx8hqFygtrhGFAY9edDxSoIIGs8e3kzbGx8Ba6d1xazPSuqzfH6OxtU1Im8VNZF54gx//Tff4aeJB1aB850fm5ZbVb1a9zh3z5f4tOub2erZb2OCJtWFVbTXA0SgGv/uxnk7X7yLYGXhmvtg6NdAMjsQUZprNZbPz6GBCxpx5jJf/rNv8INEIxd4SRMl9JYbGlWdvrDMxa88zWej5mrUuPgkgesTND02JHHXI4ZNeYKBYAXcn8XhdOhPYfg9YOX6Urg6c7VrkEqTFz/6L3w9cdsHXmiHele225G9/MCTPLNc5ydB7TKqESYMEiAT4DeFVfJ+CM2XYyK5G2DyT+LwckY3DRj669gef4Gv9Mw4L6iq1/uM0w+9qqqIvCTgYhIzT8f6vSFDMg96pls1ENXiKVYccEZg7FgcVurH9/wl0qOD+P4FIneReovOQbACL6pufbq97Z5YVaPhAdZQAyaKc6BfyCRnpU0eSuiNB/5lcF+BxumYlHcJALt4mIHrbgfAGDpxPqOqq/0w9vVAt4HNsDgZVCNEeuM/4YEu4J6k3tCm10OJtlEL9eYIKi4gjBd1kHgnNr0tvp0IAAcklQcTYdusg9guZDbptgK/+b6dEtTEZcdYIRwFmrrDqcOuCFhODtUIy5adwe8EuGv5iI3eM9iOgEaIZTM0EI62B9tWtiVQLkkKGBUnBxph2dtZOxE+m0gpuwkv26HtAYtChlF2cW61kwdGABE7jYhB+gF+LR7YcN9sIG070ClLBjKMA+k3SqAI8QmBSD/rbgTsey2aqw3ctRqRH5DOp8gP58gW03EObUkq7ltsUDWIWKQsBvaMAGLH1t9g1XUPGBPhrtSoLVUJ3BaqBtNaw/gNAncI92oVMORHcwzuK5IZcLYIL4NldQwBjr2HBERsxNq8iEVBQG2pSn25hgkCTFAnqM4QuUto1DkZFKx0ATs3Ri0coba4RibvMLS/SH4kF3u23a9lxTmgqjgWOUBExOktH14LgWx86cR63LlXaVBfqeFVXTSKCN0Fgso0xlvBGPzFKj86fYmny3PM3DGltx3ZV7u94NfeQnUWOzuK8YfxKnVsRyiO5ylMDJDJOSCdRdEQbwUBsIHXTWAJwAQNAs9nZfYqzaqL8QNM6BJUZwmrs2jk4Ycsn73Cv3/he3znmXOsdTp46CleBb7658e45f03R3fvG1w6Ks0lS1J5onSRVa/Jyqwhk0/hZKx4xTchrYCVBIG+shOBl4FaUJku2rkx6o0KUXM5PohtLqOqutrg1LMXefTTj/DDNbe/pe5/lNP3P8rpY29j/73v5oNHJhrvywSNiQgLK53HCzLxwa+JX46sNji3G4w7Hq+XS/IJVf5BBOnoWgGL5xZ57OEf8dh/PMdWu/mO+MQbkByw4R1ALo31qbt4x7uu5wMTg7zLknWgfsjyp77Nx//zeRaA09vVQrt6P/CZD8vv33CQo65P9acznP2n/+ZnftgtdXtFgWVgTlW7oSQiGeL/TlxDz+xy6xEGP/IObj48zlu8APeBJ3niJ+eptG8/r6oV+siuCIjIjRCfGW0jIfEx5WXV/i8nRESACWCSHq/0kZPb9bebWghgkf4E1oB5YElV+3mlK+3ibBFYFJECsVf298Gyth142KUHAERkEjgEpIA6cWwvbLVLeq0iIhYwRuyZQeKZZxV4VXXboz/+HwP+91Qbpr+OAAAAAElFTkSuQmCC");
+ background-repeat: no-repeat;
+ padding-left: 64px;
+ padding-top: 0.2em;
+ margin-bottom: 00px;
+ height: 60px;
+ border-bottom: 3px solid #666;
+}
+h2 {
+ margin-top: 1.5em;
+
+}
+pre {
+ padding: 1em;
+ background-color: #EEE;
+ border: 1px solid #BBB;
+ width: auto;
+}
+
+tt, code {
+ background-color: #DDD;
+ padding: 0.2ex;
+}
diff --git a/scripts/avahi-export.php b/scripts/avahi-export.php
new file mode 100644
index 0000000..f609949
--- /dev/null
+++ b/scripts/avahi-export.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Exports bookmarks tagged with "zeroconf"
+ * as avahi service files.
+ *
+ * XML Documentation: "man 5 avahi.service"
+ *
+ *
+ * This file is part of
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once dirname(__FILE__) . '/../src/SemanticScuttle/header-standalone.php';
+
+$fileprefix = $GLOBALS['avahiServiceFilePrefix'];
+$filepath = $GLOBALS['avahiServiceFilePath'];
+
+$arSchemes = array(
+ 'ftp' => array(21, '_ftp._tcp'),
+ 'ssh' => array(22, '_ftp._tcp'),
+ 'sftp' => array(22, '_sftp-ssh._tcp'),
+ 'http' => array(80, '_http._tcp'),
+);
+
+if (!is_writable($filepath)) {
+ echo "avahi service directory is not writable:\n";
+ echo $filepath . "\n";
+ exit(1);
+}
+
+//clean out existing SemanticScuttle service files
+$existing = glob($filepath . '/' . $fileprefix . '*');
+if (count($existing) > 0) {
+ foreach ($existing as $file) {
+ unlink($file);
+ }
+}
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+$bookmarks = $bs->getBookmarks(0, null, null, $GLOBALS['avahiTagName']);
+$bookmarks = $bookmarks['bookmarks'];
+
+if (count($bookmarks) == 0) {
+ echo 'No "' . $GLOBALS['avahiTagName'] . '"-tagged bookmarks available.' . "\n";
+ exit(0);
+}
+
+$written = 0;
+foreach ($bookmarks as $bm) {
+ $xTitle = htmlspecialchars($bm['bTitle']);
+ $parts = parse_url($bm['bAddress']);
+
+ if (!isset($parts['host'])) {
+ echo 'No hostname in: ' . $bm['bAddress'] . "\n";
+ exit(2);
+ }
+
+ $xHostname = htmlspecialchars($parts['host']);
+ $xPath = isset($parts['path']) ? $parts['path'] : '';
+ if (isset($parts['query'])) {
+ $xPath .= '?' . $parts['query'];
+ }
+ if (isset($parts['fragment'])) {
+ $xPath .= '#' . $parts['fragment'];
+ }
+
+ $scheme = isset($parts['scheme']) ? $parts['scheme'] : 'http';
+ if (!isset($arSchemes[$scheme])) {
+ //dying is hard, but at least the user knows
+ // that something is seriously wrong
+ echo "Unknown scheme: $scheme\n";
+ exit(3);
+ }
+ list($xPort, $xType) = $arSchemes[$scheme];
+
+ if (isset($parts['port'])) {
+ $xPort = (int)$parts['port'];
+ }
+
+ $xml = <<<XML
+<?xml version="1.0" standalone='no'?>
+<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
+<service-group>
+ <name>{$xTitle}</name>
+ <service>
+ <type>{$xType}</type>
+ <host-name>{$xHostname}</host-name>
+ <port>{$xPort}</port>
+ <txt-record>path={$xPath}</txt-record>
+ </service>
+</service-group>
+XML;
+
+ $file = $filepath . '/' . $fileprefix . $bm['bId'] . '.service';
+ file_put_contents($file, $xml);
+ ++$written;
+}
+
+echo $written . " service files created\n";
+?> \ No newline at end of file
diff --git a/scripts/compile-translation.php b/scripts/compile-translation.php
new file mode 100644
index 0000000..9d48e69
--- /dev/null
+++ b/scripts/compile-translation.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Compile a translation file from .po to .mo
+ */
+chdir(dirname(dirname(__FILE__)));
+
+if ($argc < 2) {
+ die("pass language name to update, i.e 'de_DE'\n");
+}
+$lang = $argv[1];
+
+$langdir = 'data/locales/' . $lang;
+if (!is_dir($langdir)) {
+ die('There is no language directory: ' . $langdir . "\n");
+}
+
+
+passthru(
+ 'msgfmt --statistics'
+ . ' ' . $langdir . '/LC_MESSAGES/messages.po'
+ . ' -o ' . $langdir . '/LC_MESSAGES/messages.mo'
+);
+
+?> \ No newline at end of file
diff --git a/scripts/create-testbookmarks.php b/scripts/create-testbookmarks.php
new file mode 100644
index 0000000..cbacb85
--- /dev/null
+++ b/scripts/create-testbookmarks.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Simply create some test bookmarks
+ */
+require_once dirname(__FILE__) . '/../src/SemanticScuttle/header-standalone.php';
+
+$us = SemanticScuttle_Service_Factory::get('User');
+$uid = $us->addUser('dummy', 'dummy', 'dummy@example.org');
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+for ($nA = 0; $nA < 10; $nA++) {
+ $rand = rand();
+ $bid = $bs->addBookmark(
+ 'http://example.org/' . $rand,
+ 'unittest bookmark #' . $rand,
+ 'description',
+ null,
+ 0,
+ array('unittest'),
+ null, null, false, false,
+ $uid
+ );
+}
+?> \ No newline at end of file
diff --git a/scripts/create-testuser.php b/scripts/create-testuser.php
new file mode 100644
index 0000000..0a74f6c
--- /dev/null
+++ b/scripts/create-testuser.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Simply create a test user in the database with "test" as password
+ */
+require_once dirname(__FILE__) . '/../src/SemanticScuttle/header-standalone.php';
+
+$us = SemanticScuttle_Service_Factory::get('User');
+$us->addUser('test', 'test', 'test@example.org');
+$us->addUser('admin', 'admin', 'admin@example.org');
+?> \ No newline at end of file
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 @@
+<?php
+/**
+ * Dumps the semanticscuttle database into a file using mysqldump.
+ *
+ * This file is part of
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @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 @@
+<?php
+/**
+ * Restores the semanticscuttle database from a given file.
+ *
+ * This file is part of
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @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
diff --git a/scripts/fix-unfiled-tags.php b/scripts/fix-unfiled-tags.php
new file mode 100644
index 0000000..8a5238e
--- /dev/null
+++ b/scripts/fix-unfiled-tags.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * SemanticScuttle from approximately 0.94 up to 0.98.2, system:unfiled
+ * tags were not created when adding new bookmarks with the web interface.
+ *
+ * This script adds system:unfiled tags for all bookmarks that have no
+ * tags.
+ */
+require_once dirname(__FILE__) . '/../src/SemanticScuttle/header-standalone.php';
+
+//needed to load the database object
+$bt = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$db = SemanticScuttle_Service_Factory::getDb();
+
+$query = <<<SQL
+SELECT b.bId
+FROM sc_bookmarks AS b
+ LEFT JOIN sc_bookmarks2tags AS bt ON b.bId = bt.bId
+WHERE bt.bId IS NULL
+SQL;
+
+if (!($dbresult = $db->sql_query($query))) {
+ die('Strange SQL error');
+}
+while ($row = $db->sql_fetchrow($dbresult)) {
+ $db->sql_query(
+ 'INSERT INTO ' . $bt->getTableName() . ' '
+ . $db->sql_build_array(
+ 'INSERT',
+ array('bId' => $row['bId'], 'tag' => 'system:unfiled')
+ )
+ );
+}
+$db->sql_freeresult($dbresult);
+?> \ No newline at end of file
diff --git a/scripts/update-translation-base.php b/scripts/update-translation-base.php
new file mode 100644
index 0000000..8237510
--- /dev/null
+++ b/scripts/update-translation-base.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Update gettext translation base file
+ */
+chdir(dirname(dirname(__FILE__)));
+
+//do not include php-gettext or database layer
+passthru(
+ 'xgettext -kT_ngettext:1,2 -kT_ -L PHP'
+ . ' -o data/locales/messages.po'
+ . ' src/SemanticScuttle/*.php'
+ . ' src/SemanticScuttle/Model/*.php'
+ . ' src/SemanticScuttle/Service/*.php'
+ . ' data/templates/default/*.php'
+ . ' www/*.php'
+ . ' www/*/*.php'
+);
+
+?> \ No newline at end of file
diff --git a/scripts/update-translation.php b/scripts/update-translation.php
new file mode 100644
index 0000000..4c950f1
--- /dev/null
+++ b/scripts/update-translation.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Update single gettext translation from
+ * gettext base file
+ */
+chdir(dirname(dirname(__FILE__)));
+
+if ($argc < 2) {
+ die("pass language name to update, i.e 'de_DE'\n");
+}
+$lang = $argv[1];
+
+$langdir = 'data/locales/' . $lang;
+if (!is_dir($langdir)) {
+ die('There is no language directory: ' . $langdir . "\n");
+}
+
+
+passthru(
+ 'msgmerge --update --backup=off'
+ . ' ' . $langdir . '/LC_MESSAGES/messages.po'
+ . ' data/locales/messages.po'
+);
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Config.php b/src/SemanticScuttle/Config.php
new file mode 100644
index 0000000..756c303
--- /dev/null
+++ b/src/SemanticScuttle/Config.php
@@ -0,0 +1,123 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Configuration handling
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Config
+{
+ /**
+ * Prefix for configuration files.
+ * Used to inject stream wrapper protocol for unit testing
+ *
+ * @var string
+ */
+ public $filePrefix = '';
+
+
+
+ /**
+ * Finds the correct data directory
+ *
+ * @return string Full path to the data directory with a trailing slash
+ */
+ protected function getDataDir()
+ {
+ if ('@data_dir@' == '@' . 'data_dir@') {
+ //non pear-install
+ $datadir = dirname(__FILE__) . '/../../data/';
+ } else {
+ //pear installation; files are in include path
+ $datadir = '@data_dir@/SemanticScuttle/';
+ }
+
+ return $datadir;
+ }
+
+
+
+ /**
+ * Tries to find a configuration file by looking in different
+ * places:
+ * - pear data_dir/SemanticScuttle/config-$hostname.php
+ * - pear data_dir/SemanticScuttle/config.php
+ * - /etc/semanticscuttle/config-$hostname.php
+ * - /etc/semanticscuttle/config.php
+ *
+ * Paths with host name have priority.
+ *
+ * When open_basedir restrictions are in effect and /etc is not part of
+ * the setting, /etc/semanticscuttle/ is not checked for config files.
+ *
+ * @return array Array with config file path as first value
+ * and default config file path as second value.
+ * Any may be NULL if not found
+ */
+ public function findFiles()
+ {
+ //use basename to prevent path injection
+ $host = basename($_SERVER['HTTP_HOST']);
+ $datadir = $this->getDataDir();
+
+ $openbase = ini_get('open_basedir');
+ if ($openbase && strpos($openbase, '/etc') === false) {
+ //open_basedir restrictions enabled and /etc not allowed?
+ // then don't look in /etc for config files.
+ // the check is not perfect, but it covers most cases
+ $arFiles = array(
+ $datadir . 'config.' . $host . '.php',
+ $datadir . 'config.php',
+ );
+ } else {
+ $arFiles = array(
+ $datadir . 'config.' . $host . '.php',
+ '/etc/semanticscuttle/config.' . $host . '.php',
+ $datadir . 'config.php',
+ '/etc/semanticscuttle/config.php',
+ );
+ }
+
+ $configfile = null;
+ foreach ($arFiles as $file) {
+ if (file_exists($this->filePrefix . $file)) {
+ $configfile = $file;
+ break;
+ }
+ }
+
+ //find default file
+ $arDefaultFiles = array_unique(
+ array(
+ substr($configfile, 0, -3) . 'default.php',
+ $datadir . 'config.default.php',
+ '/etc/semanticscuttle/config.default.php',
+ )
+ );
+ $defaultfile = null;
+ foreach ($arDefaultFiles as $file) {
+ if (file_exists($this->filePrefix . $file)) {
+ $defaultfile = $file;
+ break;
+ }
+ }
+ return array($configfile, $defaultfile);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/DbService.php b/src/SemanticScuttle/DbService.php
new file mode 100644
index 0000000..875ee98
--- /dev/null
+++ b/src/SemanticScuttle/DbService.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Base class for services utilizing the database.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_DbService extends SemanticScuttle_Service
+{
+ /**
+ * Database object
+ *
+ * @var sql_db
+ */
+ protected $db;
+
+
+
+ /**
+ * Database table name
+ *
+ * @var string
+ */
+ protected $tablename;
+
+
+
+ /**
+ * Returns database table name
+ *
+ * @return string Table name
+ */
+ public function getTableName()
+ {
+ return $this->tablename;
+ }
+
+
+
+ /**
+ * Set the database table name
+ *
+ * @param string $value New table name
+ *
+ * @return void
+ */
+ function setTableName($value)
+ {
+ $this->tablename = $value;
+ }
+
+}
diff --git a/src/SemanticScuttle/Environment.php b/src/SemanticScuttle/Environment.php
new file mode 100644
index 0000000..7ccb466
--- /dev/null
+++ b/src/SemanticScuttle/Environment.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Server environment handling methods
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Environment
+{
+ /**
+ * Determines the correct $_SERVER['PATH_INFO'] value
+ *
+ * @return string New value
+ */
+ public static function getServerPathInfo()
+ {
+ if (isset($_SERVER['PATH_INFO'])) {
+ return $_SERVER['PATH_INFO'];
+ }
+
+ if (isset($_SERVER['ORIG_PATH_INFO'])) {
+ //1&1 servers
+ if ($_SERVER['ORIG_PATH_INFO'] == $_SERVER['SCRIPT_NAME']) {
+ return '';
+ }
+ return $_SERVER['ORIG_PATH_INFO'];
+ }
+
+ //fallback when no special path after the php file is given
+ return '';
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Model/Bookmark.php b/src/SemanticScuttle/Model/Bookmark.php
new file mode 100644
index 0000000..1330642
--- /dev/null
+++ b/src/SemanticScuttle/Model/Bookmark.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Bookmark model class, keeping the data of a single bookmark.
+ * It will slowly replace the old array style format.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_Bookmark
+{
+ /**
+ * Status "public" / visible for all
+ */
+ const SPUBLIC = 0;
+
+ /**
+ * Status "shared" / visible for people on your watchlist
+ */
+ const SWATCHLIST = 1;
+
+ /**
+ * Status "private" / visible for yourself only
+ */
+ const SPRIVATE = 2;
+
+
+
+ /**
+ * Checks if the given URL is valid and may be used with this
+ * SemanticScuttle installation.
+ *
+ * @param string $url URL to verify.
+ *
+ * @return boolean True if the URL is allowed, false if not
+ */
+ public static function isValidUrl($url)
+ {
+ $scheme = parse_url($url, PHP_URL_SCHEME);
+ if (array_search($scheme, $GLOBALS['allowedProtocols']) === false) {
+ return false;
+ }
+ return true;
+ }
+
+}
+
+
+?> \ No newline at end of file
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 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Remote User helper methods.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @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/Model/Template.php b/src/SemanticScuttle/Model/Template.php
new file mode 100644
index 0000000..234e23f
--- /dev/null
+++ b/src/SemanticScuttle/Model/Template.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle HTML templating system.
+ * This templating system is really, really simple and based
+ * on including php files while proving a set of
+ * variables in the template scope.
+ * When rendering templates, they are directly echoed to the
+ * browser. There is no in-built way to capture their output.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_Template
+{
+ /**
+ * Array of variables to be available in template
+ * scope.
+ *
+ * @var array
+ */
+ protected $vars = array();
+
+ /**
+ * File name of template
+ */
+ protected $file = '';
+
+ /**
+ * Template service instance
+ *
+ * @var SemanticScuttle_Service_Template
+ */
+ protected $ts;
+
+
+
+ /**
+ * Create a new template instance
+ *
+ * @param string $file Template filename,
+ * full path
+ * @param array $vars Template variables
+ * @param SemanticScuttle_Service_Template $ts Template service
+ */
+ public function __construct(
+ $file, $vars = null,
+ SemanticScuttle_Service_Template $ts = null
+ ) {
+ $this->vars = $vars;
+ $this->file = $file;
+ $this->ts = $ts;
+ }
+
+
+
+ /**
+ * Sets variables and includes the template file,
+ * causing it to be rendered.
+ *
+ * Does not take care of themes and so.
+ * The include path must be set so the correct theme is used.
+ *
+ * @return void
+ */
+ public function parse()
+ {
+ if (isset($this->vars)) {
+ extract($this->vars);
+ }
+ include $this->file;
+ }
+
+
+
+ /**
+ * Loads another template
+ *
+ * @param string $file Filename of template, relative
+ * to template directory
+ *
+ * @return SemanticScuttle_Service_Template Template object
+ */
+ public function includeTemplate($file)
+ {
+ return $this->ts->loadTemplate($file, $this->vars);
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Model/Theme.php b/src/SemanticScuttle/Model/Theme.php
new file mode 100644
index 0000000..65861b8
--- /dev/null
+++ b/src/SemanticScuttle/Model/Theme.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL v3 or later http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * A theme, the visual representation of SemanticScuttle.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL v3 or later http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_Theme
+{
+ /**
+ * Theme name. Also the path part of template and resource files
+ *
+ * @var string
+ */
+ protected $name = null;
+
+ /**
+ * Local path to the www themes directory.
+ * Needs to have a trailing slash.
+ *
+ * @var string
+ */
+ protected $wwwThemeDir = null;
+
+
+
+ /**
+ * Create a new theme instance.
+ *
+ * @param string $name Theme name "data/templates/(*)/"
+ */
+ public function __construct($name = 'default')
+ {
+ $this->name = $name;
+ $this->wwwThemeDir = $GLOBALS['wwwdir'] . '/themes/';
+ //TODO: implement theme hierarchies with parent fallback
+ }
+
+
+
+ /**
+ * Returns the URL path to a resource file (www/themes/$name/$file).
+ * Automatically falls back to the parent theme if the file does not exist
+ * in the theme.
+ *
+ * Must always be used when adding i.e. images to the output.
+ *
+ * @param string $file File name to find the path for, i.e. "scuttle.css".
+ *
+ * @return string Full path
+ */
+ public function resource($file)
+ {
+ $themeFile = $this->wwwThemeDir . $this->name . '/' . $file;
+ if (file_exists($themeFile)) {
+ return ROOT . 'themes/' . $this->name . '/' . $file;
+ }
+
+ $defaultFile = $this->wwwThemeDir . 'default/' . $file;
+ if (file_exists($defaultFile)) {
+ return ROOT . 'themes/default/' . $file;
+ }
+
+ //file does not exist. fall back to the theme file
+ // to guide the theme author a bit.
+ // TODO: logging. in admin mode, there should be a message
+ return ROOT . 'themes/' . $this->name . '/' . $file;
+ }
+
+
+
+ /**
+ * Returns the theme name.
+ *
+ * @return string Theme name
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Model/User.php b/src/SemanticScuttle/Model/User.php
new file mode 100644
index 0000000..e5d29af
--- /dev/null
+++ b/src/SemanticScuttle/Model/User.php
@@ -0,0 +1,209 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle user object.
+ * Rarely used fields are filled if required.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_User
+{
+ var $id;
+ var $username;
+ var $name;
+ var $email;
+ var $homepage;
+ var $content;
+ var $datetime;
+ var $isAdmin;
+ var $privateKey;
+
+ /**
+ * Create a new user object
+ *
+ * @param integer $id User ID
+ * @param string $username Username
+ */
+ public function __construct($id, $username)
+ {
+ $this->id = $id;
+ $this->username = $username;
+ }
+
+ /**
+ * Returns user ID
+ *
+ * @return integer ID
+ */
+ public function getId()
+ {
+ return (int)$this->id;
+ }
+
+ /**
+ * Returns logon user name
+ *
+ * @return string User name
+ */
+ public function getUsername()
+ {
+ return $this->username;
+ }
+
+ /**
+ * Returns private key
+ *
+ * @param boolean return sanitized value which basically drops
+ * leading dash if exists
+ *
+ * @return string private key
+ */
+ public function getPrivateKey($sanitized = false)
+ {
+ // Look for value only if not already set
+ if (!isset($this->privateKey)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $user = $us->getUser($this->id);
+ $this->privateKey = $user['privateKey'];
+ }
+ if ($sanitized == true) {
+ return substr($this->privateKey, -32);
+ } else {
+ return $this->privateKey;
+ }
+ }
+
+ /**
+ * Returns full user name as specified in the profile.
+ *
+ * @return string Full name
+ */
+ public function getName()
+ {
+ // Look for value only if not already set
+ if (!isset($this->name)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $user = $us->getUser($this->id);
+ $this->name = $user['name'];
+ }
+ return $this->name;
+ }
+
+ /**
+ * Returns user email address
+ *
+ * @return string Email address
+ */
+ public function getEmail()
+ {
+ // Look for value only if not already set
+ if (!isset($this->email)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $user = $us->getUser($this->id);
+ $this->email = $user['email'];
+ }
+ return $this->email;
+ }
+
+ /**
+ * Returns user homepage as specified in the profile.
+ *
+ * @return string Homepage
+ */
+ public function getHomepage()
+ {
+ // Look for value only if not already set
+ if(!isset($this->homepage)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $user = $us->getUser($this->id);
+ $this->homepage = $user['homepage'];
+ }
+ return $this->homepage;
+ }
+
+ /**
+ * Returns custom user description as specified in the profile.
+ *
+ * @return string User description
+ */
+ public function getContent()
+ {
+ // Look for value only if not already set
+ if(!isset($this->content)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $user = $us->getUser($this->id);
+ $this->content = $user['uContent'];
+ }
+ return $this->content;
+ }
+
+ /**
+ * Returns user creation time.
+ * UTC/Zulu time zone is used.
+ *
+ * @return string Datetime value: "YYYY-MM-DD HH:MM:SS"
+ */
+ public function getDatetime()
+ {
+ // Look for value only if not already set
+ if(!isset($this->content)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $user = $us->getUser($this->id);
+ $this->datetime = $user['uDatetime'];
+ }
+ return $this->datetime;
+ }
+
+ /**
+ * Tells you if the user is an administrator
+ *
+ * @return boolean True if the user is admin
+ */
+ public function isAdmin()
+ {
+ // Look for value only if not already set
+ if(!isset($this->isAdmin)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $this->isAdmin = $us->isAdmin($this->username);
+ }
+ return $this->isAdmin;
+ }
+
+ /**
+ * Returns the number of bookmarks the user owns
+ *
+ * @param string $range Range of bookmarks:
+ * 'public', 'shared', 'private'
+ * or 'all'
+ *
+ * @return integer Number of bookmarks
+ *
+ * @uses SemanticScuttle_Service_Bookmark::countBookmarks()
+ */
+ public function getNbBookmarks($range = 'public')
+ {
+ $bs = SemanticScuttle_Service_Factory::get('Bookmark');
+ return $bs->countBookmarks($this->getId(), $range);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Model/User/SslClientCert.php b/src/SemanticScuttle/Model/User/SslClientCert.php
new file mode 100644
index 0000000..383b601
--- /dev/null
+++ b/src/SemanticScuttle/Model/User/SslClientCert.php
@@ -0,0 +1,148 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SSL client certificate model. Represents one single client certificate
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_User_SslClientCert
+{
+ public $id;
+ public $uId;
+ public $sslSerial;
+ public $sslClientIssuerDn;
+ public $sslName;
+ public $sslEmail;
+
+
+
+ /**
+ * Creates and returns a new object and fills it with
+ * the passed values from the database.
+ *
+ * @param array $arCertRow Database row array
+ *
+ * @return SemanticScuttle_Model_User_SslClientCert
+ */
+ public static function fromDb($arCertRow)
+ {
+ $cert = new self();
+ foreach (get_object_vars($cert) as $variable => $dummy) {
+ if (isset($arCertRow[$variable])) {
+ $cert->$variable = $arCertRow[$variable];
+ }
+ }
+ return $cert;
+ }
+
+
+
+ /**
+ * Loads the user's/browser's client certificate information into
+ * an object and returns it.
+ * Expects that all information is available.
+ * Better check with
+ * SemanticScuttle_Service_User_SslClientCert::hasValidCert() before.
+ *
+ * @return SemanticScuttle_Model_User_SslClientCert
+ *
+ * @see SemanticScuttle_Service_User_SslClientCert::hasValidCert()
+ */
+ public static function fromCurrentCert()
+ {
+ $cert = new self();
+ $cert->sslSerial = $_SERVER['SSL_CLIENT_M_SERIAL'];
+ $cert->sslClientIssuerDn = $_SERVER['SSL_CLIENT_I_DN'];
+ $cert->sslName = $_SERVER['SSL_CLIENT_S_DN_CN'];
+ $cert->sslEmail = $_SERVER['SSL_CLIENT_S_DN_Email'];
+ return $cert;
+ }
+
+
+
+ /**
+ * Tells you if this certificate is the one the user is currently browsing
+ * with.
+ *
+ * @return boolean True if this certificate is the current browser's
+ */
+ public function isCurrent()
+ {
+ if (!isset($_SERVER['SSL_CLIENT_M_SERIAL'])
+ || !isset($_SERVER['SSL_CLIENT_I_DN'])
+ ) {
+ return false;
+ }
+
+ return $this->sslSerial == $_SERVER['SSL_CLIENT_M_SERIAL']
+ && $this->sslClientIssuerDn == $_SERVER['SSL_CLIENT_I_DN'];
+ }
+
+
+
+ /**
+ * Checks if this certificate is registered (exists) in the certificate
+ * array
+ *
+ * @param array $arCertificates Array of certificate objects
+ *
+ * @return boolean True or false
+ */
+ public function isRegistered($arCertificates)
+ {
+ foreach ($arCertificates as $cert) {
+ if ($cert->equals($this)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+ /**
+ * Deletes this certificate from database
+ *
+ * @return boolean True if all went well, false if not
+ */
+ public function delete()
+ {
+ $ok = SemanticScuttle_Service_Factory::get('User_SslClientCert')
+ ->delete($this);
+ if ($ok) {
+ $this->id = null;
+ }
+ return $ok;
+ }
+
+
+
+ /**
+ * Compares this certificate with the given one.
+ *
+ * @param SemanticScuttle_Service_Factory $cert Another user certificate
+ *
+ * @return boolean True if both match.
+ */
+ public function equals(SemanticScuttle_Model_User_SslClientCert $cert)
+ {
+ return $this->sslSerial == $cert->sslSerial
+ && $this->sslClientIssuerDn == $cert->sslClientIssuerDn;
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Model/UserArray.php b/src/SemanticScuttle/Model/UserArray.php
new file mode 100644
index 0000000..a0d9c9b
--- /dev/null
+++ b/src/SemanticScuttle/Model/UserArray.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Mostly static methods that help working with a user row array from database.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_UserArray
+{
+ /**
+ * Returns full user name as specified in the profile if it is set,
+ * otherwise the nickname/loginname is returned.
+ *
+ * @param array $row User row array from database
+ *
+ * @return string Full name or username
+ */
+ public static function getName($row)
+ {
+ if (isset($row['name']) && $row['name']) {
+ return $row['name'];
+ }
+ return $row['username'];
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service.php b/src/SemanticScuttle/Service.php
new file mode 100644
index 0000000..cd79f2c
--- /dev/null
+++ b/src/SemanticScuttle/Service.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle base service class.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service
+{
+ /**
+ * SQL database object
+ *
+ * @var sql_db
+ */
+ protected $db;
+
+
+
+ /**
+ * Returns the single service instance
+ *
+ * @internal
+ * This function can be used once PHP 5.3 is minimum, because only
+ * 5.3 supports late static binding. For all lower php versions,
+ * we still need a copy of this method in each service class.
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new static($db);
+ }
+ return $instance;
+ }
+
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service/AuthUser.php b/src/SemanticScuttle/Service/AuthUser.php
new file mode 100644
index 0000000..9447ee4
--- /dev/null
+++ b/src/SemanticScuttle/Service/AuthUser.php
@@ -0,0 +1,232 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+require_once 'Auth.php';
+require_once 'SemanticScuttle/Service/User.php';
+
+/**
+ * SemanticScuttle extendet user management service utilizing
+ * the PEAR Auth package to enable authentication against
+ * different services, i.e. LDAP or other databases.
+ *
+ * Requires the Log packages for debugging purposes.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_AuthUser extends SemanticScuttle_Service_User
+{
+ /**
+ * PEAR Auth instance
+ *
+ * @var Auth
+ */
+ protected $auth = null;
+
+ /**
+ * If we want to debug authentication process
+ *
+ * @var boolean
+ */
+ protected $authdebug = false;
+
+ /**
+ * Authentication type (i.e. LDAP)
+ *
+ * @var string
+ *
+ * @link http://pear.php.net/manual/en/package.authentication.auth.intro-storage.php
+ */
+ var $authtype = null;
+
+ /**
+ * Authentication options
+ *
+ * @var array
+ *
+ * @link http://pear.php.net/manual/en/package.authentication.auth.intro.php
+ */
+ var $authoptions = null;
+
+
+
+ /**
+ * Returns the single service instance
+ *
+ * @param sql_db $db Database object
+ *
+ * @return SemanticScuttle_Service_AuthUser
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+
+
+ /**
+ * Create new instance
+ *
+ * @var sql_db $db Database object
+ */
+ protected function __construct($db)
+ {
+ parent::__construct($db);
+
+ $this->authtype = $GLOBALS['authType'];
+ $this->authoptions = $GLOBALS['authOptions'];
+ $this->authdebug = $GLOBALS['authDebug'];
+
+ //FIXME: throw error when no authtype set?
+ if (!$this->authtype) {
+ return;
+ }
+ require_once 'Auth.php';
+ $this->auth = new Auth($this->authtype, $this->authoptions);
+ //FIXME: check if it worked (i.e. db connection)
+ if ($this->authdebug) {
+ require_once 'Log.php';
+ $this->auth->logger = Log::singleton(
+ 'display', '', '', array(), PEAR_LOG_DEBUG
+ );
+ $this->auth->enableLogging = true;
+ }
+ $this->auth->setShowLogin(false);
+ }
+
+
+
+ /**
+ * Return current user id based on session or cookie
+ *
+ * @return mixed Integer user id or boolean false when user
+ * could not be found or is not logged on.
+ */
+ public function getCurrentUserId()
+ {
+ if (!$this->auth) {
+ return parent::getCurrentUserId();
+ }
+
+ //FIXME: caching?
+ $name = $this->auth->getUsername();
+ if (!$name) {
+ return parent::getCurrentUserId();
+ }
+ return $this->getIdFromUser($name);
+ }
+
+
+
+ /**
+ * Try to authenticate and login a user with
+ * username and password.
+ *
+ * @param string $username Name of user
+ * @param string $password Password
+ * @param boolean $remember If a long-time cookie shall be set
+ *
+ * @return boolean True if the user could be authenticated,
+ * false if not.
+ */
+ public function login($username, $password, $remember = false)
+ {
+ if (!$this->auth) {
+ return parent::login($username, $password, $remember);
+ }
+
+ $ok = $this->loginAuth($username, $password);
+ if (!$ok) {
+ return false;
+ }
+
+ //utilize real login method to get longtime cookie support etc.
+ $ok = parent::login($username, $password, $remember);
+ if ($ok) {
+ return $ok;
+ }
+
+ //user must have changed password in external auth.
+ //we need to update the local database.
+ $user = $this->getUserByUsername($username);
+ $this->_updateuser(
+ $user['uId'], $this->getFieldName('password'),
+ $this->sanitisePassword($password)
+ );
+
+ return parent::login($username, $password, $remember);
+ }
+
+
+ /**
+ * Uses PEAR's Auth class to authenticate the user against a container.
+ * This allows us to use LDAP, a different database or some other
+ * external system.
+ *
+ * @param string $username Username to check
+ * @param string $password Password to check
+ *
+ * @return boolean If the user has been successfully authenticated or not
+ */
+ public function loginAuth($username, $password)
+ {
+ $this->auth->post = array(
+ 'username' => $username,
+ 'password' => $password,
+ );
+ $this->auth->start();
+
+ if (!$this->auth->checkAuth()) {
+ return false;
+ }
+
+ //put user in database
+ if (!$this->getUserByUsername($username)) {
+ $this->addUser(
+ $username, $password,
+ $username . $GLOBALS['authEmailSuffix']
+ );
+ }
+
+ return true;
+ }
+
+
+
+
+ /**
+ * Logs the current user out of the system.
+ *
+ * @return void
+ */
+ public function logout()
+ {
+ parent::logout();
+
+ if ($this->auth) {
+ $this->auth->logout();
+ $this->auth = null;
+ }
+ }
+
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php
new file mode 100644
index 0000000..1315350
--- /dev/null
+++ b/src/SemanticScuttle/Service/Bookmark.php
@@ -0,0 +1,1161 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'SemanticScuttle/Model/RemoteUser.php';
+
+/**
+ * SemanticScuttle bookmark service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+
+
+ /**
+ * Creates a new instance. Initializes the table name.
+ *
+ * @param DB $db Database object
+ *
+ * @uses $GLOBALS['tableprefix']
+ */
+ public function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'bookmarks';
+ }
+
+
+
+ /**
+ * Retrieves the first bookmark whose $fieldname equals
+ * the given $value.
+ *
+ * @param string $fieldname Name of database field
+ * @param mixed $value Desired value of $fieldname
+ * @param boolean $all Retrieve from all users (true)
+ * or only bookmarks owned by the current
+ * user (false)
+ *
+ * @return mixed Database row array when found, boolean false
+ * when no bookmark matched.
+ *
+ * @TODO: merge with getBookmark()
+ */
+ protected function _getbookmark($fieldname, $value, $all = false)
+ {
+ if (!$all) {
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+ $uId = $userservice->getCurrentUserId();
+ $range = ' AND uId = '. $uId;
+ } else {
+ $range = '';
+ }
+
+ $query = 'SELECT * FROM '. $this->getTableName()
+ . ' WHERE ' . $fieldname . ' ='
+ . ' "' . $this->db->sql_escape($value) .'"'
+ . $range;
+
+ if (!($dbresult = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(
+ GENERAL_ERROR,
+ 'Could not get bookmark', '', __LINE__, __FILE__,
+ $query, $this->db
+ );
+ }
+
+ if ($row = $this->db->sql_fetchrow($dbresult)) {
+ $output = $row;
+ } else {
+ $output = false;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+
+
+ /**
+ * Load a single bookmark and return it.
+ * When a user is logged on, the returned array will contain
+ * keys "hasVoted" and "vote".
+ *
+ * DOES NOT RESPECT PRIVACY SETTINGS!
+ *
+ * @param integer $bid Bookmark ID
+ * @param boolean $include_tags If tags shall be loaded
+ *
+ * @return mixed Array with bookmark data or false in case
+ * of an error.
+ */
+ function getBookmark($bid, $include_tags = false)
+ {
+ if (!is_numeric($bid)) {
+ return false;
+ }
+
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+
+ $query_1 = 'B.*';
+ $query_2 = $this->getTableName() . ' as B';
+
+ //Voting system
+ //needs to be directly after FROM bookmarks
+ if ($GLOBALS['enableVoting'] && $userservice->isLoggedOn()) {
+ $cuid = $userservice->getCurrentUserId();
+ $vs = SemanticScuttle_Service_Factory::get('Vote');
+ $query_1 .= ', !ISNULL(V.bId) as hasVoted, V.vote as vote';
+ $query_2 .= ' LEFT JOIN ' . $vs->getTableName() . ' AS V'
+ . ' ON B.bId = V.bId'
+ . ' AND V.uId = ' . (int)$cuid;
+ }
+
+ $sql = 'SELECT ' . $query_1 . ' FROM '
+ . $query_2
+ .' WHERE B.bId = '. $this->db->sql_escape($bid);
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get bookmark',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+
+ if ($row = $this->db->sql_fetchrow($dbresult)) {
+ if ($include_tags) {
+ $b2tservice = SemanticScuttle_Service_Factory::get(
+ 'Bookmark2Tag'
+ );
+ $row['tags'] = $b2tservice->getTagsForBookmark($bid);
+ }
+ $output = $row;
+ } else {
+ $output = false;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+
+
+ /**
+ * Retrieves a bookmark with the given URL.
+ * DOES NOT RESPECT PRIVACY SETTINGS!
+ *
+ * @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).
+ *
+ * @uses getBookmarkByHash()
+ * @see getBookmarkByShortname()
+ */
+ public function getBookmarkByAddress($address, $all = true)
+ {
+ return $this->getBookmarkByHash($this->getHash($address), $all);
+ }
+
+
+
+ /**
+ * Retrieves a bookmark with the given hash.
+ * DOES NOT RESPECT PRIVACY SETTINGS!
+ *
+ * @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, $all = true)
+ {
+ return $this->_getbookmark('bHash', $hash, $all);
+ }
+
+
+
+ /**
+ * Returns the hash value of a given address.
+ *
+ * @param string $address URL to hash
+ * @param boolean $bNormalize If the address shall be normalized before
+ * being hashed
+ *
+ * @return string Hash value
+ */
+ public function getHash($address, $bNormalize = true)
+ {
+ if ($bNormalize) {
+ $address = $this->normalize($address);
+ }
+ return md5($address);
+ }
+
+
+
+ /**
+ * Retrieves a bookmark that has a given short
+ * name.
+ *
+ * @param string $short Short URL name
+ *
+ * @return mixed Array with bookmark data or false in case
+ * of an error (i.e. not found).
+ */
+ public function getBookmarkByShortname($short)
+ {
+ return $this->_getbookmark('bShort', $short, true);
+ }
+
+
+
+ /**
+ * Counts bookmarks for a user.
+ *
+ * @param integer $uId User ID
+ * @param string $status Bookmark visibility/privacy settings:
+ * 'public', 'shared', 'private'
+ * or 'all'
+ *
+ * @return integer Number of bookmarks
+ */
+ public function countBookmarks($uId, $status = 'public')
+ {
+ $sql = 'SELECT COUNT(*) as "0" FROM '. $this->getTableName();
+ $sql.= ' WHERE uId = ' . intval($uId);
+ switch ($status) {
+ case 'all':
+ //no constraints
+ break;
+ case 'private':
+ $sql .= ' AND bStatus = 2';
+ break;
+ case 'shared':
+ $sql .= ' AND bStatus = 1';
+ break;
+ case 'public':
+ default:
+ $sql .= ' AND bStatus = 0';
+ break;
+ }
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get vars',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+ $count = $this->db->sql_fetchfield(0, 0);
+ $this->db->sql_freeresult($dbresult);
+ return $count;
+ }
+
+
+
+ /**
+ * Check if a bookmark may be edited by the current user
+ *
+ * @param integer|array $bookmark Bookmark uId or bookmark array
+ *
+ * @return boolean True if allowed
+ */
+ function editAllowed($bookmark)
+ {
+ if (!is_numeric($bookmark)
+ && (!is_array($bookmark)
+ || !isset($bookmark['bId'])
+ || !is_numeric($bookmark['bId'])
+ )
+ ) {
+ return false;
+ }
+
+ if (!is_array($bookmark)
+ && !($bookmark = $this->getBookmark($bookmark))
+ ) {
+ return false;
+ }
+
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+ $user = $userservice->getCurrentObjectUser();
+ if ($user === null) {
+ return false;
+ }
+
+ //user has to be either admin, or owner
+ if ($GLOBALS['adminsCanModifyBookmarksFromOtherUsers']
+ && $userservice->isAdmin($user->username)
+ ) {
+ return true;
+ } else {
+ return ($bookmark['uId'] == $user->id);
+ }
+ }
+
+
+
+ /**
+ * Checks if a bookmark for the given URL exists
+ * already
+ *
+ * @param string $address URL of bookmark to check
+ * @param integer $uid User id the bookmark has to belong to.
+ * null for all users
+ *
+ * @return boolean True when the bookmark with the given URL
+ * exists for the user, false if not.
+ */
+ public function bookmarkExists($address = false, $uid = null)
+ {
+ if (!$address) {
+ return false;
+ }
+
+ $crit = array('bHash' => $this->getHash($address));
+ if (isset ($uid)) {
+ $crit['uId'] = $uid;
+ }
+
+ $sql = 'SELECT COUNT(*) as "0" FROM '
+ . $GLOBALS['tableprefix'] . 'bookmarks'
+ . ' WHERE '. $this->db->sql_build_array('SELECT', $crit);
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get vars', '',
+ __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+ if ($this->db->sql_fetchfield(0, 0) > 0) {
+ $output = true;
+ } else {
+ $output = false;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+
+
+ /**
+ * Checks if the given addresses exist
+ *
+ * @param array $addresses Array of addresses
+ * @param integer $uid User ID the addresses shall belong to
+ *
+ * @return array Array with addresses as keys, true/false for existence
+ * as value
+ */
+ public function bookmarksExist($addresses, $uid = null)
+ {
+ if (count($addresses) == 0) {
+ return array();
+ }
+
+ $hashes = array();
+ $sql = '(0';
+ foreach ($addresses as $key => $address) {
+ $hash = $this->getHash($address);
+ $hashes[$hash] = $address;
+ $sql .= ' OR bHash = "'
+ . $this->db->sql_escape($hash)
+ . '"';
+ }
+ $sql .= ')';
+ if ($uid !== null) {
+ $sql .= ' AND uId = ' . intval($uid);
+ }
+
+ $sql = 'SELECT bHash, COUNT(*) as "count" FROM '
+ . $this->getTableName()
+ . ' WHERE ' . $sql
+ . ' GROUP BY bHash';
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get bookmark counts', '',
+ __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+
+ $existence = array_combine(
+ $addresses,
+ array_fill(0, count($addresses), false)
+ );
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $existence[$hashes[$row['bHash']]] = $row['count'] > 0;
+ }
+
+ $this->db->sql_freeresult($dbresult);
+ return $existence;
+ }
+
+
+
+ /**
+ * Adds a bookmark to the database.
+ *
+ * Security checks are being made here, but no error reasons will be
+ * returned. It is the responsibility of the code that calls
+ * addBookmark() to verify the data.
+ *
+ * @param string $address Full URL of the bookmark
+ * @param string $title Bookmark title
+ * @param string $description Long bookmark description
+ * @param string $privateNote Private note for the user.
+ * @param string $status Bookmark visibility / privacy settings:
+ * 0 - public
+ * 1 - shared
+ * 2 - private
+ * @param array $tags Array of tags
+ * @param string $short Short URL name. May be null
+ * @param string $date Date when the bookmark has been created
+ * originally. Used in combination with
+ * $fromImport. Has to be a strtotime()
+ * interpretable string.
+ * @param boolean $fromApi True when api call is responsible.
+ * @param boolean $fromImport True when the bookmark is from an import.
+ * @param integer $sId ID of user who creates the bookmark.
+ *
+ * @return mixed Integer bookmark ID if saving succeeded, false in
+ * case of an error. Error reasons are not returned.
+ */
+ public function addBookmark(
+ $address, $title, $description, $privateNote, $status, $tags,
+ $short = null,
+ $date = null, $fromApi = false, $fromImport = false, $sId = null
+ ) {
+ if ($sId === null) {
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+ $sId = $userservice->getCurrentUserId();
+ }
+
+ $address = $this->normalize($address);
+ if (!SemanticScuttle_Model_Bookmark::isValidUrl($address)) {
+ return false;
+ }
+
+ /*
+ * Note that if date is NULL, then it's added with a date and
+ * time of now, and if it's present,
+ * it's expected to be a string that's interpretable by strtotime().
+ */
+ if (is_null($date) || $date == '') {
+ $time = time();
+ } else {
+ $time = strtotime($date);
+ }
+ $datetime = gmdate('Y-m-d H:i:s', $time);
+
+ if ($short === '') {
+ $short = null;
+ }
+
+ // Set up the SQL insert statement and execute it.
+ $values = array(
+ 'uId' => intval($sId),
+ 'bIp' => SemanticScuttle_Model_RemoteUser::getIp(),
+ 'bDatetime' => $datetime,
+ 'bModified' => $datetime,
+ 'bTitle' => $title,
+ 'bAddress' => $address,
+ 'bDescription' => $description,
+ 'bPrivateNote' => $privateNote,
+ 'bStatus' => intval($status),
+ 'bHash' => $this->getHash($address),
+ 'bShort' => $short
+ );
+
+ $sql = 'INSERT INTO '. $this->getTableName()
+ .' ' . $this->db->sql_build_array('INSERT', $values);
+ $this->db->sql_transaction('begin');
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR,
+ 'Could not insert bookmark',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+
+ // Get the resultant row ID for the bookmark.
+ $bId = $this->db->sql_nextid($dbresult);
+ if (!isset($bId) || !is_int($bId)) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR,
+ 'Could not insert bookmark',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+
+ $uriparts = explode('.', $address);
+ $extension = end($uriparts);
+ unset($uriparts);
+
+ $b2tservice = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+ $attachok = $b2tservice->attachTags(
+ $bId, $tags, $fromApi, $extension, false, $fromImport
+ );
+ if (!$attachok) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR,
+ 'Could not insert bookmark',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+ $this->db->sql_transaction('commit');
+
+ // Everything worked out, so return the new bookmark's bId.
+ return $bId;
+ }//public function addBookmark(..)
+
+
+
+ /**
+ * Update an existing bookmark.
+ *
+ * @param integer $bId Bookmark ID
+ * @param string $address Full URL of the bookmark
+ * @param string $title Bookmark title
+ * @param string $description Long bookmark description
+ * @param string $privateNote Private note for the user.
+ * @param string $status Bookmark visibility / privacy setting:
+ * 0 - public
+ * 1 - shared
+ * 2 - private
+ * @param array $categories Array of tags
+ * @param string $short Short URL name. May be null.
+ * @param string $date Date when the bookmark has been created
+ * originally. Used in combination with
+ * $fromImport. Has to be a strtotime()
+ * interpretable string.
+ * @param boolean $fromApi True when api call is responsible.
+ *
+ * @return boolean True if all went well, false if not.
+ */
+ public function updateBookmark(
+ $bId, $address, $title, $description, $privateNote, $status,
+ $categories, $short = null, $date = null, $fromApi = false
+ ) {
+ if (!is_numeric($bId)) {
+ return false;
+ }
+
+ // Get the the date; note that the date is in GMT.
+ $moddatetime = gmdate('Y-m-d H:i:s', time());
+
+ $address = $this->normalize($address);
+
+ //check if a new address ($address) doesn't already exist
+ // for another bookmark from the same user
+ $bookmark = $this->getBookmark($bId);
+ if ($bookmark['bAddress'] != $address
+ && $this->bookmarkExists($address, $bookmark['uId'])
+ ) {
+ message_die(
+ GENERAL_ERROR,
+ 'Could not update bookmark (URL already exists: ' . $address . ')',
+ '', __LINE__, __FILE__
+ );
+ return false;
+ }
+
+ if ($short === '') {
+ $short = null;
+ }
+
+ // Set up the SQL update statement and execute it.
+ $updates = array(
+ 'bModified' => $moddatetime,
+ 'bTitle' => $title,
+ 'bAddress' => $address,
+ 'bDescription' => $description,
+ 'bPrivateNote' => $privateNote,
+ 'bStatus' => $status,
+ 'bHash' => $this->getHash($address, false),
+ 'bShort' => $short
+ );
+
+ if (!is_null($date)) {
+ $datetime = gmdate('Y-m-d H:i:s', strtotime($date));
+ $updates['bDatetime'] = $datetime;
+ }
+
+ $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
+ );
+ }
+
+ $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
+ );
+ }
+
+ $this->db->sql_transaction('commit');
+ // Everything worked out, so return true.
+ return true;
+ }
+
+
+
+ /**
+ * Only get the bookmarks that are visible to the current user.
+ * Our rules:
+ * - if the $user is NULL, that means get bookmarks from ALL users,
+ * so we need to make sure to check the logged-in user's
+ * watchlist and get the contacts-only bookmarks from
+ * those users.
+ * If the user isn't logged-in, just get the public bookmarks.
+ *
+ * - if the $user is set and isn't the logged-in user, then get
+ * that user's bookmarks, and if that user is on the logged-in
+ * user's watchlist, get the public AND contacts-only
+ * bookmarks; otherwise, just get the public bookmarks.
+ *
+ * - if the $user is set and IS the logged-in user, then
+ * get all bookmarks.
+ *
+ * In case voting is enabled and a user is logged in,
+ * each bookmark array contains two additional keys:
+ * 'hasVoted' and 'vote'.
+ *
+ * @param integer $start Page number
+ * @param integer $perpage Number of bookmarks per page
+ * @param integer $user User ID
+ * @param mixed $tags Array of tags or tags separated
+ * by "+" signs
+ * @param string $terms Search terms separated by spaces
+ * @param string $sortOrder One of the following values:
+ * "date_asc", "date_desc",
+ * "modified_asc", "modified_desc"
+ * "title_desc", "title_asc",
+ * "url_desc", "url_asc",
+ * "voting_asc", "voting_desc"
+ * @param boolean $watched True if only watched bookmarks
+ * shall be returned (FIXME)
+ * @param integer $startdate Filter for creation date.
+ * SQL-DateTime value
+ * "YYYY-MM-DD hh:ii:ss'
+ * @param integer $enddate Filter for creation date.
+ * SQL-DateTime value
+ * "YYYY-MM-DD hh:ii:ss'
+ * @param string $hash Filter by URL hash
+ *
+ * @return array Array with two keys: 'bookmarks' and 'total'.
+ * First contains an array of bookmarks, 'total'
+ * the total number of bookmarks (without paging).
+ */
+ public function getBookmarks(
+ $start = 0, $perpage = null, $user = null, $tags = null,
+ $terms = null, $sortOrder = null, $watched = null,
+ $startdate = null, $enddate = null, $hash = null
+ ) {
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+ $b2tservice = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+ $tag2tagservice = SemanticScuttle_Service_Factory::get('Tag2Tag');
+ $sId = $userservice->getCurrentUserId();
+
+ if ($userservice->isLoggedOn()) {
+ // All public bookmarks, user's own bookmarks
+ // and any shared with user
+ $privacy = ' AND ((B.bStatus = 0) OR (B.uId = '. $sId .')';
+ $watchnames = $userservice->getWatchNames($sId, true);
+ foreach ($watchnames as $watchuser) {
+ $privacy .= ' OR (U.username = "'. $watchuser .'" AND B.bStatus = 1)';
+ }
+ $privacy .= ')';
+ } else {
+ // Just public bookmarks
+ $privacy = ' AND B.bStatus = 0';
+ }
+
+ // Set up the tags, if need be.
+ if (!is_array($tags) && !is_null($tags)) {
+ $tags = explode('+', trim($tags));
+ }
+
+ $tagcount = count($tags);
+ for ($i = 0; $i < $tagcount; $i ++) {
+ $tags[$i] = trim($tags[$i]);
+ }
+
+ // Set up the SQL query.
+ $query_1 = 'SELECT DISTINCT ';
+ if (SQL_LAYER == 'mysql4') {
+ $query_1 .= 'SQL_CALC_FOUND_ROWS ';
+ }
+ $query_1 .= 'B.*, U.'. $userservice->getFieldName('username')
+ . ', U.name';
+
+ $query_2 = ' FROM '. $userservice->getTableName() .' AS U'
+ . ', '. $this->getTableName() .' AS B';
+
+ $query_3 = ' WHERE B.uId = U.'. $userservice->getFieldName('primary') . $privacy;
+
+ if ($GLOBALS['enableVoting'] && $GLOBALS['hideBelowVoting'] !== null
+ && !$userservice->isAdmin($userservice->getCurrentUserId())
+ ) {
+ $query_3 .= ' AND B.bVoting >= ' . (int)$GLOBALS['hideBelowVoting'];
+ }
+
+ if (is_null($watched)) {
+ if (!is_null($user)) {
+ $query_3 .= ' AND B.uId = '. $user;
+ }
+ } else {
+ $arrWatch = $userservice->getWatchlist($user);
+ if (count($arrWatch) > 0) {
+ $query_3_1 = '';
+ foreach ($arrWatch as $row) {
+ $query_3_1 .= 'B.uId = '. intval($row) .' OR ';
+ }
+ $query_3_1 = substr($query_3_1, 0, -3);
+ } else {
+ $query_3_1 = 'B.uId = -1';
+ }
+ $query_3 .= ' AND ('. $query_3_1 .') AND B.bStatus IN (0, 1)';
+ }
+
+ $query_5 = '';
+ if ($hash == null) {
+ $query_5.= ' GROUP BY B.bHash';
+ }
+
+
+ //Voting system
+ //needs to be directly after FROM bookmarks
+ if ($GLOBALS['enableVoting'] && $userservice->isLoggedOn()) {
+ $cuid = $userservice->getCurrentUserId();
+ $vs = SemanticScuttle_Service_Factory::get('Vote');
+ $query_1 .= ', !ISNULL(V.bId) as hasVoted, V.vote as vote';
+ $query_2 .= ' LEFT JOIN ' . $vs->getTableName() . ' AS V'
+ . ' ON B.bId = V.bId'
+ . ' AND V.uId = ' . (int)$cuid;
+ }
+
+ switch($sortOrder) {
+ case 'date_asc':
+ $query_5 .= ' ORDER BY B.' . $GLOBALS['dateOrderField'] . ' ASC ';
+ break;
+ case 'title_desc':
+ $query_5 .= ' ORDER BY B.bTitle DESC ';
+ break;
+ case 'title_asc':
+ $query_5 .= ' ORDER BY B.bTitle ASC ';
+ break;
+ case 'voting_desc':
+ $query_5 .= ' ORDER BY B.bVoting DESC ';
+ break;
+ case 'voting_asc':
+ $query_5 .= ' ORDER BY B.bVoting ASC ';
+ break;
+ case 'url_desc':
+ $query_5 .= ' ORDER BY B.bAddress DESC ';
+ break;
+ case 'url_asc':
+ $query_5 .= ' ORDER BY B.bAddress ASC ';
+ break;
+ case 'modified_desc':
+ $query_5 .= ' ORDER BY B.bModified DESC ';
+ break;
+ case 'modified_asc':
+ $query_5 .= ' ORDER BY B.bModified ASC ';
+ break;
+ default:
+ $query_5 .= ' ORDER BY B.' . $GLOBALS['dateOrderField'] . ' DESC ';
+ }
+
+ // Handle the parts of the query that depend on any tags that are present.
+ $query_4 = '';
+ for ($i = 0; $i < $tagcount; $i ++) {
+ $query_2 .= ', '. $b2tservice->getTableName() .' AS T'. $i;
+ $query_4 .= ' AND (';
+
+ $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) .'"';
+ $query_4 .= ' OR';
+ }
+
+ $query_4 .= ' T'. $i .'.tag = "'. $this->db->sql_escape($tags[$i]) .'"';
+
+ $query_4 .= ') AND T'. $i .'.bId = B.bId';
+ //die($query_4);
+ }
+
+ // Search terms
+ if ($terms) {
+ // Multiple search terms okay
+ $aTerms = explode(' ', $terms);
+ $aTerms = array_map('trim', $aTerms);
+
+ // 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';
+ $dotags = true;
+ } else {
+ $dotags = false;
+ }
+
+ $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])
+ . '%"';
+ //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 .= ')';
+ }
+ }
+
+ // Start and end dates
+ if ($startdate) {
+ $query_4 .= ' AND B.bDatetime > "'. $startdate .'"';
+ }
+ if ($enddate) {
+ $query_4 .= ' AND B.bDatetime < "'. $enddate .'"';
+ }
+
+ // Hash
+ if ($hash) {
+ $query_4 .= ' AND B.bHash = "'. $hash .'"';
+ }
+
+
+ $query = $query_1 . $query_2 . $query_3 . $query_4 . $query_5;
+
+ $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;
+ } else {
+ $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
+ );
+ }
+
+ $total = $row['total'];
+ $this->db->sql_freeresult($totalresult);
+
+ $bookmarks = array();
+ $bookmarkids = array();
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $bookmarks[] = $row;
+ $bookmarkids[] = $row['bId'];
+ }
+ if (count($bookmarkids)) {
+ $tags = $b2tservice->getTagsForBookmarks($bookmarkids);
+ foreach ($bookmarks as &$bookmark) {
+ $bookmark['tags'] = $tags[$bookmark['bId']];
+ }
+ }
+
+ $this->db->sql_freeresult($dbresult);
+ $output = array ('bookmarks' => $bookmarks, 'total' => $total);
+ return $output;
+ }
+
+
+
+ /**
+ * Delete the bookmark with the given id.
+ * Also deletes tags and votes for the given bookmark.
+ *
+ * @param integer $bookmark Bookmark ID
+ *
+ * @return boolean True if all went well, false if not
+ */
+ public function deleteBookmark($bookmark)
+ {
+ $bookmark = (int)$bookmark;
+
+ $query = 'DELETE FROM ' . $GLOBALS['tableprefix'] . 'bookmarks'
+ . ' WHERE bId = '. $bookmark;
+ $this->db->sql_transaction('begin');
+ if (!($dbres = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not delete bookmark',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ }
+
+ $query = 'DELETE FROM ' . $GLOBALS['tableprefix'] . 'bookmarks2tags'
+ . ' WHERE bId = '. $bookmark;
+ $this->db->sql_transaction('begin');
+ if (!($dbres = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not delete tags for bookmark',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ }
+
+ $query = 'DELETE FROM '. $GLOBALS['tableprefix'] .'votes'
+ . ' WHERE bid = '. $bookmark;
+ $this->db->sql_transaction('begin');
+ if (!($dbres = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not delete votes for bookmark',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ }
+
+ $this->db->sql_transaction('commit');
+
+ return true;
+ }
+
+
+
+ /**
+ * Deletes all bookmarks of the given user
+ *
+ * @param integer $uId User ID
+ *
+ * @return boolean true when all went well
+ */
+ public function deleteBookmarksForUser($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
+ );
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * Counts the number of bookmarks that have the same address
+ * as the given address.
+ *
+ * @param string|array $addresses Address/URL to look for, string
+ * of one address or array with
+ * multiple ones
+ *
+ * @return integer Number of bookmarks minus one that have the address.
+ * 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)
+ {
+ if (!$addresses) {
+ return false;
+ }
+ $bArray = is_array($addresses);
+
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $sId = (int)$us->getCurrentUserId();
+
+ if ($us->isLoggedOn()) {
+ //All public bookmarks, user's own bookmarks
+ // and any shared with our user
+ $privacy = ' AND ((B.bStatus = 0) OR (B.uId = ' . $sId . ')';
+ $watchnames = $us->getWatchNames($sId, true);
+ foreach ($watchnames as $watchuser) {
+ $privacy .= ' OR (U.username = "'
+ . $this->db->sql_escape($watchuser)
+ . '" AND B.bStatus = 1)';
+ }
+ $privacy .= ')';
+ } else {
+ //Just public bookmarks
+ $privacy = ' AND B.bStatus = 0';
+ }
+
+ $addressesSql = ' AND (0';
+ foreach ((array)$addresses as $address) {
+ $addressesSql .= ' OR B.bHash = "'
+ . $this->db->sql_escape($this->getHash($address))
+ . '"';
+ }
+ $addressesSql .= ')';
+
+
+ $sql = 'SELECT B.bAddress, COUNT(*) as count FROM '
+ . $us->getTableName() . ' AS U'
+ . ', '. $GLOBALS['tableprefix'] . 'bookmarks AS B'
+ . ' WHERE U.'. $us->getFieldName('primary') .' = B.uId'
+ . $addressesSql
+ . $privacy
+ . ' GROUP BY B.bHash';
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get other count',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+
+ //be sure we also list urls in our array
+ // that are not found in the database
+ $counts = array_combine(
+ (array)$addresses,
+ array_fill(0, count((array)$addresses), 0)
+ );
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $counts[$row['bAddress']]
+ = $row['count'] > 0 ? $row['count'] - 1 : 0;
+ }
+ $this->db->sql_freeresult($dbresult);
+
+ return $bArray ? $counts : reset($counts);
+ }
+
+
+
+ /**
+ * Normalizes a given address.
+ * Prepends http:// if there is no protocol specified,
+ * and removes the trailing slash
+ *
+ * @param string $address URL to check
+ *
+ * @return string Fixed URL
+ */
+ public function normalize($address)
+ {
+ //you know, there is "callto:" and "mailto:"
+ if (strpos($address, ':') === false) {
+ $address = 'http://'. $address;
+ }
+
+ // Delete final /
+ if (substr($address, -1) == '/') {
+ $address = substr($address, 0, count($address)-2);
+ }
+
+ return $address;
+ }
+
+
+
+ /**
+ * Delete all bookmarks.
+ * Mainly used in unit tests.
+ *
+ * @return void
+ */
+ public function deleteAll()
+ {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+}
+
+?>
diff --git a/src/SemanticScuttle/Service/Bookmark2Tag.php b/src/SemanticScuttle/Service/Bookmark2Tag.php
new file mode 100644
index 0000000..a01b5d7
--- /dev/null
+++ b/src/SemanticScuttle/Service/Bookmark2Tag.php
@@ -0,0 +1,714 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle bookmark-tag combination service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ public function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'bookmarks2tags';
+ }
+
+ function isNotSystemTag($var) {
+ if (utf8_substr($var, 0, 7) == 'system:')
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Attach tags to a bookmark.
+ *
+ * Make sure that categories is an array of trimmed strings.
+ * If the categories are coming in from an API call, be sure
+ * that underscores are converted into strings.
+ *
+ * @param integer $bookmarkid ID of the bookmark
+ * @param array $tags Array of tags (strings, trimmed)
+ * @param boolean $fromApi If this is from an API call
+ * @param string $extension File extension (i.e. 'pdf')
+ * @param boolean $replace If existing tags for this bookmark
+ * are to be replaced
+ * @param boolean $fromImport If this is from a file import
+ *
+ * @return boolean True if all went well
+ */
+ public function attachTags(
+ $bookmarkid, $tags, $fromApi = false,
+ $extension = null, $replace = true, $fromImport = false
+ ) {
+ if (!is_array($tags)) {
+ $tags = trim($tags);
+ if ($tags != '') {
+ if (substr($tags, -1) == ',') {
+ $tags = substr($tags, 0, -1);
+ }
+ if ($fromApi) {
+ $tags = explode(' ', $tags);
+ } else {
+ $tags = explode(',', $tags);
+ }
+ } else {
+ $tags = null;
+ }
+ }
+
+ $tagservice = SemanticScuttle_Service_Factory::get('Tag');
+ $tags = $tagservice->normalize($tags);
+
+ $tags_count = is_array($tags)?count($tags):0;
+ if (is_array($tags)) {
+ foreach ($tags as $i => $tag) {
+ $tags[$i] = trim(utf8_strtolower($tags[$i]));
+ if ($fromApi) {
+ $tags[$i] = convertTag($tags[$i], 'in');
+ }
+ }
+ }
+
+ if ($tags_count > 0) {
+ // Remove system tags
+ $tags = array_filter($tags, array($this, "isNotSystemTag"));
+
+ // Eliminate any duplicate categories
+ $temp = array_unique($tags);
+ $tags = array_values($temp);
+ } else {
+ // Unfiled
+ $tags[] = 'system:unfiled';
+ }
+
+ // Media and file types
+ if (!is_null($extension)) {
+ include_once 'SemanticScuttle/functions.php';
+
+ if ($keys = multi_array_search($extension, $GLOBALS['filetypes'])) {
+ $tags[] = 'system:filetype:'. $extension;
+ $tags[] = 'system:media:'. array_shift($keys);
+ }
+ }
+
+ // Imported
+ if ($fromImport) {
+ $tags[] = 'system:imported';
+ }
+
+ $this->db->sql_transaction('begin');
+
+ if ($replace) {
+ if (!$this->deleteTagsForBookmark($bookmarkid)){
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not attach tags (deleting old ones failed)', '', __LINE__, __FILE__, $sql, $this->db);
+ return false;
+ }
+ }
+
+ $bs = SemanticScuttle_Service_Factory::get('Bookmark');
+ $tts = SemanticScuttle_Service_Factory::get('Tag2Tag');
+
+ // Create links between tags
+ foreach ($tags as $key => $tag) {
+ if (strpos($tag, '=')) {
+ // case "="
+ $pieces = explode('=', $tag);
+ $nbPieces = count($pieces);
+ if ($nbPieces <= 1) {
+ continue;
+ }
+ for ($i = 0; $i < $nbPieces-1; $i++) {
+ $bookmark = $bs->getBookmark($bookmarkid);
+ $uId = $bookmark['uId'];
+ $tts->addLinkedTags($pieces[$i], $pieces[$i+1], '=', $uId);
+ }
+ // Attach just the last tag to the bookmark
+ $tags[$key] = $pieces[0];
+ } else {
+ // case ">"
+ $pieces = explode('>', $tag);
+ $nbPieces = count($pieces);
+ if ($nbPieces <= 1) {
+ continue;
+ }
+ for ($i = 0; $i < $nbPieces-1; $i++) {
+ $bookmark = $bs->getBookmark($bookmarkid);
+ $uId = $bookmark['uId'];
+ $tts->addLinkedTags($pieces[$i], $pieces[$i+1], '>', $uId);
+ }
+ // Attach just the last tag to the bookmark
+ $tags[$key] = $pieces[$nbPieces-1];
+ }
+ }
+
+ //after exploding, there may be duplicate keys
+ //since we are in a transaction, hasTag() may
+ // not return true for newly added duplicate tags
+ $tags = array_unique($tags);
+
+ // Add the tags to the DB.
+ foreach ($tags as $tag) {
+ if ($tag == '') {
+ continue;
+ }
+ if ($this->hasTag($bookmarkid, $tag)) {
+ continue;
+ }
+
+ $values = array(
+ 'bId' => intval($bookmarkid),
+ 'tag' => $tag
+ );
+
+ $sql = 'INSERT INTO '. $this->getTableName()
+ . ' ' . $this->db->sql_build_array('INSERT', $values);
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not attach tags',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ return false;
+ }
+ }
+ $this->db->sql_transaction('commit');
+ return true;
+ }
+
+ function deleteTag($uId, $tag) {
+ $bs =SemanticScuttle_Service_Factory::get('Bookmark');
+
+ $query = 'DELETE FROM '. $this->getTableName();
+ $query.= ' USING '. $this->getTableName() .', '. $bs->getTableName();
+ $query.= ' WHERE '. $this->getTableName() .'.bId = '. $bs->getTableName() .'.bId';
+ $query.= ' AND '. $bs->getTableName() .'.uId = '. $uId;
+ $query.= ' AND '. $this->getTableName() .'.tag = "'. $this->db->sql_escape($tag) .'"';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not delete tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+ function deleteTagsForBookmark($bookmarkid) {
+ if (!is_int($bookmarkid)) {
+ message_die(GENERAL_ERROR, 'Could not delete tags (invalid bookmarkid)', '', __LINE__, __FILE__, $query);
+ return false;
+ }
+
+ $query = 'DELETE FROM '. $this->getTableName() .' WHERE bId = '. intval($bookmarkid);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not delete tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+ /* Allow deletion in admin page */
+ function deleteTagsForUser($uId) {
+ $qmask = 'DELETE FROM %s USING %s, %s WHERE %s.bId = %s.bId AND %s.uId = %d';
+ $query = sprintf($qmask,
+ $this->getTableName(),
+ $this->getTableName(),
+ $GLOBALS['tableprefix'].'bookmarks',
+ $this->getTableName(),
+ $GLOBALS['tableprefix'].'bookmarks',
+ $GLOBALS['tableprefix'].'bookmarks',
+ $uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not delete tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Retrieves all tags for a given bookmark except system tags.
+ *
+ * @param integer $bookmarkid ID of the bookmark
+ * @param boolean $systemTags Return "system:*" tags or not
+ *
+ * @return array Array of tags
+ */
+ public function getTagsForBookmark($bookmarkid, $systemTags = false)
+ {
+ if (!is_numeric($bookmarkid)) {
+ message_die(
+ GENERAL_ERROR, 'Could not get tags (invalid bookmarkid)',
+ '', __LINE__, __FILE__, $query
+ );
+ return false;
+ }
+
+ $query = 'SELECT tag FROM ' . $this->getTableName()
+ . ' WHERE bId = ' . intval($bookmarkid);
+ if (!$systemTags) {
+ $query .= ' AND LEFT(tag, 7) <> "system:"';
+ }
+ $query .= ' ORDER BY id ASC';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get tags',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $tags = array();
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $tags[] = $row['tag'];
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $tags;
+ }
+
+
+ /**
+ * Retrieves all tags for an array of bookmark IDs
+ *
+ * @param array $bookmarkids Array of bookmark IDs
+ *
+ * @return array Array of tag arrays. Key is bookmark ID.
+ */
+ public function getTagsForBookmarks($bookmarkids)
+ {
+ if (!is_array($bookmarkids)) {
+ message_die(
+ GENERAL_ERROR, 'Could not get tags (invalid bookmarkids)',
+ '', __LINE__, __FILE__, $query
+ );
+ return false;
+ } else if (count($bookmarkids) == 0) {
+ return array();
+ }
+
+ $query = 'SELECT tag, bId FROM ' . $this->getTableName()
+ . ' WHERE bId IN (' . implode(',', $bookmarkids) . ')'
+ . ' AND LEFT(tag, 7) <> "system:"'
+ . ' ORDER BY id, bId ASC';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get tags',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $tags = array_combine(
+ $bookmarkids,
+ array_fill(0, count($bookmarkids), array())
+ );
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $tags[$row['bId']][] = $row['tag'];
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $tags;
+ }
+
+
+ function &getTags($userid = NULL) {
+ $userservice =SemanticScuttle_Service_Factory::get('User');
+ $logged_on_user = $userservice->getCurrentUserId();
+
+ $query = 'SELECT T.tag, COUNT(B.bId) AS bCount FROM '. $GLOBALS['tableprefix'] .'bookmarks AS B INNER JOIN '. $userservice->getTableName() .' AS U ON B.uId = U.'. $userservice->getFieldName('primary') .' INNER JOIN '. $GLOBALS['tableprefix'] .'bookmarks2tags AS T ON B.bId = T.bId';
+
+ $conditions = array();
+ if (!is_null($userid)) {
+ $conditions['U.'. $userservice->getFieldName('primary')] = intval($userid);
+ if ($logged_on_user != $userid)
+ $conditions['B.bStatus'] = 0;
+ } else {
+ $conditions['B.bStatus'] = 0;
+ }
+
+ $query .= ' WHERE '. $this->db->sql_build_array('SELECT', $conditions) .' AND LEFT(T.tag, 7) <> "system:" GROUP BY T.tag ORDER BY bCount DESC, tag';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $output = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+
+ // Returns the tags related to the specified tags; i.e. attached to the same bookmarks
+ function &getRelatedTags($tags, $for_user = NULL, $logged_on_user = NULL, $limit = 10) {
+ $conditions = array();
+ // Only count the tags that are visible to the current user.
+ if ($for_user != $logged_on_user || is_null($for_user))
+ $conditions['B.bStatus'] = 0;
+
+ if (!is_null($for_user))
+ $conditions['B.uId'] = $for_user;
+
+ // Set up the tags, if need be.
+ if (is_numeric($tags))
+ $tags = NULL;
+ if (!is_array($tags) and !is_null($tags))
+ $tags = explode('+', trim($tags));
+
+ $tagcount = count($tags);
+ for ($i = 0; $i < $tagcount; $i++) {
+ $tags[$i] = trim($tags[$i]);
+ }
+
+ // Set up the SQL query.
+ $query_1 = 'SELECT DISTINCTROW T0.tag, COUNT(B.bId) AS bCount FROM '. $GLOBALS['tableprefix'] .'bookmarks AS B, '. $this->getTableName() .' AS T0';
+ $query_2 = '';
+ $query_3 = ' WHERE B.bId = T0.bId ';
+ if (count($conditions) > 0)
+ $query_4 = ' AND '. $this->db->sql_build_array('SELECT', $conditions);
+ else
+ $query_4 = '';
+ // Handle the parts of the query that depend on any tags that are present.
+ for ($i = 1; $i <= $tagcount; $i++) {
+ $query_2 .= ', '. $this->getTableName() .' AS T'. $i;
+ $query_4 .= ' AND T'. $i .'.bId = B.bId AND T'. $i .'.tag = "'. $this->db->sql_escape($tags[$i - 1]) .'" AND T0.tag <> "'. $this->db->sql_escape($tags[$i - 1]) .'"';
+ }
+ $query_5 = ' AND LEFT(T0.tag, 7) <> "system:" GROUP BY T0.tag ORDER BY bCount DESC, T0.tag';
+ $query = $query_1 . $query_2 . $query_3 . $query_4 . $query_5;
+
+ if (! ($dbresult = $this->db->sql_query_limit($query, $limit)) ){
+ message_die(GENERAL_ERROR, 'Could not get related tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $output = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+ // Returns the most popular tags used for a particular bookmark hash
+ function &getRelatedTagsByHash($hash, $limit = 20) {
+ $userservice = SemanticScuttle_Service_Factory :: get('User');
+ $sId = $userservice->getCurrentUserId();
+ // Logged in
+ if ($userservice->isLoggedOn()) {
+ $arrWatch = $userservice->getWatchList($sId);
+ // From public bookmarks or user's own
+ $privacy = ' AND ((B.bStatus = 0) OR (B.uId = '. $sId .')';
+ // From shared bookmarks in watchlist
+ foreach ($arrWatch as $w) {
+ $privacy .= ' OR (B.uId = '. $w .' AND B.bStatus = 1)';
+ }
+ $privacy .= ') ';
+ // Not logged in
+ } else {
+ $privacy = ' AND B.bStatus = 0 ';
+ }
+
+ $query = 'SELECT T.tag, COUNT(T.tag) AS bCount FROM '.$GLOBALS['tableprefix'].'bookmarks AS B LEFT JOIN '.$GLOBALS['tableprefix'].'bookmarks2tags AS T ON B.bId = T.bId WHERE B.bHash = \''. $this->db->sql_escape($hash) .'\' '. $privacy .'AND LEFT(T.tag, 7) <> "system:" GROUP BY T.tag ORDER BY bCount DESC';
+
+ if (!($dbresult = $this->db->sql_query_limit($query, $limit))) {
+ message_die(GENERAL_ERROR, 'Could not get related tags for this hash', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $output = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+
+
+ /**
+ * Returns the tags used by admin users
+ *
+ * @param integer $limit Number of tags to return
+ * @param integer $logged_on_user ID of the user that's currently logged in.
+ * If the logged in user equals the $user to find
+ * tags for, tags of private bookmarks are
+ * returned.
+ * @param integer $days Bookmarks have to be changed in the last X days
+ * if their tags shall count
+ * @param string $beginsWith The tag name shall begin with that string
+ *
+ * @return array Array of found tags. Each tag entry is an array with two keys,
+ * 'tag' (tag name) and 'bCount'.
+ *
+ * @see getPopularTags()
+ */
+ public function getAdminTags(
+ $limit = 30, $logged_on_user = null, $days = null, $beginsWith = null
+ ) {
+ // look for admin ids
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+ $adminIds = $userservice->getAdminIds();
+
+ // ask for their tags
+ return $this->getPopularTags(
+ $adminIds, $limit, $logged_on_user, $days, $beginsWith
+ );
+ }
+
+
+
+
+ /**
+ * Returns the tags used by users that are part of the user's watchlist,
+ * and the current user's own tags.
+ *
+ * @param integer $user ID of the user to get the watchlist from
+ * @param integer $limit Number of tags to return
+ * @param integer $logged_on_user ID of the user that's currently logged in.
+ * If set, that user is added to the list of
+ * people to get the tags from
+ * @param integer $days Bookmarks have to be changed in the last X days
+ * if their tags shall count
+ * @param string $beginsWith The tag name shall begin with that string
+ *
+ * @return array Array of found tags. Each tag entry is an array with two keys,
+ * 'tag' (tag name) and 'bCount'.
+ *
+ * @see getPopularTags()
+ */
+ public function getContactTags(
+ $user, $limit = 30, $logged_on_user = null, $days = null,
+ $beginsWith = null
+ ) {
+ // look for contact ids
+ $userservice = SemanticScuttle_Service_Factory::get('User');
+ $contacts = $userservice->getWatchlist($user);
+
+ // add the user (to show him also his own tags)
+ if (!is_null($logged_on_user)) {
+ $contacts[] = $logged_on_user;
+ }
+
+ // ask for their tags
+ return $this->getPopularTags(
+ $contacts, $limit, $logged_on_user, $days, $beginsWith
+ );
+ }
+
+
+
+ /**
+ * The the most popular tags and their usage count
+ *
+ * @param mixed $user Integer user ID or array of user IDs to limit tag
+ * finding to
+ * @param integer $limit Number of tags to return
+ * @param integer $logged_on_user ID of the user that's currently logged in.
+ * If the logged in user equals the $user to find
+ * tags for, tags of private bookmarks are
+ * returned.
+ * @param integer $days Bookmarks have to be changed in the last X days
+ * if their tags shall count
+ * @param string $beginsWith The tag name shall begin with that string
+ *
+ * @return array Array of found tags. Each tag entry is an array with two keys,
+ * 'tag' (tag name) and 'bCount'.
+ *
+ * @see getAdminTags()
+ * @see getContactTags()
+ */
+ public function getPopularTags(
+ $user = null, $limit = 30, $logged_on_user = null, $days = null,
+ $beginsWith = null
+ ) {
+ $query = 'SELECT'
+ . ' T.tag, COUNT(T.bId) AS bCount'
+ . ' FROM '
+ . $this->getTableName() . ' AS T'
+ . ', ' . $GLOBALS['tableprefix'] . 'bookmarks AS B'
+ . ' WHERE';
+
+ if (is_null($user) || $user === false) {
+ $query .= ' B.bId = T.bId AND B.bStatus = 0';
+ } else if (is_array($user)) {
+ $query .= ' (1 = 0'; //tricks
+ foreach ($user as $u) {
+ if (!is_numeric($u)) {
+ continue;
+ }
+ $query .= ' OR ('
+ . ' B.uId = ' . $this->db->sql_escape($u)
+ . ' AND B.bId = T.bId';
+ if ($u !== $logged_on_user) {
+ //public bookmarks of others
+ $query .= ' AND B.bStatus = 0';
+ }
+ $query .= ')';
+ }
+ $query .= ' )';
+ } else {
+ $query .= ' B.uId = ' . $this->db->sql_escape($user)
+ . ' AND B.bId = T.bId';
+ if ($user !== $logged_on_user) {
+ $query .= ' AND B.bStatus = 0';
+ }
+ }
+
+ if (is_int($days)) {
+ $query .= ' AND B.bDatetime > "'
+ . gmdate('Y-m-d H:i:s', time() - (86400 * $days))
+ . '"';
+ }
+
+ if (!is_null($beginsWith)) {
+ $query .= ' AND T.tag LIKE \''
+ . $this->db->sql_escape($beginsWith)
+ . '%\'';
+ }
+
+ $query .= ' AND LEFT(T.tag, 7) <> "system:"'
+ . ' GROUP BY T.tag'
+ . ' ORDER BY bCount DESC, tag';
+
+ if (!($dbresult = $this->db->sql_query_limit($query, $limit))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get popular tags',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $output = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+
+
+ function hasTag($bookmarkid, $tag) {
+ $query = 'SELECT COUNT(*) AS tCount FROM '. $this->getTableName() .' WHERE bId = '. intval($bookmarkid) .' AND tag ="'. $this->db->sql_escape($tag) .'"';
+
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(GENERAL_ERROR, 'Could not find tag', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $output = false;
+ if ($row = $this->db->sql_fetchrow($dbresult)) {
+ if ($row['tCount'] > 0) {
+ $output = true;
+ }
+ }
+
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+ function renameTag($userid, $old, $new, $fromApi = false) {
+ $bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+
+ if (is_null($userid) || is_null($old) || is_null($new))
+ return false;
+
+ // Find bookmarks with old tag
+ $bookmarksInfo = $bookmarkservice->getBookmarks(0, NULL, $userid, $old);
+ $bookmarks = $bookmarksInfo['bookmarks'];
+
+ // Delete old tag
+ $this->deleteTag($userid, $old);
+
+ // Attach new tags
+ $new = $tagservice->normalize($new);
+
+ foreach (array_keys($bookmarks) as $key) {
+ $row = $bookmarks[$key];
+ $this->attachTags($row['bId'], $new, $fromApi, NULL, false);
+ }
+
+ return true;
+ }
+
+ function &tagCloud($tags = NULL, $steps = 5, $sizemin = 90, $sizemax = 225, $sortOrder = NULL) {
+
+ if (is_null($tags) || count($tags) < 1) {
+ $output = false;
+ return $output;
+ }
+
+ $min = $tags[count($tags) - 1]['bCount'];
+ $max = $tags[0]['bCount'];
+
+ for ($i = 1; $i <= $steps; $i++) {
+ $delta = ($max - $min) / (2 * $steps - $i);
+ $limit[$i] = $i * $delta + $min;
+ }
+ $sizestep = ($sizemax - $sizemin) / $steps;
+ foreach ($tags as $row) {
+ $next = false;
+ for ($i = 1; $i <= $steps; $i++) {
+ if (!$next && $row['bCount'] <= $limit[$i]) {
+ $size = $sizestep * ($i - 1) + $sizemin;
+ $next = true;
+ }
+ }
+ $tempArray = array('size' => $size .'%');
+ $row = array_merge($row, $tempArray);
+ $output[] = $row;
+ }
+
+ if ($sortOrder == 'alphabet_asc') {
+ usort($output, create_function('$a,$b','return strcasecmp(utf8_deaccent($a["tag"]), utf8_deaccent($b["tag"]));'));
+ }
+
+ return $output;
+ }
+
+
+
+ /**
+ * Deletes all tags in bookmarks2tags
+ *
+ * @return void
+ */
+ public function deleteAll()
+ {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/Cache.php b/src/SemanticScuttle/Service/Cache.php
new file mode 100644
index 0000000..fa719d3
--- /dev/null
+++ b/src/SemanticScuttle/Service/Cache.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle caching service
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Cache extends SemanticScuttle_Service
+{
+ var $basedir;
+ var $fileextension = '.cache';
+
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ protected function __construct()
+ {
+ $this->basedir = $GLOBALS['dir_cache'];
+ }
+
+ function Start($hash, $time = 300) {
+ $cachefile = $this->basedir .'/'. $hash . $this->fileextension;
+ if (file_exists($cachefile) && time() < filemtime($cachefile) + $time) {
+ @readfile($cachefile);
+ echo "\n<!-- Cached: ". date('r', filemtime($cachefile)) ." -->\n";
+ unset($cachefile);
+ exit;
+ }
+ ob_start("ob_gzhandler");
+ }
+
+ function End($hash) {
+ $cachefile = $this->basedir .'/'. $hash . $this->fileextension;
+ $handle = fopen($cachefile, 'w');
+ fwrite($handle, ob_get_contents());
+ fclose($handle);
+ ob_flush();
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service/CommonDescription.php b/src/SemanticScuttle/Service/CommonDescription.php
new file mode 100644
index 0000000..d03b34a
--- /dev/null
+++ b/src/SemanticScuttle/Service/CommonDescription.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle common bookmark description service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_CommonDescription extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ public function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'commondescription';
+ }
+
+ function addTagDescription($tag, $desc, $uId, $time) {
+ $tag = self::getSortedTag($tag);
+
+ // Check if no modification
+ $lastDesc = $this->getLastTagDescription($tag);
+ if($lastDesc['cdDescription'] == $desc) {
+ return true;
+ }
+
+ // If modification
+ $datetime = gmdate('Y-m-d H:i:s', $time);
+ $values = array('tag'=>$tag, 'cdDescription'=>$desc, 'uId'=>$uId, 'cdDatetime'=>$datetime);
+ $sql = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values);
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not add tag description', '', __LINE__, __FILE__, $sql, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+ function getLastTagDescription($tag) {
+ $tag = self::getSortedTag($tag);
+
+ $query = "SELECT *";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= ' WHERE tag=\'' . $this->db->sql_escape($tag) . "'";
+ $query.= " ORDER BY cdDatetime DESC";
+
+ if (!($dbresult = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(GENERAL_ERROR, 'Could not get tag description', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ if ($row) {
+ return $row;
+ } else {
+ return false;
+ }
+ }
+
+ function getAllTagsDescription($tag) {
+ $query = "SELECT *";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= ' WHERE tag=\'' . $this->db->sql_escape($tag) . "'";
+ $query.= " ORDER BY cdDatetime DESC";
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get tag descriptions', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $res = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $res;
+ }
+
+ function getDescriptionById($cdId) {
+ $query = "SELECT *";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= ' WHERE cdId=\'' . $this->db->sql_escape($cdId) . "'";
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get tag descriptions', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ if ($row = $this->db->sql_fetchrow($dbresult)) {
+ $this->db->sql_freeresult($dbresult);
+ return $row;
+ } else {
+ return false;
+ }
+
+ }
+
+ function addBookmarkDescription($bHash, $title, $desc, $uId, $time) {
+ // Check if no modification
+ $lastDesc = $this->getLastBookmarkDescription($bHash);
+ if($lastDesc['cdTitle'] == $title && $lastDesc['cdDescription'] == $desc) {
+ return true;
+ }
+
+ // If modification
+ $datetime = gmdate('Y-m-d H:i:s', $time);
+ $values = array('bHash'=>$bHash, 'cdTitle'=>$title, 'cdDescription'=>$desc, 'uId'=>$uId, 'cdDatetime'=>$datetime);
+ $sql = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values);
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not add bookmark description', '', __LINE__, __FILE__, $sql, $this->db);
+ return false;
+ }
+ return true;
+ }
+
+ function getLastBookmarkDescription($bHash) {
+ $query = "SELECT *";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= ' WHERE bHash=\'' . $this->db->sql_escape($bHash) . "'";
+ $query.= " ORDER BY cdDatetime DESC";
+
+ if (!($dbresult = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(GENERAL_ERROR, 'Could not get bookmark description', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ if ($row) {
+ return $row;
+ } else {
+ return false;
+ }
+ }
+
+ function getAllBookmarksDescription($bHash) {
+ $query = "SELECT *";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= ' WHERE bHash=\'' . $this->db->sql_escape($bHash) . "'";
+ $query.= " ORDER BY cdDatetime DESC";
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get bookmark descriptions', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $rowset = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $rowset;
+ }
+
+ function deleteDescriptionsForUser($uId){
+ $query = 'DELETE FROM '. $this->getTableName() . ' WHERE uId = '. intval($uId);
+
+ $this->db->sql_transaction('begin');
+ if (!($dbresult = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not delete user descriptions', '',
+ __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ function deleteAll() {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+
+
+ /**
+ * Sorts a tag combination.
+ *
+ * Multiple tags are separated by "+" signs. Semantically,
+ * tag combinations like "blue+flower" and "flower+blue" are identical.
+ * This method sorts the single tags so i.e. the common bookmark description
+ * of "blue+flower" is also shown for "flower+blue".
+ *
+ * @param string $tag Single tag or tag combination ("+")
+ *
+ * @return string Sorted tag combination
+ */
+ protected static function getSortedTag($tag)
+ {
+ $tags = explode('+', $tag);
+ sort($tags);
+ return implode('+', $tags);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/Factory.php b/src/SemanticScuttle/Service/Factory.php
new file mode 100644
index 0000000..b661cdb
--- /dev/null
+++ b/src/SemanticScuttle/Service/Factory.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Connect to the database and build services.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Factory
+{
+ /**
+ * Array of service instances.
+ * Key is service name (i.e. "Bookmark")
+ *
+ * @var array
+ */
+ protected static $instances = array();
+
+ /**
+ * Database connection
+ *
+ * @var sql_qb
+ */
+ protected static $db = null;
+
+ /**
+ * Array of service names -> new service class
+ * in case you want to overwrite the services.
+ *
+ * Key is the old service name (i.e. "Bookmark"),
+ * value the new class name, e.g.
+ * "My_Cool_Own_BookmarkService"
+ *
+ * @var array
+ */
+ public static $serviceoverrides = array();
+
+
+
+ /**
+ * Returns the service for the given name.
+ *
+ * @param string $name Service name (i.e. "Bookmark")
+ *
+ * @return SemanticScuttle_Service Service object
+ */
+ public static function get($name)
+ {
+ self::loadDb();
+ self::loadService($name);
+ return self::$instances[$name];
+ }
+
+
+
+ /**
+ * Loads service with the given name into
+ * self::$instances[$name].
+ *
+ * @param string $name Service name (i.e. 'Bookmark')
+ *
+ * @return void
+ */
+ protected static function loadService($name)
+ {
+ if (isset(self::$instances[$name])) {
+ return;
+ }
+
+ if (isset(self::$serviceoverrides[$name])) {
+ $class = self::$serviceoverrides[$name];
+ } else {
+ $class = 'SemanticScuttle_Service_' . $name;
+ }
+
+ if (!class_exists($class)) {
+ //PEAR classname to filename rule
+ $file = str_replace('_', '/', $class) . '.php';
+ include_once $file;
+ }
+
+ self::$instances[$name] = call_user_func(
+ array($class, 'getInstance'),
+ self::$db
+ );
+ }
+
+
+
+ /**
+ * Loads self::$db if it is not loaded already.
+ * Dies if the connection could not be established.
+ *
+ * @return void
+ */
+ protected static function loadDb()
+ {
+ global $dbhost, $dbuser, $dbpass, $dbname,
+ $dbport, $dbpersist, $dbtype, $dbneedssetnames;
+
+ if (self::$db !== null) {
+ return;
+ }
+ include_once 'SemanticScuttle/db/'. $dbtype .'.php';
+ $db = new sql_db();
+ $db->sql_connect(
+ $dbhost, $dbuser, $dbpass, $dbname, $dbport, $dbpersist
+ );
+ if (!$db->db_connect_id) {
+ message_die(
+ CRITICAL_ERROR,
+ 'Could not connect to the database',
+ self::$db
+ );
+ }
+
+ $dbneedssetnames && $db->sql_query('SET NAMES UTF8');
+
+ self::$db = $db;
+ }
+
+
+
+ /**
+ * Returns sql database object
+ *
+ * @return sql_db Database instance
+ */
+ public static function getDb()
+ {
+ self::loadDb();
+ return self::$db;
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/SearchHistory.php b/src/SemanticScuttle/Service/SearchHistory.php
new file mode 100644
index 0000000..ac0b1c7
--- /dev/null
+++ b/src/SemanticScuttle/Service/SearchHistory.php
@@ -0,0 +1,280 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle search history service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class 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;
+
+
+
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $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';
+ if (isset($GLOBALS['sizeSearchHistory'])) {
+ $this->sizeSearchHistory = $GLOBALS['sizeSearchHistory'];
+ } else {
+ $this->sizeSearchHistory = 10;
+ }
+ }
+
+
+
+ /**
+ * 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
+ * - '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);
+
+ $this->db->sql_transaction('begin');
+ 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
+ );
+ return false;
+ }
+
+ if ($this->sizeSearchHistory != -1
+ && $this->countSearches() > $this->sizeSearchHistory
+ ) {
+ $this->deleteOldestSearch();
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * 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. null for any 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) {
+ $sql.= ' AND shRange = "'.$range.'"';
+ } else {
+ $sql.= ' AND shRange = "all"';
+ }
+ if ($uId != null) {
+ $sql.= ' AND uId = '.$uId;
+ }
+ if ($withResults == true) {
+ $sql.= ' AND shNbResults > 0';
+ }
+ 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
+ );
+ return false;
+ }
+
+ $searches = array();
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $searches[] = $row;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $searches;
+ }
+
+
+
+ /**
+ * 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
+ );
+ 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
+ *
+ * @return boolean True when all went well, false in case of an error
+ */
+ public function deleteOldestSearch()
+ {
+ $sql = 'DELETE FROM '.$this->getTableName();
+ // 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))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not delete bookmarks',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * 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);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/Tag.php b/src/SemanticScuttle/Service/Tag.php
new file mode 100644
index 0000000..bd6bf70
--- /dev/null
+++ b/src/SemanticScuttle/Service/Tag.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle tag management service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Tag extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ public function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'tags';
+ }
+
+ function getDescription($tag, $uId) {
+ $query = 'SELECT tag, uId, tDescription';
+ $query.= ' FROM '.$this->getTableName();
+ $query.= ' WHERE tag = \''. $this->db->sql_escape($tag) . "'";
+ $query.= ' AND uId = ' . intval($uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get tag description', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ if ($row) {
+ return $row;
+ } else {
+ return array('tDescription'=>'');
+ }
+ }
+
+ function existsDescription($tag, $uId) {
+ $query = 'SELECT tag, uId, tDescription';
+ $query.= ' FROM '.$this->getTableName();
+ $query.= ' WHERE tag = \'' . $this->db->sql_escape($tag) . "'";
+ $query.= ' AND uId = "' . intval($uId) . '"';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get tag description', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ if ($row) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function getAllDescriptions($tag) {
+ $query = 'SELECT tag, uId, tDescription';
+ $query.= ' FROM '.$this->getTableName();
+ $query.= ' WHERE tag = \''. $this->db->sql_escape($tag) . "'";
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not get tag description', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $rowset = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $rowset;
+ }
+
+ function updateDescription($tag, $uId, $desc) {
+ if($this->existsDescription($tag, $uId)) {
+ $query = 'UPDATE '.$this->getTableName();
+ $query.= ' SET tDescription= \'' . $this->db->sql_escape($desc) . "'";
+ $query.= ' WHERE tag=\'' . $this->db->sql_escape($tag) . "' AND uId=" . intval($uId);
+ } else {
+ $values = array('tag'=>$tag, 'uId'=>$uId, 'tDescription'=>$desc);
+ $query = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values);
+ }
+
+ $this->db->sql_transaction('begin');
+ if (!($dbresult = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not delete bookmarks', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $this->db->sql_transaction('commit');
+ return true;
+ }
+
+ function renameTag($uId, $oldName, $newName) {
+ $newname = $this->normalize($newName);
+
+ $query = 'UPDATE `'. $this->getTableName() .'`';
+ $query.= ' SET tag=\'' . $this->db->sql_escape($newName) . "'";
+ $query.= ' WHERE tag=\'' . $this->db->sql_escape($oldName) . "'";
+ $query.= ' AND uId=' . intval($uId);
+ $this->db->sql_query($query);
+ return true;
+ }
+
+ /* normalize the input tags which could be a string or an array*/
+ function normalize($tags) {
+ //clean tags from strange characters
+ $tags = str_replace(array('"', '\'', '/'), "_", $tags);
+
+ //normalize
+ if(!is_array($tags)) {
+ $tags = utf8_strtolower(trim($tags));
+ } else {
+ $tags = array_filter($tags);//remove empty values
+ foreach($tags as $i => $tag) {
+ $tags[$i] = utf8_strtolower(trim($tags[$i]));
+ }
+ }
+ return $tags;
+ }
+
+ function deleteAll() {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/Tag2Tag.php b/src/SemanticScuttle/Service/Tag2Tag.php
new file mode 100644
index 0000000..9dddc44
--- /dev/null
+++ b/src/SemanticScuttle/Service/Tag2Tag.php
@@ -0,0 +1,470 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle tag-to-tag service which works with tag relations.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Tag2Tag extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+
+ function __construct($db)
+ {
+ $this->db =$db;
+ $this->tablename = $GLOBALS['tableprefix'] .'tags2tags';
+ }
+
+ /**
+ * Add a tag-to-tag link relation
+ *
+ * @param string $tag1 First tag
+ * @param string $tag2 Second tag
+ * @param string $relationType Relation:
+ * "=" (both tags are equal)
+ * ">" (tag2 is part of tag1)
+ * @param integer $uId User ID
+ *
+ * @return boolean True when it was created, false in case of
+ * an error or when the link already existed.
+ */
+ public function addLinkedTags($tag1, $tag2, $relationType, $uId)
+ {
+ $tagservice = SemanticScuttle_Service_Factory::get('Tag');
+ $tag1 = $tagservice->normalize($tag1);
+ $tag2 = $tagservice->normalize($tag2);
+
+ if ($tag1 == $tag2 || strlen($tag1) == 0 || strlen($tag2) == 0
+ || ($relationType != '>' && $relationType != '=')
+ || !is_numeric($uId) || $uId<=0
+ || ($this->existsLinkedTags($tag1, $tag2, $relationType, $uId))
+ ) {
+ return false;
+ }
+
+ $values = array(
+ 'tag1' => $tag1,
+ 'tag2' => $tag2,
+ 'relationType' => $relationType,
+ 'uId' => $uId
+ );
+ $query = 'INSERT INTO ' . $this->getTableName()
+ . ' ' . $this->db->sql_build_array('INSERT', $values);
+
+ //die($query);
+ if (!($dbresult = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not attach tag to tag',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+ $this->db->sql_transaction('commit');
+
+ // Update stats and cache
+ $this->update($tag1, $tag2, $relationType, $uId);
+
+ return true;
+ }
+
+
+
+ /**
+ * Same as getLinkedTags(), but only tags that have been created
+ * by admin users are returned.
+ *
+ * @param string $tag Tag to get related tags for
+ * @param string $relationType Type of tag-to-tag relation: >, < or =
+ * @param boolean $inverseRelation Reverse relation (parent -> child)
+ * @param array $stopList Array of tags that shall not be returned
+ *
+ * @return array Array of tag names
+ *
+ * @see getLinkedTags()
+ */
+ public function getAdminLinkedTags(
+ $tag, $relationType, $inverseRelation = false, $stopList = array()
+ ) {
+ // look for admin ids
+ $userservice = SemanticScuttle_Service_Factory :: get('User');
+ $adminIds = $userservice->getAdminIds();
+
+ //ask for their linked tags
+ return $this->getLinkedTags(
+ $tag, $relationType, $adminIds, $inverseRelation, $stopList
+ );
+ }
+
+
+
+ /**
+ * Returns an array of tags that are in relation to the given $tag.
+ *
+ * @param string $tag Tag to get related tags for
+ * @param string $relationType Type of tag-to-tag relation: >, < or =
+ * @param boolean $inverseRelation Reverse relation (parent -> child)
+ * @param array $stopList Array of tags that shall not be returned
+ *
+ * @return array Array of tag names
+ */
+ public function getLinkedTags(
+ $tag, $relationType, $uId = null, $inverseRelation = false, $stopList = array()
+ ) {
+ // Set up the SQL query.
+ if($inverseRelation) {
+ $queriedTag = "tag1";
+ $givenTag = "tag2";
+ } else {
+ $queriedTag = "tag2";
+ $givenTag = "tag1";
+ }
+
+ $query = "SELECT DISTINCT ". $queriedTag ." as 'tag'";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= " WHERE 1=1";
+ if($tag !=null) {
+ $query.= " AND ". $givenTag ." = '". $this->db->sql_escape($tag) ."'";
+ }
+ if($relationType) {
+ $query.= " AND relationType = '". $this->db->sql_escape($relationType) ."'";
+ }
+ if(is_array($uId)) {
+ $query.= " AND ( 1=0 "; //tricks always false
+ foreach($uId as $u) {
+ $query.= " OR uId = '".intval($u)."'";
+ }
+ $query.= " ) ";
+ } elseif($uId != null) {
+ $query.= " AND uId = '".intval($uId)."'";
+ }
+ //die($query);
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(GENERAL_ERROR, 'Could not get related tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $rowset = $this->db->sql_fetchrowset($dbresult);
+ $output = array();
+ foreach($rowset as $row) {
+ if(!in_array($row['tag'], $stopList)) {
+ $output[] = $row['tag'];
+ }
+ }
+
+ //bijective case for '='
+ if($relationType == '=' && $inverseRelation == false) {
+ //$stopList[] = $tag;
+ $bijectiveOutput = $this->getLinkedTags($tag, $relationType, $uId, true, $stopList);
+ $output = array_merge($output, $bijectiveOutput);
+ //$output = array_unique($output); // remove duplication
+ }
+
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+ /*
+ * Returns all linked tags (all descendants if relation is >,
+ * all synonyms if relation is = )
+ * $stopList allows to avoid cycle (a > b > a) between tags
+ */
+ function getAllLinkedTags($tag1, $relationType, $uId, $stopList=array()) {
+ if(in_array($tag1, $stopList) || $tag1 == '') {
+ return array();
+ }
+
+ // try to find data in cache
+ $tcs = SemanticScuttle_Service_Factory::get('TagCache');
+ if(count($stopList) == 0) {
+ $activatedCache = true;
+ } else {
+ $activatedCache = false;
+ }
+
+ // look for existing links
+ $stopList[] = $tag1;
+ $linkedTags = $this->getLinkedTags($tag1, $relationType, $uId, false, $stopList);
+ if($relationType != '=') {
+ $linkedTags = array_merge($linkedTags, $this->getLinkedTags($tag1, '=', $uId, false, $stopList));
+ }
+
+ if(count($linkedTags) == 0) {
+ return array();
+
+ } else {
+ // use cache if possible
+ if($activatedCache) {
+ if($relationType == '>') {
+ $output = $tcs->getChildren($tag1, $uId);
+ } elseif($relationType == '=') {
+ $output = $tcs->getSynonyms($tag1, $uId);
+ }
+ if(count($output)>0) {
+ return $output;
+ }
+ }
+
+ // else compute the links
+ $output = array();
+
+ foreach($linkedTags as $linkedTag) {
+ $allLinkedTags = $this->getAllLinkedTags($linkedTag, $relationType, $uId, $stopList);
+ $output[] = $linkedTag;
+ if(is_array($allLinkedTags)) {
+ $output = array_merge($output, $allLinkedTags);
+ } else {
+ $output[] = $allLinkedTags;
+ }
+ }
+
+ // and save in cache
+ if($activatedCache == true && $uId>0) {
+ $tcs->updateTag($tag1, $relationType, $output, $uId);
+ }
+
+ //$output = array_unique($output); // remove duplication
+ return $output;
+
+ }
+ }
+
+ function getOrphewTags($relationType, $uId = 0, $limit = null, $orderBy = null) {
+ $query = "SELECT DISTINCT tts.tag1 as tag";
+ $query.= " FROM `". $this->getTableName() ."` tts";
+ if($orderBy != null) {
+ $tsts =SemanticScuttle_Service_Factory::get('TagStat');
+ $query.= ", ".$tsts->getTableName() ." tsts";
+ }
+ $query.= " WHERE tts.tag1 <> ALL";
+ $query.= " (SELECT DISTINCT tag2 FROM `". $this->getTableName() ."`";
+ $query.= " WHERE relationType = '" . $this->db->sql_escape($relationType) . "'";
+ if($uId > 0) {
+ $query.= " AND uId = '".intval($uId)."'";
+ }
+ $query.= ")";
+ if($uId > 0) {
+ $query.= " AND tts.uId = '".intval($uId)."'";
+ }
+
+ switch($orderBy) {
+ case "nb":
+ $query.= " AND tts.tag1 = tsts.tag1";
+ $query.= " AND tsts.relationType = '" . $this->db->sql_escape($relationType) . "'";
+ if($uId > 0) {
+ $query.= " AND tsts.uId = " . intval($uId);
+ }
+ $query.= " ORDER BY tsts.nb DESC";
+ break;
+ case "depth": // by nb of descendants
+ $query.= " AND tts.tag1 = tsts.tag1";
+ $query.= " AND tsts.relationType = '" . $this->db->sql_escape($relationType) . "'";
+ if($uId > 0) {
+ $query.= " AND tsts.uId = " . intval($uId);
+ }
+ $query.= " ORDER BY tsts.depth DESC";
+ break;
+ case "nbupdate":
+ $query.= " AND tts.tag1 = tsts.tag1";
+ $query.= " AND tsts.relationType = '" . $this->db->sql_escape($relationType) . "'";
+ if($uId > 0) {
+ $query.= " AND tsts.uId = " . intval($uId);
+ }
+ $query.= " ORDER BY tsts.nbupdate DESC";
+ break;
+ }
+
+ if($limit != null) {
+ $query.= " LIMIT 0," . intval($limit);
+ }
+
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(GENERAL_ERROR, 'Could not get linked tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $output = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+ function getMenuTags($uId) {
+ if(strlen($GLOBALS['menuTag']) < 1) {
+ return array();
+ } else {
+ // we don't use the getAllLinkedTags function in order to improve performance
+ $query = "SELECT tag2 as 'tag', COUNT(tag2) as 'count'";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" . $this->db->sql_escape($GLOBALS['menuTag']) . "'";
+ $query.= " AND relationType = '>'";
+ if($uId > 0) {
+ $query.= " AND uId = " . intval($uId);
+ }
+ $query.= " GROUP BY tag2";
+ $query.= " ORDER BY count DESC";
+ $query.= " LIMIT 0, " . intval($GLOBALS['maxSizeMenuBlock']);
+
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(GENERAL_ERROR, 'Could not get linked tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $output = $this->db->sql_fetchrowset($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+ }
+
+
+ function existsLinkedTags($tag1, $tag2, $relationType, $uId) {
+
+ //$tag1 = mysql_real_escape_string($tag1);
+ //$tag2 = mysql_real_escape_string($tag2);
+
+ $query = "SELECT tag1, tag2, relationType, uId FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" . $this->db->sql_escape($tag1) . "'";
+ $query.= " AND tag2 = '" . $this->db->sql_escape($tag2) . "'";
+ $query.= " AND relationType = '" . $this->db->sql_escape($relationType) . "'";
+ $query.= " AND uId = " . intval($uId);
+
+ //echo($query."<br>\n");
+
+ $dbres = $this->db->sql_query($query);
+ $hasTags = $this->db->sql_numrows($dbres) > 0;
+ $this->db->sql_freeresult($dbres);
+ return $hasTags;
+ }
+
+ function getLinks($uId) {
+ $query = "SELECT tag1, tag2, relationType, uId FROM `". $this->getTableName() ."`";
+ $query.= " WHERE 1=1";
+ if($uId > 0) {
+ $query.= " AND uId = " . intval($uId);
+ }
+
+ $dbres = $this->db->sql_query($query);
+ $rowset = $this->db->sql_fetchrowset($dbres);
+ $this->db->sql_freeresult($dbres);
+ return $rowset;
+ }
+
+ function removeLinkedTags($tag1, $tag2, $relationType, $uId) {
+ if(($tag1 != '' && $tag1 == $tag2) ||
+ ($relationType != ">" && $relationType != "=" && $relationType != "") ||
+ ($tag1 == '' && $tag2 == '')) {
+ return false;
+ }
+ $query = 'DELETE FROM '. $this->getTableName();
+ $query.= ' WHERE 1=1';
+ $query.= strlen($tag1)>0 ? ' AND tag1 = \''. $this->db->sql_escape($tag1) . "'" : '';
+ $query.= strlen($tag2)>0 ? ' AND tag2 = \''. $this->db->sql_escape($tag2) . "'" : '';
+ $query.= strlen($relationType)>0 ? ' AND relationType = \''. $this->db->sql_escape($relationType) . "'" : '';
+ $query.= strlen($uId)>0 ? ' AND uId = '. intval($uId) : '';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not remove tag relation', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+
+ // Update stats and cache
+ $this->update($tag1, $tag2, $relationType, $uId);
+
+ $this->db->sql_freeresult($dbresult);
+ return true;
+ }
+
+ function removeLinkedTagsForUser($uId) {
+ $query = 'DELETE FROM '. $this->getTableName();
+ $query.= ' WHERE uId = '. intval($uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not remove tag relation', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+
+ // Update stats and cache
+ $this->update('', '', '', $uId);
+
+ $this->db->sql_freeresult($dbresult);
+ return true;
+ }
+
+ function renameTag($uId, $oldName, $newName) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $newName = $tagservice->normalize($newName);
+
+ $query = 'UPDATE `'. $this->getTableName() .'`';
+ $query.= ' SET tag1=\'' . $this->db->sql_escape($newName) ."'";
+ $query.= ' WHERE tag1=\'' . $this->db->sql_escape($oldName) . "'";
+ $query.= ' AND uId=' . intval($uId);
+ $this->db->sql_query($query);
+
+ $query = 'UPDATE `'. $this->getTableName() .'`';
+ $query.= ' SET tag2=\'' . $this->db->sql_escape($newName) . "'";
+ $query.= ' WHERE tag2=\'' . $this->db->sql_escape($oldName) . "'";
+ $query.= ' AND uId=' . intval($uId);
+ $this->db->sql_query($query);
+
+
+ // Update stats and cache
+ $this->update($oldName, NULL, '=', $uId);
+ $this->update($oldName, NULL, '>', $uId);
+ $this->update($newName, NULL, '=', $uId);
+ $this->update($newName, NULL, '>', $uId);
+
+ return true;
+
+ }
+
+ function update($tag1, $tag2, $relationType, $uId) {
+ $tsts =SemanticScuttle_Service_Factory::get('TagStat');
+ $tsts->updateStat($tag1, $relationType, $uId);
+
+ $tcs = SemanticScuttle_Service_Factory::get('TagCache');
+ $tcs->deleteByUser($uId);
+ }
+
+ function deleteAll() {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+
+ $tsts =SemanticScuttle_Service_Factory::get('TagStat');
+ $tsts->deleteAll();
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/TagCache.php b/src/SemanticScuttle/Service/TagCache.php
new file mode 100644
index 0000000..f8a28af
--- /dev/null
+++ b/src/SemanticScuttle/Service/TagCache.php
@@ -0,0 +1,397 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle tag caching service.
+ *
+ * This class infers on relation between tags by storing all
+ * the including tags or synonymous tag.
+ * For example, if the user creates: tag1>tag2>tag3, the system
+ * can infer that tag is included into tag1.
+ * Instead of computing this relation several times, it is saved
+ * into this current table.
+ * For synonymy, this table stores also the group of synonymous tags.
+ * The table must be updated for each modification of
+ * the relations between tags.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_TagCache extends SemanticScuttle_DbService
+{
+
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ protected function __construct($db)
+ {
+ $this->db =$db;
+ $this->tablename = $GLOBALS['tableprefix'] .'tagscache';
+ }
+
+ function getChildren($tag1, $uId) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag1 = $tagservice->normalize($tag1);
+
+ if($tag1 == '') return false;
+
+ $query = "SELECT DISTINCT tag2 as 'tag'";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= " WHERE relationType = '>'";
+ $query.= " AND tag1 = '" . $this->db->sql_escape($tag1) . "'";
+ $query.= " AND uId = " . intval($uId);
+
+ //die($query);
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(GENERAL_ERROR, 'Could not get related tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+
+ $rowset = $this->db->sql_fetchrowset($dbresult);
+ $output = array();
+ foreach($rowset as $row) {
+ $output[] = $row['tag'];
+ }
+
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+ function addChild($tag1, $tag2, $uId) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag1 = $tagservice->normalize($tag1);
+ $tag2 = $tagservice->normalize($tag2);
+
+ if($tag1 == $tag2 || strlen($tag1) == 0 || strlen($tag2) == 0
+ || ($this->existsChild($tag1, $tag2, $uId))) {
+ return false;
+ }
+
+ $values = array('tag1' => $tag1, 'tag2' => $tag2, 'relationType'=> '>', 'uId'=> $uId);
+ $query = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values);
+ //die($query);
+ if (!($dbresult = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not add tag cache inference', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $this->db->sql_transaction('commit');
+ }
+
+ function removeChild($tag1, $tag2, $uId) {
+ if(($tag1 != '' && $tag1 == $tag2) ||
+ ($tag1 == '' && $tag2 == '' && $uId == '')) {
+ return false;
+ }
+
+ $query = 'DELETE FROM '. $this->getTableName();
+ $query.= ' WHERE 1=1';
+ $query.= strlen($tag1)>0 ? ' AND tag1 = \''. $this->db->sql_escape($tag1) . "'" : '';
+ $query.= strlen($tag2)>0 ? ' AND tag2 = \''. $this->db->sql_escape($tag2) . "'" : '';
+ $query.= ' AND relationType = ">"';
+ $query.= strlen($uId)>0 ? ' AND uId = ' . intval($uId) : '';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not remove tag cache inference', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ }
+
+ function removeChildren($tag1, $uId) {
+ $this->removeChild($tag1, NULL, $uId);
+ }
+
+ function existsChild($tag1, $tag2, $uId) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag1 = $tagservice->normalize($tag1);
+ $tag2 = $tagservice->normalize($tag2);
+
+ $query = "SELECT tag1, tag2, relationType, uId FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" . $this->db->sql_escape($tag1) . "'";
+ $query.= " AND tag2 = '" . $this->db->sql_escape($tag2) . "'";
+ $query.= " AND relationType = '>'";
+ $query.= " AND uId = " . intval($uId);
+
+ //echo($query."<br>\n");
+
+ $dbres = $this->db->sql_query($query);
+ $rows = $this->db->sql_numrows($dbres);
+ $this->db->sql_freeresult($dbres);
+ return $rows > 0;
+ }
+
+ /*
+ * Synonyms of a same concept are a group. A group has one main synonym called key
+ * and a list of synonyms called values.
+ */
+ function addSynonym($tag1, $tag2, $uId) {
+
+ if($tag1 == $tag2 || strlen($tag1) == 0 || strlen($tag2) == 0
+ || ($this->existsSynonym($tag1, $tag2, $uId))) {
+ return false;
+ }
+
+ $case1 = '0'; // not in DB
+ if($this->_isSynonymKey($tag1, $uId)) {
+ $case1 = 'key';
+ } elseif($this->_isSynonymValue($tag1, $uId)) {
+ $case1 = 'value';
+ }
+
+ $case2 = '0'; // not in DB
+ if($this->_isSynonymKey($tag2, $uId)) {
+ $case2 = 'key';
+ } elseif($this->_isSynonymValue($tag2, $uId)) {
+ $case2 = 'value';
+ }
+ $case = $case1.$case2;
+
+ // all the possible cases
+ switch ($case) {
+ case 'keykey':
+ $values = $this->_getSynonymValues($tag2, $uId);
+ $this->removeSynonymGroup($tag2, $uId);
+ foreach($values as $value) {
+ $this->addSynonym($tag1, $value, $uId);
+ }
+ $this->addSynonym($tag1, $tag2, $uId);
+ break;
+
+ case 'valuekey':
+ $key = $this->_getSynonymKey($tag1, $uId);
+ $this->addSynonym($key, $tag2, $uId);
+ break;
+
+ case 'keyvalue':
+ $this->addSynonym($tag2, $tag1, $uId);
+ break;
+ case 'valuevalue':
+ $key1 = $this->_getSynonymKey($tag1, $uId);
+ $key2 = $this->_getSynonymKey($tag2, $uId);
+ $this->addSynonym($key1, $key2, $uId);
+ break;
+ case '0value':
+ $key = $this->_getSynonymKey($tag2, $uId);
+ $this->addSynonym($key, $tag1, $uId);
+ break;
+ case 'value0':
+ $this->addSynonym($tag2, $tag1, $uId);
+ break;
+ case '0key':
+ $this->addSynonym($tag2, $tag1, $uId);
+ break;
+ default:
+ $values = array('tag1' => $tag1, 'tag2' => $tag2, 'relationType'=> '=', 'uId'=> $uId);
+ $query = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values);
+ //die($query);
+ if (!($dbresult = $this->db->sql_query($query))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not add tag cache synonymy', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ $this->db->sql_transaction('commit');
+ break;
+ }
+ }
+
+ function removeSynonymGroup($tag1, $uId) {
+ $query = 'DELETE FROM '. $this->getTableName();
+ $query.= ' WHERE 1=1';
+ $query.= ' AND tag1 = \''. $this->db->sql_escape($tag1) . "'";
+ $query.= ' AND relationType = "="';
+ $query.= ' AND uId = ' . intval($uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not remove tag cache inference', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+ }
+
+ function _isSynonymKey($tag1, $uId) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag1 = $tagservice->normalize($tag1);
+
+ $query = "SELECT tag1 FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" . $this->db->sql_escape($tag1) ."'";
+ $query.= " AND relationType = '='";
+ $query.= " AND uId = " . intval($uId);
+
+ $dbres = $this->db->sql_query($query);
+ $rows = $this->db->sql_numrows($dbres);
+ $this->db->sql_freeresult($dbres);
+ return $rows > 0;
+ }
+
+ function _isSynonymValue($tag2, $uId) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag2 = $tagservice->normalize($tag2);
+
+ $query = "SELECT tag2 FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag2 = '" . $this->db->sql_escape($tag2) . "'";
+ $query.= " AND relationType = '='";
+ $query.= " AND uId = " . intval($uId);
+
+ $dbres = $this->db->sql_query($query);
+ $rows = $this->db->sql_numrows($dbres);
+ $this->db->sql_freeresult($dbres);
+ return $rows > 0;
+ }
+
+ function getSynonyms($tag1, $uId) {
+ $values = array();
+ if($this->_isSynonymKey($tag1, $uId)) {
+ $values = $this->_getSynonymValues($tag1, $uId);
+ } elseif($this->_isSynonymValue($tag1, $uId)) {
+ $key = $this->_getSynonymKey($tag1, $uId);
+ $values = $this->_getSynonymValues($key, $uId, $tag1);
+ $values[] = $key;
+ }
+ return $values;
+ }
+
+ function _getSynonymKey($tag2, $uId) {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag2 = $tagservice->normalize($tag2);
+
+ if($this->_isSynonymKey($tag2, $uId)) return $tag2;
+
+ if($tag2 == '') return false;
+
+ $query = "SELECT DISTINCT tag1 as 'tag'";
+ $query.= " FROM `". $this->getTableName() ."`";
+ $query.= " WHERE relationType = '='";
+ $query.= " AND tag2 = '" . $this->db->sql_escape($tag2) . "'";
+ $query.= " AND uId = " . intval($uId);
+
+ //die($query);
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(GENERAL_ERROR, 'Could not get related tags', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ return $row['tag'];
+ }
+
+ /*
+ * Return values associated with a key.
+ * $tagExcepted allows to hide a value.
+ *
+ * @return array Array with tag names
+ */
+ protected function _getSynonymValues($tag1, $uId, $tagExcepted = NULL)
+ {
+ $tagservice =SemanticScuttle_Service_Factory::get('Tag');
+ $tag1 = $tagservice->normalize($tag1);
+ $tagExcepted = $tagservice->normalize($tagExcepted);
+
+ if($tag1 == '') return false;
+
+ $query = "SELECT DISTINCT tag2 as 'tag'";
+ $query .= " FROM `". $this->getTableName() ."`";
+ $query .= " WHERE relationType = '='";
+ $query .= " AND tag1 = '" . $this->db->sql_escape($tag1) . "'";
+ $query .= " AND uId = " . intval($uId);
+ $query .= $tagExcepted != ''
+ ? " AND tag2!='" . $this->db->sql_escape($tagExcepted) . "'"
+ : '';
+
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(
+ GENERAL_ERROR, 'Could not get related tags',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $rowset = $this->db->sql_fetchrowset($dbresult);
+
+ $output = array();
+ foreach($rowset as $row) {
+ $output[] = $row['tag'];
+ }
+
+ $this->db->sql_freeresult($dbresult);
+ return $output;
+ }
+
+ function existsSynonym($tag1, $tag2, $uId) {
+ if($this->_getSynonymKey($tag1, $uId) == $tag2 || $this->_getSynonymKey($tag2, $uId) == $tag1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ function updateTag($tag1, $relationType, $otherTags, $uId) {
+ if($relationType == '=') {
+ if($this->getSynonyms($tag1, $uId)) { // remove previous data avoiding unconstistency
+ $this->removeSynonymGroup($tag1, $uId);
+ }
+
+ foreach($otherTags as $tag2) {
+ $this->addSynonym($tag1, $tag2, $uId);
+ }
+ } elseif($relationType == '>') {
+ if(count($this->getChildren($tag1, $uId))>0) { // remove previous data avoiding unconstistency
+ $this->removeChildren($tag1);
+ }
+
+ foreach($otherTags as $tag2) {
+ $this->addChild($tag1, $tag2, $uId);
+ }
+ }
+ }
+
+ function deleteByUser($uId) {
+ $query = 'DELETE FROM '. $this->getTableName() .' WHERE uId = '. intval($uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not delete user tags cache', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+
+ }
+
+ function deleteAll() {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/TagStat.php b/src/SemanticScuttle/Service/TagStat.php
new file mode 100644
index 0000000..af8d110
--- /dev/null
+++ b/src/SemanticScuttle/Service/TagStat.php
@@ -0,0 +1,233 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle tag statistics service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_TagStat extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ protected function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'tagsstats';
+ }
+
+ function getNbChildren($tag1, $relationType, $uId) {
+ $tts =SemanticScuttle_Service_Factory::get('Tag2Tag');
+ $query = "SELECT tag1, relationType, uId FROM `". $tts->getTableName() ."`";
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+
+ $dbres = $this->db->sql_query($query);
+ $rows = $this->db->sql_numrows($dbres);
+ $this->db->sql_freeresult($dbres);
+ return $rows;
+ }
+
+ function getNbDescendants($tag1, $relationType, $uId) {
+ $query = "SELECT nb FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+
+ $dbresults = $this->db->sql_query($query);
+ $row = $this->db->sql_fetchrow($dbresults);
+ $this->db->sql_freeresult($dbresults);
+ if($row['nb'] == null) {
+ return 0;
+ } else {
+ return (int) $row['nb'];
+ }
+ }
+
+ function getMaxDepth($tag1, $relationType, $uId) {
+ $query = "SELECT depth FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+
+ $dbresults = $this->db->sql_query($query);
+ $row = $this->db->sql_fetchrow($dbresults);
+ $this->db->sql_freeresult($dbresults);
+ if($row['depth'] == null) {
+ return 0;
+ } else {
+ return (int) $row['depth'];
+ };
+ }
+
+ function getNbUpdates($tag1, $relationType, $uId) {
+ $query = "SELECT nbupdate FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+
+ $dbresults = $this->db->sql_query($query);
+ $row = $this->db->sql_fetchrow($dbresults);
+ $this->db->sql_freeresult($dbresults);
+ if($row['nbupdate'] == null) {
+ return 0;
+ } else {
+ return (int) $row['nbupdate'];
+ }
+ }
+
+ function existStat($tag1, $relationType, $uId) {
+ $query = "SELECT tag1, relationType, uId FROM `". $this->getTableName() ."`";
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+
+ $dbres = $this->db->sql_query($query);
+ $rows = $this->db->sql_numrows($dbres);
+ $this->db->sql_freeresult($dbres);
+ return $rows > 0;
+ }
+
+ function createStat($tag1, $relationType, $uId) {
+ $query = "INSERT INTO `". $this->getTableName() ."`";
+ $query.= "(tag1, relationType, uId)";
+ $query.= " VALUES ('".$tag1."','".$relationType."','".$uId."')";
+ $this->db->sql_query($query);
+ }
+
+ function updateStat($tag1, $relationType, $uId=null, $stoplist=array()) {
+ if(in_array($tag1, $stoplist)) {
+ return false;
+ }
+
+ $tts =SemanticScuttle_Service_Factory::get('Tag2Tag');
+ $linkedTags = $tts->getLinkedTags($tag1, $relationType, $uId);
+ $nbDescendants = 0;
+ $maxDepth = 0;
+ foreach($linkedTags as $linkedTag) {
+ $nbDescendants+= 1 + $this->getNbDescendants($linkedTag, $relationType, $uId);
+ $maxDepth = max($maxDepth, 1 + $this->getMaxDepth($linkedTag, $relationType, $uId));
+ }
+ $this->setNbDescendants($tag1, $relationType, $uId, $nbDescendants);
+ $this->setMaxDepth($tag1, $relationType, $uId, $maxDepth);
+ $this->increaseNbUpdate($tag1, $relationType, $uId);
+
+ // propagation to the precedent tags
+ $linkedTags = $tts->getLinkedTags($tag1, $relationType, $uId, true);
+ $stoplist[] = $tag1;
+ foreach($linkedTags as $linkedTag) {
+ $this->updateStat($linkedTag, $relationType, $uId, $stoplist);
+ }
+ }
+
+ function updateAllStat() {
+ $tts =SemanticScuttle_Service_Factory::get('Tag2Tag');
+
+ $query = "SELECT tag1, uId FROM `". $tts->getTableName() ."`";
+ $query.= " WHERE relationType = '>'";
+
+ //die($query);
+
+ if (! ($dbresult = $this->db->sql_query($query)) ){
+ message_die(GENERAL_ERROR, 'Could not update stats', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $rowset = $this->db->sql_fetchrowset($dbresult);
+ foreach($rowset as $row) {
+ $this->updateStat($row['tag1'], '>', $row['uId']);
+ }
+ $this->db->sql_freeresult($dbresult);
+ }
+
+ function setNbDescendants($tag1, $relationType, $uId, $nb) {
+ if(!$this->existStat($tag1, $relationType, $uId)) {
+ $this->createStat($tag1, $relationType, $uId);
+ }
+ $query = "UPDATE `". $this->getTableName() ."`";
+ $query.= " SET nb = ". $nb;
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+ $this->db->sql_freeresult($this->db->sql_query($query));
+ }
+
+ function setMaxDepth($tag1, $relationType, $uId, $depth) {
+ if(!$this->existStat($tag1, $relationType, $uId)) {
+ $this->createStat($tag1, $relationType, $uId);
+ }
+ $query = "UPDATE `". $this->getTableName() ."`";
+ $query.= " SET depth = ". $depth;
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+ $this->db->sql_query($query);
+ }
+
+ function increaseNbUpdate($tag1, $relationType, $uId) {
+ if(!$this->existStat($tag1, $relationType, $uId)) {
+ $this->createStat($tag1, $relationType, $uId);
+ }
+ $query = "UPDATE `". $this->getTableName() ."`";
+ $query.= " SET nbupdate = nbupdate + 1";
+ $query.= " WHERE tag1 = '" .$tag1 ."'";
+ $query.= " AND relationType = '". $relationType ."'";
+ $query.= " AND uId = '".$uId."'";
+
+ //die($query);
+
+ $this->db->sql_query($query);
+ }
+
+ function deleteTagStatForUser($uId) {
+ $query = 'DELETE FROM '. $this->getTableName() .' WHERE uId = '. intval($uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not delete tag stats', '', __LINE__,
+ __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+ function deleteAll() {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+}
+?>
diff --git a/src/SemanticScuttle/Service/Template.php b/src/SemanticScuttle/Service/Template.php
new file mode 100644
index 0000000..b5d4cfa
--- /dev/null
+++ b/src/SemanticScuttle/Service/Template.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+require_once 'SemanticScuttle/Model/Template.php';
+require_once 'SemanticScuttle/Model/Theme.php';
+
+/**
+ * SemanticScuttle template service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Template extends SemanticScuttle_Service
+{
+ /**
+ * Full path to template directory.
+ *
+ * Set in constructor to
+ * $GLOBALS['TEMPLATES_DIR']
+ *
+ * @var string
+ */
+ protected $basedir;
+
+ /**
+ * The template theme to use.
+ * Set in constructor based on $GLOBALS['theme']
+ *
+ * @var SemanticScuttle_Model_Theme
+ */
+ protected $theme;
+
+
+
+ /**
+ * Returns the single service instance
+ *
+ * @param DB $db Database object
+ *
+ * @return SemanticScuttle_Service
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+
+
+ /**
+ * Create a new instance
+ */
+ protected function __construct()
+ {
+ $this->basedir = $GLOBALS['TEMPLATES_DIR'];
+ $this->theme = new SemanticScuttle_Model_Theme($GLOBALS['theme']);
+ //FIXME: verify the theme exists
+ }
+
+
+
+ /**
+ * Loads and displays a template file.
+ *
+ * @param string $template Template filename relative
+ * to template dir
+ * @param array $vars Array of template variables.
+ * The current theme object will be added
+ * automatically with name "theme".
+ *
+ * @return SemanticScuttle_Model_Template Template object
+ */
+ public function loadTemplate($template, $vars = null)
+ {
+ if (substr($template, -4) != '.php') {
+ $template .= '.php';
+ }
+
+ $oldIncPath = get_include_path();
+ set_include_path(
+ $this->basedir . $this->theme->getName()
+ . PATH_SEPARATOR . $this->basedir . 'default'
+ //needed since services are instantiated in templates
+ . PATH_SEPARATOR . $oldIncPath
+ );
+
+ $vars['theme'] = $this->theme;
+ $tpl = new SemanticScuttle_Model_Template(
+ $template, $vars, $this
+ );
+ $tpl->parse();
+
+ set_include_path($oldIncPath);
+
+ return $tpl;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service/Thumbnails.php b/src/SemanticScuttle/Service/Thumbnails.php
new file mode 100644
index 0000000..6151254
--- /dev/null
+++ b/src/SemanticScuttle/Service/Thumbnails.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Instantiates the configured website thumbnailer object.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Thumbnails extends SemanticScuttle_Service
+{
+ /**
+ * Instantiates the configured website thumbnailer object.
+ *
+ * @return object Website thumbnailer
+ */
+ public function getThumbnailer()
+ {
+ if (!isset($GLOBALS['thumbnailsType'])
+ || $GLOBALS['thumbnailsType'] == ''
+ ) {
+ $class = 'SemanticScuttle_Thumbnailer_Null';
+ } else {
+ $class = 'SemanticScuttle_Thumbnailer_'
+ . ucfirst($GLOBALS['thumbnailsType']);
+ }
+ if (!class_exists($class)) {
+ //PEAR classname to filename rule
+ $file = str_replace('_', '/', $class) . '.php';
+ include_once $file;
+ }
+
+ $thumbnailer = new $class();
+
+ if (!isset($GLOBALS['thumbnailsConfig'])
+ || $GLOBALS['thumbnailsConfig'] == ''
+ ) {
+ $thumbnailer->setConfig(null);
+ } else {
+ $thumbnailer->setConfig($GLOBALS['thumbnailsConfig']);
+ }
+
+ return $thumbnailer;
+ }
+}
+?>
diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php
new file mode 100644
index 0000000..434e6b7
--- /dev/null
+++ b/src/SemanticScuttle/Service/User.php
@@ -0,0 +1,1090 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+require_once 'SemanticScuttle/Model/User.php';
+
+/**
+ * SemanticScuttle user management service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_User extends SemanticScuttle_DbService
+{
+ /**
+ * The ID of the currently logged on user.
+ * NULL when not logged in.
+ *
+ * @var integer
+ */
+ protected $currentuserId = null;
+
+ /**
+ * Currently logged on user from database
+ *
+ * @var array
+ *
+ * @see getCurrentUserId()
+ * @see getCurrentUser()
+ * @see setCurrentUserId()
+ */
+ protected $currentuser = null;
+
+ protected $fields = array(
+ 'primary' => 'uId',
+ 'username' => 'username',
+ 'password' => 'password',
+ 'privateKey' => 'privateKey'
+ );
+
+ protected $profileurl;
+ protected $sessionkey;
+ protected $cookiekey;
+ protected $cookietime = 1209600; // 2 weeks
+
+ /**
+ * Returns the single service instance
+ *
+ * @param sql_db $db Database object
+ *
+ * @return SemanticScuttle_Service_User
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ protected function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'users';
+ $this->sessionkey = INSTALLATION_ID.'-currentuserid';
+ $this->cookiekey = INSTALLATION_ID.'-login';
+ $this->profileurl = createURL('profile', '%2$s');
+ $this->updateSessionStability();
+ }
+
+ /**
+ * Fetches the desired user row from database, specified by column and value
+ *
+ * @param string $fieldname Name of database column to identify user
+ * @param string $value Value of $fieldname
+ *
+ * @return array Database row or boolean false
+ */
+ protected function _getuser($fieldname, $value)
+ {
+ $query = 'SELECT * FROM '. $this->getTableName()
+ . ' WHERE ' . $fieldname . ' = "' . $this->db->sql_escape($value) . '"';
+
+ if (!($dbresult = $this->db->sql_query($query)) ) {
+ message_die(
+ GENERAL_ERROR, 'Could not get user',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+ if ($row) {
+ return $row;
+ } else {
+ return false;
+ }
+ }
+
+ function & getUsers($nb=0) {
+ $query = 'SELECT * FROM '. $this->getTableName() .' ORDER BY `uId` DESC';
+ if($nb>0) {
+ $query .= ' LIMIT 0, '.$nb;
+ }
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(GENERAL_ERROR, 'Could not get user', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $users[] = $row;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $users;
+ }
+
+ /**
+ * Returns an array of user objects.
+ * Array is in order of uids
+ *
+ * @param integer $nb Number of users to fetch.
+ *
+ * @return array Array of SemanticScuttle_Model_User objects
+ */
+ public function getObjectUsers($nb = 0)
+ {
+ $query = 'SELECT * FROM ' . $this->getTableName()
+ . ' ORDER BY uId DESC';
+
+ if ($nb > 0) {
+ $query .= ' LIMIT 0, ' . intval($nb);
+ }
+
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(
+ GENERAL_ERROR, 'Could not get user',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $users = array();
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $users[] = new SemanticScuttle_Model_User(
+ $row[$this->getFieldName('primary')],
+ $row[$this->getFieldName('username')]
+ );
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $users;
+ }
+
+ function _randompassword() {
+ $seed = (integer) md5(microtime());
+ mt_srand($seed);
+ $password = mt_rand(1, 99999999);
+ $password = substr(md5($password), mt_rand(0, 19), mt_rand(6, 12));
+ return $password;
+ }
+
+ /**
+ * Updates a single field in the user's database row
+ *
+ * @param integer $uId ID of the user
+ * @param string $fieldname Name of table column to change
+ * @param string $value New value
+ *
+ * @return boolean True if all was well, false if not
+ */
+ public function _updateuser($uId, $fieldname, $value)
+ {
+ $updates = array ($fieldname => $value);
+ $sql = 'UPDATE '. $this->getTableName()
+ . ' SET '. $this->db->sql_build_array('UPDATE', $updates)
+ . ' WHERE '. $this->getFieldName('primary') . '=' . intval($uId);
+
+ // Execute the statement.
+ $this->db->sql_transaction('begin');
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not update user', '',
+ __LINE__, __FILE__, $sql, $this->db
+ );
+ return false;
+ }
+ $this->db->sql_transaction('commit');
+
+ // Everything worked out, so return true.
+ return true;
+ }
+
+ function getProfileUrl($id, $username) {
+ return sprintf($this->profileurl, urlencode($id), urlencode($username));
+ }
+
+ function getUserByUsername($username) {
+ return $this->_getuser($this->getFieldName('username'), $username);
+ }
+
+ /**
+ * Returns user row from database.
+ *
+ * @param string $privateKey Private Key
+ *
+ * @return array User array from database, false if no user was found
+ */
+ public function getUserByPrivateKey($privateKey)
+ {
+ return $this->_getuser($this->getFieldName('privateKey'), $privateKey);
+ }
+
+ function getObjectUserByUsername($username) {
+ $user = $this->_getuser($this->getFieldName('username'), $username);
+ if($user != false) {
+ return new SemanticScuttle_Model_User(
+ $user[$this->getFieldName('primary')], $username
+ );
+ } else {
+ return NULL;
+ }
+ }
+
+ /**
+ * 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) {
+ return $objectUser->getId();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns user row from database.
+ *
+ * @param integer $id User ID
+ *
+ * @return array User array from database
+ */
+ public function getUser($id)
+ {
+ return $this->_getuser($this->getFieldName('primary'), $id);
+ }
+
+ /**
+ * Returns user object for given user id
+ *
+ * @param integer $id User ID
+ *
+ * @return SemanticScuttle_Model_User User object
+ */
+ public function getObjectUser($id)
+ {
+ $user = $this->_getuser($this->getFieldName('primary'), $id);
+ return new SemanticScuttle_Model_User(
+ $id, $user[$this->getFieldName('username')]
+ );
+ }
+
+ function isLoggedOn() {
+ return ($this->getCurrentUserId() !== false);
+ }
+
+ /**
+ * Tells you if the private key is enabled and valid
+ *
+ * @param string $privateKey Private Key
+ *
+ * @return boolean True if enabled and valid
+ */
+ public function isPrivateKeyValid($privateKey)
+ {
+ // check length of private key
+ if (strlen($privateKey) == 32) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current user object
+ *
+ * @param boolean $refresh Reload the user from database
+ * based on current user id
+ * @param mixed $newval New user value (used internally
+ * as setter method)
+ *
+ * @return array User from database
+ */
+ public function getCurrentUser($refresh = false, $newval = null)
+ {
+ if (!is_null($newval)) {
+ //internal use only: reset currentuser
+ $this->currentuser = $newval;
+ } else if ($refresh || !isset($this->currentuser)) {
+ if ($id = $this->getCurrentUserId()) {
+ $this->currentuser = $this->getUser($id);
+ } else {
+ $this->currentuser = null;
+ }
+ }
+ return $this->currentuser;
+ }
+
+ /**
+ * Return current user as object
+ *
+ * @param boolean $refresh Reload the user from database
+ * based on current user id
+ * @param mixed $newval New user value (used internally
+ * as setter method)
+ *
+ * @return SemanticScuttle_Model_User User object
+ */
+ function getCurrentObjectUser($refresh = false, $newval = null)
+ {
+ static $currentObjectUser;
+ if (!is_null($newval)) {
+ //internal use only: reset currentuser
+ $currentObjectUser = $newval;
+ } else if ($refresh || !isset($currentObjectUser)) {
+ if ($id = $this->getCurrentUserId()) {
+ $currentObjectUser = $this->getObjectUser($id);
+ } else {
+ $currentObjectUser = null;
+ }
+ }
+ return $currentObjectUser;
+ }
+
+ function existsUserWithUsername($username) {
+ if($this->getUserByUsername($username) != '') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function existsUser($id) {
+ if($this->getUser($id) != '') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the given user is an administrator.
+ * Uses global admin_users property containing admin
+ * user names.
+ *
+ * Passing the user id makes this function load the user
+ * from database. For efficiency reasons, try to pass
+ * the user name or database row.
+ *
+ * @param integer|array|string $user User ID or user row from DB
+ * or user name
+ *
+ * @return boolean True if the user is admin
+ */
+ function isAdmin($user)
+ {
+ if (is_numeric($user)) {
+ $user = $this->getUser($user);
+ $user = $user['username'];
+ } else if (is_array($user)) {
+ $user = $user['username'];
+ }
+
+ if (isset($GLOBALS['admin_users'])
+ && in_array($user, $GLOBALS['admin_users'])
+ ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return current user id based on session or cookie
+ *
+ * @return mixed Integer user id or boolean false when user
+ * could not be found or is not logged on.
+ */
+ public function getCurrentUserId()
+ {
+ if ($this->currentuserId !== null) {
+ return $this->currentuserId;
+ }
+
+ if (isset($_SESSION[$this->getSessionKey()])) {
+ $this->currentuserId = (int)$_SESSION[$this->getSessionKey()];
+ return $this->currentuserId;
+
+ }
+
+ if (isset($_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') .
+ ', '.$this->getFieldName('password') .
+ ')) = \''.$this->db->sql_escape($cook[1]).'\' AND '.
+ $this->getFieldName('primary'). ' = '. $this->db->sql_escape($cook[0]);
+
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(
+ GENERAL_ERROR, 'Could not get user',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ if ($row = $this->db->sql_fetchrow($dbresult)) {
+ $this->setCurrentUserId(
+ (int)$row[$this->getFieldName('primary')], true
+ );
+ $this->db->sql_freeresult($dbresult);
+ return $this->currentuserId;
+ }
+ }
+
+ $ssls = SemanticScuttle_Service_Factory::get('User_SslClientCert');
+ if ($ssls->hasValidCert()) {
+ $id = $ssls->getUserIdFromCert();
+ if ($id !== false) {
+ $this->setCurrentUserId($id, true);
+ return $this->currentuserId;
+ }
+ }
+ return false;
+ }
+
+
+
+ /**
+ * Set the current user ID (i.e. when logging on)
+ *
+ * @internal
+ * No ID verification is being done.
+ *
+ * @param integer $user User ID or null to unset the user
+ * @param boolean $storeInSession Store the user ID in the session
+ *
+ * @return void
+ */
+ public function setCurrentUserId($user, $storeInSession = false)
+ {
+ if ($user === null) {
+ $this->currentuserId = null;
+ if ($storeInSession) {
+ unset($_SESSION[$this->getSessionKey()]);
+ }
+ } else {
+ $this->currentuserId = (int)$user;
+ if ($storeInSession) {
+ $_SESSION[$this->getSessionKey()] = $this->currentuserId;
+ }
+ }
+ //reload user object
+ $this->getCurrentUser(true);
+ $this->getCurrentObjectUser(true);
+ }
+
+
+
+ /**
+ * Try to authenticate and login a user with
+ * username and password.
+ *
+ * @param string $username Name of user
+ * @param string $password Password
+ * @param boolean $remember If a long-time cookie shall be set
+ *
+ * @return boolean True if the user could be authenticated,
+ * false if not.
+ */
+ public function login($username, $password, $remember = false)
+ {
+ $password = $this->sanitisePassword($password);
+ $query = 'SELECT '. $this->getFieldName('primary') .' FROM '. $this->getTableName() .' WHERE '. $this->getFieldName('username') .' = "'. $this->db->sql_escape($username) .'" AND '. $this->getFieldName('password') .' = "'. $this->db->sql_escape($password) .'"';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR,
+ 'Could not get user',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+
+ if ($row) {
+ $this->setCurrentUserId($row[$this->getFieldName('primary')], true);
+ if ($remember) {
+ $cookie = $this->currentuserId . ':' . md5($username.$password);
+ setcookie(
+ $this->cookiekey, $cookie,
+ time() + $this->cookietime, '/'
+ );
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Try to authenticate via the privateKey
+ *
+ * @param string $privateKey Private Key
+ *
+ * @return boolean true if the user could be authenticated,
+ * false if not.
+ */
+ public function loginPrivateKey($privateKey)
+ {
+ /* Check if private key valid and enabled */
+ if (!$this->isPrivateKeyValid($privateKey)) {
+ return false;
+ }
+
+ $query = 'SELECT '. $this->getFieldName('primary') .' FROM '
+ . $this->getTableName() .' WHERE '
+ . $this->getFieldName('privateKey') .' = "'
+ . $this->db->sql_escape($privateKey) .'"';
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR,
+ 'Could not get user',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+
+ if ($row) {
+ $this->setCurrentUserId($row[$this->getFieldName('primary')], false);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Logs the user off
+ *
+ * @return void
+ */
+ public function logout()
+ {
+ @setcookie($this->getCookiekey(), '', time() - 1, '/');
+ unset($_COOKIE[$this->getCookiekey()]);
+ session_unset();
+ $this->currentuserId = null;
+ $this->currentuser = null;
+ }
+
+ function getWatchlist($uId) {
+ // Gets the list of user IDs being watched by the given user.
+ $query = 'SELECT watched FROM '. $GLOBALS['tableprefix'] .'watched WHERE uId = '. intval($uId);
+
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(GENERAL_ERROR, 'Could not get watchlist', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $arrWatch = array();
+ if ($this->db->sql_numrows($dbresult) == 0) {
+ $this->db->sql_freeresult($dbresult);
+ return $arrWatch;
+ }
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $arrWatch[] = $row['watched'];
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $arrWatch;
+ }
+
+
+ /**
+ * Gets the list of user names being watched by the given user.
+ *
+ * @param integer $uId User ID
+ * @param boolean $watchedby if false: get the list of users that $uId watches
+ * if true: get the list of users that watch $uId
+ *
+ * @return array Array of user names
+ */
+ public function getWatchNames($uId, $watchedby = false)
+ {
+ if ($watchedby) {
+ $table1 = 'b';
+ $table2 = 'a';
+ } else {
+ $table1 = 'a';
+ $table2 = 'b';
+ }
+ $primary = $this->getFieldName('primary');
+ $userfield = $this->getFieldName('username');
+ $query = 'SELECT '. $table1 .'.'. $userfield
+ . ' FROM '. $GLOBALS['tableprefix'] . 'watched AS W,'
+ . ' ' . $this->getTableName() .' AS a,'
+ . ' ' . $this->getTableName() .' AS b'
+ . ' WHERE W.watched = a.' . $primary
+ . ' AND W.uId = b.' . $primary
+ . ' AND ' . $table2 . '.' . $primary . ' = '. intval($uId)
+ . ' ORDER BY '. $table1 . '.' . $userfield;
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get watchlist',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $arrWatch = array();
+ if ($this->db->sql_numrows($dbresult) == 0) {
+ $this->db->sql_freeresult($dbresult);
+ return $arrWatch;
+ }
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $arrWatch[] = $row[$this->getFieldName('username')];
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $arrWatch;
+ }
+
+
+ function getWatchStatus($watcheduser, $currentuser) {
+ // Returns true if the current user is watching the given user, and false otherwise.
+ $query = 'SELECT watched FROM '. $GLOBALS['tableprefix'] .'watched AS W INNER JOIN '. $this->getTableName() .' AS U ON U.'. $this->getFieldName('primary') .' = W.watched WHERE U.'. $this->getFieldName('primary') .' = '. intval($watcheduser) .' AND W.uId = '. intval($currentuser);
+
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(GENERAL_ERROR, 'Could not get watchstatus', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $retval = true;
+ if ($this->db->sql_numrows($dbresult) == 0)
+ $retval = false;
+
+ $this->db->sql_freeresult($dbresult);
+ return $retval;
+ }
+
+ function setWatchStatus($subjectUserID) {
+ if (!is_numeric($subjectUserID))
+ return false;
+
+ $currentUserID = $this->getCurrentUserId();
+ $watched = $this->getWatchStatus($subjectUserID, $currentUserID);
+
+ if ($watched) {
+ $sql = 'DELETE FROM '. $GLOBALS['tableprefix'] .'watched WHERE uId = '. intval($currentUserID) .' AND watched = '. intval($subjectUserID);
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not add user to watch list', '', __LINE__, __FILE__, $sql, $this->db);
+ return false;
+ }
+ } else {
+ $values = array(
+ 'uId' => intval($currentUserID),
+ 'watched' => intval($subjectUserID)
+ );
+ $sql = 'INSERT INTO '. $GLOBALS['tableprefix'] .'watched '. $this->db->sql_build_array('INSERT', $values);
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(GENERAL_ERROR, 'Could not add user to watch list', '', __LINE__, __FILE__, $sql, $this->db);
+ return false;
+ }
+ }
+
+ $this->db->sql_transaction('commit');
+ return true;
+ }
+
+ /**
+ * Create a new user in database.
+ * No checks are done in here - you ought to have checked
+ * everything before calling this method!
+ *
+ * @param string $username Username to use
+ * @param string $password Password to use
+ * @param string $email Email to use
+ * @param string $privateKey Key for RSS auth
+ *
+ * @return mixed Integer user ID if all is well,
+ * boolean false if an error occured
+ */
+ public function addUser($username, $password, $email, $privateKey = null)
+ {
+ // Set up the SQL UPDATE statement.
+ $datetime = gmdate('Y-m-d H:i:s', time());
+ $password = $this->sanitisePassword($password);
+ $values = array(
+ 'username' => $username,
+ 'password' => $password,
+ 'email' => $email,
+ 'uDatetime' => $datetime,
+ 'uModified' => $datetime,
+ 'privateKey' => $privateKey
+ );
+ $sql = 'INSERT INTO '. $this->getTableName()
+ . ' '. $this->db->sql_build_array('INSERT', $values);
+
+ // Execute the statement.
+ $this->db->sql_transaction('begin');
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not insert user',
+ '', __LINE__, __FILE__, $sql, $this->db
+ );
+ return false;
+ }
+ $uId = $this->db->sql_nextid($dbresult);
+ $this->db->sql_transaction('commit');
+
+ return $uId;
+ }
+
+ /**
+ * Updates the given user
+ *
+ * @param integer $uId ID of user to change
+ * @param string $password Password to use
+ * @param string $name Realname to use
+ * @param string $email Email to use
+ * @param string $homepage User's homepage
+ * @param string $uContent User note
+ * @param string $privateKey RSS Private Key
+ * @param boolean $enablePrivateKey RSS Private Key Flag
+ *
+ * @return boolean True when all is well, false if not
+ */
+ public function updateUser(
+ $uId, $password, $name, $email, $homepage, $uContent,
+ $privateKey = null, $enablePrivateKey = false
+ ) {
+ if (!is_numeric($uId)) {
+ return false;
+ }
+
+ // prepend '-' to privateKey if disabled
+ if ($privateKey != null && strlen($privateKey) == 32
+ && $enablePrivateKey == false
+ ) {
+ $privateKey = '-' . $privateKey;
+ }
+
+ // remove '-' from privateKey if enabling
+ if ($privateKey != null && strlen($privateKey) == 33
+ && $enablePrivateKey == true
+ ) {
+ $privateKey = substr($privateKey, 1, 32);
+ }
+
+ // if new user is enabling Private Key, create new key
+ if ($privateKey == null && $enablePrivateKey == true) {
+ $privateKey = $this->getNewPrivateKey();
+ }
+
+ // Set up the SQL UPDATE statement.
+ $moddatetime = gmdate('Y-m-d H:i:s', time());
+ if ($password == '') {
+ $updates = array(
+ 'uModified' => $moddatetime,
+ 'name' => $name,
+ 'email' => $email,
+ 'homepage' => $homepage,
+ 'uContent' => $uContent,
+ 'privateKey' => $privateKey
+ );
+ } else {
+ $updates = array(
+ 'uModified' => $moddatetime,
+ 'password' => $this->sanitisePassword($password),
+ 'name' => $name,
+ 'email' => $email,
+ 'homepage' => $homepage,
+ 'uContent' => $uContent,
+ 'privateKey' => $privateKey
+ );
+ }
+ $sql = 'UPDATE '. $this->getTableName()
+ . ' SET '. $this->db->sql_build_array('UPDATE', $updates)
+ . ' WHERE '. $this->getFieldName('primary') . '=' . intval($uId);
+
+ // Execute the statement.
+ $this->db->sql_transaction('begin');
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ $this->db->sql_transaction('rollback');
+ message_die(
+ GENERAL_ERROR, 'Could not update user', '',
+ __LINE__, __FILE__, $sql, $this->db
+ );
+ return false;
+ }
+ $this->db->sql_transaction('commit');
+
+ // Everything worked out, so return true.
+ return true;
+ }
+
+
+
+ function getAllUsers ( ) {
+ $query = 'SELECT * FROM '. $this->getTableName();
+
+ if (! ($dbresult = $this->db->sql_query($query)) ) {
+ message_die(GENERAL_ERROR, 'Could not get users', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ $rows = array();
+
+ while ( $row = $this->db->sql_fetchrow($dbresult) ) {
+ $rows[] = $row;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $rows;
+ }
+
+ // Returns an array with admin uIds
+ function getAdminIds() {
+ $admins = array();
+ foreach($GLOBALS['admin_users'] as $adminName) {
+ if($this->getIdFromUser($adminName) != NULL)
+ $admins[] = $this->getIdFromUser($adminName);
+ }
+ return $admins;
+ }
+
+ function deleteUser($uId) {
+ $query = 'DELETE FROM '. $this->getTableName() .' WHERE uId = '. intval($uId);
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(GENERAL_ERROR, 'Could not delete user', '', __LINE__, __FILE__, $query, $this->db);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Delete all users and their watch states.
+ * Mainly used in unit tests.
+ *
+ * @return void
+ */
+ public function deleteAll()
+ {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+
+ $query = 'TRUNCATE TABLE `' . $GLOBALS['tableprefix'] . 'watched' . '`';
+ $this->db->sql_query($query);
+ }
+
+ /**
+ * Hashes the password for storage in/querying the database.
+ *
+ * @param string $password Password to hash
+ *
+ * @return string Hashed password
+ */
+ public function sanitisePassword($password)
+ {
+ return sha1(trim($password));
+ }
+
+ /**
+ * Changes the password for the given user to a new, random one.
+ *
+ * @param integer $uId User ID
+ *
+ * @return string New password of false if something went wrong
+ */
+ public function generatePassword($uId)
+ {
+ if (!is_numeric($uId)) {
+ return false;
+ }
+
+ $password = $this->_randompassword();
+
+ $ok = $this->_updateuser(
+ $uId, $this->getFieldName('password'),
+ $this->sanitisePassword($password)
+ );
+
+ if ($ok) {
+ return $password;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Generates a new private key and confirms it isn't being used.
+ * Private key is 32 characters long, consisting of lowercase and
+ * numeric characters.
+ *
+ * @return string the new key value
+ */
+ public function getNewPrivateKey()
+ {
+ do {
+ $newKey = md5(uniqid('SemanticScuttle', true));
+ } while ($this->privateKeyExists($newKey));
+
+ return $newKey;
+ }
+
+ /**
+ * Checks if a private key already exists
+ *
+ * @param string $privateKey key that has been generated
+ *
+ * @return boolean true when the private key exists,
+ * False if not.
+ */
+ public function privateKeyExists($privateKey)
+ {
+ if (!$privateKey) {
+ return false;
+ }
+ $crit = array('privateKey' => $privateKey);
+
+ $sql = 'SELECT COUNT(*) as "0" FROM '
+ . $GLOBALS['tableprefix'] . 'users'
+ . ' WHERE '. $this->db->sql_build_array('SELECT', $crit);
+
+ if (!($dbresult = $this->db->sql_query($sql))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get vars', '',
+ __LINE__, __FILE__, $sql, $this->db
+ );
+ }
+ if ($this->db->sql_fetchfield(0, 0) > 0) {
+ $exists = true;
+ } else {
+ $exists = false;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $exists;
+ }
+
+ function isReserved($username) {
+ if (in_array($username, $GLOBALS['reservedusers'])) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function isValidUsername($username) {
+ if (strlen($username) < 4) {
+ return false;
+ }elseif (strlen($username) > 24) {
+ // too long usernames are cut by database and may cause bugs when compared
+ return false;
+ } elseif (preg_match('/(\W)/', $username) > 0) {
+ // forbidden non-alphanumeric characters
+ return false;
+ }
+ return true;
+ }
+
+
+
+ /**
+ * Checks if the given email address is valid
+ *
+ * @param string $email Email address
+ *
+ * @return boolean True if it is valid, false if not
+ */
+ public function isValidEmail($email)
+ {
+ return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
+ }
+
+ /**
+ * Sets a session variable.
+ * Updates it when it is already set.
+ * This is used to detect if cookies work.
+ *
+ * @return void
+ *
+ * @see isSessionStable()
+ */
+ public function updateSessionStability()
+ {
+ //find out if we have cookies enabled
+ if (!isset($_SESSION['sessionStable'])) {
+ $_SESSION['sessionStable'] = 0;
+ } else {
+ $_SESSION['sessionStable'] = 1;
+ }
+ }
+
+ /**
+ * Tells you if the session is fresh or old.
+ * If the session is fresh, it's the first page
+ * call with that session id. If the session is old,
+ * we know that cookies (or session persistance) works
+ *
+ * @return boolean True if the
+ *
+ * @see updateSessionStability()
+ */
+ public function isSessionStable()
+ {
+ return $_SESSION['sessionStable'] == 1;
+ }
+
+ /**
+ * Get database column name.
+ *
+ * @param string $field Field name like 'primary', 'username'
+ * and 'password'
+ *
+ * @return string Real field name
+ */
+ public function getFieldName($field)
+ {
+ return $this->fields[$field];
+ }
+
+ /**
+ * Set field name
+ *
+ * @param string $field Field name like 'primary', 'username'
+ * and 'password'
+ * @param string $value Real database column name
+ *
+ * @return void
+ */
+ public function setFieldName($field, $value)
+ {
+ $this->fields[$field] = $value;
+ }
+
+ function getSessionKey() { return $this->sessionkey; }
+ function setSessionKey($value) { $this->sessionkey = $value; }
+
+ function getCookieKey() { return $this->cookiekey; }
+ function setCookieKey($value) { $this->cookiekey = $value; }
+}
+
+?>
diff --git a/src/SemanticScuttle/Service/User/SslClientCert.php b/src/SemanticScuttle/Service/User/SslClientCert.php
new file mode 100644
index 0000000..a6d43be
--- /dev/null
+++ b/src/SemanticScuttle/Service/User/SslClientCert.php
@@ -0,0 +1,283 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle SSL client certificate management service
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_User_SslClientCert extends SemanticScuttle_DbService
+{
+ /**
+ * Creates a new instance, sets database variable and table name.
+ *
+ * @param sql_db $db Database object
+ */
+ protected function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] .'users_sslclientcerts';
+ }
+
+ /**
+ * Returns the single service instance
+ *
+ * @param sql_db $db Database object
+ *
+ * @return SemanticScuttle_Service_User
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+ /**
+ * Determines if the browser provided a valid SSL client certificate
+ *
+ * @return boolean True if the client cert is there and is valid
+ */
+ public function hasValidCert()
+ {
+ if (!isset($_SERVER['SSL_CLIENT_M_SERIAL'])
+ || !isset($_SERVER['SSL_CLIENT_V_END'])
+ || !isset($_SERVER['SSL_CLIENT_VERIFY'])
+ || $_SERVER['SSL_CLIENT_VERIFY'] !== 'SUCCESS'
+ || !isset($_SERVER['SSL_CLIENT_I_DN'])
+ ) {
+ return false;
+ }
+
+ if ($_SERVER['SSL_CLIENT_V_REMAIN'] <= 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * Registers the currently available SSL client certificate
+ * with the given user. As a result, the user will be able to login
+ * using the certifiate
+ *
+ * @param integer $uId User ID to attach the client cert to.
+ *
+ * @return boolean True if registration was well, false if not.
+ */
+ public function registerCurrentCertificate($uId)
+ {
+ $serial = $_SERVER['SSL_CLIENT_M_SERIAL'];
+ $clientIssuerDn = $_SERVER['SSL_CLIENT_I_DN'];
+
+ $query = 'INSERT INTO ' . $this->getTableName()
+ . ' '. $this->db->sql_build_array(
+ 'INSERT', array(
+ 'uId' => $uId,
+ 'sslSerial' => $serial,
+ 'sslClientIssuerDn' => $clientIssuerDn,
+ 'sslName' => $_SERVER['SSL_CLIENT_S_DN_CN'],
+ 'sslEmail' => $_SERVER['SSL_CLIENT_S_DN_Email']
+ )
+ );
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not load user for client certificate',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * Takes values from the currently available SSL client certificate
+ * and adds the available profile data to the user.
+ *
+ * @param integer $uId User ID to attach the client cert to.
+ *
+ * @return array Array of profile data that were registered.
+ * Database column name as key, new value as value
+ */
+ public function updateProfileFromCurentCert($uId)
+ {
+ $arData = array();
+
+ if (isset($_SERVER['SSL_CLIENT_S_DN_CN'])
+ && trim($_SERVER['SSL_CLIENT_S_DN_CN']) != ''
+ ) {
+ $arData['name'] = trim($_SERVER['SSL_CLIENT_S_DN_CN']);
+ }
+
+ if (count($arData)) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ foreach ($arData as $column => $value) {
+ $us->_updateuser($uId, $column, $value);
+ }
+ }
+ return $arData;
+ }
+
+
+
+ /**
+ * Tries to detect the user ID from the SSL client certificate passed
+ * to the web server.
+ *
+ * @return mixed Integer user ID if the certificate is valid and
+ * assigned to a user, boolean false otherwise
+ */
+ public function getUserIdFromCert()
+ {
+ if (!$this->hasValidCert()) {
+ return false;
+ }
+
+ $serial = $_SERVER['SSL_CLIENT_M_SERIAL'];
+ $clientIssuerDn = $_SERVER['SSL_CLIENT_I_DN'];
+
+ $query = 'SELECT uId'
+ . ' FROM ' . $this->getTableName()
+ . ' WHERE sslSerial = \'' . $this->db->sql_escape($serial) . '\''
+ . ' AND sslClientIssuerDn = \''
+ . $this->db->sql_escape($clientIssuerDn)
+ . '\'';
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not load user for client certificate',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($dbresult);
+ $this->db->sql_freeresult($dbresult);
+
+ if (!$row) {
+ return false;
+ }
+ return (int)$row['uId'];
+ }
+
+
+
+ /**
+ * Fetches the certificate with the given ID from database.
+ *
+ * @param integer $id Certificate ID in database
+ *
+ * @return SemanticScuttle_Model_User_SslClientCert Certificate object
+ * or null if not found
+ */
+ public function getCert($id)
+ {
+ $query = 'SELECT * FROM ' . $this->getTableName()
+ . ' WHERE id = ' . (int)$id;
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not load SSL client certificate',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return null;
+ }
+
+ if ($row = $this->db->sql_fetchrow($dbresult)) {
+ $cert = SemanticScuttle_Model_User_SslClientCert::fromDb($row);
+ } else {
+ $cert = null;
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $cert;
+ }
+
+
+
+ /**
+ * Fetches all registered certificates for the user from the database
+ * and returns it.
+ *
+ * @return array Array with all certificates for the user. Empty if
+ * there are none, SemanticScuttle_Model_User_SslClientCert
+ * objects otherwise.
+ */
+ public function getUserCerts($uId)
+ {
+ $query = 'SELECT * FROM ' . $this->getTableName()
+ . ' WHERE uId = ' . (int)$uId
+ . ' ORDER BY sslSerial DESC';
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not load SSL client certificates',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return array();
+ }
+
+ $certs = array();
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
+ $certs[] = SemanticScuttle_Model_User_SslClientCert::fromDb($row);
+ }
+ $this->db->sql_freeresult($dbresult);
+ return $certs;
+ }
+
+
+
+ /**
+ * Deletes a SSL client certificate.
+ * No security checks are made here.
+ *
+ * @param mixed $cert Certificate object or certificate database id.
+ * Objects are of type
+ * SemanticScuttle_Model_User_SslClientCert
+ *
+ * @return boolean True if all went well, false if it could not be deleted
+ */
+ public function delete($cert)
+ {
+ if ($cert instanceof SemanticScuttle_Model_User_SslClientCert) {
+ $id = (int)$cert->id;
+ } else {
+ $id = (int)$cert;
+ }
+
+ if ($id === 0) {
+ return false;
+ }
+
+ $query = 'DELETE FROM ' . $this->getTableName()
+ .' WHERE id = ' . $id;
+
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not delete user certificate',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ return false;
+ }
+
+ return true;
+ }
+}
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service/Vote.php b/src/SemanticScuttle/Service/Vote.php
new file mode 100644
index 0000000..85380e7
--- /dev/null
+++ b/src/SemanticScuttle/Service/Vote.php
@@ -0,0 +1,337 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle voting system.
+ *
+ * Each registered user in SemanticScuttle may vote
+ * for or against a bookmark, counting +1 or -1.
+ * The sum of all votes determines the "voting" of a bookmark.
+ * Every user is equal in voting. Each vote is tied to a user.
+ *
+ * @internal
+ * Votes are saved in a separate "votes" table.
+ * Additionally to that, the voting and number of votes
+ * of a bookmark is also stored in the bookmarks table.
+ * This is done to make sure lookups are really fast, since
+ * every bookmark in a list shows its voting.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Service_Vote extends SemanticScuttle_DbService
+{
+ /**
+ * Returns the single service instance
+ *
+ * @param sql_db $db Database object
+ *
+ * @return SemanticScuttle_Service_Vote
+ */
+ public static function getInstance($db)
+ {
+ static $instance;
+ if (!isset($instance)) {
+ $instance = new self($db);
+ }
+ return $instance;
+ }
+
+
+
+ /**
+ * Create a new instance.
+ *
+ * @param sql_db $db Database object
+ */
+ protected function __construct($db)
+ {
+ $this->db = $db;
+ $this->tablename = $GLOBALS['tableprefix'] . 'votes';
+ }
+
+
+
+ /**
+ * Returns the sum of votes for the given bookmark.
+ *
+ * @internal
+ * Uses the "votes" table to retrieve the votes, which
+ * has high costs. It is more efficient to get the sum of
+ * all votes for a bookmark from the bookmarks table,
+ * field bVoting.
+ *
+ * @param integer $bookmark Bookmark ID
+ *
+ * @return integer Vote (can be positive, 0 or negative)
+ */
+ public function getVoting($bookmark)
+ {
+ $query = 'SELECT SUM(vote) as sum FROM ' . $this->getTableName()
+ . ' WHERE bid = "' . $this->db->sql_escape($bookmark) . '"';
+
+ if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get voting',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ //FIXME: throw exception
+ }
+
+ $row = $this->db->sql_fetchrow($dbres);
+ $this->db->sql_freeresult($dbres);
+
+ return (int)$row['sum'];
+ }//public function getVoting(..)
+
+
+
+ /**
+ * Returns the number of users that voted for or
+ * against the given bookmark.
+ *
+ * @internal
+ * This method uses the votes table to calculate the
+ * number of votes for a bookmark. In normal life, it
+ * is more efficient to use the "bVotes" field in the
+ * bookmarks table.
+ *
+ * @param integer $bookmark Bookmark ID
+ *
+ * @return integer Number of votes
+ */
+ public function getVotes($bookmark)
+ {
+ $query = 'SELECT COUNT(vote) as count FROM '
+ . $this->getTableName()
+ . ' WHERE bid = "' . $this->db->sql_escape($bookmark) . '"';
+
+ if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get vote count',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ //FIXME: throw exception
+ }
+
+ $row = $this->db->sql_fetchrow($dbres);
+ $this->db->sql_freeresult($dbres);
+
+ return (int)$row['count'];
+ }
+
+
+
+ /**
+ * Returns if the user has already voted for
+ * the given bookmark.
+ *
+ * @param integer $bookmark Bookmark ID
+ * @param integer $user User ID
+ *
+ * @return boolean True if the user has already voted
+ */
+ public function hasVoted($bookmark, $user)
+ {
+ $query = 'SELECT COUNT(vote) as count FROM '
+ . $this->getTableName()
+ . ' WHERE'
+ . ' bid = "' . $this->db->sql_escape($bookmark) . '"'
+ . ' AND uid = "' . $this->db->sql_escape($user) . '"';
+
+ if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get vote count',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ //FIXME: throw exception
+ }
+
+ $row = $this->db->sql_fetchrow($dbres);
+ $this->db->sql_freeresult($dbres);
+
+ return (int)$row['count'] == 1;
+ }
+
+
+
+ /**
+ * Returns the actual vote the given user
+ * gave the bookmark.
+ *
+ * @param integer $bookmark Bookmark ID
+ * @param integer $user User ID
+ *
+ * @return integer Either 1 or -1, null when not voted.
+ */
+ public function getVote($bookmark, $user)
+ {
+ $query = 'SELECT vote FROM ' . $this->getTableName()
+ . ' WHERE'
+ . ' bid = "' . $this->db->sql_escape($bookmark) . '"'
+ . ' AND uid = "' . $this->db->sql_escape($user) . '"';
+
+ if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get vote count',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
+ //FIXME: throw exception
+ }
+
+ $row = $this->db->sql_fetchrow($dbres);
+ $this->db->sql_freeresult($dbres);
+
+ if (!$row) {
+ return null;
+ }
+
+ return $row['vote'];
+ }
+
+
+
+ /**
+ * Let a user vote for the bookmark.
+ *
+ * @internal
+ * We check if voting is enabled or not,
+ * and if the user has already voted.
+ * It is up to the calling code to make sure
+ * the user is authorized to vote.
+ *
+ * @param integer $bookmark Bookmark ID
+ * @param integer $user User ID
+ * @param integer $vote 1 or -1
+ *
+ * @return boolean True if the vote was saved,
+ * false if there was a problem
+ * (i.e. already voted)
+ */
+ public function vote($bookmark, $user, $vote = 1)
+ {
+ if ($GLOBALS['enableVoting'] == false) {
+ return false;
+ }
+
+ if ($vote != -1 && $vote != 1) {
+ return false;
+ }
+
+ if ($this->hasVoted($bookmark, $user)) {
+ $this->removeVote($bookmark, $user);
+ }
+
+ $res = $this->db->sql_query(
+ 'INSERT INTO ' . $this->getTableName()
+ . ' SET'
+ . ' bid = ' . (int)$bookmark
+ . ',uid = ' . (int)$user
+ . ',vote = ' . (int)$vote
+ );
+ //FIXME: check for sql error
+ $this->db->sql_freeresult($res);
+
+ //update bookmark table
+ $bm = SemanticScuttle_Service_Factory::get('Bookmark');
+ $res = $this->db->sql_query(
+ $sql='UPDATE ' . $bm->getTableName()
+ . ' SET bVoting = bVoting + ' . (int)$vote
+ . ' , bVotes = bVotes + 1'
+ . ' WHERE bId = ' . (int)$bookmark
+ );
+ $this->db->sql_freeresult($res);
+
+ return true;
+ }
+
+
+
+ /**
+ * Removes a vote from the database
+ *
+ * @param integer $bookmark Bookmark ID
+ * @param integer $user User ID
+ *
+ * @return boolean True if all went well, false if not
+ */
+ protected function removeVote($bookmark, $user)
+ {
+ $vote = $this->getVote($bookmark, $user);
+ if ($vote === null) {
+ return false;
+ }
+
+ //remove from votes table
+ $query = 'DELETE FROM ' . $this->getTableName()
+ . ' WHERE bId = ' . (int)$bookmark
+ . ' AND uId = ' . (int)$user;
+ $this->db->sql_query($query);
+
+ //change voting sum in bookmarks table
+ $bm = SemanticScuttle_Service_Factory::get('Bookmark');
+ $res = $this->db->sql_query(
+ $sql='UPDATE ' . $bm->getTableName()
+ . ' SET bVoting = bVoting - ' . (int)$vote
+ . ' , bVotes = bVotes - 1'
+ . ' WHERE bId = ' . (int)$bookmark
+ );
+ $this->db->sql_freeresult($res);
+
+ return true;
+ }
+
+
+
+ /**
+ * Re-calculates all votings for all bookmarks
+ * and updates the voting values in the bookmarks
+ * table.
+ * This is mainly meant to be an administrative method
+ * to fix a broken database.
+ *
+ * @return void
+ */
+ public function rewriteVotings()
+ {
+ $bm = SemanticScuttle_Service_Factory::get('Bookmark');
+ $query = 'UPDATE ' . $bm->getTableName() . ' as B SET bVoting = '
+ . '(SELECT SUM(vote) FROM ' . $this->getTableName() . ' as V'
+ . ' WHERE V.bId = B.bId GROUP BY bid)'
+ . ', bVotes = '
+ . '(SELECT COUNT(vote) FROM ' . $this->getTableName() . ' as V'
+ . ' WHERE V.bId = B.bId GROUP BY bid)';
+ $this->db->sql_query($query);
+ }
+
+
+
+ /**
+ * Delete all votes from the database table.
+ * Used in unit tests.
+ *
+ * @return void
+ */
+ public function deleteAll()
+ {
+ $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`';
+ $this->db->sql_query($query);
+ }
+
+
+}
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Thumbnailer/Null.php b/src/SemanticScuttle/Thumbnailer/Null.php
new file mode 100644
index 0000000..ec12135
--- /dev/null
+++ b/src/SemanticScuttle/Thumbnailer/Null.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Dummy thumbnailer that never returns a thumbnail URL
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Thumbnailer_Null
+{
+ /**
+ * Set dummy configuration
+ *
+ * @param array $config Dummy configuration
+ *
+ * @return void
+ */
+ public function setConfig($config)
+ {
+ }
+
+ /**
+ * Get the URL for a website thumbnail.
+ * Always returns false.
+ *
+ * @param string $bookmarkUrl URL of website to create thumbnail for
+ * @param integer $width Screenshot width
+ * @param integer $height Screenshot height
+ *
+ * @return mixed FALSE when no screenshot could be obtained,
+ * string with the URL otherwise
+ */
+ public function getThumbnailUrl($bookmarkUrl, $width, $height)
+ {
+ return false;
+ }
+}
+?>
diff --git a/src/SemanticScuttle/Thumbnailer/Phancap.php b/src/SemanticScuttle/Thumbnailer/Phancap.php
new file mode 100644
index 0000000..1b76849
--- /dev/null
+++ b/src/SemanticScuttle/Thumbnailer/Phancap.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Show website thumbnails/screenshots using phancap
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ * @see http://cweiske.de/phancap.htm
+ */
+class SemanticScuttle_Thumbnailer_Phancap
+{
+ /**
+ * Configuration array.
+ * Required keys:
+ * - url
+ * - token
+ * - secret
+ */
+ protected $config = array();
+
+ /**
+ * Set phancap configuration
+ *
+ * @param array $config Phancap configuration
+ *
+ * @return void
+ */
+ public function setConfig($config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * Get the URL for a website thumbnail
+ *
+ * @param string $bookmarkUrl URL of website to create thumbnail for
+ * @param integer $width Screenshot width
+ * @param integer $height Screenshot height
+ *
+ * @return mixed FALSE when no screenshot could be obtained,
+ * string with the URL otherwise
+ */
+ public function getThumbnailUrl($bookmarkUrl, $width, $height)
+ {
+ //default parameters for the phancap service
+ $parameters = array(
+ 'url' => $bookmarkUrl,
+ 'swidth' => $width,
+ 'sheight' => $height,
+ 'sformat' => 'jpg',
+ );
+
+ if (isset($this->config['token']) && $this->config['token'] != '') {
+ $parameters['atoken'] = $this->config['token'];
+ $parameters['atimestamp'] = time();
+
+ //create signature
+ ksort($parameters);
+ foreach ($parameters as $key => $value) {
+ $encparams[] = $key . '=' . rawurlencode($value);
+ }
+ $encstring = implode('&', $encparams);
+ $signature = hash_hmac('sha1', $encstring, $this->config['secret']);
+ //append signature to parameters
+ $parameters['asignature'] = $signature;
+ }
+
+ //url-encode the parameters
+ $urlParams = array();
+ foreach ($parameters as $key => $value) {
+ $urlParams[] = $key . '=' . urlencode($value);
+ }
+
+ //final URL
+ return $this->config['url'] . '?' . implode('&', $urlParams);
+ }
+}
+?>
diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php
new file mode 100644
index 0000000..306c32a
--- /dev/null
+++ b/src/SemanticScuttle/constants.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Define constants used in all the application.
+ * Some constants are based on variables from configuration file.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @subcategory Base
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// Debug managament
+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)
+ if ($piece != '' && !strstr($piece, '.php')
+ && $piece != 'gsearch' && $piece != 'ajax'
+ ) {
+ $rootTmp .= $piece .'/';
+ }
+ }
+ if (($rootTmp != '/') && (substr($rootTmp, -1, 1) != '/')) {
+ $rootTmp .= '/';
+ }
+
+ //we do not prepend http since we also want to support https connections
+ // "http" is not required; it's automatically determined by the browser
+ // depending on the current connection.
+ define('ROOT', '//'. $_SERVER['HTTP_HOST'] . $rootTmp);
+} else {
+ define('ROOT', $GLOBALS['root']);
+}
+define('ROOT_JS', ROOT . 'js/jstree-1.0-rc2/');
+
+// Error codes
+define('GENERAL_MESSAGE', 200);
+define('GENERAL_ERROR', 202);
+define('CRITICAL_MESSAGE', 203);
+define('CRITICAL_ERROR', 204);
+
+// Page name
+define('PAGE_INDEX', "index");
+define('PAGE_BOOKMARKS', "bookmarks");
+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
+define('INSTALLATION_ID', md5($GLOBALS['dbname'].$GLOBALS['tableprefix']));
+
+//fix PATH_INFO on certain hosts
+$_SERVER['PATH_INFO'] = SemanticScuttle_Environment::getServerPathInfo();
+?>
diff --git a/src/SemanticScuttle/db/db2.php b/src/SemanticScuttle/db/db2.php
new file mode 100644
index 0000000..b1abf1a
--- /dev/null
+++ b/src/SemanticScuttle/db/db2.php
@@ -0,0 +1,417 @@
+<?php
+/**
+*
+* @package dbal_db2
+* @version $Id: db2.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if(!defined("SQL_LAYER"))
+{
+
+define("SQL_LAYER","db2");
+
+/**
+* @package dbal_db2
+* DB2 Database Abstraction Layer
+*/
+class sql_db
+{
+
+ var $db_connect_id;
+ var $query_result;
+ var $query_resultset;
+ var $query_numrows;
+ var $next_id;
+ var $row = array();
+ var $rowset = array();
+ var $row_index;
+ var $num_queries = 0;
+
+ //
+ // Constructor
+ //
+ function sql_db($sqlserver, $sqluser, $sqlpassword, $database, $persistency = true)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->password = $sqlpassword;
+ $this->dbname = $database;
+
+ $this->server = $sqlserver;
+
+ if($this->persistency)
+ {
+ $this->db_connect_id = odbc_pconnect($this->server, "", "");
+ }
+ else
+ {
+ $this->db_connect_id = odbc_connect($this->server, "", "");
+ }
+
+ if($this->db_connect_id)
+ {
+ @odbc_autocommit($this->db_connect_id, off);
+
+ return $this->db_connect_id;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if($this->db_connect_id)
+ {
+ if($this->query_result)
+ {
+ @odbc_free_result($this->query_result);
+ }
+ $result = @odbc_close($this->db_connect_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+ //
+ // Query method
+ //
+ function sql_query($query = "", $transaction = FALSE)
+ {
+ //
+ // Remove any pre-existing queries
+ //
+ unset($this->query_result);
+ unset($this->row);
+ if($query != "")
+ {
+ $this->num_queries++;
+
+ if(!eregi("^INSERT ",$query))
+ {
+ if(eregi("LIMIT", $query))
+ {
+ preg_match("/^(.*)LIMIT ([0-9]+)[, ]*([0-9]+)*/s", $query, $limits);
+
+ $query = $limits[1];
+ if($limits[3])
+ {
+ $row_offset = $limits[2];
+ $num_rows = $limits[3];
+ }
+ else
+ {
+ $row_offset = 0;
+ $num_rows = $limits[2];
+ }
+
+ $query .= " FETCH FIRST ".($row_offset+$num_rows)." ROWS ONLY OPTIMIZE FOR ".($row_offset+$num_rows)." ROWS";
+
+ $this->query_result = odbc_exec($this->db_connect_id, $query);
+
+ $query_limit_offset = $row_offset;
+ $this->result_numrows[$this->query_result] = $num_rows;
+ }
+ else
+ {
+ $this->query_result = odbc_exec($this->db_connect_id, $query);
+
+ $row_offset = 0;
+ $this->result_numrows[$this->query_result] = 5E6;
+ }
+
+ $result_id = $this->query_result;
+ if($this->query_result && eregi("^SELECT", $query))
+ {
+
+ for($i = 1; $i < odbc_num_fields($result_id)+1; $i++)
+ {
+ $this->result_field_names[$result_id][] = odbc_field_name($result_id, $i);
+ }
+
+ $i = $row_offset + 1;
+ $k = 0;
+ while(odbc_fetch_row($result_id, $i) && $k < $this->result_numrows[$result_id])
+ {
+
+ for($j = 1; $j < count($this->result_field_names[$result_id])+1; $j++)
+ {
+ $this->result_rowset[$result_id][$k][$this->result_field_names[$result_id][$j-1]] = odbc_result($result_id, $j);
+ }
+ $i++;
+ $k++;
+ }
+
+ $this->result_numrows[$result_id] = $k;
+ $this->row_index[$result_id] = 0;
+ }
+ else
+ {
+ $this->result_numrows[$result_id] = @odbc_num_rows($result_id);
+ $this->row_index[$result_id] = 0;
+ }
+ }
+ else
+ {
+ if(eregi("^(INSERT|UPDATE) ", $query))
+ {
+ $query = preg_replace("/\\\'/s", "''", $query);
+ }
+
+ $this->query_result = odbc_exec($this->db_connect_id, $query);
+
+ if($this->query_result)
+ {
+ $sql_id = "VALUES(IDENTITY_VAL_LOCAL())";
+
+ $id_result = odbc_exec($this->db_connect_id, $sql_id);
+ if($id_result)
+ {
+ $row_result = odbc_fetch_row($id_result);
+ if($row_result)
+ {
+ $this->next_id[$this->query_result] = odbc_result($id_result, 1);
+ }
+ }
+ }
+
+ odbc_commit($this->db_connect_id);
+
+ $this->query_limit_offset[$this->query_result] = 0;
+ $this->result_numrows[$this->query_result] = 0;
+ }
+
+ return $this->query_result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //
+ // Other query methods
+ //
+ function sql_numrows($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ return $this->result_numrows[$query_id];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_affectedrows($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ return $this->result_numrows[$query_id];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_numfields($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = count($this->result_field_names[$query_id]);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fieldname($offset, $query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = $this->result_field_names[$query_id][$offset];
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fieldtype($offset, $query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @odbc_field_type($query_id, $offset);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fetchrow($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ if($this->row_index[$query_id] < $this->result_numrows[$query_id])
+ {
+ $result = $this->result_rowset[$query_id][$this->row_index[$query_id]];
+ $this->row_index[$query_id]++;
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fetchrowset($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $this->row_index[$query_id] = $this->result_numrows[$query_id];
+ return $this->result_rowset[$query_id];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fetchfield($field, $row = -1, $query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ if($row < $this->result_numrows[$query_id])
+ {
+ if($row == -1)
+ {
+ $getrow = $this->row_index[$query_id]-1;
+ }
+ else
+ {
+ $getrow = $row;
+ }
+
+ return $this->result_rowset[$query_id][$getrow][$this->result_field_names[$query_id][$field]];
+
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_rowseek($offset, $query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $this->row_index[$query_id] = 0;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_nextid($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ return $this->next_id[$query_id];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_freeresult($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @odbc_free_result($query_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_error($query_id = 0)
+ {
+// $result['code'] = @odbc_error($this->db_connect_id);
+// $result['message'] = @odbc_errormsg($this->db_connect_id);
+
+ return "";
+ }
+
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/firebird.php b/src/SemanticScuttle/db/firebird.php
new file mode 100644
index 0000000..58a7d07
--- /dev/null
+++ b/src/SemanticScuttle/db/firebird.php
@@ -0,0 +1,527 @@
+<?php
+/**
+*
+* @package dbal_firebird
+* @version $Id: firebird.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'firebird');
+
+/**
+* @package dbal_firebird
+* Firebird/Interbase Database Abstraction Layer
+* Minimum Requirement is Firebird 1.5+/Interbase 7.1+
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ var $last_query_text = '';
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @ibase_pconnect($this->server . ':' . $this->dbname, $this->user, $sqlpassword, false, false, 3) : @ibase_connect($this->server . ':' . $this->dbname, $this->user, $sqlpassword, false, false, 3);
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if ($this->transaction)
+ {
+ @ibase_commit($this->db_connect_id);
+ }
+
+ if (sizeof($this->open_queries))
+ {
+ foreach ($this->open_queries as $i_query_id => $query_id)
+ {
+ @ibase_free_query($query_id);
+ }
+ }
+
+ return @ibase_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @ibase_commit();
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @ibase_rollback();
+ }
+ break;
+
+ case 'rollback':
+ $result = @ibase_rollback();
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+
+ if (($this->query_result = @ibase_query($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ // TODO: have to debug the commit states in firebird
+ if (!$this->transaction)
+ {
+ @ibase_commit_ret();
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ $this->query_result = false;
+
+ $query = 'SELECT FIRST ' . $total . ((!empty($offset)) ? ' SKIP ' . $offset : '') . substr($query, 6);
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ return FALSE;
+ }
+
+ function sql_affectedrows()
+ {
+ // TODO: hmm, maybe doing something similar as in mssql-odbc.php?
+ return ($this->query_result) ? true : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ $row = array();
+ $cur_row = @ibase_fetch_object($query_id, IBASE_TEXT);
+
+ if (!$cur_row)
+ {
+ return false;
+ }
+
+ foreach (get_object_vars($cur_row) as $key => $value)
+ {
+ $row[strtolower($key)] = trim(str_replace("\\0", "\0", str_replace("\\n", "\n", $value)));
+ }
+ return ($query_id) ? $row : false;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ unset($this->rowset[$query_id]);
+ unset($this->row[$query_id]);
+
+ $result = array();
+ while ($this->rowset[$query_id] = get_object_vars(@ibase_fetch_object($query_id, IBASE_TEXT)))
+ {
+ $result[] = $this->rowset[$query_id];
+ }
+
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = 0)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum > -1)
+ {
+ // erm... ok, my bad, we always use zero. :/
+ for ($i = 0; $i <= $rownum; $i++)
+ {
+ $row = $this->sql_fetchrow($query_id);
+ }
+
+ return $row[$field];
+ }
+ else
+ {
+ if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+ {
+ if ($this->sql_fetchrow($query_id))
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ else
+ {
+ if ($this->rowset[$query_id])
+ {
+ $result = $this->rowset[$query_id][$field];
+ }
+ else if ($this->row[$query_id])
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ }
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ function sql_rowseek($rownum, $query_id = 0)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ for($i = 1; $i < $rownum; $i++)
+ {
+ if (!$this->sql_fetchrow($query_id))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function sql_nextid()
+ {
+ if ($this->query_result && preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename))
+ {
+ $query = "SELECT GEN_ID('" . $tablename[1] . "_gen', 0) AS new_id
+ FROM RDB\$DATABASE";
+ if (!($temp_q_id = @ibase_query($this->db_connect_id, $query)))
+ {
+ return false;
+ }
+
+ $temp_result = @ibase_fetch_object($temp_q_id);
+ $this->sql_freeresult($temp_q_id);
+
+ return ($temp_result) ? $temp_result->last_value : false;
+ }
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (!$this->transaction && $query_id)
+ {
+ @ibase_commit();
+ }
+
+ return ($query_id) ? @ibase_free_result($query_id) : false;
+ }
+
+ function sql_escape($msg)
+ {
+ return (@ini_get('magic_quotes_sybase') || strtolower(@ini_get('magic_quotes_sybase')) == 'on') ? str_replace('\\\'', '\'', addslashes($msg)) : str_replace('\'', '\'\'', stripslashes($msg));
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page =(!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' .((!empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : $_ENV['QUERY_STRING']);
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @ibase_errmsg() . '<br /><br /><u>CALLING PAGE</u><br /><br />' . $this_page .(($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result['message'] = @ibase_errmsg();
+ $result['code'] = '';
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $this->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>' . $msg_title . '</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @ibase_query($this->db_connect_id, $query);
+ while ($void = @ibase_fetch_object($result, IBASE_TEXT))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ @ibase_freeresult($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/mssql-odbc.php b/src/SemanticScuttle/db/mssql-odbc.php
new file mode 100644
index 0000000..a2d3d02
--- /dev/null
+++ b/src/SemanticScuttle/db/mssql-odbc.php
@@ -0,0 +1,576 @@
+<?php
+/**
+*
+* @package dbal_odbc_mssql
+* @version $Id: mssql-odbc.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mssql-odbc');
+
+/**
+* @package dbal_odbc_mssql
+* MSSQL ODBC Database Abstraction Layer for MSSQL
+* Minimum Requirement is Version 2000+
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ var $result_rowset = array();
+ var $field_names = array();
+ var $field_types = array();
+ var $num_rows = array();
+ var $current_row = array();
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @odbc_pconnect($this->server, $this->user, $sqlpassword) : @odbc_connect($this->server, $this->user, $sqlpassword);
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if ($this->transaction)
+ {
+ @odbc_commit($this->db_connect_id);
+ }
+
+ if (sizeof($this->result_rowset))
+ {
+ unset($this->result_rowset);
+ unset($this->field_names);
+ unset($this->field_types);
+ unset($this->num_rows);
+ unset($this->current_row);
+ }
+
+ if (sizeof($this->open_queries))
+ {
+ foreach ($this->open_queries as $i_query_id => $query_id)
+ {
+ @odbc_free_result($query_id);
+ }
+ }
+
+ return @odbc_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $result = @odbc_autocommit($this->db_connect_id, false);
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @odbc_commit($this->db_connect_id);
+ @odbc_autocommit($this->db_connect_id, true);
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @odbc_rollback($this->db_connect_id);
+ @odbc_autocommit($this->db_connect_id, true);
+ }
+ break;
+
+ case 'rollback':
+ $result = @odbc_rollback($this->db_connect_id);
+ @odbc_autocommit($this->db_connect_id, true);
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+
+ if (($this->query_result = $this->_odbc_execute_query($query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ // odbc_free_result called within sql_save()
+ }
+ else if (strpos($query, 'SELECT') !== false && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function _odbc_execute_query($query)
+ {
+ $result = false;
+
+ if (eregi("^SELECT ", $query))
+ {
+ $result = @odbc_exec($this->db_connect_id, $query);
+
+ if ($result)
+ {
+ if (empty($this->field_names[$result]))
+ {
+ for ($i = 1, $j = @odbc_num_fields($result) + 1; $i < $j; $i++)
+ {
+ $this->field_names[$result][] = @odbc_field_name($result, $i);
+ $this->field_types[$result][] = @odbc_field_type($result, $i);
+ }
+ }
+
+ $this->current_row[$result] = 0;
+ $this->result_rowset[$result] = array();
+
+ $row_outer = (isset($row_offset)) ? $row_offset + 1 : 1;
+ $row_outer_max = (isset($num_rows)) ? $row_offset + $num_rows + 1 : 1E9;
+ $row_inner = 0;
+
+ while (@odbc_fetch_row($result, $row_outer) && $row_outer < $row_outer_max)
+ {
+ for ($i = 0, $j = sizeof($this->field_names[$result]); $i < $j; $i++)
+ {
+ $this->result_rowset[$result][$row_inner][$this->field_names[$result][$i]] = stripslashes(@odbc_result($result, $i + 1));
+ }
+
+ $row_outer++;
+ $row_inner++;
+ }
+
+ $this->num_rows[$result] = sizeof($this->result_rowset[$result]);
+ }
+ }
+ else if (eregi("^INSERT ", $query))
+ {
+ $result = @odbc_exec($this->db_connect_id, $query);
+
+ if ($result)
+ {
+ $result_id = @odbc_exec($this->db_connect_id, 'SELECT @@IDENTITY');
+ if ($result_id)
+ {
+ if (@odbc_fetch_row($result_id))
+ {
+ $this->next_id[$this->db_connect_id] = @odbc_result($result_id, 1);
+ $this->affected_rows[$this->db_connect_id] = @odbc_num_rows($result);
+ }
+ }
+ }
+ }
+ else
+ {
+ $result = @odbc_exec($this->db_connect_id, $query);
+
+ if ($result)
+ {
+ $this->affected_rows[$this->db_connect_id] = @odbc_num_rows($result);
+ }
+ }
+
+ return $result;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ $total = -1;
+ }
+
+ $row_offset = ($total) ? $offset : '';
+ $num_rows = ($total) ? $total : $offset;
+
+ $query = 'SELECT TOP ' . ($row_offset + $num_rows) . ' ' . substr($query, 6);
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @$this->num_rows($query_id) : false;
+ }
+
+ function sql_affectedrows()
+ {
+ return ($this->affected_rows[$this->db_connect_id]) ? $this->affected_rows[$this->db_connect_id] : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($this->num_rows[$query_id] && $this->current_row[$query_id] < $this->num_rows[$query_id]) ? $this->result_rowset[$query_id][$this->current_row[$query_id]++] : false;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($this->num_rows[$query_id]) ? $this->result_rowset[$query_id] : false;
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum < $this->num_rows[$query_id])
+ {
+ $getrow = ($rownum == -1) ? $this->current_row[$query_id] - 1 : $rownum;
+
+ return $this->result_rowset[$query_id][$getrow][$this->field_names[$query_id][$field]];
+ }
+ }
+
+ return false;
+ }
+
+ function sql_rowseek($rownum, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($this->current_row[$query_id]))
+ {
+ $this->current_row[$query_id] = $rownum;
+ return true;
+ }
+
+ return false;
+ }
+
+ function sql_nextid()
+ {
+ return ($this->next_id[$this->db_connect_id]) ? $this->next_id[$this->db_connect_id] : false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ unset($this->num_rows[$query_id]);
+ unset($this->current_row[$query_id]);
+ unset($this->result_rowset[$query_id]);
+ unset($this->field_names[$query_id]);
+ unset($this->field_types[$query_id]);
+
+ return @odbc_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ function sql_escape($msg)
+ {
+ return str_replace("'", "''", str_replace('\\', '\\\\', $msg));
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @odbc_errormsg() . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @odbc_errormsg(),
+ 'code' => @odbc_error()
+ );
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $this->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>' . $msg_title . '</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = $this->_odbc_execute_query($query);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ @odbc_free_result($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/mssql.php b/src/SemanticScuttle/db/mssql.php
new file mode 100644
index 0000000..2b17b9e
--- /dev/null
+++ b/src/SemanticScuttle/db/mssql.php
@@ -0,0 +1,551 @@
+<?php
+/**
+*
+* @package dbal_mssql
+* @version $Id: mssql.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mssql');
+
+/**
+* @package dbal_mssql
+* MSSQL Database Abstraction Layer
+* Minimum Requirement is MSSQL 2000+
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword) : @mssql_connect($this->server, $this->user, $sqlpassword);
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ if (!@mssql_select_db($this->dbname, $this->db_connect_id))
+ {
+ @mssql_close($this->db_connect_id);
+ return false;
+ }
+ }
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if ($this->transaction)
+ {
+ @mssql_query('COMMIT', $this->db_connect_id);
+ }
+
+ if (sizeof($this->open_queries))
+ {
+ foreach ($this->open_queries as $i_query_id => $query_id)
+ {
+ @mssql_free_result($query_id);
+ }
+ }
+
+ return @mssql_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $result = @mssql_query('BEGIN TRANSACTION', $this->db_connect_id);
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @mssql_query('commit', $this->db_connect_id);
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @mssql_query('ROLLBACK', $this->db_connect_id);
+ }
+ break;
+
+ case 'rollback':
+ $result = @mssql_query('ROLLBACK', $this->db_connect_id);
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+
+ if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ // sql_freeresult called within sql_save()
+ }
+ else if (strpos($query, 'SELECT') !== false && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ $total = -1;
+ }
+
+ $row_offset = ($total) ? $offset : '';
+ $num_rows = ($total) ? $total : $offset;
+
+ $query = 'SELECT TOP ' . ($row_offset + $num_rows) . ' ' . substr($query, 6);
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+// return (isset($this->limit_offset[$query_id])) ? @mssql_num_rows($query_id) - $this->limit_offset[$query_id] : @mssql_num_rows($query_id);
+ return ($query_id) ? @mssql_num_rows($query_id) : false;
+ }
+
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ $row = @mssql_fetch_array($query_id, MSSQL_ASSOC);
+
+ if ($row)
+ {
+ foreach ($row as $key => $value)
+ {
+ $row[$key] = ($value === ' ') ? trim($value) : $value;
+ }
+ }
+
+ return $row;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ unset($this->rowset[$query_id]);
+ unset($this->row[$query_id]);
+
+ $result = array();
+ while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+ {
+ $result[] = $this->rowset[$query_id];
+ }
+ return $result;
+ }
+
+ return false;
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum > -1)
+ {
+// (!empty($this->limit_offset[$query_id])) ? @mssql_data_seek($query_id, ($this->limit_offset[$query_id] + $rownum)) : @mssql_data_seek($query_id, $rownum);
+ @mssql_data_seek($query_id, $rownum);
+ $row = @mssql_fetch_array($query_id, MSSQL_ASSOC);
+ $result = isset($row[$field]) ? $row[$field] : false;
+ }
+ else
+ {
+ if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+ {
+ if ($this->sql_fetchrow($query_id))
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ else
+ {
+ if ($this->rowset[$query_id])
+ {
+ $result = $this->rowset[$query_id][$field];
+ }
+ elseif ($this->row[$query_id])
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ return false;
+ }
+
+ function sql_rowseek($rownum, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($this->current_row[$query_id]))
+ {
+// (!empty($this->limit_offset[$query_id])) ? @mssql_data_seek($query_id, ($this->limit_offset[$query_id] + $rownum)) : @mssql_data_seek($query_id, $rownum);
+ @mssql_data_seek($query_id, $rownum);
+ return true;
+ }
+
+ return false;
+ }
+
+ function sql_nextid()
+ {
+ $result_id = @mssql_query('SELECT @@IDENTITY', $this->db_connect_id);
+ if ($result_id)
+ {
+ if (@mssql_fetch_array($result_id, MSSQL_ASSOC))
+ {
+ return @mssql_result($result_id, 1);
+ }
+ }
+
+ return false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($this->open_queries[$query_id]))
+ {
+ unset($this->open_queries[$query_id]);
+ unset($this->result_rowset[$query_id]);
+
+ return @mssql_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ function sql_escape($msg)
+ {
+ return str_replace("'", "''", str_replace('\\', '\\\\', $msg));
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mssql_get_last_message() . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @mssql_get_last_message($this->db_connect_id),
+ 'code' => ''
+ );
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $this->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>' . $msg_title . '</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @mssql_query($query, $this->db_connect_id);
+ while ($void = @mssql_fetch_array($result, MSSQL_ASSOC))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ @mssql_free_result($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/mysql.php b/src/SemanticScuttle/db/mysql.php
new file mode 100644
index 0000000..a646e0d
--- /dev/null
+++ b/src/SemanticScuttle/db/mysql.php
@@ -0,0 +1,552 @@
+<?php
+/**
+*
+* @package dbal_mysql
+* @version $Id: mysql.php,v 1.5 2006/02/10 01:30:19 scronide Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mysql');
+
+/**
+* @package dbal_mysql
+* MySQL Database Abstraction Layer
+* Minimum Requirement is 3.23+/4.0+/4.1+
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword);
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ if (@mysql_select_db($this->dbname))
+ {
+ return $this->db_connect_id;
+ }
+ }
+
+ return $this->sql_error('');
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if (sizeof($this->open_queries))
+ {
+ foreach ($this->open_queries as $i_query_id => $query_id)
+ {
+ @mysql_free_result($query_id);
+ }
+ }
+
+ return @mysql_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $result = @mysql_query('BEGIN', $this->db_connect_id);
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @mysql_query('COMMIT', $this->db_connect_id);
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @mysql_query('ROLLBACK', $this->db_connect_id);
+ }
+ break;
+
+ case 'rollback':
+ $result = @mysql_query('ROLLBACK', $this->db_connect_id);
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+
+ if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ // mysql_free_result called within sql_save()
+ }
+ else if (strpos($query, 'SELECT') !== false && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) {
+ if ($query != '') {
+ $this->query_result = false;
+
+ // only limit the number of rows if $total is greater than 0
+ if ($total > 0)
+ $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+ return $this->sql_query($query, $cache_ttl);
+ } else {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @mysql_num_rows($query_id) : false;
+ }
+
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ unset($this->rowset[$query_id]);
+ unset($this->row[$query_id]);
+
+ $result = array();
+ while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+ {
+ $result[] = $this->rowset[$query_id];
+ }
+ return $result;
+ }
+
+ return false;
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum > -1)
+ {
+ $result = @mysql_result($query_id, $rownum, $field);
+ }
+ else
+ {
+ if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+ {
+ if ($this->sql_fetchrow($query_id))
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ else
+ {
+ if ($this->rowset[$query_id])
+ {
+ $result = $this->rowset[$query_id][$field];
+ }
+ elseif ($this->row[$query_id])
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ function sql_rowseek($rownum, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @mysql_data_seek($query_id, $rownum) : false;
+ }
+
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @mysql_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ function sql_escape($msg) {
+ if (function_exists('mysql_real_escape_string')) {
+ return @mysql_real_escape_string($msg, $this->db_connect_id);
+ } else {
+ return mysql_escape_string($msg);
+ }
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mysql_error() . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @mysql_error(),
+ 'code' => @mysql_errno()
+ );
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $db, $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $db->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>' . $msg_title . '</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = FALSE;
+
+ if ($result = mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
+ {
+ while ($row = mysql_fetch_assoc($result))
+ {
+ if (!$html_table && sizeof($row))
+ {
+ $html_table = TRUE;
+ $html_hold .= '<table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0" align="center"><tr>';
+
+ foreach (array_keys($row) as $val)
+ {
+ $html_hold .= '<th nowrap="nowrap">' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+ }
+ $html_hold .= '</tr>';
+ }
+ $html_hold .= '<tr>';
+
+ $class = 'row1';
+ foreach (array_values($row) as $val)
+ {
+ $class = ($class == 'row1') ? 'row2' : 'row1';
+ $html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+ }
+ $html_hold .= '</tr>';
+ }
+ }
+
+ if ($html_table)
+ {
+ $html_hold .= '</table>';
+ }
+ }
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = mysql_query($query, $this->db_connect_id);
+ while ($void = mysql_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ mysql_free_result($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - MySQL Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/mysql4.php b/src/SemanticScuttle/db/mysql4.php
new file mode 100644
index 0000000..0639518
--- /dev/null
+++ b/src/SemanticScuttle/db/mysql4.php
@@ -0,0 +1,552 @@
+<?php
+/**
+*
+* @package dbal_mysql4
+* @version $Id: mysql4.php,v 1.4 2006/02/10 01:30:19 scronide Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mysql4');
+
+/**
+* @package dbal_mysql4
+* MySQL4 Database Abstraction Layer
+* Minimum Requirement is 4.0+/4.1+
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword);
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ if (@mysql_select_db($this->dbname))
+ {
+ return $this->db_connect_id;
+ }
+ }
+
+ return $this->sql_error('');
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if (sizeof($this->open_queries))
+ {
+ foreach ($this->open_queries as $i_query_id => $query_id)
+ {
+ @mysql_free_result($query_id);
+ }
+ }
+
+ return @mysql_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $result = @mysql_query('BEGIN', $this->db_connect_id);
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @mysql_query('COMMIT', $this->db_connect_id);
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @mysql_query('ROLLBACK', $this->db_connect_id);
+ }
+ break;
+
+ case 'rollback':
+ $result = @mysql_query('ROLLBACK', $this->db_connect_id);
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+
+ if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ // mysql_free_result called within sql_save()
+ }
+ else if (strpos($query, 'SELECT') !== false && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) {
+ if ($query != '') {
+ $this->query_result = false;
+
+ // only limit the number of rows if $total is greater than 0
+ if ($total > 0)
+ $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+ return $this->sql_query($query, $cache_ttl);
+ } else {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @mysql_num_rows($query_id) : false;
+ }
+
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ unset($this->rowset[$query_id]);
+ unset($this->row[$query_id]);
+
+ $result = array();
+ while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+ {
+ $result[] = $this->rowset[$query_id];
+ }
+ return $result;
+ }
+
+ return false;
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum > -1)
+ {
+ $result = @mysql_result($query_id, $rownum, $field);
+ }
+ else
+ {
+ if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+ {
+ if ($this->sql_fetchrow($query_id))
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ else
+ {
+ if ($this->rowset[$query_id])
+ {
+ $result = $this->rowset[$query_id][$field];
+ }
+ elseif ($this->row[$query_id])
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ function sql_rowseek($rownum, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @mysql_data_seek($query_id, $rownum) : false;
+ }
+
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @mysql_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ function sql_escape($msg) {
+ if (function_exists('mysql_real_escape_string')) {
+ return @mysql_real_escape_string($msg, $this->db_connect_id);
+ } else {
+ return mysql_escape_string($msg);
+ }
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mysql_error() . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @mysql_error(),
+ 'code' => @mysql_errno()
+ );
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $db, $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $db->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>' . $msg_title . '</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = FALSE;
+
+ if ($result = mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
+ {
+ while ($row = mysql_fetch_assoc($result))
+ {
+ if (!$html_table && sizeof($row))
+ {
+ $html_table = TRUE;
+ $html_hold .= '<table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0" align="center"><tr>';
+
+ foreach (array_keys($row) as $val)
+ {
+ $html_hold .= '<th nowrap="nowrap">' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+ }
+ $html_hold .= '</tr>';
+ }
+ $html_hold .= '<tr>';
+
+ $class = 'row1';
+ foreach (array_values($row) as $val)
+ {
+ $class = ($class == 'row1') ? 'row2' : 'row1';
+ $html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+ }
+ $html_hold .= '</tr>';
+ }
+ }
+
+ if ($html_table)
+ {
+ $html_hold .= '</table>';
+ }
+ }
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = mysql_query($query, $this->db_connect_id);
+ while ($void = mysql_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ mysql_free_result($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - MySQL Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/mysqli.php b/src/SemanticScuttle/db/mysqli.php
new file mode 100644
index 0000000..9a2709a
--- /dev/null
+++ b/src/SemanticScuttle/db/mysqli.php
@@ -0,0 +1,562 @@
+<?php
+/**
+*
+* @package dbal_mysqli
+* @version $Id: mysqli.php,v 1.4 2006/02/10 01:30:19 scronide Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mysqli');
+
+/**
+* @package dbal_mysqli
+* MySQLi Database Abstraction Layer
+* Minimum Requirement is MySQL 4.1+ and the mysqli-extension
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ var $indexed = 0;
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @mysqli_pconnect($this->server, $this->user, $sqlpassword) : @mysqli_connect($this->server, $this->user, $sqlpassword);
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ if (@mysqli_select_db($this->db_connect_id, $this->dbname))
+ {
+ return $this->db_connect_id;
+ }
+ }
+
+ return $this->sql_error(mysqli_connect_error());
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if ($this->transaction)
+ {
+ @mysqli_commit($this->db_connect_id);
+ }
+
+ return @mysqli_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $result = @mysqli_autocommit($this->db_connect_id, false);
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @mysqli_commit($this->db_connect_id);
+ @mysqli_autocommit($this->db_connect_id, true);
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @mysqli_rollback($this->db_connect_id);
+ @mysqli_autocommit($this->db_connect_id, true);
+ }
+ break;
+
+ case 'rollback':
+ $result = @mysqli_rollback($this->db_connect_id);
+ @mysqli_autocommit($this->db_connect_id, true);
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+
+ if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (is_object($this->query_result))
+ {
+ $this->query_result->cur_index = $this->indexed++;
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) {
+ if ($query != '') {
+ $this->query_result = false;
+
+ // only limit the number of rows if $total is greater than 0
+ if ($total > 0)
+ $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+ return $this->sql_query($query, $cache_ttl);
+ } else {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @mysqli_num_rows($query_id) : false;
+ }
+
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ $cur_index = (is_object($query_id)) ? $query_id->cur_index : $query_id;
+
+ unset($this->rowset[$cur_index]);
+ unset($this->row[$cur_index]);
+
+ $result = array();
+ while ($this->rowset[$cur_index] = $this->sql_fetchrow($query_id))
+ {
+ $result[] = $this->rowset[$cur_index];
+ }
+ return $result;
+ }
+
+ return false;
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum > -1)
+ {
+ @mysqli_data_seek($query_id, $rownum);
+ $row = @mysqli_fetch_assoc($query_id);
+ $result = isset($row[$field]) ? $row[$field] : false;
+ }
+ else
+ {
+ $cur_index = (is_object($query_id)) ? $query_id->cur_index : $query_id;
+
+ if (empty($this->row[$cur_index]) && empty($this->rowset[$cur_index]))
+ {
+ if ($this->row[$cur_index] = $this->sql_fetchrow($query_id))
+ {
+ $result = $this->row[$cur_index][$field];
+ }
+ }
+ else
+ {
+ if ($this->rowset[$cur_index])
+ {
+ $result = $this->rowset[$cur_index][$field];
+ }
+ elseif ($this->row[$cur_index])
+ {
+ $result = $this->row[$cur_index][$field];
+ }
+ }
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ function sql_rowseek($rownum, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @mysqli_data_seek($query_id, $rownum) : false;
+ }
+
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ $cur_index = (is_object($query_id)) ? $query_id->cur_index : $query_id;
+
+ unset($this->rowset[$cur_index]);
+ unset($this->row[$cur_index]);
+
+ if (is_object($query_id))
+ {
+ $this->indexed--;
+ return @mysqli_free_result($query_id);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ function sql_escape($msg) {
+ return mysqli_real_escape_string($this->db_connect_id, $msg);
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mysqli_error($this->db_connect_id) . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @mysqli_error($this->db_connect_id),
+ 'code' => @mysqli_errno($this->db_connect_id)
+ );
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $db, $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $db->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>DB query explanation</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = FALSE;
+
+ if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
+ {
+ while ($row = @mysqli_fetch_assoc($result))
+ {
+ if (!$html_table && sizeof($row))
+ {
+ $html_table = TRUE;
+ $html_hold .= '<table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0" align="center"><tr>';
+
+ foreach (array_keys($row) as $val)
+ {
+ $html_hold .= '<th nowrap="nowrap">' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+ }
+ $html_hold .= '</tr>';
+ }
+ $html_hold .= '<tr>';
+
+ $class = 'row1';
+ foreach (array_values($row) as $val)
+ {
+ $class = ($class == 'row1') ? 'row2' : 'row1';
+ $html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+ }
+ $html_hold .= '</tr>';
+ }
+ }
+
+ if ($html_table)
+ {
+ $html_hold .= '</table>';
+ }
+ }
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @mysqli_query($this->db_connect_id, $query);
+ while ($void = @mysqli_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ @mysqli_free_result($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - MySQL Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/oracle.php b/src/SemanticScuttle/db/oracle.php
new file mode 100644
index 0000000..7ef10e5
--- /dev/null
+++ b/src/SemanticScuttle/db/oracle.php
@@ -0,0 +1,468 @@
+<?php
+/**
+*
+* @package dbal_oracle
+* @version $Id: oracle.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if(!defined("SQL_LAYER"))
+{
+
+define("SQL_LAYER","oracle");
+
+/**
+* @package dbal_oracle
+* Oracle Database Abstraction Layer
+*/
+class sql_db
+{
+
+ var $db_connect_id;
+ var $query_result;
+ var $in_transaction = 0;
+ var $row = array();
+ var $rowset = array();
+ var $num_queries = 0;
+ var $last_query_text = "";
+
+ //
+ // Constructor
+ //
+ function sql_db($sqlserver, $sqluser, $sqlpassword, $database="", $persistency = true)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->password = $sqlpassword;
+ $this->server = $sqlserver;
+ $this->dbname = $database;
+
+ if($this->persistency)
+ {
+ $this->db_connect_id = @OCIPLogon($this->user, $this->password, $this->server);
+ }
+ else
+ {
+ $this->db_connect_id = @OCINLogon($this->user, $this->password, $this->server);
+ }
+ if($this->db_connect_id)
+ {
+ return $this->db_connect_id;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if($this->db_connect_id)
+ {
+ // Commit outstanding transactions
+ if($this->in_transaction)
+ {
+ OCICommit($this->db_connect_id);
+ }
+
+ if($this->query_result)
+ {
+ @OCIFreeStatement($this->query_result);
+ }
+ $result = @OCILogoff($this->db_connect_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //
+ // Base query method
+ //
+ function sql_query($query = "", $transaction = FALSE)
+ {
+ // Remove any pre-existing queries
+ unset($this->query_result);
+
+ // Put us in transaction mode because with Oracle as soon as you make a query you're in a transaction
+ $this->in_transaction = TRUE;
+
+ if($query != "")
+ {
+ $this->last_query = $query;
+ $this->num_queries++;
+
+ if(eregi("LIMIT", $query))
+ {
+ preg_match("/^(.*)LIMIT ([0-9]+)[, ]*([0-9]+)*/s", $query, $limits);
+
+ $query = $limits[1];
+ if($limits[3])
+ {
+ $row_offset = $limits[2];
+ $num_rows = $limits[3];
+ }
+ else
+ {
+ $row_offset = 0;
+ $num_rows = $limits[2];
+ }
+ }
+
+ if(eregi("^(INSERT|UPDATE) ", $query))
+ {
+ $query = preg_replace("/\\\'/s", "''", $query);
+ }
+
+ $this->query_result = @OCIParse($this->db_connect_id, $query);
+ $success = @OCIExecute($this->query_result, OCI_DEFAULT);
+ }
+ if($success)
+ {
+ if($transaction == END_TRANSACTION)
+ {
+ OCICommit($this->db_connect_id);
+ $this->in_transaction = FALSE;
+ }
+
+ unset($this->row[$this->query_result]);
+ unset($this->rowset[$this->query_result]);
+ $this->last_query_text[$this->query_result] = $query;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if($this->in_transaction)
+ {
+ OCIRollback($this->db_connect_id);
+ }
+ return false;
+ }
+ }
+
+ //
+ // Other query methods
+ //
+ function sql_numrows($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @OCIFetchStatement($query_id, $this->rowset);
+ // OCIFetchStatment kills our query result so we have to execute the statment again
+ // if we ever want to use the query_id again.
+ @OCIExecute($query_id, OCI_DEFAULT);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_affectedrows($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @OCIRowCount($query_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_numfields($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @OCINumCols($query_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fieldname($offset, $query_id = 0)
+ {
+ // OCIColumnName uses a 1 based array so we have to up the offset by 1 in here to maintain
+ // full abstraction compatibitly
+ $offset += 1;
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = strtolower(@OCIColumnName($query_id, $offset));
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fieldtype($offset, $query_id = 0)
+ {
+ // This situation is the same as fieldname
+ $offset += 1;
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @OCIColumnType($query_id, $offset);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fetchrow($query_id = 0, $debug = FALSE)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result_row = "";
+ $result = @OCIFetchInto($query_id, $result_row, OCI_ASSOC+OCI_RETURN_NULLS);
+ if($debug)
+ {
+ echo "Query was: ".$this->last_query . "<br>";
+ echo "Result: $result<br>";
+ echo "Query ID: $query_id<br>";
+ echo "<pre>";
+ var_dump($result_row);
+ echo "</pre>";
+ }
+ if($result_row == "")
+ {
+ return false;
+ }
+
+ for($i = 0; $i < count($result_row); $i++)
+ {
+ list($key, $val) = each($result_row);
+ $return_arr[strtolower($key)] = $val;
+ }
+ $this->row[$query_id] = $return_arr;
+
+ return $this->row[$query_id];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ // This function probably isn't as efficant is it could be but any other way I do it
+ // I end up losing 1 row...
+ function sql_fetchrowset($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $rows = @OCIFetchStatement($query_id, $results);
+ @OCIExecute($query_id, OCI_DEFAULT);
+ for($i = 0; $i <= $rows; $i++)
+ {
+ @OCIFetchInto($query_id, $tmp_result, OCI_ASSOC+OCI_RETURN_NULLS);
+
+ for($j = 0; $j < count($tmp_result); $j++)
+ {
+ list($key, $val) = each($tmp_result);
+ $return_arr[strtolower($key)] = $val;
+ }
+ $result[] = $return_arr;
+ }
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_fetchfield($field, $rownum = -1, $query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ if($rownum > -1)
+ {
+ // Reset the internal rownum pointer.
+ @OCIExecute($query_id, OCI_DEFAULT);
+ for($i = 0; $i < $rownum; $i++)
+ {
+ // Move the interal pointer to the row we want
+ @OCIFetch($query_id);
+ }
+ // Get the field data.
+ $result = @OCIResult($query_id, strtoupper($field));
+ }
+ else
+ {
+ // The internal pointer should be where we want it
+ // so we just grab the field out of the current row.
+ $result = @OCIResult($query_id, strtoupper($field));
+ }
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_rowseek($rownum, $query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ @OCIExecute($query_id, OCI_DEFAULT);
+ for($i = 0; $i < $rownum; $i++)
+ {
+ @OCIFetch($query_id);
+ }
+ $result = @OCIFetch($query_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_nextid($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id && $this->last_query_text[$query_id] != "")
+ {
+ if( eregi("^(INSERT{1}|^INSERT INTO{1})[[:space:]][\"]?([a-zA-Z0-9\_\-]+)[\"]?", $this->last_query_text[$query_id], $tablename))
+ {
+ $query = "SELECT ".$tablename[2]."_id_seq.currval FROM DUAL";
+ $stmt = @OCIParse($this->db_connect_id, $query);
+ @OCIExecute($stmt,OCI_DEFAULT );
+ $temp_result = @OCIFetchInto($stmt, $temp_result, OCI_ASSOC+OCI_RETURN_NULLS);
+ if($temp_result)
+ {
+ return $temp_result['CURRVAL'];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ function sql_nextid($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id && $this->last_query_text[$query_id] != "")
+ {
+ if( eregi("^(INSERT{1}|^INSERT INTO{1})[[:space:]][\"]?([a-zA-Z0-9\_\-]+)[\"]?", $this->last_query_text[$query_id], $tablename))
+ {
+ $query = "SELECT ".$tablename[2]."_id_seq.CURRVAL FROM DUAL";
+ $temp_q_id = @OCIParse($this->db_connect_id, $query);
+ @OCIExecute($temp_q_id, OCI_DEFAULT);
+ @OCIFetchInto($temp_q_id, $temp_result, OCI_ASSOC+OCI_RETURN_NULLS);
+
+ if($temp_result)
+ {
+ return $temp_result['CURRVAL'];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+
+ function sql_freeresult($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ if($query_id)
+ {
+ $result = @OCIFreeStatement($query_id);
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ function sql_error($query_id = 0)
+ {
+ if(!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+ $result = @OCIError($query_id);
+ return $result;
+ }
+
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/postgres.php b/src/SemanticScuttle/db/postgres.php
new file mode 100644
index 0000000..b5bad20
--- /dev/null
+++ b/src/SemanticScuttle/db/postgres.php
@@ -0,0 +1,597 @@
+<?php
+/**
+*
+* @package dbal_postgres
+* @version $Id: postgres.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'postgresql');
+
+/**
+* @package dbal_postgres
+* PostgreSQL Database Abstraction Layer
+* Minimum Requirement is Version 7.3+
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+ {
+ $this->connect_string = '';
+
+ if ($sqluser)
+ {
+ $this->connect_string .= "user=$sqluser ";
+ }
+
+ if ($sqlpassword)
+ {
+ $this->connect_string .= "password=$sqlpassword ";
+ }
+
+ if ($sqlserver)
+ {
+ if (ereg(":", $sqlserver))
+ {
+ list($sqlserver, $sqlport) = split(":", $sqlserver);
+ $this->connect_string .= "host=$sqlserver port=$sqlport ";
+ }
+ else
+ {
+ if ($sqlserver != "localhost")
+ {
+ $this->connect_string .= "host=$sqlserver ";
+ }
+
+ if ($port)
+ {
+ $this->connect_string .= "port=$port ";
+ }
+ }
+ }
+
+ if ($database)
+ {
+ $this->dbname = $database;
+ $this->connect_string .= "dbname=$database";
+ }
+
+ $this->persistency = $persistency;
+
+ $this->db_connect_id = ($this->persistency) ? @pg_pconnect($this->connect_string) : @pg_connect($this->connect_string);
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ //
+ // Other base methods
+ //
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if ($this->transaction)
+ {
+ @pg_exec($this->db_connect_id, 'COMMIT');
+ }
+
+ return @pg_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $result = @pg_exec($this->db_connect_id, 'BEGIN');
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ $result = @pg_exec($this->db_connect_id, 'COMMIT');
+ $this->transaction = false;
+
+ if (!$result)
+ {
+ @pg_exec($this->db_connect_id, 'ROLLBACK');
+ }
+ break;
+
+ case 'rollback':
+ $result = @pg_exec($this->db_connect_id, 'ROLLBACK');
+ $this->transaction = false;
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+ if (!$this->query_result)
+ {
+ $this->num_queries++;
+ $this->last_query_text = $query;
+
+ if (($this->query_result = @pg_exec($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ $total = -1;
+ }
+
+ $query .= "\n LIMIT $total OFFSET $offset";
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @pg_numrows($query_id) : false;
+ }
+
+ function sql_affectedrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @pg_cmdtuples($query_id) : false;
+ }
+
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (!isset($this->rownum[$query_id]))
+ {
+ $this->rownum[$query_id] = 0;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ $result = @pg_fetch_array($query_id, NULL, PGSQL_ASSOC);
+
+ if ($result)
+ {
+ $this->rownum[$query_id]++;
+ }
+
+ return $result;
+ }
+
+ function sql_fetchrowset($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ $result = array();
+
+ if ($query_id)
+ {
+ unset($this->rowset[$query_id]);
+ unset($this->row[$query_id]);
+
+ $result = array();
+ while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+ {
+ $result[] = $this->rowset[$query_id];
+ }
+ return $result;
+ }
+
+ return false;
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($rownum > -1)
+ {
+ if (@function_exists('pg_result_seek'))
+ {
+ @pg_result_seek($query_id, $rownum);
+ $row = @pg_fetch_assoc($query_id);
+ $result = isset($row[$field]) ? $row[$field] : false;
+ }
+ else
+ {
+ $this->sql_rowseek($offset, $query_id);
+ $row = $this->sql_fetchrow($query_id);
+ $result = isset($row[$field]) ? $row[$field] : false;
+ }
+ }
+ else
+ {
+ if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+ {
+ if ($this->sql_fetchrow($query_id))
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ else
+ {
+ if ($this->rowset[$query_id])
+ {
+ $result = $this->rowset[$query_id][$field];
+ }
+ elseif ($this->row[$query_id])
+ {
+ $result = $this->row[$query_id][$field];
+ }
+ }
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ function sql_rowseek($offset, $query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ if ($offset > -1)
+ {
+ if (@function_exists('pg_result_seek'))
+ {
+ @pg_result_seek($query_id, $rownum);
+ }
+ else
+ {
+ for ($i = $this->rownum[$query_id]; $i < $offset; $i++)
+ {
+ $this->sql_fetchrow($query_id);
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ function sql_nextid()
+ {
+ $query_id = $this->query_result;
+
+ if ($query_id && $this->last_query_text != '')
+ {
+ if (preg_match("/^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)/is", $this->last_query_text, $tablename))
+ {
+ $query = "SELECT currval('" . $tablename[1] . "_id_seq') AS last_value";
+ $temp_q_id = @pg_exec($this->db_connect_id, $query);
+ if (!$temp_q_id)
+ {
+ return false;
+ }
+
+ $temp_result = @pg_fetch_array($temp_q_id, NULL, PGSQL_ASSOC);
+
+ return ($temp_result) ? $temp_result['last_value'] : false;
+ }
+ }
+
+ return false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return (is_resource($query_id)) ? @pg_freeresult($query_id) : false;
+ }
+
+ function sql_escape($msg)
+ {
+ return str_replace("'", "''", str_replace('\\', '\\\\', $msg));
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @pg_errormessage() . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @pg_errormessage(),
+ 'code' => ''
+ );
+
+ return $result;
+ }
+
+ function sql_report($mode, $query = '')
+ {
+ if (empty($_GET['explain']))
+ {
+ return;
+ }
+
+ global $cache, $starttime, $phpbb_root_path;
+ static $curtime, $query_hold, $html_hold;
+ static $sql_report = '';
+ static $cache_num_queries = 0;
+
+ if (!$query && !empty($query_hold))
+ {
+ $query = $query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $this->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+ echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+ echo 'td.cat { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+ echo '</style><title>' . $msg_title . '</title></head><body>';
+ echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+ echo $sql_report;
+ echo '</td></tr></table><br /></body></html>';
+ exit;
+ break;
+
+ case 'start':
+ $query_hold = $query;
+ $html_hold = '';
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1];
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @pg_exec($this->db_connect_id, $query);
+ while ($void = @pg_fetch_array($result, NULL, PGSQL_ASSOC))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $time_cache = $endtime - $curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ @pg_freeresult($result);
+ $cache_num_queries++;
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ }
+ $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $sql_report .= '</p>';
+
+ $this->sql_time += $endtime - $curtime;
+ break;
+ }
+ }
+
+} // class ... db_sql
+
+} // if ... defined
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/db/sqlite.php b/src/SemanticScuttle/db/sqlite.php
new file mode 100644
index 0000000..1591396
--- /dev/null
+++ b/src/SemanticScuttle/db/sqlite.php
@@ -0,0 +1,387 @@
+<?php
+/**
+*
+* @package dbal_sqlite
+* @version $Id: sqlite.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined("SQL_LAYER"))
+{
+
+define("SQL_LAYER","sqlite");
+
+/**
+* @package dbal_sqlite
+* Sqlite Database Abstraction Layer
+*/
+class sql_db
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_report = '';
+ var $sql_time = 0;
+ var $num_queries = 0;
+ var $open_queries = array();
+
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port, $persistency = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->db_connect_id = ($this->persistency) ? @sqlite_popen($this->server, 0, $error) : @sqlite_open($this->server, 0, $error);
+
+ return ($this->db_connect_id) ? true : $error;
+ }
+
+ // Other base methods
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ return @sqlite_close($this->db_connect_id);
+ }
+
+ function sql_return_on_error($fail = false)
+ {
+ $this->return_on_error = $fail;
+ }
+
+ function sql_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ $this->transaction = true;
+ $result = @sqlite_query('BEGIN', $this->db_connect_id);
+ break;
+
+ case 'commit':
+ $this->transaction = false;
+ $result = @sqlite_query('COMMIT', $this->db_connect_id);
+ break;
+
+ case 'rollback':
+ $this->transaction = false;
+ $result = @sqlite_query('ROLLBACK', $this->db_connect_id);
+ break;
+
+ default:
+ $result = true;
+ }
+
+ return $result;
+ }
+
+ // Base query method
+ function sql_query($query = '', $expire_time = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ $query = preg_replace('#FROM \((.*?)\)(,|[\n\t ]+?WHERE) #s', 'FROM \1\2 ', $query);
+
+ if (!$expire_time || !$cache->sql_load($query, $expire_time))
+ {
+ if ($expire_time)
+ {
+ $cache_result = true;
+ }
+
+ $this->query_result = false;
+ $this->num_queries++;
+
+ if (!empty($_GET['explain']))
+ {
+ global $starttime;
+
+ $curtime = explode(' ', microtime());
+ $curtime = $curtime[0] + $curtime[1] - $starttime;
+ }
+
+ if (!($this->query_result = @sqlite_query($query, $this->db_connect_id)))
+ {
+ $this->sql_error($query);
+ }
+
+ if (!empty($_GET['explain']))
+ {
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1] - $starttime;
+
+ $this->sql_report .= "<pre>Query:\t" . htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n\t", $query)) . "\n\n";
+
+ if ($this->query_result)
+ {
+ $this->sql_report .= "Time before: $curtime\nTime after: $endtime\nElapsed time: <b>" . ($endtime - $curtime) . "</b>\n</pre>";
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $this->sql_report .= '<b>FAILED</b> - SQLite ' . $error['code'] . ': ' . htmlspecialchars($error['message']) . '<br><br><pre>';
+ }
+
+ $this->sql_time += $endtime - $curtime;
+
+ if (preg_match('#^SELECT#', $query))
+ {
+ $html_table = FALSE;
+ if ($result = @sqlite_query("EXPLAIN $query", $this->db_connect_id))
+ {
+ while ($row = @sqlite_fetch_array($result, @sqlite_ASSOC))
+ {
+ if (!$html_table && sizeof($row))
+ {
+ $html_table = TRUE;
+ $this->sql_report .= "<table width=100% border=1 cellpadding=2 cellspacing=1>\n";
+ $this->sql_report .= "<tr>\n<td><b>" . implode("</b></td>\n<td><b>", array_keys($row)) . "</b></td>\n</tr>\n";
+ }
+ $this->sql_report .= "<tr>\n<td>" . implode("&nbsp;</td>\n<td>", array_values($row)) . "&nbsp;</td>\n</tr>\n";
+ }
+ }
+
+ if ($html_table)
+ {
+ $this->sql_report .= '</table><br>';
+ }
+ }
+
+ $this->sql_report .= "<hr>\n";
+ }
+
+ if (preg_match('#^SELECT#', $query))
+ {
+ $this->open_queries[] = $this->query_result;
+ }
+ }
+
+ if (!empty($cache_result))
+ {
+ $cache->sql_save($query, $this->query_result);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return ($this->query_result) ? $this->query_result : false;
+ }
+
+ function sql_query_limit($query, $total, $offset = 0, $expire_time = 0)
+ {
+ if ($query != '')
+ {
+ $this->query_result = false;
+
+ $query .= ' LIMIT ' . ((!empty($offset)) ? $total . ' OFFSET ' . $offset : $total);
+
+ return $this->sql_query($query, $expire_time);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Idea for this from Ikonboard
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = array();
+ $values = array();
+ if ($query == 'INSERT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_null($var))
+ {
+ $values[] = 'NULL';
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+ }
+ else if ($query == 'UPDATE')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ if (is_null($var))
+ {
+ $values[] = "$key = NULL";
+ }
+ elseif (is_string($var))
+ {
+ $values[] = "$key = '" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+ }
+ }
+ $query = implode(', ', $values);
+ }
+
+ return $query;
+ }
+
+ // Other query methods
+ //
+ // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+ // don't want this here by a middle Milestone
+ function sql_numrows($query_id = false)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @sqlite_num_rows($query_id) : false;
+ }
+
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @sqlite_changes($this->db_connect_id) : false;
+ }
+
+ function sql_fetchrow($query_id = 0)
+ {
+ global $cache;
+
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id) ? @sqlite_fetch_array($query_id, @sqlite_ASSOC) : false;
+ }
+
+ function sql_fetchrowset($query_id = 0)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ unset($this->rowset[$query_id]);
+ unset($this->row[$query_id]);
+ while ($this->rowset[$query_id] = @sqlite_fetch_array($query_id, @sqlite_ASSOC))
+ {
+ $result[] = $this->rowset[$query_id];
+ }
+ return $result;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ function sql_fetchfield($field, $rownum = -1, $query_id = 0)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id)
+ {
+ return ($rownum > -1) ? ((@sqlite_seek($query_id, $rownum)) ? @sqlite_column($query_id, $field) : false) : @sqlite_column($query_id, $field);
+ }
+ }
+
+ function sql_rowseek($rownum, $query_id = 0)
+ {
+ if (!$query_id)
+ {
+ $query_id = $this->query_result;
+ }
+
+ return ($query_id) ? @sqlite_seek($query_id, $rownum) : false;
+ }
+
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @sqlite_last_insert_rowid($this->db_connect_id) : false;
+ }
+
+ function sql_freeresult($query_id = false)
+ {
+ return true;
+ }
+
+ function sql_escape($msg)
+ {
+ return @sqlite_escape_string(stripslashes($msg));
+ }
+
+ function sql_error($sql = '')
+ {
+ if (!$this->return_on_error)
+ {
+ $this_page = (!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+ $this_page .= '&' . ((!empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : $_ENV['QUERY_STRING']);
+
+ $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @sqlite_error_string(@sqlite_last_error($this->db_connect_id)) . '<br /><br /><u>CALLING PAGE</u><br /><br />' . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ $result = array(
+ 'message' => @sqlite_error_string(@sqlite_last_error($this->db_connect_id)),
+ 'code' => @sqlite_last_error($this->db_connect_id)
+ );
+
+ return $result;
+ }
+
+} // class sql_db
+
+} // if ... define
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/functions.php b/src/SemanticScuttle/functions.php
new file mode 100644
index 0000000..802a7f1
--- /dev/null
+++ b/src/SemanticScuttle/functions.php
@@ -0,0 +1,292 @@
+<?php
+/**
+ * Defines some commonly used functions.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// Converts tags:
+// - direction = out: convert spaces to underscores;
+// - direction = in: convert underscores to spaces.
+function convertTag($tag, $direction = 'out') {
+ if ($direction == 'out') {
+ $tag = str_replace(' ', '_', $tag);
+ } else {
+ $tag = str_replace('_', ' ', $tag);
+ }
+ return $tag;
+}
+
+function filter($data, $type = NULL) {
+ if (is_string($data)) {
+ $data = trim($data);
+ $data = stripslashes($data);
+ switch ($type) {
+ case 'url':
+ $data = rawurlencode($data);
+ break;
+ default:
+ $data = htmlspecialchars($data);
+ break;
+ }
+ } else if (is_array($data)) {
+ foreach(array_keys($data) as $key) {
+ $row =& $data[$key];
+ $row = filter($row, $type);
+ }
+ }
+ return $data;
+}
+
+/**
+ * Returns the number of bookmarks that shall be displayed on one page.
+ *
+ * @param SemanticScuttle_Model_User $userObject Object of the current user
+ *
+ * @return integer Number of bookmarks per page
+ *
+ * @uses $defaultPerPage
+ * @uses $defaultPerPageForAdmins
+ */
+function getPerPageCount($userObject = null)
+{
+ global $defaultPerPage, $defaultPerPageForAdmins;
+
+ if (isset($defaultPerPageForAdmins)
+ && $userObject != null && $userObject->isAdmin()
+ ) {
+ return $defaultPerPageForAdmins;
+ } else {
+ return $defaultPerPage;
+ }
+}
+
+function getSortOrder($override = NULL) {
+ global $defaultOrderBy;
+
+ if (isset($_GET['sort'])) {
+ return preg_replace('/[^a-z_]/', '', $_GET['sort']);
+ } else if (isset($override)) {
+ return $override;
+ } else {
+ return $defaultOrderBy;
+ }
+}
+
+function multi_array_search($needle, $haystack) {
+ if (is_array($haystack)) {
+ foreach(array_keys($haystack) as $key) {
+ $value =& $haystack[$key];
+ $result = multi_array_search($needle, $value);
+ if (is_array($result)) {
+ $return = $result;
+ array_unshift($return, $key);
+ return $return;
+ } elseif ($result == true) {
+ $return[] = $key;
+ return $return;
+ }
+ }
+ return false;
+ } else {
+ if ($needle === $haystack) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+function createURL($page = '', $ending = '') {
+ global $cleanurls;
+ if (!$cleanurls && $page != '') {
+ $page .= '.php';
+ }
+ if(strlen($ending)>0) {
+ return ROOT . $page .'/'. $ending;
+ } else {
+ return ROOT . $page;
+ }
+}
+
+/**
+ * Adds the protocol to the URL if it's missing.
+ * If the current URL is served via HTTPS, https will be used as protocol.
+ *
+ * Useful to fix ROOT urls of SemanticScuttle when it's needed, e.g.
+ * in the bookmarklet or the feed.
+ *
+ * @param string $url Url with or without the protocol ("//example.org/foo")
+ *
+ * @return string URL with a HTTP protocol
+ */
+function addProtocolToUrl($url)
+{
+ if (substr($url, 0, 2) != '//') {
+ return $url;
+ }
+
+ if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) {
+ $protocol = 'https:';
+ } else {
+ $protocol = 'http:';
+ }
+ return $protocol . $url;
+}
+/**
+ * Creates a "vote for/against this bookmark" URL.
+ * Also runs htmlspecialchars() on them to prevent XSS.
+ *
+ * @param boolean $for For the bookmark (true) or against (false)
+ * @param integer $bId Bookmark ID
+ *
+ * @return string URL to use
+ */
+function createVoteURL($for, $bId)
+{
+ return htmlspecialchars(
+ createURL(
+ 'vote',
+ ($for ? 'for' : 'against') . '/' . $bId
+ ),
+ ENT_QUOTES
+ );
+}
+
+/* Shorten a string like a URL for example by cutting the middle of it */
+function shortenString($string, $maxSize=75) {
+ $output = '';
+ if(strlen($string) > $maxSize) {
+ $output = substr($string, 0, $maxSize/2).'...'.substr($string, -$maxSize/2);
+ } else {
+ $output = $string;
+ }
+ return $output;
+}
+
+/* Check url format and check online if the url is a valid page (Not a 404 error for example) */
+function checkUrl($url, $checkOnline = true) {
+ //check format
+ if(!preg_match("#(ht|f)tp(s?)\://\S+\.\S+#i",$url)) {
+ return false;
+ }
+
+ if($checkOnline) {
+ //look if the page doesn't return a void or 40X or 50X HTTP code error
+ $h = @get_headers($url);
+ if(is_array($h) && strpos($h[0], '40') === false && strpos($h[0], '50') === false) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+}
+
+/* Returns a concatenated String
+ * including all the tags from the array $arrayTags (excepted of the $exceptedTag)
+ * separated by the $separator.
+ * */
+function aggregateTags($arrayTags, $separator = ' + ', $exceptedTag = '') {
+ $output = '';
+
+ for($i = 0; $i<count($arrayTags); $i++) {
+ if($arrayTags[$i] != $exceptedTag) {
+ $output.= $arrayTags[$i] . $separator;
+ }
+ }
+ return substr($output, 0, strlen($output) - strlen($separator) );
+}
+
+function message_die($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '', $db = NULL) {
+ if(defined('HAS_DIED'))
+ die(T_('message_die() was called multiple times.'));
+ define('HAS_DIED', 1);
+
+ $sql_store = $sql;
+
+ // Get SQL error if we are debugging. Do this as soon as possible to prevent
+ // subsequent queries from overwriting the status of sql_error()
+ if (DEBUG_MODE && ($msg_code == GENERAL_ERROR || $msg_code == CRITICAL_ERROR)) {
+ $sql_error = is_null($db) ? '' : $db->sql_error();
+ $debug_text = '';
+
+ if ($sql_error['message'] != '')
+ $debug_text .= '<br /><br />'. T_('SQL Error') .' : '. $sql_error['code'] .' '. $sql_error['message'];
+
+ if ($sql_store != '')
+ $debug_text .= '<br /><br />'. $sql_store;
+
+ if ($err_line != '' && $err_file != '')
+ $debug_text .= '</br /><br />'. T_('Line') .' : '. $err_line .'<br />'. T_('File') .' :'. $err_file;
+
+ debug_print_backtrace();
+ }
+
+ switch($msg_code) {
+ case GENERAL_MESSAGE:
+ if ($msg_title == '')
+ $msg_title = T_('Information');
+ break;
+
+ case CRITICAL_MESSAGE:
+ if ($msg_title == '')
+ $msg_title = T_('Critical Information');
+ break;
+
+ case GENERAL_ERROR:
+ if ($msg_text == '')
+ $msg_text = T_('An error occured');
+
+ if ($msg_title == '')
+ $msg_title = T_('General Error');
+ break;
+
+ case CRITICAL_ERROR:
+ // Critical errors mean we cannot rely on _ANY_ DB information being
+ // available so we're going to dump out a simple echo'd statement
+
+ if ($msg_text == '')
+ $msg_text = T_('An critical error occured');
+
+ if ($msg_title == '')
+ $msg_title = T_('Critical Error');
+ break;
+ }
+
+ // Add on DEBUG_MODE info if we've enabled debug mode and this is an error. This
+ // prevents debug info being output for general messages should DEBUG_MODE be
+ // set TRUE by accident (preventing confusion for the end user!)
+ if (DEBUG_MODE && ($msg_code == GENERAL_ERROR || $msg_code == CRITICAL_ERROR)) {
+ if ($debug_text != '')
+ $msg_text = $msg_text . '<br /><br /><strong>'. T_('DEBUG MODE') .'</strong>'. $debug_text;
+ }
+
+ echo "<html>\n<body>\n". $msg_title ."\n<br /><br />\n". $msg_text ."</body>\n</html>";
+ exit;
+}
+
+/**
+ * Calls reset() on the given arg, without the E_STRICT error
+ * "Only variables should be passed by reference"
+ *
+ * @param array $arg Array to return first element of
+ *
+ * @return mixed First element of the array
+ */
+function rreset($array)
+{
+ return reset($array);
+}
+?>
diff --git a/src/SemanticScuttle/header-standalone.php b/src/SemanticScuttle/header-standalone.php
new file mode 100644
index 0000000..3755ea1
--- /dev/null
+++ b/src/SemanticScuttle/header-standalone.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Load this file instead of header.php if you
+ * are using it in a standalone non-webserver script.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+$_SERVER['HTTP_HOST'] = 'http://localhost/';
+
+require_once dirname(__FILE__) . '/header.php';
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php
new file mode 100644
index 0000000..1f2f12c
--- /dev/null
+++ b/src/SemanticScuttle/header.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * Base file that is used by shell scripts and www/www-header.php.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+if ('@data_dir@' == '@' . 'data_dir@') {
+ //non pear-install
+ $datadir = dirname(__FILE__) . '/../../data/';
+ $wwwdir = dirname(__FILE__) . '/../../www/';
+} else {
+ //pear installation; files are in include path
+ $datadir = '@data_dir@/SemanticScuttle/';
+ //FIXME: when you have multiple installations, the www_dir will be wrong
+ $wwwdir = '@www_dir@/SemanticScuttle/';
+}
+require_once dirname(__FILE__) . '/Environment.php';
+require_once dirname(__FILE__) . '/Config.php';
+
+$cfg = new SemanticScuttle_Config();
+list($configfile, $defaultfile) = $cfg->findFiles();
+if ($defaultfile === null) {
+ header('HTTP/1.0 500 Internal Server Error');
+ echo 'No default configuration file config.default.php found.'
+ . ' This is really, really strange'
+ . "\n";
+ exit(1);
+}
+if ($configfile === null) {
+ header('HTTP/1.0 500 Internal Server Error');
+ echo 'Please copy "config.php.dist" to "config.php" in data/ folder.' . "\n";
+ exit(1);
+}
+set_include_path(
+ get_include_path() . PATH_SEPARATOR
+ . dirname(__FILE__) . '/../'
+);
+
+// 1 // First requirements part (before debug management)
+require_once $defaultfile;
+require_once $configfile;
+
+if (isset($_GET['unittestMode']) && $_GET['unittestMode'] == 1
+) {
+ if ($allowUnittestMode !== true) {
+ header('HTTP/1.0 400 Bad Request');
+ echo "Unittestmode is not allowed\n";
+ exit(2);
+ }
+
+ define('HTTP_UNIT_TEST_MODE', true);
+ define('UNIT_TEST_MODE', true);
+}
+if (defined('UNIT_TEST_MODE')) {
+ //load configuration for unit tests
+ $testingConfigFile = $datadir . '/config.testing.php';
+ if (file_exists($testingConfigFile)) {
+ require_once $testingConfigFile;
+ }
+ //test-specific configuration file
+ $unittestConfigFile = $datadir . '/config.testing-tmp.php';
+ if (file_exists($unittestConfigFile)) {
+ require_once $unittestConfigFile;
+ }
+
+ //make local config vars global - needed for unit tests
+ //run with phpunit
+ foreach (get_defined_vars() as $var => $value) {
+ $GLOBALS[$var] = $value;
+ }
+}
+
+// some constants are based on variables from config file
+require_once 'SemanticScuttle/constants.php';
+
+
+// Debug Management using constants
+if (DEBUG_MODE) {
+ ini_set('display_errors', '1');
+ ini_set('mysql.trace_mode', '1');
+ error_reporting(E_ALL);
+} else {
+ ini_set('display_errors', '0');
+ ini_set('mysql.trace_mode', '0');
+ error_reporting(0);
+}
+
+// 2 // Second requirements part which could display bugs
+// (must come after debug management)
+require_once 'SemanticScuttle/Service.php';
+require_once 'SemanticScuttle/DbService.php';
+require_once 'SemanticScuttle/Service/Factory.php';
+require_once 'SemanticScuttle/functions.php';
+require_once 'SemanticScuttle/Model/Bookmark.php';
+require_once 'SemanticScuttle/Model/UserArray.php';
+require_once 'SemanticScuttle/Model/User/SslClientCert.php';
+
+if (count($GLOBALS['serviceoverrides']) > 0
+ && !defined('UNIT_TEST_MODE')
+) {
+ SemanticScuttle_Service_Factory::$serviceoverrides
+ = $GLOBALS['serviceoverrides'];
+}
+
+// 3 // Third requirements part which import functions from includes/ directory
+
+// UTF-8 functions
+require_once 'SemanticScuttle/utf8.php';
+
+// Translation
+require_once 'php-gettext/gettext.inc';
+$domain = 'messages';
+T_setlocale(LC_MESSAGES, $locale);
+T_bindtextdomain($domain, realpath($datadir . 'locales/'));
+T_bind_textdomain_codeset($domain, 'UTF-8');
+T_textdomain($domain);
+
+// 4 // Session
+if (isset($_SERVER['REMOTE_ADDR'])) {
+ session_start();
+ if ($GLOBALS['enableVoting']) {
+ if (isset($_SESSION['lastUrl'])) {
+ $GLOBALS['lastUrl'] = $_SESSION['lastUrl'];
+ }
+ //this here is hacky, but currently the only way to
+ // differentiate between css/js php files and normal
+ // http files
+ if (!isset($GLOBALS['saveInLastUrl'])
+ || $GLOBALS['saveInLastUrl']
+ ) {
+ $_SESSION['lastUrl'] = $_SERVER['REQUEST_URI'];
+ }
+ }
+}
+
+// 5 // Create mandatory services and objects
+$userservice = SemanticScuttle_Service_Factory::get('User');
+$currentUser = $userservice->getCurrentObjectUser();
+
+$templateservice = SemanticScuttle_Service_Factory::get('Template');
+$tplVars = array();
+$tplVars['currentUser'] = $currentUser;
+$tplVars['userservice'] = $userservice;
+
+// 6 // Force UTF-8 behaviour for server (cannot be moved into top.inc.php which is not included into every file)
+if (!defined('UNIT_TEST_MODE') || defined('HTTP_UNIT_TEST_MODE')) {
+ //API files define that, so we need a way to support both of them
+ if (!isset($httpContentType)) {
+ if (DEBUG_MODE) {
+ //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';
+ } else {
+ //until we are sure that all pages validate, we
+ // keep the non-strict mode on for normal installations
+ $httpContentType = 'text/html';
+ }
+ }
+ if ($httpContentType !== false) {
+ header('Content-Type: ' . $httpContentType . '; charset=utf-8');
+ }
+}
+?>
diff --git a/src/SemanticScuttle/utf8.php b/src/SemanticScuttle/utf8.php
new file mode 100644
index 0000000..9ef8113
--- /dev/null
+++ b/src/SemanticScuttle/utf8.php
@@ -0,0 +1,478 @@
+<?php
+/**
+ * UTF8 helper functions
+ *
+ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+/**
+ * URL-Encode a filename to allow unicodecharacters
+ *
+ * Slashes are not encoded
+ *
+ * When the second parameter is true the string will
+ * be encoded only if non ASCII characters are detected -
+ * This makes it safe to run it multiple times on the
+ * same string (default is true)
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see urlencode
+ */
+function utf8_encodeFN($file,$safe=true){
+ if($safe && preg_match('#^[a-zA-Z0-9/_\-.%]+$#',$file)){
+ return $file;
+ }
+ $file = urlencode($file);
+ $file = str_replace('%2F','/',$file);
+ return $file;
+}
+
+/**
+ * URL-Decode a filename
+ *
+ * This is just a wrapper around urldecode
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see urldecode
+ */
+function utf8_decodeFN($file){
+ $file = urldecode($file);
+ return $file;
+}
+
+/**
+ * Checks if a string contains 7bit ASCII only
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function utf8_isASCII($str){
+ for($i=0; $i<strlen($str); $i++){
+ if(ord($str{$i}) >127) return false;
+ }
+ return true;
+}
+
+/**
+ * Tries to detect if a string is in Unicode encoding
+ *
+ * @author <bmorel@ssi.fr>
+ * @link http://www.php.net/manual/en/function.utf8-encode.php
+ */
+function utf8_check($Str) {
+ for ($i=0; $i<strlen($Str); $i++) {
+ if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb
+ elseif ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
+ elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
+ elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
+ elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
+ elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
+ else return false; # Does not match any model
+ for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
+ if ((++$i == strlen($Str)) || ((ord($Str[$i]) & 0xC0) != 0x80))
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * This is a unicode aware replacement for strlen()
+ *
+ * Uses mb_string extension if available
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see strlen()
+ */
+function utf8_strlen($string){
+ if(!defined('UTF8_NOMBSTRING') && function_exists('mb_strlen'))
+ return mb_strlen($string,'utf-8');
+
+ $uni = utf8_to_unicode($string);
+ return count($uni);
+}
+
+/**
+ * This is a unicode aware replacement for substr()
+ *
+ * Uses mb_string extension if available
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see substr()
+ */
+function utf8_substr($str, $start, $length=null){
+ if(!defined('UTF8_NOMBSTRING') && function_exists('mb_substr'))
+ return mb_substr($str,$start,$length,'utf-8');
+
+ $uni = utf8_to_unicode($str);
+ return unicode_to_utf8(array_slice($uni,$start,$length));
+}
+
+/**
+ * This is a unicode aware replacement for strtolower()
+ *
+ * Uses mb_string extension if available
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see strtolower()
+ * @see utf8_strtoupper()
+ */
+function utf8_strtolower($string){
+ if(!defined('UTF8_NOMBSTRING') && function_exists('mb_strtolower'))
+ return mb_strtolower($string,'utf-8');
+
+ global $UTF8_UPPER_TO_LOWER;
+ $uni = utf8_to_unicode($string);
+ for ($i=0; $i < count($uni); $i++){
+ if($UTF8_UPPER_TO_LOWER[$uni[$i]]){
+ $uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]];
+ }
+ }
+ return unicode_to_utf8($uni);
+}
+
+/**
+ * This is a unicode aware replacement for strtoupper()
+ *
+ * Uses mb_string extension if available
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see strtoupper()
+ * @see utf8_strtoupper()
+ */
+function utf8_strtoupper($string){
+ if(!defined('UTF8_NOMBSTRING') && function_exists('mb_strtolower'))
+ return mb_strtolower($string,'utf-8');
+
+ global $UTF8_LOWER_TO_UPPER;
+ $uni = utf8_to_unicode($string);
+ for ($i=0; $i < count($uni); $i++){
+ if($UTF8_LOWER_TO_UPPER[$uni[$i]]){
+ $uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]];
+ }
+ }
+ return unicode_to_utf8($uni);
+}
+
+/**
+ * Replace accented UTF-8 characters by unaccented ASCII-7 equivalents
+ *
+ * Use the optional parameter to just deaccent lower ($case = -1) or upper ($case = 1)
+ * letters. Default is to deaccent both cases ($case = 0)
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function utf8_deaccent($string,$case=0){
+ if($case <= 0){
+ global $UTF8_LOWER_ACCENTS;
+ $string = str_replace(array_keys($UTF8_LOWER_ACCENTS),array_values($UTF8_LOWER_ACCENTS),$string);
+ }
+ if($case >= 0){
+ global $UTF8_UPPER_ACCENTS;
+ $string = str_replace(array_keys($UTF8_UPPER_ACCENTS),array_values($UTF8_UPPER_ACCENTS),$string);
+ }
+ return $string;
+}
+
+/**
+ * Removes special characters (nonalphanumeric) from a UTF-8 string
+ *
+ * Be sure to specify all specialchars you give in $repl in $keep, too
+ * or it won't work.
+ *
+ * This function adds the controlchars 0x00 to 0x19 to the array of
+ * stripped chars (they are not included in $UTF8_SPECIAL_CHARS)
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @param string $string The UTF8 string to strip of special chars
+ * @param string $repl Replace special with this string
+ * @param string $keep Special chars to keep (in UTF8)
+ */
+function utf8_stripspecials($string,$repl='',$keep=''){
+ global $UTF8_SPECIAL_CHARS;
+ if($keep != ''){
+ $specials = array_diff($UTF8_SPECIAL_CHARS, utf8_to_unicode($keep));
+ }else{
+ $specials = $UTF8_SPECIAL_CHARS;
+ }
+
+ $specials = unicode_to_utf8($specials);
+ $specials = preg_quote($specials, '/');
+
+ return preg_replace('/[\x00-\x19'.$specials.']/u',$repl,$string);
+}
+
+/**
+ * This is an Unicode aware replacement for strpos
+ *
+ * Uses mb_string extension if available
+ *
+ * @author Scott Michael Reynen <scott@randomchaos.com>
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @link http://www.randomchaos.com/document.php?source=php_and_unicode
+ * @see strpos()
+ */
+function utf8_strpos($haystack, $needle,$offset=0) {
+ if(!defined('UTF8_NOMBSTRING') && function_exists('mb_strpos'))
+ return mb_strpos($haystack,$needle,$offset,'utf-8');
+
+ $haystack = utf8_to_unicode($haystack);
+ $needle = utf8_to_unicode($needle);
+ $position = $offset;
+ $found = false;
+
+ while( (! $found ) && ( $position < count( $haystack ) ) ) {
+ if ( $needle[0] == $haystack[$position] ) {
+ for ($i = 1; $i < count( $needle ); $i++ ) {
+ if ( $needle[$i] != $haystack[ $position + $i ] ) break;
+ }
+ if ( $i == count( $needle ) ) {
+ $found = true;
+ $position--;
+ }
+ }
+ $position++;
+ }
+ return ( $found == true ) ? $position : false;
+}
+
+/**
+ * This function will any UTF-8 encoded text and return it as
+ * a list of Unicode values:
+ *
+ * @author Scott Michael Reynen <scott@randomchaos.com>
+ * @link http://www.randomchaos.com/document.php?source=php_and_unicode
+ * @see unicode_to_utf8()
+ */
+function utf8_to_unicode( $str ) {
+ $unicode = array();
+ $values = array();
+ $lookingFor = 1;
+
+ for ($i = 0; $i < strlen( $str ); $i++ ) {
+ $thisValue = ord( $str[ $i ] );
+ if ( $thisValue < 128 ) $unicode[] = $thisValue;
+ else {
+ if ( count( $values ) == 0 ) $lookingFor = ( $thisValue < 224 ) ? 2 : 3;
+ $values[] = $thisValue;
+ if ( count( $values ) == $lookingFor ) {
+ $number = ( $lookingFor == 3 ) ?
+ ( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ):
+ ( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 );
+ $unicode[] = $number;
+ $values = array();
+ $lookingFor = 1;
+ }
+ }
+ }
+ return $unicode;
+}
+
+/**
+ * This function will convert a Unicode array back to its UTF-8 representation
+ *
+ * @author Scott Michael Reynen <scott@randomchaos.com>
+ * @link http://www.randomchaos.com/document.php?source=php_and_unicode
+ * @see utf8_to_unicode()
+ */
+function unicode_to_utf8( $str ) {
+ $utf8 = '';
+ foreach( $str as $unicode ) {
+ if ( $unicode < 128 ) {
+ $utf8.= chr( $unicode );
+ } elseif ( $unicode < 2048 ) {
+ $utf8.= chr( 192 + ( ( $unicode - ( $unicode % 64 ) ) / 64 ) );
+ $utf8.= chr( 128 + ( $unicode % 64 ) );
+ } else {
+ $utf8.= chr( 224 + ( ( $unicode - ( $unicode % 4096 ) ) / 4096 ) );
+ $utf8.= chr( 128 + ( ( ( $unicode % 4096 ) - ( $unicode % 64 ) ) / 64 ) );
+ $utf8.= chr( 128 + ( $unicode % 64 ) );
+ }
+ }
+ return $utf8;
+}
+
+/**
+ * UTF-8 Case lookup table
+ *
+ * This lookuptable defines the upper case letters to their correspponding
+ * lower case letter in UTF-8
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+$UTF8_LOWER_TO_UPPER = array(
+ 0x0061=>0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042,
+ 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100,
+ 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393,
+ 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C,
+ 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F,
+ 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E,
+ 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3,
+ 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A,
+ 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9,
+ 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C,
+ 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4,
+ 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164,
+ 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156,
+ 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118,
+ 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128,
+ 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428,
+ 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055,
+ 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A,
+ 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC,
+ 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0,
+ 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D,
+ 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0,
+ 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5,
+ 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA,
+ 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045,
+ 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F,
+ 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048,
+ 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6,
+ 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407,
+ 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395,
+ 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396,
+ 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051,
+ 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408,
+ 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F,
+ 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126,
+ 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C,
+ 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E,
+ 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB,
+ 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421,
+ 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A,
+ 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102,
+ 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9,
+ 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122,
+);
+
+/**
+ * UTF-8 Case lookup table
+ *
+ * This lookuptable defines the lower case letters to their correspponding
+ * upper case letter in UTF-8 (it does so by flipping $UTF8_LOWER_TO_UPPER)
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+$UTF8_UPPER_TO_LOWER = @array_flip($UTF8_LOWER_TO_UPPER);
+
+/**
+ * UTF-8 lookup table for lower case accented letters
+ *
+ * This lookuptable defines replacements for accented characters from the ASCII-7
+ * range. This are lower case letters only.
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see utf8_deaccent()
+ */
+$UTF8_LOWER_ACCENTS = array(
+ 'à ' => 'a', 'ô' => 'o', 'Ä' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'Å¡' => 's', 'Æ¡' => 'o',
+ 'ß' => 'ss', 'ă' => 'a', 'Å™' => 'r', 'È›' => 't', 'ň' => 'n', 'Ä' => 'a', 'Ä·' => 'k',
+ 'Å' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'á¹—' => 'p', 'ó' => 'o',
+ 'ú' => 'u', 'Ä›' => 'e', 'é' => 'e', 'ç' => 'c', 'áº' => 'w', 'Ä‹' => 'c', 'õ' => 'o',
+ 'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c',
+ 'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't',
+ 'Å«' => 'u', 'Ä' => 'c', 'ö' => 'oe', 'è' => 'e', 'Å·' => 'y', 'Ä…' => 'a', 'Å‚' => 'l',
+ 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z',
+ 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't',
+ 'ŗ' => 'r', 'ä' => 'ae', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'ue', 'ò' => 'o',
+ 'Ä“' => 'e', 'ñ' => 'n', 'Å„' => 'n', 'Ä¥' => 'h', 'Ä' => 'g', 'Ä‘' => 'd', 'ĵ' => 'j',
+ 'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o',
+ 'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g',
+ 'á¹' => 'm', 'Å' => 'o', 'Ä©' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a',
+ 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u',
+);
+
+/**
+ * UTF-8 lookup table for upper case accented letters
+ *
+ * This lookuptable defines replacements for accented characters from the ASCII-7
+ * range. This are upper case letters only.
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see utf8_deaccent()
+ */
+$UTF8_UPPER_ACCENTS = array(
+ 'à ' => 'A', 'ô' => 'O', 'Ä' => 'D', 'ḟ' => 'F', 'ë' => 'E', 'Å¡' => 'S', 'Æ¡' => 'O',
+ 'ß' => 'Ss', 'ă' => 'A', 'Å™' => 'R', 'È›' => 'T', 'ň' => 'N', 'Ä' => 'A', 'Ä·' => 'K',
+ 'Å' => 'S', 'ỳ' => 'Y', 'ņ' => 'N', 'ĺ' => 'L', 'ħ' => 'H', 'á¹—' => 'P', 'ó' => 'O',
+ 'ú' => 'U', 'Ä›' => 'E', 'é' => 'E', 'ç' => 'C', 'áº' => 'W', 'Ä‹' => 'C', 'õ' => 'O',
+ 'ṡ' => 'S', 'ø' => 'O', 'ģ' => 'G', 'ŧ' => 'T', 'ș' => 'S', 'ė' => 'E', 'ĉ' => 'C',
+ 'ś' => 'S', 'î' => 'I', 'ű' => 'U', 'ć' => 'C', 'ę' => 'E', 'ŵ' => 'W', 'ṫ' => 'T',
+ 'Å«' => 'U', 'Ä' => 'C', 'ö' => 'Oe', 'è' => 'E', 'Å·' => 'Y', 'Ä…' => 'A', 'Å‚' => 'L',
+ 'ų' => 'U', 'ů' => 'U', 'ş' => 'S', 'ğ' => 'G', 'ļ' => 'L', 'ƒ' => 'F', 'ž' => 'Z',
+ 'ẃ' => 'W', 'ḃ' => 'B', 'å' => 'A', 'ì' => 'I', 'ï' => 'I', 'ḋ' => 'D', 'ť' => 'T',
+ 'ŗ' => 'R', 'ä' => 'Ae', 'í' => 'I', 'ŕ' => 'R', 'ê' => 'E', 'ü' => 'Ue', 'ò' => 'O',
+ 'Ä“' => 'E', 'ñ' => 'N', 'Å„' => 'N', 'Ä¥' => 'H', 'Ä' => 'G', 'Ä‘' => 'D', 'ĵ' => 'J',
+ 'ÿ' => 'Y', 'ũ' => 'U', 'ŭ' => 'U', 'ư' => 'U', 'ţ' => 'T', 'ý' => 'Y', 'ő' => 'O',
+ 'â' => 'A', 'ľ' => 'L', 'ẅ' => 'W', 'ż' => 'Z', 'ī' => 'I', 'ã' => 'A', 'ġ' => 'G',
+ 'á¹' => 'M', 'Å' => 'O', 'Ä©' => 'I', 'ù' => 'U', 'į' => 'I', 'ź' => 'Z', 'á' => 'A',
+ 'û' => 'U', 'Þ' => 'Th', 'Ã' => 'Dh', 'Æ' => 'Ae',
+);
+
+/**
+ * UTF-8 array of common special characters
+ *
+ * This array should contain all special characters (not a letter or digit)
+ * defined in the various local charsets - it's not a complete list of non-alphanum
+ * characters in UTF-8. It's not perfect but should match most cases of special
+ * chars.
+ *
+ * The controlchars 0x00 to 0x19 are _not_ included in this array. The space 0x20 is!
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see utf8_stripspecials()
+ */
+$UTF8_SPECIAL_CHARS = array(
+ 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d,
+ 0x002e, 0x002f, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0142, 0x007b, 0x007c, 0x007d, 0x007e,
+ 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088,
+ 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092,
+ 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c,
+ 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6,
+ 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0,
+ 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba,
+ 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00d7, 0x00f7, 0x02c7, 0x02d8, 0x02d9,
+ 0x02da, 0x02db, 0x02dc, 0x02dd, 0x0300, 0x0301, 0x0303, 0x0309, 0x0323, 0x0384,
+ 0x0385, 0x0387, 0x03b2, 0x03c6, 0x03d1, 0x03d2, 0x03d5, 0x03d6, 0x05b0, 0x05b1,
+ 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05bb, 0x05bc,
+ 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f3, 0x05f4, 0x060c,
+ 0x061b, 0x061f, 0x0640, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651,
+ 0x0652, 0x066a, 0x0e3f, 0x200c, 0x200d, 0x200e, 0x200f, 0x2013, 0x2014, 0x2015,
+ 0x2017, 0x2018, 0x2019, 0x201a, 0x201c, 0x201d, 0x201e, 0x2020, 0x2021, 0x2022,
+ 0x2026, 0x2030, 0x2032, 0x2033, 0x2039, 0x203a, 0x2044, 0x20a7, 0x20aa, 0x20ab,
+ 0x20ac, 0x2116, 0x2118, 0x2122, 0x2126, 0x2135, 0x2190, 0x2191, 0x2192, 0x2193,
+ 0x2194, 0x2195, 0x21b5, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x2200, 0x2202,
+ 0x2203, 0x2205, 0x2206, 0x2207, 0x2208, 0x2209, 0x220b, 0x220f, 0x2211, 0x2212,
+ 0x2215, 0x2217, 0x2219, 0x221a, 0x221d, 0x221e, 0x2220, 0x2227, 0x2228, 0x2229,
+ 0x222a, 0x222b, 0x2234, 0x223c, 0x2245, 0x2248, 0x2260, 0x2261, 0x2264, 0x2265,
+ 0x2282, 0x2283, 0x2284, 0x2286, 0x2287, 0x2295, 0x2297, 0x22a5, 0x22c5, 0x2310,
+ 0x2320, 0x2321, 0x2329, 0x232a, 0x2469, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514,
+ 0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2550, 0x2551, 0x2552, 0x2553,
+ 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d,
+ 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567,
+ 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590,
+ 0x2591, 0x2592, 0x2593, 0x25a0, 0x25b2, 0x25bc, 0x25c6, 0x25ca, 0x25cf, 0x25d7,
+ 0x2605, 0x260e, 0x261b, 0x261e, 0x2660, 0x2663, 0x2665, 0x2666, 0x2701, 0x2702,
+ 0x2703, 0x2704, 0x2706, 0x2707, 0x2708, 0x2709, 0x270c, 0x270d, 0x270e, 0x270f,
+ 0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719,
+ 0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723,
+ 0x2724, 0x2725, 0x2726, 0x2727, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e,
+ 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738,
+ 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f, 0x2740, 0x2741, 0x2742,
+ 0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274a, 0x274b, 0x274d,
+ 0x274f, 0x2750, 0x2751, 0x2752, 0x2756, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c,
+ 0x275d, 0x275e, 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x277f,
+ 0x2789, 0x2793, 0x2794, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e,
+ 0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7, 0x27a8,
+ 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af, 0x27b1, 0x27b2, 0x27b3,
+ 0x27b4, 0x27b5, 0x27b6, 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd,
+ 0x27be, 0xf6d9, 0xf6da, 0xf6db, 0xf8d7, 0xf8d8, 0xf8d9, 0xf8da, 0xf8db, 0xf8dc,
+ 0xf8dd, 0xf8de, 0xf8df, 0xf8e0, 0xf8e1, 0xf8e2, 0xf8e3, 0xf8e4, 0xf8e5, 0xf8e6,
+ 0xf8e7, 0xf8e8, 0xf8e9, 0xf8ea, 0xf8eb, 0xf8ec, 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0,
+ 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4, 0xf8f5, 0xf8f6, 0xf8f7, 0xf8f8, 0xf8f9, 0xf8fa,
+ 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0xfe7c, 0xfe7d,
+);
+?>
diff --git a/src/php-gettext/AUTHORS b/src/php-gettext/AUTHORS
new file mode 100644
index 0000000..da6ade7
--- /dev/null
+++ b/src/php-gettext/AUTHORS
@@ -0,0 +1,3 @@
+Danilo Segan <danilo@kvota.net>
+Nico Kaiser <nico@siriux.net> (contributed most changes between 1.0.2 and 1.0.3, bugfix for 1.0.5)
+Steven Armstrong <sa@c-area.ch> (gettext.inc, leading to 1.0.6)
diff --git a/src/php-gettext/COPYING b/src/php-gettext/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/src/php-gettext/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/php-gettext/ChangeLog b/src/php-gettext/ChangeLog
new file mode 100644
index 0000000..31e782e
--- /dev/null
+++ b/src/php-gettext/ChangeLog
@@ -0,0 +1,152 @@
+---- semanticscuttle-specific -----
+2009-11-03 Christian Weiske <cweiske@cweiske.de>
+ * fix magic detection on 64 bit systems, apply patch from:
+ http://source.ibiblio.org/trac/lyceum/attachment/ticket/652/gettext-64bit-fix.diff
+
+
+---- version 1.07 -----
+
+2006-02-07 Danilo Å egan <danilo@gnome.org>
+
+ * examples/pigs_dropin.php: comment-out bind_textdomain_codeset
+
+ * gettext.inc (T_bind_textdomain_codeset): bind_textdomain_codeset
+ is available only in PHP 4.2.0+ (thanks to Jens A. Tkotz).
+
+ * Makefile: Include gettext.inc in DIST_FILES, VERSION up to
+ 1.0.7.
+
+2006-02-03 Danilo Å egan <danilo@gnome.org>
+
+ Added setlocale() emulation as well.
+
+ * examples/pigs_dropin.php: Use T_setlocale() and locale_emulation().
+ * examples/pigs_fallback.php: Use T_setlocale() and locale_emulation().
+
+ * gettext.inc: Added globals $EMULATEGETTEXT and $CURRENTLOCALE.
+ (locale_emulation): Whether emulation is active.
+ (_check_locale): Rewrite.
+ (_setlocale): Added emulated setlocale function.
+ (T_setlocale): Wrapper around _setlocale.
+ (_get_reader): Use variables and _setlocale.
+
+2006-02-02 Danilo Å egan <danilo@gnome.org>
+
+ Fix bug #12192.
+
+ * examples/locale/sr_CS/LC_MESSAGES/messages.po: Correct grammar.
+ * examples/locale/sr_CS/LC_MESSAGES/messages.mo: Rebuild.
+
+2006-02-02 Danilo Å egan <danilo@gnome.org>
+
+ Fix bug #15419.
+
+ * streams.php: Support for PHP 5.1.1 fread() which reads most 8kb.
+ (Fix by Piotr Szotkowski <shot@hot.pl>)
+
+2006-02-02 Danilo Å egan <danilo@gnome.org>
+
+ Merge Steven Armstrong's changes, supporting standard gettext
+ interfaces:
+
+ * examples/*: Restructured examples.
+ * gettext.inc: Added.
+ * AUTHORS: Added Steven.
+ * Makefile (VERSION): Up to 1.0.6.
+
+2006-01-28 Nico Kaiser <nico@siriux.net>
+
+ * gettext.php (select_string): Fix "true" <-> 1 difference of PHP
+
+2005-07-29 Danilo Å egan <danilo@gnome.org>
+
+ * Makefile (VERSION): Up to 1.0.5.
+
+2005-07-29 Danilo Å egan <danilo@gnome.org>
+
+ Fixes bug #13850.
+
+ * gettext.php (gettext_reader): check $Reader->error as well.
+
+2005-07-29 Danilo Å egan <danilo@gnome.org>
+
+ * Makefile (VERSION): Up to 1.0.4.
+
+2005-07-29 Danilo Å egan <danilo@gnome.org>
+
+ Fixes bug #13771.
+
+ * gettext.php (gettext_reader->get_plural_forms): Plural forms
+ header extraction regex change. Reported by Edgar Gonzales.
+
+2005-02-28 Danilo Å egan <dsegan@gmx.net>
+
+ * AUTHORS: Added Nico to the list.
+
+ * Makefile (VERSION): Up to 1.0.3.
+
+ * README: Updated.
+
+2005-02-28 Danilo Å egan <dsegan@gmx.net>
+
+ * gettext.php: Added pre-loading, code documentation, and many
+ code clean-ups by Nico Kaiser <nico@siriux.net>.
+
+2005-02-28 Danilo Å egan <dsegan@gmx.net>
+
+ * streams.php (FileReader.read): Handle read($bytes = 0).
+
+ * examples/pigs.php: Prefix gettext function names with T or T_.
+
+ * examples/update: Use the same keywords T_ and T_ngettext.
+
+ * streams.php: Added CachedFileReader.
+
+2003-11-11 Danilo Å egan <dsegan@gmx.net>
+
+ * gettext.php: Added hashing to find_string.
+
+2003-11-01 Danilo Å egan <dsegan@gmx.net>
+
+ * Makefile (DIST_FILES): Replaced LICENSE with COPYING.
+ (VERSION): Up to 1.0.2.
+
+ * AUTHORS: Minor edits.
+
+ * README: Minor edits.
+
+ * COPYING: Removed LICENSE, added this file.
+
+ * gettext.php: Added copyright notice and disclaimer.
+ * streams.php: Same.
+ * examples/pigs.php: Same.
+
+2003-10-23 Danilo Å egan <dsegan@gmx.net>
+
+ * Makefile: Upped version to 1.0.1.
+
+ * gettext.php (gettext_reader): Remove a call to set_total_plurals.
+ (set_total_plurals): Removed unused function for some better days.
+
+2003-10-23 Danilo Å egan <dsegan@gmx.net>
+
+ * Makefile: Added, version 1.0.0.
+
+ * examples/*: Added an example of usage.
+
+ * README: Described all the crap.
+
+2003-10-22 Danilo Å egan <dsegan@gmx.net>
+
+ * gettext.php: Plural forms implemented too.
+
+ * streams.php: Added FileReader for direct access to files (no
+ need to keep file in memory).
+
+ * gettext.php: It works, except for plural forms.
+
+ * streams.php: Created abstract class StreamReader.
+ Added StringReader class.
+
+ * gettext.php: Started writing gettext_reader.
+
diff --git a/src/php-gettext/Makefile b/src/php-gettext/Makefile
new file mode 100644
index 0000000..a6cce12
--- /dev/null
+++ b/src/php-gettext/Makefile
@@ -0,0 +1,38 @@
+PACKAGE = php-gettext-$(VERSION)
+VERSION = 1.0.10
+
+DIST_FILES = \
+ gettext.php \
+ gettext.inc \
+ streams.php \
+ AUTHORS \
+ README \
+ COPYING \
+ Makefile \
+ examples/index.php \
+ examples/pigs_dropin.php \
+ examples/pigs_fallback.php \
+ examples/locale/sr_CS/LC_MESSAGES/messages.po \
+ 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 \
+ tests/LocalesTest.php \
+ tests/ParsingTest.php
+
+check:
+ phpunit --verbose tests
+
+dist: check
+ if [ -d $(PACKAGE) ]; then \
+ rm -rf $(PACKAGE); \
+ fi; \
+ mkdir $(PACKAGE); \
+ if [ -d $(PACKAGE) ]; then \
+ cp -rp --parents $(DIST_FILES) $(PACKAGE); \
+ tar cvzf $(PACKAGE).tar.gz $(PACKAGE); \
+ rm -rf $(PACKAGE); \
+ fi;
+
+clean:
+ rm -f $(PACKAGE).tar.gz
diff --git a/src/php-gettext/README b/src/php-gettext/README
new file mode 100644
index 0000000..bca4f91
--- /dev/null
+++ b/src/php-gettext/README
@@ -0,0 +1,161 @@
+PHP-gettext 1.0 (https://launchpad.net/php-gettext)
+
+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
+ "works-doesn't-work" (UTF-8: Ради-Ðе-Ради)
+
+
+Introduction
+
+ How many times did you look for a good translation tool, and
+ found out that gettext is best for the job? Many times.
+
+ How many times did you try to use gettext in PHP, but failed
+ miserably, because either your hosting provider didn't support
+ it, or the server didn't have adequate locale? Many times.
+
+ Well, this is a solution to your needs. It allows using gettext
+ tools for managing translations, yet it doesn't require gettext
+ library at all. It parses generated MO files directly, and thus
+ might be a bit slower than the (maybe provided) gettext library.
+
+ PHP-gettext is a simple reader for GNU gettext MO files. Those
+ are binary containers for translations, produced by GNU msgfmt.
+
+Why?
+
+ I got used to having gettext work even without gettext
+ library. It's there in my favourite language Python, so I was
+ surprised that I couldn't find it in PHP. I even Googled for it,
+ but to no avail.
+
+ So, I said, what the heck, I'm going to write it for this
+ disguisting language of PHP, because I'm often constrained to it.
+
+Features
+
+ o Support for simple translations
+ Just define a simple alias for translate() function (suggested
+ use of _() or gettext(); see provided example).
+
+ o Support for ngettext calls (plural forms, see a note under bugs)
+ You may also use plural forms. Translations in MO files need to
+ provide this, and they must also provide "plural-forms" header.
+ Please see 'info gettext' for more details.
+
+ o Support for reading straight files, or strings (!!!)
+ Since I can imagine many different backends for reading in the MO
+ 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.
+
+
+Bugs
+
+ Report them on https://bugs.launchpad.net/php-gettext
+
+Usage
+
+ Put files streams.php and gettext.php somewhere you can load them
+ from, and require 'em in where you want to use them.
+
+ Then, create one 'stream reader' (a class that provides functions
+ like read(), seekto(), currentpos() and length()) which will
+ provide data for the 'gettext_reader', with eg.
+ $streamer = new FileStream('data.mo');
+
+ 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
+ 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:
+ 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
+ xgettext -ktranslate -kngettext:1,2 file.php
+ what should create messages.po which contains two messages for
+ translation.
+
+ I suggest creating simple aliases for these functions (see
+ example/pigs.php for how do I do it, which means it's probably a
+ bad way).
+
+
+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
+ documented on:
+
+ http://www.php.net/gettext
+
+ The only catch is that you can check return value of setlocale()
+ to see if your locale is system supported or not.
+
+
+Example
+
+ See in examples/ subdirectory. There are a couple of files.
+ pigs.php is an example, serbian.po is a translation to Serbian
+ language, and serbian.mo is generated with
+ msgfmt -o serbian.mo serbian.po
+ There is also simple "update" script that can be used to generate
+ POT file and to update the translation using msgmerge.
+
+TODO:
+
+ o Improve speed to be even more comparable to the native gettext
+ implementation.
+
+ o Try to use hash tables in MO files: with pre-loading, would it
+ be useful at all?
+
+Never-asked-questions:
+
+ o Why did you mark this as version 1.0 when this is the first code
+ release?
+
+ 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
+ "version 1".
+
+ I plan to use version numbers 1.0.* for small bugfixes, and to
+ release 1.1 as "first stable release of version 1".
+
+ This may trick someone that this is actually useful software, but
+ as with any other free software, I take NO RESPONSIBILITY for
+ creating such a masterpiece that will smoke crack, trash your
+ hard disk, and make lasers in your CD device dance to the tune of
+ 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).
+
+ I'm considering licensing this under LGPL, but I *do* want
+ *every* PHP-gettext user to contribute and respect ideas of free
+ software, so don't count on it happening anytime soon.
+
+ I'm sorry that I'm taking away your freedom of taking others'
+ freedom away, but I believe that's neglible as compared to what
+ freedoms you could take away. ;-)
+
+ Uhm, whatever.
diff --git a/src/php-gettext/examples/index.php b/src/php-gettext/examples/index.php
new file mode 100644
index 0000000..263cd3d
--- /dev/null
+++ b/src/php-gettext/examples/index.php
@@ -0,0 +1,27 @@
+<html>
+<head>
+<title>PHP-gettext examples</title>
+</head>
+<body>
+<h1>PHP-gettext</h1>
+
+<h2>Introduction</h2>
+<p>PHP-gettext 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.</p>
+<p>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 second parameter in the constructor (e.g. when using very large MO files
+that you don't want to keep in memory)</p>
+
+
+<h2>Examples</h2>
+<ul>
+ <li><a href="pigs_dropin.php">PHP-gettext as a dropin replacement</a></li>
+ <li><a href="pigs_fallback.php">PHP-gettext as a fallback solution</a></li>
+</ul>
+
+<hr />
+<p>Copyright (c) 2003-2006 Danilo Segan</p>
+<p>Copyright (c) 2005-2006 Steven Armstrong</p>
+
+</body>
+</html>
diff --git a/src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.mo b/src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.mo
new file mode 100644
index 0000000..9193037
--- /dev/null
+++ b/src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.po b/src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..6e4886b
--- /dev/null
+++ b/src/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.po
@@ -0,0 +1,30 @@
+# Sample translation for PHP-gettext 1.0
+# Copyright (c) 2003 Danilo Segan <danilo@kvota.net>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pigs\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-10-23 04:50+0200\n"
+"PO-Revision-Date: 2003-11-01 23:40+0100\n"
+"Last-Translator: Danilo Segan <danilo@kvota.net>\n"
+"Language-Team: Serbian (sr) <danilo@kvota.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+#"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: pigs.php:19
+msgid ""
+"This is how the story goes.\n"
+"\n"
+msgstr ""
+"Und so geht die Geschichte.\n"
+"\n"
+
+#: pigs.php:21
+#, php-format
+msgid "%d pig went to the market\n"
+msgid_plural "%d pigs went to the market\n"
+msgstr[0] "%d Schwein ging zum Markt\n"
+msgstr[1] "%d Schweine gingen zum Markt\n"
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
new file mode 100644
index 0000000..497c883
--- /dev/null
+++ b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..e5da0e9
--- /dev/null
+++ b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po
@@ -0,0 +1,30 @@
+# Sample translation for PHP-gettext 1.0
+# Copyright (c) 2003,2006 Danilo Segan <danilo@kvota.net>
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pigs\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-10-23 04:50+0200\n"
+"PO-Revision-Date: 2006-02-02 21:06+0100\n"
+"Last-Translator: Danilo Segan <danilo@kvota.net>\n"
+"Language-Team: Serbian (sr) <danilo@kvota.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\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 ""
+"This is how the story goes.\n"
+"\n"
+msgstr "Овако иде прича.\n\n"
+
+#: pigs.php:21
+#, php-format
+msgid "%d pig went to the market\n"
+msgid_plural "%d pigs went to the market\n"
+msgstr[0] "%d мало праÑе је отишло на пијац\n"
+msgstr[1] "%d мала праÑета Ñу отишла на пијац\n"
+msgstr[2] "%d малих праÑића је отишло на пијац\n"
diff --git a/src/php-gettext/examples/pigs_dropin.php b/src/php-gettext/examples/pigs_dropin.php
new file mode 100644
index 0000000..94fd850
--- /dev/null
+++ b/src/php-gettext/examples/pigs_dropin.php
@@ -0,0 +1,89 @@
+<?php
+/*
+ Copyright (c) 2003,2004,2005,2009 Danilo Segan <danilo@kvota.net>.
+ Copyright (c) 2005,2006 Steven Armstrong <sa@c-area.ch>
+
+ This file is part of PHP-gettext.
+
+ PHP-gettext 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.
+
+ PHP-gettext 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 PHP-gettext; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+error_reporting(E_ALL | E_STRICT);
+
+// define constants
+define('PROJECT_DIR', realpath('./'));
+define('LOCALE_DIR', PROJECT_DIR .'/locale');
+define('DEFAULT_LOCALE', 'en_US');
+
+require_once('../gettext.inc');
+
+$supported_locales = array('en_US', 'sr_CS', 'de_CH');
+$encoding = 'UTF-8';
+
+$locale = (isset($_GET['lang']))? $_GET['lang'] : DEFAULT_LOCALE;
+
+// gettext setup
+T_setlocale(LC_MESSAGES, $locale);
+// Set the text domain as 'messages'
+$domain = 'messages';
+bindtextdomain($domain, LOCALE_DIR);
+// bind_textdomain_codeset is supported only in PHP 4.2.0+
+if (function_exists('bind_textdomain_codeset'))
+ bind_textdomain_codeset($domain, $encoding);
+textdomain($domain);
+
+header("Content-type: text/html; charset=$encoding");
+?>
+<html>
+<head>
+<title>PHP-gettext dropin example</title>
+</head>
+<body>
+<h1>PHP-gettext as a dropin replacement</h1>
+<p>Example showing how to use PHP-gettext as a dropin replacement for the native gettext library.</p>
+<?php
+print "<p>";
+foreach($supported_locales as $l) {
+ print "[<a href=\"?lang=$l\">$l</a>] ";
+}
+print "</p>\n";
+
+if (!locale_emulation()) {
+ print "<p>locale '$locale' is supported by your system, using native gettext implementation.</p>\n";
+}
+else {
+ print "<p>locale '$locale' is _not_ supported on your system, using the default locale '". DEFAULT_LOCALE ."'.</p>\n";
+}
+?>
+
+<hr />
+
+<?php
+// using PHP-gettext
+print "<pre>";
+print _("This is how the story goes.\n\n");
+for ($number=6; $number>=0; $number--) {
+ print sprintf(T_ngettext("%d pig went to the market\n",
+ "%d pigs went to the market\n", $number),
+ $number );
+}
+print "</pre>\n";
+?>
+
+<hr />
+<p>&laquo; <a href="./">back</a></p>
+</body>
+</html>
diff --git a/src/php-gettext/examples/pigs_fallback.php b/src/php-gettext/examples/pigs_fallback.php
new file mode 100644
index 0000000..353190d
--- /dev/null
+++ b/src/php-gettext/examples/pigs_fallback.php
@@ -0,0 +1,88 @@
+<?php
+/*
+ Copyright (c) 2003,2004,2005,2009 Danilo Segan <danilo@kvota.net>.
+ Copyright (c) 2005,2006 Steven Armstrong <sa@c-area.ch>
+
+ This file is part of PHP-gettext.
+
+ PHP-gettext 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.
+
+ PHP-gettext 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 PHP-gettext; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+error_reporting(E_ALL | E_STRICT);
+
+// define constants
+define('PROJECT_DIR', realpath('./'));
+define('LOCALE_DIR', PROJECT_DIR .'/locale');
+define('DEFAULT_LOCALE', 'en_US');
+
+require_once('../gettext.inc');
+
+$supported_locales = array('en_US', 'sr_CS', 'de_CH');
+$encoding = 'UTF-8';
+
+$locale = (isset($_GET['lang']))? $_GET['lang'] : DEFAULT_LOCALE;
+
+// gettext setup
+T_setlocale(LC_MESSAGES, $locale);
+// Set the text domain as 'messages'
+$domain = 'messages';
+T_bindtextdomain($domain, LOCALE_DIR);
+T_bind_textdomain_codeset($domain, $encoding);
+T_textdomain($domain);
+
+header("Content-type: text/html; charset=$encoding");
+?>
+<html>
+<head>
+<title>PHP-gettext fallback example</title>
+</head>
+<body>
+<h1>PHP-gettext as a fallback solution</h1>
+<p>Example showing how to use PHP-gettext as a fallback solution if the native gettext library is not available or the system does not support the requested locale.</p>
+
+<?php
+print "<p>";
+foreach($supported_locales as $l) {
+ print "[<a href=\"?lang=$l\">$l</a>] ";
+}
+print "</p>\n";
+
+if (!locale_emulation()) {
+ print "<p>locale '$locale' is supported by your system, using native gettext implementation.</p>\n";
+}
+else {
+ print "<p>locale '$locale' is <strong>not</strong> supported on your system, using custom gettext implementation.</p>\n";
+}
+?>
+
+<hr />
+
+<?php
+// using PHP-gettext
+print "<pre>";
+print T_("This is how the story goes.\n\n");
+for ($number=6; $number>=0; $number--) {
+ print sprintf( T_ngettext("%d pig went to the market\n",
+ "%d pigs went to the market\n", $number),
+ $number );
+}
+print "</pre>\n";
+?>
+
+<hr />
+<p>&laquo; <a href="./">back</a></p>
+</body>
+</html>
diff --git a/src/php-gettext/examples/update b/src/php-gettext/examples/update
new file mode 100644
index 0000000..76b4308
--- /dev/null
+++ b/src/php-gettext/examples/update
@@ -0,0 +1,14 @@
+#!/bin/sh
+TEMPLATE=pigs.pot
+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
+ msgmerge -o .tmp$1.po $1.po $TEMPLATE
+ mv .tmp$1.po $1.po
+ msgfmt --statistics $1.po
+ else
+ echo "Usage: $0 [-p|<basename>]"
+ fi
+fi
diff --git a/src/php-gettext/gettext.inc b/src/php-gettext/gettext.inc
new file mode 100644
index 0000000..399a0f2
--- /dev/null
+++ b/src/php-gettext/gettext.inc
@@ -0,0 +1,534 @@
+<?php
+/*
+ Copyright (c) 2005 Steven Armstrong <sa at c-area dot ch>
+ Copyright (c) 2009 Danilo Segan <danilo@kvota.net>
+
+ 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
+ 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.
+
+ PHP-gettext 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 PHP-gettext; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+/*
+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');
+
+
+// Variables
+
+global $text_domains, $default_domain, $LC_CATEGORIES, $EMULATEGETTEXT, $CURRENTLOCALE;
+$text_domains = array();
+$default_domain = 'messages';
+$LC_CATEGORIES = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MONETARY', 'LC_MESSAGES', 'LC_ALL');
+$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<lang>[a-z]{2,3})" // language code
+ ."(?:_(?P<country>[A-Z]{2}))?" // country code
+ ."(?:\.(?P<charset>[-A-Za-z0-9_]+))?" // charset
+ ."(?:@(?P<modifier>[-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);
+ $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;
+}
+
+/**
+ * Returns whether we are using our emulated gettext API or PHP built-in one.
+ */
+function locale_emulation() {
+ global $EMULATEGETTEXT;
+ return $EMULATEGETTEXT;
+}
+
+/**
+ * Checks if the current locale is supported on this system.
+ */
+function _check_locale_and_function($function=false) {
+ global $EMULATEGETTEXT;
+ if ($function and !function_exists($function))
+ return false;
+ return !$EMULATEGETTEXT;
+}
+
+/**
+ * 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');
+}
+
+/**
+ * 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;
+ }
+}
+
+
+// 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 != '')
+ return $CURRENTLOCALE;
+ 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 {
+ 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;
+ }
+}
+
+/**
+ * Sets the path for a domain.
+ */
+function _bindtextdomain($domain, $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;
+}
+
+/**
+ * Sets the default domain.
+ */
+function _textdomain($domain) {
+ global $default_domain;
+ $default_domain = $domain;
+}
+
+/**
+ * Lookup a message in the current domain.
+ */
+function _gettext($msgid) {
+ $l10n = _get_reader();
+ return _encode($l10n->translate($msgid));
+}
+
+/**
+ * Alias for gettext.
+ */
+function __($msgid) {
+ return _gettext($msgid);
+}
+
+/**
+ * Plural version of gettext.
+ */
+function _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 _encode($l10n->translate($msgid));
+}
+
+/**
+ * Plural version of dgettext.
+ */
+function _dngettext($domain, $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 _encode($l10n->translate($msgid));
+}
+/**
+ * Plural version of dcgettext.
+ */
+function _dcngettext($domain, $single, $plural, $number, $category) {
+ $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.
+
+function T_setlocale($category, $locale) {
+ return _setlocale($category, $locale);
+}
+
+function T_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('bind_textdomain_codeset'))
+ return bind_textdomain_codeset($domain, $codeset);
+ else return _bind_textdomain_codeset($domain, $codeset);
+}
+function T_textdomain($domain) {
+ if (_check_locale_and_function()) return textdomain($domain);
+ else return _textdomain($domain);
+}
+function T_gettext($msgid) {
+ if (_check_locale_and_function()) return gettext($msgid);
+ else return _gettext($msgid);
+}
+function T_($msgid) {
+ if (_check_locale_and_function()) return _($msgid);
+ return __($msgid);
+}
+function T_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_and_function()) return dgettext($domain, $msgid);
+ else return _dgettext($domain, $msgid);
+}
+function T_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_and_function())
+ return dcgettext($domain, $msgid, $category);
+ else return _dcgettext($domain, $msgid, $category);
+}
+function T_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);
+}
+
+
+
+// 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 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
new file mode 100644
index 0000000..a121f9c
--- /dev/null
+++ b/src/php-gettext/gettext.php
@@ -0,0 +1,421 @@
+<?php
+/*
+ Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>.
+ Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
+
+ This file is part of PHP-gettext.
+
+ PHP-gettext 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.
+
+ PHP-gettext 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 PHP-gettext; if not, write to the Free Software
+ 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
+ * second parameter in the constructor (e.g. whenusing very large MO files
+ * that you don't want to keep in memory)
+ */
+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;
+ var $short_circuit = false;
+ var $enable_cache = false;
+ var $originals = NULL; // offset of original table
+ var $translations = NULL; // offset of translation table
+ var $pluralheader = NULL; // cache header field for plural forms
+ var $total = 0; // total string count
+ var $table_originals = NULL; // table for original strings (offsets)
+ var $table_translations = NULL; // table for translated strings (offsets)
+ var $cache_translations = NULL; // original -> translation mapping
+
+
+ /* Methods */
+
+
+ /**
+ * Reads a 32bit Integer from the Stream
+ *
+ * @access private
+ * @return Integer from the Stream
+ */
+ function readint() {
+ if ($this->BYTEORDER == 0) {
+ // low endian
+ $input=unpack('V', $this->STREAM->read(4));
+ return array_shift($input);
+ } else {
+ // big endian
+ $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
+ */
+ function readintarray($count) {
+ if ($this->BYTEORDER == 0) {
+ // low endian
+ return unpack('V'.$count, $this->STREAM->read(4 * $count));
+ } else {
+ // big endian
+ 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)
+ */
+ function gettext_reader($Reader, $enable_cache = true) {
+ // If there isn't a StreamReader, turn on short circuit mode.
+ if (! $Reader || isset($Reader->error) ) {
+ $this->short_circuit = true;
+ return;
+ }
+
+ // Caching can be turned off
+ $this->enable_cache = $enable_cache;
+
+ $MAGIC1 = "\x95\x04\x12\xde";
+ $MAGIC2 = "\xde\x12\x04\x95";
+
+ $this->STREAM = $Reader;
+ $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() {
+ if (is_array($this->cache_translations) &&
+ is_array($this->table_originals) &&
+ is_array($this->table_translations))
+ return;
+
+ /* get original and translations tables */
+ 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 */
+ for ($i = 0; $i < $this->total; $i++) {
+ $this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
+ $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
+ $this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
+ $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
+ $this->cache_translations[$original] = $translation;
+ }
+ }
+ }
+
+ /**
+ * Returns a string from the "originals" table
+ *
+ * @access private
+ * @param int num Offset number of original string
+ * @return string Requested string if found, otherwise ''
+ */
+ function get_original_string($num) {
+ $length = $this->table_originals[$num * 2 + 1];
+ $offset = $this->table_originals[$num * 2 + 2];
+ if (! $length)
+ return '';
+ $this->STREAM->seekto($offset);
+ $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 ''
+ */
+ function get_translation_string($num) {
+ $length = $this->table_translations[$num * 2 + 1];
+ $offset = $this->table_translations[$num * 2 + 2];
+ if (! $length)
+ return '';
+ $this->STREAM->seekto($offset);
+ $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)
+ * @param int end (internally used in recursive function)
+ * @return int string number (offset in originals table)
+ */
+ function find_string($string, $start = -1, $end = -1) {
+ if (($start == -1) or ($end == -1)) {
+ // find_string is called with only one parameter, set start end end
+ $start = 0;
+ $end = $this->total;
+ }
+ if (abs($start - $end) <= 1) {
+ // We're done, now we either found the string, or it doesn't exist
+ $txt = $this->get_original_string($start);
+ if ($string == $txt)
+ return $start;
+ else
+ return -1;
+ } else if ($start > $end) {
+ // start > end -> turn around and start over
+ return $this->find_string($string, $end, $start);
+ } else {
+ // Divide table in two parts
+ $half = (int)(($start + $end) / 2);
+ $cmp = strcmp($string, $this->get_original_string($half));
+ if ($cmp == 0)
+ // string is exactly in the middle => return it
+ return $half;
+ else if ($cmp < 0)
+ // The string is in the upper half
+ return $this->find_string($string, $start, $half);
+ else
+ // The string is in the lower half
+ 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)
+ */
+ function translate($string) {
+ if ($this->short_circuit)
+ return $string;
+ $this->load_tables();
+
+ if ($this->enable_cache) {
+ // Caching enabled, get translated string from cache
+ if (array_key_exists($string, $this->cache_translations))
+ return $this->cache_translations[$string];
+ else
+ return $string;
+ } else {
+ // Caching not enabled, try to find string
+ $num = $this->find_string($string);
+ if ($num == -1)
+ return $string;
+ else
+ return $this->get_translation_string($num);
+ }
+ }
+
+ /**
+ * 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
+ // this is true, right?
+ $this->load_tables();
+
+ // cache header field for plural forms
+ if (! is_string($this->pluralheader)) {
+ if ($this->enable_cache) {
+ $header = $this->cache_translations[""];
+ } else {
+ $header = $this->get_translation_string(0);
+ }
+ $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
+ */
+ function select_string($n) {
+ $string = $this->get_plural_forms();
+ $string = str_replace('nplurals',"\$total",$string);
+ $string = str_replace("n",$n,$string);
+ $string = str_replace('plural',"\$plural",$string);
+
+ $total = 0;
+ $plural = 0;
+
+ eval("$string");
+ if ($plural >= $total) $plural = $total - 1;
+ return $plural;
+ }
+
+ /**
+ * Plural version of gettext
+ *
+ * @access public
+ * @param string single
+ * @param string plural
+ * @param string number
+ * @return translated plural form
+ */
+ function ngettext($single, $plural, $number) {
+ if ($this->short_circuit) {
+ if ($number != 1)
+ return $plural;
+ else
+ return $single;
+ }
+
+ // find out the appropriate form
+ $select = $this->select_string($number);
+
+ // this should contains all strings separated by NULLs
+ $key = $single . chr(0) . $plural;
+
+
+ if ($this->enable_cache) {
+ if (! array_key_exists($key, $this->cache_translations)) {
+ return ($number != 1) ? $plural : $single;
+ } else {
+ $result = $this->cache_translations[$key];
+ $list = explode(chr(0), $result);
+ return $list[$select];
+ }
+ } else {
+ $num = $this->find_string($key);
+ if ($num == -1) {
+ return ($number != 1) ? $plural : $single;
+ } else {
+ $result = $this->get_translation_string($num);
+ $list = explode(chr(0), $result);
+ return $list[$select];
+ }
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..3cdc158
--- /dev/null
+++ b/src/php-gettext/streams.php
@@ -0,0 +1,167 @@
+<?php
+/*
+ Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
+
+ This file is part of PHP-gettext.
+
+ PHP-gettext 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.
+
+ PHP-gettext 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 PHP-gettext; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+
+ // 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;
+ var $_str;
+
+ function StringReader($str='') {
+ $this->_str = $str;
+ $this->_pos = 0;
+ }
+
+ function read($bytes) {
+ $data = substr($this->_str, $this->_pos, $bytes);
+ $this->_pos += $bytes;
+ if (strlen($this->_str)<$this->_pos)
+ $this->_pos = strlen($this->_str);
+
+ return $data;
+ }
+
+ function seekto($pos) {
+ $this->_pos = $pos;
+ if (strlen($this->_str)<$this->_pos)
+ $this->_pos = strlen($this->_str);
+ return $this->_pos;
+ }
+
+ function currentpos() {
+ return $this->_pos;
+ }
+
+ function length() {
+ return strlen($this->_str);
+ }
+
+};
+
+
+class FileReader {
+ var $_pos;
+ var $_fd;
+ var $_length;
+
+ function FileReader($filename) {
+ if (file_exists($filename)) {
+
+ $this->_length=filesize($filename);
+ $this->_pos = 0;
+ $this->_fd = fopen($filename,'rb');
+ if (!$this->_fd) {
+ $this->error = 3; // Cannot read file, probably permissions
+ return false;
+ }
+ } else {
+ $this->error = 2; // File doesn't exist
+ return false;
+ }
+ }
+
+ function read($bytes) {
+ if ($bytes) {
+ fseek($this->_fd, $this->_pos);
+
+ // PHP 5.1.1 does not read more than 8192 bytes in one fread()
+ // the discussions at PHP Bugs suggest it's the intended behaviour
+ $data = '';
+ while ($bytes > 0) {
+ $chunk = fread($this->_fd, $bytes);
+ $data .= $chunk;
+ $bytes -= strlen($chunk);
+ }
+ $this->_pos = ftell($this->_fd);
+
+ return $data;
+ } else return '';
+ }
+
+ function seekto($pos) {
+ fseek($this->_fd, $pos);
+ $this->_pos = ftell($this->_fd);
+ return $this->_pos;
+ }
+
+ function currentpos() {
+ return $this->_pos;
+ }
+
+ function length() {
+ return $this->_length;
+ }
+
+ function close() {
+ fclose($this->_fd);
+ }
+
+};
+
+// 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) {
+ if (file_exists($filename)) {
+
+ $length=filesize($filename);
+ $fd = fopen($filename,'rb');
+
+ if (!$fd) {
+ $this->error = 3; // Cannot read file, probably permissions
+ return false;
+ }
+ $this->_str = fread($fd, $length);
+ fclose($fd);
+
+ } else {
+ $this->error = 2; // File doesn't exist
+ 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 @@
+<?php
+require_once('PHPUnit/Framework.php');
+require_once('gettext.inc');
+
+class LocaleTest extends PHPUnit_Framework_TestCase
+{
+ public function test_setlocale()
+ {
+ // _setlocale defaults to a locale name from environment variable LANG.
+ putenv("LANG=sr_RS");
+ $this->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 @@
+<?php
+require_once('PHPUnit/Framework.php');
+//require_once('gettext.php');
+
+class ParsingTest extends PHPUnit_Framework_TestCase
+{
+ public function test_extract_plural_forms_header_from_po_header()
+ {
+ $parser = new gettext_reader(NULL);
+ // It defaults to a "Western-style" plural header.
+ $this->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"
+ ));
+
+ }
+
+}
+?>
diff --git a/tests/AllTests.php b/tests/AllTests.php
new file mode 100644
index 0000000..92f8960
--- /dev/null
+++ b/tests/AllTests.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * SemanticScuttle unit tests.
+ *
+ * To launch this tests, you need PHPUnit 3.
+ * Run them with:
+ * $ cd tests; phpunit .
+ * or single files like:
+ * $ cd tests; phpunit BookmarkTest.php
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class AllTests extends PHPUnit_Framework_TestSuite
+{
+ public static function suite()
+ {
+ $suite = new AllTests();
+ $tdir = dirname(__FILE__);
+ $suite->addTestFile($tdir . '/BookmarkTest.php');
+ $suite->addTestFile($tdir . '/Bookmark2TagTest.php');
+ $suite->addTestFile($tdir . '/Tag2TagTest.php');
+ $suite->addTestFile($tdir . '/TagsCacheTest.php');
+ $suite->addTestFile($tdir . '/CommonDescriptionTest.php');
+ $suite->addTestFile($tdir . '/SearchHistoryTest.php');
+ $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/OpenSearchTest.php');
+ $suite->addTestFile($tdir . '/Api/PostsAddTest.php');
+ $suite->addTestFile($tdir . '/Api/PostsDeleteTest.php');
+ $suite->addTestFile($tdir . '/Api/PostsUpdateTest.php');
+ return $suite;
+ }
+
+
+
+ protected function tearDown()
+ {
+ }
+}
+?>
diff --git a/tests/Api/ExportCsvTest.php b/tests/Api/ExportCsvTest.php
new file mode 100644
index 0000000..a0a9eae
--- /dev/null
+++ b/tests/Api/ExportCsvTest.php
@@ -0,0 +1,262 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the SemanticScuttle csv export API
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Api_ExportCsvTest extends TestBaseApi
+{
+ protected $us;
+ protected $bs;
+ protected $urlPart = 'api/export_csv.php';
+
+
+
+ /**
+ * 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 MIME content type and filename header fields
+ */
+ public function testMimeTypeFilename()
+ {
+ $res = rreset($this->getAuthRequest())->send();
+
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'application/csv-tab-delimited-table; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+ //we need a file name
+ $this->assertNotNull($res->getHeader('content-disposition'));
+ }
+
+
+
+ /**
+ * Test CSV export without bookmarks
+ */
+ public function testNoBookmarks()
+ {
+ list($req, $uid) = $this->getAuthRequest();
+ $body = $req->send()->getBody();
+ $csv = $this->getCsvArray($body);
+
+ $this->assertEquals(1, count($csv));
+ $this->assertCsvHeader($csv);
+ }
+
+
+
+ /**
+ * Test CSV export with some bookmarks
+ */
+ public function testBookmarks()
+ {
+ list($req, $uid) = $this->getAuthRequest();
+ //public
+ $this->addBookmark(
+ $uid, 'http://example.org/testBookmarks', 0,
+ array('unittest', 'testBookmarks'), 'mytitle'
+ );
+ //shared
+ $this->addBookmark(
+ $uid, 'http://example.org/testBookmarks-shared', 1,
+ array('unittest', 'testBookmarks'), 'mytitle-shared'
+ );
+ //private
+ $this->addBookmark(
+ $uid, 'http://example.org/testBookmarks-private', 2,
+ array('unittest', 'testBookmarks'), 'mytitle-private'
+ );
+
+ //private other that should not in the export
+ $this->addBookmark(
+ null, 'http://example.org/testBookmarks-private2', 2
+ );
+ //public bookmark from other people that should not be
+ // exported, too
+ $this->addBookmark(
+ null, 'http://example.org/testBookmarks-other', 0
+ );
+
+ $body = $req->send()->getBody();
+ $csv = $this->getCsvArray($body);
+
+ $this->assertEquals(4, count($csv));
+ $this->assertCsvHeader($csv);
+
+ $this->assertEquals('http://example.org/testBookmarks', $csv[1][0]);
+ $this->assertEquals('mytitle', $csv[1][1]);
+ $this->assertEquals('unittest,testbookmarks', $csv[1][2]);
+
+ $this->assertEquals('http://example.org/testBookmarks-shared', $csv[2][0]);
+ $this->assertEquals('mytitle-shared', $csv[2][1]);
+ $this->assertEquals('unittest,testbookmarks', $csv[2][2]);
+
+ $this->assertEquals('http://example.org/testBookmarks-private', $csv[3][0]);
+ $this->assertEquals('mytitle-private', $csv[3][1]);
+ $this->assertEquals('unittest,testbookmarks', $csv[3][2]);
+ }
+
+
+
+ /**
+ * Test CSV export with tag filter
+ */
+ public function testTagFilter()
+ {
+ list($req, $uid) = $this->getAuthRequest('?tag=tag1');
+ $this->addBookmark(
+ $uid, 'http://example.org/tag-1', 0,
+ array('unittest', 'tag1')
+ );
+ $this->addBookmark(
+ $uid, 'http://example.org/tag-2', 0,
+ array('unittest', 'tag2')
+ );
+ $this->addBookmark(
+ $uid, 'http://example.org/tag-3', 0,
+ array('unittest', 'tag1', 'tag2')
+ );
+
+ $body = $req->send()->getBody();
+ $csv = $this->getCsvArray($body);
+
+ $this->assertEquals(3, count($csv));
+ $this->assertCsvHeader($csv);
+
+ $this->assertEquals('http://example.org/tag-1', $csv[1][0]);
+ $this->assertEquals('http://example.org/tag-3', $csv[2][0]);
+ }
+
+
+
+ /**
+ * Test CSV export with tag filter for multiple tags
+ */
+ public function testTagFilterMultiple()
+ {
+ list($req, $uid) = $this->getAuthRequest('?tag=tag1+tag2');
+ $this->addBookmark(
+ $uid, 'http://example.org/tag-1', 0,
+ array('unittest', 'tag1')
+ );
+ $this->addBookmark(
+ $uid, 'http://example.org/tag-2', 0,
+ array('unittest', 'tag2')
+ );
+ $this->addBookmark(
+ $uid, 'http://example.org/tag-3', 0,
+ array('unittest', 'tag1', 'tag2')
+ );
+
+ $body = $req->send()->getBody();
+ $csv = $this->getCsvArray($body);
+
+ $this->assertEquals(2, count($csv));
+ $this->assertCsvHeader($csv);
+
+ $this->assertEquals('http://example.org/tag-3', $csv[1][0]);
+ }
+
+
+
+ /**
+ * Asserts that the CSV array contains the correct header
+ *
+ * @param array $csv CSV array from getCsvArray()
+ *
+ * @return void
+ */
+ protected function assertCsvHeader($csv)
+ {
+ $this->assertEquals(
+ array('url', 'title', 'tags', 'description'),
+ $csv[0]
+ );
+ }
+
+
+
+ /**
+ * Converts a string of CSV data to an array
+ *
+ * @param string $body String containing the full CSV file
+ *
+ * @return array Array of CSV data
+ */
+ protected function getCsvArray($body)
+ {
+ $v53 = (version_compare(PHP_VERSION, '5.3.0') === 1);
+
+ //dead simple implementation that does not work with
+ // advanced CSV files
+ $ar = array();
+ foreach (explode("\n", $body) as $line) {
+ if ($v53) {
+ $ar[] = str_getcsv($line, ';');
+ } else {
+ $arl = explode(';', $line);
+ foreach ($arl as &$str) {
+ if (substr($str, 0, 1) == '"'
+ && substr($str, -1) == '"'
+ ) {
+ $str = substr($str, 1, -1);
+ }
+ }
+ $ar[] = $arl;
+ }
+ }
+ if (count(end($ar)) == 1 && rreset(end($ar)) == '') {
+ unset($ar[key($ar)]);
+ }
+ return $ar;
+ }
+}
+?> \ No newline at end of file
diff --git a/tests/Api/OpenSearchTest.php b/tests/Api/OpenSearchTest.php
new file mode 100644
index 0000000..09166da
--- /dev/null
+++ b/tests/Api/OpenSearchTest.php
@@ -0,0 +1,76 @@
+<?php
+
+
+class Api_OpenSearchTest extends TestBaseApi
+{
+ protected $urlPart = '';
+
+
+ public function testOpenSearchAvailable()
+ {
+ $req = $this->getRequest();
+ $xhtml = $req->send()->getBody();
+
+ $xml = simplexml_load_string($xhtml);
+ $xml->registerXPathNamespace('h', rreset($xml->getDocNamespaces()));
+
+ $this->assertInstanceOf(
+ 'SimpleXMLElement', $xml,
+ 'SemanticScuttle main page XHTML could not be loaded - maybe invalid?'
+ );
+
+ $arElements = $xml->xpath(
+ '//h:head/h:link'
+ . '[@rel="search" and @type="application/opensearchdescription+xml"]'
+ );
+ $this->assertEquals(
+ 1, count($arElements),
+ 'OpenSearch link in HTML is missing'
+ );
+ $searchDescUrl = $this->completeUrl((string)$arElements[0]['href']);
+ $this->assertNotNull($searchDescUrl, 'Search description URL is empty');
+
+ $req = new HTTP_Request2($searchDescUrl);
+ $res = $req->send();
+ $this->assertEquals(
+ 200, $res->getStatus(),
+ 'HTTP response status code is not 200'
+ );
+
+ $this->assertEquals(
+ $GLOBALS['unittestUrl'] . 'api/opensearch.php',
+ $searchDescUrl,
+ 'OpenSearch URL found, but it is not the expected one.'
+ . ' It may be that you misconfigured the "unittestUrl" setting'
+ );
+ }
+
+ public function testOpenSearchContentType()
+ {
+ $res = $this->getRequest('api/opensearch.php')->send();
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+ }
+
+ public function testOpenSearchSearchUrl()
+ {
+ $xml = $this->getRequest('api/opensearch.php')->send()->getBody();
+ $x = simplexml_load_string($xml);
+ $x->registerXPathNamespace('os', rreset($x->getDocNamespaces()));
+
+ $arElements = $x->xpath('//os:Url[@type="text/html"]');
+ $this->assertEquals(
+ 1, count($arElements),
+ 'Url in OpenSearch description is missing'
+ );
+ $this->assertEquals(
+ $GLOBALS['unittestUrl'] . 'search.php/all/{searchTerms}',
+ (string)$arElements[0]['template']
+ );
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/tests/Api/PostsAddTest.php b/tests/Api/PostsAddTest.php
new file mode 100644
index 0000000..e6d0531
--- /dev/null
+++ b/tests/Api/PostsAddTest.php
@@ -0,0 +1,456 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the SemanticScuttle post addition API.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Api_PostsAddTest extends TestBaseApi
+{
+ protected $urlPart = 'api/posts/add';
+
+
+
+ /**
+ * Test if authentication is required when sending no auth data
+ */
+ public function testAuthWithoutAuthData()
+ {
+ $req = $this->getRequest(null, false);
+ $res = $req->send();
+ $this->assertEquals(401, $res->getStatus());
+ }
+
+
+
+ /**
+ * Test if authentication is required when sending wrong user data
+ */
+ public function testAuthWrongCredentials()
+ {
+ $req = $this->getRequest(null, false);
+ $req->setAuth('user', 'password', HTTP_Request2::AUTH_BASIC);
+ $res = $req->send();
+ $this->assertEquals(401, $res->getStatus());
+ }
+
+
+
+ /**
+ * Test if adding a bookmark via POST works.
+ */
+ public function testAddBookmarkPost()
+ {
+ $this->bs->deleteAll();
+
+ $bmUrl = 'http://example.org/tag-1';
+ $bmTags = array('foo', 'bar', 'baz');
+ $bmDatetime = '2010-09-08T03:02:01Z';
+ $bmTitle = 'This is a foo title';
+ $bmDescription = <<<TXT
+This is the description of
+my bookmark with some
+newlines and <some>?&\$ÄÖ'"§special"'
+characters
+TXT;
+
+ list($req, $uId) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $bmUrl);
+ $req->addPostParameter('description', $bmTitle);
+ $req->addPostParameter('extended', $bmDescription);
+ $req->addPostParameter('tags', implode(' ', $bmTags));
+ $req->addPostParameter('dt', $bmDatetime);
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user should have one bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ $bm = $data['bookmarks'][0];
+
+ $this->assertEquals($bmUrl, $bm['bAddress']);
+ $this->assertEquals($bmTitle, $bm['bTitle']);
+ $this->assertEquals($bmDescription, stripslashes($bm['bDescription']));
+ $this->assertEquals($bmTags, $bm['tags']);
+ $this->assertEquals(
+ gmdate('Y-m-d H:i:s', strtotime($bmDatetime)),
+ $bm['bDatetime']
+ );
+ }
+
+
+
+ /**
+ * Test if adding a bookmark via GET works.
+ */
+ public function testAddBookmarkGet()
+ {
+ $this->bs->deleteAll();
+
+ $bmUrl = 'http://example.org/tag-1';
+ $bmTags = array('foo', 'bar', 'baz');
+ $bmDatetime = '2010-09-08T03:02:01Z';
+ $bmTitle = 'This is a foo title';
+ $bmDescription = <<<TXT
+This is the description of
+my bookmark with some
+newlines and <some>?&\$ÄÖ'"§special"'
+characters
+TXT;
+
+ list($req, $uId) = $this->getAuthRequest(
+ '?url=' . urlencode($bmUrl)
+ . '&description=' . urlencode($bmTitle)
+ . '&extended=' . urlencode($bmDescription)
+ . '&tags=' . urlencode(implode(' ', $bmTags))
+ . '&dt=' . urlencode($bmDatetime)
+ );
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user should have one bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ $bm = $data['bookmarks'][0];
+
+ $this->assertEquals($bmUrl, $bm['bAddress']);
+ $this->assertEquals($bmTitle, $bm['bTitle']);
+ $this->assertEquals($bmDescription, stripslashes($bm['bDescription']));
+ $this->assertEquals($bmTags, $bm['tags']);
+ $this->assertEquals(
+ gmdate('Y-m-d H:i:s', strtotime($bmDatetime)),
+ $bm['bDatetime']
+ );
+ }
+
+ /**
+ * Verify that the URL and description/title are enough parameters
+ * to add a bookmark.
+ */
+ public function testUrlDescEnough()
+ {
+ $this->bs->deleteAll();
+
+ list($req, $uId) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', 'http://example.org/tag2');
+ $req->addPostParameter('description', 'foo bar');
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user has 1 bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ }
+
+ /**
+ * Verify that the URL is required
+ */
+ public function testUrlRequired()
+ {
+ $this->bs->deleteAll();
+
+ list($req, $uId) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ //$req->addPostParameter('url', 'http://example.org/tag2');
+ $req->addPostParameter('description', 'foo bar');
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(400, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'URL missing')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user still has 0 bookmarks
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(0, $data['total']);
+ }
+
+ /**
+ * Verify that the description/title is required
+ */
+ public function testDescriptionRequired()
+ {
+ $this->bs->deleteAll();
+
+ list($req, $uId) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', 'http://example.org/tag2');
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(400, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'Description missing')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user still has 0 bookmarks
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(0, $data['total']);
+ }
+
+ /**
+ * Test that the replace=no parameter prevents the bookmark from being
+ * overwritten.
+ */
+ public function testReplaceNo()
+ {
+ $this->bs->deleteAll();
+
+ $url = 'http://example.org/tag2';
+ $title1 = 'foo bar 1';
+ $title2 = 'bar 2 foo';
+
+ list($req, $uId) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $url);
+ $req->addPostParameter('description', $title1);
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(200, $res->getStatus());
+
+ //send it a second time, with different title
+ list($req, $dummy) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $url);
+ $req->addPostParameter('description', $title2);
+ $req->addPostParameter('replace', 'no');
+ $res = $req->send();
+
+ //this time we should get an error
+ $this->assertEquals(409, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'bookmark does already exist')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user still has 1 bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ $this->assertEquals($title1, $data['bookmarks'][0]['bTitle']);
+
+ //send it a third time, without the replace parameter
+ // it defaults to "no", so the bookmark should not get overwritten
+ list($req, $dummy) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $url);
+ $req->addPostParameter('description', $title2);
+ $res = $req->send();
+
+ //this time we should get an error
+ $this->assertEquals(409, $res->getStatus());
+
+ //bookmark should not have changed
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ $this->assertEquals($title1, $data['bookmarks'][0]['bTitle']);
+ }
+
+ /**
+ * Test that the replace=yes parameter causes the bookmark to be updated.
+ */
+ public function testReplaceYes()
+ {
+ $this->bs->deleteAll();
+
+ $url = 'http://example.org/tag2';
+ $title1 = 'foo bar 1';
+ $title2 = 'bar 2 foo';
+
+ list($req, $uId) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $url);
+ $req->addPostParameter('description', $title1);
+ $res = $req->send();
+
+ //all should be well
+ $this->assertEquals(200, $res->getStatus());
+
+ //send it a second time, with different title
+ list($req, $dummy) = $this->getAuthRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $url);
+ $req->addPostParameter('description', $title2);
+ $req->addPostParameter('replace', 'yes');
+ $res = $req->send();
+
+ //no error
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //user still has 1 bookmark now, but with the new title
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ $this->assertEquals($title2, $data['bookmarks'][0]['bTitle']);
+ }
+
+
+ /**
+ * Test that a default privacy setting of 2 (Private) is used in adding
+ * a bookmark.
+ */
+ public function testDefaultPrivacyPrivate()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 2))
+ );
+ list($req, $uId) = $this->getAuthRequest('?unittestMode=1');
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', 'http://www.example.org/testdefaultprivacyposts_addprivate');
+ $req->addPostParameter('description', 'Test bookmark 1 for default privacy.');
+ $req->send();
+ $this->us->setCurrentUserId($uId);
+ $bms = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, count($bms['bookmarks']));
+ $bm = reset($bms['bookmarks']);
+ $this->assertEquals('2', $bm['bStatus']);
+ }//end testDefaultPrivacyPrivate
+
+
+ /**
+ * Test that a default privacy setting of 0 (Public) is used in adding
+ * a bookmark.
+ */
+ public function testDefaultPrivacyPublic()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 0))
+ );
+ list($req, $uId) = $this->getAuthRequest('?unittestMode=1');
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', 'http://www.example.org/testdefaultprivacyposts_addpublic');
+ $req->addPostParameter('description', 'Test bookmark 1 for default privacy.');
+ $req->send();
+ $this->us->setCurrentUserId($uId);
+ $bms = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, count($bms['bookmarks']));
+ $bm = reset($bms['bookmarks']);
+ $this->assertEquals('0', $bm['bStatus']);
+ }//end testDefaultPrivacyPublic
+
+
+}
+?>
diff --git a/tests/Api/PostsDeleteTest.php b/tests/Api/PostsDeleteTest.php
new file mode 100644
index 0000000..7ba1285
--- /dev/null
+++ b/tests/Api/PostsDeleteTest.php
@@ -0,0 +1,278 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the SemanticScuttle post deletion API.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Api_PostsDeleteTest extends TestBaseApi
+{
+ protected $urlPart = 'api/posts/delete';
+
+
+
+ /**
+ * Test if authentication is required when sending no auth data
+ */
+ public function testAuthWithoutAuthData()
+ {
+ $req = $this->getRequest(null, false);
+ $res = $req->send();
+ $this->assertEquals(401, $res->getStatus());
+ }
+
+
+
+ /**
+ * Test if authentication is required when sending wrong user data
+
+ */
+ public function testAuthWrongCredentials()
+ {
+ $req = $this->getRequest(null, false);
+ $req->setAuth('user', 'password', HTTP_Request2::AUTH_BASIC);
+ $res = $req->send();
+ $this->assertEquals(401, $res->getStatus());
+ }
+
+
+
+ /**
+ * Test if deleting an own bookmark works.
+ */
+ public function testDeleteOwnBookmark()
+ {
+ $this->bs->deleteAll();
+
+ $bookmarkUrl = 'http://example.org/tag-1';
+
+ list($req, $uId) = $this->getAuthRequest(
+ '?url=' . urlencode($bookmarkUrl)
+ );
+
+ $bId = $this->addBookmark(
+ $uId, $bookmarkUrl, 0,
+ array('unittest', 'tag1')
+ );
+ //user has one bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+
+ //send request
+ $res = $req->send();
+
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //bookmark should be deleted now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(0, $data['total']);
+ }
+
+
+
+ /**
+ * Test if deleting an own bookmark via POST works.
+ */
+ public function testDeleteOwnBookmarkPost()
+ {
+ $this->bs->deleteAll();
+
+ $bookmarkUrl = 'http://example.org/tag-1';
+
+ list($req, $uId) = $this->getAuthRequest();
+
+ $bId = $this->addBookmark(
+ $uId, $bookmarkUrl, 0,
+ array('unittest', 'tag1')
+ );
+ //user has one bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+
+ //send request
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', $bookmarkUrl);
+ $res = $req->send();
+
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ null, false
+ );
+
+ //bookmark should be deleted now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(0, $data['total']);
+ }
+
+
+
+ /**
+ * Verify that deleting a bookmark of a different does not work
+ */
+ public function testDeleteOtherBookmark()
+ {
+ $this->bs->deleteAll();
+
+ $bookmarkUrl = 'http://example.org/tag-1';
+
+ list($req, $uId) = $this->getAuthRequest(
+ '?url=' . urlencode($bookmarkUrl)
+ );
+ $uId2 = $this->addUser();
+
+ $bId = $this->addBookmark(
+ $uId2, $bookmarkUrl, 0,
+ array('unittest', 'tag1')
+ );
+ //user 1 has no bookmarks
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(0, $data['total']);
+ //user 2 has one bookmark
+ $data = $this->bs->getBookmarks(0, null, $uId2);
+ $this->assertEquals(1, $data['total']);
+
+ //send request
+ $res = $req->send();
+
+ //404 - user does not have that bookmark
+ $this->assertEquals(404, $res->getStatus());
+
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'item not found')
+ ),
+ $res->getBody(),
+ '', false
+ );
+
+ //bookmark should still be there
+ $data = $this->bs->getBookmarks(0, null, $uId2);
+ $this->assertEquals(1, $data['total']);
+ }
+
+
+
+ /**
+ * Test if deleting a bookmark works that also other users
+ * bookmarked.
+ */
+ public function testDeleteBookmarkOneOfTwo()
+ {
+ $this->bs->deleteAll();
+
+ $bookmarkUrl = 'http://example.org/tag-1';
+
+ list($req, $uId) = $this->getAuthRequest(
+ '?url=' . urlencode($bookmarkUrl)
+ );
+ $uId2 = $this->addUser();
+ $uId3 = $this->addUser();
+
+ //important: the order of addition is crucial here
+ $this->addBookmark(
+ $uId2, $bookmarkUrl, 0,
+ array('unittest', 'tag1')
+ );
+ $bId = $this->addBookmark(
+ $uId, $bookmarkUrl, 0,
+ array('unittest', 'tag1')
+ );
+ $this->addBookmark(
+ $uId3, $bookmarkUrl, 0,
+ array('unittest', 'tag1')
+ );
+
+ //user one and two have a bookmark now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(1, $data['total']);
+ $data = $this->bs->getBookmarks(0, null, $uId2);
+ $this->assertEquals(1, $data['total']);
+
+ //send request
+ $res = $req->send();
+
+ $this->assertEquals(200, $res->getStatus());
+ //verify MIME content type
+ $this->assertEquals(
+ 'text/xml; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+
+ //verify xml
+ $this->assertTag(
+ array(
+ 'tag' => 'result',
+ 'attributes' => array('code' => 'done')
+ ),
+ $res->getBody(),
+ '', false
+ );
+
+ //bookmark should be deleted now
+ $data = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(0, $data['total']);
+ //user 2 should still have his
+ $data = $this->bs->getBookmarks(0, null, $uId2);
+ $this->assertEquals(1, $data['total']);
+ //user 3 should still have his, too
+ $data = $this->bs->getBookmarks(0, null, $uId3);
+ $this->assertEquals(1, $data['total']);
+ }
+
+}
+?> \ No newline at end of file
diff --git a/tests/Api/PostsUpdateTest.php b/tests/Api/PostsUpdateTest.php
new file mode 100644
index 0000000..51f8be2
--- /dev/null
+++ b/tests/Api/PostsUpdateTest.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the SemanticScuttle last-update time API.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Api_PostsUpdateTest extends TestBaseApi
+{
+ protected $urlPart = 'api/posts/update';
+
+
+
+ /**
+ * 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']
+ )
+ );
+ }
+
+}
+?> \ No newline at end of file
diff --git a/tests/Bookmark2TagTest.php b/tests/Bookmark2TagTest.php
new file mode 100644
index 0000000..76390d9
--- /dev/null
+++ b/tests/Bookmark2TagTest.php
@@ -0,0 +1,688 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle bookmark-tag combination service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Bookmark2TagTest extends TestBase
+{
+ protected $us;
+ protected $bs;
+ protected $ts;
+ protected $tts;
+
+
+ /**
+ * Create a bookmark. Like addBookmark(), just with other paramter order
+ * to make some tests in that class easier to write.
+ *
+ * @param integer $user User ID the bookmark shall belong
+ * @param array $tags Array of tags to attach. If "null" is given,
+ * it will automatically be "unittest"
+ * @param string $date strtotime-compatible string
+ * @param string $title Bookmark title
+ *
+ * @return integer ID of bookmark
+ */
+ protected function addTagBookmark($user, $tags, $date = null, $title = null)
+ {
+ return $this->addBookmark(
+ $user, null, 0, $tags, $title, $date
+ );
+ }
+
+
+
+ protected function setUp()
+ {
+ $this->us = SemanticScuttle_Service_Factory::get('User');
+ $this->us->deleteAll();
+ $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->vs = SemanticScuttle_Service_Factory::get('Vote');
+ $this->vs->deleteAll();
+ }
+
+
+
+ public function testAttachTagsWithoutTagsAddsSystemUnfiled()
+ {
+ $bid = $this->addBookmark(null, null, 0, array());
+ $this->assertEquals(
+ array('system:unfiled'),
+ $this->b2ts->getTagsForBookmark($bid, true)
+ );
+ }
+
+ public function testAttachTagsWithArrayWithEmptyStringAddsSystemUnfiled()
+ {
+ $bid = $this->addBookmark(null, null, 0, array(''));
+ $this->assertEquals(
+ array('system:unfiled'),
+ $this->b2ts->getTagsForBookmark($bid, true)
+ );
+ }
+
+ public function testAttachTagsWithEmptyStringAddsSystemUnfiled()
+ {
+ $originalDisplayErros = ini_get('display_errors');
+ $originalErrorReporting = ini_get('error_reporting');
+ ini_set('display_errors', 1);
+ error_reporting(E_ALL);
+ $bid = $this->addBookmark(null, null, 0, '');
+ $this->assertEquals(
+ array('system:unfiled'),
+ $this->b2ts->getTagsForBookmark($bid, true)
+ );
+ ini_set('display_errors', $originalDisplayErros);
+ error_reporting($originalErrorReporting);
+ }
+
+ public function testAttachTagsWithSomeEmptyTags()
+ {
+ $bid = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid, array('foo', '', 'bar', 'baz'));
+ $this->assertEquals(
+ array('foo', 'bar', 'baz'),
+ $this->b2ts->getTagsForBookmark($bid)
+ );
+ }
+
+ /**
+ * Test getTagsForBookmark() when the bookmark has no tags
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getTagsForBookmark
+ */
+ public function testGetTagsForBookmarkNone()
+ {
+ $this->addBookmark(null, null, 0, array('forz', 'barz'));
+
+ $bid = $this->addBookmark(null, null, 0, array());
+ $this->assertEquals(
+ array(),
+ $this->b2ts->getTagsForBookmark($bid)
+ );
+ }
+
+
+
+ /**
+ * Test getTagsForBookmark() when the bookmark has one tag
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getTagsForBookmark
+ */
+ public function testGetTagsForBookmarkOne()
+ {
+ $this->addBookmark(null, null, 0, array('forz', 'barz'));
+
+ $bid = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid, array('foo'));
+ $this->assertEquals(
+ array('foo'),
+ $this->b2ts->getTagsForBookmark($bid)
+ );
+ }
+
+
+
+ /**
+ * Test getTagsForBookmark() when the bookmark has three tags
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getTagsForBookmark
+ */
+ public function testGetTagsForBookmarkThr()
+ {
+ $this->addBookmark(null, null, 0, array('forz', 'barz'));
+
+ $bid = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid, array('foo', 'bar', 'fuu'));
+
+ $tags = $this->b2ts->getTagsForBookmark($bid);
+ $this->assertInternalType('array', $tags);
+ $this->assertContains('foo', $tags);
+ $this->assertContains('bar', $tags);
+ $this->assertContains('fuu', $tags);
+ }
+
+
+
+ /**
+ * Test getTagsForBookmarks() when no bookmarks have tags.
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getTagsForBookmarks
+ */
+ public function testGetTagsForBookmarksNone()
+ {
+ $bid1 = $this->addBookmark(null, null, 0, array());
+ $bid2 = $this->addBookmark(null, null, 0, array());
+
+ $alltags = $this->b2ts->getTagsForBookmarks(
+ array($bid1, $bid2)
+ );
+ $this->assertInternalType('array', $alltags);
+ $this->assertEquals(2, count($alltags));
+ $this->assertInternalType('array', $alltags[$bid1]);
+ $this->assertInternalType('array', $alltags[$bid2]);
+ $this->assertEquals(0, count($alltags[$bid1]));
+ $this->assertEquals(0, count($alltags[$bid2]));
+ }
+
+
+
+ /**
+ * Test getTagsForBookmarks() when most bookmarks have tags.
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getTagsForBookmarks
+ */
+ public function testGetTagsForBookmarksMost()
+ {
+ $bid1 = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid1, array('foo', 'bar', 'fuu'));
+
+ $bid2 = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid2, array('foo', 'bar2', 'fuu2'));
+
+ $bid3 = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid3, array('foo', 'bar2', 'fuu3'));
+
+ $bid4 = $this->addBookmark(null, null, 0, array());
+ //no tags
+
+ //bookmark that does not get queried
+ //http://sourceforge.net/projects/semanticscuttle/forums/forum/759510/topic/3752670
+ $bid5 = $this->addBookmark(null, null, 0, array());
+ $this->b2ts->attachTags($bid5, array('foo', 'bar2', 'fuu5'));
+
+
+ $alltags = $this->b2ts->getTagsForBookmarks(
+ array($bid1, $bid2, $bid3, $bid4)
+ );
+ $this->assertInternalType('array', $alltags);
+ foreach ($alltags as $bid => $btags) {
+ $this->assertInternalType('array', $btags);
+ if ($bid == $bid1) {
+ $this->assertEquals(3, count($btags));
+ $this->assertContains('foo', $btags);
+ $this->assertContains('bar', $btags);
+ $this->assertContains('fuu', $btags);
+ } else if ($bid == $bid2) {
+ $this->assertEquals(3, count($btags));
+ $this->assertContains('foo', $btags);
+ $this->assertContains('bar2', $btags);
+ $this->assertContains('fuu2', $btags);
+ } else if ($bid == $bid3) {
+ $this->assertEquals(3, count($btags));
+ $this->assertContains('foo', $btags);
+ $this->assertContains('bar2', $btags);
+ $this->assertContains('fuu3', $btags);
+ } else if ($bid == $bid4) {
+ $this->assertEquals(0, count($btags));
+ } else {
+ $this->assertTrue(false, 'Unknown bookmark id');
+ }
+ }
+ }
+
+
+
+ /**
+ * Fetch the most popular tags in descending order
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsOrder()
+ {
+ $user = $this->addUser();
+ $this->addTagBookmark($user, array('one', 'two'));
+ $this->addTagBookmark($user, array('one', 'thr'));
+ $this->addTagBookmark($user, array('one', 'two'));
+
+ $arTags = $this->b2ts->getPopularTags();
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(3, count($arTags));
+
+ $this->assertInternalType('array', $arTags[0]);
+
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '3'),
+ array('tag' => 'two', 'bCount' => '2'),
+ array('tag' => 'thr', 'bCount' => '1')
+ ),
+ $arTags
+ );
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsLimit()
+ {
+ $user = $this->addUser();
+ $this->addTagBookmark($user, array('one', 'two'));
+ $this->addTagBookmark($user, array('one', 'thr'));
+ $this->addTagBookmark($user, array('one', 'two'));
+
+ $arTags = $this->b2ts->getPopularTags();
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(3, count($arTags));
+
+ $arTags = $this->b2ts->getPopularTags(null, 2);
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(2, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '3'),
+ array('tag' => 'two', 'bCount' => '2'),
+ ),
+ $arTags
+ );
+
+ $arTags = $this->b2ts->getPopularTags(null, 1);
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(1, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '3'),
+ ),
+ $arTags
+ );
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsDays()
+ {
+ $user = $this->addUser();
+ $this->addTagBookmark($user, array('one', 'two'), 'now');
+ $this->addTagBookmark($user, array('one', 'thr'), 'now');
+ $this->addTagBookmark($user, array('one', 'two'), '-1 day -1 hour');
+ $this->addTagBookmark($user, array('one', 'thr'), '-3 days -1 hour');
+
+ $arTags = $this->b2ts->getPopularTags(null, 10, null, 1);
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'one', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'two', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'thr', 'bCount' => '1'), $arTags);
+
+ $arTags = $this->b2ts->getPopularTags(null, 10, null, 2);
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(3, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '3'),
+ array('tag' => 'two', 'bCount' => '2'),
+ array('tag' => 'thr', 'bCount' => '1'),
+ ),
+ $arTags
+ );
+
+ $arTags = $this->b2ts->getPopularTags(null, 10, null, 5);
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'one', 'bCount' => '4'), $arTags);
+ $this->assertContains(array('tag' => 'two', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'thr', 'bCount' => '2'), $arTags);
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsBeginsWith()
+ {
+ $user = $this->addUser();
+ $this->addTagBookmark($user, array('one', 'two'));
+ $this->addTagBookmark($user, array('one', 'thr'));
+ $this->addTagBookmark($user, array('one', 'two'));
+ $this->addTagBookmark($user, array('one', 'thr'));
+
+ $arTags = $this->b2ts->getPopularTags(null, 10, null, null, 'o');
+ $this->assertEquals(1, count($arTags));
+ $this->assertContains(array('tag' => 'one', 'bCount' => '4'), $arTags);
+
+ $arTags = $this->b2ts->getPopularTags(null, 10, null, null, 'tw');
+ $this->assertEquals(1, count($arTags));
+ $this->assertContains(array('tag' => 'two', 'bCount' => '2'), $arTags);
+
+ $arTags = $this->b2ts->getPopularTags(null, 10, null, null, 't');
+ $this->assertEquals(2, count($arTags));
+ $this->assertContains(array('tag' => 'two', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'thr', 'bCount' => '2'), $arTags);
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsExcludesSystemTags()
+ {
+ $user = $this->addUser();
+ $this->addTagBookmark($user, array('one', 'system:test'));
+ $this->addTagBookmark($user, array('one', 'system:unittest'));
+ $this->addTagBookmark($user, array('one', 'sys:unittest'));
+
+ $arTags = $this->b2ts->getPopularTags();
+ $this->assertInternalType('array', $arTags);
+ $this->assertEquals(2, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '3'),
+ array('tag' => 'sys:unittest', 'bCount' => '1'),
+ ),
+ $arTags
+ );
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsUserTags()
+ {
+ $user1 = $this->addUser();
+ $user2 = $this->addUser();
+ $user3 = $this->addUser();
+ $this->addTagBookmark($user1, array('one'));
+ $this->addTagBookmark($user2, array('one', 'two'));
+ $this->addTagBookmark($user2, array('two'));
+ $this->addTagBookmark($user3, array('one', 'thr'));
+
+ $arTags = $this->b2ts->getPopularTags($user1);
+ $this->assertEquals(1, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '1'),
+ ),
+ $arTags
+ );
+
+ $arTags = $this->b2ts->getPopularTags($user2);
+ $this->assertEquals(2, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'two', 'bCount' => '2'),
+ array('tag' => 'one', 'bCount' => '1'),
+ ),
+ $arTags
+ );
+
+ $arTags = $this->b2ts->getPopularTags(array($user2, $user3));
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'one', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'two', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'thr', 'bCount' => '1'), $arTags);
+ }
+
+
+
+ /**
+ * This may happen when the method is called with a problematic user array.
+ * In that case we may not generate invalid SQL or so.
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsUserArrayWithNull()
+ {
+ $user1 = $this->addUser();
+ $this->addTagBookmark($user1, array('one'));
+
+ $arTags = $this->b2ts->getPopularTags(array(null));
+ $this->assertEquals(0, count($arTags));
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsPublicOnlyNoUser()
+ {
+ $user1 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('one'));
+ $this->addBookmark($user1, null, 1, array('one', 'two'));
+ $this->addBookmark($user1, null, 2, array('thr'));
+
+ $arTags = $this->b2ts->getPopularTags();
+ $this->assertEquals(1, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '1'),
+ ),
+ $arTags
+ );
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsPublicOnlySingleUser()
+ {
+ $user1 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('one'));
+ $this->addBookmark($user1, null, 1, array('one', 'two'));
+ $this->addBookmark($user1, null, 2, array('thr'));
+
+ $arTags = $this->b2ts->getPopularTags($user1);
+ $this->assertEquals(1, count($arTags));
+ $this->assertEquals(
+ array(
+ array('tag' => 'one', 'bCount' => '1'),
+ ),
+ $arTags
+ );
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsPublicOnlySeveralUsers()
+ {
+ $user1 = $this->addUser();
+ $user2 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('one'));
+ $this->addBookmark($user1, null, 1, array('one', 'two'));
+ $this->addBookmark($user1, null, 2, array('thr'));
+ $this->addBookmark($user2, null, 0, array('fou'));
+ $this->addBookmark($user2, null, 1, array('fiv'));
+ $this->addBookmark($user2, null, 2, array('six'));
+
+ $arTags = $this->b2ts->getPopularTags(array($user1, $user2));
+ $this->assertEquals(2, count($arTags));
+ $this->assertContains(array('tag' => 'one', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'fou', 'bCount' => '1'), $arTags);
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsUserPrivatesWhenLoggedIn()
+ {
+ $user1 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('one'));
+ $this->addBookmark($user1, null, 1, array('one', 'two'));
+ $this->addBookmark($user1, null, 2, array('thr'));
+
+ $arTags = $this->b2ts->getPopularTags($user1, 10, $user1);
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'one', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'two', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'thr', 'bCount' => '1'), $arTags);
+ }
+
+ /**
+ * Should return the logged on user's public, protected and private tags
+ * as well as public ones of other specified users.
+ *
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getPopularTags
+ */
+ public function testGetPopularTagsUserPrivatesAndOthersWhenLoggedIn()
+ {
+ $user1 = $this->addUser();
+ $user2 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('one'));
+ $this->addBookmark($user1, null, 1, array('one', 'two'));
+ $this->addBookmark($user1, null, 2, array('thr'));
+ $this->addBookmark($user2, null, 0, array('fou'));
+ $this->addBookmark($user2, null, 1, array('fiv'));
+ $this->addBookmark($user2, null, 2, array('six'));
+
+ $arTags = $this->b2ts->getPopularTags(array($user2, $user1), 10, $user1);
+ $this->assertContains(array('tag' => 'one', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'two', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'thr', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'fou', 'bCount' => '1'), $arTags);
+ $this->assertEquals(4, count($arTags));
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getAdminTags
+ */
+ public function testGetAdminTags()
+ {
+ $admin1 = $this->addUser('admin1');
+ $admin2 = $this->addUser('admin2');
+ $user1 = $this->addUser();
+ $this->addBookmark($admin1, null, 0, array('admintag', 'admintag1'));
+ $this->addBookmark($admin2, null, 0, array('admintag', 'admintag2'));
+ $this->addBookmark($user1, null, 0, array('usertag'));
+
+ $GLOBALS['admin_users'] = array('admin1', 'admin2');
+
+ $arTags = $this->b2ts->getAdminTags(4);
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'admintag', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'admintag1', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'admintag2', 'bCount' => '1'), $arTags);
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getAdminTags
+ */
+ public function testGetAdminTagsBeginsWith()
+ {
+ $admin1 = $this->addUser('admin1');
+ $this->addBookmark($admin1, null, 0, array('admintag', 'admintag1'));
+ $this->addBookmark($admin1, null, 0, array('tester', 'testos'));
+
+ $GLOBALS['admin_users'] = array('admin1');
+
+ $arTags = $this->b2ts->getAdminTags(4, null, null, 'test');
+ $this->assertEquals(2, count($arTags));
+ $this->assertContains(array('tag' => 'tester', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'testos', 'bCount' => '1'), $arTags);
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getContactTags
+ */
+ public function testGetContactTagsWatchlistOnly()
+ {
+ $user1 = $this->addUser();
+ $user2 = $this->addUser();
+ $user3 = $this->addUser();
+ $this->us->setCurrentUserId($user1);
+ $this->us->setWatchStatus($user2);
+ //user1 watches user2 now
+
+ $this->addBookmark($user1, null, 0, array('usertag', 'usertag1'));
+ $this->addBookmark($user2, null, 0, array('usertag', 'usertag2'));
+ $this->addBookmark($user3, null, 0, array('usertag', 'usertag3'));
+
+ $arTags = $this->b2ts->getContactTags($user1, 10);
+ $this->assertEquals(2, count($arTags));
+ $this->assertContains(array('tag' => 'usertag', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'usertag2', 'bCount' => '1'), $arTags);
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getContactTags
+ */
+ public function testGetContactTagsIncludingUser()
+ {
+ $user1 = $this->addUser();
+ $user2 = $this->addUser();
+ $user3 = $this->addUser();
+ $this->us->setCurrentUserId($user1);
+ $this->us->setWatchStatus($user2);
+ //user1 watches user2 now
+
+ $this->addBookmark($user1, null, 0, array('usertag', 'usertag1'));
+ $this->addBookmark($user2, null, 0, array('usertag', 'usertag2'));
+ $this->addBookmark($user3, null, 0, array('usertag', 'usertag3'));
+
+ $arTags = $this->b2ts->getContactTags($user1, 10, $user1);
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'usertag', 'bCount' => '2'), $arTags);
+ $this->assertContains(array('tag' => 'usertag1', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'usertag2', 'bCount' => '1'), $arTags);
+ }
+
+ /**
+ * @covers SemanticScuttle_Service_Bookmark2Tag::getContactTags
+ */
+ public function testGetContactTagsBeginsWith()
+ {
+ $user1 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('usertag', 'usertag1'));
+ $this->addBookmark($user1, null, 0, array('usable', 'undefined'));
+ $this->addBookmark($user1, null, 0, array('fußbad', 'usable'));
+
+ $arTags = $this->b2ts->getContactTags($user1, 10, $user1, null, 'user');
+ $this->assertEquals(2, count($arTags));
+ $this->assertContains(array('tag' => 'usertag', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'usertag1', 'bCount' => '1'), $arTags);
+
+ $arTags = $this->b2ts->getContactTags($user1, 10, $user1, null, 'us');
+ $this->assertEquals(3, count($arTags));
+ $this->assertContains(array('tag' => 'usertag', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'usertag1', 'bCount' => '1'), $arTags);
+ $this->assertContains(array('tag' => 'usable', 'bCount' => '2'), $arTags);
+ }
+
+ public function testHasTag()
+ {
+ $bid = $this->addBookmark(null, null, 0, array('foo'));
+
+ $this->assertTrue($this->b2ts->hasTag($bid, 'foo'));
+ $this->assertFalse($this->b2ts->hasTag($bid, 'bar'));
+
+ }
+}
+?>
diff --git a/tests/BookmarkTest.php b/tests/BookmarkTest.php
new file mode 100644
index 0000000..c436859
--- /dev/null
+++ b/tests/BookmarkTest.php
@@ -0,0 +1,1380 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle bookmark service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class BookmarkTest extends TestBase
+{
+ protected $us;
+ protected $bs;
+ protected $ts;
+ protected $tts;
+
+
+ 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->vs = SemanticScuttle_Service_Factory::get('Vote');
+ $this->vs->deleteAll();
+ }
+
+ /**
+ * Tests if adding a bookmark with short url name
+ * saves it in the database.
+ */
+ public function testAddBookmarkShort()
+ {
+ $bid = $this->bs->addBookmark(
+ 'http://example.org', 'title', 'desc', 'priv',
+ 0, array(), 'myShortName'
+ );
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals('http://example.org', $bm['bAddress']);
+ $this->assertArrayHasKey('bShort', $bm);
+ $this->assertEquals('myShortName', $bm['bShort']);
+ }
+
+ public function testAddBookmarkInvalidUrl()
+ {
+ $retval = $this->bs->addBookmark(
+ 'javascript:alert(123)', 'title', 'desc', 'priv',
+ 0, array()
+ );
+ $this->assertFalse($retval, 'Bookmark with invalid URL was accepted');
+ }
+
+ public function testAddBookmarkWithSpecialCharacters()
+ {
+ $bs = $this->bs;
+ $title = "title&é\"'(-è_çà)=";
+ $desc = "description#{[|`\^@]}³<> ¹¡÷׿&é\"'(-è\\_çà)=";
+ $tag1 = "#{|`^@]³¹¡¿<&é\"'(-è\\_çà)";
+ $tag2 = "&é\"'(-è.[?./§!_çà)";
+
+ $uid = $this->addUser();
+ $bid = $bs->addBookmark(
+ 'http://site1.com', $title, $desc, 'note',
+ 0, array($tag1, $tag2),
+ null, null, false, false, $uid
+ );
+
+ $bookmarks = $bs->getBookmarks(0, 1);
+
+ $b0 = $bookmarks['bookmarks'][0];
+ $this->assertEquals($title, $b0['bTitle']);
+ $this->assertEquals($desc, $b0['bDescription']);
+ $this->assertEquals(
+ str_replace(array('"', '\'', '/'), "_", $tag1),
+ $b0['tags'][0]
+ );
+ $this->assertEquals(
+ str_replace(array('"', '\'', '/'), "_", $tag2),
+ $b0['tags'][1]
+ );
+ }
+
+ public function testAddBookmarkUnification()
+ {
+ $bs = $this->bs;
+
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+
+ $bs->addBookmark(
+ 'http://site1.com', "title", "description", 'note',
+ 0, array('tag1'), null, null, false, false,
+ $uid
+ );
+ $bs->addBookmark(
+ "http://site1.com", "title2", "description2", 'note',
+ 0, array('tag2'), null, null, false, false,
+ $uid2
+ );
+
+ $bookmarks = $bs->getBookmarks();
+ $this->assertEquals(1, $bookmarks['total']);
+ }
+
+ /*public function testSearchingBookmarksAccentsInsensible()
+ {
+ $bs = $this->bs;
+
+ $bs->addBookmark("http://site1.com", "title", "éèüaàê", "status", array('tag1'), null, false, false, 1);
+ $bookmarks = $bs->getBookmarks(0, NULL, NULL, NULL, $terms = "eeaae"); //void
+ $this->assertEquals(0, $bookmarks['total']);
+ $bookmarks = $bs->getBookmarks(0, NULL, NULL, NULL, $terms = "eeuaae");
+ $this->assertEquals(1, $bookmarks['total']);
+ }*/
+
+
+
+ /**
+ * Tests if bookmarkExists() returns false when the given
+ * parameter is invalid.
+ *
+ * @return void
+ */
+ public function testBookmarkExistsInvalidParam()
+ {
+ $this->assertFalse($this->bs->bookmarkExists(false));
+ $this->assertFalse($this->bs->bookmarkExists(null));
+ }
+
+
+
+ /**
+ * Tests if bookmarkExists() returns true when a bookmark
+ * exists
+ *
+ * @return void
+ */
+ public function testBookmarkExistsTrue()
+ {
+ $bid = $this->addBookmark();
+ $bookmark = $this->bs->getBookmark($bid);
+
+ $this->assertTrue($this->bs->bookmarkExists($bookmark['bAddress']));
+ }
+
+
+
+ /**
+ * Tests if bookmarkExists() returns false when a bookmark
+ * does not exist
+ *
+ * @return void
+ */
+ public function testBookmarkExistsFalse()
+ {
+ $this->assertFalse($this->bs->bookmarkExists('does-not-exist'));
+ }
+
+
+
+ /**
+ * Tests if bookmarkExists() returns true when a bookmark
+ * exists for a user
+ *
+ * @return void
+ */
+ public function testBookmarkExistsUserTrue()
+ {
+ $bid = $this->addBookmark();
+ $bookmark = $this->bs->getBookmark($bid);
+
+ $this->assertTrue(
+ $this->bs->bookmarkExists(
+ $bookmark['bAddress'],
+ $bookmark['uId']
+ )
+ );
+ }
+
+
+
+ /**
+ * Tests if bookmarkExists() returns false when a bookmark
+ * does not exist for a user
+ *
+ * @return void
+ */
+ public function testBookmarkExistsUserFalse()
+ {
+ $this->assertFalse(
+ $this->bs->bookmarkExists('does-not-exist', 1234)
+ );
+ }
+
+
+
+ /**
+ * Tests if bookmarkExists() returns false when a bookmark
+ * does not exist for a user but for another user
+ *
+ * @return void
+ */
+ public function testBookmarkExistsOtherUser()
+ {
+ $bid = $this->addBookmark();
+ $bookmark = $this->bs->getBookmark($bid);
+
+ $this->assertFalse(
+ $this->bs->bookmarkExists(
+ $bookmark['bAddress'],
+ $bookmark['uId'] + 1
+ )
+ );
+ }
+
+
+
+ /**
+ * Tests if bookmarksExist() returns true when a bookmark
+ * exists
+ *
+ * @return void
+ */
+ public function testBookmarksExistTrueSingle()
+ {
+ $bid = $this->addBookmark();
+ $bookmark = $this->bs->getBookmark($bid);
+
+ $ret = $this->bs->bookmarksExist(array($bookmark['bAddress']));
+ $this->assertInternalType('array', $ret);
+ $this->assertEquals(1, count($ret));
+ $this->assertTrue($ret[$bookmark['bAddress']]);
+ }
+
+
+
+ /**
+ * Tests if bookmarksExist() returns true when all bookmarks
+ * exist
+ *
+ * @return void
+ */
+ public function testBookmarksExistTrueMultiple()
+ {
+ $bid = $this->addBookmark();
+ $bookmark = $this->bs->getBookmark($bid);
+
+ $bid2 = $this->addBookmark();
+ $bookmark2 = $this->bs->getBookmark($bid2);
+
+
+ $ret = $this->bs->bookmarksExist(
+ array(
+ $bookmark['bAddress'],
+ $bookmark2['bAddress']
+ )
+ );
+ $this->assertInternalType('array', $ret);
+ $this->assertEquals(2, count($ret));
+ $this->assertTrue($ret[$bookmark['bAddress']]);
+ $this->assertTrue($ret[$bookmark2['bAddress']]);
+ }
+
+
+
+ /**
+ * Tests if bookmarksExist() returns false when a bookmark
+ * does not exist
+ *
+ * @return void
+ */
+ public function testBookmarksExistFalseSingle()
+ {
+ $ret = $this->bs->bookmarksExist(array('does-not-exist'));
+ $this->assertInternalType('array', $ret);
+ $this->assertEquals(1, count($ret));
+ $this->assertFalse($ret['does-not-exist']);
+ }
+
+
+
+ /**
+ * Tests if bookmarksExist() returns false when all bookmarks
+ * do not exist
+ *
+ * @return void
+ */
+ public function testBookmarksExistFalseMultiple()
+ {
+ $bms = array(
+ 'does-not-exist',
+ 'does-not-exist-2',
+ 'does-not-exist-3',
+ );
+ $ret = $this->bs->bookmarksExist($bms);
+ $this->assertInternalType('array', $ret);
+ $this->assertEquals(3, count($ret));
+ $this->assertFalse($ret['does-not-exist']);
+ $this->assertFalse($ret['does-not-exist-2']);
+ $this->assertFalse($ret['does-not-exist-3']);
+ }
+
+
+
+ /**
+ * Tests if bookmarksExist() returns true when some bookmarks
+ * exist.
+ *
+ * @return void
+ */
+ public function testBookmarksExistSome()
+ {
+ $bid = $this->addBookmark();
+ $bookmark = $this->bs->getBookmark($bid);
+
+ $bid2 = $this->addBookmark();
+ $bookmark2 = $this->bs->getBookmark($bid2);
+
+ //do not search for this one
+ $bid3 = $this->addBookmark();
+ $bookmark3 = $this->bs->getBookmark($bid3);
+
+
+ $ret = $this->bs->bookmarksExist(
+ array(
+ $bookmark['bAddress'],
+ 'does-not-exist',
+ $bookmark2['bAddress'],
+ 'does-not-exist-2',
+ 'does-not-exist-3'
+ )
+ );
+ $this->assertInternalType('array', $ret);
+ $this->assertEquals(5, count($ret));
+ $this->assertTrue($ret[$bookmark['bAddress']]);
+ $this->assertTrue($ret[$bookmark2['bAddress']]);
+ $this->assertFalse($ret['does-not-exist']);
+ $this->assertFalse($ret['does-not-exist-2']);
+ $this->assertFalse($ret['does-not-exist-3']);
+ }
+
+
+
+ /**
+ * Test if countBookmarks() works with no bookmarks
+ *
+ * @return void
+ */
+ public function testCountBookmarksNone()
+ {
+ $uid = $this->addUser();
+ $this->assertEquals(0, $this->bs->countBookmarks($uid));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'public'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'private'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'shared'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'all'));
+ }
+
+
+
+ /**
+ * Test if countBookmarks() works with one public bookmark
+ *
+ * @return void
+ */
+ public function testCountBookmarksOnePublic()
+ {
+ $uid = $this->addUser();
+ $this->addBookmark($uid);
+ $this->assertEquals(1, $this->bs->countBookmarks($uid));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'public'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'private'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'shared'));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'all'));
+ }
+
+
+
+ /**
+ * Test if countBookmarks() works with one private bookmark
+ *
+ * @return void
+ */
+ public function testCountBookmarksOnePrivate()
+ {
+ $uid = $this->addUser();
+ $this->bs->addBookmark(
+ 'http://test', 'test', 'desc', 'note',
+ 2,//private
+ array(), null, null, false, false, $uid
+ );
+ $this->assertEquals(0, $this->bs->countBookmarks($uid));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'public'));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'private'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'shared'));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'all'));
+ }
+
+
+
+ /**
+ * Test if countBookmarks() works with one shared bookmark
+ *
+ * @return void
+ */
+ public function testCountBookmarksOneShared()
+ {
+ $uid = $this->addUser();
+ $this->bs->addBookmark(
+ 'http://test', 'test', 'desc', 'note',
+ 1,//shared
+ array(), null, null, false, false, $uid
+ );
+ $this->assertEquals(0, $this->bs->countBookmarks($uid));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'public'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'private'));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'shared'));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'all'));
+ }
+
+
+
+ /**
+ * Check tag loading functionality of getBookmarks()
+ *
+ * @return void
+ */
+ public function testGetBookmarksIncludeTags()
+ {
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ $this->b2ts->attachTags($bid, array('foo', 'bar'));
+ $bid2 = $this->addBookmark($uid);
+ $this->b2ts->attachTags($bid2, array('fuu', 'baz'));
+
+ $bms = $this->bs->getBookmarks();
+ $this->assertEquals(2, count($bms['bookmarks']));
+ $this->assertEquals(2, $bms['total']);
+
+ foreach ($bms['bookmarks'] as $bm) {
+ $this->assertArrayHasKey('tags', $bm);
+ $this->assertInternalType('array', $bm['tags']);
+ if ($bm['bId'] == $bid) {
+ $this->assertContains('foo', $bm['tags']);
+ $this->assertContains('bar', $bm['tags']);
+ } else if ($bm['bId'] == $bid2) {
+ $this->assertContains('fuu', $bm['tags']);
+ $this->assertContains('baz', $bm['tags']);
+ } else {
+ $this->assertTrue(false, 'Unknown bookmark id');
+ }
+ }
+ }
+
+
+
+ /**
+ * Test if deleting a bookmark works.
+ *
+ * @return void
+ */
+ public function testDeleteBookmark()
+ {
+ $bookmarks = $this->bs->getBookmarks();
+ $this->assertEquals(0, $bookmarks['total']);
+
+ $bid = $this->addBookmark();
+ $bookmarks = $this->bs->getBookmarks();
+ $this->assertEquals(1, $bookmarks['total']);
+
+ $bid2 = $this->addBookmark();
+ $bookmarks = $this->bs->getBookmarks();
+ $this->assertEquals(2, $bookmarks['total']);
+
+ $this->assertTrue($this->bs->deleteBookmark($bid));
+ $bookmarks = $this->bs->getBookmarks();
+ $this->assertEquals(1, $bookmarks['total']);
+
+ $this->assertTrue($this->bs->deleteBookmark($bid2));
+ $bookmarks = $this->bs->getBookmarks();
+ $this->assertEquals(0, $bookmarks['total']);
+ }
+
+
+
+ /**
+ * Test if deleting all bookmarks for a user works.
+ *
+ * @return void
+ */
+ public function testDeleteBookmarksForUser()
+ {
+ $uid = $this->addUser();
+ $bookmarks = $this->bs->getBookmarks(0, null, $uid);
+ $this->assertEquals(0, $bookmarks['total']);
+
+ $this->addBookmark($uid);
+ $this->addBookmark($uid);
+ $bookmarks = $this->bs->getBookmarks(0, null, $uid);
+ $this->assertEquals(2, $bookmarks['total']);
+
+ $this->bs->deleteBookmarksForUser($uid);
+ $bookmarks = $this->bs->getBookmarks(0, null, $uid);
+ $this->assertEquals(0, $bookmarks['total']);
+ }
+
+
+
+ /**
+ * Test if deleting all bookmarks for a user works
+ * and does not damage other user's bookmarks.
+ *
+ * @return void
+ */
+ public function testDeleteBookmarksForUserOthers()
+ {
+ $uidOther = $this->addUser();
+ $this->addBookmark($uidOther);
+
+ $uid = $this->addUser();
+ $bookmarks = $this->bs->getBookmarks(0, null, $uid);
+ $this->assertEquals(0, $bookmarks['total']);
+
+ $this->addBookmark($uid);
+ $this->addBookmark($uid);
+ $bookmarks = $this->bs->getBookmarks(0, null, $uid);
+ $this->assertEquals(2, $bookmarks['total']);
+
+ $this->bs->deleteBookmarksForUser($uid);
+ $bookmarks = $this->bs->getBookmarks(0, null, $uid);
+ $this->assertEquals(0, $bookmarks['total']);
+
+ $bookmarks = $this->bs->getBookmarks(0, null, $uidOther);
+ $this->assertEquals(1, $bookmarks['total']);
+ }
+
+
+
+ /**
+ * Test if deleting a bookmark with a vote works.
+ *
+ * @return void
+ */
+ public function testDeleteBookmarkWithVote()
+ {
+ $GLOBALS['enableVoting'] = true;
+
+ $uid = $this->addUser();
+ $bid = $this->addBookmark();
+
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, $uid, 1);
+ $this->assertTrue($this->vs->hasVoted($bid, $uid));
+
+ $bid2 = $this->addBookmark();
+ $this->vs->vote($bid2, $uid, 1);
+ $this->assertTrue($this->vs->hasVoted($bid2, $uid));
+
+ $this->assertTrue($this->bs->deleteBookmark($bid));
+ $this->assertFalse($this->vs->hasVoted($bid, $uid));
+ $this->assertTrue($this->vs->hasVoted($bid2, $uid));
+ }
+
+
+
+ /**
+ * Test if editAllowed() returns false when the bookmark
+ * id is invalid.
+ *
+ * @return void
+ */
+ public function testEditAllowedInvalidBookmarkId()
+ {
+ $this->assertFalse($this->bs->editAllowed('invalid'));
+ $this->assertFalse($this->bs->editAllowed(array()));
+ $this->assertFalse($this->bs->editAllowed(array('some', 'where')));
+ $this->assertFalse($this->bs->editAllowed(array('bId' => false)));
+ $this->assertFalse($this->bs->editAllowed(array('bId' => 'foo')));
+ }
+
+
+
+ /**
+ * Test if editAllowed() works when passing the ID of
+ * an existing bookmark.
+ *
+ * @return void
+ */
+ public function testEditAllowedBookmarkId()
+ {
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ $this->us->setCurrentUserId($uid);
+ $this->assertTrue($this->bs->editAllowed($bid));
+ }
+
+
+
+ /**
+ * Test if editAllowed() works when passing the ID of
+ * an existing bookmark that does not belong to the current
+ * user.
+ *
+ * @return void
+ */
+ public function testEditAllowedBookmarkIdNotOwn()
+ {
+ $uid = $this->addUser();
+ $bid = $this->addBookmark();
+ $this->us->setCurrentUserId($uid);
+ $this->assertFalse($this->bs->editAllowed($bid));
+ }
+
+
+
+ /**
+ * Test if editAllowed() works when passing the ID of
+ * an existing bookmark that does not belong to the current
+ * user.
+ *
+ * @return void
+ */
+ public function testEditAllowedBookmarkIdNoUser()
+ {
+ $bid = $this->addBookmark();
+ $this->us->setCurrentUserId(null);
+ $this->assertFalse($this->bs->editAllowed($bid));
+ }
+
+
+
+ /**
+ * Test if editAllowed() works when passing a bookmark
+ * row.
+ *
+ * @return void
+ */
+ public function testEditAllowedBookmarkRow()
+ {
+ $uid = $this->addUser();
+ $this->us->setCurrentUserId($uid);
+
+ $bid = $this->addBookmark($uid);
+ $bookmark = $this->bs->getBookmark($bid);
+ $this->assertTrue($this->bs->editAllowed($bookmark));
+ }
+
+
+
+ /**
+ * Test if editAllowed() returns false when the bookmark
+ * specified by the ID does not exist.
+ *
+ * @return void
+ */
+ public function testEditAllowedIdNotFound()
+ {
+ $this->assertFalse($this->bs->editAllowed(98765));
+ }
+
+
+
+ /**
+ * Test if editAllowed() works when the user is an administrator.
+ *
+ * @return void
+ */
+ public function testEditAllowedBookmarkAdmin()
+ {
+ //make the user admin
+ $uid = $this->addUser();
+ $user = $this->us->getUser($uid);
+ $GLOBALS['admin_users'][] = $user['username'];
+
+ $bid = $this->addBookmark($uid);
+ $this->us->setCurrentUserId($uid);
+ $this->assertTrue($this->bs->editAllowed($bid));
+ }
+
+
+
+ /**
+ * Verify that getBookmark() returns false when the
+ * bookmark cannot be found.
+ *
+ * @return void
+ */
+ public function testGetBookmarkNotFound()
+ {
+ $this->assertFalse($this->bs->getBookmark(987654));
+ }
+
+
+
+ /**
+ * Verify that getBookmark() returns false when the
+ * bookmark ID is not numeric
+ *
+ * @return void
+ */
+ public function testGetBookmarkInvalidParam()
+ {
+ $this->assertFalse($this->bs->getBookmark('foo'));
+ }
+
+
+
+ /**
+ * Check tag loading functionality of getBookmark()
+ *
+ * @return void
+ */
+ public function testGetBookmarkIncludeTags()
+ {
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ $this->b2ts->attachTags($bid, array('foo', 'bar'));
+ $bid2 = $this->addBookmark($uid);
+ $this->b2ts->attachTags($bid2, array('fuu', 'baz'));
+
+ $bm = $this->bs->getBookmark($bid, true);
+ $this->assertArrayHasKey('tags', $bm);
+ $this->assertInternalType('array', $bm['tags']);
+ $this->assertContains('foo', $bm['tags']);
+ $this->assertContains('bar', $bm['tags']);
+ }
+
+
+
+ /**
+ * Verify that getBookmark() does not include user voting
+ * data when no user is logged on.
+ *
+ * @return void
+ */
+ public function testGetBookmarkUserVotingNoUser()
+ {
+ $GLOBALS['enableVoting'] = true;
+
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ //no user
+ $this->us->setCurrentUserId(null);
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertArrayNotHasKey('hasVoted', $bm);
+ $this->assertArrayNotHasKey('vote', $bm);
+ }
+
+
+
+ /**
+ * Verify that getBookmark() automatically includes
+ * voting data of the currently logged on user,
+ * even if he did not vote yet.
+ *
+ * @return void
+ */
+ public function testGetBookmarkUserVotingWithUserNoVote()
+ {
+ $GLOBALS['enableVoting'] = true;
+
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ //log user in
+ $this->us->setCurrentUserId($uid);
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertArrayHasKey('hasVoted', $bm);
+ $this->assertArrayHasKey('vote', $bm);
+ $this->assertEquals(0, $bm['hasVoted']);
+ $this->assertEquals(null, $bm['vote']);
+ }
+
+
+
+ /**
+ * Verify that getBookmark() automatically includes
+ * voting data of the currently logged on user
+ * when he voted positive.
+ *
+ * @return void
+ */
+ public function testGetBookmarkUserVotingWithUserPositiveVote()
+ {
+ $GLOBALS['enableVoting'] = true;
+
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ //log user in
+ $this->us->setCurrentUserId($uid);
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertArrayHasKey('hasVoted', $bm);
+ $this->assertArrayHasKey('vote', $bm);
+ $this->assertEquals(1, $bm['hasVoted']);
+ $this->assertEquals(1, $bm['vote']);
+ }
+
+
+
+ /**
+ * Verify that getBookmark() automatically includes
+ * voting data of the currently logged on user
+ * when he voted positive.
+ *
+ * @return void
+ */
+ public function testGetBookmarkUserVotingWithUserNegativeVote()
+ {
+ $GLOBALS['enableVoting'] = true;
+
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid);
+ //log user in
+ $this->us->setCurrentUserId($uid);
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertArrayHasKey('hasVoted', $bm);
+ $this->assertArrayHasKey('vote', $bm);
+ $this->assertEquals(1, $bm['hasVoted']);
+ $this->assertEquals(-1, $bm['vote']);
+ }
+
+
+
+ /**
+ * Tests if getBookmarkByAddress() works correctly.
+ *
+ * @return void
+ */
+ public function testGetBookmarkByAddress()
+ {
+ $url = 'http://example.org';
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid, $url);
+
+ $bm = $this->bs->getBookmarkByAddress($url);
+ $this->assertInternalType('array', $bm);
+ $this->assertEquals($url, $bm['bAddress']);
+ }
+
+
+
+ /**
+ * Tests if getBookmarkByAddress() works correctly with aliases.
+ * When passing an incomplete address i.e. without protocol,
+ * the full URL needs to be searched for.
+ *
+ * The failure of this test lead to #2953732.
+ *
+ * @return void
+ *
+ * @link https://sourceforge.net/tracker/?func=detail&atid=1017430&aid=2953732&group_id=211356
+ */
+ public function testGetBookmarkByAddressAlias()
+ {
+ $url = 'http://example.org';
+ $incomplete = 'example.org';
+
+ $uid = $this->addUser();
+ $bid = $this->addBookmark($uid, $url);
+
+ $bm = $this->bs->getBookmarkByAddress($incomplete);
+ $this->assertInternalType('array', $bm);
+ $this->assertEquals($url, $bm['bAddress']);
+ }
+
+
+
+ public function testNormalize()
+ {
+ $this->assertEquals(
+ 'http://example.org', $this->bs->normalize('http://example.org')
+ );
+ $this->assertEquals(
+ 'ftp://example.org', $this->bs->normalize('ftp://example.org')
+ );
+ $this->assertEquals(
+ 'http://example.org', $this->bs->normalize('http://example.org/')
+ );
+ $this->assertEquals(
+ 'http://example.org', $this->bs->normalize('example.org')
+ );
+ $this->assertEquals(
+ 'mailto:foo@example.org',
+ $this->bs->normalize('mailto:foo@example.org')
+ );
+ }
+
+
+
+ /**
+ * test if updating an existing bookmark works
+ */
+ public function testUpdateBookmark()
+ {
+ $bid = $this->addBookmark();
+ $this->assertTrue(
+ $this->bs->updateBookmark(
+ $bid,
+ 'http://example.org/foo',
+ 'my new title',
+ 'new description',
+ 'new private note',
+ 1,
+ array('new')
+ )
+ );
+ $bm = $this->bs->getBookmark($bid, true);
+ $this->assertEquals('http://example.org/foo', $bm['bAddress']);
+ $this->assertEquals('my new title', $bm['bTitle']);
+ $this->assertEquals('new description', $bm['bDescription']);
+ $this->assertEquals('new private note', $bm['bPrivateNote']);
+ $this->assertEquals(1, $bm['bStatus']);
+ $this->assertInternalType('array', $bm['tags']);
+ $this->assertEquals(1, count($bm['tags']));
+ $this->assertContains('new', $bm['tags']);
+ }
+
+ /**
+ * Tests if updating a bookmark's short url name
+ * saves it in the database.
+ *
+ * @return void
+ */
+ public function testUpdateBookmarkShort()
+ {
+ $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'
+ )
+ );
+ $bm = $this->bs->getBookmark($bid);
+ $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']);
+ }
+
+
+
+ /**
+ * Test what countOther() returns when the address does not exist
+ *
+ * @return void
+ */
+ public function testCountOthersAddressDoesNotExist()
+ {
+ $this->assertEquals(0, $this->bs->countOthers('http://example.org'));
+ }
+
+
+
+ /**
+ * Test what countOther() returns when nobody else has the same bookmark
+ *
+ * @return void
+ */
+ public function testCountOthersNone()
+ {
+ $uid = $this->addUser();
+ $address = 'http://example.org';
+ $this->addBookmark($uid, $address);
+ $this->assertEquals(0, $this->bs->countOthers($address));
+ }
+
+
+
+ /**
+ * Test what countOther() returns when the address exists only once
+ * and multiple bookmarks are in the database.
+ *
+ * @return void
+ */
+ public function testCountOthersMultipleNone()
+ {
+ $uid = $this->addUser();
+ $address = 'http://example.org';
+ $this->addBookmark($uid, $address);
+ $this->addBookmark($uid);
+ $this->addBookmark($uid);
+ $this->assertEquals(0, $this->bs->countOthers($address));
+ }
+
+
+
+ /**
+ * Test what countOther() returns when the address exists only once
+ * and the same user and other users have other bookmarks
+ *
+ * @return void
+ */
+ public function testCountOthersMultipleUsersNone()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+ $address = 'http://example.org';
+ $this->addBookmark($uid, $address);
+ $this->addBookmark($uid);
+ $this->addBookmark($uid2);
+ $this->assertEquals(0, $this->bs->countOthers($address));
+ }
+
+
+
+ /**
+ * Test what countOther() returns when the address exists two
+ * times in the database.
+ *
+ * @return void
+ */
+ public function testCountOthersOne()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+ $address = 'http://example.org';
+ $this->addBookmark($uid, $address);
+ $this->addBookmark($uid2, $address);
+ $this->assertEquals(1, $this->bs->countOthers($address));
+ }
+
+
+
+ /**
+ * Test what countOther() returns when the address exists four
+ * times in the database.
+ *
+ * @return void
+ */
+ public function testCountOthersThree()
+ {
+ $uid = $this->addUser();
+ $address = 'http://example.org';
+ $this->addBookmark($uid, $address);
+ $this->addBookmark(null, $address);
+ $this->addBookmark(null, $address);
+ $this->addBookmark(null, $address);
+ $this->assertEquals(3, $this->bs->countOthers($address));
+ }
+
+
+
+ /**
+ * 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
+ * and shared the same address.
+ *
+ * @return void
+ */
+ public function testCountOthersWatchlistComplex()
+ {
+ $uid = $this->addUser();
+ $address = 'http://example.org';
+ //log user in
+ $this->us->setCurrentUserId($uid);
+
+ //setup users
+ $otherPublic1 = $this->addUser();
+ $otherPublic2 = $this->addUser();
+ $otherShared1 = $this->addUser();
+ $otherPrivate1 = $this->addUser();
+ $friendPublic1 = $this->addUser();
+ $friendShared1 = $this->addUser();
+ $friendShared2 = $this->addUser();
+ $friendPrivate1 = $this->addUser();
+ $friendSharing1 = $this->addUser();
+
+ //setup watchlists
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $this->us->setCurrentUserId($friendPublic1);
+ $us->setWatchStatus($uid);
+ $this->us->setCurrentUserId($friendShared1);
+ $us->setWatchStatus($uid);
+ $this->us->setCurrentUserId($friendShared2);
+ $us->setWatchStatus($uid);
+ $this->us->setCurrentUserId($friendPrivate1);
+ $us->setWatchStatus($uid);
+
+ //back to login of main user
+ $this->us->setCurrentUserId($uid);
+ $us->setWatchStatus($friendSharing1);
+
+ //add bookmarks
+ $this->addBookmark($uid, $address, 0);
+ $this->addBookmark($otherPublic1, $address, 0);
+ $this->addBookmark($otherPublic2, $address, 0);
+ $this->addBookmark($otherShared1, $address, 1);
+ $this->addBookmark($otherPrivate1, $address, 2);
+ $this->addBookmark($friendPublic1, $address, 0);
+ $this->addBookmark($friendShared1, $address, 1);
+ $this->addBookmark($friendShared2, $address, 1);
+ $this->addBookmark($friendPrivate1, $address, 2);
+ //this user is on our watchlist, but we not on his
+ $this->addBookmark($friendSharing1, $address, 1);
+
+ //2 public
+ //1 public (friend)
+ //2 shared
+ //-> 5
+ $this->assertEquals(5, $this->bs->countOthers($address));
+ }
+
+
+
+ /**
+ * Test what countOther() returns when multiple addresses are
+ * passed to it and none of them exists.
+ *
+ * @return void
+ */
+ public function testCountOthersArrayNone()
+ {
+ $this->assertEquals(
+ array('1' => 0, '2' => 0, '3' => 0),
+ $this->bs->countOthers(array('1', '2', '3'))
+ );
+ }
+
+
+
+ /**
+ * Test what countOther() returns when multiple addresses are
+ * passed to it and only one of them exists.
+ *
+ * @return void
+ */
+ public function testCountOthersArrayOneNone()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+ $address1 = 'http://example.org/1';
+ $address2 = 'http://example.org/2';
+ $this->addBookmark($uid, $address1);
+ $this->addBookmark($uid, $address2);
+ $this->addBookmark($uid2, $address1);
+ $this->assertEquals(
+ array(
+ $address1 => 1,
+ $address2 => 0
+ ),
+ $this->bs->countOthers(
+ array($address1, $address2)
+ )
+ );
+ }
+
+
+
+ /**
+ * Test what countOther() returns when multiple addresses are passed
+ * to it and both of them exist with different numbers for each.
+ *
+ * @return void
+ */
+ public function testCountOthersArrayTwoOne()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+ $uid3 = $this->addUser();
+
+ $address1 = 'http://example.org/1';
+ $address2 = 'http://example.org/2';
+
+ $this->addBookmark($uid, $address1);
+ $this->addBookmark($uid, $address2);
+
+ $this->addBookmark($uid2, $address1);
+ $this->addBookmark($uid2, $address2);
+
+ $this->addBookmark($uid3, $address1);
+
+ $this->assertEquals(
+ array(
+ $address1 => 2,
+ $address2 => 1
+ ),
+ $this->bs->countOthers(
+ array($address1, $address2)
+ )
+ );
+ }
+
+
+
+ /**
+ * Test private bookmarks
+ *
+ * @return void
+ */
+ public function testPrivateBookmarks()
+ {
+ $uid = $this->addUser();
+ /* create private bookmark */
+ $this->bs->addBookmark(
+ 'http://test', 'test', 'desc', 'note',
+ 2,//private
+ array(), null, null, false, false, $uid
+ );
+ /* create public bookmark */
+ $this->bs->addBookmark(
+ 'http://example.org', 'title', 'desc', 'priv',
+ 0,//public
+ array(), null, null, false, false, $uid
+ );
+
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'public'));
+ $this->assertEquals(1, $this->bs->countBookmarks($uid, 'private'));
+ $this->assertEquals(0, $this->bs->countBookmarks($uid, 'shared'));
+ $this->assertEquals(2, $this->bs->countBookmarks($uid, 'all'));
+
+ $this->us->setCurrentUserId($uid);
+ $bookmarks = $this->bs->getBookmarks();
+ // first record should be private bookmark
+ $b0 = $bookmarks['bookmarks'][0];
+ $this->assertEquals('test', $b0['bTitle']);
+ // second record should be public bookmark
+ $b0 = $bookmarks['bookmarks'][1];
+ $this->assertEquals('title', $b0['bTitle']);
+
+ // test non authenticated query
+ $this->us->setCurrentUserId(null);
+ $bookmarks = $this->bs->getBookmarks();
+ // should only result in one link - public
+ $b2 = $bookmarks['bookmarks'][0];
+ $this->assertEquals('title', $b2['bTitle']);
+ // there should be no second record
+ $this->assertEquals(1,count($bookmarks['bookmarks']));
+
+ }
+
+}
+?>
diff --git a/tests/CommonDescriptionTest.php b/tests/CommonDescriptionTest.php
new file mode 100644
index 0000000..7748cb3
--- /dev/null
+++ b/tests/CommonDescriptionTest.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle common description service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class CommonDescriptionTest extends TestBase
+{
+ protected $us;
+ protected $bs;
+ protected $b2ts;
+ protected $tts;
+ protected $tsts;
+ protected $cds;
+
+
+ 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->cds =SemanticScuttle_Service_Factory::get('CommonDescription');
+ $this->cds->deleteAll();
+ }
+
+ public function testModifyDescription()
+ {
+ $cds = $this->cds;
+
+ $uId1 = 1;
+ $uId2 = 2;
+ $title1 = "title1";
+ $title2 = "title2";
+ $desc1 = "&é\"'(-è_çà)=´~#'#{{[\\\\[||`\^\^@^@}¹²¡×¿ ?./§µ%";
+ $desc2 = "æâ€êþÿûîîôôöŀï'üð’‘ßä«≤»©»  ↓¿×÷¡¹²³";
+ $time1 = time();
+ $time2 = time()+200;
+
+ $tagDesc1 = array('cdId'=>1, 'tag'=>'taghouse', 'cdDescription'=>$desc1, 'uId'=>$uId1,'cdDatetime'=>$time1);
+ $tagDesc2 = array('cdId'=>2, 'tag'=>'taghouse', 'cdDescription'=>$desc2, 'uId'=>$uId2,'cdDatetime'=>$time2);
+
+ $cds->addTagDescription('taghouse', $desc1, $uId1, $time1);
+ $cds->addTagDescription('taghouse', $desc2, $uId2, $time2);
+
+ $desc = $cds->getLastTagDescription('taghouse');
+ $this->assertContains('taghouse', $desc);
+ $this->assertContains($desc2, $desc);
+ $this->assertContains(gmdate('Y-m-d H:i:s', $time2), $desc);
+
+ $desc = $cds->getAllTagsDescription('taghouse');
+ $this->assertContains($desc1, $desc[1]);
+ $this->assertContains(gmdate('Y-m-d H:i:s', $time1), $desc[1]);
+ $this->assertContains($desc2, $desc[0]);
+ $this->assertContains(gmdate('Y-m-d H:i:s', $time2), $desc[0]);
+
+ $desc = $cds->getDescriptionById(1);
+ $this->assertContains($desc1, $desc);
+
+ $bkDesc1 = array('cdId'=>3, 'bHash'=>'10', 'cdTitle'=>$title1, 'cdDescription'=>$desc1, 'uId'=>$uId1,'cdDatetime'=>$time1);
+ $bkDesc2 = array('cdId'=>4, 'bHash'=>'10', 'cdTitle'=>$title2, 'cdDescription'=>$desc2, 'uId'=>$uId2,'cdDatetime'=>$time2);
+
+ $cds->addBookmarkDescription(10, $title1, $desc1, $uId1, $time1);
+ $cds->addBookmarkDescription(10, $title2, $desc2, $uId2, $time2);
+
+ $desc = $cds->getLastBookmarkDescription(10);
+ $this->assertContains($title2, $desc);
+ $this->assertContains($desc2, $desc);
+ $this->assertContains(gmdate('Y-m-d H:i:s', $time2), $desc);
+
+ $desc = $cds->getAllBookmarksDescription(10);
+ $this->assertContains($title1, $desc[1]);
+ $this->assertContains($desc1, $desc[1]);
+ $this->assertContains(gmdate('Y-m-d H:i:s', $time1), $desc[1]);
+ $this->assertContains($title2, $desc[0]);
+ $this->assertContains($desc2, $desc[0]);
+ $this->assertContains(gmdate('Y-m-d H:i:s', $time2), $desc[0]);
+
+ $desc = $cds->getDescriptionById(3);
+ $this->assertContains($desc1, $desc);
+
+
+ }
+
+}
+?>
diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php
new file mode 100644
index 0000000..980e92e
--- /dev/null
+++ b/tests/FactoryTest.php
@@ -0,0 +1,13 @@
+<?php
+
+class FactoryTest extends TestBase
+{
+ public function testGetDb()
+ {
+ $this->assertInstanceOf(
+ 'sql_db',
+ SemanticScuttle_Service_Factory::getDb()
+ );
+ }
+}
+?> \ No newline at end of file
diff --git a/tests/LAUNCH_TESTS b/tests/LAUNCH_TESTS
new file mode 100644
index 0000000..7ee9227
--- /dev/null
+++ b/tests/LAUNCH_TESTS
@@ -0,0 +1,28 @@
+Running SemanticScuttle unit tests
+==================================
+
+Prerequisites
+-------------
+The unit tests use PHPUnit.
+For installation instructions, see http://phpunit.de/
+
+
+Warning
+-------
+The unit tests are DESTRUCTIBLE! Never ever run them on your
+normal SemanticScuttle database! Always use a different database
+for testing - whole tables are emptied and re-filled during the
+tests, and you will definitely lose all data in there.
+
+
+Running the tests
+-----------------
+To run a single test class, execute it like a normal php script:
+ php tests/BookmarkTest.php
+
+To run *all* the tests, run
+ php tests/AllTests.php
+
+If you want features like code coverage, you need to use
+the phpunit executable:
+ phpunit --coverage-html /tmp/sc-coverage/ tests/AllTests.php
diff --git a/tests/Model/BookmarkTest.php b/tests/Model/BookmarkTest.php
new file mode 100644
index 0000000..9f55143
--- /dev/null
+++ b/tests/Model/BookmarkTest.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle Bookmark model
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Model_BookmarkTest extends TestBase
+{
+ public function testIsValidUrlValid()
+ {
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'http://example.org/foo/bar?baz=foorina'
+ )
+ );
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'https://example.org/'
+ )
+ );
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'ftp://user:pass@example.org/'
+ )
+ );
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'mailto:cweiske@example.org'
+ )
+ );
+ }
+
+ public function testIsValidUrlInvalid()
+ {
+ $this->assertFalse(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'javascript:alert("foo")'
+ )
+ );
+ $this->assertFalse(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'foo://example.org/foo/bar'
+ )
+ );
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/tests/SearchHistoryTest.php b/tests/SearchHistoryTest.php
new file mode 100644
index 0000000..f05dd29
--- /dev/null
+++ b/tests/SearchHistoryTest.php
@@ -0,0 +1,373 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle search history service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SearchHistoryTest extends TestBase
+{
+ protected $us;
+ protected $bs;
+ protected $b2ts;
+ protected $tts;
+ protected $shs;
+
+
+ /**
+ * 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();
+ }
+
+ /**
+ * Tests if adding searches to the database works
+ *
+ * @covers SemanticScuttle_Service_SearchHistory::addSearch
+ */
+ 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
+ *
+ * @covers SemanticScuttle_Service_SearchHistory::addSearch
+ */
+ public function testAddSearchNoTerms()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->assertFalse(
+ $this->shs->addSearch('', 'all', 0)
+ );
+ $this->assertEquals(0, $this->shs->countSearches());
+ }
+
+ /**
+ * Tests if adding a search deletes the history if it is too
+ * large.
+ *
+ * @covers SemanticScuttle_Service_SearchHistory::addSearch
+ */
+ public function testAddSearchDeleteHistory()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->sizeSearchHistory = 5;
+ $this->shs->addSearch('eins', 'all', 1);
+ $this->shs->addSearch('zwei', 'all', 1);
+ $this->shs->addSearch('drei', 'all', 1);
+ $this->shs->addSearch('view', 'all', 1);
+ $this->shs->addSearch('fünf', 'all', 1);
+ $this->assertEquals(5, $this->shs->countSearches());
+
+ $this->shs->addSearch('sechs', 'all', 1);
+ $this->assertEquals(5, $this->shs->countSearches());
+
+ $this->shs->sizeSearchHistory = 6;
+ $this->shs->addSearch('sieben', 'all', 1);
+ $this->assertEquals(6, $this->shs->countSearches());
+ $this->shs->addSearch('acht', 'all', 1);
+ $this->assertEquals(6, $this->shs->countSearches());
+ }
+
+ /**
+ * Test getAllSearches() without any parameters
+ */
+ public function testGetAllSearches()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1);
+ $this->shs->addSearch('zwei', 'all', 1);
+ $this->shs->addSearch('drei', 'all', 1);
+
+ $rows = $this->shs->getAllSearches();
+ $this->assertEquals(3, count($rows));
+
+ $terms = array();
+ foreach ($rows as $row) {
+ $terms[] = $row['shTerms'];
+ }
+ sort($terms);
+ $this->assertEquals(
+ array('drei', 'eins', 'zwei'),
+ $terms
+ );
+ }
+
+ /**
+ * Test getAllSearches() return value row array keys.
+ */
+ public function testGetAllSearchesTypes()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1);
+
+ $rows = $this->shs->getAllSearches();
+ $this->assertEquals(1, count($rows));
+ $row = reset($rows);
+
+ $this->assertArrayHasKey('shTerms', $row);
+ $this->assertArrayHasKey('shId', $row);
+ $this->assertArrayHasKey('shRange', $row);
+ $this->assertArrayHasKey('shNbResults', $row);
+ $this->assertArrayHasKey('shDatetime', $row);
+ $this->assertArrayHasKey('uId', $row);
+ }
+
+ /**
+ * Test getAllSearches() range parameter
+ */
+ public function testGetAllSearchesRange()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1);
+ $this->shs->addSearch('zwei', 'watchlist', 1);
+ $this->shs->addSearch('drei', 'watchlist', 1);
+ $this->shs->addSearch('vier', 'user1', 1);
+ $this->shs->addSearch('fünf', 'user2', 1);
+
+ $rows = $this->shs->getAllSearches('all');
+ $this->assertEquals(1, count($rows));
+
+ $rows = $this->shs->getAllSearches('watchlist');
+ $this->assertEquals(2, count($rows));
+
+ $rows = $this->shs->getAllSearches('user0');
+ $this->assertEquals(0, count($rows));
+
+ $rows = $this->shs->getAllSearches('user1');
+ $this->assertEquals(1, count($rows));
+ $this->assertEquals('vier', $rows[0]['shTerms']);
+ }
+
+ /**
+ * Test getAllSearches() uId parameter
+ */
+ public function testGetAllSearchesUid()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1, 0);
+ $this->shs->addSearch('zwei', 'all', 1, 0);
+ $this->shs->addSearch('drei', 'all', 1, 1);
+
+ $rows = $this->shs->getAllSearches(null, null);
+ $this->assertEquals(3, count($rows));
+
+ $rows = $this->shs->getAllSearches(null, 1);
+ $this->assertEquals(1, count($rows));
+ $this->assertEquals('drei', $rows[0]['shTerms']);
+ }
+
+ /**
+ * Test getAllSearches() number parameter
+ */
+ public function testGetAllSearchesNb()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1, 0);
+ $this->shs->addSearch('zwei', 'all', 1, 0);
+ $this->shs->addSearch('drei', 'all', 1, 1);
+
+ $rows = $this->shs->getAllSearches(null, null, 1);
+ $this->assertEquals(1, count($rows));
+
+ $rows = $this->shs->getAllSearches(null, null, 2);
+ $this->assertEquals(2, count($rows));
+
+ $rows = $this->shs->getAllSearches(null, null, 3);
+ $this->assertEquals(3, count($rows));
+
+ $rows = $this->shs->getAllSearches(null, null, 4);
+ $this->assertEquals(3, count($rows));
+ }
+
+ /**
+ * Test getAllSearches() paging start parameter
+ */
+ public function testGetAllSearchesStart()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1, 0);
+ $this->shs->addSearch('zwei', 'all', 1, 0);
+ $this->shs->addSearch('drei', 'all', 1, 1);
+
+ $rows = $this->shs->getAllSearches(null, null, 1, 0);
+ $this->assertEquals(1, count($rows));
+ $this->assertEquals('drei', $rows[0]['shTerms']);
+
+ $rows = $this->shs->getAllSearches(null, null, 1, 1);
+ $this->assertEquals(1, count($rows));
+ $this->assertEquals('zwei', $rows[0]['shTerms']);
+
+ $rows = $this->shs->getAllSearches(null, null, 3, 2);
+ $this->assertEquals(1, count($rows));
+ $this->assertEquals('eins', $rows[0]['shTerms']);
+ }
+
+ /**
+ * Test getAllSearches() distinct parameter
+ */
+ public function testGetAllSearchesDistinct()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1);
+ $this->shs->addSearch('eins', 'all', 1);
+ $this->shs->addSearch('drei', 'all', 1);
+
+ $rows = $this->shs->getAllSearches(null, null, null, null, false);
+ $this->assertEquals(3, count($rows));
+
+ $rows = $this->shs->getAllSearches(null, null, null, null, true);
+ $this->assertEquals(2, count($rows));
+ }
+
+ /**
+ * Test getAllSearches() withResults parameter
+ */
+ public function testGetAllSearchesWithResults()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 0);
+ $this->shs->addSearch('zwei', 'all', 0);
+ $this->shs->addSearch('drei', 'all', 1);
+
+ $rows = $this->shs->getAllSearches(null, null, null, null, false, false);
+ $this->assertEquals(3, count($rows));
+
+ $rows = $this->shs->getAllSearches(null, null, null, null, false, true);
+ $this->assertEquals(1, count($rows));
+ }
+
+ /**
+ * Deleting the oldest search without any historical searches
+ *
+ * @covers SemanticScuttle_Service_SearchHistory::deleteOldestSearch
+ */
+ 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
+ *
+ * @covers SemanticScuttle_Service_SearchHistory::deleteOldestSearch
+ */
+ 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();
+
+ $this->assertEquals(1, $this->shs->countSearches());
+
+ $rows = $this->shs->getAllSearches();
+ $this->assertEquals(1, count($rows));
+ $this->assertEquals(
+ $highestId,
+ $rows[0]['shId']
+ );
+ }
+
+ /**
+ * Test if deleting the search history for a certain user works
+ */
+ public function testDeleteSearchHistoryForUser()
+ {
+ $this->assertEquals(0, $this->shs->countSearches());
+
+ $this->shs->addSearch('eins', 'all', 1, 0);
+ $this->shs->addSearch('zwei', 'all', 1, 22);
+ $this->shs->addSearch('drei', 'all', 1, 1);
+ $this->shs->addSearch('vier', 'all', 1, 22);
+
+ $this->shs->deleteSearchHistoryForUser(22);
+ $this->assertEquals(2, $this->shs->countSearches());
+
+ $this->shs->deleteSearchHistoryForUser(20);
+ $this->assertEquals(2, $this->shs->countSearches());
+
+ $this->shs->deleteSearchHistoryForUser(1);
+ $this->assertEquals(1, $this->shs->countSearches());
+ }
+
+
+ /**
+ * Test deleting all of the search history
+ */
+ public function testDeleteAll()
+ {
+ $this->shs->addSearch('testsearchterm1', 'all', 0);
+ $this->shs->addSearch('testsearchterm2', 'all', 0);
+ $this->shs->deleteAll();
+ $this->assertEquals(0, $this->shs->countSearches());
+ }
+}
+?>
diff --git a/tests/SemanticScuttle/ConfigTest.php b/tests/SemanticScuttle/ConfigTest.php
new file mode 100644
index 0000000..670f82a
--- /dev/null
+++ b/tests/SemanticScuttle/ConfigTest.php
@@ -0,0 +1,206 @@
+<?php
+//that's PEAR's Stream_Var package
+require_once 'Stream/Var.php';
+
+class SemanticScuttle_ConfigTest_StreamVar extends Stream_Var {
+ public function url_stat($path, $flags)
+ {
+ $url = parse_url($path);
+
+ $scope = $url['host'];
+ if (isset($url['path'])) {
+ $varpath = substr($url['path'], 1);
+ } else {
+ $varpath = '';
+ }
+
+ if (!$this->_setPointer($scope, $varpath)) {
+ return false;
+ }
+
+ return parent::url_stat($path, $flags);
+ }
+}
+
+class SemanticScuttle_ConfigTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Configuration object to test
+ */
+ protected $cfg;
+
+
+ public function setUpWrapper()
+ {
+ if (!in_array('unittest', stream_get_wrappers())) {
+ stream_wrapper_register(
+ 'unittest', 'SemanticScuttle_ConfigTest_StreamVar'
+ );
+ }
+
+ $this->cfg = $this->getMock(
+ 'SemanticScuttle_Config',
+ array('getDataDir')
+ );
+ $this->cfg->expects($this->once())
+ ->method('getDataDir')
+ ->will($this->returnValue('/data-dir/'));
+
+ $this->cfg->filePrefix = 'unittest://GLOBALS/unittest-dir';
+ }
+
+
+
+ public function testFindLocalData()
+ {
+ $this->setUpWrapper();
+ $GLOBALS['unittest-dir']['data-dir'] = array(
+ 'config.php' => 'content',
+ 'config.default.php' => 'content'
+ );
+ $this->assertEquals(
+ array(
+ '/data-dir/config.php',
+ '/data-dir/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+ public function testFindHostPreferredOverNonHostConfig()
+ {
+ $this->setUpWrapper();
+ $_SERVER['HTTP_HOST'] = 'foo.example.org';
+
+ $GLOBALS['unittest-dir']['data-dir'] = array(
+ 'config.php' => 'content',
+ 'config.foo.example.org.php' => 'content',
+ 'config.default.php' => 'content'
+ );
+ $this->assertEquals(
+ array(
+ '/data-dir/config.foo.example.org.php',
+ '/data-dir/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+ public function testFindEtcHostPreferredOverLocalConfigPhp()
+ {
+ $this->setUpWrapper();
+ $_SERVER['HTTP_HOST'] = 'foo.example.org';
+
+ $GLOBALS['unittest-dir'] = array(
+ 'etc' => array(
+ 'semanticscuttle' => array(
+ 'config.foo.example.org.php' => 'content',
+ )
+ ),
+ 'data-dir' => array(
+ 'config.php' => 'content',
+ 'config.default.php' => 'content'
+ )
+ );
+
+ $this->assertEquals(
+ array(
+ '/etc/semanticscuttle/config.foo.example.org.php',
+ '/data-dir/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+ public function testFindEtcConfig()
+ {
+ $this->setUpWrapper();
+ $GLOBALS['unittest-dir'] = array(
+ 'etc' => array(
+ 'semanticscuttle' => array(
+ 'config.php' => 'content'
+ )
+ ),
+ 'data-dir' => array(
+ 'config.default.php' => 'content'
+ )
+ );
+ $this->assertEquals(
+ array(
+ '/etc/semanticscuttle/config.php',
+ '/data-dir/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+ public function testFindEtcDefaultConfig()
+ {
+ $this->setUpWrapper();
+ $GLOBALS['unittest-dir'] = array(
+ 'etc' => array(
+ 'semanticscuttle' => array(
+ 'config.php' => 'content',
+ 'config.default.php' => 'content'
+ )
+ ),
+ );
+ $this->assertEquals(
+ array(
+ '/etc/semanticscuttle/config.php',
+ '/etc/semanticscuttle/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+ public function testFindLocalDefaultPreferredOverEtcDefault()
+ {
+ $this->setUpWrapper();
+ $GLOBALS['unittest-dir'] = array(
+ 'etc' => array(
+ 'semanticscuttle' => array(
+ 'config.php' => 'content',
+ 'config.default.php' => 'content'
+ )
+ ),
+ 'data-dir' => array(
+ 'config.php' => 'content',
+ 'config.default.php' => 'content'
+ )
+ );
+ $this->assertEquals(
+ array(
+ '/data-dir/config.php',
+ '/data-dir/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+ public function testFindSameDirDefaultPreferred()
+ {
+ $this->setUpWrapper();
+ $GLOBALS['unittest-dir'] = array(
+ 'etc' => array(
+ 'semanticscuttle' => array(
+ 'config.php' => 'content',
+ 'config.default.php' => 'content'
+ )
+ ),
+ 'data-dir' => array(
+ 'config.default.php' => 'content'
+ )
+ );
+ $this->assertEquals(
+ array(
+ '/etc/semanticscuttle/config.php',
+ '/etc/semanticscuttle/config.default.php'
+ ),
+ $this->cfg->findFiles()
+ );
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/tests/SemanticScuttle/EnvironmentTest.php b/tests/SemanticScuttle/EnvironmentTest.php
new file mode 100644
index 0000000..3baa9ed
--- /dev/null
+++ b/tests/SemanticScuttle/EnvironmentTest.php
@@ -0,0 +1,234 @@
+<?php
+
+class SemanticScuttle_EnvironmentTest extends PHPUnit_Framework_TestCase
+{
+ public function testServerPathInfoModPhpNoPath()
+ {
+ $_SERVER = array (
+ 'HTTP_USER_AGENT' => 'Opera/9.80 (X11; Linux x86_64) Presto/2.12.388 Version/12.16',
+ 'HTTP_HOST' => 'bm.bogo',
+ 'HTTP_ACCEPT' => 'text/html, application/xml;q=0.9, applicaton/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en,de-DE;q=0.9,de;q=0.8',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
+ 'HTTP_CONNECTION' => 'Keep-Alive',
+ 'HTTP_DNT' => '1',
+ 'PATH' => '/usr/local/bin:/usr/bin:/bin',
+ 'SERVER_SIGNATURE' => '<address>Apache/2.2.22 (Ubuntu) Server at bm.bogo Port 80</address>',
+ 'SERVER_SOFTWARE' => 'Apache/2.2.22 (Ubuntu)',
+ 'SERVER_NAME' => 'bm.bogo',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => '/var/www',
+ 'SERVER_ADMIN' => '[no address given]',
+ 'SCRIPT_FILENAME' => '/home/cweiske/Dev/html/hosts/bm.bogo/test.php',
+ 'REMOTE_PORT' => '38545',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => '/test.php',
+ 'SCRIPT_NAME' => '/test.php',
+ 'PHP_SELF' => '/test.php',
+ 'REQUEST_TIME_FLOAT' => 1377024570.296,
+ 'REQUEST_TIME' => 1377024570,
+ );
+ $this->assertEquals(
+ '', SemanticScuttle_Environment::getServerPathInfo()
+ );
+ }
+
+ public function testServerPathInfoModPhp()
+ {
+ $_SERVER = array(
+ 'HTTP_USER_AGENT' => 'Opera/9.80 (X11; Linux x86_64; U; de) Presto/2.9.168 Version/11.50',
+ 'HTTP_HOST' => 'bm-cgi.bogo',
+ 'HTTP_ACCEPT' => 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
+ 'HTTP_ACCEPT_LANGUAGE' => 'de-DE,de;q=0.9,en;q=0.8',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
+ 'HTTP_COOKIE' => 'PHPSESSID=ga446jhs0e09hkt60u9bsmp0n0',
+ 'HTTP_CACHE_CONTROL' => 'no-cache',
+ 'HTTP_CONNECTION' => 'Keep-Alive',
+ 'PATH' => '/usr/local/bin:/usr/bin:/bin',
+ 'SERVER_SIGNATURE' => '<address>Apache/2.2.17 (Ubuntu) Server at bm-cgi.bogo Port 80</address>',
+ 'SERVER_SOFTWARE' => 'Apache/2.2.17 (Ubuntu)',
+ 'SERVER_NAME' => 'bm-cgi.bogo',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => '/etc/apache2/htdocs',
+ 'SERVER_ADMIN' => '[no address given]',
+ 'SCRIPT_FILENAME' => '/home/cweiske/Dev/html/hosts/bm-cgi.bogo/profile.php',
+ 'REMOTE_PORT' => '45349',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => '/profile.php/dummy',
+ 'SCRIPT_NAME' => '/profile.php',
+ 'PATH_INFO' => '/dummy',
+ 'PATH_TRANSLATED' => '/home/cweiske/Dev/html/hosts/bm-cgi.bogo/dummy',
+ 'PHP_SELF' => '/profile.php/dummy',
+ 'REQUEST_TIME' => 1311422546,
+ );
+ $this->assertEquals(
+ '/dummy', SemanticScuttle_Environment::getServerPathInfo()
+ );
+ }
+
+
+ public function testServerPathInfoFastCgi()
+ {
+ $_SERVER = array(
+ 'PHP_FCGI_MAX_REQUESTS' => '5000',
+ 'PHPRC' => '/etc/php5/cgi/5.3.6/',
+ 'PHP_FCGI_CHILDREN' => '3',
+ 'PWD' => '/var/www/cgi-bin',
+ 'FCGI_ROLE' => 'RESPONDER',
+ 'REDIRECT_HANDLER' => 'php-cgi',
+ 'REDIRECT_STATUS' => '200',
+ 'HTTP_USER_AGENT' => 'Opera/9.80 (X11; Linux x86_64; U; de) Presto/2.9.168 Version/11.50',
+ 'HTTP_HOST' => 'bm-cgi.bogo',
+ 'HTTP_ACCEPT' => 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
+ 'HTTP_ACCEPT_LANGUAGE' => 'de-DE,de;q=0.9,en;q=0.8',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
+ 'HTTP_COOKIE' => 'PHPSESSID=ga446jhs0e09hkt60u9bsmp0n0',
+ 'HTTP_CONNECTION' => 'Keep-Alive',
+ 'PATH' => '/usr/local/bin:/usr/bin:/bin',
+ 'SERVER_SIGNATURE' => '<address>Apache/2.2.17 (Ubuntu) Server at bm-cgi.bogo Port 80</address>',
+ 'SERVER_SOFTWARE' => 'Apache/2.2.17 (Ubuntu)',
+ 'SERVER_NAME' => 'bm-cgi.bogo',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => '/etc/apache2/htdocs',
+ 'SERVER_ADMIN' => '[no address given]',
+ 'SCRIPT_FILENAME' => '/home/cweiske/Dev/html/hosts/bm-cgi.bogo/profile.php',
+ 'REMOTE_PORT' => '45342',
+ 'REDIRECT_URL' => '/profile.php/dummy',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => '/profile.php/dummy',
+ 'SCRIPT_NAME' => '/profile.php',
+ 'PATH_INFO' => '/dummy',
+ 'PATH_TRANSLATED' => '/etc/apache2/htdocs/dummy',
+ 'ORIG_PATH_INFO' => '/profile.php/dummy',
+ 'ORIG_SCRIPT_NAME' => '/cgi-bin-php/php-cgi-5.3.6',
+ 'ORIG_SCRIPT_FILENAME' => '/var/www/cgi-bin/php-cgi-5.3.6',
+ 'ORIG_PATH_TRANSLATED' => '/home/cweiske/Dev/html/hosts/bm-cgi.bogo/profile.php/dummy',
+ 'PHP_SELF' => '/profile.php/dummy',
+ 'REQUEST_TIME' => 1311422521,
+ );
+ $this->assertEquals(
+ '/dummy', SemanticScuttle_Environment::getServerPathInfo()
+ );
+ }
+
+ public function testServerPathInfo1and1NoPath()
+ {
+ $_SERVER = array(
+ 'REDIRECT_SCRIPT_URL' => '/dummy.php',
+ 'REDIRECT_SCRIPT_URI' => 'http://www.example.org/dummy.php',
+ 'REDIRECT_DOCUMENT_ROOT' => '/kunden/homepages/44/dexample/htdocs/example/www',
+ 'REDIRECT_HANDLER' => 'x-mapp-php6',
+ 'REDIRECT_STATUS' => '200',
+ 'DBENTRY_HOST' => 'example.org',
+ 'DBENTRY' => '/kunden/homepages/44/dexample/htdocs/example/www:d0000#CPU 6 #MEM 10240 #CGI 18 #NPROC 12 #TAID 46322755 #WERB 0 #LANG 2 #STAT 1',
+ 'SCRIPT_URL' => '/dummy.php',
+ 'SCRIPT_URI' => 'http://www.example.org/dummy.php',
+ 'HTTP_USER_AGENT' => 'Opera/9.80 (X11; Linux x86_64) Presto/2.12.388 Version/12.16',
+ 'HTTP_HOST' => 'www.example.org',
+ 'HTTP_ACCEPT' => 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en,de-DE;q=0.9,de;q=0.8',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
+ 'HTTP_COOKIE' => 'PHPSESSID=8c7853d7f639b3c6d24c224cf7d4cb1c',
+ 'HTTP_CONNECTION' => 'Keep-Alive',
+ 'HTTP_DNT' => '1',
+ 'PATH' => '/bin:/usr/bin',
+ 'SERVER_SIGNATURE' => '',
+ 'SERVER_SOFTWARE' => 'Apache',
+ 'SERVER_NAME' => 'example.org',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => '/kunden/homepages/44/dexample/htdocs/example/www',
+ 'SERVER_ADMIN' => 'webmaster@example.org',
+ 'SCRIPT_FILENAME' => '/kunden/homepages/44/dexample/htdocs/example/www/dummy.php',
+ 'REMOTE_PORT' => '35368',
+ 'REDIRECT_URL' => '/dummy.php',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => '/dummy.php',
+ 'SCRIPT_NAME' => '/dummy.php',
+ 'STATUS' => '200',
+ 'ORIG_PATH_INFO' => '/dummy.php',
+ 'ORIG_PATH_TRANSLATED' => '/kunden/homepages/44/dexample/htdocs/example/www/dummy.php',
+ 'PHP_SELF' => '/dummy.php',
+ 'REQUEST_TIME_FLOAT' => 1377022156.0101,
+ 'REQUEST_TIME' => 1377022156,
+ 'argv' => array(),
+ 'argc' => 0,
+ );
+ $this->assertEquals(
+ '', SemanticScuttle_Environment::getServerPathInfo()
+ );
+ }
+
+ public function testServerPathInfo1and1WithPath()
+ {
+ $_SERVER = array(
+ 'REDIRECT_SCRIPT_URL' => '/dummy.php/dummy/foo',
+ 'REDIRECT_SCRIPT_URI' => 'http://www.example.org/dummy.php/dummy/foo',
+ 'REDIRECT_DOCUMENT_ROOT' => '/kunden/homepages/44/dexample/htdocs/example/www',
+ 'REDIRECT_HANDLER' => 'x-mapp-php6',
+ 'REDIRECT_STATUS' => '200',
+ 'DBENTRY_HOST' => 'example.org',
+ 'DBENTRY' => '/kunden/homepages/44/dexample/htdocs/example/www:d0000#CPU 6 #MEM 10240 #CGI 18 #NPROC 12 #TAID 46322755 #WERB 0 #LANG 2 #STAT 1',
+ 'SCRIPT_URL' => '/dummy.php/dummy/foo',
+ 'SCRIPT_URI' => 'http://www.example.org/dummy.php/dummy/foo',
+ 'HTTP_USER_AGENT' => 'Opera/9.80 (X11; Linux x86_64) Presto/2.12.388 Version/12.16',
+ 'HTTP_HOST' => 'www.example.org',
+ 'HTTP_ACCEPT' => 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en,de-DE;q=0.9,de;q=0.8',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
+ 'HTTP_COOKIE' => 'PHPSESSID=8c7853d7f639b3c6d24c224cf7d4cb1c',
+ 'HTTP_CONNECTION' => 'Keep-Alive',
+ 'HTTP_DNT' => '1',
+ 'PATH' => '/bin:/usr/bin',
+ 'SERVER_SIGNATURE' => '',
+ 'SERVER_SOFTWARE' => 'Apache',
+ 'SERVER_NAME' => 'example.org',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => '/kunden/homepages/44/dexample/htdocs/example/www',
+ 'SERVER_ADMIN' => 'webmaster@example.org',
+ 'SCRIPT_FILENAME' => '/kunden/homepages/44/dexample/htdocs/example/www/dummy.php',
+ 'REMOTE_PORT' => '35857',
+ 'REDIRECT_URL' => '/dummy.php/dummy/foo',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => '/dummy.php/dummy/foo',
+ 'SCRIPT_NAME' => '/dummy.php',
+ 'STATUS' => '200',
+ 'ORIG_PATH_INFO' => '/dummy/foo',
+ 'ORIG_PATH_TRANSLATED' => '/kunden/homepages/44/dexample/htdocs/example/www/dummy.php',
+ 'PHP_SELF' => '/dummy.php',
+ 'REQUEST_TIME_FLOAT' => 1377024137.8098,
+ 'REQUEST_TIME' => 1377024137,
+ 'argv' => array(),
+ 'argc' => 0,
+ );
+ $this->assertEquals(
+ '/dummy/foo', SemanticScuttle_Environment::getServerPathInfo()
+ );
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/Tag2TagTest.php b/tests/Tag2TagTest.php
new file mode 100644
index 0000000..89da483
--- /dev/null
+++ b/tests/Tag2TagTest.php
@@ -0,0 +1,537 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle tag2tag service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Tag2TagTest extends TestBase
+{
+ protected $us;
+ protected $bs;
+ protected $b2ts;
+ protected $tts;
+
+
+ protected function setUp()
+ {
+ $this->us =SemanticScuttle_Service_Factory::get('User');
+ $this->us->deleteAll();
+ $this->addUser();
+ $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();
+ }
+
+ public function testManipulateTag2TagRelationsOfInclusion()
+ {
+ $tts = $this->tts;
+
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('a', 'c', '>', 1);
+ $tts->addLinkedTags('a', 'd', '>', 20);
+ $tts->addLinkedTags('b', 'd', '>', 1);
+ $tts->addLinkedTags('d', 'e', '>', 1);
+ $tts->addLinkedTags('d', 'e', '>', 20);
+ $tts->addLinkedTags('f', 'g', '>', 20);
+
+ // basic test
+
+
+ $links = $tts->getLinks(1);
+ $this->assertEquals(4, count($links));
+
+ $allLinkedTags = $tts->getAllLinkedTags('e', '>', 1);
+ $this->assertEquals(array(), $allLinkedTags);
+
+ $allLinkedTags = $tts->getAllLinkedTags('d', '>', 1);
+ $this->assertEquals(array('e'), $allLinkedTags);
+
+ $allLinkedTags = $tts->getAllLinkedTags('b', '>', 1);
+ $this->assertEquals(array('d', 'e'), $allLinkedTags);
+ $this->assertEquals(2, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+
+ $allLinkedTags = $tts->getAllLinkedTags('a', '>', 1);
+ $this->assertEquals(4, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('b', $allLinkedTags));
+ $this->assertTrue(in_array('c', $allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+
+
+ // warning: we add recursive link
+ $tts->addLinkedTags('b', 'a', '>', 1);
+
+ $allLinkedTags = $tts->getAllLinkedTags('a', '>', 1);
+ $this->assertEquals(4, sizeof($allLinkedTags));
+ //$this->assertTrue(in_array('a', $allLinkedTags));
+ $this->assertTrue(in_array('b', $allLinkedTags));
+ $this->assertTrue(in_array('c', $allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+
+ $orphewTags = $tts->getOrphewTags('>', 1);
+ $this->assertEquals(0, sizeof($orphewTags));
+ $orphewTags = $tts->getOrphewTags('>', 20);
+ $this->assertEquals(2, sizeof($orphewTags));
+ $this->assertSame('a', $orphewTags[0]['tag']);
+ $this->assertSame('f', $orphewTags[1]['tag']);
+ $orphewTags = $tts->getOrphewTags('>');
+ $this->assertEquals(1, sizeof($orphewTags));
+ $this->assertSame('f', $orphewTags[0]['tag']);
+
+ $linkedTags = $tts->getLinkedTags('a', '>');
+ $this->assertSame(array('b', 'c', 'd'), $linkedTags);
+ $linkedTags = $tts->getLinkedTags('a', '>', 1);
+ $this->assertSame(array('b', 'c'), $linkedTags);
+ $tts->removeLinkedTags('a', 'b', '>', 1);
+ $linkedTags = $tts->getLinkedTags('a', '>', 1);
+ $this->assertSame(array('c'), $linkedTags);
+ $tts->removeLinkedTags('a', 'c', '>', 1);
+ $linkedTags = $tts->getLinkedTags('a', '>', 1);
+ $this->assertEquals(0, sizeof($linkedTags));
+ }
+
+ public function testManipulateTag2TagRelationsOfSynonym()
+ {
+ $tts = $this->tts;
+
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'c', '>', 1);
+ $tts->addLinkedTags('b', 'd', '=', 1);
+ $tts->addLinkedTags('d', 'e', '=', 1);
+ $tts->addLinkedTags('d', 'f', '=', 1);
+ $tts->addLinkedTags('e', 'g', '>', 1);
+
+ $linkedTags = $tts->getLinkedTags('a', '>', 1);
+ $this->assertSame(array('b'), $linkedTags);
+
+ $linkedTags = $tts->getLinkedTags('a', '=', 1);
+ $this->assertSame(array(), $linkedTags);
+
+ $linkedTags = $tts->getLinkedTags('b', '=', 1);
+ $this->assertSame(array('d'), $linkedTags);
+
+ $linkedTags = $tts->getLinkedTags('d', '=', 1);
+ $this->assertEquals(3, sizeof($linkedTags));
+ $this->assertTrue(in_array('b', $linkedTags)); // '=' is bijective
+ $this->assertTrue(in_array('e', $linkedTags));
+ $this->assertTrue(in_array('f', $linkedTags));
+
+ $linkedTags = $tts->getLinkedTags('f', '=', 1);
+ $this->assertEquals(1, sizeof($linkedTags));
+ $this->assertTrue(in_array('d', $linkedTags)); // '=' is bijective
+
+ // test allLinkTags (with inference)
+ $allLinkedTags = $tts->getAllLinkedTags('a', '=', 1);
+ $this->assertEquals(0, sizeof($allLinkedTags));
+
+ $allLinkedTags = $tts->getAllLinkedTags('b', '=', 1);
+ $this->assertEquals(3, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+ $this->assertTrue(in_array('f', $allLinkedTags));
+
+ $allLinkedTags = $tts->getAllLinkedTags('f', '>', 1);
+ $this->assertEquals(5, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('b', $allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+ $this->assertTrue(in_array('c', $allLinkedTags));
+ $this->assertTrue(in_array('g', $allLinkedTags));
+
+ $allLinkedTags = $tts->getAllLinkedTags('a', '>', 1);
+ $this->assertEquals(6, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('b', $allLinkedTags));
+ $this->assertTrue(in_array('c', $allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+ $this->assertTrue(in_array('f', $allLinkedTags));
+ $this->assertTrue(in_array('g', $allLinkedTags));
+
+ $tts->addLinkedTags('g', 'h', '>', 1);
+ $tts->addLinkedTags('i', 'h', '=', 1);
+ $tts->addLinkedTags('j', 'f', '>', 1);
+
+ $allLinkedTags = $tts->getAllLinkedTags('j', '>', 1);
+ $this->assertEquals(8, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('b', $allLinkedTags));
+ $this->assertTrue(in_array('c', $allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+ $this->assertTrue(in_array('f', $allLinkedTags));
+ $this->assertTrue(in_array('g', $allLinkedTags));
+ $this->assertTrue(in_array('h', $allLinkedTags));
+ $this->assertTrue(in_array('i', $allLinkedTags));
+
+ // complex case: test cycle
+ $tts->addLinkedTags('g', 'a', '>', 1);
+ $allLinkedTags = $tts->getAllLinkedTags('b', '>', 1);
+ $this->assertEquals(8, sizeof($allLinkedTags));
+ $this->assertTrue(in_array('a', $allLinkedTags));
+ $this->assertTrue(in_array('c', $allLinkedTags));
+ $this->assertTrue(in_array('d', $allLinkedTags));
+ $this->assertTrue(in_array('e', $allLinkedTags));
+ $this->assertTrue(in_array('f', $allLinkedTags));
+ $this->assertTrue(in_array('g', $allLinkedTags));
+ $this->assertTrue(in_array('h', $allLinkedTags));
+ $this->assertTrue(in_array('i', $allLinkedTags));
+
+ }
+
+ // Test function that select the best tags to display?
+ public function testViewTag2TagRelations()
+ {
+ $tts = $this->tts;
+
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('c', 'd', '>', 1);
+ $tts->addLinkedTags('d', 'e', '>', 1);
+ $tts->addLinkedTags('f', 'g', '>', 1);
+ $tts->addLinkedTags('f', 'h', '>', 1);
+ $tts->addLinkedTags('f', 'i', '>', 1);
+
+ $orphewTags = $tts->getOrphewTags('>', 1);
+ $this->assertEquals(3, sizeof($orphewTags));
+ $this->assertSame('a', $orphewTags[0]['tag']);
+ $this->assertSame('c', $orphewTags[1]['tag']);
+ $this->assertSame('f', $orphewTags[2]['tag']);
+
+ // with limit
+ $orphewTags = $tts->getOrphewTags('>', 1, 2);
+ $this->assertEquals(2, sizeof($orphewTags));
+ $this->assertSame('a', $orphewTags[0]['tag']);
+ $this->assertSame('c', $orphewTags[1]['tag']);
+
+ // with sorting
+ $orphewTags = $tts->getOrphewTags('>', 1, 2, 'nb'); // nb descendants
+ $this->assertEquals(2, sizeof($orphewTags));
+ $this->assertSame('f', $orphewTags[0]['tag']);
+ $this->assertSame('c', $orphewTags[1]['tag']);
+
+ $orphewTags = $tts->getOrphewTags('>', 1, 1, 'depth');
+ $this->assertEquals(1, sizeof($orphewTags));
+ $this->assertSame('c', $orphewTags[0]['tag']);
+
+ $orphewTags = $tts->getOrphewTags('>', 1, null, 'nbupdate');
+ $this->assertEquals(3, sizeof($orphewTags));
+ $this->assertSame('f', $orphewTags[0]['tag']);
+ $this->assertSame('c', $orphewTags[1]['tag']);
+ $this->assertSame('a', $orphewTags[2]['tag']);
+
+ }
+
+ public function testAddLinkedTagsThroughBookmarking()
+ {
+ $bs = $this->bs;
+ $tags = array('a>b', 'b>c', 'a>d>e', 'a>a', 'a', 'r=s', 's=t=u');
+ $uid = $this->addUser();
+ $bs->addBookmark(
+ "http://google.com", "title", "description", 'note',
+ 0, $tags, null, null, false, false,
+ $uid
+ );
+ $bookmark = $bs->getBookmarkByAddress("http://google.com");
+
+ $b2ts = $this->b2ts;
+ $savedTags = $b2ts->getTagsForBookmark(intval($bookmark['bId']));
+ $this->assertEquals(6, sizeof($savedTags));
+ $this->assertContains('b', $savedTags);
+ $this->assertContains('c', $savedTags);
+ $this->assertContains('e', $savedTags);
+ $this->assertContains('a', $savedTags);
+ $this->assertContains('r', $savedTags);
+ $this->assertContains('s', $savedTags);
+
+ $tts = $this->tts;
+ $linkedTags = $tts->getLinkedTags('a', '>', $uid);
+ $this->assertEquals(2, count($linkedTags));
+ $this->assertInternalType('string', $linkedTags[0]);
+ $this->assertSame('b', $linkedTags[0]);
+ $this->assertInternalType('string', $linkedTags[1]);
+ $this->assertSame('d', $linkedTags[1]);
+
+ $linkedTags = $tts->getLinkedTags('b', '>', $uid);
+ $this->assertEquals(1, count($linkedTags));
+ $this->assertSame('c', $linkedTags[0]);
+ $this->assertTrue($tts->existsLinkedTags('d', 'e', '>', $uid));
+ $this->assertFalse($tts->existsLinkedTags('e', 'd', '>', $uid));
+ }
+
+ public function testSearchThroughLinkedTags()
+ {
+ $tts = $this->tts;
+ $bs = $this->bs;
+
+ $tts->addLinkedTags('aa', 'bb', '>', 1);
+
+ $tags = array('aa>bb>cc', 'dd');
+ $bs->addBookmark(
+ "web1.com", "B1", "description", 'note', 0,
+ $tags, null, null, false, false, 1
+ );
+ $tags = array('bb>gg', 'ee>ff');
+ $bs->addBookmark(
+ "web2.com", "B2", "description", 'note', 0,
+ $tags, null, null, false, false, 1
+ );
+ $tags = array('ee=ii');
+ $bs->addBookmark(
+ "web3.com", "B3", "description", 'note', 0,
+ $tags, null, null, false, false, 1
+ );
+
+ // Query format:
+ // $bs->getBookmarks($start = 0, $perpage = NULL, $user = NULL, $tags = NULL, $terms = NULL, $sortOrder = NULL, $watched = NULL, $startdate = NULL, $enddate = NULL, $hash = NULL);
+
+ // basic queries
+ $results = $bs->getBookmarks(0, NULL, 1, 'dd');
+ $this->assertSame(1, intval($results['total']));
+ $this->assertSame('B1', $results['bookmarks'][0]['bTitle']);
+
+ $results = $bs->getBookmarks(0, NULL, 1, 'cc');
+ $this->assertSame(1, intval($results['total']));
+ $this->assertSame('B1', $results['bookmarks'][0]['bTitle']);
+
+ //advanced queries
+ $results = $bs->getBookmarks(0, NULL, 1, 'aa', null, 'title_asc');
+ $this->assertSame(2, intval($results['total']));
+ $this->assertSame('B1', $results['bookmarks'][0]['bTitle']);
+ $this->assertSame('B2', $results['bookmarks'][1]['bTitle']);
+
+ $results = $bs->getBookmarks(0, NULL, 1, 'ee', null, 'title_asc');
+ $this->assertSame(2, intval($results['total']));
+ $this->assertSame('B2', $results['bookmarks'][0]['bTitle']);
+ $this->assertSame('B3', $results['bookmarks'][1]['bTitle']);
+
+ $results = $bs->getBookmarks(0, NULL, 1, 'ii', null, 'title_asc');
+ $this->assertSame(2, intval($results['total']));
+ $this->assertSame('B2', $results['bookmarks'][0]['bTitle']);
+ $this->assertSame('B3', $results['bookmarks'][1]['bTitle']);
+
+ $results = $bs->getBookmarks(0, NULL, 1, 'aa+ee');
+
+ $this->assertSame(1, intval($results['total']));
+ $this->assertSame('B2', $results['bookmarks'][0]['bTitle']);
+
+ }
+
+ public function testStatsBetweenTags()
+ {
+ $tsts = $this->tsts;
+ $tts = $this->tts;
+
+ // basic functions
+ $this->assertFalse($tsts->existStat('a', '>', 10));
+ $tsts->setNbDescendants('a', '>', 10, 2);
+ $this->assertSame(2, $tsts->getNbDescendants('a', '>', 10));
+ $tsts->setMaxDepth('a', '>', 10, 3);
+ $this->assertSame(3, $tsts->getMaxDepth('a', '>', 10));
+ $this->assertTrue($tsts->existStat('a', '>', 10));
+ $this->assertFalse($tsts->existStat('a', '>', 20));
+ $tsts->increaseNbUpdate('a', '>', 10);
+ $this->assertSame(1, $tsts->getNbUpdates('a', '>', 10));
+
+ $tsts->deleteAll();
+
+ // no structure
+ $nbC = $tsts->getNbChildren('a', '>', 1);
+ $nbD = $tsts->getNbDescendants('a', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('a', '>', 1);
+ $this->assertSame(0, $nbC);
+ $this->assertSame(0, $nbD);
+ $this->assertSame(0, $maxDepth);
+
+ // simple case
+ $tts->addLinkedTags('b', 'c', '>', 1);
+ $tts->addLinkedTags('a', 'd', '>', 1);
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'e', '>', 1);
+
+ $this->assertSame(3, $tsts->getNbUpdates('a', '>', '1'));
+ $this->assertSame(2, $tsts->getNbUpdates('b', '>', '1'));
+ $this->assertSame(0, $tsts->getNbUpdates('c', '>', '1'));
+ $this->assertSame(0, $tsts->getNbUpdates('d', '>', '1'));
+ $this->assertSame(0, $tsts->getNbUpdates('e', '>', '1'));
+
+
+ $nbC = $tsts->getNbChildren('a', '>', 1);
+ $nbD = $tsts->getNbDescendants('a', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('a', '>', 1);
+ $this->assertSame(2, $nbC);
+ $this->assertSame(4, $nbD);
+ $this->assertSame(2, $maxDepth);
+
+ $nbC = $tsts->getNbChildren('b', '>', 1);
+ $nbD = $tsts->getNbDescendants('b', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('b', '>', 1);
+ $this->assertSame(2, $nbC);
+ $this->assertSame(2, $nbD);
+ $this->assertSame(1, $maxDepth);
+
+ $nbC = $tsts->getNbChildren('c', '>', 1);
+ $nbD = $tsts->getNbDescendants('c', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('c', '>', 1);
+ $this->assertSame(0, $nbC);
+ $this->assertSame(0, $nbD);
+ $this->assertSame(0, $maxDepth);
+
+ $nbC = $tsts->getNbChildren('d', '>', 1);
+ $nbD = $tsts->getNbDescendants('d', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('d', '>', 1);
+ $this->assertSame(0, $nbC);
+ $this->assertSame(0, $nbD);
+ $this->assertSame(0, $maxDepth);
+
+ // deletion
+ $tts->removeLinkedTags('b', 'e', '>', 1);
+
+ $nbC = $tsts->getNbChildren('b', '>', 1);
+ $nbD = $tsts->getNbDescendants('b', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('b', '>', 1);
+ $this->assertSame(1, $nbC);
+ $this->assertSame(1, $nbD);
+ $this->assertSame(1, $maxDepth);
+
+ $nbC = $tsts->getNbChildren('a', '>', 1);
+ $nbD = $tsts->getNbDescendants('a', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('a', '>', 1);
+ $this->assertSame(2, $nbC);
+ $this->assertSame(3, $nbD);
+ $this->assertSame(2, $maxDepth);
+
+ //do cases for synonyms
+
+ $this->markTestSkipped('Check stats');
+
+ $tsts->deleteAll();
+ $tts->deleteAll();
+
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'c', '=', 1);
+ /*$tts->addLinkedTags('a', 'c', '>', 1);
+ $tts->addLinkedTags('j', 'i', '=', 1);
+ $tts->addLinkedTags('f', 'i', '=', 1);
+ $tts->addLinkedTags('d', 'f', '>', 1);
+ $tts->addLinkedTags('d', 'e', '>', 1);
+ $tts->addLinkedTags('j', 'k', '>', 1);*/
+
+ $nbC = $tsts->getNbChildren('a', '>', 1);
+ $nbD = $tsts->getNbDescendants('a', '>', 1);
+ $nbU = $tsts->getNbUpdates('a', '>', 1);
+ $maxDepth = $tsts->getMaxDepth('a', '>', 1);
+ //$this->assertSame(2, $tts->getLinkedTags('a', '>', 1));
+ $this->assertSame(1, $nbC);
+ //$this->assertSame(2, $nbD);
+ $this->assertSame(2, $nbU);
+ $this->assertSame(1, $maxDepth);
+
+ // advanced case with fore loop
+ //$tts->addLinkedTags('d', 'c', '>', 1);
+
+ // advanced case with back loop
+ //$tts->addLinkedTags('e', 'a', '>', 1);
+ }
+
+ public function testRenameFunction()
+ {
+ $tts = $this->tts;
+ $b2ts = $this->b2ts;
+ $bs = $this->bs;
+ $tsts = $this->tsts;
+
+ $uid1 = $this->addUser();
+ $uid2 = $this->addUser();
+
+ // with classic tags (users 10 & 20)
+ $bid1 = $bs->addBookmark(
+ "http://site1.com", "title", "description", 'note', 0,
+ array('tag1', 'tag11', 'tag111'), null, null, false, false,
+ $uid1
+ );
+ $bid2 = $bs->addBookmark(
+ "http://site1.com", "title2", "description2", 'note', 0,
+ array('tag2', 'tag22', 'tag222'), null, null, false, false,
+ $uid2
+ );
+
+ $bookmarks = $bs->getBookmarks();
+ $this->assertEquals(1, $bookmarks['total']);
+
+ $b2ts->renameTag($uid1, 'tag1', 'newtag1');
+ $tags1 = $b2ts->getTagsForBookmark($bid1);
+ $this->assertContains('newtag1', $tags1);
+ $this->assertContains('tag11', $tags1);
+ $this->assertContains('tag111', $tags1);
+
+ //should not be changed
+ $tags2 = $b2ts->getTagsForBookmark($bid2);
+ $this->assertContains('tag2', $tags2);
+ $this->assertContains('tag22', $tags2);
+ $this->assertContains('tag222', $tags2);
+
+
+ // with linked tags
+
+ $tts->addLinkedTags('b', 'c', '>', 1);
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'a', '>', 2); // should not be modified because of userid
+
+ $tts->renameTag(1, 'b', 'e');
+ $linkedTags = $tts->getLinkedTags('e', '>', 1);
+ $this->assertSame(array('c'), $linkedTags);
+ $linkedTags = $tts->getLinkedTags('a', '>', 1);
+ $this->assertSame(array('e'), $linkedTags);
+ $linkedTags = $tts->getLinkedTags('b', '>', 2);
+ $this->assertSame(array('a'), $linkedTags);
+
+ //with stats
+
+ }
+
+ // Cannot be test because the function use GLOBALS variables
+ // not taken into account by tests
+ /*public function testMenuTags()
+ {
+ $tts = $this->tts;
+ $bs = $this->bs;
+
+ $bs->addBookmark("http://site1.com", "title", "description", "status", array('menu>tag1'), null, false, false, 1);
+ $bs->addBookmark("http://site1.com", "title2", "description2", "status", array('menu>tag2>tag3'), null, false, false, 1);
+ $bs->addBookmark("http://site1.com", "title3", "description3", "status", array('menu>tag1', 'menu>tag4'), null, false, false, 2);
+
+ $menuTags = $tts->getMenuTags($uId);
+ $this->assertEquals(3, sizeof($menuTags));
+ $this->assertContains('tag1', $menuTags);
+ $this->assertContains('tag2', $menuTags);
+ $this->assertContains('tag4', $menuTags);
+
+ }*/
+}
+?>
diff --git a/tests/TagTest.php b/tests/TagTest.php
new file mode 100644
index 0000000..bac8f28
--- /dev/null
+++ b/tests/TagTest.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle tag service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class TagTest extends TestBase
+{
+ protected $ts;
+
+
+ protected function setUp()
+ {
+ $this->ts =SemanticScuttle_Service_Factory::get('Tag');
+ $this->ts->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();
+ }
+
+ public function testTagDescriptions()
+ {
+ $ts = $this->ts;
+
+ $desc = $ts->getAllDescriptions('tag1');
+ $this->assertSame(array(), $desc);
+
+ $desc = $ts->getDescription('tag1', 1); // user 1
+ $this->assertSame(array('tDescription'=>''), $desc);
+
+ $desc1 = "test description";
+ $ts->updateDescription('tag1', 1, $desc1); // first desc
+ $desc = $ts->getDescription('tag1', 1);
+ $this->assertEquals(array('tag'=>'tag1', 'uId'=>1, 'tDescription'=>$desc1), $desc);
+
+ $desc1 = "&é\"'(-è_çà)=´~#'#{{[\\\\[||`\^\^@^@}¹²¡×¿ ?./§µ%";
+ $ts->updateDescription('tag1', 1, $desc1); // update desc
+ $desc = $ts->getDescription('tag1', 1);
+ $this->assertEquals(array('tag'=>'tag1', 'uId'=>1, 'tDescription'=>$desc1), $desc);
+
+ $desc2 = "æâ€êþÿûîîôôöŀï'üð’‘ßä«≤»©»  ↓¿×÷¡¹²³";
+ $ts->updateDescription('tag1', 2, $desc2); // user 2
+ $desc = $ts->getDescription('tag1', 2);
+ $this->assertEquals(array('tag'=>'tag1', 'uId'=>2, 'tDescription'=>$desc2), $desc);
+
+ $desc = $ts->getAllDescriptions('tag1');
+ $this->assertEquals($desc, array(array('tag'=>'tag1', 'uId'=>1, 'tDescription'=>$desc1), array('tag'=>'tag1', 'uId'=>2, 'tDescription'=>$desc2)));
+
+ }
+
+ public function testRenameFunction()
+ {
+ $ts = $this->ts;
+
+ $ts->updateDescription('tag1', 10, 'xxx');
+ $ts->renameTag(10, 'tag1', 'tag2');
+ $desc = $ts->getDescription('tag1', 10);
+ $this->assertSame(array('tDescription'=>''), $desc);
+ $desc = $ts->getDescription('tag2', 10);
+ $this->assertEquals(array('tag'=>'tag2', 'uId'=>10, 'tDescription'=>'xxx'), $desc);
+
+ }
+
+ public function testNormalizeEmptyValues()
+ {
+ $tags = $this->ts->normalize(
+ array('foo', '', 'bar', 'baz')
+ );
+ $this->assertEquals(array(0 => 'foo', 2 => 'bar', 3 => 'baz'), $tags);
+ }
+
+}
+?>
diff --git a/tests/TagsCacheTest.php b/tests/TagsCacheTest.php
new file mode 100644
index 0000000..1f69b58
--- /dev/null
+++ b/tests/TagsCacheTest.php
@@ -0,0 +1,192 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle tags cache service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class TagsCacheTest extends PHPUnit_Framework_TestCase
+{
+ protected $us;
+ protected $bs;
+ protected $b2ts;
+ protected $tts;
+
+
+
+ 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->tcs =SemanticScuttle_Service_Factory::get('TagCache');
+ $this->tcs->deleteAll();
+ }
+
+ public function testInclusionAllowsToAddAndDeleteChildrenTags() {
+ //message_die(GENERAL_ERROR, $GLOBALS['dbname'].'1');
+
+ $tts = $this->tts;
+ $tcs = $this->tcs;
+
+ // test adding children
+ $tcs->addChild('a', 'b', 1);
+ $tcs->addChild('a', 'c', 1);
+ $this->assertEquals(array('b','c'), $tcs->getChildren('a', 1));
+
+ // test adding a same child
+ $tcs->addChild('a', 'b', 1);
+ $this->assertEquals(array('b','c'), $tcs->getChildren('a', 1));
+
+ // test removing a child
+ $tcs->removeChild('a', 'b', 1);
+ $this->assertEquals(array('c'), $tcs->getChildren('a', 1));
+
+ // test removing a same child
+ $tcs->removeChild('a', 'b', 1);
+ $this->assertEquals(array('c'), $tcs->getChildren('a', 1));
+
+ // test existing child
+ $this->assertTrue($tcs->existsChild('a', 'c', 1));
+ $this->assertTrue(!$tcs->existsChild('a', 'c', 2)); // wrong user
+ $this->assertTrue(!$tcs->existsChild('a', 'b', 1)); // wrong child
+
+ // test removing several children
+ $tcs->addChild('e', 'f', 1);
+ $tcs->addChild('e', 'g', 1);
+ $tcs->addChild('e', 'h', 1);
+ $tcs->removeChild('e', NULL, 1);
+
+ $this->assertTrue(!$tcs->existsChild('e', 'f', 1));
+ $this->assertTrue(!$tcs->existsChild('e', 'g', 1));
+ $this->assertTrue(!$tcs->existsChild('e', 'h', 1));
+
+ }
+
+ public function testInclusionCacheIsUpdatedWhenATag2TagLinkIsCreatedOrRemoved() {
+ $tts = $this->tts;
+ $tcs = $this->tcs;
+
+ // test inclusion without possible errors
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'c', '>', 1);
+ $tts->addLinkedTags('c', 'd', '>', 1);
+ $tts->addLinkedTags('e', 'f', '>', 1);
+ $tts->addLinkedTags('b', 'e', '>', 1);
+
+ $this->assertSame(array('b','c','d','e','f'), $tts->getAllLinkedTags('a', '>', 1));
+ $this->assertSame(array('c','d','e','f'), $tts->getAllLinkedTags('b', '>', 1));
+
+ // test inclusion with deletion
+ $tts->removeLinkedTags('b', 'c', '>', 1);
+ $this->assertSame(array('b','e','f'), $tts->getAllLinkedTags('a', '>', 1));
+ $this->assertSame(array('e','f'), $tts->getAllLinkedTags('b', '>', 1));
+ $this->assertSame(array('d'), $tts->getAllLinkedTags('c', '>', 1));
+ $this->assertSame(array('f'), $tts->getAllLinkedTags('e', '>', 1));
+
+ }
+
+ public function testInclusionResistsToTagCycles() {
+ $tts = $this->tts;
+ $tcs = $this->tcs;
+
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'c', '>', 1);
+ $tts->addLinkedTags('c', 'a', '>', 1); // creates cycle a>c>a
+
+ $this->assertSame(array('b','c'), $tts->getAllLinkedTags('a', '>', 1));
+ $this->assertSame(array('c', 'a'), $tts->getAllLinkedTags('b', '>', 1));
+ $this->assertSame(array('a', 'b'), $tts->getAllLinkedTags('c', '>', 1));
+ }
+
+ public function testSynonymyAllowsToAddAndDeleteSynonyms() {
+ $tts = $this->tts;
+ $tcs = $this->tcs;
+
+ // simple synonymy
+ $tcs->addSynonym('a', 'b', 1);
+ $tcs->addSynonym('a', 'c', 1);
+
+ $this->assertEquals(array('b', 'c'), $tcs->getSynonyms('a', 1));
+ $this->assertEquals(array('c', 'a'), $tcs->getSynonyms('b', 1));
+ $this->assertEquals(array('b', 'a'), $tcs->getSynonyms('c', 1));
+
+ //more advanced one 1
+ $tcs->deleteByUser(1);
+ $tcs->addSynonym('a', 'b', 1);
+ $tcs->addSynonym('a', 'c', 1);
+ $tcs->addSynonym('d', 'e', 1);
+ $tcs->addSynonym('a', 'e', 1);
+ $this->assertEquals(array('b', 'c', 'e', 'd'), $tcs->getSynonyms('a', 1));
+
+ //more advanced one 2
+ $tcs->deleteByUser(1);
+ $tcs->addSynonym('a', 'b', 1);
+ $tcs->addSynonym('a', 'c', 1);
+ $tcs->addSynonym('d', 'e', 1);
+ $tcs->addSynonym('a', 'd', 1);
+ $this->assertEquals(array('b', 'c', 'd', 'e'), $tcs->getSynonyms('a', 1));
+
+ //with Linked tags
+ $tcs->deleteByUser(1);
+ $tts->addLinkedTags('a', 'b', '=', 1);
+ $tts->addLinkedTags('c', 'd', '=', 1);
+ $tts->addLinkedTags('c', 'e', '=', 1);
+ $tts->addLinkedTags('e', 'a', '=', 1);
+ $this->assertEquals(array('b', 'e', 'c', 'd'), $tts->getAllLinkedTags('a', '=', 1));
+
+ }
+
+ public function testInclusionTakesSynonymsIntoAccount() {
+ $tts = $this->tts;
+ $tcs = $this->tcs;
+
+ $tts->addLinkedTags('a', 'b', '>', 1);
+ $tts->addLinkedTags('b', 'c', '>', 1);
+ $tts->addLinkedTags('d', 'e', '>', 1);
+ $tts->addLinkedTags('c', 'd', '=', 1);
+
+ // results are put into cache
+ $this->assertEquals(array('b', 'c', 'd', 'e'), $tts->getAllLinkedTags('a', '>', 1));
+ $this->assertEquals(array('d', 'e'), $tts->getAllLinkedTags('c', '>', 1));
+
+ // same results must be taken out from cache
+ $this->assertEquals(array('b', 'c', 'd', 'e'), $tts->getAllLinkedTags('a', '>', 1));
+ $this->assertEquals(array('d', 'e'), $tts->getAllLinkedTags('c', '>', 1));
+
+ // check that result comes from cache (artificial changes in cache must appear in result)
+ $tcs->removeChild('a', 'e', 1);
+ $this->assertEquals(array('b', 'c', 'd'), $tts->getAllLinkedTags('a', '>', 1));
+
+ //cache must be deleted for user when links are modified
+ $tts->addLinkedTags('a', 'f', '=', 1);
+ $this->assertEquals(array(), $tcs->getChildren('a', 1));
+ $this->assertEquals(array(), $tcs->getSynonyms('d', 1));
+ }
+}
+?>
diff --git a/tests/TestBase.php b/tests/TestBase.php
new file mode 100644
index 0000000..4bffd1b
--- /dev/null
+++ b/tests/TestBase.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Base unittest class that provides several helper methods.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class TestBase extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Create a new bookmark.
+ *
+ * @param integer $user User ID the bookmark shall belong
+ * @param string $address Bookmark address to use
+ * @param integer $status Bookmark visibility
+ * @param array $tags Array of tags to attach. If "null" is given,
+ * it will automatically be "unittest"
+ * @param string $title Bookmark title
+ * @param string $date strtotime-compatible string
+ *
+ * @return integer ID of bookmark
+ *
+ * @see SemanticScuttle_Service_Bookmark::addBookmark()
+ */
+ protected function addBookmark(
+ $user = null, $address = null, $status = 0,
+ $tags = null, $title = null, $date = null
+ ) {
+ if ($user === null) {
+ $user = $this->addUser();
+ }
+ if ($tags === null) {
+ $tags = array('unittest');
+ }
+
+ $bs = SemanticScuttle_Service_Factory::get('Bookmark');
+ $rand = rand();
+
+ if ($address === null) {
+ $address = 'http://example.org/' . $rand;
+ }
+ if ($title === null) {
+ $title = 'unittest bookmark #' . $rand;
+ }
+
+ $bid = $bs->addBookmark(
+ $address,
+ $title,
+ 'description',
+ null,
+ $status,
+ $tags,
+ null, $date, false, false,
+ $user
+ );
+ return $bid;
+ }
+
+
+
+ /**
+ * Creates a new user in the database.
+ *
+ * @param string $username Username, may be null
+ * @param string $password Password, may be null
+ * @param mixed $privateKey String private key or boolean true to generate one
+ *
+ * @return integer ID of user
+ *
+ * @uses addUserData()
+ */
+ protected function addUser(
+ $username = null, $password = null, $privateKey = null
+ ) {
+ return rreset($this->addUserData($username, $password, $privateKey));
+ }
+
+
+
+ /**
+ * Creates a new user in the database and returns id, username and password.
+ *
+ * @param string $username Username, may be null
+ * @param string $password Password, may be null
+ * @param mixed $privateKey String private key or boolean true to generate one
+ *
+ * @return array ID of user, Name of user, password of user, privateKey
+ */
+ protected function addUserData(
+ $username = null, $password = null, $privateKey = null
+ ) {
+ $us = SemanticScuttle_Service_Factory::get('User');
+ $rand = rand();
+
+ if ($username === null) {
+ $username = 'unittestuser-' . $rand;
+ }
+ if ($password === null) {
+ $password = $rand;
+ }
+ if ($privateKey === true) {
+ $privateKey = $this->us->getNewPrivateKey();
+ }
+
+ $uid = $us->addUser(
+ $username,
+ $password,
+ 'unittest-' . $rand . '@example.org',
+ $privateKey
+ );
+ return array($uid, $username, $password, $privateKey);
+ }
+
+
+
+ /**
+ * 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;
+ }
+}
+
+?>
diff --git a/tests/TestBaseApi.php b/tests/TestBaseApi.php
new file mode 100644
index 0000000..ff3b695
--- /dev/null
+++ b/tests/TestBaseApi.php
@@ -0,0 +1,271 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+require_once 'HTTP/Request2.php';
+
+/**
+ * Base unittest class for web API tests.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class TestBaseApi extends TestBase
+{
+ /**
+ * Created from the configured host and the $urlPart.
+ * Should be used as base for all generated URLs
+ *
+ * @var string
+ */
+ protected $url;
+
+ /**
+ * Part of the URL behind the configured host.
+ * Needs to be overwritten in each derived test case class.
+ *
+ * @var string
+ */
+ protected $urlPart = null;
+
+ /**
+ * @var SemanticScuttle_Service_User
+ */
+ protected $us;
+
+ /**
+ * @var SemanticScuttle_Service_Bookmark
+ */
+ protected $bs;
+
+
+
+ protected function setUp()
+ {
+ if ($GLOBALS['unittestUrl'] === null) {
+ $this->markTestSkipped('Unittest URL not set in config');
+ }
+ if ($this->urlPart === null) {
+ $this->assertTrue(false, 'Set the urlPart variable');
+ }
+ $this->url = $GLOBALS['unittestUrl'] . $this->urlPart;
+
+ //clean up before test
+ $configFile = $GLOBALS['datadir'] . '/config.testing-tmp.php';
+ if (file_exists($configFile)) {
+ unlink($configFile);
+ }
+
+ $this->us = SemanticScuttle_Service_Factory::get('User');
+ $this->us->deleteAll();
+ $this->bs = SemanticScuttle_Service_Factory::get('Bookmark');
+ $this->bs->deleteAll();
+ $this->b2t = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+ $this->b2t->deleteAll();
+ }
+
+
+
+ /**
+ * Creates and returns a HTTP GET request object.
+ * Uses $this->url plus $urlSuffix as request URL.
+ *
+ * @param string $urlSuffix Suffix for the URL
+ *
+ * @return HTTP_Request2 HTTP request object
+ */
+ protected function getRequest($urlSuffix = null)
+ {
+ $url = $this->getTestUrl($urlSuffix);
+ $req = new HTTP_Request2($url, HTTP_Request2::METHOD_GET);
+
+ return $req;
+ }
+
+ /**
+ * Creates an URL from $this->url plus $urlSuffix and an appended
+ * unittestMode=1 parameter.
+ *
+ * @param string $urlSuffix Suffix for the URL
+ *
+ * @return string URL
+ *
+ * @uses $url
+ */
+ protected function getTestUrl($urlSuffix = null)
+ {
+ $url = $this->url . $urlSuffix;
+ if (strpos($urlSuffix, '?') !== false) {
+ $url .= '&unittestMode=1';
+ } else {
+ $url .= '?unittestMode=1';
+ }
+ return $url;
+ }
+
+
+ /**
+ * Completes an URL that's missing the protocol.
+ * Useful when re-using URLs extracted from HTML
+ *
+ * @param string $url Potentially partial URL
+ *
+ * @return string Full URL
+ */
+ protected function completeUrl($url)
+ {
+ if (substr($url, 0, 2) == '//') {
+ $url = 'http:' . $url;
+ }
+ return $url;
+ }
+
+
+
+ /**
+ * Creates a user and a HTTP GET request object and prepares
+ * the request object with authentication details, so that
+ * the user is logged in.
+ *
+ * Useful for HTTP API methods only, cannot be used with
+ * "normal" HTML pages since they do not support HTTP auth.
+ *
+ * @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()
+ * @see getLoggedInRequest()
+ */
+ protected function getAuthRequest($urlSuffix = null, $auth = true)
+ {
+ $req = $this->getRequest($urlSuffix);
+ if (is_array($auth)) {
+ list($username, $password) = $auth;
+ } else {
+ $username = 'testuser';
+ $password = 'testpassword';
+ }
+ $uid = $this->addUser($username, $password);
+ $req->setAuth(
+ $username, $password,
+ HTTP_Request2::AUTH_BASIC
+ );
+ return array($req, $uid);
+ }
+
+
+
+ /**
+ * Creates a user and a HTTP_Request2 object, does a normal login
+ * and prepares the cookies for the HTTP GET request object so that
+ * the user is seen as logged in when requesting any HTML page.
+ *
+ * Useful for testing HTML pages or ajax URLs.
+ *
+ * @param string $urlSuffix Suffix for the URL
+ * @param mixed $auth If user authentication is needed (true/false)
+ * or array with username and password
+ * @param boolean $privateKey True if to add user with private key
+ *
+ * @return array(HTTP_Request2, integer) HTTP request object and user id
+ *
+ * @uses getRequest()
+ */
+ protected function getLoggedInRequest(
+ $urlSuffix = null, $auth = true, $privateKey = null
+ ) {
+ if (is_array($auth)) {
+ list($username, $password) = $auth;
+ } else {
+ $username = 'testuser';
+ $password = 'testpassword';
+ }
+ $uid = $this->addUser($username, $password, $privateKey);
+
+ $req = new HTTP_Request2(
+ $GLOBALS['unittestUrl'] . '/login.php?unittestMode=1',
+ HTTP_Request2::METHOD_POST
+ );
+ $cookies = $req->setCookieJar()->getCookieJar();
+ $req->addPostParameter('username', $username);
+ $req->addPostParameter('password', $password);
+ $req->addPostParameter('submitted', 'Log In');
+ $res = $req->send();
+
+ //after login, we normally get redirected
+ $this->assertEquals(302, $res->getStatus(), 'Login failure');
+
+ $req = $this->getRequest($urlSuffix);
+ $req->setCookieJar($cookies);
+
+ return array($req, $uid);
+ }
+
+
+
+ /**
+ * Verifies that the HTTP response has status code 200 and
+ * content-type application/json; charset=utf-8
+ *
+ * @param HTTP_Request2_Response $res HTTP Response object
+ *
+ * @return void
+ */
+ protected function assertResponseJson200(HTTP_Request2_Response $res)
+ {
+ $this->assertEquals(200, $res->getStatus());
+ $this->assertEquals(
+ 'application/json; charset=utf-8',
+ $res->getHeader('content-type')
+ );
+ }
+
+
+
+ /**
+ * Writes a special unittest configuration file.
+ * The unittest config file is read when a GET request with unittestMode=1
+ * is sent, and the user allowed unittestmode in config.php.
+ *
+ * @param array $arConfig Array with config names as key and their value as
+ * value
+ *
+ * @return void
+ */
+ protected function setUnittestConfig($arConfig)
+ {
+ $str = '<' . "?php\n";
+ foreach ($arConfig as $name => $value) {
+ $str .= '$' . $name . ' = '
+ . var_export($value, true) . ";\n";
+ }
+
+ if (!is_dir($GLOBALS['datadir'])) {
+ $this->fail(
+ 'datadir not set or not a directory: ' . $GLOBALS['datadir']
+ );
+ }
+
+ $this->assertInternalType(
+ 'integer',
+ file_put_contents($GLOBALS['datadir'] . '/config.testing-tmp.php', $str),
+ 'Writing config.unittest.php failed'
+ );
+ }
+}
+?>
diff --git a/tests/UserArrayTest.php b/tests/UserArrayTest.php
new file mode 100644
index 0000000..a60e37f
--- /dev/null
+++ b/tests/UserArrayTest.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle user array model.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class UserArrayTest extends PHPUnit_Framework_TestCase
+{
+
+ public function testGetNameLongName()
+ {
+ $this->assertEquals(
+ 'John Doe',
+ SemanticScuttle_Model_UserArray::getName(
+ array(
+ 'name' => 'John Doe',
+ 'username' => 'jdoe'
+ )
+ )
+ );
+ }
+
+ public function testGetNameUsernameIfNameIsEmpty()
+ {
+ $this->assertEquals(
+ 'jdoe',
+ SemanticScuttle_Model_UserArray::getName(
+ array(
+ 'name' => '',
+ 'username' => 'jdoe'
+ )
+ )
+ );
+ }
+
+ public function testGetNameUsernameIfNameIsNotSet()
+ {
+ $this->assertEquals(
+ 'jdoe',
+ SemanticScuttle_Model_UserArray::getName(
+ array(
+ 'username' => 'jdoe'
+ )
+ )
+ );
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/tests/UserTest.php b/tests/UserTest.php
new file mode 100644
index 0000000..85d2204
--- /dev/null
+++ b/tests/UserTest.php
@@ -0,0 +1,527 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle user service.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class UserTest extends TestBase
+{
+ protected function setUp()
+ {
+ $this->us = SemanticScuttle_Service_Factory::get('User');
+ $this->us->deleteAll();
+ }
+
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::addUser
+ */
+ public function testAddUserPrivateKey()
+ {
+ $name = substr(md5(uniqid()), 0, 6);
+ $pkey = 'my-privateKey';
+ $id = $this->us->addUser(
+ $name, uniqid(), 'foo@example.org', $pkey
+ );
+ $this->assertNotEquals(false, $id);
+ $this->assertInternalType('integer', $id);
+
+ $arUser = $this->us->getUserByPrivateKey($pkey);
+ $this->assertNotEquals(false, $arUser, 'user not found by private key');
+ $this->assertEquals($id, $arUser['uId'], 'wrong user loaded');
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::updateUser
+ */
+ public function testUpdateUserFalseWhenIdNotNumeric()
+ {
+ $this->assertFalse(
+ $this->us->updateUser('foo', null, null, null, null, null)
+ );
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::updateUser
+ */
+ public function testUpdateUserPrivateKeyNewKeyEnabled()
+ {
+ $pkey = 'testUpdateUserPrivateKeyNewKey12';
+ $uid = $this->addUser();
+
+ $this->assertTrue(
+ $this->us->updateUser(
+ $uid, 'password', 'name', 'test@example.org', '', '',
+ $pkey, true
+ )
+ );
+ $arUser = $this->us->getUser($uid);
+ $this->assertInternalType('array', $arUser);
+ $this->assertEquals($pkey, $arUser['privateKey']);
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::updateUser
+ */
+ public function testUpdateUserPrivateKeyNewKeyDisabled()
+ {
+ $pkey = 'testUpdateUserPrivateKeyNewKeyDi';
+ $uid = $this->addUser();
+
+ $this->assertTrue(
+ $this->us->updateUser(
+ $uid, 'password', 'name', 'test@example.org', '', '',
+ $pkey, false
+ )
+ );
+ $arUser = $this->us->getUser($uid);
+ $this->assertInternalType('array', $arUser);
+ $this->assertEquals(
+ '-' . $pkey, $arUser['privateKey'],
+ 'private key did not get disabled'
+ );
+ }
+
+
+ /**
+ * Passing an empty string / NULL as key but enabling it
+ * should automatically create a new key.
+ *
+ * @covers SemanticScuttle_Service_User::updateUser
+ */
+ public function testUpdateUserPrivateKeyNoKeyEnabled()
+ {
+ $pkey = 'testUpdateUserPrivateKeyNoKeyEna';
+ $uid = $this->addUser();
+
+ $this->assertTrue(
+ $this->us->updateUser(
+ $uid, 'password', 'name', 'test@example.org', '', '',
+ null, true
+ )
+ );
+ $arUser = $this->us->getUser($uid);
+ $this->assertInternalType('array', $arUser);
+ $this->assertNotEquals(
+ '', $arUser['privateKey'], 'private key was not created'
+ );
+ }
+
+
+ /**
+ * Passing an empty string / NULL as key and disabling it
+ * should keep no key
+ *
+ * @covers SemanticScuttle_Service_User::updateUser
+ */
+ public function testUpdateUserPrivateKeyNoKeyDisabled()
+ {
+ $pkey = 'testUpdateUserPrivateKeyNoKeyDis';
+ $uid = $this->addUser();
+
+ $this->assertTrue(
+ $this->us->updateUser(
+ $uid, 'password', 'name', 'test@example.org', '', '',
+ null, false
+ )
+ );
+ $arUser = $this->us->getUser($uid);
+ $this->assertInternalType('array', $arUser);
+ $this->assertEquals(
+ '', $arUser['privateKey'], 'private key was set'
+ );
+ }
+
+
+ /**
+ * Passing an empty string / NULL as key and disabling it
+ * should keep no key
+ *
+ * @covers SemanticScuttle_Service_User::updateUser
+ */
+ public function testUpdateUserPrivateKeyExistingKeyEnabled()
+ {
+ $pkey = '12345678901234567890123456789012';
+ $uid = $this->addUser();
+
+ $this->assertTrue(
+ $this->us->updateUser(
+ $uid, 'password', 'name', 'test@example.org', '', '',
+ '-' . $pkey, true
+ )
+ );
+ $arUser = $this->us->getUser($uid);
+ $this->assertInternalType('array', $arUser);
+ $this->assertEquals(
+ $pkey, $arUser['privateKey'], 'private key was not enabled'
+ );
+ }
+
+ //FIXME: verify I cannot re-use private key of different user
+
+
+
+ /**
+ * Test that setting the current user ID is permanent.
+ * and that the current user array is the same ID
+ *
+ * @return void
+ */
+ public function testSetCurrentUserId()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+
+ $this->us->setCurrentUserId($uid);
+ $this->assertEquals($uid, $this->us->getCurrentUserId());
+
+ $user = $this->us->getCurrentUser();
+ $this->assertEquals($uid, $user['uId']);
+ }
+
+
+
+ /**
+ * Test that changing the current user also
+ * changes the current user array
+ *
+ * @return void
+ */
+ public function testSetCurrentUserIdChange()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+ $this->assertNotEquals($uid, $uid2);
+
+ $this->us->setCurrentUserId($uid);
+ $this->assertEquals($uid, $this->us->getCurrentUserId());
+
+ $user = $this->us->getCurrentUser();
+ $this->assertEquals($uid, $user['uId']);
+
+ //change it
+ $this->us->setCurrentUserId($uid2);
+ $this->assertEquals($uid2, $this->us->getCurrentUserId());
+
+ $user = $this->us->getCurrentUser();
+ $this->assertEquals($uid2, $user['uId']);
+ }
+
+
+
+ /**
+ * Test login() function with invalid creditentials
+ *
+ * @return void
+ */
+ public function testLoginInvalid()
+ {
+ $this->us->deleteAll();
+ $this->assertFalse(
+ $this->us->login('doesnot', 'exist', false)
+ );
+ }
+
+ public function testGetIdFromUserParamId()
+ {
+ $uid = $this->addUser();
+ $newId = $this->us->getIdFromUser($uid);
+ $this->assertInternalType('integer', $newId);
+ $this->assertEquals($uid, $newId);
+ }
+
+ public function testGetIdFromUserParamUsername()
+ {
+ $uid = $this->addUser('someusername');
+ $newId = $this->us->getIdFromUser('someusername');
+ $this->assertInternalType('integer', $newId);
+ $this->assertEquals($uid, $newId);
+ }
+
+
+
+ /**
+ * Check if getObjectUsers() without any user works
+ *
+ * @return void
+ */
+ public function testGetObjectUsersNone()
+ {
+ $users = $this->us->getObjectUsers();
+ $this->assertEquals(0, count($users));
+ }
+
+
+
+ /**
+ * Check if getObjectUsers() with a single user works
+ *
+ * @return void
+ */
+ public function testGetObjectUsersSingle()
+ {
+ $uid = $this->addUser();
+ $users = $this->us->getObjectUsers();
+ $this->assertEquals(1, count($users));
+ $this->assertInstanceOf('SemanticScuttle_Model_User', reset($users));
+ }
+
+
+
+ /**
+ * Check if getObjectUsers() with a several users works
+ *
+ * @return void
+ */
+ public function testGetObjectUsersMultiple()
+ {
+ $uid = $this->addUser();
+ $uid2 = $this->addUser();
+ $uid3 = $this->addUser();
+ $users = $this->us->getObjectUsers();
+ $this->assertEquals(3, count($users));
+ $this->assertInstanceOf('SemanticScuttle_Model_User', reset($users));
+ }
+
+
+
+ /**
+ * Test if the email validation function works
+ *
+ * @return void
+ */
+ public function testIsValidEmail()
+ {
+ $this->assertTrue(
+ $this->us->isValidEmail('foo@example.org')
+ );
+ $this->assertTrue(
+ $this->us->isValidEmail('foo-bar@semantic-scuttle.example.net')
+ );
+ $this->assertTrue(
+ $this->us->isValidEmail('2334ABC@302.example.org')
+ );
+
+ $this->assertFalse(
+ $this->us->isValidEmail('302.example.org')
+ );
+ $this->assertFalse(
+ $this->us->isValidEmail('foo@302')
+ );
+ $this->assertFalse(
+ $this->us->isValidEmail('foo@example!org')
+ );
+ $this->assertFalse(
+ $this->us->isValidEmail('foo@@example.org')
+ );
+ $this->assertFalse(
+ $this->us->isValidEmail('f@oo@example.org')
+ );
+ }
+
+
+ public function testGetUserByPrivateKeyEmptyKey()
+ {
+ $arUser = $this->us->getUserByPrivateKey(null);
+ $this->assertFalse($arUser);
+ }
+
+
+ public function testGetUserByPrivateKeyInvalid()
+ {
+ $arUser = $this->us->getUserByPrivateKey('foobar');
+ $this->assertFalse($arUser);
+
+ $arUser = $this->us->getUserByPrivateKey('%');
+ $this->assertFalse($arUser);
+ }
+
+
+ public function testGetUserByPrivateKeyValidKey()
+ {
+ $pkey = $this->us->getNewPrivateKey();
+ $uId = $this->addUser(null, null, $pkey);
+
+ $arUser = $this->us->getUserByPrivateKey($pkey);
+ $this->assertInternalType('array', $arUser);
+ $this->assertArrayHasKey('uId', $arUser);
+ $this->assertArrayHasKey('username', $arUser);
+
+ $this->assertEquals($uId, $arUser['uId']);
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::privateKeyExists
+ */
+ public function testPrivateKeyExistsEmpty()
+ {
+ $this->assertFalse($this->us->privateKeyExists(null));
+ $this->assertFalse($this->us->privateKeyExists(''));
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::privateKeyExists
+ */
+ public function testPrivateKeyExistsInvalid()
+ {
+ $this->assertFalse($this->us->privateKeyExists('-1'));
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::privateKeyExists
+ */
+ public function testPrivateKeyExists()
+ {
+ $randKey = $this->us->getNewPrivateKey();
+ $this->assertFalse($this->us->privateKeyExists($randKey));
+ $uid = $this->addUser(null, null, $randKey);
+
+ $this->us->setCurrentUserId($uid);
+ $this->assertEquals($uid, $this->us->getCurrentUserId());
+
+ $this->assertTrue($this->us->privateKeyExists($randKey));
+ }
+
+
+ /**
+ * @covers SemanticScuttle_Service_User::isPrivateKeyValid
+ */
+ public function testIsPrivateKeyValid()
+ {
+ $this->assertFalse(
+ $this->us->isPrivateKeyValid(null),
+ 'NULL is an invalid private key'
+ );
+
+ $randKey = $this->us->getNewPrivateKey();
+ $this->assertTrue(
+ $this->us->isPrivateKeyValid($randKey),
+ 'generated key should be valid'
+ );
+
+ $randKey2 = '-'.$this->us->getNewPrivateKey();
+ $this->assertFalse(
+ $this->us->isPrivateKeyValid($randKey2),
+ 'disabled privateKey should return false'
+ );
+ }
+
+
+ public function testLoginPrivateKeyInvalid()
+ {
+ /* normal user with enabled privateKey */
+ $randKey = $this->us->getNewPrivateKey();
+ $uid1 = $this->addUser('testusername', 'passw0rd', $randKey);
+ /* user that has disabled privateKey */
+ $randKey2 = '-'.$this->us->getNewPrivateKey();
+ $uid2 = $this->addUser('seconduser', 'passw0RD', $randKey2);
+
+ /* test invalid private key */
+ $this->assertFalse(
+ $this->us->loginPrivateKey('02848248084082408240824802408248')
+ );
+ }
+
+
+ public function testLoginPrivateKeyValidEnabledKey()
+ {
+ /* normal user with enabled privateKey */
+ $randKey = $this->us->getNewPrivateKey();
+ $uid1 = $this->addUser('testusername', 'passw0rd', $randKey);
+ /* user that has disabled privateKey */
+ $randKey2 = '-'.$this->us->getNewPrivateKey();
+ $uid2 = $this->addUser('seconduser', 'passw0RD', $randKey2);
+
+
+ /* test valid credentials with private key enabled */
+ $this->assertTrue(
+ $this->us->loginPrivateKey($randKey)
+ );
+ }
+
+
+ public function testLoginPrivateKeyInvalidEnabledKey()
+ {
+ /* normal user with enabled privateKey */
+ $randKey = $this->us->getNewPrivateKey();
+ $uid1 = $this->addUser('testusername', 'passw0rd', $randKey);
+ /* user that has disabled privateKey */
+ $randKey2 = '-'.$this->us->getNewPrivateKey();
+ $uid2 = $this->addUser('seconduser', 'passw0RD', $randKey2);
+
+
+ /* test valid credentials with private key enabled but invalid key */
+ $this->assertFalse(
+ $this->us->loginPrivateKey('123')
+ );
+ }
+
+
+ public function testLoginPrivateKeyValidDisabledKey()
+ {
+ /* normal user with enabled privateKey */
+ $randKey = $this->us->getNewPrivateKey();
+ $uid1 = $this->addUser('testusername', 'passw0rd', $randKey);
+ /* user that has disabled privateKey */
+ $randKey2 = '-'.$this->us->getNewPrivateKey();
+ $uid2 = $this->addUser('seconduser', 'passw0RD', $randKey2);
+
+ /* confirm user exists so future fails should be due to randkey */
+ $this->assertTrue(
+ $this->us->login('seconduser', 'passw0RD', false)
+ );
+
+ /* test valid credentials with private key disabled */
+ $this->assertFalse(
+ $this->us->loginPrivateKey($randKey2)
+ );
+ }
+
+
+ public function testLoginPrivateKeyInvalidDisabled()
+ {
+ /* normal user with enabled privateKey */
+ $randKey = $this->us->getNewPrivateKey();
+ $uid1 = $this->addUser('testusername', 'passw0rd', $randKey);
+ /* user that has disabled privateKey */
+ $randKey2 = '-'.$this->us->getNewPrivateKey();
+ $uid2 = $this->addUser('seconduser', 'passw0RD', $randKey2);
+
+ /* test valid credentials with private key disabled and invalid key */
+ $this->assertFalse(
+ $this->us->loginPrivateKey('-1')
+ );
+ $this->assertFalse(
+ $this->us->loginPrivateKey(null)
+ );
+ }
+
+}
+?>
diff --git a/tests/VoteTest.php b/tests/VoteTest.php
new file mode 100644
index 0000000..0073678
--- /dev/null
+++ b/tests/VoteTest.php
@@ -0,0 +1,536 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle voting system.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class VoteTest extends TestBase
+{
+ /**
+ * Vote service instance to test.
+ *
+ * @var SemanticScuttle_Service_Vote
+ */
+ protected $vs = null;
+
+ /**
+ * Bookmark service instance.
+ *
+ * @var SemanticScuttle_Service_Bookmark
+ */
+ protected $bs = null;
+
+
+
+ public function setUp()
+ {
+ $GLOBALS['enableVoting'] = true;
+ //FIXME: create true new instance
+ $this->vs = SemanticScuttle_Service_Factory::get('Vote');
+ $this->vs->deleteAll();
+
+ $this->bs = SemanticScuttle_Service_Factory::get('Bookmark');
+ }
+
+
+
+ /**
+ * Test getVoting() when no votes have been cast.
+ *
+ * @return void
+ */
+ public function testGetVotingZero()
+ {
+ $bid = $this->addBookmark();
+ $this->assertEquals(0, $this->vs->getVoting($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(0, $bm['bVoting']);
+ $this->assertEquals(0, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVoting() when one positive vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVotingOne()
+ {
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, 1, 1);
+ $this->assertEquals(1, $this->vs->getVoting($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVoting() when one nevative vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVotingMinusOne()
+ {
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, 1, -1);
+ $this->assertEquals(-1, $this->vs->getVoting($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(-1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVoting() when several votes have been cast.
+ *
+ * @return void
+ */
+ public function testGetVotingSum()
+ {
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, 1, 1);
+ $this->vs->vote($bid, 2, -1);
+ $this->vs->vote($bid, 3, 1);
+ $this->vs->vote($bid, 4, 1);
+ $this->assertEquals(2, $this->vs->getVoting($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(2, $bm['bVoting']);
+ $this->assertEquals(4, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVotes() when no vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVotesZero()
+ {
+ $bid = $this->addBookmark();
+ $this->assertEquals(0, $this->vs->getVotes($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(0, $bm['bVoting']);
+ $this->assertEquals(0, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVotes() when one vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVotesOne()
+ {
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, 1, 1);
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVotes() when several votes have been cast.
+ *
+ * @return void
+ */
+ public function testGetVotesMultiple()
+ {
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, 1, 1);
+ $this->vs->vote($bid, 2, -1);
+ $this->vs->vote($bid, 3, 1);
+ $this->vs->vote($bid, 4, 1);
+ $this->assertEquals(4, $this->vs->getVotes($bid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(2, $bm['bVoting']);
+ $this->assertEquals(4, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test hasVoted() when a no vote has been cast
+ *
+ * @return void
+ */
+ public function testHasVotedFalse()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertFalse($this->vs->hasVoted($bid, $uid));
+ }
+
+
+
+ /**
+ * Test hasVoted() when a vote has been cast
+ *
+ * @return void
+ */
+ public function testHasVotedTrue()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, $uid, 1);
+ $this->assertTrue($this->vs->hasVoted($bid, $uid));
+ }
+
+
+
+ /**
+ * Test hasVoted() when a vote has been cast for other bookmarks.
+ * Also verify that the bookmark voting did not change for
+ * the other bookmarks.
+ *
+ * @return void
+ */
+ public function testHasVotedFalseOthers()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $bid2 = $this->addBookmark();
+ $bid3 = $this->addBookmark();
+
+ $this->vs->vote($bid, $uid, 1);
+ $this->vs->vote($bid3, $uid, 1);
+
+ $this->assertFalse($this->vs->hasVoted($bid2, $uid));
+
+ $bm2 = $this->bs->getBookmark($bid2);
+ $this->assertEquals(0, $bm2['bVoting']);
+ $this->assertEquals(0, $bm2['bVotes']);
+ }
+
+
+
+ /**
+ * Test getVote() when no vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVoteNone()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertNull($this->vs->getVote($bid, $uid));
+ }
+
+
+
+ /**
+ * Test getVote() when a positive vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVoteOne()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, $uid, 1);
+ $this->assertEquals(1, $this->vs->getVote($bid, $uid));
+ }
+
+
+
+ /**
+ * Test getVote() when a negavitve vote has been cast.
+ *
+ * @return void
+ */
+ public function testGetVoteMinusOne()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->vs->vote($bid, $uid, -1);
+ $this->assertEquals(-1, $this->vs->getVote($bid, $uid));
+ }
+
+
+
+ /**
+ * Test vote() when voting is deactivated
+ *
+ * @return void
+ */
+ public function testVoteVotingDeactivated()
+ {
+ $GLOBALS['enableVoting'] = false;
+
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertFalse($this->vs->vote($bid, $uid, 1));
+ }
+
+
+
+ /**
+ * Test vote() with wrong vote parameter
+ *
+ * @return void
+ */
+ public function testVoteWrongVoteParam()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertFalse($this->vs->vote($bid, $uid, 2));
+ $this->assertFalse($this->vs->vote($bid, $uid, 0));
+ $this->assertFalse($this->vs->vote($bid, $uid, 1.5));
+ $this->assertFalse($this->vs->vote($bid, $uid, -1.1));
+ $this->assertFalse($this->vs->vote($bid, $uid, 'yes'));
+ $this->assertFalse($this->vs->vote($bid, $uid, 'no'));
+ }
+
+
+
+ /**
+ * Test vote() when the user already has voted
+ *
+ * @return void
+ */
+ public function testVoteHasVoted()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+
+ $bid = $this->addBookmark();
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test vote() with positive vote
+ *
+ * @return void
+ */
+ public function testVotePositive()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+ $this->assertEquals(1, $this->vs->getVote($bid, $uid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Test vote() with negative vote
+ *
+ * @return void
+ */
+ public function testVoteNegative()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+ $this->assertEquals(-1, $this->vs->getVote($bid, $uid));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(-1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+ }
+
+
+
+ /**
+ * Verify that changing the vote from positive to negative
+ * works.
+ *
+ * @return void
+ */
+ public function testVoteChangePosNeg()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+ $this->assertEquals(1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+
+ //change vote
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+ $this->assertEquals(-1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(-1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+ }
+
+
+
+ /**
+ * Verify that changing the vote from negative to positive
+ * works.
+ *
+ * @return void
+ */
+ public function testVoteChangeNegPos()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+ $this->assertEquals(-1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(-1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+
+ //change vote
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+ $this->assertEquals(1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+ }
+
+
+
+ /**
+ * Verify that changing the vote from postitive to positive
+ * has no strange effects
+ *
+ * @return void
+ */
+ public function testVoteChangePosPos()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+ $this->assertEquals(1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+
+ //change vote
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+ $this->assertEquals(1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+ }
+
+
+
+ /**
+ * Verify that changing the vote from negative to negative
+ * has no strange effects
+ *
+ * @return void
+ */
+ public function testVoteChangeNegNeg()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+ $this->assertEquals(-1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(-1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+
+ //change vote to same value
+ $this->assertTrue($this->vs->vote($bid, $uid, -1));
+ $this->assertEquals(-1, $this->vs->getVote($bid, $uid));
+ $this->assertEquals(1, $this->vs->getVotes($bid));
+
+ $b = $this->bs->getBookmark($bid);
+ $this->assertEquals(-1, $b['bVoting']);
+ $this->assertEquals(1, $b['bVotes']);
+ }
+
+
+
+ /**
+ * Test that rewriting votings does work
+ *
+ * @return void
+ */
+ public function testRewriteVotings()
+ {
+ $uid = 1;
+ $bid = $this->addBookmark();
+ $this->assertTrue($this->vs->vote($bid, $uid, 1));
+
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+
+ $this->vs->deleteAll();
+ //we assume that $vs->deleteAll() does *not* reset
+ //voting in bookmarks table
+ $bm = $this->bs->getBookmark($bid);
+ $this->assertEquals(1, $bm['bVoting']);
+ $this->assertEquals(1, $bm['bVotes']);
+
+ $this->vs->rewriteVotings();
+ $bm = $this->bs->getBookmark($bid);
+ //now it should be reset to 0
+ $this->assertEquals(0, $bm['bVoting']);
+ $this->assertEquals(0, $bm['bVotes']);
+ }
+
+}//class VoteTest extends TestBase
+?> \ No newline at end of file
diff --git a/tests/ajax/GetAdminLinkedTagsTest.php b/tests/ajax/GetAdminLinkedTagsTest.php
new file mode 100644
index 0000000..43cb17a
--- /dev/null
+++ b/tests/ajax/GetAdminLinkedTagsTest.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the ajax linked admin tags script
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @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';
+
+
+ /**
+ * 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->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('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->assertResponseJson200($res);
+
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('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->assertResponseJson200($res);
+
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+
+ //we should have only one subtag now, the admin one
+ $this->assertEquals(1, count($data));
+ $this->assertEquals('adminsubtag', $data[0]->data->title);
+ }
+}
+?> \ No newline at end of file
diff --git a/tests/ajax/GetAdminTagsTest.php b/tests/ajax/GetAdminTagsTest.php
new file mode 100644
index 0000000..8bf8a83
--- /dev/null
+++ b/tests/ajax/GetAdminTagsTest.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the ajax getadmintags.php script
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class ajax_GetAdminTagsTest extends TestBaseApi
+{
+ protected $urlPart = 'ajax/getadmintags.php';
+
+
+ public function testTags()
+ {
+ list($user1, $uname1) = $this->addUserData();
+ $user2 = $this->addUser();
+ $this->addBookmark($user1, null, 0, array('admintag', 'admintag2'));
+ $this->addBookmark($user2, null, 0, array('lusertag', 'lusertag2'));
+
+ $this->setUnittestConfig(
+ array(
+ 'admin_users' => array($uname1)
+ )
+ );
+
+ $req = $this->getRequest('?unittestMode=1');
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(2, count($data));
+ $this->assertContains('admintag', $data);
+ $this->assertContains('admintag2', $data);
+ }
+
+ public function testParameterBeginsWith()
+ {
+ list($user1, $uname1) = $this->addUserData();
+ $this->addBookmark($user1, null, 0, array('foo', 'foobar', 'bar'));
+
+ $this->setUnittestConfig(
+ array(
+ 'admin_users' => array($uname1)
+ )
+ );
+
+ $req = $this->getRequest('?unittestMode=1&beginsWith=foo');
+ $res = $req->send();
+ $data = json_decode($res->getBody());
+ $this->assertResponseJson200($res);
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(2, count($data));
+ $this->assertContains('foo', $data);
+ $this->assertContains('foobar', $data);
+ }
+
+
+
+ public function testParameterLimit()
+ {
+ list($user1, $uname1) = $this->addUserData();
+ list($user2, $uname2) = $this->addUserData();
+ $this->addBookmark($user1, null, 0, array('foo', 'foobar'));
+ $this->addBookmark($user2, null, 0, array('foo', 'bar'));
+
+ $this->setUnittestConfig(
+ array(
+ 'admin_users' => array($uname1, $uname2)
+ )
+ );
+
+ $req = $this->getRequest('?unittestMode=1&limit=1');
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(1, count($data));
+ $this->assertContains('foo', $data);
+
+ $req = $this->getRequest('?unittestMode=1&limit=2');
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(2, count($data));
+ $this->assertContains('foo', $data);
+
+ $req = $this->getRequest('?unittestMode=1&limit=3');
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(3, count($data));
+ $this->assertContains('foo', $data);
+ $this->assertContains('foobar', $data);
+ $this->assertContains('bar', $data);
+ }
+
+}
+
+
+?> \ No newline at end of file
diff --git a/tests/ajax/GetContactTagsTest.php b/tests/ajax/GetContactTagsTest.php
new file mode 100644
index 0000000..268ed66
--- /dev/null
+++ b/tests/ajax/GetContactTagsTest.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Unit tests for the ajax getcontacttags.php script
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class ajax_GetContactTagsTest extends TestBaseApi
+{
+ protected $urlPart = 'ajax/getcontacttags.php';
+
+
+ /**
+ * If no user is logged in, no data are returned
+ */
+ public function testNoUserLoggedIn()
+ {
+ $res = $this->getRequest()->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(0, count($data));
+ }
+
+
+ public function testUserLoggedInWatchlist()
+ {
+ list($req, $uId) = $this->getLoggedInRequest();
+ $this->addBookmark($uId, null, 0, array('public', 'public2'));
+
+ $user2 = $this->addUser();
+ $this->us->setCurrentUserId($uId);
+ $this->us->setWatchStatus($user2);
+ //uId watches user2 now
+ $this->addBookmark($user2, null, 0, array('user2tag'));
+
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(3, count($data));
+ $this->assertContains('public', $data);
+ $this->assertContains('public2', $data);
+ $this->assertContains('user2tag', $data);
+ }
+
+ public function testParameterBeginsWith()
+ {
+ list($req, $uId) = $this->getLoggedInRequest('?beginsWith=bar');
+ $this->addBookmark($uId, null, 0, array('foobar', 'barmann'));
+
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(1, count($data));
+ $this->assertContains('barmann', $data);
+ }
+
+ public function testParameterLimit()
+ {
+ list($req, $uId) = $this->getLoggedInRequest('?limit=2');
+ $this->addBookmark($uId, null, 0, array('foo', 'bar', 'baz', 'omg'));
+
+ $res = $req->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(2, count($data));
+
+ $req2 = $this->getRequest('?limit=3');
+ $req2->setCookieJar($req->getCookieJar());
+ $res = $req2->send();
+ $this->assertResponseJson200($res);
+ $data = json_decode($res->getBody());
+ $this->assertInternalType('array', $data);
+ $this->assertEquals(3, count($data));
+ }
+}
+
+
+?> \ No newline at end of file
diff --git a/tests/data/BookmarkTest_deliciousbookmarks.xml b/tests/data/BookmarkTest_deliciousbookmarks.xml
new file mode 100755
index 0000000..87c67dc
--- /dev/null
+++ b/tests/data/BookmarkTest_deliciousbookmarks.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<posts user="dpuser" update="2011-03-24T21:09:22Z" tag="" total="3">
+ <post href="http://www.example.org/testdefaultprivacyposts_import1/" hash="4f8533885bb5740b98b6415140a0c8c6" description="Test bookmark 1 for default privacy." tag="tag1, tag2" time="2011-03-24T21:09:11Z" extended="" meta="5c80704730a1bf1b9eb615d4ba7a59bd" />
+ <post href="http://www.example.org/testdefaultprivacyposts_import2/" hash="21eee08d7945ac23c4cc2b6d08d02212" description="Test bookmark 2 for default privacy." tag="tag2, tag3" time="2011-03-24T21:08:33Z" extended="" meta="7514ab84f61ba1dd0413572f7c12348d" />
+ <post href="http://www.example.org/testdefaultprivacyposts_import3/" hash="cd64042205a083f1cf1e009344daef84" description="Test bookmark 3 for default privacy." tag="tag1, tag2, tag3" time="2011-03-24T21:07:44Z" extended="" meta="9cb24fddbc011e9634595d2f5e1a6537" />
+</posts>
+<!-- fe09.api.del.ac4.yahoo.net uncompressed/chunked Thur Mar 24 21:09:33 UTC 2011 -->
diff --git a/tests/data/BookmarkTest_deliciousbookmarks_longdesc_3469257.xml b/tests/data/BookmarkTest_deliciousbookmarks_longdesc_3469257.xml
new file mode 100644
index 0000000..9c09f29
--- /dev/null
+++ b/tests/data/BookmarkTest_deliciousbookmarks_longdesc_3469257.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<posts user="dpuser" update="2012-01-20T21:09:22Z" tag="" total="1">
+ <post href="http://www.example.org/" hash="4f8533885bb5740b98b6415140a0c8c6" description="title" tag="unittest" time="2012-01-20T21:09:11Z" extended="0.........10........20........30........40........50........60........70........80........90........100.......110.......120.......130.......140.......150.......160.......170.......180.......190.......200.......210.......220.......230.......240.......250.......260.......270.......280.......290......." meta="5c80704730a1bf1b9eb615d4ba7a59bd" />
+</posts> \ No newline at end of file
diff --git a/tests/data/BookmarkTest_deliciousbookmarks_private.xml b/tests/data/BookmarkTest_deliciousbookmarks_private.xml
new file mode 100644
index 0000000..0ad8142
--- /dev/null
+++ b/tests/data/BookmarkTest_deliciousbookmarks_private.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><posts tag="" total="1" user="cweiske"><post description="Christians Tagebuch" extended="" hash="d82ca757a4583a24260a1126b5dafb0d" href="http://cweiske.de/tagebuch/" private="yes" shared="no" tag="private" time="2012-01-20T20:18:56Z"/></posts> \ No newline at end of file
diff --git a/tests/data/BookmarkTest_netscapebookmarks.html b/tests/data/BookmarkTest_netscapebookmarks.html
new file mode 100755
index 0000000..305662c
--- /dev/null
+++ b/tests/data/BookmarkTest_netscapebookmarks.html
@@ -0,0 +1,27 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+
+<!-- This is an automatically generated file.
+
+It will be read and overwritten.
+
+Do Not Edit! -->
+
+<TITLE>Bookmarks for testuser</TITLE>
+
+<H1>Bookmarks for testuser</H1>
+
+
+<DL><p>
+
+ <DT><A HREF="http://www.example.org/testdefaultprivacyposts_importNetscape1" ADD_DATE="1301178264" LAST_VISIT="1301178075" LAST_MODIFIED="1301178075">Test bookmark 1 for default privacy.</A>
+ <DT><A HREF="http://www.example.org/testdefaultprivacyposts_importNetscape2" ADD_DATE="1301178264" LAST_VISIT="1301178075" LAST_MODIFIED="1301178075">Test bookmark 2 for default privacy.</A>
+ <DT><A HREF="http://www.example.org/testdefaultprivacyposts_importNetscape3" ADD_DATE="1301178264" LAST_VISIT="1301178075" LAST_MODIFIED="1301178075">Test bookmark 3 for default privacy.</A>
+ <DT><A HREF="http://www.thisbookmarkwillnotbeadded.com" ADD_DATE="1301178264" LAST_VISIT="1301178075" LAST_MODIFIED="1301178075">This bookmark will be ignored by importNetscape.php.</A>
+
+</DL><p>
+
+<DL><p>
+
+
+
+</DL><p>
diff --git a/tests/dox.html b/tests/dox.html
new file mode 100644
index 0000000..98f6d00
--- /dev/null
+++ b/tests/dox.html
@@ -0,0 +1 @@
+<html><body><h2>TagsCache</h2><ul> \ No newline at end of file
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
new file mode 100644
index 0000000..3956445
--- /dev/null
+++ b/tests/phpunit.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<phpunit strict="true" colors="true"
+ bootstrap="prepare.php"
+>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="false">
+ <directory suffix=".php">../src/SemanticScuttle/</directory>
+ <directory suffix=".php">../data/templates/</directory>
+ <directory suffix=".php">../www/</directory>
+ <exclude>
+ <directory suffix=".php">../src/SemanticScuttle/db/</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file
diff --git a/tests/prepare.php b/tests/prepare.php
new file mode 100644
index 0000000..ea4d77d
--- /dev/null
+++ b/tests/prepare.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Prepare the application for unit testing
+ */
+//that's needed in constants.php
+$_SERVER['HTTP_HOST'] = 'http://localhost/';
+
+define('UNIT_TEST_MODE', true);
+
+if ('@data_dir@' == '@' . 'data_dir@') {
+ //non pear-installation (i.e. git checkout)
+ 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';
+
+if ($GLOBALS['debugMode'] == true
+ && $GLOBALS['dbtype'] == 'mysql4'
+) {
+ echo "\n"
+ . '!! The combination of debugMode and dbtype==mysql4'
+ . ' will wreck some tests' . "\n\n";
+}
+?> \ No newline at end of file
diff --git a/tests/www/bookmarksTest.php b/tests/www/bookmarksTest.php
new file mode 100755
index 0000000..ac549d8
--- /dev/null
+++ b/tests/www/bookmarksTest.php
@@ -0,0 +1,156 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_bookmarksTest extends TestBaseApi
+{
+ protected $urlPart = 'bookmarks.php';
+
+ /**
+ * Test that the default privacy setting is selected in the Privacy
+ * drop-down list when adding a new bookmark, sending the form and
+ * missing the title and the privacy setting.
+ */
+ public function testDefaultPrivacyBookmarksAddMissingTitleMissingPrivacy()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 2))
+ );
+
+ list($req, $uId) = $this->getLoggedInRequest();
+ $user = $this->us->getUser($uId);
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->setUrl($this->getTestUrl('/' . $user['username'] . '?action=get'));
+ $req->addPostParameter('submitted', '1');
+ $response = $req->send();
+ $response_body = $response->getBody();
+
+ $x = simplexml_load_string($response_body);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath(
+ '//ns:select[@name="status"]/ns:option[@selected="selected"]'
+ );
+ $this->assertEquals(1, count($elements), 'No selected status option found');
+ $this->assertEquals(2, (string)$elements[0]['value']);
+ }//end testDefaultPrivacyBookmarksAddMissingTitleMissingPrivacy
+
+
+
+ /**
+ * Test that the default privacy setting is selected in the Privacy
+ * drop-down list when a new bookmark is being created.
+ */
+ public function testDefaultPrivacyBookmarksAdd()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 1))
+ );
+ list($req, $uId) = $this->getLoggedInRequest();
+
+ $user = $this->us->getUser($uId);
+ $req->setUrl($this->getTestUrl('/' . $user['username'] . '?action=add'));
+ $response = $req->send();
+ $response_body = $response->getBody();
+ $this->assertNotEquals('', $response_body, 'Response is empty');
+
+ $x = simplexml_load_string($response_body);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath(
+ '//ns:select[@name="status"]/ns:option[@selected="selected"]'
+ );
+ $this->assertEquals(1, count($elements), 'No selected status option found');
+ $this->assertEquals(1, (string)$elements[0]['value']);
+ }//end testDefaultPrivacyBookmarksAdd
+
+
+
+ /**
+ * Test that the private RSS link exists when a user
+ * has a private key and is enabled
+ */
+ public function testVerifyPrivateRSSLinkExists()
+ {
+ list($req, $uId) = $this->getLoggedInRequest('?unittestMode=1', true, true);
+
+ $user = $this->us->getUser($uId);
+ $req->setUrl($this->getTestUrl('/' . $user['username']));
+ $response = $req->send();
+ $response_body = $response->getBody();
+ $this->assertNotEquals('', $response_body, 'Response is empty');
+
+ $x = simplexml_load_string($response_body);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath(
+ '//ns:link[@rel="alternate" and @type="application/rss+xml"]'
+ );
+ $this->assertEquals(
+ 2, count($elements), 'Number of Links in Head not correct'
+ );
+ $this->assertContains('privateKey=', (string)$elements[1]['href']);
+ }//end testVerifyPrivateRSSLinkExists
+
+
+
+ /**
+ * Test that the private RSS link doesn't exists when a user
+ * does not have a private key or is not enabled
+ */
+ public function testVerifyPrivateRSSLinkDoesNotExist()
+ {
+ list($req, $uId) = $this->getLoggedInRequest('?unittestMode=1', true);
+
+ $user = $this->us->getUser($uId);
+ $req->setUrl($this->getTestUrl('/' . $user['username']));
+ $response = $req->send();
+ $response_body = $response->getBody();
+ $this->assertNotEquals('', $response_body, 'Response is empty');
+
+ $x = simplexml_load_string($response_body);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath(
+ '//ns:link[@rel="alternate" and @type="application/rss+xml"]'
+ );
+ $this->assertEquals(
+ 1, count($elements), 'Number of Links in Head not correct'
+ );
+ $this->assertNotContains('privateKey=', (string)$elements[0]['href']);
+ }//end testVerifyPrivateRSSLinkDoesNotExist
+
+
+
+ /**
+ * We once had the bug that URLs with special characters were escaped too
+ * often. & -> &amp;
+ */
+ public function testAddressEncoding()
+ {
+ $this->addBookmark(null, 'http://example.org?foo&bar=baz');
+
+ //get rid of bookmarks.php
+ $this->url = $GLOBALS['unittestUrl'];
+
+ $html = $this->getRequest()->send()->getBody();
+ $x = simplexml_load_string($html);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath('//ns:a[@class="taggedlink"]');
+ $this->assertEquals(
+ 1, count($elements), 'Number of links is not 1'
+ );
+ $this->assertEquals(
+ 'http://example.org?foo&bar=baz',
+ (string)$elements[0]['href']
+ );
+ }
+
+}//end class www_bookmarksTest
+?>
diff --git a/tests/www/editTest.php b/tests/www/editTest.php
new file mode 100755
index 0000000..1e0fbd5
--- /dev/null
+++ b/tests/www/editTest.php
@@ -0,0 +1,48 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_editTest extends TestBaseApi
+{
+ protected $urlPart = 'api/posts/add';
+
+ /**
+ * Test that the default privacy setting is used when an existing
+ * bookmark is updated with edit.php.
+ */
+ public function testDefaultPrivacyEdit()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 2))
+ );
+
+ list($req, $uId) = $this->getLoggedInRequest('?unittestMode=1');
+ $cookies = $req->getCookieJar();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->addPostParameter('url', 'http://www.example.org/testdefaultprivacyposts_edit');
+ $req->addPostParameter('description', 'Test bookmark 2 for default privacy.');
+ $req->addPostParameter('status', '0');
+ $res = $req->send();
+ $this->assertEquals(
+ 200, $res->getStatus(),
+ 'Adding bookmark failed: ' . $res->getBody());
+ $bms = $this->bs->getBookmarks(0, null, $uId);
+ $bm = reset($bms['bookmarks']);
+ $bmId = $bm['bId'];
+
+ $reqUrl = $GLOBALS['unittestUrl'] . 'edit.php/' . $bmId . '?unittestMode=1';
+ $req2 = new HTTP_Request2($reqUrl, HTTP_Request2::METHOD_POST);
+ $req2->setCookieJar($cookies);
+ $req2->addPostParameter('address', 'http://www.example.org/testdefaultprivacyposts_edit');
+ $req2->addPostParameter('title', 'Test bookmark 2 for default privacy.');
+ $req2->addPostParameter('submitted', '1');
+ $res = $req2->send();
+
+ $this->assertEquals(302, $res->getStatus(), 'Editing bookmark failed');
+
+ $bm = $this->bs->getBookmark($bmId);
+ $this->assertEquals('2', $bm['bStatus']);
+ }//end testDefaultPrivacyEdit
+
+}//end class www_editTest
+?>
diff --git a/tests/www/importNetscapeTest.php b/tests/www/importNetscapeTest.php
new file mode 100755
index 0000000..9d4cacd
--- /dev/null
+++ b/tests/www/importNetscapeTest.php
@@ -0,0 +1,33 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_importNetscapeTest extends TestBaseApi
+{
+ protected $urlPart = 'importNetscape.php';
+
+ /**
+ * Test that the default privacy setting is used when bookmarks
+ * are imported from an HTML bookmarks file using importNetscape.php.
+ */
+ public function testDefaultPrivacyImportNetscape()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 1))
+ );
+ list($req, $uId) = $this->getLoggedInRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->setUrl($GLOBALS['unittestUrl'] . 'importNetscape.php' . '?unittestMode=1');
+ $req->addUpload('userfile', dirname(__FILE__) . '/../data/BookmarkTest_netscapebookmarks.html');
+ $res = $req->send();
+ $this->assertEquals(200, $res->getStatus(), 'Bookmark import failed');
+
+ $this->us->setCurrentUserId($uId);
+ $bms = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(3, count($bms['bookmarks']));
+ $bm = reset($bms['bookmarks']);
+ $this->assertEquals('1', $bm['bStatus']);
+ }//end testDefaultPrivacyImportNetscape
+
+}//end class www_importNetscapeTest
+?>
diff --git a/tests/www/importTest.php b/tests/www/importTest.php
new file mode 100755
index 0000000..895a320
--- /dev/null
+++ b/tests/www/importTest.php
@@ -0,0 +1,33 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_importTest extends TestBaseApi
+{
+ protected $urlPart = 'import.php';
+
+ /**
+ * Test that the default privacy setting is used when bookmarks
+ * are imported from an XML bookmarks file using import.php.
+ */
+ public function testDefaultPrivacyImport()
+ {
+ $this->setUnittestConfig(
+ array('defaults' => array('privacy' => 2))
+ );
+ list($req, $uId) = $this->getLoggedInRequest();
+ $req->setMethod(HTTP_Request2::METHOD_POST);
+ $req->setUrl($GLOBALS['unittestUrl'] . 'import.php' . '?unittestMode=1');
+ $req->addUpload('userfile', dirname(__FILE__) . '/../data/BookmarkTest_deliciousbookmarks.xml');
+ $res = $req->send();
+ $this->assertEquals(302, $res->getStatus(), 'Bookmark import failed');
+
+ $this->us->setCurrentUserId($uId);
+ $bms = $this->bs->getBookmarks(0, null, $uId);
+ $this->assertEquals(3, count($bms['bookmarks']));
+ $bm = reset($bms['bookmarks']);
+ $this->assertEquals('2', $bm['bStatus']);
+ }//end testDefaultPrivacyImport
+
+}//end class www_importTest
+?>
diff --git a/tests/www/indexTest.php b/tests/www/indexTest.php
new file mode 100644
index 0000000..503fd1f
--- /dev/null
+++ b/tests/www/indexTest.php
@@ -0,0 +1,58 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_indexTest extends TestBaseApi
+{
+ protected $urlPart = '';
+
+ /**
+ * Test that the private rss feed exists when user is setup
+ * with a private key and is enabled
+ */
+ public function testVerifyPrivateRSSLinkExists()
+ {
+ list($req, $uId) = $this->getLoggedInRequest('?unittestMode=1', true, true);
+
+ $user = $this->us->getUser($uId);
+ $response = $req->send();
+ $response_body = $response->getBody();
+
+ $this->assertNotEquals('', $response_body, 'Response is empty');
+
+ $x = simplexml_load_string($response_body);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath('//ns:link[@rel="alternate" and @type="application/rss+xml"]');
+ $this->assertEquals(2, count($elements), 'Number of Links in Head not correct');
+ $this->assertContains('privateKey=', (string)$elements[1]['href']);
+ }//end testVerifyPrivateRSSLinkExists
+
+
+
+ /**
+ * Test that the private RSS link doesn't exists when a user
+ * does not have a private key, or the private key is not enabled
+ */
+ public function testVerifyPrivateRSSLinkDoesNotExist()
+ {
+ list($req, $uId) = $this->getLoggedInRequest('?unittestMode=1', true);
+
+ $user = $this->us->getUser($uId);
+ $response = $req->send();
+ $response_body = $response->getBody();
+ $this->assertNotEquals('', $response_body, 'Response is empty');
+
+ $x = simplexml_load_string($response_body);
+ $ns = $x->getDocNamespaces();
+ $x->registerXPathNamespace('ns', reset($ns));
+
+ $elements = $x->xpath('//ns:link[@rel="alternate" and @type="application/rss+xml"]');
+ $this->assertEquals(1, count($elements), 'Number of Links in Head not correct');
+ $this->assertNotContains('privateKey=', (string)$elements[0]['href']);
+ }//end testVerifyPrivateRSSLinkDoesNotExist
+
+
+}//end class www_bookmarksTest
+?>
diff --git a/tests/www/rssTest.php b/tests/www/rssTest.php
new file mode 100644
index 0000000..71d0198
--- /dev/null
+++ b/tests/www/rssTest.php
@@ -0,0 +1,151 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_rssTest extends TestBaseApi
+{
+ protected $urlPart = 'rss.php';
+
+
+ /**
+ * Verifies that the given number of feed items exist in the feed
+ * XML tree.
+ *
+ * @var SimpleXMLElement $simpleXml RSS feed root element
+ * @var integer $nCount Number of expected feed items
+ * @var string $msg Error message
+ */
+ protected function assertItemCount(
+ SimpleXMLElement $simpleXml, $nCount, $msg = null
+ ) {
+ $this->assertEquals($nCount, count($simpleXml->channel->item), $msg);
+ }
+
+
+
+
+ /**
+ * A private bookmark should not show up in the global rss feed if the
+ * user is not logged in nor passes the private key
+ */
+ public function testAllPrivateBookmarkNotLoggedIn()
+ {
+ list($uId, $username) = $this->addUserData();
+ $this->addBookmark(
+ $uId, null, SemanticScuttle_Model_Bookmark::SPRIVATE
+ );
+
+ $req = $this->getRequest();
+ $response_body = $req->send()->getBody();
+
+ $rss = simplexml_load_string($response_body);
+ $this->assertItemCount($rss, 0, 'I see a private bookmark');
+ }
+
+
+
+ /**
+ * A private bookmark should not show up in the user's rss feed if the
+ * user is not logged in nor passes the private key
+ */
+ public function testUserPrivateBookmarkNotLoggedIn()
+ {
+ list($uId, $username) = $this->addUserData();
+ $this->addBookmark(
+ $uId, null, SemanticScuttle_Model_Bookmark::SPRIVATE
+ );
+
+ $req = $this->getRequest('/' . $username);
+ $response_body = $req->send()->getBody();
+
+ $rss = simplexml_load_string($response_body);
+ $this->assertItemCount($rss, 0, 'I see a private bookmark');
+ }
+
+
+
+
+ /**
+ * Test the global feed by passing the private key
+ */
+ public function testAllPrivateBookmarkWithPrivateKey()
+ {
+ list($uId, $username, $password, $privateKey) = $this->addUserData(
+ null, null, true
+ );
+ $this->addBookmark(
+ $uId, null, SemanticScuttle_Model_Bookmark::SPRIVATE,
+ null, 'private bookmark'
+ );
+
+ $req = $this->getRequest('?privateKey=' . $privateKey);
+ $response_body = $req->send()->getBody();
+
+ $rss = simplexml_load_string($response_body);
+ $this->assertItemCount($rss, 1, 'I miss the private bookmark');
+ $this->assertEquals(
+ 'private bookmark', (string)$rss->channel->item[0]->title
+ );
+ }
+
+
+
+ /**
+ * Test the user feed by passing the private key
+ */
+ public function testUserPrivateBookmarkWithPrivateKey()
+ {
+ list($uId, $username, $password, $privateKey) = $this->addUserData(
+ null, null, true
+ );
+ $this->addBookmark(
+ $uId, null, SemanticScuttle_Model_Bookmark::SPRIVATE,
+ null, 'private bookmark'
+ );
+
+ $req = $this->getRequest('/' . $username . '?privateKey=' . $privateKey);
+ $response_body = $req->send()->getBody();
+
+ $rss = simplexml_load_string($response_body);
+ $this->assertItemCount($rss, 1, 'I miss the private bookmark');
+ $this->assertEquals(
+ 'private bookmark', (string)$rss->channel->item[0]->title
+ );
+ }
+
+
+
+ /**
+ * Verify that fetching the feed with a private key
+ * does not keep you logged in
+ */
+ public function testUserPrivateKeyDoesNotKeepLoggedYouIn()
+ {
+ list($uId, $username, $password, $privateKey) = $this->addUserData(
+ null, null, true
+ );
+ $this->addBookmark(
+ $uId, null, SemanticScuttle_Model_Bookmark::SPRIVATE,
+ null, 'private bookmark'
+ );
+
+ $req = $this->getRequest('/' . $username . '?privateKey=' . $privateKey);
+ $cookies = $req->setCookieJar()->getCookieJar();
+ $response_body = $req->send()->getBody();
+
+ $rss = simplexml_load_string($response_body);
+ $items = $rss->channel->item;
+
+ $this->assertEquals(1, count($items), 'I miss the private bookmark');
+ $this->assertEquals('private bookmark', (string)$items[0]->title);
+
+ //request the feed again, with the same cookies
+ $req = $this->getRequest('/' . $username);
+ $req->setCookieJar($cookies);
+ $response_body = $req->send()->getBody();
+ $rss = simplexml_load_string($response_body);
+ $this->assertItemCount($rss, 0, 'I still see the private bookmark');
+ }
+
+}//end class www_rssTest
+?>
diff --git a/tests/www/searchTest.php b/tests/www/searchTest.php
new file mode 100644
index 0000000..89af32d
--- /dev/null
+++ b/tests/www/searchTest.php
@@ -0,0 +1,70 @@
+<?php
+require_once dirname(__FILE__) . '/../prepare.php';
+require_once 'HTTP/Request2.php';
+
+class www_SearchTest extends TestBaseApi
+{
+ protected $urlPart = 'search.php';
+
+
+ /**
+ * Some browsers using opensearch do "urlencode" on the terms,
+ * for example Firefox. Multiple terms separated with space
+ * appear as "foo+bar" in the URL.
+ */
+ public function testMultipleTermsUrlEncoded()
+ {
+ $this->addBookmark(null, null, 0, null, 'unittest foo bar');
+ $res = $this->getRequest('/all/foo+bar')->send();
+ $this->assertSelectCount(
+ '.xfolkentry', true, $res->getBody(),
+ 'No bookmark found', false
+ );
+
+ $res = $this->getRequest('/all/baz+bat')->send();
+ $this->assertSelectCount(
+ '.xfolkentry', false, $res->getBody(),
+ 'Bookmarks found', false
+ );
+ }
+
+
+ /**
+ * Some browsers using opensearch do "rawurlencode" on the terms,
+ * for example Opera. Multiple terms separated with space
+ * appear as "foo%20bar" in the URL.
+ */
+ public function testMultipleTermsRawUrlEncoded()
+ {
+ $this->addBookmark(null, null, 0, null, 'unittest foo bar');
+ $res = $this->getRequest('/all/foo%20bar')->send();
+ $this->assertSelectCount(
+ '.xfolkentry', true, $res->getBody(),
+ 'No bookmark found', false
+ );
+
+ $res = $this->getRequest('/all/baz%20bat')->send();
+ $this->assertSelectCount(
+ '.xfolkentry', false, $res->getBody(),
+ 'Bookmarks found', false
+ );
+ }
+
+
+ public function testMultipleTags()
+ {
+ $this->markTestSkipped(
+ 'FIXME: SemanticScuttle currently does not search multiple tags'
+ );
+
+ $this->addBookmark(null, null, 0, array('foo', 'bar'));
+ $res = $this->getRequest('/all/foo+bar')->send();
+ $this->assertSelectCount(
+ '.xfolkentry', true, $res->getBody(),
+ 'No bookmark found', false
+ );
+ }
+
+}
+
+?>
diff --git a/www/.htaccess b/www/.htaccess
new file mode 100644
index 0000000..5799444
--- /dev/null
+++ b/www/.htaccess
@@ -0,0 +1,40 @@
+#####################################
+# the first part of this file is used if you configure your scuttle for nice urls
+# (see $cleanurls in config.inc.php)
+#####################################
+
+# See https://stackoverflow.com/questions/4558629/mod-rewrite-not-behaving-nicely
+# http://www.bennadel.com/blog/2218-Negotiation-Discovered-File-s-Matching-Request-None-Could-Be-Negotiated.htm
+# https://httpd.apache.org/docs/current/content-negotiation.html
+Options -MultiViews +FollowSymLinks
+
+# Rewrite clean URLs onto real files
+<IfModule mod_rewrite.c>
+Options +FollowSymlinks
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME}.php -f
+RewriteRule ^([^/.]+)/?(.*)$ /$1.php/$2 [QSA,L]
+RewriteRule ^api/([a-z]+)/([a-z]+) /api/$1_$2.php
+</IfModule>
+
+
+#####################################
+# This second part is used to speed-up the application
+#####################################
+
+<IfModule mod_expires.c>
+ExpiresActive On
+
+ExpiresByType image/ico "access plus 15 days"
+ExpiresByType image/png "access plus 15 days"
+ExpiresByType image/gif "access plus 15 days"
+ExpiresByType text/js "access plus 15 days"
+ExpiresByType text/css "access plus 15 days"
+</IfModule>
+
+
+<IfModule mod_deflate.c>
+#<FilesMatch "\.(js|css)$">
+SetOutputFilter DEFLATE
+#</FilesMatch>
+</IfModule>
diff --git a/www/about.php b/www/about.php
new file mode 100644
index 0000000..9bd37b0
--- /dev/null
+++ b/www/about.php
@@ -0,0 +1,27 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004, 2005 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+$tplVars['pagetitle'] = T_('About');
+$tplVars['subtitle'] = T_('About');
+
+$templateservice->loadTemplate('about.tpl', $tplVars);
+?> \ No newline at end of file
diff --git a/www/admin.php b/www/admin.php
new file mode 100644
index 0000000..034872a
--- /dev/null
+++ b/www/admin.php
@@ -0,0 +1,95 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2007 - 2008 SemanticScuttle project (fork from Scuttle)
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmark2tagservice = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$bookmarkservice = SemanticScuttle_Service_Factory::get('Bookmark');
+$tag2tagservice = SemanticScuttle_Service_Factory::get('Tag2Tag');
+$tagcacheservice = SemanticScuttle_Service_Factory::get('TagCache');
+$commondescriptionservice = SemanticScuttle_Service_Factory::get('CommonDescription');
+$searchhistoryservice = SemanticScuttle_Service_Factory::get('SearchHistory');
+$tagstatservice = SemanticScuttle_Service_Factory::get('TagStat');
+
+// Header variables
+$tplVars['subtitle'] = T_('Manage users');
+$tplVars['loadjs'] = true;
+$tplVars['sidebar_blocks'] = array('users' );
+$tplVars['error'] = '';
+$tplVars['msg'] = '';
+
+if ( !$userservice->isLoggedOn() ) {
+ header('Location: '. createURL('login', ''));
+ exit();
+}
+
+if ( !$currentUser->isAdmin() ) {
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername()));
+ exit();
+}
+
+@list($url, $action, $user) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+if ($action
+ && (strpos($_SERVER['HTTP_REFERER'], ROOT.'admin') <= 6)
+ // Prevent CSRF attacks. 6 is needed for "//example.org"-root urls
+) {
+ switch ( $action ) {
+ case 'delete':
+ if ( $user && ($userinfo = $userservice->getUserByUsername($user)) ) {
+ $uId = $userinfo['uId'];
+
+ $tagcacheservice->deleteByUser($uId);
+ $tag2tagservice->removeLinkedTagsForUser($uId);
+ $userservice->deleteUser($uId);
+ $bookmark2tagservice->deleteTagsForUser($uId);
+ $commondescriptionservice->deleteDescriptionsForUser($uId);
+ $searchhistoryservice->deleteSearchHistoryForUser($uId);
+ $tagstatservice->deleteTagStatForUser($uId);
+ // XXX: don't delete bookmarks before tags, else tags can't be deleted !!!
+ $bookmarkservice->deleteBookmarksForUser($uId);
+
+ $tplVars['msg'] = sprintf(T_('%s and all his bookmarks and tags were deleted.'), $user);
+ }
+ break;
+ case 'checkUrl' :
+ $bookmarks = $bookmarkservice->getBookmarks(0, NULL, NULL, NULL, NULL, getSortOrder());
+ foreach($bookmarks['bookmarks'] as $bookmark) {
+ if(!checkUrl($bookmark['bAddress'])) {
+ $tplVars['error'].= T_('Problem with ').$bookmark['bAddress'].' ('. $bookmark['username'] .')<br/>';
+ }
+ }
+ break;
+ default:
+ // DO NOTHING
+ }
+}
+
+$templatename = 'admin.tpl';
+$users = $userservice->getObjectUsers();
+
+if ( !is_array($users) ) {
+ $users = array();
+}
+
+$tplVars['users'] = $users;
+
+$templateservice->loadTemplate($templatename, $tplVars);
+?>
diff --git a/www/ajax/getadminlinkedtags.php b/www/ajax/getadminlinkedtags.php
new file mode 100644
index 0000000..5f939a6
--- /dev/null
+++ b/www/ajax/getadminlinkedtags.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * Returns a list of tags managed by the admins, in json format
+ * suitable for jsTree consumption.
+ *
+ * @param string $tag Tag for which the children tags shall be returned
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @subcategory Templates
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+$httpContentType = 'application/json';
+require_once '../www-header.php';
+
+/**
+ * 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'];
+ } else {
+ $linkedTags = $t2t->getAdminLinkedTags($tag, '>');
+ }
+
+ $tagData = array();
+ foreach ($linkedTags as $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;
+}
+
+/**
+ * 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".
+ *
+ * @return array Array to be sent back to the browser as json
+ */
+function createTagArray($tag, $hasChildren = true)
+{
+ $ar = array(
+ 'data' => array(
+ //<a> attributes
+ 'title' => $tag,
+ 'attr' => array(
+ 'href' => createUrl('tags', $tag)
+ )
+ ),
+ //<li> attributes
+ 'attr' => array(
+ 'rel' => $tag,//needed for identifying the tag in html
+ ),
+ );
+ if ($hasChildren) {
+ //jstree needs that to show the arrows
+ $ar['state'] = 'closed';
+ }
+
+ return $ar;
+}
+
+
+$tag = isset($_GET['tag']) ? trim($_GET['tag']) : '';
+$tagData = assembleAdminTagData(
+ $tag,
+ SemanticScuttle_Service_Factory::get('Tag2Tag')
+);
+echo json_encode($tagData);
+?> \ No newline at end of file
diff --git a/www/ajax/getadmintags.php b/www/ajax/getadmintags.php
new file mode 100644
index 0000000..2f13060
--- /dev/null
+++ b/www/ajax/getadmintags.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Return a json file with list of public tags used by admins and sorted
+ * by popularity.
+ *
+ * The following GET parameters are accepted:
+ * @param string $beginsWith The tag name shall start with that string.
+ * No default.
+ * @param integer $limit Number of tags to return. Defaults to 1000
+ *
+ * Part of SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+$httpContentType = 'application/json';
+require_once '../www-header.php';
+
+$limit = 30;
+$beginsWith = null;
+$currentUserId = $userservice->getCurrentUserId();
+
+if (isset($_GET['limit']) && is_numeric($_GET['limit'])) {
+ $limit = (int)$_GET['limit'];
+}
+if (isset($_GET['beginsWith']) && strlen(trim($_GET['beginsWith']))) {
+ $beginsWith = trim($_GET['beginsWith']);
+}
+
+$listTags = SemanticScuttle_Service_Factory::get('Bookmark2Tag')->getAdminTags(
+ $limit, $currentUserId, null, $beginsWith
+);
+$tags = array();
+foreach ($listTags as $t) {
+ $tags[] = $t['tag'];
+}
+
+echo json_encode($tags);
+?> \ No newline at end of file
diff --git a/www/ajax/getcontacttags.php b/www/ajax/getcontacttags.php
new file mode 100644
index 0000000..d353226
--- /dev/null
+++ b/www/ajax/getcontacttags.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Return a json file with list of tags according to current user
+ * and sorted by popularity.
+ *
+ * The following GET parameters are accepted:
+ * @param string $beginsWith The tag name shall start with that string.
+ * No default.
+ * @param integer $limit Number of tags to return. Defaults to 1000
+ *
+ * Part of SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+$httpContentType = 'application/json';
+require_once '../www-header.php';
+
+$limit = 30;
+$beginsWith = null;
+$currentUserId = $userservice->getCurrentUserId();
+
+if (isset($_GET['limit']) && is_numeric($_GET['limit'])) {
+ $limit = (int)$_GET['limit'];
+}
+if (isset($_GET['beginsWith']) && strlen(trim($_GET['beginsWith']))) {
+ $beginsWith = trim($_GET['beginsWith']);
+}
+
+$listTags = SemanticScuttle_Service_Factory::get('Bookmark2Tag')->getContactTags(
+ $currentUserId, $limit, $currentUserId, null, $beginsWith
+);
+$tags = array();
+foreach ($listTags as $t) {
+ $tags[] = $t['tag'];
+}
+
+echo json_encode($tags);
+?>
diff --git a/www/ajax/getlinkedtags.php b/www/ajax/getlinkedtags.php
new file mode 100644
index 0000000..9bb3b1f
--- /dev/null
+++ b/www/ajax/getlinkedtags.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * Returns a list of tags linked to the given one(s),
+ * suitable for jsTree consumption.
+ *
+ * Accepted GET parameters:
+ *
+ * @param string $tag Tag for which the children tags shall be returned
+ * Multiple tags (separated with comma) are supported.
+ * If no tag is given, all top-level tags are loaded.
+ * @param integer $uId User ID to fetch the tags for
+ * @param boolean $parent Load parent tags
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @subpackage Templates
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+$httpContentType = 'application/json';
+require_once '../www-header.php';
+
+$tag = isset($_GET['tag']) ? $_GET['tag'] : null;
+$uId = isset($_GET['uId']) ? (int)$_GET['uId'] : 0;
+$loadParentTags = isset($_GET['parent']) ? (bool)$_GET['parent'] : false;
+
+$tags = explode(',', trim($tag));
+if (count($tags) == 1 && $tags[0] == '') {
+ //no tags
+ $tags = array();
+}
+
+
+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(
+ //<a> attributes
+ 'title' => $title,
+ 'attr' => array(
+ 'href' => createUrl('tags', $tag)
+ )
+ ),
+ //<li> 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
diff --git a/www/ajax/gettags.php b/www/ajax/gettags.php
new file mode 100644
index 0000000..cb73720
--- /dev/null
+++ b/www/ajax/gettags.php
@@ -0,0 +1,44 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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 tags according to current user and sort by popularity*/
+$httpContentType = 'application/json';
+require_once '../www-header.php';
+
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Tag');
+
+?>
+
+{identifier:"tag",
+items: [
+<?php
+ $listTags = $b2tservice->getPopularTags($userservice->getCurrentUserId(), 1000, $userservice->getCurrentUserId());
+ foreach($listTags as $t) {
+ echo "{tag: \"".$t['tag']."\"},";
+ }
+?>
+]}
+
+
+
+
diff --git a/www/ajaxDelete.php b/www/ajaxDelete.php
new file mode 100644
index 0000000..2812612
--- /dev/null
+++ b/www/ajaxDelete.php
@@ -0,0 +1,48 @@
+<?php
+/***************************************************************************
+Copyright (C) 2005 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+header('Last-Modified: '. gmdate("D, d M Y H:i:s") .' GMT');
+header('Cache-Control: no-cache, must-revalidate');
+$httpContentType = 'text/xml';
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice = SemanticScuttle_Service_Factory :: get('Bookmark');
+
+/* Managing all possible inputs */
+isset($_GET['id']) ? define('GET_ID', $_GET['id']): define('GET_ID', '');
+
+
+
+$bookmark = intval(GET_ID);
+if (!$bookmarkservice->editAllowed($bookmark)) {
+ $result = T_('You are not allowed to delete this bookmark');
+} elseif ($bookmarkservice->deleteBookmark($bookmark)) {
+ $result = 'true';
+} else {
+ $result = T_('Failed to delete bookmark');
+}
+
+?>
+<response>
+ <method>deleteConfirmed</method>
+ <result><?php echo $result; ?></result>
+</response> \ No newline at end of file
diff --git a/www/ajaxGetNewPrivateKey.php b/www/ajaxGetNewPrivateKey.php
new file mode 100644
index 0000000..eacebd8
--- /dev/null
+++ b/www/ajaxGetNewPrivateKey.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Ajax script to retrieve new Private Key
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Mark Pemberton <mpemberton5@gmail.com>
+ * @license AGPL http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
+header("Cache-Control: no-cache, must-revalidate");
+
+$httpContentType = 'text/xml';
+require_once 'www-header.php';
+
+$us = SemanticScuttle_Service_Factory::get('User');
+
+/* Managing all possible inputs */
+isset($_GET['url']) ? define('GET_URL', $_GET['url']): define('GET_URL', '');
+
+echo '<?xml version="1.0" encoding="utf-8"?>';
+?>
+<response>
+<method>
+getNewPrivateKey
+</method>
+<result>
+<?php echo $us->getNewPrivateKey(); ?>
+</result>
+</response>
diff --git a/www/ajaxGetTitle.php b/www/ajaxGetTitle.php
new file mode 100644
index 0000000..e1fbe30
--- /dev/null
+++ b/www/ajaxGetTitle.php
@@ -0,0 +1,79 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2005 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
+header("Cache-Control: no-cache, must-revalidate");
+
+$httpContentType = 'text/xml';
+require_once 'www-header.php';
+
+/* Managing all possible inputs */
+isset($_GET['url']) ? define('GET_URL', $_GET['url']): define('GET_URL', '');
+
+function getTitle($url) {
+ $fd = @fopen($url, 'r');
+ if ($fd) {
+ $html = fread($fd, 1750);
+ fclose($fd);
+
+ // Get title from title tag
+ preg_match_all('/<title>(.*)<\/title>/si', $html, $matches);
+ $title = $matches[1][0];
+
+ $encoding = 'utf-8';
+ // Get encoding from charset attribute
+ preg_match_all('/<meta.*charset=([^;"]*)">/i', $html, $matches);
+ if (isset($matches[1][0])) {
+ $encoding = strtoupper($matches[1][0]);
+ }
+
+ // Convert to UTF-8 from the original encoding
+ if (function_exists("mb_convert_encoding")) {
+ $title = @mb_convert_encoding($title, 'UTF-8', $encoding);
+ }
+
+ $title = trim($title);
+
+ if (utf8_strlen($title) > 0) {
+ $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
+ return $title;
+ } else {
+ // No title, so return filename
+ $uriparts = explode('/', $url);
+ $filename = end($uriparts);
+ unset($uriparts);
+
+ return $filename;
+ }
+ } else {
+ return false;
+ }
+}
+echo '<?xml version="1.0" encoding="utf-8"?>';
+?>
+<response>
+<method>
+getTitle
+</method>
+<result>
+<?php echo getTitle(GET_URL); ?>
+</result>
+</response>
diff --git a/www/ajaxIsAvailable.php b/www/ajaxIsAvailable.php
new file mode 100644
index 0000000..41e825e
--- /dev/null
+++ b/www/ajaxIsAvailable.php
@@ -0,0 +1,44 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
+header("Cache-Control: no-cache, must-revalidate");
+
+$httpContentType = 'text/xml';
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+//No specific services
+
+/* Managing all possible inputs */
+isset($_GET['username']) ? define('GET_USERNAME', $_GET['username']): define('GET_USERNAME', '');
+
+
+if ($userservice->isReserved(GET_USERNAME)) {
+ $result = 'false';
+} else {
+ $result = $userservice->getUserByUsername(GET_USERNAME) ? 'false' : 'true';
+}
+?>
+<response>
+ <method>isAvailable</method>
+ <result><?php echo $result; ?></result>
+</response> \ No newline at end of file
diff --git a/www/ajaxVote.php b/www/ajaxVote.php
new file mode 100644
index 0000000..f2572fb
--- /dev/null
+++ b/www/ajaxVote.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * We re-use vote.php but set the ajax flag
+ */
+$httpContentType = 'text/xml';
+$GLOBALS['ajaxRequest'] = true;
+require 'vote.php';
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+$ts = SemanticScuttle_Service_Factory::get('Template');
+$bmrow = $bs->getBookmark($bookmark);
+
+switch ($GLOBALS['votingMode']) {
+case 2:
+ $template = 'bookmarks-vote-horizontal.inc.tpl.php';
+ break;
+default:
+ $template = 'bookmarks-vote.inc.tpl.php';
+}
+
+echo '<voteresult><bookmark>' . $bookmark . '</bookmark>'
+ . '<html xmlns="http://www.w3.org/1999/xhtml">';
+$ts->loadTemplate(
+ $template, array('row' => $bmrow)
+);
+
+echo '</html></voteresult>';
+?> \ No newline at end of file
diff --git a/www/alltags.php b/www/alltags.php
new file mode 100644
index 0000000..16af7fd
--- /dev/null
+++ b/www/alltags.php
@@ -0,0 +1,88 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$b2tservice = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$cacheservice = SemanticScuttle_Service_Factory::get('Cache');
+
+list($url, $user) = explode(
+ '/',
+ isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/'
+);
+if (!$user) {
+ header('Location: '. createURL('populartags'));
+ exit;
+}
+
+if ($usecache) {
+ // Generate hash for caching on
+ $hashtext = $_SERVER['REQUEST_URI'];
+ if ($userservice->isLoggedOn()) {
+ $hashtext .= $userservice->getCurrentUserID();
+ }
+ $hash = md5($hashtext);
+
+ // Cache for an hour
+ $cacheservice->Start($hash, 3600);
+}
+
+// Header variables
+$pagetitle = T_('All Tags');
+
+if (isset($user) && $user != '') {
+
+ $userid = $userservice->getIdFromUser($user);
+ if($userid == NULL) {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+ }
+
+ $pagetitle .= ': '. ucfirst($user);
+} else {
+ $userid = NULL;
+}
+
+$tags = $b2tservice->getTags($userid);
+$tplVars['tags'] = $b2tservice->tagCloud($tags, 5, 90, 225, getSortOrder());
+$tplVars['user'] = $user;
+
+if (isset($userid)) {
+ $tplVars['cat_url'] = createURL('bookmarks', '%s/%s');
+} else {
+ $tplVars['cat_url'] = createURL('tags', '%2$s');
+}
+
+$tplVars['sidebar_blocks'] = array('linked');
+$tplVars['userid'] = $userid;
+$tplVars['loadjs'] = true;
+
+$tplVars['pagetitle'] = $pagetitle;
+$tplVars['subtitle'] = $pagetitle;
+$templateservice->loadTemplate('tags.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/api/export_csv.php b/www/api/export_csv.php
new file mode 100644
index 0000000..bb469b1
--- /dev/null
+++ b/www/api/export_csv.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Export own bookmarks in CSV format in order to allow the import
+ * into a spreadsheet tool like Excel
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// Force HTTP authentication first!
+$httpContentType = 'application/csv-tab-delimited-table';
+require_once 'httpauth.inc.php';
+header("Content-disposition: filename=exportBookmarks.csv");
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != '')) {
+ //$_GET vars have + replaced to " " automatically
+ $tag = str_replace(' ', '+', trim($_REQUEST['tag']));
+} else {
+ $tag = null;
+}
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(
+ 0, null, $userservice->getCurrentUserId(),
+ $tag, null, getSortOrder()
+);
+
+//columns titles
+echo 'url;title;tags;description';
+echo "\n";
+
+foreach($bookmarks['bookmarks'] as $row) {
+ if (is_null($row['bDescription']) || (trim($row['bDescription']) == ''))
+ $description = '';
+ else
+ $description = filter(str_replace(array("\r\n", "\n", "\r"),"", $row['bDescription']), 'xml');
+
+ $taglist = '';
+ if (count($row['tags']) > 0) {
+ foreach($row['tags'] as $tag)
+ $taglist .= convertTag($tag) .',';
+ $taglist = substr($taglist, 0, -1);
+ } else {
+ $taglist = 'system:unfiled';
+ }
+
+ echo '"'.filter($row['bAddress'], 'xml') .'";"'. filter($row['bTitle'], 'xml') .'";"'. filter($taglist, 'xml') .'";"'. $description .'"';
+ echo "\n";
+}
+
+
+?>
diff --git a/www/api/export_gcs.php b/www/api/export_gcs.php
new file mode 100644
index 0000000..06ab217
--- /dev/null
+++ b/www/api/export_gcs.php
@@ -0,0 +1,67 @@
+<?php
+/*
+ Export for Google Custom Search
+ */
+
+// Force HTTP authentication first!
+//require_once('httpauth.inc.php');
+$httpContentType = false;
+require_once '../www-header.php';
+
+if($GLOBALS['enableGoogleCustomSearch'] == false) {
+ echo "Google Custom Search disabled. You can enable it into the config.php file.";
+ die;
+}
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+
+
+/*
+ // Restrict to admins?
+ if(!$userservice->isAdmin($userservice->getCurrentUserId())) {
+ die(T_('You are not allowed to do this action (admin access)'));
+ }*/
+
+// Check if queried format is xml
+if (isset($_REQUEST['xml']) && (trim($_REQUEST['xml']) == 1))
+$xml = true;
+else
+$xml = false;
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+$tag = trim($_REQUEST['tag']);
+else
+$tag = NULL;
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(0, NULL, NULL, $tag, NULL, getSortOrder());
+
+
+// Set up the plain file and output all the posts.
+header('Content-Type: text/plain; charset=utf-8');
+if(!$xml) {
+ header('Content-Type: text/plain');
+ foreach($bookmarks['bookmarks'] as $row) {
+ if(checkUrl($row['bAddress'], false)) {
+ echo $row['bAddress']."\n";
+ }
+ }
+} else {
+ header('Content-Type: text/xml');
+ echo '<GoogleCustomizations>'."\n";
+ echo ' <Annotations>'."\n";
+ foreach($bookmarks['bookmarks'] as $row) {
+ //if(substr($row['bAddress'], 0, 7) == "http://") {
+ if(checkUrl($row['bAddress'], false)) {
+ echo ' <Annotation about="'.filter($row['bAddress']).'">'."\n";
+ echo ' <Label name="include"/>'."\n";
+ echo ' </Annotation>'."\n";
+ }
+ }
+ echo ' </Annotations>'."\n";
+ echo '</GoogleCustomizations>'."\n";
+}
+
+?>
diff --git a/www/api/export_html.php b/www/api/export_html.php
new file mode 100644
index 0000000..33401e7
--- /dev/null
+++ b/www/api/export_html.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Implements the del.icio.us API request for all a user's posts,
+ * optionally filtered by tag.
+ *
+ * Netscape bookmark file format is documented at
+ * http://msdn.microsoft.com/en-us/library/aa753582(VS.85).aspx
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// del.icio.us behavior:
+// - doesn't include the filtered tag as an attribute on the root element (we do)
+
+//this page here is really not valid in any way
+$httpContentType = 'text/html';
+// Force HTTP authentication first!
+require_once 'httpauth.inc.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice = SemanticScuttle_Service_Factory::get('Bookmark');
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != '')) {
+ //$_GET vars have + replaced to " " automatically
+ $tag = str_replace(' ', '+', trim($_REQUEST['tag']));
+} else {
+ $tag = null;
+}
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(
+ 0, null, $userservice->getCurrentUserId(),
+ $tag, null, getSortOrder()
+);
+
+
+// Set up the XML file and output all the posts.
+echo '<!DOCTYPE NETSCAPE-Bookmark-file-1>'."\r\n";
+echo '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8" />';
+echo '<!-- This is an automatically generated file. -->'."\r\n";
+echo '<TITLE>Bookmarks</TITLE>'."\r\n";
+echo '<H1 LAST_MODIFIED="'. date('U') .'">Bookmarks for '. htmlspecialchars($currentUser->getUsername()) .''. (is_null($tag) ? '' : ' tag="'. htmlspecialchars($tag) .'"') ." from " . $sitename ."</H1>\r\n";
+echo '<DL>'."\r\n";
+
+
+
+foreach ($bookmarks['bookmarks'] as $row) {
+ if (is_null($row['bDescription']) || (trim($row['bDescription']) == '')) {
+ $description = '';
+ } else {
+ $description = 'description="'. filter($row['bDescription'], 'xml') .'" ';
+ }
+
+ $taglist = '';
+ if (count($row['tags']) > 0) {
+ foreach ($row['tags'] as $tag) {
+ $taglist .= convertTag($tag) .',';
+ }
+
+ $taglist = substr($taglist, 0, -1);
+ } else {
+ $taglist = 'system:unfiled';
+ }
+
+ echo "\t<DT><A HREF=\"". filter($row['bAddress'], 'xml') .'" '. $description .' hash="'. md5($row['bAddress']) .'" tags="'. filter($taglist, 'xml') .'" ADD_DATE="'. date('U', strtotime($row['bDatetime'])) ."\" >" . filter($row['bTitle'], 'xml') ."</a>\r\n";
+}
+
+
+echo '</DL>';
+?>
diff --git a/www/api/export_sioc.php b/www/api/export_sioc.php
new file mode 100644
index 0000000..45cbf14
--- /dev/null
+++ b/www/api/export_sioc.php
@@ -0,0 +1,90 @@
+<?php
+/* Export data with semantic format (SIOC: http://sioc-project.org/, FOAF, SKOS, Annotea Ontology) */
+
+$httpContentType = 'text/xml';
+require_once '../www-header.php';
+
+/* Service creation: only useful services are created */
+$userservice =SemanticScuttle_Service_Factory::get('User');
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+
+?>
+<?php echo "<?xml version=\"1.0\" encoding=\"utf-8\"\n?>"; ?>
+<rdf:RDF
+ xmlns="http://xmlns.com/foaf/0.1/"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:rss="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:dcterms="http://purl.org/dc/terms/"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:sioc="http://rdfs.org/sioc/ns#"
+ xmlns:sioc_t="http://rdfs.org/sioc/types#"
+ xmlns:bm="http://www.w3.org/2002/01/bookmark#"
+ xmlns:skos="http://www.w3.org/2004/02/skos/core#">
+
+<?php
+//site and community are described using FOAF and SIOC ontology
+?>
+<sioc:Site rdf:about="<?php echo ROOT?>" >
+ <rdf:label><?php echo $GLOBALS['sitename']?></rdf:label>
+</sioc:Site>
+
+<?php //<sioc_t:BookmarkFolder />?>
+
+<?php
+//users are described using FOAF and SIOC ontology
+$users = $userservice->getObjectUsers();
+
+$usersArray = array(); // useful for bookmarks display
+foreach($users as $user) {
+ $usersArray[$user->getId()] = $user->getUserName();
+}
+?>
+
+<?php foreach($users as $user) :?>
+<sioc:User rdf:about="<?php echo createUrl('profile', $user->getUserName())?>">
+ <sioc:name><?php echo $user->getUserName() ?></sioc:name>
+ <sioc:member_of rdf:resource="<?php echo ROOT?>" />
+</sioc:User>
+<?php endforeach; ?>
+
+<?php
+/*
+No page for usergroup (users/admin) for the moment
+ <sioc:Usergroup rdf:ID="authors">
+ <sioc:name>Authors at PlanetRDF.com</sioc:name>
+ <sioc:has_member rdf:nodeID="sioc-id2245901" />
+ </sioc:Usergroup>
+*/
+?>
+
+<?php
+//bookmarks are described using Annotea ontology
+$bookmarks = $bookmarkservice->getBookmarks(0, NULL, NULL, NULL);
+?>
+
+<?php foreach($bookmarks['bookmarks'] as $bookmark): ?>
+<bm:Bookmark rdf:about="<?php echo createUrl('history', $bookmark['bHash']) ?>">
+ <dc:title><?php echo filter($bookmark['bTitle']) ?></dc:title>
+ <dc:created><?php echo filter($bookmark['bDatetime']) ?></dc:created>
+ <dc:description><?php echo filter(strip_tags($bookmark['bDescription'])) ?></dc:description>
+ <dc:date><?php echo $bookmark['bModified'] ?></dc:date>
+ <bm:recalls rdf:resource="<?php echo filter($bookmark['bAddress']) ?>"/>
+ <sioc:owner_of rdf:resource="<?php echo createUrl('profile', $usersArray[$bookmark['uId']]) ?>"/>
+ <?php foreach($bookmark['tags'] as $tag): ?>
+ <sioc:topic>
+ <skos:concept rdf:about="<?php echo createUrl('bookmarks', $usersArray[$bookmark['uId']].'/'.$tag) ?>" />
+ </sioc:topic>
+ <?php endforeach; ?>
+</bm:Bookmark>
+
+<?php endforeach; ?>
+
+<?php
+// tags and concepts are described using SKOS ontology
+//concept for user/admins, preflabel, definition, top concept
+?>
+
+</rdf:RDF>
+
diff --git a/www/api/httpauth.inc.php b/www/api/httpauth.inc.php
new file mode 100644
index 0000000..ee5c7f2
--- /dev/null
+++ b/www/api/httpauth.inc.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Checks if the user is logged on and sends a HTTP basic auth
+ * request to the browser if not. In that case the script ends.
+ * If username and password are available, the user service's
+ * login method is used to log the user in.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+require_once '../www-header.php';
+
+/**
+ * Sends HTTP auth headers to the browser
+ */
+function authenticate()
+{
+ header('WWW-Authenticate: Basic realm="SemanticScuttle API"');
+ header('HTTP/1.0 401 Unauthorized');
+
+ die(T_("Use of the API calls requires authentication."));
+}
+
+if (!$userservice->isLoggedOn()) {
+ /* Maybe we have caught authentication data in $_SERVER['REMOTE_USER']
+ ( Inspired by http://www.yetanothercommunitysystem.com/article-321-regle-comment-utiliser-l-authentification-http-en-php-chez-ovh ) */
+ if ((!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']))
+ && isset($_SERVER['REMOTE_USER'])
+ && preg_match('/Basic\s+(.*)$/i', $_SERVER['REMOTE_USER'], $matches)
+ ) {
+ list($name, $password) = explode(':', base64_decode($matches[1]));
+ $_SERVER['PHP_AUTH_USER'] = strip_tags($name);
+ $_SERVER['PHP_AUTH_PW'] = strip_tags($password);
+ }
+
+ if (!isset($_SERVER['PHP_AUTH_USER'])) {
+ authenticate();
+ } else {
+ $login = $userservice->login(
+ $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']
+ );
+ if ($login) {
+ $currentUser = $userservice->getCurrentObjectUser();
+ } else {
+ authenticate();
+ }
+ }
+}
+?>
diff --git a/www/api/opensearch.php b/www/api/opensearch.php
new file mode 100644
index 0000000..9d3eae7
--- /dev/null
+++ b/www/api/opensearch.php
@@ -0,0 +1,16 @@
+<?php
+$httpContentType = 'text/xml';
+require_once '../www-header.php';
+echo '<' . '?xml version="1.0" encoding="utf-8" ?' . ">\n";
+?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+ <ShortName><?php echo $GLOBALS['sitename']?></ShortName>
+ <LongName></LongName>
+ <Description><?php echo $GLOBALS['welcomeMessage']?></Description>
+ <InputEncoding>UTF-8</InputEncoding>
+ <Contact><?php echo $GLOBALS['adminemail']?></Contact>
+ <Developer>Jan Seifert "jan.seifert@uid.com"</Developer>
+ <Tags>semanticscuttle bookmark web</Tags>
+ <Image width="16" height="16">data:image/gif;base64,R0lGODlhEAAQAMZ9ANaPE9mREteTHtSXLdmXIdiXJtaYKdiYJ9iYKNeaLtKdP9CdRd2dLNWfQuWiFMqjX9+hNMykXt6jPOCkPM2oaM+paeCpQuGoR+OqOeKpR+GqS9+uWeSwU+ayS+uzOeWxVeWxWOSyWOu0POazXOS0YOm8Zee8cOy+WOm9a/jBLum+bPbCNurAbe7BYuvBc/LDV/LEV+zEdv/KKf/KLP/KLf/LLuzHe//MNP/NN/nMTv/OOf/OPP/OP/jNW//PPvnOVv/QQv7QRv/QRP/RR/HOhP/SSf/STPnRav/TT//TUPfScf/UUvzUXP/UVP/VV/bTfv/WWf/WW//WXP7XYf/XX//XYP/YZPPVmf/ZZv/ZZ/vYeP/aaf/aa//abP/abf/abvvaef/bb//bcfzbfP/ccv/cc//cdP/cdf/dd//deP/def3egP/ee//efP/efv/ffv/ggf/gg//ghP/ghfrfmv/hif/ii//ijP/jkfzjm//kkv7klv/lmP///////////yH5BAEKAH8ALAAAAAAQABAAAAergH+Cg38hhIeHIFcmiIgDLnt0Go2EE1pyd0QbjQMQDC9bZGprKBcjA4IfAQMrQUhOVFlhaGNPMRl/Aw4yMzc7QkZNUlZdZmASqAJKcW1WPDpARUtQUx0RgyR5fHpzUTU4PkMiqIMVNnh2c25lSTQpAIgqdXJvaWReXz0JiCxwbWhiuGCp8uMAohJszhw5AYMJlBwFEHFoYQHBAwUEMHgwgKjBA0QLKFAaKSgQADs=</Image>
+ <Url type="text/html" template="<?php if ($GLOBALS['root'] != null) { echo $GLOBALS['root']; } else { echo $_SERVER['SERVER_PROTOCOL'] .'://'. $_SERVER['HTTP_HOST'] . '/'; } ?>search.php/all/{searchTerms}"/>
+</OpenSearchDescription>
diff --git a/www/api/posts_add.php b/www/api/posts_add.php
new file mode 100644
index 0000000..80d6515
--- /dev/null
+++ b/www/api/posts_add.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * API for adding a new bookmark.
+ *
+ * The following POST and GET parameters are accepted:
+ * @param string $url URL of the bookmark (required)
+ * @param string $description Bookmark title (required)
+ * @param string $extended Extended bookmark description (optional)
+ * @param string $tags Space-separated list of tags (optional)
+ * @param string $dt Date and time of bookmark creation (optional)
+ * Must be of format YYYY-MM-DDTHH:II:SSZ
+ * @param integer $status Visibility status (optional):
+ * - 2 or 'private': Bookmark is totally private
+ * - 1 or 'shared': People on the user's watchlist
+ * can see it
+ * - 0 or 'public': Everyone can see the bookmark
+ * @param string $shared "no" or "yes": Switches between private and
+ * public (optional)
+ * @param string $replace "yes" or "no" - replaces a bookmark with the
+ * same URL (optional)
+ *
+ * Notes:
+ * - tags cannot have spaces
+ * - URL and description (title) are mandatory
+ * - delicious "description" is the "title" in SemanticScuttle
+ * - delicious "extended" is the "description" in SemanticScuttle
+ * - "status" is a SemanticScuttle addition to this API method
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ * @link http://www.delicious.com/help/api
+ */
+
+// Force HTTP authentication
+$httpContentType = 'text/xml';
+require_once 'httpauth.inc.php';
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+
+// Get all the bookmark's passed-in information
+if (isset($_REQUEST['url']) && (trim($_REQUEST['url']) != '')) {
+ $url = trim(urldecode($_REQUEST['url']));
+} else {
+ $url = null;
+}
+
+if (isset($_REQUEST['description']) && (trim($_REQUEST['description']) != '')) {
+ $description = trim($_REQUEST['description']);
+} else {
+ $description = null;
+}
+
+if (isset($_REQUEST['extended']) && (trim($_REQUEST['extended']) != '')) {
+ $extended = trim($_REQUEST['extended']);
+} else {
+ $extended = null;
+}
+
+if (isset($_REQUEST['tags']) && (trim($_REQUEST['tags']) != '')
+ && (trim($_REQUEST['tags']) != ',')
+) {
+ $tags = trim($_REQUEST['tags']);
+} else {
+ $tags = null;
+}
+
+if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != '')) {
+ $dt = trim($_REQUEST['dt']);
+} else {
+ $dt = null;
+}
+
+$replace = isset($_REQUEST['replace']) && ($_REQUEST['replace'] == 'yes');
+
+$status = $GLOBALS['defaults']['privacy'];
+if (isset($_REQUEST['status'])) {
+ $status_str = trim($_REQUEST['status']);
+ if (is_numeric($status_str)) {
+ $status = intval($status_str);
+ 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;
+ }
+ }
+}
+
+if (isset($_REQUEST['shared']) && (trim($_REQUEST['shared']) == 'no')) {
+ $status = 2;
+}
+
+// Error out if there's no address or description
+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!
+ $exists = $bs->bookmarkExists($url, $userservice->getCurrentUserId());
+ if ($exists) {
+ if (!$replace) {
+ header('HTTP/1.0 409 Conflict');
+ $msg = 'bookmark does already exist';
+ } else {
+ //delete it before we re-add it
+ $bookmark = $bs->getBookmarkByAddress($url, false);
+ $bId = $bookmark['bId'];
+ $bs->deleteBookmark($bId);
+
+ $exists = false;
+ }
+ }
+
+ if (!$exists) {
+ $added = $bs->addBookmark(
+ $url, $description, $extended, '', $status, $tags, null, $dt, true
+ );
+ $msg = 'done';
+ }
+}
+
+// Set up the XML file and output the result.
+echo '<?xml version="1.0" standalone="yes" ?' . ">\r\n";
+echo '<result code="' . $msg .'" />';
+?> \ No newline at end of file
diff --git a/www/api/posts_all.php b/www/api/posts_all.php
new file mode 100644
index 0000000..c70d14c
--- /dev/null
+++ b/www/api/posts_all.php
@@ -0,0 +1,50 @@
+<?php
+// Implements the del.icio.us API request for all a user's posts, optionally filtered by tag.
+
+// del.icio.us behavior:
+// - doesn't include the filtered tag as an attribute on the root element (we do)
+
+// Scuttle behavior:
+// - returns privacy status of each bookmark.
+
+// 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');
+
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+ $tag = trim($_REQUEST['tag']);
+else
+ $tag = NULL;
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(0, NULL, $userservice->getCurrentUserId(), $tag);
+
+// Set up the XML file and output all the posts.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts update="'. gmdate('Y-m-d\TH:i:s\Z') .'" user="'. htmlspecialchars($currentUser->getUsername()) .'"'. (is_null($tag) ? '' : ' tag="'. htmlspecialchars($tag) .'"') .">\r\n";
+
+foreach($bookmarks['bookmarks'] as $row) {
+ if (is_null($row['bDescription']) || (trim($row['bDescription']) == ''))
+ $description = '';
+ else
+ $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+
+ $taglist = '';
+ if (count($row['tags']) > 0) {
+ foreach($row['tags'] as $tag)
+ $taglist .= convertTag($tag) .' ';
+ $taglist = substr($taglist, 0, -1);
+ } else {
+ $taglist = 'system:unfiled';
+ }
+
+ echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. md5($row['bAddress']) .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) . '" status="'. filter($row['bStatus'], 'xml') ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
diff --git a/www/api/posts_dates.php b/www/api/posts_dates.php
new file mode 100644
index 0000000..508c46a
--- /dev/null
+++ b/www/api/posts_dates.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Implements the del.icio.us API request for a user's post counts by date
+ * (and optionally by tag).
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// 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');
+
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != '')) {
+ $tag = trim($_REQUEST['tag']);
+} else {
+ $tag = null;
+}
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(
+ 0, null, $userservice->getCurrentUserId(), $tag
+);
+
+// Set up the XML file and output all the tags.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<dates tag="'. (is_null($tag) ? '' : filter($tag, 'xml')) .'" user="'. filter($currentUser->getUsername(), 'xml') ."\">\r\n";
+
+$lastdate = null;
+$count = 0;
+foreach ($bookmarks['bookmarks'] as $row) {
+ $thisdate = gmdate('Y-m-d', strtotime($row['bDatetime']));
+ if ($thisdate != $lastdate && $lastdate != null) {
+ echo "\t<date count=\"". $count .'" date="'. $lastdate ."\" />\r\n";
+ $count = 1;
+ } else {
+ ++$count;
+ }
+ $lastdate = $thisdate;
+}
+if ($lastdate !== null) {
+ echo "\t<date count=\"". $count .'" date="'. $lastdate ."\" />\r\n";
+}
+
+echo "</dates>";
+?> \ No newline at end of file
diff --git a/www/api/posts_delete.php b/www/api/posts_delete.php
new file mode 100644
index 0000000..69b2429
--- /dev/null
+++ b/www/api/posts_delete.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * API for deleting a bookmark.
+ * The delicious API is implemented here.
+ *
+ * The delicious API behaves like that:
+ * - does NOT allow the hash for the url parameter
+ * - doesn't set the Content-Type to text/xml
+ * - we do it correctly, too
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ * @link http://www.delicious.com/help/api
+ */
+
+// Force HTTP authentication first!
+$httpContentType = 'text/xml';
+require_once 'httpauth.inc.php';
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+$uId = $userservice->getCurrentUserId();
+
+
+// Error out if there's no address
+if (!isset($_REQUEST['url'])
+ || $_REQUEST['url'] == ''
+) {
+ $msg = 'something went wrong';
+} else if (!$bs->bookmarkExists($_REQUEST['url'], $uId)) {
+ //the user does not have such a bookmark
+ header('HTTP/1.0 404 Not Found');
+ $msg = 'item not found';
+} else {
+ $bookmark = $bs->getBookmarkByAddress($_REQUEST['url'], false);
+ $bId = $bookmark['bId'];
+ $deleted = $bs->deleteBookmark($bId);
+ $msg = 'done';
+ if (!$deleted) {
+ //something really went wrong
+ header('HTTP/1.0 500 Internal Server Error');
+ $msg = 'something really went wrong';
+ }
+}
+
+// Set up the XML file and output the result.
+echo '<?xml version="1.0" standalone="yes" ?' . ">\r\n";
+echo '<result code="' . $msg . '" />';
+?> \ No newline at end of file
diff --git a/www/api/posts_get.php b/www/api/posts_get.php
new file mode 100644
index 0000000..e39058b
--- /dev/null
+++ b/www/api/posts_get.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Implements the del.icio.us API request for a user's posts,
+ * optionally filtered by tag and/or date.
+ * Note that when using a date to select the posts returned, del.icio.us
+ * uses GMT dates -- so we do too.
+ *
+ * del.icio.us behavior:
+ * - includes an empty tag attribute on the root element when it hasn't been specified
+ *
+ * Scuttle behavior:
+ * - Uses today, instead of the last bookmarked date, if no date is specified
+ * - returns privacy status of each bookmark.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// 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');
+
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != '')) {
+ $tag = trim($_REQUEST['tag']);
+} else {
+ $tag = null;
+}
+
+// Check to see if a date was specified; the format should be YYYY-MM-DD
+if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != '')) {
+ $dtstart = trim($_REQUEST['dt']);
+} else {
+ $dtstart = date('Y-m-d H:i:s');
+}
+$dtend = date('Y-m-d H:i:s', strtotime($dtstart .'+1 day'));
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(
+ 0, null, $userservice->getCurrentUserId(), $tag,
+ null, null, null, $dtstart, $dtend
+);
+
+
+// Set up the XML file and output all the tags.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts'. (is_null($dtstart) ? '' : ' dt="'. $dtstart .'"') .' tag="'. (is_null($tag) ? '' : filter($tag, 'xml')) .'" user="'. filter($currentUser->getUsername(), 'xml') ."\">\r\n";
+
+foreach ($bookmarks['bookmarks'] as $row) {
+ if (is_null($row['bDescription']) || (trim($row['bDescription']) == '')) {
+ $description = '';
+ } else {
+ $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+ }
+
+ $taglist = '';
+ if (count($row['tags']) > 0) {
+ foreach ($row['tags'] as $tag) {
+ $taglist .= convertTag($tag) . ' ';
+ }
+ $taglist = substr($taglist, 0, -1);
+ } else {
+ $taglist = 'system:unfiled';
+ }
+
+ echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. $row['bHash'] .'" others="'. $bookmarkservice->countOthers($row['bAddress']) .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) . '" status="'. filter($row['bStatus'], 'xml') ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
diff --git a/www/api/posts_public.php b/www/api/posts_public.php
new file mode 100644
index 0000000..e4086b2
--- /dev/null
+++ b/www/api/posts_public.php
@@ -0,0 +1,51 @@
+<?php
+// Implements the del.icio.us API request for all a user's posts, optionally filtered by tag.
+
+// del.icio.us behavior:
+// - doesn't include the filtered tag as an attribute on the root element (we do)
+
+// Scuttle behavior:
+// - returns privacy status of each bookmark.
+
+// Force HTTP authentication first!
+//require_once('httpauth.inc.php');
+$httpContentType = 'text/xml';
+require_once '../www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+ $tag = trim($_REQUEST['tag']);
+else
+ $tag = NULL;
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(0, NULL, NULL, $tag);
+
+// Set up the XML file and output all the posts.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts update="'. gmdate('Y-m-d\TH:i:s\Z') .'" '. (is_null($tag) ? '' : ' tag="'. htmlspecialchars($tag) .'"') .">\r\n";
+
+foreach($bookmarks['bookmarks'] as $row) {
+ if (is_null($row['bDescription']) || (trim($row['bDescription']) == ''))
+ $description = '';
+ else
+ $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+
+ $taglist = '';
+ if (count($row['tags']) > 0) {
+ foreach($row['tags'] as $tag)
+ $taglist .= convertTag($tag) .' ';
+ $taglist = substr($taglist, 0, -1);
+ } else {
+ $taglist = 'system:unfiled';
+ }
+
+ echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. md5($row['bAddress']) .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) . '" status="'. filter($row['bStatus'], 'xml') ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
diff --git a/www/api/posts_recent.php b/www/api/posts_recent.php
new file mode 100644
index 0000000..5fca30d
--- /dev/null
+++ b/www/api/posts_recent.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Implements the del.icio.us API request for a user's recent posts,
+ * optionally filtered by tag and/or number of posts
+ * (default 15, max 100, just like del.icio.us).
+ *
+ * Scuttle behavior:
+ * - returns privacy status of each bookmark.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+// Set default and max number of posts
+$countDefault = 15;
+$countMax = 100;
+
+// 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');
+
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != '')) {
+ $tag = trim($_REQUEST['tag']);
+} else {
+ $tag = null;
+}
+
+// Check to see if the number of items was specified.
+if (isset($_REQUEST['count']) && (intval($_REQUEST['count']) != 0)) {
+ $count = intval($_REQUEST['count']);
+ if ($count > $countMax) {
+ $count = $countMax;
+ } else if ($count < 0) {
+ $count = 0;
+ }
+} else {
+ $count = $countDefault;
+}
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks = $bookmarkservice->getBookmarks(
+ 0, $count, $userservice->getCurrentUserId(), $tag
+);
+
+
+// Set up the XML file and output all the tags.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts tag="'. (is_null($tag) ? '' : filter($tag, 'xml')) .'" user="'. filter($currentUser->getUsername(), 'xml') ."\">\r\n";
+
+foreach ($bookmarks['bookmarks'] as $row) {
+ if (is_null($row['bDescription']) || (trim($row['bDescription']) == '')) {
+ $description = '';
+ } else {
+ $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+ }
+
+ $taglist = '';
+ if (count($row['tags']) > 0) {
+ foreach ($row['tags'] as $tag) {
+ $taglist .= convertTag($tag) .' ';
+ }
+ $taglist = substr($taglist, 0, -1);
+ } else {
+ $taglist = 'system:unfiled';
+ }
+
+ echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. $row['bHash'] .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) . '" status="'. filter($row['bStatus'], 'xml') ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
diff --git a/www/api/posts_update.php b/www/api/posts_update.php
new file mode 100644
index 0000000..0e08ee7
--- /dev/null
+++ b/www/api/posts_update.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * API for retrieving a user's last update time.
+ * That is the time the user changed a bookmark lastly.
+ * The delicious API is implemented here.
+ *
+ * Delicious also returns "the number of new items in
+ * the user's inbox since it was last visited." - we do
+ * that too, so we are as close at the API as possible,
+ * not breaking delicious clients.
+ *
+ * SemanticScuttle supports an extra parameter:
+ * - ?datemode=modified
+ * - sorts by modified date and returns modification time
+ * instead of creation time
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ * @link http://www.delicious.com/help/api
+ */
+
+// Force HTTP authentication first!
+$httpContentType = 'text/xml';
+require_once 'httpauth.inc.php';
+
+// parameter "datemode=modified" will get last modified date
+// instead of last created date
+$orderby = null;
+$timeField = 'bDatetime';
+if (isset($_GET['datemode']) && $_GET['datemode'] == 'modified') {
+ $orderby = 'modified_desc';
+ $timeField = 'bModified';
+}
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+
+$bookmarks = $bs->getBookmarks(0, 1, $userservice->getCurrentUserId(), null, null, $orderby);
+
+// Set up the XML file and output all the tags.
+echo '<?xml version="1.0" standalone="yes" ?' . ">\r\n";
+//foreach is used in case there are no bookmarks
+foreach ($bookmarks['bookmarks'] as $row) {
+ echo '<update time="'
+ . gmdate('Y-m-d\TH:i:s\Z', strtotime($row[$timeField]))
+ . '"'
+ . ' inboxnew="0"'
+ . ' />';
+}
+?>
diff --git a/www/api/tags_get.php b/www/api/tags_get.php
new file mode 100644
index 0000000..c0fb83d
--- /dev/null
+++ b/www/api/tags_get.php
@@ -0,0 +1,25 @@
+<?php
+// Implements the del.icio.us API request for all a user's tags.
+
+// del.icio.us behavior:
+// - tags can't have spaces
+
+// Force HTTP authentication first!
+$httpContentType = 'text/xml';
+require_once 'httpauth.inc.php';
+
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+
+
+// Get the tags relevant to the passed-in variables.
+$tags = $b2tservice->getTags($userservice->getCurrentUserId());
+
+// Set up the XML file and output all the tags.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo "<tags>\r\n";
+foreach($tags as $row) {
+ echo "\t<tag count=\"". $row['bCount'] .'" tag="'. filter(convertTag($row['tag'], 'out'), 'xml') ."\" />\r\n";
+}
+echo "</tags>";
+?>
diff --git a/www/api/tags_rename.php b/www/api/tags_rename.php
new file mode 100644
index 0000000..cf56c83
--- /dev/null
+++ b/www/api/tags_rename.php
@@ -0,0 +1,36 @@
+<?php
+// Implements the del.icio.us API request to rename a user's tag.
+
+// del.icio.us behavior:
+// - oddly, returns an entirely different result (<result></result>) than the other API calls.
+
+// Force HTTP authentication first!
+$httpContentType = 'text/xml';
+require_once 'httpauth.inc.php';
+
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+
+// Get the tag info.
+if (isset($_REQUEST['old']) && (trim($_REQUEST['old']) != ''))
+ $old = trim($_REQUEST['old']);
+else
+ $old = NULL;
+
+if (isset($_REQUEST['new']) && (trim($_REQUEST['new']) != ''))
+ $new = trim($_REQUEST['new']);
+else
+ $new = NULL;
+
+if (is_null($old) || is_null($new)) {
+ $renamed = false;
+} else {
+ // Rename the tag.
+ $result = $b2tservice->renameTag($userservice->getCurrentUserId(), $old, $new, true);
+ $renamed = $result;
+}
+
+// Set up the XML file and output the result.
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<result>'. ($renamed ? 'done' : 'something went wrong') .'</result>';
+?>
diff --git a/www/bookmarkcommondescriptionedit.php b/www/bookmarkcommondescriptionedit.php
new file mode 100644
index 0000000..d182dd0
--- /dev/null
+++ b/www/bookmarkcommondescriptionedit.php
@@ -0,0 +1,78 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2006 - 2007 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice = SemanticScuttle_Service_Factory :: get('Bookmark');
+$cdservice = SemanticScuttle_Service_Factory :: get('CommonDescription');
+
+/* Managing all possible inputs */
+isset($_POST['confirm']) ? define('POST_CONFIRM', $_POST['confirm']): define('POST_CONFIRM', '');
+isset($_POST['cancel']) ? define('POST_CANCEL', $_POST['cancel']): define('POST_CANCEL', '');
+isset($_POST['hash']) ? define('POST_HASH', $_POST['hash']): define('POST_HASH', '');
+isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', '');
+isset($_POST['description']) ? define('POST_DESCRIPTION', $_POST['description']): define('POST_DESCRIPTION', '');
+
+// prevent cycle between personal and common edit page
+if(!isset($_POST['referrer'])) {
+ define('POST_REFERRER', '');
+} elseif(strpos($_POST['referrer'], ROOT.'edit.php') == 0) {
+ define('POST_REFERRER', createUrl('history', POST_HASH));
+} else {
+ define('POST_REFERRER', $_POST['referrer']);
+}
+
+
+list ($url, $hash) = explode('/', $_SERVER['PATH_INFO']);
+$template = 'bookmarkcommondescriptionedit.tpl';
+
+
+//permissions
+if(is_null($currentUser)) {
+ $tplVars['error'] = T_('Permission denied.');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+}
+
+if (POST_CONFIRM) {
+ if (strlen($hash)>0 &&
+ $cdservice->addBookmarkDescription(POST_HASH, stripslashes(POST_TITLE), stripslashes(POST_DESCRIPTION), $currentUser->getId(), time())
+ ) {
+ $tplVars['msg'] = T_('Bookmark common description updated');
+ header('Location: '. POST_REFERRER);
+ } else {
+ $tplVars['error'] = T_('Failed to update the bookmark common description');
+ $template = 'error.500.tpl';
+ }
+} elseif (POST_CANCEL) {
+ header('Location: '. POST_REFERRER);
+} else {
+ $bkm = $bookmarkservice->getBookmarkByHash($hash);
+
+ $tplVars['subtitle'] = T_('Edit Bookmark Common Description') .': '. $bkm['bAddress'];
+ $tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $hash;
+ $tplVars['referrer'] = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
+ $tplVars['hash'] = $hash;
+ $tplVars['description'] = $cdservice->getLastBookmarkDescription($hash);
+}
+$templateservice->loadTemplate($template, $tplVars);
+?>
diff --git a/www/bookmarks.php b/www/bookmarks.php
new file mode 100644
index 0000000..bf06fb6
--- /dev/null
+++ b/www/bookmarks.php
@@ -0,0 +1,323 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing all possible inputs */
+isset($_GET['action']) ? define('GET_ACTION', $_GET['action']): define('GET_ACTION', '');
+isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', '');
+
+isset($_GET['title']) ? define('GET_TITLE', $_GET['title']): define('GET_TITLE', '');
+isset($_GET['address']) ? define('GET_ADDRESS', $_GET['address']): define('GET_ADDRESS', '');
+isset($_GET['description']) ? define('GET_DESCRIPTION', $_GET['description']): define('GET_DESCRIPTION', '');
+isset($_GET['privateNote']) ? define('GET_PRIVATENOTE', $_GET['privateNote']): define('GET_PRIVATENOTE', '');
+isset($_GET['tags']) ? define('GET_TAGS', $_GET['tags']): define('GET_TAGS', '');
+isset($_GET['copyOf']) ? define('GET_COPYOF', $_GET['copyOf']): define('GET_COPYOF', '');
+
+isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', '');
+isset($_POST['address']) ? define('POST_ADDRESS', $_POST['address']): define('POST_ADDRESS', '');
+isset($_POST['description']) ? define('POST_DESCRIPTION', $_POST['description']): define('POST_DESCRIPTION', '');
+isset($_POST['privateNote']) ? define('POST_PRIVATENOTE', $_POST['privateNote']): define('POST_PRIVATENOTE', '');
+isset($_POST['status']) ? define('POST_STATUS', $_POST['status']): define('POST_STATUS', '');
+isset($_POST['referrer']) ? define('POST_REFERRER', $_POST['referrer']): define('POST_REFERRER', '');
+
+isset($_GET['popup']) ? define('GET_POPUP', $_GET['popup']): define('GET_POPUP', '');
+isset($_POST['popup']) ? define('POST_POPUP', $_POST['popup']): define('POST_POPUP', '');
+
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+if (!isset($_POST['tags'])) {
+ $_POST['tags'] = array();
+}
+//echo '<p>' . var_export($_POST, true) . '</p>';die();
+
+
+if ((GET_ACTION == "add") && !$userservice->isLoggedOn()) {
+ $loginqry = str_replace("'", '%27', stripslashes($_SERVER['QUERY_STRING']));
+ header('Location: '. createURL('login', '?'. $loginqry));
+ exit();
+}
+
+if ($userservice->isLoggedOn()) {
+ $currentUser = $userservice->getCurrentObjectUser();
+ $currentUserID = $currentUser->getId();
+ $currentUsername = $currentUser->getUsername();
+}
+
+
+@list($url, $user, $cat) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+
+$endcache = false;
+if ($usecache) {
+ // Generate hash for caching on
+ $hash = md5($_SERVER['REQUEST_URI'] . $user);
+
+ // Don't cache if its users' own bookmarks
+ if ($userservice->isLoggedOn()) {
+ if ($currentUsername != $user) {
+ // Cache for 5 minutes
+ $cacheservice->Start($hash);
+ $endcache = true;
+ }
+ } else {
+ // Cache for 30 minutes
+ $cacheservice->Start($hash, 1800);
+ $endcache = true;
+ }
+}
+
+$pagetitle = $rssCat = $catTitle = '';
+if ($user) {
+ if (is_int($user)) {
+ $userid = intval($user);
+ } else {
+ if (!($userinfo = $userservice->getUserByUsername($user))) {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+ } else {
+ $userid = $userinfo['uId'];
+ }
+ }
+ $pagetitle .= ': '. $user;
+}
+if ($cat) {
+ $catTitle = ': '. str_replace('+', ' + ', $cat);
+
+ $catTitleWithUrls = ': ';
+ $titleTags = explode('+', filter($cat));
+ for($i = 0; $i<count($titleTags);$i++) {
+ $catTitleWithUrls.= $titleTags[$i].'<a href="'.createUrl('bookmarks', $user.'/'.aggregateTags($titleTags, '+', $titleTags[$i])).'" title="'.T_('Remove the tag from the selection').'">*</a> + ';
+ }
+ $catTitleWithUrls = substr($catTitleWithUrls, 0, strlen($catTitleWithUrls) - strlen(' + '));
+
+ $pagetitle .= $catTitleWithUrls;
+}
+else
+{
+ $catTitleWithUrls = '';
+}
+$pagetitle = substr($pagetitle, 2);
+
+// Header variables
+$tplVars['loadjs'] = true;
+
+// ADD A BOOKMARK
+$saved = false;
+$templatename = 'bookmarks.tpl';
+if ($userservice->isLoggedOn() && POST_SUBMITTED != '') {
+ if (!POST_TITLE || !POST_ADDRESS) {
+ $tplVars['error'] = T_('Your bookmark must have a title and an address');
+ $templatename = 'editbookmark.tpl';
+ } else {
+ $address = trim(POST_ADDRESS);
+ if (!SemanticScuttle_Model_Bookmark::isValidUrl($address)) {
+ $tplVars['error'] = T_('This bookmark URL may not be added');
+ $templatename = 'editbookmark.tpl';
+ } else if ($bookmarkservice->bookmarkExists($address, $currentUserID)) {
+ // If the bookmark exists already, edit the original
+ $bookmark = $bookmarkservice->getBookmarkByAddress($address);
+ header('Location: '. createURL('edit', $bookmark['bId']));
+ exit();
+ // If it's new, save it
+ } else {
+ $title = trim(POST_TITLE);
+ $description = trim(POST_DESCRIPTION);
+ $privateNote = trim(POST_PRIVATENOTE);
+ $status = intval(POST_STATUS);
+ $categories = explode(',', $_POST['tags']);
+ $saved = true;
+ if ($bookmarkservice->addBookmark($address, $title, $description, $privateNote, $status, $categories)) {
+ if (POST_POPUP != '') {
+ $tplVars['msg'] = '<script type="text/javascript">window.close();</script>';
+ } else {
+ $tplVars['msg'] = T_('Bookmark saved') . ' <a href="javascript:history.go(-2)">'.T_('(Come back to previous page.)').'</a>';
+ // Redirection option
+ if ($GLOBALS['useredir']) {
+ $address = $GLOBALS['url_redir'] . $address;
+ }
+ }
+ } else {
+ $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.');
+ $templatename = 'editbookmark.tpl';
+ $saved = false;
+ }
+ }
+ }
+}
+
+if (GET_ACTION == "add") {
+ // If the bookmark exists already, edit the original
+ if ($bookmarkservice->bookmarkExists(stripslashes(GET_ADDRESS), $currentUserID)) {
+ $bookmark = $bookmarkservice->getBookmarks(0, NULL, $currentUserID, NULL, NULL, NULL, NULL, NULL, NULL, $bookmarkservice->getHash(stripslashes(GET_ADDRESS)));
+ $popup = (GET_POPUP!='') ? '?popup=1' : '';
+ header('Location: '. createURL('edit', $bookmark['bookmarks'][0]['bId'] . $popup));
+ exit();
+ }
+ $templatename = 'editbookmark.tpl';
+}
+
+if ($templatename == 'editbookmark.tpl') {
+ if ($userservice->isLoggedOn()) {
+ $tplVars['formaction'] = createURL('bookmarks', $currentUsername);
+ if (POST_SUBMITTED != '') {
+ $tplVars['row'] = array(
+ 'bTitle' => stripslashes(POST_TITLE),
+ 'bAddress' => stripslashes(POST_ADDRESS),
+ 'bDescription' => stripslashes(POST_DESCRIPTION),
+ 'bPrivateNote' => stripslashes(POST_PRIVATENOTE),
+ 'tags' => ($_POST['tags'] ? $_POST['tags'] : array()),
+ 'bStatus' => $GLOBALS['defaults']['privacy'],
+ );
+ $tplVars['tags'] = $_POST['tags'];
+ } else {
+ if(GET_COPYOF != '') { //copy from bookmarks page
+ $tplVars['row'] = $bookmarkservice->getBookmark(intval(GET_COPYOF), true);
+ if(!$currentUser->isAdmin()) {
+ $tplVars['row']['bPrivateNote'] = ''; //only admin can copy private note
+ }
+ }else { //copy from pop-up bookmarklet
+ $tplVars['row'] = array(
+ 'bTitle' => stripslashes(GET_TITLE),
+ 'bAddress' => stripslashes(GET_ADDRESS),
+ 'bDescription' => stripslashes(GET_DESCRIPTION),
+ 'bPrivateNote' => stripslashes(GET_PRIVATENOTE),
+ 'tags' => (GET_TAGS ? explode(',', stripslashes(GET_TAGS)) : array()),
+ 'bStatus' => $GLOBALS['defaults']['privacy']
+ );
+ }
+
+ }
+ $title = T_('Add a Bookmark');
+ $tplVars['referrer'] = '';;
+ if (isset($_SERVER['HTTP_REFERER'])) {
+ $tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+ }
+ $tplVars['pagetitle'] = $title;
+ $tplVars['subtitle'] = $title;
+ $tplVars['btnsubmit'] = T_('Add Bookmark');
+ $tplVars['popup'] = (GET_POPUP!='') ? GET_POPUP : null;
+ } else {
+ $tplVars['error'] = T_('You must be logged in before you can add bookmarks.');
+ }
+} else if ($user && GET_POPUP == '') {
+
+ $tplVars['sidebar_blocks'] = array('watchstatus');
+
+ if (!$cat) { //user page without tags
+ $rssTitle = "My Bookmarks";
+ $cat = NULL;
+ $tplVars['currenttag'] = NULL;
+ //$tplVars['sidebar_blocks'][] = 'menu2';
+ $tplVars['sidebar_blocks'][] = 'linked';
+ $tplVars['sidebar_blocks'][] = 'popular';
+ } else { //pages with tags
+ $rssTitle = "Tags" . $catTitle;
+ $rssCat = '/'. filter($cat, 'url');
+ $tplVars['currenttag'] = $cat;
+ $tplVars['sidebar_blocks'][] = 'tagactions';
+ //$tplVars['sidebar_blocks'][] = 'menu2';
+ $tplVars['sidebar_blocks'][] = 'linked';
+ $tplVars['sidebar_blocks'][] = 'related';
+ /*$tplVars['sidebar_blocks'][] = 'menu';*/
+ }
+ $tplVars['sidebar_blocks'][] = 'menu2';
+ $tplVars['popCount'] = 30;
+ //$tplVars['sidebar_blocks'][] = 'popular';
+
+ $tplVars['userid'] = $userid;
+ $tplVars['userinfo'] = $userinfo;
+ $tplVars['user'] = $user;
+ $tplVars['range'] = 'user';
+
+ // Pagination
+ $perpage = getPerPageCount($currentUser);
+ if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+ } else {
+ $page = 0;
+ $start = 0;
+ }
+
+ // Set template vars
+ $tplVars['rsschannels'] = array(
+ array(
+ sprintf(T_('%s: %s'), $sitename, $rssTitle),
+ createURL('rss', filter($user, 'url'))
+ . $rssCat . '?sort='.getSortOrder()
+ )
+ );
+
+ if ($userservice->isLoggedOn()) {
+ $currentUsername = $currentUser->getUsername();
+ if ($userservice->isPrivateKeyValid($currentUser->getPrivateKey())) {
+ array_push(
+ $tplVars['rsschannels'],
+ array(
+ sprintf(
+ T_('%s: %s (+private %s)'),
+ $sitename, $rssTitle, $currentUsername
+ ),
+ createURL('rss', filter($currentUsername, 'url'))
+ . $rssCat
+ . '?sort=' . getSortOrder()
+ . '&privateKey=' . $currentUser->getPrivateKey()
+ )
+ );
+ }
+ }
+
+ $tplVars['page'] = $page;
+ $tplVars['start'] = $start;
+ $tplVars['bookmarkCount'] = $start + 1;
+
+ $bookmarks = $bookmarkservice->getBookmarks($start, $perpage, $userid, $cat, null, getSortOrder());
+ $tplVars['total'] = $bookmarks['total'];
+ $tplVars['bookmarks'] = $bookmarks['bookmarks'];
+ $tplVars['cat_url'] = createURL('bookmarks', '%s/%s');
+ $tplVars['nav_url'] = createURL('bookmarks', '%s/%s%s');
+ if ($userservice->isLoggedOn() && $user == $currentUsername) {
+ $tplVars['pagetitle'] = T_('My Bookmarks') . $catTitle;
+ $tplVars['subtitlehtml'] = T_('My Bookmarks') . $catTitleWithUrls;
+ } else {
+ $tplVars['pagetitle'] = $user.': '.$cat;
+ $tplVars['subtitlehtml'] = $user . $catTitleWithUrls;
+ }
+}
+
+$tplVars['summarizeLinkedTags'] = true;
+$tplVars['pageName'] = PAGE_BOOKMARKS;
+
+
+$templateservice->loadTemplate($templatename, $tplVars);
+
+if ($usecache && $endcache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/edit.php b/www/edit.php
new file mode 100644
index 0000000..cbdcbcc
--- /dev/null
+++ b/www/edit.php
@@ -0,0 +1,121 @@
+<?php
+
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice = SemanticScuttle_Service_Factory :: get('Bookmark');
+
+/* Managing all possible inputs */
+isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', '');
+isset($_POST['delete']) ? define('POST_DELETE', $_POST['delete']): define('POST_DELETE', '');
+
+isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', '');
+isset($_POST['address']) ? define('POST_ADDRESS', $_POST['address']): define('POST_ADDRESS', '');
+isset($_POST['description']) ? define('POST_DESCRIPTION', $_POST['description']): define('POST_DESCRIPTION', '');
+isset($_POST['privateNote']) ? define('POST_PRIVATENOTE', $_POST['privateNote']): define('POST_PRIVATENOTE', '');
+isset($_POST['status']) ? define('POST_STATUS', $_POST['status']): define('POST_STATUS', $GLOBALS['defaults']['privacy']);
+isset($_POST['tags']) ? define('POST_TAGS', $_POST['tags']): define('POST_TAGS', '');
+
+isset($_GET['popup']) ? define('GET_POPUP', $_GET['popup']): define('GET_POPUP', '');
+isset($_POST['popup']) ? define('POST_POPUP', $_POST['popup']): define('POST_POPUP', '');
+isset($_POST['referrer']) ? define('POST_REFERRER', $_POST['referrer']): define('POST_REFERRER', '');
+
+// Header variables
+$tplVars['pagetitle'] = T_('Edit Bookmark');
+$tplVars['subtitle'] = T_('Edit Bookmark');
+$tplVars['loadjs'] = true;
+
+list ($url, $bookmark) = explode('/', $_SERVER['PATH_INFO']);
+
+if (!($row = $bookmarkservice->getBookmark(intval($bookmark), true))) {
+ $tplVars['error'] = sprintf(T_('Bookmark with id %s not was not found'), $bookmark);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+} else {
+
+ if (!$bookmarkservice->editAllowed($row)) {
+ $tplVars['error'] = T_('You are not allowed to edit this bookmark');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+ } else if (POST_SUBMITTED != '') {
+
+
+
+ if (!POST_TITLE || !POST_ADDRESS) {
+ $tplVars['error'] = T_('Your bookmark must have a title and an address');
+ } else {
+ // Update bookmark
+ $bId = intval($bookmark);
+ $address = trim(POST_ADDRESS);
+ $title = trim(POST_TITLE);
+ $description = trim(POST_DESCRIPTION);
+ $privateNote = trim(POST_PRIVATENOTE);
+ $status = intval(POST_STATUS);
+ $tags = trim(POST_TAGS);
+
+ if (!$bookmarkservice->updateBookmark($bId, $address, $title, $description, $privateNote, $status, $tags)) {
+ $tplvars['error'] = T_('Error while saving your bookmark');
+ } else {
+ if (POST_POPUP != '') {
+ //$tplVars['msg'] = (POST_POPUP != '') ? '<script type="text/javascript">window.close();</script>' : T_('Bookmark saved');
+ $tplVars['msg'] = '<script type="text/javascript">window.close();</script>';
+ } elseif (POST_REFERRER != '') {
+ $tplVars['msg'] = T_('Bookmark saved');
+ header('Location: '. POST_REFERRER);
+ } else {
+ $tplVars['msg'] = T_('Bookmark saved');
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername()));
+ }
+ }
+ }
+ } else {
+ if (POST_DELETE != '') {
+ // Delete bookmark
+ if ($bookmarkservice->deleteBookmark($bookmark)) {
+ if (POST_POPUP != '') {
+ $tplVars['msg'] = '<script type="text/javascript">window.close();</script>';
+ } elseif (POST_REFERRER != '') {
+ header('Location: '. POST_REFERRER);
+ } else {
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername()));
+ }
+ } else {
+ $tplVars['error'] = T_('Failed to delete bookmark');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+ }
+ }
+ }
+
+ $tplVars['popup'] = (GET_POPUP) ? GET_POPUP : null;
+ $tplVars['row'] = $row;
+ $tplVars['formaction'] = createURL('edit', $bookmark);
+ $tplVars['btnsubmit'] = T_('Save Changes');
+ $tplVars['showdelete'] = true;
+ $tplVars['referrer'] = '';
+ if (isset($_SERVER['HTTP_REFERER'])) {
+ $tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+ }
+ $templateservice->loadTemplate('editbookmark.tpl', $tplVars);
+}
+?>
diff --git a/www/go.php b/www/go.php
new file mode 100644
index 0000000..6a36ba9
--- /dev/null
+++ b/www/go.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * Short URL redirection service.
+ * Just call http://example.org/go/shortname
+ * to get redirected to it. Helpful to get static URLs for
+ * moving targets.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+$httpContentType = false;
+require_once 'www-header.php';
+
+if (!$GLOBALS['shorturl']) {
+ header('HTTP/1.0 500 Internal Server Error');
+ header('Content-Type: text/plain');
+ echo 'Short URL service deactivated';
+ exit();
+}
+
+if (!isset($_SERVER['PATH_INFO'])) {
+ header('HTTP/1.0 400 Bad Request');
+ header('Content-Type: text/plain');
+ echo 'Short URL name missing';
+ exit();
+}
+
+list($url, $short) = explode('/', $_SERVER['PATH_INFO']);
+
+$bs = SemanticScuttle_Service_Factory::get('Bookmark');
+$bookmark = $bs->getBookmarkByShortname($short);
+if ($bookmark === false) {
+ header('HTTP/1.0 404 Not found');
+ header('Content-Type: text/plain');
+ echo 'No bookmark found with short name of: ' . $short;
+ exit();
+}
+
+header('HTTP/1.0 302 Found');
+header('Location: ' . $bookmark['bAddress']);
+?> \ No newline at end of file
diff --git a/www/gsearch/context.php b/www/gsearch/context.php
new file mode 100644
index 0000000..fd10c4e
--- /dev/null
+++ b/www/gsearch/context.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Google custom search context information for SemanticScuttle.
+ * Tells Google meta data about the search.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ * @link http://www.google.com/cse/docs/cref.html
+ */
+require_once '../www-header.php';
+
+if ($GLOBALS['enableGoogleCustomSearch'] == false) {
+ header('HTTP/1.0 403 Forbidden');
+ header('Content-Type: text/plain; charset=utf-8');
+ echo "Google Custom Search disabled."
+ . " You can enable it into the config.php file.\n";
+ die();
+}
+?>
+<?xml version="1.0" encoding="UTF-8" ?>
+<GoogleCustomizations>
+ <CustomSearchEngine>
+ <Title><?php echo htmlspecialchars($GLOBALS['sitename']) ?></Title>
+ <Description><?php echo filter($GLOBALS['welcomeMessage']) ?></Description>
+ <Context>
+ <BackgroundLabels>
+ <Label name="include" mode="FILTER" />
+ </BackgroundLabels>
+ </Context>
+ <LookAndFeel nonprofit="false">
+ </LookAndFeel>
+ </CustomSearchEngine>
+ <Include type="Annotations" href="<?php echo ROOT;?>api/export_gcs.php?xml=1" />
+</GoogleCustomizations>
diff --git a/www/gsearch/index.php b/www/gsearch/index.php
new file mode 100644
index 0000000..477aacb
--- /dev/null
+++ b/www/gsearch/index.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Google custom search for SemanticScuttle.
+ * This is a "linked CSE", a linked custom search engine.
+ * See the linked docs for more info.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ * @link http://www.google.com/cse/docs/cref.html
+ */
+require_once '../www-header.php';
+
+if ($GLOBALS['enableGoogleCustomSearch'] == false) {
+ header('HTTP/1.0 403 Forbidden');
+ header('Content-Type: text/plain; charset=utf-8');
+ echo "Google Custom Search disabled."
+ . " You can enable it into the config.php file.\n";
+ die();
+}
+
+?>
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+ <head>
+ <title><?php echo htmlspecialchars($GLOBALS['sitename']); ?></title>
+ </head>
+ <body>
+ <div style="text-align: center; padding-top: 2em">
+
+<!-- Google CSE Search Box Begins -->
+<form id="cref" action="http://www.google.com/cse">
+ <input type="hidden" name="cref" value="<?php echo ROOT;?>gsearch/context.php" />
+ <input type="text" name="q" size="40" />
+ <input type="submit" name="sa" value="Search" />
+</form>
+<script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=cref"></script>
+<!-- Google CSE Search Box Ends -->
+<small>Based on <a href="http://www.google.com/coop/cse/">Google Custom Search</a> over this <a href="<?php echo ROOT ?>api/export_gcs.php">list of websites</a> from <?php echo $GLOBALS['sitename'] ?>.</small>
+<br />
+<br />
+<small><a href="<?php echo ROOT?>"><?php echo T_('Come back to ').$GLOBALS['sitename'] ?>...</a></small>
+
+
+<?php if ($userservice->isLoggedOn() && $currentUser->isAdmin()) {
+ echo '<p><small>';
+ echo T_('Admin tips: ');
+ echo T_('To refresh manually Google Custom Search Engine, goes to: ');
+ echo '<a href="http://www.google.com/coop/cse/cref?cref='
+ . ROOT . 'gsearch/context.php">http://www.google.com/coop/cse/cref</a>'
+ . '<br/>';
+ echo T_('If no result appears, check that all the urls are valid in the admin section.');
+ echo '</small></p>';
+
+}
+?>
+
+ </div>
+ </body>
+</html>
+
+
diff --git a/www/history.php b/www/history.php
new file mode 100644
index 0000000..50cf3eb
--- /dev/null
+++ b/www/history.php
@@ -0,0 +1,93 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing all possible inputs */
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+@list($url, $hash) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+
+
+if ($usecache) {
+ // Generate hash for caching on
+ $hashtext = $_SERVER['REQUEST_URI'];
+ if ($userservice->isLoggedOn()) {
+ $hashtext .= $currentUser->getUsername();
+ }
+ $cachehash = md5($hashtext);
+
+ // Cache for 30 minutes
+ $cacheservice->Start($cachehash, 1800);
+}
+
+// Pagination
+$perpage = getPerPageCount($currentUser);
+if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+} else {
+ $page = 0;
+ $start = 0;
+}
+
+if ($bookmark = $bookmarkservice->getBookmarkByHash($hash)) {
+ // Template variables
+ $bookmarks = $bookmarkservice->getBookmarks($start, $perpage, NULL, NULL, NULL, getSortOrder(), NULL, NULL, NULL, $hash);
+ $tplVars['pagetitle'] = T_('History') .': '. $bookmark['bAddress'];
+ $tplVars['subtitle'] = sprintf(T_('History for %s'), $bookmark['bAddress']);
+ $tplVars['loadjs'] = true;
+ $tplVars['page'] = $page;
+ $tplVars['start'] = $start;
+ $tplVars['bookmarkCount'] = $start + 1;
+ $tplVars['total'] = $bookmarks['total'];
+ $tplVars['bookmarks'] = $bookmarks['bookmarks'];
+ $tplVars['hash'] = $hash;
+ $tplVars['popCount'] = 50;
+ $tplVars['sidebar_blocks'] = array('common');
+ //$tplVars['cat_url'] = createURL('tags', '%2$s');
+ $tplVars['cat_url'] = createURL('bookmarks', '%1$s/%2$s');
+ $tplVars['nav_url'] = createURL('history', $hash .'/%3$s');
+ $tplVars['rsschannels'] = array();
+ if($userservice->isLoggedOn()) {
+ $tplVars['user'] = $currentUser->getUsername();
+ } else {
+ $tplVars['user'] = '';
+ }
+ $templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+} else {
+ // Throw a 404 error
+ $tplVars['error'] = T_('Address was not found');
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+}
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($cachehash);
+}
+?>
diff --git a/www/import.php b/www/import.php
new file mode 100644
index 0000000..f5fca0d
--- /dev/null
+++ b/www/import.php
@@ -0,0 +1,133 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+// No specific services
+
+/* Managing all possible inputs */
+// First input is $_FILES
+// Other inputs
+isset($_POST['status'])
+ ? define('POST_STATUS', $_POST['status'])
+ : define('POST_STATUS', $GLOBALS['defaults']['privacy']);
+
+
+if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['size'] > 0) {
+ $userinfo = $userservice->getCurrentObjectUser();
+
+ if (is_numeric(POST_STATUS)) {
+ $status = intval(POST_STATUS);
+ } else {
+ $status = $GLOBALS['defaults']['privacy'];
+ }
+
+ $depth = array();
+ $xml_parser = xml_parser_create();
+ xml_set_element_handler($xml_parser, "startElement", "endElement");
+
+ if (!($fp = fopen($_FILES['userfile']['tmp_name'], "r")))
+ die(T_("Could not open XML input"));
+
+ while ($data = fread($fp, 4096)) {
+ if (!xml_parse($xml_parser, $data, feof($fp))) {
+ die(sprintf(T_("XML error: %s at line %d"),
+ xml_error_string(xml_get_error_code($xml_parser)),
+ xml_get_current_line_number($xml_parser)));
+ }
+ }
+ xml_parser_free($xml_parser);
+ //FIXME: show errors and warnings
+ header('Location: '. createURL('bookmarks', $userinfo->getUsername()));
+} else {
+ $templatename = 'importDelicious.tpl';
+ $tplVars['subtitle'] = T_('Import Bookmarks from del.icio.us');
+ $tplVars['formaction'] = createURL('import');
+ $templateservice->loadTemplate($templatename, $tplVars);
+}
+
+
+
+function startElement($parser, $name, $attrs) {
+ global $depth, $status, $tplVars, $userservice;
+
+ $bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+
+ if ($name == 'POST') {
+ $newstatus = $status;
+ while (list($attrTitle, $attrVal) = each($attrs)) {
+ switch ($attrTitle) {
+ case 'HREF':
+ $bAddress = $attrVal;
+ break;
+ case 'DESCRIPTION':
+ $bTitle = $attrVal;
+ break;
+ case 'EXTENDED':
+ $bDescription = $attrVal;
+ break;
+ case 'TIME':
+ $bDatetime = $attrVal;
+ break;
+ case 'TAG':
+ $tags = strtolower($attrVal);
+ break;
+ case 'PRIVATE':
+ if ($attrVal == 'yes') {
+ $newstatus = 2;
+ }
+ break;
+ }
+ }
+ if ($bookmarkservice->bookmarkExists($bAddress, $userservice->getCurrentUserId())) {
+ $tplVars['error'] = T_('You have already submitted this bookmark.');
+ } else {
+ // Strangely, PHP can't work out full ISO 8601 dates, so we have to chop off the Z.
+ $bDatetime = substr($bDatetime, 0, -1);
+
+ // If bookmark claims to be from the future, set it to be now instead
+ if (strtotime($bDatetime) > time()) {
+ $bDatetime = gmdate('Y-m-d H:i:s');
+ }
+
+ $res = $bookmarkservice->addBookmark(
+ $bAddress, $bTitle, $bDescription, '', $newstatus, $tags,
+ null, $bDatetime, true, true
+ );
+ if ($res) {
+ $tplVars['msg'] = T_('Bookmark imported.');
+ } else {
+ $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.');
+ }
+ }
+ }
+ if (!isset($depth[(int)$parser])) {
+ $depth[(int)$parser] = 0;
+ }
+ $depth[(int)$parser]++;
+}
+
+function endElement($parser, $name) {
+ global $depth;
+ $depth[(int)$parser]--;
+}
+?>
diff --git a/www/importNetscape.php b/www/importNetscape.php
new file mode 100644
index 0000000..9a52407
--- /dev/null
+++ b/www/importNetscape.php
@@ -0,0 +1,136 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+
+
+/* Managing all possible inputs */
+// First input is $_FILES
+// Other inputs
+isset($_POST['status']) ? define('POST_STATUS', $_POST['status']): define('POST_STATUS', $GLOBALS['defaults']['privacy']);
+
+$countImportedBookmarks = 0;
+$tplVars['msg'] = '';
+
+if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['size'] > 0) {
+ $userinfo = $userservice->getCurrentObjectUser();
+
+ if (is_numeric(POST_STATUS)) {
+ $status = intval(POST_STATUS);
+ } else {
+ $status = $GLOBALS['defaults']['privacy'];
+ }
+
+ // File handle
+ $html = file_get_contents($_FILES['userfile']['tmp_name']);
+
+ // Create link array
+ //preg_match_all('/<a\s+(.*?)\s*\/*>([^<]*)/si', $html, $matches);
+ preg_match_all('/<a\s+(.*?)>([^<]*?)<\/a>.*?(<dd>([^<]*)|<dt>)/si', $html, $matches);
+
+ //var_dump($matches);die();
+
+
+ $links = $matches[1];
+ $titles = $matches[2];
+ $descriptions = $matches[4];
+
+ $size = count($links);
+ for ($i = 0; $i < $size; $i++) {
+
+ // echo "<hr/>";
+ // echo $links[$i]."<br/>";
+
+ preg_match_all('/(\w*\s*=\s*"[^"]*")/', $links[$i], $attributes);
+ //$attributes = $attributes[0]; // we keep just one row
+
+ $bDatetime = ""; //bDateTime optional
+ $bCategories = ""; //bCategories optional
+ $bPrivateNote = ""; //bPrivateNote optional
+ $bPrivate = $status; //bPrivate set default
+
+ foreach ($attributes[0] as $attribute) {
+ $att = preg_split('/\s*=\s*/s', $attribute, 2);
+ $attrTitle = $att[0];
+
+ $attrVal = str_replace(
+ '&quot;', '"',
+ preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1])
+ );
+
+ switch ($attrTitle) {
+ case "HREF":
+ $bAddress = $attrVal;
+ break;
+ case "ADD_DATE":
+ $bDatetime = gmdate('Y-m-d H:i:s', $attrVal);
+ break;
+ case "TAGS":
+ $bCategories = $attrVal;
+ break;
+ case "NOTE":
+ $bPrivateNote = $attrVal;
+ break;
+ case "PRIVATE":
+ if ($attrVal) {
+ $bPrivate = 2;//private
+ }
+ }
+ }
+ $bTitle = trim($titles[$i]);
+ $bDescription = trim($descriptions[$i]);
+
+ if ($bookmarkservice->bookmarkExists($bAddress, $userservice->getCurrentUserId())) {
+ $tplVars['error'] = T_('You have already submitted some of these bookmarks.');
+ } else {
+ // If bookmark is local (like javascript: or place: in Firefox3), do nothing
+ if(substr($bAddress, 0, 7) == "http://" || substr($bAddress, 0, 8) == "https://") {
+
+ // If bookmark claims to be from the future, set it to be now instead
+ if (strtotime($bDatetime) > time()) {
+ $bDatetime = gmdate('Y-m-d H:i:s');
+ }
+
+ if ($bookmarkservice->addBookmark($bAddress, $bTitle, $bDescription, $bPrivateNote, $bPrivate, $bCategories, null, $bDatetime, false, true)) {
+ $countImportedBookmarks++;
+ } else {
+ $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.');
+ }
+ }
+ }
+ }
+ //header('Location: '. createURL('bookmarks', $userinfo->getUsername()));
+ $templatename = 'importNetscape.tpl';
+ $tplVars['msg'].= T_('Bookmarks found: ').$size.' ';
+ $tplVars['msg'].= T_('Bookmarks imported: ').' '.$countImportedBookmarks;
+ $tplVars['subtitle'] = T_('Import Bookmarks from Browser File');
+ $tplVars['formaction'] = createURL('importNetscape');
+ $templateservice->loadTemplate($templatename, $tplVars);
+} else {
+ $templatename = 'importNetscape.tpl';
+ $tplVars['subtitle'] = T_('Import Bookmarks from Browser File');
+ $tplVars['formaction'] = createURL('importNetscape');
+ $templateservice->loadTemplate($templatename, $tplVars);
+}
+?>
diff --git a/www/importStructure.php b/www/importStructure.php
new file mode 100644
index 0000000..53b1b66
--- /dev/null
+++ b/www/importStructure.php
@@ -0,0 +1,76 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$tag2tagservice =SemanticScuttle_Service_Factory::get('Tag2Tag');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+
+/* Managing all possible inputs */
+// First input is $_FILES
+
+$tplVars['msg'] = '';
+
+if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['size'] > 0) {
+ $userinfo = $userservice->getCurrentObjectUser();
+
+
+ // File handle
+ $html = file_get_contents($_FILES['userfile']['tmp_name']);
+
+ // Create link array
+ preg_match_all('/(.*?)\n/', $html, $matches);
+
+ //print_r($matches); die();
+
+ $fatherTag = '';
+ $countNewLinks = 0;
+ foreach($matches[1] as $match) {
+ if($match == '') {
+ // do nothing because void line
+ }elseif(substr($match, 0, 2) == '//') {
+ // do nothing because commentary
+ } elseif(substr($match, 0, 2) == ' ') {
+ // add as child of previous tag
+ if($fatherTag != '') {
+ $tag2tagservice->addLinkedTags($fatherTag, $match, '>', $currentUser->getId());
+ $countNewLinks++;
+ } else {
+ $tplVars['error'] = T_('Bad indentation'). ' '.$match;
+ }
+ } else{
+ $fatherTag = $match;
+ }
+ }
+ $tplVars['msg'] = T_('New links between tags: ').$countNewLinks;
+
+}
+
+$templatename = 'importStructure.tpl';
+$tplVars['subtitle'] = T_('Import Structure');
+$tplVars['formaction'] = createURL('importStructure');
+$templateservice->loadTemplate($templatename, $tplVars);
+
+?>
diff --git a/www/index.php b/www/index.php
new file mode 100644
index 0000000..007aae0
--- /dev/null
+++ b/www/index.php
@@ -0,0 +1,117 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing all possible inputs */
+isset($_GET['action']) ? define('GET_ACTION', $_GET['action']): define('GET_ACTION', '');
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+
+// Logout action
+if (GET_ACTION == "logout") {
+ $userservice->logout();
+ $tplVars['currentUser'] = null;
+ $tplvars['msg'] = T_('You have now logged out');
+}
+
+
+// Header variables
+$tplVars['loadjs'] = true;
+$tplVars['rsschannels'] = array(
+ array(
+ sprintf(T_('%s: Recent bookmarks'), $sitename),
+ createURL('rss') . '?sort=' . getSortOrder()
+ )
+);
+
+if ($userservice->isLoggedOn()) {
+ if ($userservice->isPrivateKeyValid($currentUser->getPrivateKey())) {
+ $currentUsername = $currentUser->getUsername();
+ array_push(
+ $tplVars['rsschannels'],
+ array(
+ sprintf(
+ T_('%s: Recent bookmarks (+private %s)'),
+ $sitename, $currentUsername
+ ),
+ createURL('rss')
+ . '?sort=' . getSortOrder()
+ . '&privateKey=' . $currentUser->getPrivateKey()
+ )
+ );
+ }
+}
+
+if ($usecache) {
+ // Generate hash for caching on
+ $hashtext = $_SERVER['REQUEST_URI'];
+ if ($userservice->isLoggedOn()) {
+ $hashtext .= $userservice->getCurrentUserID();
+ }
+ $hash = md5($hashtext);
+
+ // Cache for 15 minutes
+ $cacheservice->Start($hash, 900);
+}
+
+// Pagination
+$perpage = getPerPageCount($currentUser);
+if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+} else {
+ $page = 0;
+ $start = 0;
+}
+
+$tplVars['page'] = $page;
+$tplVars['start'] = $start;
+$tplVars['popCount'] = 30;
+$tplVars['sidebar_blocks'] = $GLOBALS["index_sidebar_blocks"];
+$tplVars['range'] = 'all';
+$tplVars['pagetitle'] = T_('Store, share and tag your favourite links');
+$tplVars['subtitle'] = T_('All Bookmarks');
+$tplVars['bookmarkCount'] = $start + 1;
+
+$bookmarks = $bookmarkservice->getBookmarks($start, $perpage, NULL, NULL, NULL, getSortOrder(), NULL, 0, NULL);
+
+$tplVars['total'] = $bookmarks['total'];
+$tplVars['bookmarks'] = $bookmarks['bookmarks'];
+$tplVars['cat_url'] = createURL('bookmarks', '%1$s/%2$s');
+$tplVars['nav_url'] = createURL('index', '%3$s');
+$tplVars['summarizeLinkedTags'] = true;
+$tplVars['pageName'] = PAGE_INDEX;
+$tplVars['user'] = '';
+$tplVars['currenttag'] = '';
+
+$templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.js b/www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.js
new file mode 100644
index 0000000..9342483
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.js
@@ -0,0 +1,612 @@
+/*
+ * jQuery UI Autocomplete 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */
+(function( $, undefined ) {
+
+// used to prevent race conditions with remote data sources
+var requestIndex = 0;
+
+$.widget( "ui.autocomplete", {
+ options: {
+ appendTo: "body",
+ autoFocus: false,
+ delay: 300,
+ minLength: 1,
+ position: {
+ my: "left top",
+ at: "left bottom",
+ collision: "none"
+ },
+ source: null
+ },
+
+ pending: 0,
+
+ _create: function() {
+ var self = this,
+ doc = this.element[ 0 ].ownerDocument,
+ suppressKeyPress;
+
+ this.element
+ .addClass( "ui-autocomplete-input" )
+ .attr( "autocomplete", "off" )
+ // TODO verify these actually work as intended
+ .attr({
+ role: "textbox",
+ "aria-autocomplete": "list",
+ "aria-haspopup": "true"
+ })
+ .bind( "keydown.autocomplete", function( event ) {
+ if ( self.options.disabled || self.element.attr( "readonly" ) ) {
+ return;
+ }
+
+ suppressKeyPress = false;
+ var keyCode = $.ui.keyCode;
+ switch( event.keyCode ) {
+ case keyCode.PAGE_UP:
+ self._move( "previousPage", event );
+ break;
+ case keyCode.PAGE_DOWN:
+ self._move( "nextPage", event );
+ break;
+ case keyCode.UP:
+ self._move( "previous", event );
+ // prevent moving cursor to beginning of text field in some browsers
+ event.preventDefault();
+ break;
+ case keyCode.DOWN:
+ self._move( "next", event );
+ // prevent moving cursor to end of text field in some browsers
+ event.preventDefault();
+ break;
+ case keyCode.ENTER:
+ case keyCode.NUMPAD_ENTER:
+ // when menu is open and has focus
+ if ( self.menu.active ) {
+ // #6055 - Opera still allows the keypress to occur
+ // which causes forms to submit
+ suppressKeyPress = true;
+ event.preventDefault();
+ }
+ //passthrough - ENTER and TAB both select the current element
+ case keyCode.TAB:
+ if ( !self.menu.active ) {
+ return;
+ }
+ self.menu.select( event );
+ break;
+ case keyCode.ESCAPE:
+ self.element.val( self.term );
+ self.close( event );
+ break;
+ default:
+ // keypress is triggered before the input value is changed
+ clearTimeout( self.searching );
+ self.searching = setTimeout(function() {
+ // only search if the value has changed
+ if ( self.term != self.element.val() ) {
+ self.selectedItem = null;
+ self.search( null, event );
+ }
+ }, self.options.delay );
+ break;
+ }
+ })
+ .bind( "keypress.autocomplete", function( event ) {
+ if ( suppressKeyPress ) {
+ suppressKeyPress = false;
+ event.preventDefault();
+ }
+ })
+ .bind( "focus.autocomplete", function() {
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ self.selectedItem = null;
+ self.previous = self.element.val();
+ })
+ .bind( "blur.autocomplete", function( event ) {
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ clearTimeout( self.searching );
+ // clicks on the menu (or a button to trigger a search) will cause a blur event
+ self.closing = setTimeout(function() {
+ self.close( event );
+ self._change( event );
+ }, 150 );
+ });
+ this._initSource();
+ this.response = function() {
+ return self._response.apply( self, arguments );
+ };
+ this.menu = $( "<ul></ul>" )
+ .addClass( "ui-autocomplete" )
+ .appendTo( $( this.options.appendTo || "body", doc )[0] )
+ // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
+ .mousedown(function( event ) {
+ // clicking on the scrollbar causes focus to shift to the body
+ // but we can't detect a mouseup or a click immediately afterward
+ // so we have to track the next mousedown and close the menu if
+ // the user clicks somewhere outside of the autocomplete
+ var menuElement = self.menu.element[ 0 ];
+ if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
+ setTimeout(function() {
+ $( document ).one( 'mousedown', function( event ) {
+ if ( event.target !== self.element[ 0 ] &&
+ event.target !== menuElement &&
+ !$.ui.contains( menuElement, event.target ) ) {
+ self.close();
+ }
+ });
+ }, 1 );
+ }
+
+ // use another timeout to make sure the blur-event-handler on the input was already triggered
+ setTimeout(function() {
+ clearTimeout( self.closing );
+ }, 13);
+ })
+ .menu({
+ focus: function( event, ui ) {
+ var item = ui.item.data( "item.autocomplete" );
+ if ( false !== self._trigger( "focus", event, { item: item } ) ) {
+ // use value to match what will end up in the input, if it was a key event
+ if ( /^key/.test(event.originalEvent.type) ) {
+ self.element.val( item.value );
+ }
+ }
+ },
+ selected: function( event, ui ) {
+ var item = ui.item.data( "item.autocomplete" ),
+ previous = self.previous;
+
+ // only trigger when focus was lost (click on menu)
+ if ( self.element[0] !== doc.activeElement ) {
+ self.element.focus();
+ self.previous = previous;
+ // #6109 - IE triggers two focus events and the second
+ // is asynchronous, so we need to reset the previous
+ // term synchronously and asynchronously :-(
+ setTimeout(function() {
+ self.previous = previous;
+ self.selectedItem = item;
+ }, 1);
+ }
+
+ if ( false !== self._trigger( "select", event, { item: item } ) ) {
+ self.element.val( item.value );
+ }
+ // reset the term after the select event
+ // this allows custom select handling to work properly
+ self.term = self.element.val();
+
+ self.close( event );
+ self.selectedItem = item;
+ },
+ blur: function( event, ui ) {
+ // don't set the value of the text field if it's already correct
+ // this prevents moving the cursor unnecessarily
+ if ( self.menu.element.is(":visible") &&
+ ( self.element.val() !== self.term ) ) {
+ self.element.val( self.term );
+ }
+ }
+ })
+ .zIndex( this.element.zIndex() + 1 )
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+ .css({ top: 0, left: 0 })
+ .hide()
+ .data( "menu" );
+ if ( $.fn.bgiframe ) {
+ this.menu.element.bgiframe();
+ }
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-autocomplete-input" )
+ .removeAttr( "autocomplete" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-autocomplete" )
+ .removeAttr( "aria-haspopup" );
+ this.menu.element.remove();
+ $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+ if ( key === "source" ) {
+ this._initSource();
+ }
+ if ( key === "appendTo" ) {
+ this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
+ }
+ if ( key === "disabled" && value && this.xhr ) {
+ this.xhr.abort();
+ }
+ },
+
+ _initSource: function() {
+ var self = this,
+ array,
+ url;
+ if ( $.isArray(this.options.source) ) {
+ array = this.options.source;
+ this.source = function( request, response ) {
+ response( $.ui.autocomplete.filter(array, request.term) );
+ };
+ } else if ( typeof this.options.source === "string" ) {
+ url = this.options.source;
+ this.source = function( request, response ) {
+ if ( self.xhr ) {
+ self.xhr.abort();
+ }
+ self.xhr = $.ajax({
+ url: url,
+ data: request,
+ dataType: "json",
+ autocompleteRequest: ++requestIndex,
+ success: function( data, status ) {
+ if ( this.autocompleteRequest === requestIndex ) {
+ response( data );
+ }
+ },
+ error: function() {
+ if ( this.autocompleteRequest === requestIndex ) {
+ response( [] );
+ }
+ }
+ });
+ };
+ } else {
+ this.source = this.options.source;
+ }
+ },
+
+ search: function( value, event ) {
+ value = value != null ? value : this.element.val();
+
+ // always save the actual value, not the one passed as an argument
+ this.term = this.element.val();
+
+ if ( value.length < this.options.minLength ) {
+ return this.close( event );
+ }
+
+ clearTimeout( this.closing );
+ if ( this._trigger( "search", event ) === false ) {
+ return;
+ }
+
+ return this._search( value );
+ },
+
+ _search: function( value ) {
+ this.pending++;
+ this.element.addClass( "ui-autocomplete-loading" );
+
+ this.source( { term: value }, this.response );
+ },
+
+ _response: function( content ) {
+ if ( !this.options.disabled && content && content.length ) {
+ content = this._normalize( content );
+ this._suggest( content );
+ this._trigger( "open" );
+ } else {
+ this.close();
+ }
+ this.pending--;
+ if ( !this.pending ) {
+ this.element.removeClass( "ui-autocomplete-loading" );
+ }
+ },
+
+ close: function( event ) {
+ clearTimeout( this.closing );
+ if ( this.menu.element.is(":visible") ) {
+ this.menu.element.hide();
+ this.menu.deactivate();
+ this._trigger( "close", event );
+ }
+ },
+
+ _change: function( event ) {
+ if ( this.previous !== this.element.val() ) {
+ this._trigger( "change", event, { item: this.selectedItem } );
+ }
+ },
+
+ _normalize: function( items ) {
+ // assume all items have the right format when the first item is complete
+ if ( items.length && items[0].label && items[0].value ) {
+ return items;
+ }
+ return $.map( items, function(item) {
+ if ( typeof item === "string" ) {
+ return {
+ label: item,
+ value: item
+ };
+ }
+ return $.extend({
+ label: item.label || item.value,
+ value: item.value || item.label
+ }, item );
+ });
+ },
+
+ _suggest: function( items ) {
+ var ul = this.menu.element
+ .empty()
+ .zIndex( this.element.zIndex() + 1 );
+ this._renderMenu( ul, items );
+ // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
+ this.menu.deactivate();
+ this.menu.refresh();
+
+ // size and position menu
+ ul.show();
+ this._resizeMenu();
+ ul.position( $.extend({
+ of: this.element
+ }, this.options.position ));
+
+ if ( this.options.autoFocus ) {
+ this.menu.next( new $.Event("mouseover") );
+ }
+ },
+
+ _resizeMenu: function() {
+ var ul = this.menu.element;
+ ul.outerWidth( Math.max(
+ ul.width( "" ).outerWidth(),
+ this.element.outerWidth()
+ ) );
+ },
+
+ _renderMenu: function( ul, items ) {
+ var self = this;
+ $.each( items, function( index, item ) {
+ self._renderItem( ul, item );
+ });
+ },
+
+ _renderItem: function( ul, item) {
+ return $( "<li></li>" )
+ .data( "item.autocomplete", item )
+ .append( $( "<a></a>" ).text( item.label ) )
+ .appendTo( ul );
+ },
+
+ _move: function( direction, event ) {
+ if ( !this.menu.element.is(":visible") ) {
+ this.search( null, event );
+ return;
+ }
+ if ( this.menu.first() && /^previous/.test(direction) ||
+ this.menu.last() && /^next/.test(direction) ) {
+ this.element.val( this.term );
+ this.menu.deactivate();
+ return;
+ }
+ this.menu[ direction ]( event );
+ },
+
+ widget: function() {
+ return this.menu.element;
+ }
+});
+
+$.extend( $.ui.autocomplete, {
+ escapeRegex: function( value ) {
+ return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ },
+ filter: function(array, term) {
+ var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
+ return $.grep( array, function(value) {
+ return matcher.test( value.label || value.value || value );
+ });
+ }
+});
+
+}( jQuery ));
+
+/*
+ * jQuery UI Menu (not officially released)
+ *
+ * This widget isn't yet finished and the API is subject to change. We plan to finish
+ * it for the next release. You're welcome to give it a try anyway and give us feedback,
+ * as long as you're okay with migrating your code later on. We can help with that, too.
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.menu", {
+ _create: function() {
+ var self = this;
+ this.element
+ .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
+ .attr({
+ role: "listbox",
+ "aria-activedescendant": "ui-active-menuitem"
+ })
+ .click(function( event ) {
+ if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
+ return;
+ }
+ // temporary
+ event.preventDefault();
+ self.select( event );
+ });
+ this.refresh();
+ },
+
+ refresh: function() {
+ var self = this;
+
+ // don't refresh list items that are already adapted
+ var items = this.element.children("li:not(.ui-menu-item):has(a)")
+ .addClass("ui-menu-item")
+ .attr("role", "menuitem");
+
+ items.children("a")
+ .addClass("ui-corner-all")
+ .attr("tabindex", -1)
+ // mouseenter doesn't work with event delegation
+ .mouseenter(function( event ) {
+ self.activate( event, $(this).parent() );
+ })
+ .mouseleave(function() {
+ self.deactivate();
+ });
+ },
+
+ activate: function( event, item ) {
+ this.deactivate();
+ if (this.hasScroll()) {
+ var offset = item.offset().top - this.element.offset().top,
+ scroll = this.element.attr("scrollTop"),
+ elementHeight = this.element.height();
+ if (offset < 0) {
+ this.element.attr("scrollTop", scroll + offset);
+ } else if (offset >= elementHeight) {
+ this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
+ }
+ }
+ this.active = item.eq(0)
+ .children("a")
+ .addClass("ui-state-hover")
+ .attr("id", "ui-active-menuitem")
+ .end();
+ this._trigger("focus", event, { item: item });
+ },
+
+ deactivate: function() {
+ if (!this.active) { return; }
+
+ this.active.children("a")
+ .removeClass("ui-state-hover")
+ .removeAttr("id");
+ this._trigger("blur");
+ this.active = null;
+ },
+
+ next: function(event) {
+ this.move("next", ".ui-menu-item:first", event);
+ },
+
+ previous: function(event) {
+ this.move("prev", ".ui-menu-item:last", event);
+ },
+
+ first: function() {
+ return this.active && !this.active.prevAll(".ui-menu-item").length;
+ },
+
+ last: function() {
+ return this.active && !this.active.nextAll(".ui-menu-item").length;
+ },
+
+ move: function(direction, edge, event) {
+ if (!this.active) {
+ this.activate(event, this.element.children(edge));
+ return;
+ }
+ var next = this.active[direction + "All"](".ui-menu-item").eq(0);
+ if (next.length) {
+ this.activate(event, next);
+ } else {
+ this.activate(event, this.element.children(edge));
+ }
+ },
+
+ // TODO merge with previousPage
+ nextPage: function(event) {
+ if (this.hasScroll()) {
+ // TODO merge with no-scroll-else
+ if (!this.active || this.last()) {
+ this.activate(event, this.element.children(".ui-menu-item:first"));
+ return;
+ }
+ var base = this.active.offset().top,
+ height = this.element.height(),
+ result = this.element.children(".ui-menu-item").filter(function() {
+ var close = $(this).offset().top - base - height + $(this).height();
+ // TODO improve approximation
+ return close < 10 && close > -10;
+ });
+
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
+ if (!result.length) {
+ result = this.element.children(".ui-menu-item:last");
+ }
+ this.activate(event, result);
+ } else {
+ this.activate(event, this.element.children(".ui-menu-item")
+ .filter(!this.active || this.last() ? ":first" : ":last"));
+ }
+ },
+
+ // TODO merge with nextPage
+ previousPage: function(event) {
+ if (this.hasScroll()) {
+ // TODO merge with no-scroll-else
+ if (!this.active || this.first()) {
+ this.activate(event, this.element.children(".ui-menu-item:last"));
+ return;
+ }
+
+ var base = this.active.offset().top,
+ height = this.element.height();
+ result = this.element.children(".ui-menu-item").filter(function() {
+ var close = $(this).offset().top - base + height - $(this).height();
+ // TODO improve approximation
+ return close < 10 && close > -10;
+ });
+
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
+ if (!result.length) {
+ result = this.element.children(".ui-menu-item:first");
+ }
+ this.activate(event, result);
+ } else {
+ this.activate(event, this.element.children(".ui-menu-item")
+ .filter(!this.active || this.first() ? ":last" : ":first"));
+ }
+ },
+
+ hasScroll: function() {
+ return this.element.height() < this.element.attr("scrollHeight");
+ },
+
+ select: function( event ) {
+ this._trigger("selected", event, { item: this.active });
+ }
+});
+
+}(jQuery));
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.min.js b/www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.min.js
new file mode 100644
index 0000000..9331247
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.autocomplete.min.js
@@ -0,0 +1,32 @@
+/*
+ * jQuery UI Autocomplete 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */
+(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g=
+false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
+a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
+this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
+a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
+d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
+b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
+this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
+this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
+b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
+d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
+"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
+(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
+-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.attr("scrollTop"),c=this.element.height();if(b<0)this.element.attr("scrollTop",g+b);else b>=c&&this.element.attr("scrollTop",g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},
+deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);
+e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,
+g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));
+this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element.attr("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.core.js b/www/js/jquery-ui-1.8.11/jquery.ui.core.js
new file mode 100644
index 0000000..4589a47
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.core.js
@@ -0,0 +1,308 @@
+/*!
+ * jQuery UI 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function( $, undefined ) {
+
+// prevent duplicate loading
+// this is only a problem because we proxy existing functions
+// and we don't want to double proxy them
+$.ui = $.ui || {};
+if ( $.ui.version ) {
+ return;
+}
+
+$.extend( $.ui, {
+ version: "1.8.11",
+
+ keyCode: {
+ ALT: 18,
+ BACKSPACE: 8,
+ CAPS_LOCK: 20,
+ COMMA: 188,
+ COMMAND: 91,
+ COMMAND_LEFT: 91, // COMMAND
+ COMMAND_RIGHT: 93,
+ CONTROL: 17,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ INSERT: 45,
+ LEFT: 37,
+ MENU: 93, // COMMAND_RIGHT
+ NUMPAD_ADD: 107,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+ NUMPAD_ENTER: 108,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_SUBTRACT: 109,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ PERIOD: 190,
+ RIGHT: 39,
+ SHIFT: 16,
+ SPACE: 32,
+ TAB: 9,
+ UP: 38,
+ WINDOWS: 91 // COMMAND
+ }
+});
+
+// plugins
+$.fn.extend({
+ _focus: $.fn.focus,
+ focus: function( delay, fn ) {
+ return typeof delay === "number" ?
+ this.each(function() {
+ var elem = this;
+ setTimeout(function() {
+ $( elem ).focus();
+ if ( fn ) {
+ fn.call( elem );
+ }
+ }, delay );
+ }) :
+ this._focus.apply( this, arguments );
+ },
+
+ scrollParent: function() {
+ var scrollParent;
+ if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+ scrollParent = this.parents().filter(function() {
+ return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ } else {
+ scrollParent = this.parents().filter(function() {
+ return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ }
+
+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+ },
+
+ zIndex: function( zIndex ) {
+ if ( zIndex !== undefined ) {
+ return this.css( "zIndex", zIndex );
+ }
+
+ if ( this.length ) {
+ var elem = $( this[ 0 ] ), position, value;
+ while ( elem.length && elem[ 0 ] !== document ) {
+ // Ignore z-index if position is set to a value where z-index is ignored by the browser
+ // This makes behavior of this function consistent across browsers
+ // WebKit always returns auto if the element is positioned
+ position = elem.css( "position" );
+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
+ // IE returns 0 when zIndex is not specified
+ // other browsers return a string
+ // we ignore the case of nested elements with an explicit value of 0
+ // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
+ value = parseInt( elem.css( "zIndex" ), 10 );
+ if ( !isNaN( value ) && value !== 0 ) {
+ return value;
+ }
+ }
+ elem = elem.parent();
+ }
+ }
+
+ return 0;
+ },
+
+ disableSelection: function() {
+ return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
+ ".ui-disableSelection", function( event ) {
+ event.preventDefault();
+ });
+ },
+
+ enableSelection: function() {
+ return this.unbind( ".ui-disableSelection" );
+ }
+});
+
+$.each( [ "Width", "Height" ], function( i, name ) {
+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
+ type = name.toLowerCase(),
+ orig = {
+ innerWidth: $.fn.innerWidth,
+ innerHeight: $.fn.innerHeight,
+ outerWidth: $.fn.outerWidth,
+ outerHeight: $.fn.outerHeight
+ };
+
+ function reduce( elem, size, border, margin ) {
+ $.each( side, function() {
+ size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
+ if ( border ) {
+ size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
+ }
+ if ( margin ) {
+ size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
+ }
+ });
+ return size;
+ }
+
+ $.fn[ "inner" + name ] = function( size ) {
+ if ( size === undefined ) {
+ return orig[ "inner" + name ].call( this );
+ }
+
+ return this.each(function() {
+ $( this ).css( type, reduce( this, size ) + "px" );
+ });
+ };
+
+ $.fn[ "outer" + name] = function( size, margin ) {
+ if ( typeof size !== "number" ) {
+ return orig[ "outer" + name ].call( this, size );
+ }
+
+ return this.each(function() {
+ $( this).css( type, reduce( this, size, true, margin ) + "px" );
+ });
+ };
+});
+
+// selectors
+function visible( element ) {
+ return !$( element ).parents().andSelf().filter(function() {
+ return $.curCSS( this, "visibility" ) === "hidden" ||
+ $.expr.filters.hidden( this );
+ }).length;
+}
+
+$.extend( $.expr[ ":" ], {
+ data: function( elem, i, match ) {
+ return !!$.data( elem, match[ 3 ] );
+ },
+
+ focusable: function( element ) {
+ var nodeName = element.nodeName.toLowerCase(),
+ tabIndex = $.attr( element, "tabindex" );
+ if ( "area" === nodeName ) {
+ var map = element.parentNode,
+ mapName = map.name,
+ img;
+ if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
+ return false;
+ }
+ img = $( "img[usemap=#" + mapName + "]" )[0];
+ return !!img && visible( img );
+ }
+ return ( /input|select|textarea|button|object/.test( nodeName )
+ ? !element.disabled
+ : "a" == nodeName
+ ? element.href || !isNaN( tabIndex )
+ : !isNaN( tabIndex ))
+ // the element and all of its ancestors must be visible
+ && visible( element );
+ },
+
+ tabbable: function( element ) {
+ var tabIndex = $.attr( element, "tabindex" );
+ return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" );
+ }
+});
+
+// support
+$(function() {
+ var body = document.body,
+ div = body.appendChild( div = document.createElement( "div" ) );
+
+ $.extend( div.style, {
+ minHeight: "100px",
+ height: "auto",
+ padding: 0,
+ borderWidth: 0
+ });
+
+ $.support.minHeight = div.offsetHeight === 100;
+ $.support.selectstart = "onselectstart" in div;
+
+ // set display to none to avoid a layout bug in IE
+ // http://dev.jquery.com/ticket/4014
+ body.removeChild( div ).style.display = "none";
+});
+
+
+
+
+
+// deprecated
+$.extend( $.ui, {
+ // $.ui.plugin is deprecated. Use the proxy pattern instead.
+ plugin: {
+ add: function( module, option, set ) {
+ var proto = $.ui[ module ].prototype;
+ for ( var i in set ) {
+ proto.plugins[ i ] = proto.plugins[ i ] || [];
+ proto.plugins[ i ].push( [ option, set[ i ] ] );
+ }
+ },
+ call: function( instance, name, args ) {
+ var set = instance.plugins[ name ];
+ if ( !set || !instance.element[ 0 ].parentNode ) {
+ return;
+ }
+
+ for ( var i = 0; i < set.length; i++ ) {
+ if ( instance.options[ set[ i ][ 0 ] ] ) {
+ set[ i ][ 1 ].apply( instance.element, args );
+ }
+ }
+ }
+ },
+
+ // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
+ contains: function( a, b ) {
+ return document.compareDocumentPosition ?
+ a.compareDocumentPosition( b ) & 16 :
+ a !== b && a.contains( b );
+ },
+
+ // only used by resizable
+ hasScroll: function( el, a ) {
+
+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
+ if ( $( el ).css( "overflow" ) === "hidden") {
+ return false;
+ }
+
+ var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
+ has = false;
+
+ if ( el[ scroll ] > 0 ) {
+ return true;
+ }
+
+ // TODO: determine which cases actually cause this to happen
+ // if the element doesn't have the scroll set, see if it's possible to
+ // set the scroll
+ el[ scroll ] = 1;
+ has = ( el[ scroll ] > 0 );
+ el[ scroll ] = 0;
+ return has;
+ },
+
+ // these are odd functions, fix the API or move into individual plugins
+ isOverAxis: function( x, reference, size ) {
+ //Determines when x coordinate is over "b" element axis
+ return ( x > reference ) && ( x < ( reference + size ) );
+ },
+ isOver: function( y, x, top, left, height, width ) {
+ //Determines when x, y coordinates is over "b" element
+ return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
+ }
+});
+
+})( jQuery );
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.core.min.js b/www/js/jquery-ui-1.8.11/jquery.ui.core.min.js
new file mode 100644
index 0000000..272fe36
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.core.min.js
@@ -0,0 +1,17 @@
+/*!
+ * jQuery UI 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.11",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,
+NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,
+"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");
+if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,
+"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,
+d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}});
+c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&
+b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.position.js b/www/js/jquery-ui-1.8.11/jquery.ui.position.js
new file mode 100644
index 0000000..b66e59e
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.position.js
@@ -0,0 +1,252 @@
+/*
+ * jQuery UI Position 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function( $, undefined ) {
+
+$.ui = $.ui || {};
+
+var horizontalPositions = /left|center|right/,
+ verticalPositions = /top|center|bottom/,
+ center = "center",
+ _position = $.fn.position,
+ _offset = $.fn.offset;
+
+$.fn.position = function( options ) {
+ if ( !options || !options.of ) {
+ return _position.apply( this, arguments );
+ }
+
+ // make a copy, we don't want to modify arguments
+ options = $.extend( {}, options );
+
+ var target = $( options.of ),
+ targetElem = target[0],
+ collision = ( options.collision || "flip" ).split( " " ),
+ offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
+ targetWidth,
+ targetHeight,
+ basePosition;
+
+ if ( targetElem.nodeType === 9 ) {
+ targetWidth = target.width();
+ targetHeight = target.height();
+ basePosition = { top: 0, left: 0 };
+ // TODO: use $.isWindow() in 1.9
+ } else if ( targetElem.setTimeout ) {
+ targetWidth = target.width();
+ targetHeight = target.height();
+ basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+ } else if ( targetElem.preventDefault ) {
+ // force left top to allow flipping
+ options.at = "left top";
+ targetWidth = targetHeight = 0;
+ basePosition = { top: options.of.pageY, left: options.of.pageX };
+ } else {
+ targetWidth = target.outerWidth();
+ targetHeight = target.outerHeight();
+ basePosition = target.offset();
+ }
+
+ // force my and at to have valid horizontal and veritcal positions
+ // if a value is missing or invalid, it will be converted to center
+ $.each( [ "my", "at" ], function() {
+ var pos = ( options[this] || "" ).split( " " );
+ if ( pos.length === 1) {
+ pos = horizontalPositions.test( pos[0] ) ?
+ pos.concat( [center] ) :
+ verticalPositions.test( pos[0] ) ?
+ [ center ].concat( pos ) :
+ [ center, center ];
+ }
+ pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
+ pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
+ options[ this ] = pos;
+ });
+
+ // normalize collision option
+ if ( collision.length === 1 ) {
+ collision[ 1 ] = collision[ 0 ];
+ }
+
+ // normalize offset option
+ offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
+ if ( offset.length === 1 ) {
+ offset[ 1 ] = offset[ 0 ];
+ }
+ offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
+
+ if ( options.at[0] === "right" ) {
+ basePosition.left += targetWidth;
+ } else if ( options.at[0] === center ) {
+ basePosition.left += targetWidth / 2;
+ }
+
+ if ( options.at[1] === "bottom" ) {
+ basePosition.top += targetHeight;
+ } else if ( options.at[1] === center ) {
+ basePosition.top += targetHeight / 2;
+ }
+
+ basePosition.left += offset[ 0 ];
+ basePosition.top += offset[ 1 ];
+
+ return this.each(function() {
+ var elem = $( this ),
+ elemWidth = elem.outerWidth(),
+ elemHeight = elem.outerHeight(),
+ marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
+ marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
+ collisionWidth = elemWidth + marginLeft +
+ ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
+ collisionHeight = elemHeight + marginTop +
+ ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
+ position = $.extend( {}, basePosition ),
+ collisionPosition;
+
+ if ( options.my[0] === "right" ) {
+ position.left -= elemWidth;
+ } else if ( options.my[0] === center ) {
+ position.left -= elemWidth / 2;
+ }
+
+ if ( options.my[1] === "bottom" ) {
+ position.top -= elemHeight;
+ } else if ( options.my[1] === center ) {
+ position.top -= elemHeight / 2;
+ }
+
+ // prevent fractions (see #5280)
+ position.left = Math.round( position.left );
+ position.top = Math.round( position.top );
+
+ collisionPosition = {
+ left: position.left - marginLeft,
+ top: position.top - marginTop
+ };
+
+ $.each( [ "left", "top" ], function( i, dir ) {
+ if ( $.ui.position[ collision[i] ] ) {
+ $.ui.position[ collision[i] ][ dir ]( position, {
+ targetWidth: targetWidth,
+ targetHeight: targetHeight,
+ elemWidth: elemWidth,
+ elemHeight: elemHeight,
+ collisionPosition: collisionPosition,
+ collisionWidth: collisionWidth,
+ collisionHeight: collisionHeight,
+ offset: offset,
+ my: options.my,
+ at: options.at
+ });
+ }
+ });
+
+ if ( $.fn.bgiframe ) {
+ elem.bgiframe();
+ }
+ elem.offset( $.extend( position, { using: options.using } ) );
+ });
+};
+
+$.ui.position = {
+ fit: {
+ left: function( position, data ) {
+ var win = $( window ),
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
+ position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
+ },
+ top: function( position, data ) {
+ var win = $( window ),
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
+ position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
+ }
+ },
+
+ flip: {
+ left: function( position, data ) {
+ if ( data.at[0] === center ) {
+ return;
+ }
+ var win = $( window ),
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
+ myOffset = data.my[ 0 ] === "left" ?
+ -data.elemWidth :
+ data.my[ 0 ] === "right" ?
+ data.elemWidth :
+ 0,
+ atOffset = data.at[ 0 ] === "left" ?
+ data.targetWidth :
+ -data.targetWidth,
+ offset = -2 * data.offset[ 0 ];
+ position.left += data.collisionPosition.left < 0 ?
+ myOffset + atOffset + offset :
+ over > 0 ?
+ myOffset + atOffset + offset :
+ 0;
+ },
+ top: function( position, data ) {
+ if ( data.at[1] === center ) {
+ return;
+ }
+ var win = $( window ),
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
+ myOffset = data.my[ 1 ] === "top" ?
+ -data.elemHeight :
+ data.my[ 1 ] === "bottom" ?
+ data.elemHeight :
+ 0,
+ atOffset = data.at[ 1 ] === "top" ?
+ data.targetHeight :
+ -data.targetHeight,
+ offset = -2 * data.offset[ 1 ];
+ position.top += data.collisionPosition.top < 0 ?
+ myOffset + atOffset + offset :
+ over > 0 ?
+ myOffset + atOffset + offset :
+ 0;
+ }
+ }
+};
+
+// offset setter from jQuery 1.4
+if ( !$.offset.setOffset ) {
+ $.offset.setOffset = function( elem, options ) {
+ // set position first, in-case top/left are set even on static elem
+ if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
+ elem.style.position = "relative";
+ }
+ var curElem = $( elem ),
+ curOffset = curElem.offset(),
+ curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
+ curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
+ props = {
+ top: (options.top - curOffset.top) + curTop,
+ left: (options.left - curOffset.left) + curLeft
+ };
+
+ if ( 'using' in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ };
+
+ $.fn.offset = function( options ) {
+ var elem = this[ 0 ];
+ if ( !elem || !elem.ownerDocument ) { return null; }
+ if ( options ) {
+ return this.each(function() {
+ $.offset.setOffset( this, options );
+ });
+ }
+ return _offset.call( this );
+ };
+}
+
+}( jQuery ));
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.position.min.js b/www/js/jquery-ui-1.8.11/jquery.ui.position.min.js
new file mode 100644
index 0000000..8a5de73
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.position.min.js
@@ -0,0 +1,16 @@
+/*
+ * jQuery UI Position 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
+left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
+k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
+m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
+d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
+a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
+g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.widget.js b/www/js/jquery-ui-1.8.11/jquery.ui.widget.js
new file mode 100644
index 0000000..b6b1bee
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.widget.js
@@ -0,0 +1,262 @@
+/*!
+ * jQuery UI Widget 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ $( elem ).triggerHandler( "remove" );
+ }
+ _cleanData( elems );
+ };
+} else {
+ var _remove = $.fn.remove;
+ $.fn.remove = function( selector, keepData ) {
+ return this.each(function() {
+ if ( !keepData ) {
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
+ $( "*", this ).add( [ this ] ).each(function() {
+ $( this ).triggerHandler( "remove" );
+ });
+ }
+ }
+ return _remove.call( $(this), selector, keepData );
+ });
+ };
+}
+
+$.widget = function( name, base, prototype ) {
+ var namespace = name.split( "." )[ 0 ],
+ fullName;
+ name = name.split( "." )[ 1 ];
+ fullName = namespace + "-" + name;
+
+ if ( !prototype ) {
+ prototype = base;
+ base = $.Widget;
+ }
+
+ // create selector for plugin
+ $.expr[ ":" ][ fullName ] = function( elem ) {
+ return !!$.data( elem, name );
+ };
+
+ $[ namespace ] = $[ namespace ] || {};
+ $[ namespace ][ name ] = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+ };
+
+ var basePrototype = new base();
+ // we need to make the options hash a property directly on the new instance
+ // otherwise we'll modify the options hash on the prototype that we're
+ // inheriting from
+// $.each( basePrototype, function( key, val ) {
+// if ( $.isPlainObject(val) ) {
+// basePrototype[ key ] = $.extend( {}, val );
+// }
+// });
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+ namespace: namespace,
+ widgetName: name,
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+ widgetBaseClass: fullName
+ }, prototype );
+
+ $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+ $.fn[ name ] = function( options ) {
+ var isMethodCall = typeof options === "string",
+ args = Array.prototype.slice.call( arguments, 1 ),
+ returnValue = this;
+
+ // allow multiple hashes to be passed on init
+ options = !isMethodCall && args.length ?
+ $.extend.apply( null, [ true, options ].concat(args) ) :
+ options;
+
+ // prevent calls to internal methods
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+ return returnValue;
+ }
+
+ if ( isMethodCall ) {
+ this.each(function() {
+ var instance = $.data( this, name ),
+ methodValue = instance && $.isFunction( instance[options] ) ?
+ instance[ options ].apply( instance, args ) :
+ instance;
+ // TODO: add this back in 1.9 and use $.error() (see #5972)
+// if ( !instance ) {
+// throw "cannot call methods on " + name + " prior to initialization; " +
+// "attempted to call method '" + options + "'";
+// }
+// if ( !$.isFunction( instance[options] ) ) {
+// throw "no such method '" + options + "' for " + name + " widget instance";
+// }
+// var methodValue = instance[ options ].apply( instance, args );
+ if ( methodValue !== instance && methodValue !== undefined ) {
+ returnValue = methodValue;
+ return false;
+ }
+ });
+ } else {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( instance ) {
+ instance.option( options || {} )._init();
+ } else {
+ $.data( this, name, new object( options, this ) );
+ }
+ });
+ }
+
+ return returnValue;
+ };
+};
+
+$.Widget = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+};
+
+$.Widget.prototype = {
+ widgetName: "widget",
+ widgetEventPrefix: "",
+ options: {
+ disabled: false
+ },
+ _createWidget: function( options, element ) {
+ // $.widget.bridge stores the plugin instance, but we do it anyway
+ // so that it's stored even before the _create function runs
+ $.data( element, this.widgetName, this );
+ this.element = $( element );
+ this.options = $.extend( true, {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ var self = this;
+ this.element.bind( "remove." + this.widgetName, function() {
+ self.destroy();
+ });
+
+ this._create();
+ this._trigger( "create" );
+ this._init();
+ },
+ _getCreateOptions: function() {
+ return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
+ },
+ _create: function() {},
+ _init: function() {},
+
+ destroy: function() {
+ this.element
+ .unbind( "." + this.widgetName )
+ .removeData( this.widgetName );
+ this.widget()
+ .unbind( "." + this.widgetName )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetBaseClass + "-disabled " +
+ "ui-state-disabled" );
+ },
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.extend( {}, this.options );
+ }
+
+ if (typeof key === "string" ) {
+ if ( value === undefined ) {
+ return this.options[ key ];
+ }
+ options = {};
+ options[ key ] = value;
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var self = this;
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+ });
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ [ value ? "addClass" : "removeClass"](
+ this.widgetBaseClass + "-disabled" + " " +
+ "ui-state-disabled" )
+ .attr( "aria-disabled", value );
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOption( "disabled", false );
+ },
+ disable: function() {
+ return this._setOption( "disabled", true );
+ },
+
+ _trigger: function( type, event, data ) {
+ var callback = this.options[ type ];
+
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ data = data || {};
+
+ // copy original event properties over to the new event
+ // this would happen if we could call $.event.fix instead of $.Event
+ // but we don't have a way to force an event to be fixed multiple times
+ if ( event.originalEvent ) {
+ for ( var i = $.event.props.length, prop; i; ) {
+ prop = $.event.props[ --i ];
+ event[ prop ] = event.originalEvent[ prop ];
+ }
+ }
+
+ this.element.trigger( event, data );
+
+ return !( $.isFunction(callback) &&
+ callback.call( this.element[0], event, data ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+})( jQuery );
diff --git a/www/js/jquery-ui-1.8.11/jquery.ui.widget.min.js b/www/js/jquery-ui-1.8.11/jquery.ui.widget.min.js
new file mode 100644
index 0000000..01cb788
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/jquery.ui.widget.min.js
@@ -0,0 +1,15 @@
+/*!
+ * jQuery UI Widget 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
+a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
+e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
+this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
+widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
+enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png
new file mode 100644
index 0000000..5b5dab2
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_75_ffffff_40x100.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_75_ffffff_40x100.png
new file mode 100644
index 0000000..ac8b229
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_flat_75_ffffff_40x100.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png
new file mode 100644
index 0000000..ad3d634
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_65_ffffff_1x400.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 0000000..42ccba2
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_dadada_1x400.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_dadada_1x400.png
new file mode 100644
index 0000000..5a46b47
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_dadada_1x400.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png
new file mode 100644
index 0000000..86c2baa
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 0000000..4443fdc
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
new file mode 100644
index 0000000..7c9fa6c
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_222222_256x240.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_222222_256x240.png
new file mode 100644
index 0000000..ee039dc
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_2e83ff_256x240.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 0000000..45e8928
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_454545_256x240.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_454545_256x240.png
new file mode 100644
index 0000000..7ec70d1
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_454545_256x240.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_888888_256x240.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_888888_256x240.png
new file mode 100644
index 0000000..5ba708c
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_888888_256x240.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_cd0a0a_256x240.png b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 0000000..7930a55
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.all.css b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.all.css
new file mode 100644
index 0000000..0302dfb
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.all.css
@@ -0,0 +1,11 @@
+/*
+ * jQuery UI CSS Framework @VERSION
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming
+ */
+@import "jquery.ui.base.css";
+@import "jquery.ui.theme.css";
diff --git a/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.autocomplete.css b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.autocomplete.css
new file mode 100644
index 0000000..83b7b9d
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.autocomplete.css
@@ -0,0 +1,53 @@
+/*
+ * jQuery UI Autocomplete 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.11
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
diff --git a/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.base.css b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.base.css
new file mode 100644
index 0000000..98267a4
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.base.css
@@ -0,0 +1,2 @@
+@import url("jquery.ui.core.css");
+@import url("jquery.ui.autocomplete.css");
diff --git a/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.core.css b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.core.css
new file mode 100644
index 0000000..ef27f47
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.core.css
@@ -0,0 +1,41 @@
+/*
+ * jQuery UI CSS Framework 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
diff --git a/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.theme.css b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.theme.css
new file mode 100644
index 0000000..30690c5
--- /dev/null
+++ b/www/js/jquery-ui-1.8.11/themes/base/jquery.ui.theme.css
@@ -0,0 +1,252 @@
+/*
+ * jQuery UI CSS Framework 1.8.11
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; }
+.ui-widget-content a { color: #222222/*{fcContent}*/; }
+.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; }
+.ui-widget-header a { color: #222222/*{fcHeader}*/; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-right { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; }
+.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/jquery-1.4.2.js b/www/js/jstree-1.0-rc2/jquery-1.4.2.js
new file mode 100644
index 0000000..fff6776
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/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 = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ 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 = "<input type='radio' name='radiotest' checked='checked'/>";
+
+ 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 = "<a name='" + id + "'/>";
+
+ // 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 = "<a href='#'></a>";
+ 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 = "<p class='TEST'></p>";
+
+ // 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 = "<div class='test e'></div><div class='test'></div>";
+
+ // 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 = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnocache = /<script|<object|<embed|<option|<style/i,
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, // checked="checked" or checked (html5)
+ fcloseTag = function( all, front, tag ) {
+ return rselfClosing.test( tag ) ?
+ all :
+ front + "></" + tag + ">";
+ },
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append(this);
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ), contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function() {
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery(arguments[0]);
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery(arguments[0]).toArray() );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function() {
+ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var html = this.outerHTML, ownerDocument = this.ownerDocument;
+ if ( !html ) {
+ var div = ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(rinlinejQuery, "")
+ // Handle the case in IE 8 where action=/test/> self-closes a tag
+ .replace(/=([^="'>\s]+\/)>/g, '="$1">')
+ .replace(rleadingWhitespace, "")], ownerDocument)[0];
+ } else {
+ return this.cloneNode(true);
+ }
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true ) {
+ cloneCopyEvent( this, ret );
+ cloneCopyEvent( this.find("*"), ret.find("*") );
+ }
+
+ // Return the cloned set
+ return ret;
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+ value = value.replace(rxhtmlTag, fcloseTag);
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery(this), old = self.html();
+ self.empty().append(function(){
+ return value.call( this, i, old );
+ });
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery(value).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling, parent = this.parentNode;
+
+ jQuery(this).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, value = args[0], scripts = [], fragment, parent;
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ i > 0 || results.cacheable || this.length > 1 ?
+ fragment.cloneNode(true) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+
+ function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+ }
+ }
+});
+
+function cloneCopyEvent(orig, ret) {
+ var i = 0;
+
+ ret.each(function() {
+ if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+ return;
+ }
+
+ var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+ }
+ });
+}
+
+function buildFragment( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults,
+ doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+ // Only cache "small" (1/2 KB) strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+ !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+ cacheable = true;
+ cacheresults = jQuery.fragments[ args[0] ];
+ if ( cacheresults ) {
+ if ( cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+}
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+jQuery.extend({
+ clean: function( elems, context, fragment, scripts ) {
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ var ret = [];
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" && !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+
+ } else if ( typeof elem === "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, fcloseTag);
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ for ( var i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id, cache = jQuery.cache,
+ special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ];
+
+ if ( data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ } else {
+ removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+// exclude the following css properties to add px
+var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ ralpha = /alpha\([^)]*\)/,
+ ropacity = /opacity=([^)]*)/,
+ rfloat = /float/i,
+ rdashAlpha = /-([a-z])/ig,
+ rupper = /([A-Z])/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+
+ cssShow = { position: "absolute", visibility: "hidden", display:"block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+
+ // cache check for defaultView.getComputedStyle
+ getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+ // normalize float css property
+ styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn.css = function( name, value ) {
+ return access( this, name, value, true, function( elem, name, value ) {
+ if ( value === undefined ) {
+ return jQuery.curCSS( elem, name );
+ }
+
+ if ( typeof value === "number" && !rexclude.test(name) ) {
+ value += "px";
+ }
+
+ jQuery.style( elem, name, value );
+ });
+};
+
+jQuery.extend({
+ style: function( elem, name, value ) {
+ // don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ // ignore negative width and height values #1599
+ if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
+ value = undefined;
+ }
+
+ var style = elem.style || elem, set = value !== undefined;
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name === "opacity" ) {
+ if ( set ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
+ var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
+ style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
+ }
+
+ return style.filter && style.filter.indexOf("opacity=") >= 0 ?
+ (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
+ "";
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( rfloat.test( name ) ) {
+ name = styleFloat;
+ }
+
+ name = name.replace(rdashAlpha, fcamelCase);
+
+ if ( set ) {
+ style[ name ] = value;
+ }
+
+ return style[ name ];
+ },
+
+ css: function( elem, name, force, extra ) {
+ if ( name === "width" || name === "height" ) {
+ var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
+
+ function getWH() {
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" ) {
+ return;
+ }
+
+ jQuery.each( which, function() {
+ if ( !extra ) {
+ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ }
+
+ if ( extra === "margin" ) {
+ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+ } else {
+ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ }
+ });
+ }
+
+ if ( elem.offsetWidth !== 0 ) {
+ getWH();
+ } else {
+ jQuery.swap( elem, props, getWH );
+ }
+
+ return Math.max(0, Math.round(val));
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style, filter;
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
+ ret = ropacity.test(elem.currentStyle.filter || "") ?
+ (parseFloat(RegExp.$1) / 100) + "" :
+ "";
+
+ return ret === "" ?
+ "1" :
+ ret;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( rfloat.test( name ) ) {
+ name = styleFloat;
+ }
+
+ if ( !force && style && style[ name ] ) {
+ ret = style[ name ];
+
+ } else if ( getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( rfloat.test( name ) ) {
+ name = "float";
+ }
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ var defaultView = elem.ownerDocument.defaultView;
+
+ if ( !defaultView ) {
+ return null;
+ }
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle ) {
+ ret = computedStyle.getPropertyValue( name );
+ }
+
+ // We should always get a number back from opacity
+ if ( name === "opacity" && ret === "" ) {
+ ret = "1";
+ }
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(rdashAlpha, fcamelCase);
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth, height = elem.offsetHeight,
+ skip = elem.nodeName.toLowerCase() === "tr";
+
+ return width === 0 && height === 0 && !skip ?
+ true :
+ width > 0 && height > 0 && !skip ?
+ false :
+ jQuery.curCSS(elem, "display") === "none";
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+var jsc = now(),
+ rscript = /<script(.|\s)*?\/script>/gi,
+ rselectTextarea = /select|textarea/i,
+ rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
+ jsre = /=\?(&|$)/,
+ rquery = /\?/,
+ rts = /(\?|&)_=.*?(&|$)/,
+ rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+ r20 = /%20/g,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load;
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" ) {
+ return _load.call( this, url );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf(" ");
+ if ( off >= 0 ) {
+ var selector = url.slice(off, url.length);
+ url = url.slice(0, off);
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function( res, status ) {
+ // If successful, inject the HTML into all the matched elements
+ if ( status === "success" || status === "notmodified" ) {
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div />")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(res.responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ res.responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [res.responseText, status, res] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param(this.serializeArray());
+ },
+ serializeArray: function() {
+ return this.map(function() {
+ return this.elements ? jQuery.makeArray(this.elements) : this;
+ })
+ .filter(function() {
+ return this.name && !this.disabled &&
+ (this.checked || rselectTextarea.test(this.nodeName) ||
+ rinput.test(this.type));
+ })
+ .map(function( i, elem ) {
+ var val = jQuery(this).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray(val) ?
+ jQuery.map( val, function( val, i ) {
+ return { name: elem.name, value: val };
+ }) :
+ { name: elem.name, value: val };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
+ jQuery.fn[o] = function( f ) {
+ return this.bind(o, f);
+ };
+});
+
+jQuery.extend({
+
+ get: function( url, data, callback, type ) {
+ // shift arguments if data argument was omited
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ type: "GET",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get(url, null, callback, "script");
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get(url, data, callback, "json");
+ },
+
+ post: function( url, data, callback, type ) {
+ // shift arguments if data argument was omited
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = {};
+ }
+
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ url: location.href,
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ username: null,
+ password: null,
+ traditional: false,
+ */
+ // Create the request object; Microsoft failed to properly
+ // implement the XMLHttpRequest in IE7 (can't request local files),
+ // so we use the ActiveXObject when it is available
+ // This function can be overriden by calling jQuery.ajaxSetup
+ xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
+ function() {
+ return new window.XMLHttpRequest();
+ } :
+ function() {
+ try {
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
+ } catch(e) {}
+ },
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ script: "text/javascript, application/javascript",
+ json: "application/json, text/javascript",
+ text: "text/plain",
+ _default: "*/*"
+ }
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {},
+
+ ajax: function( origSettings ) {
+ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
+
+ var jsonp, status, data,
+ callbackContext = origSettings && origSettings.context || s,
+ type = s.type.toUpperCase();
+
+ // convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Handle JSONP Parameter Callbacks
+ if ( s.dataType === "jsonp" ) {
+ if ( type === "GET" ) {
+ if ( !jsre.test( s.url ) ) {
+ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ }
+ } else if ( !s.data || !jsre.test(s.data) ) {
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ }
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data ) {
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ }
+
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+
+ try {
+ delete window[ jsonp ];
+ } catch(e) {}
+
+ if ( head ) {
+ head.removeChild( script );
+ }
+ };
+ }
+
+ if ( s.dataType === "script" && s.cache === null ) {
+ s.cache = false;
+ }
+
+ if ( s.cache === false && type === "GET" ) {
+ var ts = now();
+
+ // try replacing _= if it is there
+ var ret = s.url.replace(rts, "$1_=" + ts + "$2");
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
+ }
+
+ // If data is available, append data to url for get requests
+ if ( s.data && type === "GET" ) {
+ s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && ! jQuery.active++ ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // Matches an absolute URL, and saves the domain
+ var parts = rurl.exec( s.url ),
+ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+ // If we're requesting a remote document
+ // and trying to load JSON or Script with a GET
+ if ( s.dataType === "script" && type === "GET" && remote ) {
+ var head = document.getElementsByTagName("head")[0] || document.documentElement;
+ var script = document.createElement("script");
+ script.src = s.url;
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ // Handle Script loading
+ if ( !jsonp ) {
+ var done = false;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function() {
+ if ( !done && (!this.readyState ||
+ this.readyState === "loaded" || this.readyState === "complete") ) {
+ done = true;
+ success();
+ complete();
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+ }
+ };
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+
+ // We handle everything using the script element injection
+ return undefined;
+ }
+
+ var requestDone = false;
+
+ // Create the request object
+ var xhr = s.xhr();
+
+ if ( !xhr ) {
+ return;
+ }
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open(type, s.url, s.async, s.username, s.password);
+ } else {
+ xhr.open(type, s.url, s.async);
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ // Set the correct header, if data is being sent
+ if ( s.data || origSettings && origSettings.contentType ) {
+ xhr.setRequestHeader("Content-Type", s.contentType);
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ if ( jQuery.lastModified[s.url] ) {
+ xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+ }
+
+ if ( jQuery.etag[s.url] ) {
+ xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+ }
+ }
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ // Only send the header if it's not a remote XHR
+ if ( !remote ) {
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+ s.accepts[ s.dataType ] + ", */*" :
+ s.accepts._default );
+ } catch(e) {}
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+
+ // close opended socket
+ xhr.abort();
+ return false;
+ }
+
+ if ( s.global ) {
+ trigger("ajaxSend", [xhr, s]);
+ }
+
+ // Wait for a response to come back
+ var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
+ // The request was aborted
+ if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
+ // Opera doesn't call onreadystatechange before this point
+ // so we simulate the call
+ if ( !requestDone ) {
+ complete();
+ }
+
+ requestDone = true;
+ if ( xhr ) {
+ xhr.onreadystatechange = jQuery.noop;
+ }
+
+ // The transfer is complete and the data is available, or the request timed out
+ } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
+ requestDone = true;
+ xhr.onreadystatechange = jQuery.noop;
+
+ status = isTimeout === "timeout" ?
+ "timeout" :
+ !jQuery.httpSuccess( xhr ) ?
+ "error" :
+ s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
+ "notmodified" :
+ "success";
+
+ var errMsg;
+
+ if ( status === "success" ) {
+ // Watch for, and catch, XML document parse errors
+ try {
+ // process the data (runs the xml through httpData regardless of callback)
+ data = jQuery.httpData( xhr, s.dataType, s );
+ } catch(err) {
+ status = "parsererror";
+ errMsg = err;
+ }
+ }
+
+ // Make sure that the request was successful or notmodified
+ if ( status === "success" || status === "notmodified" ) {
+ // JSONP handles its own success callback
+ if ( !jsonp ) {
+ success();
+ }
+ } else {
+ jQuery.handleError(s, xhr, status, errMsg);
+ }
+
+ // Fire the complete handlers
+ complete();
+
+ if ( isTimeout === "timeout" ) {
+ xhr.abort();
+ }
+
+ // Stop memory leaks
+ if ( s.async ) {
+ xhr = null;
+ }
+ }
+ };
+
+ // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
+ // Opera doesn't fire onreadystatechange at all on abort
+ try {
+ var oldAbort = xhr.abort;
+ xhr.abort = function() {
+ if ( xhr ) {
+ oldAbort.call( xhr );
+ }
+
+ onreadystatechange( "abort" );
+ };
+ } catch(e) { }
+
+ // Timeout checker
+ if ( s.async && s.timeout > 0 ) {
+ setTimeout(function() {
+ // Check to see if the request is still happening
+ if ( xhr && !requestDone ) {
+ onreadystatechange( "timeout" );
+ }
+ }, s.timeout);
+ }
+
+ // Send the data
+ try {
+ xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
+ } catch(e) {
+ jQuery.handleError(s, xhr, null, e);
+ // Fire the complete handlers
+ complete();
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async ) {
+ onreadystatechange();
+ }
+
+ function success() {
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success ) {
+ s.success.call( callbackContext, data, status, xhr );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ trigger( "ajaxSuccess", [xhr, s] );
+ }
+ }
+
+ function complete() {
+ // Process result
+ if ( s.complete ) {
+ s.complete.call( callbackContext, xhr, status);
+ }
+
+ // The request was completed
+ if ( s.global ) {
+ trigger( "ajaxComplete", [xhr, s] );
+ }
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+
+ function trigger(type, args) {
+ (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+ }
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xhr;
+ },
+
+ handleError: function( s, xhr, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) {
+ s.error.call( s.context || s, xhr, status, e );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
+ }
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( xhr ) {
+ try {
+ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+ return !xhr.status && location.protocol === "file:" ||
+ // Opera returns 0 when status is 304
+ ( xhr.status >= 200 && xhr.status < 300 ) ||
+ xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
+ } catch(e) {}
+
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xhr, url ) {
+ var lastModified = xhr.getResponseHeader("Last-Modified"),
+ etag = xhr.getResponseHeader("Etag");
+
+ if ( lastModified ) {
+ jQuery.lastModified[url] = lastModified;
+ }
+
+ if ( etag ) {
+ jQuery.etag[url] = etag;
+ }
+
+ // Opera returns 0 when status is 304
+ return xhr.status === 304 || xhr.status === 0;
+ },
+
+ httpData: function( xhr, type, s ) {
+ var ct = xhr.getResponseHeader("content-type") || "",
+ xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if ( xml && data.documentElement.nodeName === "parsererror" ) {
+ jQuery.error( "parsererror" );
+ }
+
+ // Allow a pre-filtering function to sanitize the response
+ // s is checked to keep backwards compatibility
+ if ( s && s.dataFilter ) {
+ data = s.dataFilter( data, type );
+ }
+
+ // The filter can actually parse the response
+ if ( typeof data === "string" ) {
+ // Get the JavaScript object, if JSON is used.
+ if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
+ data = jQuery.parseJSON( data );
+
+ // If the type is "script", eval it in global context
+ } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
+ jQuery.globalEval( data );
+ }
+ }
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [];
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray(a) || a.jquery ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[prefix] );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join("&").replace(r20, "+");
+
+ function buildParams( prefix, obj ) {
+ if ( jQuery.isArray(obj) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || /\[\]$/.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj === "object" ) {
+ // Serialize object item.
+ jQuery.each( obj, function( k, v ) {
+ buildParams( prefix + "[" + k + "]", v );
+ });
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+ }
+
+ function add( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction(value) ? value() : value;
+ s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
+ }
+ }
+});
+var elemdisplay = {},
+ rfxtypes = /toggle|show|hide/,
+ rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ];
+
+jQuery.fn.extend({
+ show: function( speed, callback ) {
+ if ( speed || speed === 0) {
+ return this.animate( genFx("show", 3), speed, callback);
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var old = jQuery.data(this[i], "olddisplay");
+
+ this[i].style.display = old || "";
+
+ if ( jQuery.css(this[i], "display") === "none" ) {
+ var nodeName = this[i].nodeName, display;
+
+ if ( elemdisplay[ nodeName ] ) {
+ display = elemdisplay[ nodeName ];
+
+ } else {
+ var elem = jQuery("<" + nodeName + " />").appendTo("body");
+
+ display = elem.css("display");
+
+ if ( display === "none" ) {
+ display = "block";
+ }
+
+ elem.remove();
+
+ elemdisplay[ nodeName ] = display;
+ }
+
+ jQuery.data(this[i], "olddisplay", display);
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var j = 0, k = this.length; j < k; j++ ) {
+ this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, callback);
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var old = jQuery.data(this[i], "olddisplay");
+ if ( !old && old !== "none" ) {
+ jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var j = 0, k = this.length; j < k; j++ ) {
+ this[j].style.display = "none";
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2 ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete );
+ }
+
+ return this[ optall.queue === false ? "each" : "queue" ](function() {
+ var opt = jQuery.extend({}, optall), p,
+ hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
+ self = this;
+
+ for ( p in prop ) {
+ var name = p.replace(rdashAlpha, fcamelCase);
+
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ p = name;
+ }
+
+ if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
+ return opt.complete.call(this);
+ }
+
+ if ( ( p === "height" || p === "width" ) && this.style ) {
+ // Store display property
+ opt.display = jQuery.css(this, "display");
+
+ // Make sure that nothing sneaks out
+ opt.overflow = this.style.overflow;
+ }
+
+ if ( jQuery.isArray( prop[p] ) ) {
+ // Create (if needed) and add to specialEasing
+ (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
+ prop[p] = prop[p][0];
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ opt.curAnim = jQuery.extend({}, prop);
+
+ jQuery.each( prop, function( name, val ) {
+ var e = new jQuery.fx( self, opt, name );
+
+ if ( rfxtypes.test(val) ) {
+ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+
+ } else {
+ var parts = rfxnum.exec(val),
+ start = e.cur(true) || 0;
+
+ if ( parts ) {
+ var end = parseFloat( parts[2] ),
+ unit = parts[3] || "px";
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ self.style[ name ] = (end || 1) + unit;
+ start = ((end || 1) / e.cur(true)) * start;
+ self.style[ name ] = start + unit;
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ });
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function( clearQueue, gotoEnd ) {
+ var timers = jQuery.timers;
+
+ if ( clearQueue ) {
+ this.queue([]);
+ }
+
+ this.each(function() {
+ // go in reverse order so anything added to the queue during the loop is ignored
+ for ( var i = timers.length - 1; i >= 0; i-- ) {
+ if ( timers[i].elem === this ) {
+ if (gotoEnd) {
+ // force the next step to be the last
+ timers[i](true);
+ }
+
+ timers.splice(i, 1);
+ }
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if ( !gotoEnd ) {
+ this.dequeue();
+ }
+
+ return this;
+ }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, callback ) {
+ return this.animate( props, speed, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? speed : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function() {
+ if ( opt.queue !== false ) {
+ jQuery(this).dequeue();
+ }
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ if ( !options.orig ) {
+ options.orig = {};
+ }
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+ // Set display property to block for height/width animations
+ if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
+ this.elem.style.display = "block";
+ }
+ },
+
+ // Get the current size
+ cur: function( force ) {
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+ return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ this.startTime = now();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || "px";
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ var self = this;
+ function t( gotoEnd ) {
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval(jQuery.fx.tick, 13);
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var t = now(), done = true;
+
+ if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ this.options.curAnim[ this.prop ] = true;
+
+ for ( var i in this.options.curAnim ) {
+ if ( this.options.curAnim[i] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ if ( this.options.display != null ) {
+ // Reset the overflow
+ this.elem.style.overflow = this.options.overflow;
+
+ // Reset the display
+ var old = jQuery.data(this.elem, "olddisplay");
+ this.elem.style.display = old ? old : this.options.display;
+
+ if ( jQuery.css(this.elem, "display") === "none" ) {
+ this.elem.style.display = "block";
+ }
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( this.options.hide ) {
+ jQuery(this.elem).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( this.options.hide || this.options.show ) {
+ for ( var p in this.options.curAnim ) {
+ jQuery.style(this.elem, p, this.options.orig[p]);
+ }
+ }
+
+ // Execute the complete function
+ this.options.complete.call( this.elem );
+ }
+
+ return false;
+
+ } else {
+ var n = t - this.startTime;
+ this.state = n / this.options.duration;
+
+ // Perform the easing function, defaults to swing
+ var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
+ var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
+ this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
+ this.now = this.start + ((this.end - this.start) * this.pos);
+
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ var timers = jQuery.timers;
+
+ for ( var i = 0; i < timers.length; i++ ) {
+ if ( !timers[i]() ) {
+ timers.splice(i--, 1);
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style(fx.elem, "opacity", fx.now);
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
+ clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
+ left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ jQuery.offset.initialize();
+
+ var offsetParent = elem.offsetParent, prevOffsetParent = elem,
+ doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+ body = doc.body, defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop, left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+ initialize: function() {
+ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
+ html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+ container.innerHTML = html;
+ body.insertBefore( container, body.firstChild );
+ innerDiv = container.firstChild;
+ checkDiv = innerDiv.firstChild;
+ td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
+ // safari subtracts parent border width here which is 5px
+ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+ checkDiv.style.position = checkDiv.style.top = "";
+
+ innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+ body.removeChild( container );
+ body = container = innerDiv = checkDiv = table = td = null;
+ jQuery.offset.initialize = jQuery.noop;
+ },
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop, left = body.offsetLeft;
+
+ jQuery.offset.initialize();
+
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0;
+ left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ // set position first, in-case top/left are set even on static elem
+ if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
+ elem.style.position = "relative";
+ }
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curTop = parseInt( jQuery.curCSS( elem, "top", true ), 10 ) || 0,
+ curLeft = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ var props = {
+ top: (options.top - curOffset.top) + curTop,
+ left: (options.left - curOffset.left) + curLeft
+ };
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.curCSS(elem, "marginTop", true) ) || 0;
+ offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0;
+ parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function(val) {
+ var elem = this[0], win;
+
+ if ( !elem ) {
+ return null;
+ }
+
+ if ( val !== undefined ) {
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery(win).scrollLeft(),
+ i ? val : jQuery(win).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ } else {
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+ };
+});
+
+function getWindow( elem ) {
+ return ("scrollTo" in elem && elem.document) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function() {
+ return this[0] ?
+ jQuery.css( this[0], type, false, "padding" ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function( margin ) {
+ return this[0] ?
+ jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
+ elem.document.body[ "client" + name ] :
+
+ // Get document width or height
+ (elem.nodeType === 9) ? // is it a document
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" + name],
+ elem.body["offset" + name], elem.documentElement["offset" + name]
+ ) :
+
+ // Get or set width or height on the element
+ size === undefined ?
+ // Get width or height on the element
+ jQuery.css( elem, type ) :
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ this.css( type, typeof size === "string" ? size : size + "px" );
+ };
+
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+})(window);
diff --git a/www/js/jstree-1.0-rc2/jquery-1.4.2.min.js b/www/js/jstree-1.0-rc2/jquery-1.4.2.min.js
new file mode 100644
index 0000000..7c24308
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/jquery-1.4.2.min.js
@@ -0,0 +1,154 @@
+/*!
+ * 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(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.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(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.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(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.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(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/www/js/jstree-1.0-rc2/jquery.jstree.js b/www/js/jstree-1.0-rc2/jquery.jstree.js
new file mode 100644
index 0000000..bbfc355
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/jquery.jstree.js
@@ -0,0 +1,3510 @@
+/*
+ * jsTree 1.0-rc1
+ * http://jstree.com/
+ *
+ * Copyright (c) 2010 Ivan Bozhanov (vakata.com)
+ *
+ * Dual licensed under the MIT and GPL licenses (same as jQuery):
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * $Date: 2010-07-01 10:51:11 +0300 (четв, 01 юли 2010) $
+ * $Revision: 191 $
+ */
+
+/*jslint browser: true, onevar: true, undef: true, bitwise: true, strict: true */
+/*global window : false, clearInterval: false, clearTimeout: false, document: false, setInterval: false, setTimeout: false, jQuery: false, navigator: false, XSLTProcessor: false, DOMParser: false, XMLSerializer: false*/
+
+"use strict";
+// Common functions not related to jsTree
+// decided to move them to a `vakata` "namespace"
+(function ($) {
+ $.vakata = {};
+ // CSS related functions
+ $.vakata.css = {
+ get_css : function(rule_name, delete_flag, sheet) {
+ rule_name = rule_name.toLowerCase();
+ var css_rules = sheet.cssRules || sheet.rules,
+ j = 0;
+ do {
+ if(css_rules.length && j > css_rules.length + 5) { return false; }
+ if(css_rules[j].selectorText && css_rules[j].selectorText.toLowerCase() == rule_name) {
+ if(delete_flag === true) {
+ if(sheet.removeRule) { sheet.removeRule(j); }
+ if(sheet.deleteRule) { sheet.deleteRule(j); }
+ return true;
+ }
+ else { return css_rules[j]; }
+ }
+ }
+ while (css_rules[++j]);
+ return false;
+ },
+ add_css : function(rule_name, sheet) {
+ if($.jstree.css.get_css(rule_name, false, sheet)) { return false; }
+ if(sheet.insertRule) { sheet.insertRule(rule_name + ' { }', 0); } else { sheet.addRule(rule_name, null, 0); }
+ return $.vakata.css.get_css(rule_name);
+ },
+ remove_css : function(rule_name, sheet) {
+ return $.vakata.css.get_css(rule_name, true, sheet);
+ },
+ add_sheet : function(opts) {
+ var tmp;
+ if(opts.str) {
+ tmp = document.createElement("style");
+ tmp.setAttribute('type',"text/css");
+ if(tmp.styleSheet) {
+ document.getElementsByTagName("head")[0].appendChild(tmp);
+ tmp.styleSheet.cssText = opts.str;
+ }
+ else {
+ tmp.appendChild(document.createTextNode(opts.str));
+ document.getElementsByTagName("head")[0].appendChild(tmp);
+ }
+ return tmp.sheet || tmp.styleSheet;
+ }
+ if(opts.url) {
+ if(document.createStyleSheet) {
+ try { tmp = document.createStyleSheet(opts.url); } catch (e) { }
+ }
+ else {
+ tmp = document.createElement('link');
+ tmp.rel = 'stylesheet';
+ tmp.type = 'text/css';
+ tmp.media = "all";
+ tmp.href = opts.url;
+ document.getElementsByTagName("head")[0].appendChild(tmp);
+ return tmp.styleSheet;
+ }
+ }
+ }
+ };
+})(jQuery);
+
+/*
+ * jsTree core 1.0
+ */
+(function ($) {
+ // private variables
+ var instances = [], // instance array (used by $.jstree.reference/create/focused)
+ focused_instance = -1, // the index in the instance array of the currently focused instance
+ plugins = {}, // list of included plugins
+ prepared_move = {}, // for the move plugin
+ is_ie6 = false;
+
+ // jQuery plugin wrapper (thanks to jquery UI widget function)
+ $.fn.jstree = function (settings) {
+ var isMethodCall = (typeof settings == 'string'), // is this a method call like $().jstree("open_node")
+ args = Array.prototype.slice.call(arguments, 1),
+ returnValue = this;
+
+ // extend settings and allow for multiple hashes and metadata
+ if(!isMethodCall && $.meta) { args.push($.metadata.get(this).jstree); }
+ settings = !isMethodCall && args.length ? $.extend.apply(null, [true, settings].concat(args)) : settings;
+ // block calls to "private" methods
+ if(isMethodCall && settings.substring(0, 1) == '_') { return returnValue; }
+
+ // if a method call execute the method on all selected instances
+ if(isMethodCall) {
+ this.each(function() {
+ var instance = instances[$.data(this, "jstree-instance-id")],
+ methodValue = (instance && $.isFunction(instance[settings])) ? instance[settings].apply(instance, args) : instance;
+ if(typeof methodValue !== "undefined" && (settings.indexOf("is_" === 0) || (methodValue !== true && methodValue !== false))) { returnValue = methodValue; return false; }
+ });
+ }
+ else {
+ this.each(function() {
+ var instance_id = $.data(this, "jstree-instance-id"),
+ s = false;
+ // if an instance already exists, destroy it first
+ if(typeof instance_id !== "undefined" && instances[instance_id]) { instances[instance_id].destroy(); }
+ // push a new empty object to the instances array
+ instance_id = parseInt(instances.push({}),10) - 1;
+ // store the jstree instance id to the container element
+ $.data(this, "jstree-instance-id", instance_id);
+ // clean up all plugins
+ if(!settings) { settings = {}; }
+ settings.plugins = $.isArray(settings.plugins) ? settings.plugins : $.jstree.defaults.plugins;
+ if($.inArray("core", settings.plugins) === -1) { settings.plugins.unshift("core"); }
+
+ // only unique plugins (NOT WORKING)
+ // settings.plugins = settings.plugins.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",");
+
+ // extend defaults with passed data
+ s = $.extend(true, {}, $.jstree.defaults, settings);
+ s.plugins = settings.plugins;
+ $.each(plugins, function (i, val) { if($.inArray(i, s.plugins) === -1) { s[i] = null; delete s[i]; } });
+ // push the new object to the instances array (at the same time set the default classes to the container) and init
+ instances[instance_id] = new $.jstree._instance(instance_id, $(this).addClass("jstree jstree-" + instance_id), s);
+ // init all activated plugins for this instance
+ $.each(instances[instance_id]._get_settings().plugins, function (i, val) { instances[instance_id].data[val] = {}; });
+ $.each(instances[instance_id]._get_settings().plugins, function (i, val) { if(plugins[val]) { plugins[val].__init.apply(instances[instance_id]); } });
+ // initialize the instance
+ instances[instance_id].init();
+ });
+ }
+ // return the jquery selection (or if it was a method call that returned a value - the returned value)
+ return returnValue;
+ };
+ // object to store exposed functions and objects
+ $.jstree = {
+ defaults : {
+ plugins : []
+ },
+ _focused : function () { return instances[focused_instance] || null; },
+ _reference : function (needle) {
+ // get by instance id
+ if(instances[needle]) { return instances[needle]; }
+ // get by DOM (if still no luck - return null
+ var o = $(needle);
+ if(!o.length && typeof needle === "string") { o = $("#" + needle); }
+ if(!o.length) { return null; }
+ return instances[o.closest(".jstree").data("jstree-instance-id")] || null;
+ },
+ _instance : function (index, container, settings) {
+ // for plugins to store data in
+ this.data = { core : {} };
+ this.get_settings = function () { return $.extend(true, {}, settings); };
+ this._get_settings = function () { return settings; };
+ this.get_index = function () { return index; };
+ this.get_container = function () { return container; };
+ this._set_settings = function (s) {
+ settings = $.extend(true, {}, settings, s);
+ };
+ },
+ _fn : { },
+ plugin : function (pname, pdata) {
+ pdata = $.extend({}, {
+ __init : $.noop,
+ __destroy : $.noop,
+ _fn : {},
+ defaults : false
+ }, pdata);
+ plugins[pname] = pdata;
+
+ $.jstree.defaults[pname] = pdata.defaults;
+ $.each(pdata._fn, function (i, val) {
+ val.plugin = pname;
+ val.old = $.jstree._fn[i];
+ $.jstree._fn[i] = function () {
+ var rslt,
+ func = val,
+ args = Array.prototype.slice.call(arguments),
+ evnt = new $.Event("before.jstree"),
+ rlbk = false;
+
+ // Check if function belongs to the included plugins of this instance
+ do {
+ if(func && func.plugin && $.inArray(func.plugin, this._get_settings().plugins) !== -1) { break; }
+ func = func.old;
+ } while(func);
+ if(!func) { return; }
+
+ // a chance to stop execution (or change arguments):
+ // * just bind to jstree.before
+ // * check the additional data object (func property)
+ // * call event.stopImmediatePropagation()
+ // * return false (or an array of arguments)
+ rslt = this.get_container().triggerHandler(evnt, { "func" : i, "inst" : this, "args" : args });
+ if(rslt === false) { return; }
+ if(typeof rslt !== "undefined") { args = rslt; }
+
+ // context and function to trigger events, then finally call the function
+ if(i.indexOf("_") === 0) {
+ rslt = func.apply(this, args);
+ }
+ else {
+ rslt = func.apply(
+ $.extend({}, this, {
+ __callback : function (data) {
+ this.get_container().triggerHandler( i + '.jstree', { "inst" : this, "args" : args, "rslt" : data, "rlbk" : rlbk });
+ },
+ __rollback : function () {
+ rlbk = this.get_rollback();
+ return rlbk;
+ },
+ __call_old : function (replace_arguments) {
+ return func.old.apply(this, (replace_arguments ? Array.prototype.slice.call(arguments, 1) : args ) );
+ }
+ }), args);
+ }
+
+ // return the result
+ return rslt;
+ };
+ $.jstree._fn[i].old = val.old;
+ $.jstree._fn[i].plugin = pname;
+ });
+ },
+ rollback : function (rb) {
+ if(rb) {
+ if(!$.isArray(rb)) { rb = [ rb ]; }
+ $.each(rb, function (i, val) {
+ instances[val.i].set_rollback(val.h, val.d);
+ });
+ }
+ }
+ };
+ // set the prototype for all instances
+ $.jstree._fn = $.jstree._instance.prototype = {};
+
+ // css functions - used internally
+
+ // load the css when DOM is ready
+ $(function() {
+ // code is copied form jQuery ($.browser is deprecated + there is a bug in IE)
+ var u = navigator.userAgent.toLowerCase(),
+ v = (u.match( /.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
+ css_string = '' +
+ '.jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } ' +
+ '.jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; } ' +
+ '.jstree-rtl li { margin-left:0; margin-right:18px; } ' +
+ '.jstree > ul > li { margin-left:0px; } ' +
+ '.jstree-rtl > ul > li { margin-right:0px; } ' +
+ '.jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } ' +
+ '.jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } ' +
+ '.jstree a:focus { outline: none; } ' +
+ '.jstree a > ins { height:16px; width:16px; } ' +
+ '.jstree a > .jstree-icon { margin-right:3px; } ' +
+ '.jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; } ' +
+ 'li.jstree-open > ul { display:block; } ' +
+ 'li.jstree-closed > ul { display:none; } ';
+ // Correct IE 6 (does not support the > CSS selector)
+ if(/msie/.test(u) && parseInt(v, 10) == 6) {
+ is_ie6 = true;
+ css_string += '' +
+ '.jstree li { height:18px; margin-left:0; margin-right:0; } ' +
+ '.jstree li li { margin-left:18px; } ' +
+ '.jstree-rtl li li { margin-left:0px; margin-right:18px; } ' +
+ 'li.jstree-open ul { display:block; } ' +
+ 'li.jstree-closed ul { display:none !important; } ' +
+ '.jstree li a { display:inline; border-width:0 !important; padding:0px 2px !important; } ' +
+ '.jstree li a ins { height:16px; width:16px; margin-right:3px; } ' +
+ '.jstree-rtl li a ins { margin-right:0px; margin-left:3px; } ';
+ }
+ // Correct IE 7 (shifts anchor nodes onhover)
+ if(/msie/.test(u) && parseInt(v, 10) == 7) {
+ css_string += '.jstree li a { border-width:0 !important; padding:0px 2px !important; } ';
+ }
+ $.vakata.css.add_sheet({ str : css_string });
+ });
+
+ // core functions (open, close, create, update, delete)
+ $.jstree.plugin("core", {
+ __init : function () {
+ this.data.core.to_open = $.map($.makeArray(this.get_settings().core.initially_open), function (n) { return "#" + n.toString().replace(/^#/,"").replace('\\/','/').replace('/','\\/'); });
+ },
+ defaults : {
+ html_titles : false,
+ animation : 500,
+ initially_open : [],
+ rtl : false,
+ strings : {
+ loading : "Loading ...",
+ new_node : "New node"
+ }
+ },
+ _fn : {
+ init : function () {
+ this.set_focus();
+ if(this._get_settings().core.rtl) {
+ this.get_container().addClass("jstree-rtl").css("direction", "rtl");
+ }
+ this.get_container().html("<ul><li class='jstree-last jstree-leaf'><ins>&#160;</ins><a class='jstree-loading' href='#'><ins class='jstree-icon'>&#160;</ins>" + this._get_settings().core.strings.loading + "</a></li></ul>");
+ this.data.core.li_height = this.get_container().find("ul li.jstree-closed, ul li.jstree-leaf").eq(0).height() || 18;
+
+ this.get_container()
+ .delegate("li > ins", "click.jstree", $.proxy(function (event) {
+ var trgt = $(event.target);
+ if(trgt.is("ins") && event.pageY - trgt.offset().top < this.data.core.li_height) { this.toggle_node(trgt); }
+ }, this))
+ .bind("mousedown.jstree", $.proxy(function () {
+ this.set_focus(); // This used to be setTimeout(set_focus,0) - why?
+ }, this))
+ .bind("dblclick.jstree", function (event) {
+ var sel;
+ if(document.selection && document.selection.empty) { document.selection.empty(); }
+ else {
+ if(window.getSelection) {
+ sel = window.getSelection();
+ try {
+ sel.removeAllRanges();
+ sel.collapse();
+ } catch (err) { }
+ }
+ }
+ });
+ this.__callback();
+ this.load_node(-1, function () { this.loaded(); this.reopen(); });
+ },
+ destroy : function () {
+ var i,
+ n = this.get_index(),
+ s = this._get_settings(),
+ _this = this;
+
+ $.each(s.plugins, function (i, val) {
+ try { plugins[val].__destroy.apply(_this); } catch(err) { }
+ });
+ this.__callback();
+ // set focus to another instance if this one is focused
+ if(this.is_focused()) {
+ for(i in instances) {
+ if(instances.hasOwnProperty(i) && i != n) {
+ instances[i].set_focus();
+ break;
+ }
+ }
+ }
+ // if no other instance found
+ if(n === focused_instance) { focused_instance = -1; }
+ // remove all traces of jstree in the DOM (only the ones set using jstree*) and cleans all events
+ this.get_container()
+ .unbind(".jstree")
+ .undelegate(".jstree")
+ .removeData("jstree-instance-id")
+ .find("[class^='jstree']")
+ .andSelf()
+ .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); });
+ // remove the actual data
+ instances[n] = null;
+ delete instances[n];
+ },
+ save_opened : function () {
+ var _this = this;
+ this.data.core.to_open = [];
+ this.get_container().find(".jstree-open").each(function () {
+ _this.data.core.to_open.push("#" + this.id.toString().replace(/^#/,"").replace('\\/','/').replace('/','\\/'));
+ });
+ this.__callback(_this.data.core.to_open);
+ },
+ reopen : function (is_callback) {
+ var _this = this,
+ done = true,
+ current = [],
+ remaining = [];
+ if(!is_callback) { this.data.core.reopen = false; this.data.core.refreshing = true; }
+ if(this.data.core.to_open.length) {
+ $.each(this.data.core.to_open, function (i, val) {
+ if(val == "#") { return true; }
+ if($(val).length && $(val).is(".jstree-closed")) { current.push(val); }
+ else { remaining.push(val); }
+ });
+ if(current.length) {
+ this.data.core.to_open = remaining;
+ $.each(current, function (i, val) {
+ _this.open_node(val, function () { _this.reopen(true); }, true);
+ });
+ done = false;
+ }
+ }
+ if(done) {
+ // TODO: find a more elegant approach to syncronizing returning requests
+ if(this.data.core.reopen) { clearTimeout(this.data.core.reopen); }
+ this.data.core.reopen = setTimeout(function () { _this.__callback({}, _this); }, 50);
+ this.data.core.refreshing = false;
+ }
+ },
+ refresh : function (obj) {
+ var _this = this;
+ this.save_opened();
+ if(!obj) { obj = -1; }
+ obj = this._get_node(obj);
+ if(!obj) { obj = -1; }
+ if(obj !== -1) { obj.children("UL").remove(); }
+ this.load_node(obj, function () { _this.__callback({ "obj" : obj}); _this.reopen(); });
+ },
+ // Dummy function to fire after the first load (so that there is a jstree.loaded event)
+ loaded : function () {
+ this.__callback();
+ },
+ // deal with focus
+ set_focus : function () {
+ var f = $.jstree._focused();
+ if(f && f !== this) {
+ f.get_container().removeClass("jstree-focused");
+ }
+ if(f !== this) {
+ this.get_container().addClass("jstree-focused");
+ focused_instance = this.get_index();
+ }
+ this.__callback();
+ },
+ is_focused : function () {
+ return focused_instance == this.get_index();
+ },
+
+ // traverse
+ _get_node : function (obj) {
+ var $obj = $(obj, this.get_container());
+ if($obj.is(".jstree") || obj == -1) { return -1; }
+ $obj = $obj.closest("li", this.get_container());
+ return $obj.length ? $obj : false;
+ },
+ _get_next : function (obj, strict) {
+ obj = this._get_node(obj);
+ if(obj === -1) { return this.get_container().find("> ul > li:first-child"); }
+ if(!obj.length) { return false; }
+ if(strict) { return (obj.nextAll("li").size() > 0) ? obj.nextAll("li:eq(0)") : false; }
+
+ if(obj.hasClass("jstree-open")) { return obj.find("li:eq(0)"); }
+ else if(obj.nextAll("li").size() > 0) { return obj.nextAll("li:eq(0)"); }
+ else { return obj.parentsUntil(".jstree","li").next("li").eq(0); }
+ },
+ _get_prev : function (obj, strict) {
+ obj = this._get_node(obj);
+ if(obj === -1) { return this.get_container().find("> ul > li:last-child"); }
+ if(!obj.length) { return false; }
+ if(strict) { return (obj.prevAll("li").length > 0) ? obj.prevAll("li:eq(0)") : false; }
+
+ if(obj.prev("li").length) {
+ obj = obj.prev("li").eq(0);
+ while(obj.hasClass("jstree-open")) { obj = obj.children("ul:eq(0)").children("li:last"); }
+ return obj;
+ }
+ else { var o = obj.parentsUntil(".jstree","li:eq(0)"); return o.length ? o : false; }
+ },
+ _get_parent : function (obj) {
+ obj = this._get_node(obj);
+ if(obj == -1 || !obj.length) { return false; }
+ var o = obj.parentsUntil(".jstree", "li:eq(0)");
+ return o.length ? o : -1;
+ },
+ _get_children : function (obj) {
+ obj = this._get_node(obj);
+ if(obj === -1) { return this.get_container().children("ul:eq(0)").children("li"); }
+ if(!obj.length) { return false; }
+ return obj.children("ul:eq(0)").children("li");
+ },
+ get_path : function (obj, id_mode) {
+ var p = [],
+ _this = this;
+ obj = this._get_node(obj);
+ if(obj === -1 || !obj || !obj.length) { return false; }
+ obj.parentsUntil(".jstree", "li").each(function () {
+ p.push( id_mode ? this.id : _this.get_text(this) );
+ });
+ p.reverse();
+ p.push( id_mode ? obj.attr("id") : this.get_text(obj) );
+ return p;
+ },
+
+ is_open : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-open"); },
+ is_closed : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-closed"); },
+ is_leaf : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-leaf"); },
+ // open/close
+ open_node : function (obj, callback, skip_animation) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ if(!obj.hasClass("jstree-closed")) { if(callback) { callback.call(); } return false; }
+ var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation,
+ t = this;
+ if(!this._is_loaded(obj)) {
+ obj.children("a").addClass("jstree-loading");
+ this.load_node(obj, function () { t.open_node(obj, callback, skip_animation); }, callback);
+ }
+ else {
+ if(s) { obj.children("ul").css("display","none"); }
+ obj.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading");
+ if(s) { obj.children("ul").stop(true).slideDown(s, function () { this.style.display = ""; }); }
+ this.__callback({ "obj" : obj });
+ if(callback) { callback.call(); }
+ }
+ },
+ close_node : function (obj, skip_animation) {
+ obj = this._get_node(obj);
+ var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation;
+ if(!obj.length || !obj.hasClass("jstree-open")) { return false; }
+ if(s) { obj.children("ul").attr("style","display:block !important"); }
+ obj.removeClass("jstree-open").addClass("jstree-closed");
+ if(s) { obj.children("ul").stop(true).slideUp(s, function () { this.style.display = ""; }); }
+ this.__callback({ "obj" : obj });
+ },
+ toggle_node : function (obj) {
+ obj = this._get_node(obj);
+ if(obj.hasClass("jstree-closed")) { return this.open_node(obj); }
+ if(obj.hasClass("jstree-open")) { return this.close_node(obj); }
+ },
+ open_all : function (obj, original_obj) {
+ obj = obj ? this._get_node(obj) : this.get_container();
+ if(!obj || obj === -1) { obj = this.get_container(); }
+ if(original_obj) {
+ obj = obj.find("li.jstree-closed");
+ }
+ else {
+ original_obj = obj;
+ if(obj.is(".jstree-closed")) { obj = obj.find("li.jstree-closed").andSelf(); }
+ else { obj = obj.find("li.jstree-closed"); }
+ }
+ var _this = this;
+ obj.each(function () {
+ var __this = this;
+ if(!_this._is_loaded(this)) { _this.open_node(this, function() { _this.open_all(__this, original_obj); }, true); }
+ else { _this.open_node(this, false, true); }
+ });
+ // so that callback is fired AFTER all nodes are open
+ if(original_obj.find('li.jstree-closed').length === 0) { this.__callback({ "obj" : original_obj }); }
+ },
+ close_all : function (obj) {
+ var _this = this;
+ obj = obj ? this._get_node(obj) : this.get_container();
+ if(!obj || obj === -1) { obj = this.get_container(); }
+ obj.find("li.jstree-open").andSelf().each(function () { _this.close_node(this); });
+ this.__callback({ "obj" : obj });
+ },
+ clean_node : function (obj) {
+ obj = obj && obj != -1 ? $(obj) : this.get_container();
+ obj = obj.is("li") ? obj.find("li").andSelf() : obj.find("li");
+ obj.removeClass("jstree-last")
+ .filter("li:last-child").addClass("jstree-last").end()
+ .filter(":has(li)")
+ .not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed");
+ obj.not(".jstree-open, .jstree-closed").addClass("jstree-leaf").children("ul").remove();
+ this.__callback({ "obj" : obj });
+ },
+ // rollback
+ get_rollback : function () {
+ this.__callback();
+ return { i : this.get_index(), h : this.get_container().children("ul").clone(true), d : this.data };
+ },
+ set_rollback : function (html, data) {
+ this.get_container().empty().append(html);
+ this.data = data;
+ this.__callback();
+ },
+ // Dummy functions to be overwritten by any datastore plugin included
+ load_node : function (obj, s_call, e_call) { this.__callback({ "obj" : obj }); },
+ _is_loaded : function (obj) { return true; },
+
+ // Basic operations: create
+ create_node : function (obj, position, js, callback, is_loaded) {
+ obj = this._get_node(obj);
+ position = typeof position === "undefined" ? "last" : position;
+ var d = $("<li>"),
+ s = this._get_settings().core,
+ tmp;
+
+ if(obj !== -1 && !obj.length) { return false; }
+ if(!is_loaded && !this._is_loaded(obj)) { this.load_node(obj, function () { this.create_node(obj, position, js, callback, true); }); return false; }
+
+ this.__rollback();
+
+ if(typeof js === "string") { js = { "data" : js }; }
+ if(!js) { js = {}; }
+ if(js.attr) { d.attr(js.attr); }
+ if(js.state) { d.addClass("jstree-" + js.state); }
+ if(!js.data) { js.data = s.strings.new_node; }
+ if(!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); }
+ $.each(js.data, function (i, m) {
+ tmp = $("<a>");
+ if($.isFunction(m)) { m = m.call(this, js); }
+ if(typeof m == "string") { tmp.attr('href','#')[ s.html_titles ? "html" : "text" ](m); }
+ else {
+ if(!m.attr) { m.attr = {}; }
+ if(!m.attr.href) { m.attr.href = '#'; }
+ tmp.attr(m.attr)[ s.html_titles ? "html" : "text" ](m.title);
+ if(m.language) { tmp.addClass(m.language); }
+ }
+ tmp.prepend("<ins class='jstree-icon'>&#160;</ins>");
+ if(m.icon) {
+ if(m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); }
+ else { tmp.children("ins").css("background","url('" + m.icon + "') center center no-repeat"); }
+ }
+ d.append(tmp);
+ });
+ d.prepend("<ins class='jstree-icon'>&#160;</ins>");
+ if(obj === -1) {
+ obj = this.get_container();
+ if(position === "before") { position = "first"; }
+ if(position === "after") { position = "last"; }
+ }
+ switch(position) {
+ case "before": obj.before(d); tmp = this._get_parent(obj); break;
+ case "after" : obj.after(d); tmp = this._get_parent(obj); break;
+ case "inside":
+ case "first" :
+ if(!obj.children("ul").length) { obj.append("<ul>"); }
+ obj.children("ul").prepend(d);
+ tmp = obj;
+ break;
+ case "last":
+ if(!obj.children("ul").length) { obj.append("<ul>"); }
+ obj.children("ul").append(d);
+ tmp = obj;
+ break;
+ default:
+ if(!obj.children("ul").length) { obj.append("<ul>"); }
+ if(!position) { position = 0; }
+ tmp = obj.children("ul").children("li").eq(position);
+ if(tmp.length) { tmp.before(d); }
+ else { obj.children("ul").append(d); }
+ tmp = obj;
+ break;
+ }
+ if(tmp === -1 || tmp.get(0) === this.get_container().get(0)) { tmp = -1; }
+ this.clean_node(tmp);
+ this.__callback({ "obj" : d, "parent" : tmp });
+ if(callback) { callback.call(this, d); }
+ return d;
+ },
+ // Basic operations: rename (deal with text)
+ get_text : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ var s = this._get_settings().core.html_titles;
+ obj = obj.children("a:eq(0)");
+ if(s) {
+ obj = obj.clone();
+ obj.children("INS").remove();
+ return obj.html();
+ }
+ else {
+ obj = obj.contents().filter(function() { return this.nodeType == 3; })[0];
+ return obj.nodeValue;
+ }
+ },
+ set_text : function (obj, val) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ obj = obj.children("a:eq(0)");
+ if(this._get_settings().core.html_titles) {
+ var tmp = obj.children("INS").clone();
+ obj.html(val).prepend(tmp);
+ this.__callback({ "obj" : obj, "name" : val });
+ return true;
+ }
+ else {
+ obj = obj.contents().filter(function() { return this.nodeType == 3; })[0];
+ this.__callback({ "obj" : obj, "name" : val });
+ return (obj.nodeValue = val);
+ }
+ },
+ rename_node : function (obj, val) {
+ obj = this._get_node(obj);
+ this.__rollback();
+ if(obj && obj.length && this.set_text.apply(this, Array.prototype.slice.call(arguments))) { this.__callback({ "obj" : obj, "name" : val }); }
+ },
+ // Basic operations: deleting nodes
+ delete_node : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ this.__rollback();
+ var p = this._get_parent(obj), prev = this._get_prev(obj);
+ obj = obj.remove();
+ if(p !== -1 && p.find("> ul > li").length === 0) {
+ p.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");
+ }
+ this.clean_node(p);
+ this.__callback({ "obj" : obj, "prev" : prev });
+ return obj;
+ },
+ prepare_move : function (o, r, pos, cb, is_cb) {
+ var p = {};
+
+ p.ot = $.jstree._reference(p.o) || this;
+ p.o = p.ot._get_node(o);
+ p.r = r === - 1 ? -1 : this._get_node(r);
+ p.p = (typeof p === "undefined") ? "last" : pos; // TODO: move to a setting
+ if(!is_cb && prepared_move.o && prepared_move.o[0] === p.o[0] && prepared_move.r[0] === p.r[0] && prepared_move.p === p.p) {
+ this.__callback(prepared_move);
+ if(cb) { cb.call(this, prepared_move); }
+ return;
+ }
+ p.ot = $.jstree._reference(p.o) || this;
+ p.rt = r === -1 ? p.ot : $.jstree._reference(p.r) || this;
+ if(p.r === -1) {
+ p.cr = -1;
+ switch(p.p) {
+ case "first":
+ case "before":
+ case "inside":
+ p.cp = 0;
+ break;
+ case "after":
+ case "last":
+ p.cp = p.rt.get_container().find(" > ul > li").length;
+ break;
+ default:
+ p.cp = p.p;
+ break;
+ }
+ }
+ else {
+ if(!/^(before|after)$/.test(p.p) && !this._is_loaded(p.r)) {
+ return this.load_node(p.r, function () { this.prepare_move(o, r, pos, cb, true); });
+ }
+ switch(p.p) {
+ case "before":
+ p.cp = p.r.index();
+ p.cr = p.rt._get_parent(p.r);
+ break;
+ case "after":
+ p.cp = p.r.index() + 1;
+ p.cr = p.rt._get_parent(p.r);
+ break;
+ case "inside":
+ case "first":
+ p.cp = 0;
+ p.cr = p.r;
+ break;
+ case "last":
+ p.cp = p.r.find(" > ul > li").length;
+ p.cr = p.r;
+ break;
+ default:
+ p.cp = p.p;
+ p.cr = p.r;
+ break;
+ }
+ }
+ p.np = p.cr == -1 ? p.rt.get_container() : p.cr;
+ p.op = p.ot._get_parent(p.o);
+ p.or = p.np.find(" > ul > li:nth-child(" + (p.cp + 1) + ")");
+
+ prepared_move = p;
+ this.__callback(prepared_move);
+ if(cb) { cb.call(this, prepared_move); }
+ },
+ check_move : function () {
+ var obj = prepared_move, ret = true;
+ if(obj.or[0] === obj.o[0]) { return false; }
+ obj.o.each(function () {
+ if(obj.r.parentsUntil(".jstree").andSelf().filter("li").index(this) !== -1) { ret = false; return false; }
+ });
+ return ret;
+ },
+ move_node : function (obj, ref, position, is_copy, is_prepared, skip_check) {
+ if(!is_prepared) {
+ return this.prepare_move(obj, ref, position, function (p) {
+ this.move_node(p, false, false, is_copy, true, skip_check);
+ });
+ }
+ if(!skip_check && !this.check_move()) { return false; }
+
+ this.__rollback();
+ var o = false;
+ if(is_copy) {
+ o = obj.o.clone();
+ o.find("*[id]").andSelf().each(function () {
+ if(this.id) { this.id = "copy_" + this.id; }
+ });
+ }
+ else { o = obj.o; }
+
+ if(obj.or.length) { obj.or.before(o); }
+ else {
+ if(!obj.np.children("ul").length) { $("<ul>").appendTo(obj.np); }
+ obj.np.children("ul:eq(0)").append(o);
+ }
+
+ try {
+ obj.ot.clean_node(obj.op);
+ obj.rt.clean_node(obj.np);
+ if(!obj.op.find("> ul > li").length) {
+ obj.op.removeClass("jstree-open jstree-closed").addClass("jstree-leaf").children("ul").remove();
+ }
+ } catch (e) { }
+
+ if(is_copy) {
+ prepared_move.cy = true;
+ prepared_move.oc = o;
+ }
+ this.__callback(prepared_move);
+ return prepared_move;
+ },
+ _get_move : function () { return prepared_move; }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree ui plugin 1.0
+ * This plugins handles selecting/deselecting/hovering/dehovering nodes
+ */
+(function ($) {
+ $.jstree.plugin("ui", {
+ __init : function () {
+ this.data.ui.selected = $();
+ this.data.ui.last_selected = false;
+ this.data.ui.hovered = null;
+ this.data.ui.to_select = this.get_settings().ui.initially_select;
+
+ this.get_container()
+ .delegate("a", "click.jstree", $.proxy(function (event) {
+ event.preventDefault();
+ this.select_node(event.currentTarget, true, event);
+ }, this))
+ .delegate("a", "mouseenter.jstree", $.proxy(function (event) {
+ this.hover_node(event.target);
+ }, this))
+ .delegate("a", "mouseleave.jstree", $.proxy(function (event) {
+ this.dehover_node(event.target);
+ }, this))
+ .bind("reopen.jstree", $.proxy(function () {
+ this.reselect();
+ }, this))
+ .bind("get_rollback.jstree", $.proxy(function () {
+ this.dehover_node();
+ this.save_selected();
+ }, this))
+ .bind("set_rollback.jstree", $.proxy(function () {
+ this.reselect();
+ }, this))
+ .bind("close_node.jstree", $.proxy(function (event, data) {
+ var s = this._get_settings().ui,
+ obj = this._get_node(data.rslt.obj),
+ clk = (obj && obj.length) ? obj.children("ul").find(".jstree-clicked") : $(),
+ _this = this;
+ if(s.selected_parent_close === false || !clk.length) { return; }
+ clk.each(function () {
+ _this.deselect_node(this);
+ if(s.selected_parent_close === "select_parent") { _this.select_node(obj); }
+ });
+ }, this))
+ .bind("delete_node.jstree", $.proxy(function (event, data) {
+ var s = this._get_settings().ui.select_prev_on_delete,
+ obj = this._get_node(data.rslt.obj),
+ clk = (obj && obj.length) ? obj.find(".jstree-clicked") : [],
+ _this = this;
+ clk.each(function () { _this.deselect_node(this); });
+ if(s && clk.length) { this.select_node(data.rslt.prev); }
+ }, this))
+ .bind("move_node.jstree", $.proxy(function (event, data) {
+ if(data.rslt.cy) {
+ data.rslt.oc.find(".jstree-clicked").removeClass("jstree-clicked");
+ }
+ }, this));
+ },
+ defaults : {
+ select_limit : -1, // 0, 1, 2 ... or -1 for unlimited
+ select_multiple_modifier : "ctrl", // on, or ctrl, shift, alt
+ selected_parent_close : "select_parent", // false, "deselect", "select_parent"
+ select_prev_on_delete : true,
+ disable_selecting_children : false,
+ initially_select : []
+ },
+ _fn : {
+ _get_node : function (obj, allow_multiple) {
+ if(typeof obj === "undefined" || obj === null) { return allow_multiple ? this.data.ui.selected : this.data.ui.last_selected; }
+ var $obj = $(obj, this.get_container());
+ if($obj.is(".jstree") || obj == -1) { return -1; }
+ $obj = $obj.closest("li", this.get_container());
+ return $obj.length ? $obj : false;
+ },
+ save_selected : function () {
+ var _this = this;
+ this.data.ui.to_select = [];
+ this.data.ui.selected.each(function () { _this.data.ui.to_select.push("#" + this.id.toString().replace(/^#/,"").replace('\\/','/').replace('/','\\/')); });
+ this.__callback(this.data.ui.to_select);
+ },
+ reselect : function () {
+ var _this = this,
+ s = this.data.ui.to_select;
+ s = $.map($.makeArray(s), function (n) { return "#" + n.toString().replace(/^#/,"").replace('\\/','/').replace('/','\\/'); });
+ this.deselect_all();
+ $.each(s, function (i, val) { if(val && val !== "#") { _this.select_node(val); } });
+ this.__callback();
+ },
+ refresh : function (obj) {
+ this.save_selected();
+ return this.__call_old();
+ },
+ hover_node : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ //if(this.data.ui.hovered && obj.get(0) === this.data.ui.hovered.get(0)) { return; }
+ if(!obj.hasClass("jstree-hovered")) { this.dehover_node(); }
+ this.data.ui.hovered = obj.children("a").addClass("jstree-hovered").parent();
+ this.__callback({ "obj" : obj });
+ },
+ dehover_node : function () {
+ var obj = this.data.ui.hovered, p;
+ if(!obj || !obj.length) { return false; }
+ p = obj.children("a").removeClass("jstree-hovered").parent();
+ if(this.data.ui.hovered[0] === p[0]) { this.data.ui.hovered = null; }
+ this.__callback({ "obj" : obj });
+ },
+ select_node : function (obj, check, e) {
+ obj = this._get_node(obj);
+ if(obj == -1 || !obj || !obj.length) { return false; }
+ var s = this._get_settings().ui,
+ is_multiple = (s.select_multiple_modifier == "on" || (s.select_multiple_modifier !== false && e && e[s.select_multiple_modifier + "Key"])),
+ is_selected = this.is_selected(obj),
+ proceed = true;
+ if(check) {
+ if(s.disable_selecting_children && is_multiple && obj.parents("li", this.get_container()).children(".jstree-clicked").length) {
+ return false;
+ }
+ proceed = false;
+ switch(!0) {
+ case (is_selected && !is_multiple):
+ this.deselect_all();
+ is_selected = false;
+ proceed = true;
+ break;
+ case (!is_selected && !is_multiple):
+ if(s.select_limit == -1 || s.select_limit > 0) {
+ this.deselect_all();
+ proceed = true;
+ }
+ break;
+ case (is_selected && is_multiple):
+ this.deselect_node(obj);
+ break;
+ case (!is_selected && is_multiple):
+ if(s.select_limit == -1 || this.data.ui.selected.length + 1 <= s.select_limit) {
+ proceed = true;
+ }
+ break;
+ }
+ }
+ if(proceed && !is_selected) {
+ obj.children("a").addClass("jstree-clicked");
+ this.data.ui.selected = this.data.ui.selected.add(obj);
+ this.data.ui.last_selected = obj;
+ this.__callback({ "obj" : obj });
+ }
+ },
+ deselect_node : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ if(this.is_selected(obj)) {
+ obj.children("a").removeClass("jstree-clicked");
+ this.data.ui.selected = this.data.ui.selected.not(obj);
+ if(this.data.ui.last_selected.get(0) === obj.get(0)) { this.data.ui.last_selected = this.data.ui.selected.eq(0); }
+ this.__callback({ "obj" : obj });
+ }
+ },
+ toggle_select : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return false; }
+ if(this.is_selected(obj)) { this.deselect_node(obj); }
+ else { this.select_node(obj); }
+ },
+ is_selected : function (obj) { return this.data.ui.selected.index(this._get_node(obj)) >= 0; },
+ get_selected : function (context) {
+ return context ? $(context).find(".jstree-clicked").parent() : this.data.ui.selected;
+ },
+ deselect_all : function (context) {
+ if(context) { $(context).find(".jstree-clicked").removeClass("jstree-clicked"); }
+ else { this.get_container().find(".jstree-clicked").removeClass("jstree-clicked"); }
+ this.data.ui.selected = $([]);
+ this.data.ui.last_selected = false;
+ this.__callback();
+ }
+ }
+ });
+ // include the selection plugin by default
+ $.jstree.defaults.plugins.push("ui");
+})(jQuery);
+//*/
+
+/*
+ * jsTree CRRM plugin 1.0
+ * Handles creating/renaming/removing/moving nodes by user interaction.
+ */
+(function ($) {
+ $.jstree.plugin("crrm", {
+ __init : function () {
+ this.get_container()
+ .bind("move_node.jstree", $.proxy(function (e, data) {
+ if(this._get_settings().crrm.move.open_onmove) {
+ var t = this;
+ data.rslt.np.parentsUntil(".jstree").andSelf().filter(".jstree-closed").each(function () {
+ t.open_node(this, false, true);
+ });
+ }
+ }, this));
+ },
+ defaults : {
+ input_width_limit : 200,
+ move : {
+ always_copy : false, // false, true or "multitree"
+ open_onmove : true,
+ default_position : "last",
+ check_move : function (m) { return true; }
+ }
+ },
+ _fn : {
+ _show_input : function (obj, callback) {
+ obj = this._get_node(obj);
+ var rtl = this._get_settings().core.rtl,
+ w = this._get_settings().crrm.input_width_limit,
+ w1 = obj.children("ins").width(),
+ w2 = obj.find("> a:visible > ins").width() * obj.find("> a:visible > ins").length,
+ t = this.get_text(obj),
+ h1 = $("<div>", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo("body"),
+ h2 = obj.css("position","relative").append(
+ $("<input>", {
+ "value" : t,
+ // "size" : t.length,
+ "css" : {
+ "padding" : "0",
+ "border" : "1px solid silver",
+ "position" : "absolute",
+ "left" : (rtl ? "auto" : (w1 + w2 + 4) + "px"),
+ "right" : (rtl ? (w1 + w2 + 4) + "px" : "auto"),
+ "top" : "0px",
+ "height" : (this.data.core.li_height - 2) + "px",
+ "lineHeight" : (this.data.core.li_height - 2) + "px",
+ "width" : "150px" // will be set a bit further down
+ },
+ "blur" : $.proxy(function () {
+ var i = obj.children("input"),
+ v = i.val();
+ if(v === "") { v = t; }
+ i.remove(); // rollback purposes
+ this.set_text(obj,t); // rollback purposes
+ this.rename_node(obj, v);
+ callback.call(this, obj, v, t);
+ obj.css("position","");
+ }, this),
+ "keyup" : function (event) {
+ var key = event.keyCode || event.which;
+ if(key == 27) { this.value = t; this.blur(); return; }
+ else if(key == 13) { this.blur(); return; }
+ else {
+ h2.width(Math.min(h1.text("pW" + this.value).width(),w));
+ }
+ }
+ })
+ ).children("input");
+ this.set_text(obj, "");
+ h1.css({
+ fontFamily : h2.css('fontFamily') || '',
+ fontSize : h2.css('fontSize') || '',
+ fontWeight : h2.css('fontWeight') || '',
+ fontStyle : h2.css('fontStyle') || '',
+ fontStretch : h2.css('fontStretch') || '',
+ fontVariant : h2.css('fontVariant') || '',
+ letterSpacing : h2.css('letterSpacing') || '',
+ wordSpacing : h2.css('wordSpacing') || ''
+ });
+ h2.width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select();
+ },
+ rename : function (obj) {
+ obj = this._get_node(obj);
+ this.__rollback();
+ var f = this.__callback;
+ this._show_input(obj, function (obj, new_name, old_name) {
+ f.call(this, { "obj" : obj, "new_name" : new_name, "old_name" : old_name });
+ });
+ },
+ create : function (obj, position, js, callback, skip_rename) {
+ var t, _this = this;
+ obj = this._get_node(obj);
+ if(!obj) { obj = -1; }
+ this.__rollback();
+ t = this.create_node(obj, position, js, function (t) {
+ var p = this._get_parent(t),
+ pos = $(t).index();
+ if(callback) { callback.call(this, t); }
+ if(p.length && p.hasClass("jstree-closed")) { this.open_node(p, false, true); }
+ if(!skip_rename) {
+ this._show_input(t, function (obj, new_name, old_name) {
+ _this.__callback({ "obj" : obj, "name" : new_name, "parent" : p, "position" : pos });
+ });
+ }
+ else { _this.__callback({ "obj" : t, "name" : this.get_text(t), "parent" : p, "position" : pos }); }
+ });
+ return t;
+ },
+ remove : function (obj) {
+ obj = this._get_node(obj, true);
+ this.__rollback();
+ this.delete_node(obj);
+ this.__callback({ "obj" : obj });
+ },
+ check_move : function () {
+ if(!this.__call_old()) { return false; }
+ var s = this._get_settings().crrm.move;
+ if(!s.check_move.call(this, this._get_move())) { return false; }
+ return true;
+ },
+ move_node : function (obj, ref, position, is_copy, is_prepared, skip_check) {
+ var s = this._get_settings().crrm.move;
+ if(!is_prepared) {
+ if(!position) { position = s.default_position; }
+ if(position === "inside" && !s.default_position.match(/^(before|after)$/)) { position = s.default_position; }
+ return this.__call_old(true, obj, ref, position, is_copy, false, skip_check);
+ }
+ // if the move is already prepared
+ if(s.always_copy === true || (s.always_copy === "multitree" && obj.rt.get_index() !== obj.ot.get_index() )) {
+ is_copy = true;
+ }
+ this.__call_old(true, obj, ref, position, is_copy, true, skip_check);
+ },
+
+ cut : function (obj) {
+ obj = this._get_node(obj);
+ this.data.crrm.cp_nodes = false;
+ this.data.crrm.ct_nodes = false;
+ if(!obj || !obj.length) { return false; }
+ this.data.crrm.ct_nodes = obj;
+ },
+ copy : function (obj) {
+ obj = this._get_node(obj);
+ this.data.crrm.cp_nodes = false;
+ this.data.crrm.ct_nodes = false;
+ if(!obj || !obj.length) { return false; }
+ this.data.crrm.cp_nodes = obj;
+ },
+ paste : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj || !obj.length) { return false; }
+ if(!this.data.crrm.ct_nodes && !this.data.crrm.cp_nodes) { return false; }
+ if(this.data.crrm.ct_nodes) { this.move_node(this.data.crrm.ct_nodes, obj); }
+ if(this.data.crrm.cp_nodes) { this.move_node(this.data.crrm.cp_nodes, obj, false, true); }
+ this.data.crrm.cp_nodes = false;
+ this.data.crrm.ct_nodes = false;
+ }
+ }
+ });
+ // include the crr plugin by default
+ $.jstree.defaults.plugins.push("crrm");
+})(jQuery);
+
+/*
+ * jsTree themes plugin 1.0
+ * Handles loading and setting themes, as well as detecting path to themes, etc.
+ */
+(function ($) {
+ var themes_loaded = [];
+ // this variable stores the path to the themes folder - if left as false - it will be autodetected
+ $.jstree._themes = false;
+ $.jstree.plugin("themes", {
+ __init : function () {
+ this.get_container()
+ .bind("init.jstree", $.proxy(function () {
+ var s = this._get_settings().themes;
+ this.data.themes.dots = s.dots;
+ this.data.themes.icons = s.icons;
+ //alert(s.dots);
+ this.set_theme(s.theme, s.url);
+ }, this))
+ .bind("loaded.jstree", $.proxy(function () {
+ // bound here too, as simple HTML tree's won't honor dots & icons otherwise
+ if(!this.data.themes.dots) { this.hide_dots(); }
+ else { this.show_dots(); }
+ if(!this.data.themes.icons) { this.hide_icons(); }
+ else { this.show_icons(); }
+ }, this));
+ },
+ defaults : {
+ theme : "default",
+ url : false,
+ dots : true,
+ icons : true
+ },
+ _fn : {
+ set_theme : function (theme_name, theme_url) {
+ if(!theme_name) { return false; }
+ if(!theme_url) { theme_url = $.jstree._themes + theme_name + '/style.css'; }
+ if($.inArray(theme_url, themes_loaded) == -1) {
+ $.vakata.css.add_sheet({ "url" : theme_url, "rel" : "jstree" });
+ themes_loaded.push(theme_url);
+ }
+ if(this.data.themes.theme != theme_name) {
+ this.get_container().removeClass('jstree-' + this.data.themes.theme);
+ this.data.themes.theme = theme_name;
+ }
+ this.get_container().addClass('jstree-' + theme_name);
+ if(!this.data.themes.dots) { this.hide_dots(); }
+ else { this.show_dots(); }
+ if(!this.data.themes.icons) { this.hide_icons(); }
+ else { this.show_icons(); }
+ this.__callback();
+ },
+ get_theme : function () { return this.data.themes.theme; },
+
+ show_dots : function () { this.data.themes.dots = true; this.get_container().children("ul").removeClass("jstree-no-dots"); },
+ hide_dots : function () { this.data.themes.dots = false; this.get_container().children("ul").addClass("jstree-no-dots"); },
+ toggle_dots : function () { if(this.data.themes.dots) { this.hide_dots(); } else { this.show_dots(); } },
+
+ show_icons : function () { this.data.themes.icons = true; this.get_container().children("ul").removeClass("jstree-no-icons"); },
+ hide_icons : function () { this.data.themes.icons = false; this.get_container().children("ul").addClass("jstree-no-icons"); },
+ toggle_icons: function () { if(this.data.themes.icons) { this.hide_icons(); } else { this.show_icons(); } }
+ }
+ });
+ // autodetect themes path
+ $(function () {
+ if($.jstree._themes === false) {
+ $("script").each(function () {
+ if(this.src.toString().match(/jquery\.jstree[^\/]*?\.js(\?.*)?$/)) {
+ $.jstree._themes = this.src.toString().replace(/jquery\.jstree[^\/]*?\.js(\?.*)?$/, "") + 'themes/';
+ return false;
+ }
+ });
+ }
+ if($.jstree._themes === false) { $.jstree._themes = "themes/"; }
+ });
+ // include the themes plugin by default
+ $.jstree.defaults.plugins.push("themes");
+})(jQuery);
+//*/
+
+/*
+ * jsTree hotkeys plugin 1.0
+ * Enables keyboard navigation for all tree instances
+ * Depends on the jstree ui & jquery hotkeys plugins
+ */
+(function ($) {
+ var bound = [];
+ function exec(i, event) {
+ var f = $.jstree._focused(), tmp;
+ if(f && f.data && f.data.hotkeys && f.data.hotkeys.enabled) {
+ tmp = f._get_settings().hotkeys[i];
+ if(tmp) { return tmp.call(f, event); }
+ }
+ }
+ $.jstree.plugin("hotkeys", {
+ __init : function () {
+ if(typeof $.hotkeys === "undefined") { throw "jsTree hotkeys: jQuery hotkeys plugin not included."; }
+ if(!this.data.ui) { throw "jsTree hotkeys: jsTree UI plugin not included."; }
+ $.each(this._get_settings().hotkeys, function (i, val) {
+ if($.inArray(i, bound) == -1) {
+ $(document).bind("keydown", i, function (event) { return exec(i, event); });
+ bound.push(i);
+ }
+ });
+ this.enable_hotkeys();
+ },
+ defaults : {
+ "up" : function () {
+ var o = this.data.ui.hovered || this.data.ui.last_selected || -1;
+ this.hover_node(this._get_prev(o));
+ return false;
+ },
+ "down" : function () {
+ var o = this.data.ui.hovered || this.data.ui.last_selected || -1;
+ this.hover_node(this._get_next(o));
+ return false;
+ },
+ "left" : function () {
+ var o = this.data.ui.hovered || this.data.ui.last_selected;
+ if(o) {
+ if(o.hasClass("jstree-open")) { this.close_node(o); }
+ else { this.hover_node(this._get_prev(o)); }
+ }
+ return false;
+ },
+ "right" : function () {
+ var o = this.data.ui.hovered || this.data.ui.last_selected;
+ if(o && o.length) {
+ if(o.hasClass("jstree-closed")) { this.open_node(o); }
+ else { this.hover_node(this._get_next(o)); }
+ }
+ return false;
+ },
+ "space" : function () {
+ if(this.data.ui.hovered) { this.data.ui.hovered.children("a:eq(0)").click(); }
+ return false;
+ },
+ "ctrl+space" : function (event) {
+ event.type = "click";
+ if(this.data.ui.hovered) { this.data.ui.hovered.children("a:eq(0)").trigger(event); }
+ return false;
+ },
+ "f2" : function () { this.rename(this.data.ui.hovered || this.data.ui.last_selected); },
+ "del" : function () { this.remove(this.data.ui.hovered || this._get_node(null)); }
+ },
+ _fn : {
+ enable_hotkeys : function () {
+ this.data.hotkeys.enabled = true;
+ },
+ disable_hotkeys : function () {
+ this.data.hotkeys.enabled = false;
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree JSON 1.0
+ * The JSON data store. Datastores are build by overriding the `load_node` and `_is_loaded` functions.
+ */
+(function ($) {
+ $.jstree.plugin("json_data", {
+ defaults : {
+ data : false,
+ ajax : false,
+ correct_state : true,
+ progressive_render : false
+ },
+ _fn : {
+ load_node : function (obj, s_call, e_call) { var _this = this; this.load_node_json(obj, function () { _this.__callback({ "obj" : obj }); s_call.call(this); }, e_call); },
+ _is_loaded : function (obj) {
+ var s = this._get_settings().json_data, d;
+ obj = this._get_node(obj);
+ if(obj && obj !== -1 && s.progressive_render && !obj.is(".jstree-open, .jstree-leaf") && obj.children("ul").children("li").length === 0 && obj.data("jstree-children")) {
+ d = this._parse_json(obj.data("jstree-children"));
+ if(d) {
+ obj.append(d);
+ $.removeData(obj, "jstree-children");
+ }
+ this.clean_node(obj);
+ return true;
+ }
+ return obj == -1 || !obj || !s.ajax || obj.is(".jstree-open, .jstree-leaf") || obj.children("ul").children("li").size() > 0;
+ },
+ load_node_json : function (obj, s_call, e_call) {
+ var s = this.get_settings().json_data, d,
+ error_func = function () {},
+ success_func = function () {};
+ obj = this._get_node(obj);
+ if(obj && obj !== -1) {
+ if(obj.data("jstree-is-loading")) { return; }
+ else { obj.data("jstree-is-loading",true); }
+ }
+ switch(!0) {
+ case (!s.data && !s.ajax): throw "Neither data nor ajax settings supplied.";
+ case (!!s.data && !s.ajax) || (!!s.data && !!s.ajax && (!obj || obj === -1)):
+ if(!obj || obj == -1) {
+ d = this._parse_json(s.data);
+ if(d) {
+ this.get_container().children("ul").empty().append(d.children());
+ this.clean_node();
+ }
+ else {
+ if(s.correct_state) { this.get_container().children("ul").empty(); }
+ }
+ }
+ if(s_call) { s_call.call(this); }
+ break;
+ case (!s.data && !!s.ajax) || (!!s.data && !!s.ajax && obj && obj !== -1):
+ error_func = function (x, t, e) {
+ var ef = this.get_settings().json_data.ajax.error;
+ if(ef) { ef.call(this, x, t, e); }
+ if(obj != -1 && obj.length) {
+ obj.children(".jstree-loading").removeClass("jstree-loading");
+ obj.data("jstree-is-loading",false);
+ if(t === "success" && s.correct_state) { obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf"); }
+ }
+ else {
+ if(t === "success" && s.correct_state) { this.get_container().children("ul").empty(); }
+ }
+ if(e_call) { e_call.call(this); }
+ };
+ success_func = function (d, t, x) {
+ var sf = this.get_settings().json_data.ajax.success;
+ if(sf) { d = sf.call(this,d,t,x) || d; }
+ if(d === "" || (!$.isArray(d) && !$.isPlainObject(d))) {
+ return error_func.call(this, x, t, "");
+ }
+ d = this._parse_json(d);
+ if(d) {
+ if(obj === -1 || !obj) { this.get_container().children("ul").empty().append(d.children()); }
+ else { obj.append(d).children(".jstree-loading").removeClass("jstree-loading"); obj.data("jstree-is-loading",false); }
+ this.clean_node(obj);
+ if(s_call) { s_call.call(this); }
+ }
+ else {
+ if(obj === -1 || !obj) {
+ if(s.correct_state) {
+ this.get_container().children("ul").empty();
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ else {
+ obj.children(".jstree-loading").removeClass("jstree-loading");
+ obj.data("jstree-is-loading",false);
+ if(s.correct_state) {
+ obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ }
+ };
+ s.ajax.context = this;
+ s.ajax.error = error_func;
+ s.ajax.success = success_func;
+ if(!s.ajax.dataType) { s.ajax.dataType = "json"; }
+ if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, obj); }
+ if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, obj); }
+ $.ajax(s.ajax);
+ break;
+ }
+ },
+ _parse_json : function (js, is_callback) {
+ var d = false,
+ p = this._get_settings(),
+ s = p.json_data,
+ t = p.core.html_titles,
+ tmp, i, j, ul1, ul2;
+
+ if(!js) { return d; }
+ if($.isFunction(js)) {
+ js = js.call(this);
+ }
+ if($.isArray(js)) {
+ d = $();
+ if(!js.length) { return false; }
+ for(i = 0, j = js.length; i < j; i++) {
+ tmp = this._parse_json(js[i], true);
+ if(tmp.length) { d = d.add(tmp); }
+ }
+ }
+ else {
+ if(typeof js == "string") { js = { data : js }; }
+ if(!js.data && js.data !== "") { return d; }
+ d = $("<li>");
+ if(js.attr) { d.attr(js.attr); }
+ if(js.metadata) { d.data("jstree", js.metadata); }
+ if(js.state) { d.addClass("jstree-" + js.state); }
+ if(!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); }
+ $.each(js.data, function (i, m) {
+ tmp = $("<a>");
+ if($.isFunction(m)) { m = m.call(this, js); }
+ if(typeof m == "string") { tmp.attr('href','#')[ t ? "html" : "text" ](m); }
+ else {
+ if(!m.attr) { m.attr = {}; }
+ if(!m.attr.href) { m.attr.href = '#'; }
+ tmp.attr(m.attr)[ t ? "html" : "text" ](m.title);
+ if(m.language) { tmp.addClass(m.language); }
+ }
+ tmp.prepend("<ins class='jstree-icon'>&#160;</ins>");
+ if(!m.icon && js.icon) { m.icon = js.icon; }
+ if(m.icon) {
+ if(m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); }
+ else { tmp.children("ins").css("background","url('" + m.icon + "') center center no-repeat"); }
+ }
+ d.append(tmp);
+ });
+ d.prepend("<ins class='jstree-icon'>&#160;</ins>");
+ if(js.children) {
+ if(s.progressive_render && js.state !== "open") {
+ d.addClass("jstree-closed").data("jstree-children", js.children);
+ }
+ else {
+ if($.isFunction(js.children)) {
+ js.children = js.children.call(this, js);
+ }
+ if($.isArray(js.children) && js.children.length) {
+ tmp = this._parse_json(js.children, true);
+ if(tmp.length) {
+ ul2 = $("<ul>");
+ ul2.append(tmp);
+ d.append(ul2);
+ }
+ }
+ }
+ }
+ }
+ if(!is_callback) {
+ ul1 = $("<ul>");
+ ul1.append(d);
+ d = ul1;
+ }
+ return d;
+ },
+ get_json : function (obj, li_attr, a_attr, is_callback) {
+ var result = [],
+ s = this._get_settings(),
+ _this = this,
+ tmp1, tmp2, li, a, t, lang;
+ obj = this._get_node(obj);
+ if(!obj || obj === -1) { obj = this.get_container().find("> ul > li"); }
+ li_attr = $.isArray(li_attr) ? li_attr : [ "id", "class" ];
+ if(!is_callback && this.data.types) { li_attr.push(s.types.type_attr); }
+ a_attr = $.isArray(a_attr) ? a_attr : [ ];
+
+ obj.each(function () {
+ li = $(this);
+ tmp1 = { data : [] };
+ if(li_attr.length) { tmp1.attr = { }; }
+ $.each(li_attr, function (i, v) {
+ tmp2 = li.attr(v);
+ if(tmp2 && tmp2.length && tmp2.replace(/jstree[^ ]*|$/ig,'').length) {
+ tmp1.attr[v] = tmp2.replace(/jstree[^ ]*|$/ig,'');
+ }
+ });
+ if(li.hasClass("jstree-open")) { tmp1.state = "open"; }
+ if(li.hasClass("jstree-closed")) { tmp1.state = "closed"; }
+ a = li.children("a");
+ a.each(function () {
+ t = $(this);
+ if(
+ a_attr.length ||
+ $.inArray("languages", s.plugins) !== -1 ||
+ t.children("ins").get(0).style.backgroundImage.length ||
+ (t.children("ins").get(0).className && t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').length)
+ ) {
+ lang = false;
+ if($.inArray("languages", s.plugins) !== -1 && $.isArray(s.languages) && s.languages.length) {
+ $.each(s.languages, function (l, lv) {
+ if(t.hasClass(lv)) {
+ lang = lv;
+ return false;
+ }
+ });
+ }
+ tmp2 = { attr : { }, title : _this.get_text(t, lang) };
+ $.each(a_attr, function (k, z) {
+ tmp1.attr[z] = (t.attr(z) || "").replace(/jstree[^ ]*|$/ig,'');
+ });
+ $.each(s.languages, function (k, z) {
+ if(t.hasClass(z)) { tmp2.language = z; return true; }
+ });
+ if(t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"").length) {
+ tmp2.icon = t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"");
+ }
+ if(t.children("ins").get(0).style.backgroundImage.length) {
+ tmp2.icon = t.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")","");
+ }
+ }
+ else {
+ tmp2 = _this.get_text(t);
+ }
+ if(a.length > 1) { tmp1.data.push(tmp2); }
+ else { tmp1.data = tmp2; }
+ });
+ li = li.find("> ul > li");
+ if(li.length) { tmp1.children = _this.get_json(li, li_attr, a_attr, true); }
+ result.push(tmp1);
+ });
+ return result;
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree languages plugin 1.0
+ * Adds support for multiple language versions in one tree
+ * This basically allows for many titles coexisting in one node, but only one of them being visible at any given time
+ * This is useful for maintaining the same structure in many languages (hence the name of the plugin)
+ */
+(function ($) {
+ $.jstree.plugin("languages", {
+ __init : function () { this._load_css(); },
+ defaults : [],
+ _fn : {
+ set_lang : function (i) {
+ var langs = this._get_settings().languages,
+ st = false,
+ selector = ".jstree-" + this.get_index() + ' a';
+ if(!$.isArray(langs) || langs.length === 0) { return false; }
+ if($.inArray(i,langs) == -1) {
+ if(!!langs[i]) { i = langs[i]; }
+ else { return false; }
+ }
+ if(i == this.data.languages.current_language) { return true; }
+ st = $.vakata.css.get_css(selector + "." + this.data.languages.current_language, false, this.data.languages.language_css);
+ if(st !== false) { st.style.display = "none"; }
+ st = $.vakata.css.get_css(selector + "." + i, false, this.data.languages.language_css);
+ if(st !== false) { st.style.display = ""; }
+ this.data.languages.current_language = i;
+ this.__callback(i);
+ return true;
+ },
+ get_lang : function () {
+ return this.data.languages.current_language;
+ },
+ get_text : function (obj, lang) {
+ obj = this._get_node(obj) || this.data.ui.last_selected;
+ if(!obj.size()) { return false; }
+ var langs = this._get_settings().languages,
+ s = this._get_settings().core.html_titles;
+ if($.isArray(langs) && langs.length) {
+ lang = (lang && $.inArray(lang,langs) != -1) ? lang : this.data.languages.current_language;
+ obj = obj.children("a." + lang);
+ }
+ else { obj = obj.children("a:eq(0)"); }
+ if(s) {
+ obj = obj.clone();
+ obj.children("INS").remove();
+ return obj.html();
+ }
+ else {
+ obj = obj.contents().filter(function() { return this.nodeType == 3; })[0];
+ return obj.nodeValue;
+ }
+ },
+ set_text : function (obj, val, lang) {
+ obj = this._get_node(obj) || this.data.ui.last_selected;
+ if(!obj.size()) { return false; }
+ var langs = this._get_settings().languages,
+ s = this._get_settings().core.html_titles,
+ tmp;
+ if($.isArray(langs) && langs.length) {
+ lang = (lang && $.inArray(lang,langs) != -1) ? lang : this.data.languages.current_language;
+ obj = obj.children("a." + lang);
+ }
+ else { obj = obj.children("a:eq(0)"); }
+ if(s) {
+ tmp = obj.children("INS").clone();
+ obj.html(val).prepend(tmp);
+ this.__callback({ "obj" : obj, "name" : val, "lang" : lang });
+ return true;
+ }
+ else {
+ obj = obj.contents().filter(function() { return this.nodeType == 3; })[0];
+ this.__callback({ "obj" : obj, "name" : val, "lang" : lang });
+ return (obj.nodeValue = val);
+ }
+ },
+ _load_css : function () {
+ var langs = this._get_settings().languages,
+ str = "/* languages css */",
+ selector = ".jstree-" + this.get_index() + ' a',
+ ln;
+ if($.isArray(langs) && langs.length) {
+ this.data.languages.current_language = langs[0];
+ for(ln = 0; ln < langs.length; ln++) {
+ str += selector + "." + langs[ln] + " {";
+ if(langs[ln] != this.data.languages.current_language) { str += " display:none; "; }
+ str += " } ";
+ }
+ this.data.languages.language_css = $.vakata.css.add_sheet({ 'str' : str });
+ }
+ },
+ create_node : function (obj, position, js, callback) {
+ var t = this.__call_old(true, obj, position, js, function (t) {
+ var langs = this._get_settings().languages,
+ a = t.children("a"),
+ ln;
+ if($.isArray(langs) && langs.length) {
+ for(ln = 0; ln < langs.length; ln++) {
+ if(!a.is("." + langs[ln])) {
+ t.append(a.eq(0).clone().removeClass(langs.join(" ")).addClass(langs[ln]));
+ }
+ }
+ a.not("." + langs.join(", .")).remove();
+ }
+ if(callback) { callback.call(this, t); }
+ });
+ return t;
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree cookies plugin 1.0
+ * Stores the currently opened/selected nodes in a cookie and then restores them
+ * Depends on the jquery.cookie plugin
+ */
+(function ($) {
+ $.jstree.plugin("cookies", {
+ __init : function () {
+ if(typeof $.cookie === "undefined") { throw "jsTree cookie: jQuery cookie plugin not included."; }
+
+ var s = this._get_settings().cookies,
+ tmp;
+ if(!!s.save_opened) {
+ tmp = $.cookie(s.save_opened);
+ if(tmp && tmp.length) { this.data.core.to_open = tmp.split(","); }
+ }
+ if(!!s.save_selected) {
+ tmp = $.cookie(s.save_selected);
+ if(tmp && tmp.length && this.data.ui) { this.data.ui.to_select = tmp.split(","); }
+ }
+ this.get_container()
+ .one( ( this.data.ui ? "reselect" : "reopen" ) + ".jstree", $.proxy(function () {
+ this.get_container()
+ .bind("open_node.jstree close_node.jstree select_node.jstree deselect_node.jstree", $.proxy(function (e) {
+ if(this._get_settings().cookies.auto_save) { this.save_cookie((e.handleObj.namespace + e.handleObj.type).replace("jstree","")); }
+ }, this));
+ }, this));
+ },
+ defaults : {
+ save_opened : "jstree_open",
+ save_selected : "jstree_select",
+ auto_save : true,
+ cookie_options : {}
+ },
+ _fn : {
+ save_cookie : function (c) {
+ if(this.data.core.refreshing) { return; }
+ var s = this._get_settings().cookies;
+ if(!c) { // if called manually and not by event
+ if(s.save_opened) {
+ this.save_opened();
+ $.cookie(s.save_opened, this.data.core.to_open.join(","), s.cookie_options);
+ }
+ if(s.save_selected && this.data.ui) {
+ this.save_selected();
+ $.cookie(s.save_selected, this.data.ui.to_select.join(","), s.cookie_options);
+ }
+ return;
+ }
+ switch(c) {
+ case "open_node":
+ case "close_node":
+ if(!!s.save_opened) {
+ this.save_opened();
+ $.cookie(s.save_opened, this.data.core.to_open.join(","), s.cookie_options);
+ }
+ break;
+ case "select_node":
+ case "deselect_node":
+ if(!!s.save_selected && this.data.ui) {
+ this.save_selected();
+ $.cookie(s.save_selected, this.data.ui.to_select.join(","), s.cookie_options);
+ }
+ break;
+ }
+ }
+ }
+ });
+ // include cookies by default
+ $.jstree.defaults.plugins.push("cookies");
+})(jQuery);
+//*/
+
+/*
+ * jsTree sort plugin 1.0
+ * Sorts items alphabetically (or using any other function)
+ */
+(function ($) {
+ $.jstree.plugin("sort", {
+ __init : function () {
+ this.get_container()
+ .bind("load_node.jstree", $.proxy(function (e, data) {
+ var obj = this._get_node(data.rslt.obj);
+ obj = obj === -1 ? this.get_container().children("ul") : obj.children("ul");
+ this.sort(obj);
+ }, this))
+ .bind("rename_node.jstree", $.proxy(function (e, data) {
+ this.sort(data.rslt.obj.parent());
+ }, this))
+ .bind("move_node.jstree", $.proxy(function (e, data) {
+ var m = data.rslt.np == -1 ? this.get_container() : data.rslt.np;
+ this.sort(m.children("ul"));
+ }, this));
+ },
+ defaults : function (a, b) { return this.get_text(a) > this.get_text(b) ? 1 : -1; },
+ _fn : {
+ sort : function (obj) {
+ var s = this._get_settings().sort,
+ t = this;
+ obj.append($.makeArray(obj.children("li")).sort($.proxy(s, t)));
+ obj.find("> li > ul").each(function() { t.sort($(this)); });
+ this.clean_node(obj);
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree DND plugin 1.0
+ * Drag and drop plugin for moving/copying nodes
+ */
+(function ($) {
+ var o = false,
+ r = false,
+ m = false,
+ sli = false,
+ sti = false,
+ dir1 = false,
+ dir2 = false;
+ $.vakata.dnd = {
+ is_down : false,
+ is_drag : false,
+ helper : false,
+ scroll_spd : 10,
+ init_x : 0,
+ init_y : 0,
+ threshold : 5,
+ user_data : {},
+
+ drag_start : function (e, data, html) {
+ if($.vakata.dnd.is_drag) { $.vakata.drag_stop({}); }
+ try {
+ e.currentTarget.unselectable = "on";
+ e.currentTarget.onselectstart = function() { return false; };
+ if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; }
+ } catch(err) { }
+ $.vakata.dnd.init_x = e.pageX;
+ $.vakata.dnd.init_y = e.pageY;
+ $.vakata.dnd.user_data = data;
+ $.vakata.dnd.is_down = true;
+ $.vakata.dnd.helper = $("<div id='vakata-dragged'>").html(html).css("opacity", "0.75");
+ $(document).bind("mousemove", $.vakata.dnd.drag);
+ $(document).bind("mouseup", $.vakata.dnd.drag_stop);
+ return false;
+ },
+ drag : function (e) {
+ if(!$.vakata.dnd.is_down) { return; }
+ if(!$.vakata.dnd.is_drag) {
+ if(Math.abs(e.pageX - $.vakata.dnd.init_x) > 5 || Math.abs(e.pageY - $.vakata.dnd.init_y) > 5) {
+ $.vakata.dnd.helper.appendTo("body");
+ $.vakata.dnd.is_drag = true;
+ $(document).triggerHandler("drag_start.vakata", { "event" : e, "data" : $.vakata.dnd.user_data });
+ }
+ else { return; }
+ }
+
+ // maybe use a scrolling parent element instead of document?
+ if(e.type === "mousemove") { // thought of adding scroll in order to move the helper, but mouse poisition is n/a
+ var d = $(document), t = d.scrollTop(), l = d.scrollLeft();
+ if(e.pageY - t < 20) {
+ if(sti && dir1 === "down") { clearInterval(sti); sti = false; }
+ if(!sti) { dir1 = "up"; sti = setInterval(function () { $(document).scrollTop($(document).scrollTop() - $.vakata.dnd.scroll_spd); }, 150); }
+ }
+ else {
+ if(sti && dir1 === "up") { clearInterval(sti); sti = false; }
+ }
+ if($(window).height() - (e.pageY - t) < 20) {
+ if(sti && dir1 === "up") { clearInterval(sti); sti = false; }
+ if(!sti) { dir1 = "down"; sti = setInterval(function () { $(document).scrollTop($(document).scrollTop() + $.vakata.dnd.scroll_spd); }, 150); }
+ }
+ else {
+ if(sti && dir1 === "down") { clearInterval(sti); sti = false; }
+ }
+
+ if(e.pageX - l < 20) {
+ if(sli && dir2 === "right") { clearInterval(sli); sli = false; }
+ if(!sli) { dir2 = "left"; sli = setInterval(function () { $(document).scrollLeft($(document).scrollLeft() - $.vakata.dnd.scroll_spd); }, 150); }
+ }
+ else {
+ if(sli && dir2 === "left") { clearInterval(sli); sli = false; }
+ }
+ if($(window).width() - (e.pageX - l) < 20) {
+ if(sli && dir2 === "left") { clearInterval(sli); sli = false; }
+ if(!sli) { dir2 = "right"; sli = setInterval(function () { $(document).scrollLeft($(document).scrollLeft() + $.vakata.dnd.scroll_spd); }, 150); }
+ }
+ else {
+ if(sli && dir2 === "right") { clearInterval(sli); sli = false; }
+ }
+ }
+
+ $.vakata.dnd.helper.css({ left : (e.pageX + 5) + "px", top : (e.pageY + 10) + "px" });
+ $(document).triggerHandler("drag.vakata", { "event" : e, "data" : $.vakata.dnd.user_data });
+ },
+ drag_stop : function (e) {
+ $(document).unbind("mousemove", $.vakata.dnd.drag);
+ $(document).unbind("mouseup", $.vakata.dnd.drag_stop);
+ $(document).triggerHandler("drag_stop.vakata", { "event" : e, "data" : $.vakata.dnd.user_data });
+ $.vakata.dnd.helper.remove();
+ $.vakata.dnd.init_x = 0;
+ $.vakata.dnd.init_y = 0;
+ $.vakata.dnd.user_data = {};
+ $.vakata.dnd.is_down = false;
+ $.vakata.dnd.is_drag = false;
+ }
+ };
+ $(function() {
+ var css_string = '#vakata-dragged { display:block; margin:0 0 0 0; padding:4px 4px 4px 24px; position:absolute; top:-2000px; line-height:16px; z-index:10000; } ';
+ $.vakata.css.add_sheet({ str : css_string });
+ });
+
+ $.jstree.plugin("dnd", {
+ __init : function () {
+ this.data.dnd = {
+ active : false,
+ after : false,
+ inside : false,
+ before : false,
+ off : false,
+ prepared : false,
+ w : 0,
+ to1 : false,
+ to2 : false,
+ cof : false,
+ cw : false,
+ ch : false,
+ i1 : false,
+ i2 : false
+ };
+ this.get_container()
+ .bind("mouseenter.jstree", $.proxy(function () {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree && this.data.themes) {
+ m.attr("class", "jstree-" + this.data.themes.theme);
+ $.vakata.dnd.helper.attr("class", "jstree-dnd-helper jstree-" + this.data.themes.theme);
+ }
+ }, this))
+ .bind("mouseleave.jstree", $.proxy(function () {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) {
+ if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); }
+ if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); }
+ }
+ }, this))
+ .bind("mousemove.jstree", $.proxy(function (e) {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) {
+ var cnt = this.get_container()[0];
+
+ // Horizontal scroll
+ if(e.pageX + 24 > this.data.dnd.cof.left + this.data.dnd.cw) {
+ if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); }
+ this.data.dnd.i1 = setInterval($.proxy(function () { this.scrollLeft += $.vakata.dnd.scroll_spd; }, cnt), 100);
+ }
+ else if(e.pageX - 24 < this.data.dnd.cof.left) {
+ if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); }
+ this.data.dnd.i1 = setInterval($.proxy(function () { this.scrollLeft -= $.vakata.dnd.scroll_spd; }, cnt), 100);
+ }
+ else {
+ if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); }
+ }
+
+ // Vertical scroll
+ if(e.pageY + 24 > this.data.dnd.cof.top + this.data.dnd.ch) {
+ if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); }
+ this.data.dnd.i2 = setInterval($.proxy(function () { this.scrollTop += $.vakata.dnd.scroll_spd; }, cnt), 100);
+ }
+ else if(e.pageY - 24 < this.data.dnd.cof.top) {
+ if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); }
+ this.data.dnd.i2 = setInterval($.proxy(function () { this.scrollTop -= $.vakata.dnd.scroll_spd; }, cnt), 100);
+ }
+ else {
+ if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); }
+ }
+
+ }
+ }, this))
+ .delegate("a", "mousedown.jstree", $.proxy(function (e) {
+ if(e.which === 1) {
+ this.start_drag(e.currentTarget, e);
+ return false;
+ }
+ }, this))
+ .delegate("a", "mouseenter.jstree", $.proxy(function (e) {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) {
+ this.dnd_enter(e.currentTarget);
+ }
+ }, this))
+ .delegate("a", "mousemove.jstree", $.proxy(function (e) {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) {
+ if(typeof this.data.dnd.off.top === "undefined") { this.data.dnd.off = $(e.target).offset(); }
+ this.data.dnd.w = (e.pageY - (this.data.dnd.off.top || 0)) % this.data.core.li_height;
+ if(this.data.dnd.w < 0) { this.data.dnd.w += this.data.core.li_height; }
+ this.dnd_show();
+ }
+ }, this))
+ .delegate("a", "mouseleave.jstree", $.proxy(function (e) {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) {
+ this.data.dnd.after = false;
+ this.data.dnd.before = false;
+ this.data.dnd.inside = false;
+ $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");
+ m.hide();
+ if(r && r[0] === e.target.parentNode) {
+ if(this.data.dnd.to1) {
+ clearTimeout(this.data.dnd.to1);
+ this.data.dnd.to1 = false;
+ }
+ if(this.data.dnd.to2) {
+ clearTimeout(this.data.dnd.to2);
+ this.data.dnd.to2 = false;
+ }
+ }
+ }
+ }, this))
+ .delegate("a", "mouseup.jstree", $.proxy(function (e) {
+ if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) {
+ this.dnd_finish(e);
+ }
+ }, this));
+
+ $(document)
+ .bind("drag_stop.vakata", $.proxy(function () {
+ this.data.dnd.after = false;
+ this.data.dnd.before = false;
+ this.data.dnd.inside = false;
+ this.data.dnd.off = false;
+ this.data.dnd.prepared = false;
+ this.data.dnd.w = false;
+ this.data.dnd.to1 = false;
+ this.data.dnd.to2 = false;
+ this.data.dnd.active = false;
+ this.data.dnd.foreign = false;
+ if(m) { m.css({ "top" : "-2000px" }); }
+ }, this))
+ .bind("drag_start.vakata", $.proxy(function (e, data) {
+ if(data.data.jstree) {
+ var et = $(data.event.target);
+ if(et.closest(".jstree").hasClass("jstree-" + this.get_index())) {
+ this.dnd_enter(et);
+ }
+ }
+ }, this));
+
+ var s = this._get_settings().dnd;
+ if(s.drag_target) {
+ $(document)
+ .delegate(s.drag_target, "mousedown.jstree", $.proxy(function (e) {
+ o = e.target;
+ $.vakata.dnd.drag_start(e, { jstree : true, obj : e.target }, "<ins class='jstree-icon'></ins>" + $(e.target).text() );
+ if(this.data.themes) {
+ m.attr("class", "jstree-" + this.data.themes.theme);
+ $.vakata.dnd.helper.attr("class", "jstree-dnd-helper jstree-" + this.data.themes.theme);
+ }
+ $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");
+ var cnt = this.get_container();
+ this.data.dnd.cof = cnt.offset();
+ this.data.dnd.cw = parseInt(cnt.width(),10);
+ this.data.dnd.ch = parseInt(cnt.height(),10);
+ this.data.dnd.foreign = true;
+ return false;
+ }, this));
+ }
+ if(s.drop_target) {
+ $(document)
+ .delegate(s.drop_target, "mouseenter.jstree", $.proxy(function (e) {
+ if(this.data.dnd.active && this._get_settings().dnd.drop_check.call(this, { "o" : o, "r" : $(e.target) })) {
+ $.vakata.dnd.helper.children("ins").attr("class","jstree-ok");
+ }
+ }, this))
+ .delegate(s.drop_target, "mouseleave.jstree", $.proxy(function (e) {
+ if(this.data.dnd.active) {
+ $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");
+ }
+ }, this))
+ .delegate(s.drop_target, "mouseup.jstree", $.proxy(function (e) {
+ if(this.data.dnd.active && $.vakata.dnd.helper.children("ins").hasClass("jstree-ok")) {
+ this._get_settings().dnd.drop_finish.call(this, { "o" : o, "r" : $(e.target) });
+ }
+ }, this));
+ }
+ },
+ defaults : {
+ copy_modifier : "ctrl",
+ check_timeout : 200,
+ open_timeout : 500,
+ drop_target : ".jstree-drop",
+ drop_check : function (data) { return true; },
+ drop_finish : $.noop,
+ drag_target : ".jstree-draggable",
+ drag_finish : $.noop,
+ drag_check : function (data) { return { after : false, before : false, inside : true }; }
+ },
+ _fn : {
+ dnd_prepare : function () {
+ if(!r || !r.length) { return; }
+ this.data.dnd.off = r.offset();
+ if(this._get_settings().core.rtl) {
+ this.data.dnd.off.right = this.data.dnd.off.left + r.width();
+ }
+ if(this.data.dnd.foreign) {
+ var a = this._get_settings().dnd.drag_check.call(this, { "o" : o, "r" : r });
+ this.data.dnd.after = a.after;
+ this.data.dnd.before = a.before;
+ this.data.dnd.inside = a.inside;
+ this.data.dnd.prepared = true;
+ return this.dnd_show();
+ }
+ this.prepare_move(o, r, "before");
+ this.data.dnd.before = this.check_move();
+ this.prepare_move(o, r, "after");
+ this.data.dnd.after = this.check_move();
+ if(this._is_loaded(r)) {
+ this.prepare_move(o, r, "inside");
+ this.data.dnd.inside = this.check_move();
+ }
+ else {
+ this.data.dnd.inside = false;
+ }
+ this.data.dnd.prepared = true;
+ return this.dnd_show();
+ },
+ dnd_show : function () {
+ if(!this.data.dnd.prepared) { return; }
+ var o = ["before","inside","after"],
+ r = false,
+ rtl = this._get_settings().core.rtl,
+ pos;
+ if(this.data.dnd.w < this.data.core.li_height/3) { o = ["before","inside","after"]; }
+ else if(this.data.dnd.w <= this.data.core.li_height*2/3) {
+ o = this.data.dnd.w < this.data.core.li_height/2 ? ["inside","before","after"] : ["inside","after","before"];
+ }
+ else { o = ["after","inside","before"]; }
+ $.each(o, $.proxy(function (i, val) {
+ if(this.data.dnd[val]) {
+ $.vakata.dnd.helper.children("ins").attr("class","jstree-ok");
+ r = val;
+ return false;
+ }
+ }, this));
+ if(r === false) { $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid"); }
+
+ pos = rtl ? (this.data.dnd.off.right - 18) : (this.data.dnd.off.left + 10);
+ switch(r) {
+ case "before":
+ m.css({ "left" : pos + "px", "top" : (this.data.dnd.off.top - 6) + "px" }).show();
+ break;
+ case "after":
+ m.css({ "left" : pos + "px", "top" : (this.data.dnd.off.top + this.data.core.li_height - 7) + "px" }).show();
+ break;
+ case "inside":
+ m.css({ "left" : pos + ( rtl ? -4 : 4) + "px", "top" : (this.data.dnd.off.top + this.data.core.li_height/2 - 5) + "px" }).show();
+ break;
+ default:
+ m.hide();
+ break;
+ }
+ return r;
+ },
+ dnd_open : function () {
+ this.data.dnd.to2 = false;
+ this.open_node(r, $.proxy(this.dnd_prepare,this), true);
+ },
+ dnd_finish : function (e) {
+ if(this.data.dnd.foreign) {
+ if(this.data.dnd.after || this.data.dnd.before || this.data.dnd.inside) {
+ this._get_settings().dnd.drag_finish.call(this, { "o" : o, "r" : r });
+ }
+ }
+ else {
+ this.dnd_prepare();
+ this.move_node(o, r, this.dnd_show(), e[this._get_settings().dnd.copy_modifier + "Key"]);
+ }
+ o = false;
+ r = false;
+ m.hide();
+ },
+ dnd_enter : function (obj) {
+ var s = this._get_settings().dnd;
+ this.data.dnd.prepared = false;
+ r = this._get_node(obj);
+ if(s.check_timeout) {
+ // do the calculations after a minimal timeout (users tend to drag quickly to the desired location)
+ if(this.data.dnd.to1) { clearTimeout(this.data.dnd.to1); }
+ this.data.dnd.to1 = setTimeout($.proxy(this.dnd_prepare, this), s.check_timeout);
+ }
+ else {
+ this.dnd_prepare();
+ }
+ if(s.open_timeout) {
+ if(this.data.dnd.to2) { clearTimeout(this.data.dnd.to2); }
+ if(r && r.length && r.hasClass("jstree-closed")) {
+ // if the node is closed - open it, then recalculate
+ this.data.dnd.to2 = setTimeout($.proxy(this.dnd_open, this), s.open_timeout);
+ }
+ }
+ else {
+ if(r && r.length && r.hasClass("jstree-closed")) {
+ this.dnd_open();
+ }
+ }
+ },
+ start_drag : function (obj, e) {
+ o = this._get_node(obj);
+ if(this.data.ui && this.is_selected(o)) { o = this._get_node(null, true); }
+ $.vakata.dnd.drag_start(e, { jstree : true, obj : o }, "<ins class='jstree-icon'></ins>" + (o.length > 1 ? "Multiple selection" : this.get_text(o)) );
+ if(this.data.themes) {
+ m.attr("class", "jstree-" + this.data.themes.theme);
+ $.vakata.dnd.helper.attr("class", "jstree-dnd-helper jstree-" + this.data.themes.theme);
+ }
+ var cnt = this.get_container();
+ this.data.dnd.cof = cnt.children("ul").offset();
+ this.data.dnd.cw = parseInt(cnt.width(),10);
+ this.data.dnd.ch = parseInt(cnt.height(),10);
+ this.data.dnd.active = true;
+ }
+ }
+ });
+ $(function() {
+ var css_string = '' +
+ '#vakata-dragged ins { display:block; text-decoration:none; width:16px; height:16px; margin:0 0 0 0; padding:0; position:absolute; top:4px; left:4px; } ' +
+ '#vakata-dragged .jstree-ok { background:green; } ' +
+ '#vakata-dragged .jstree-invalid { background:red; } ' +
+ '#jstree-marker { padding:0; margin:0; line-height:12px; font-size:1px; overflow:hidden; height:12px; width:8px; position:absolute; top:-30px; z-index:10000; background-repeat:no-repeat; display:none; background-color:silver; } ';
+ $.vakata.css.add_sheet({ str : css_string });
+ m = $("<div>").attr({ id : "jstree-marker" }).hide().appendTo("body");
+ $(document).bind("drag_start.vakata", function (e, data) {
+ if(data.data.jstree) {
+ m.show();
+ }
+ });
+ $(document).bind("drag_stop.vakata", function (e, data) {
+ if(data.data.jstree) { m.hide(); }
+ });
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree checkbox plugin 1.0
+ * Inserts checkboxes in front of every node
+ * Depends on the ui plugin
+ * DOES NOT WORK NICELY WITH MULTITREE DRAG'N'DROP
+ */
+(function ($) {
+ $.jstree.plugin("checkbox", {
+ __init : function () {
+ this.select_node = this.deselect_node = this.deselect_all = $.noop;
+ this.get_selected = this.get_checked;
+
+ this.get_container()
+ .bind("open_node.jstree create_node.jstree clean_node.jstree", $.proxy(function (e, data) {
+ this._prepare_checkboxes(data.rslt.obj);
+ }, this))
+ .bind("loaded.jstree", $.proxy(function (e) {
+ this._prepare_checkboxes();
+ }, this))
+ .delegate("a", "click.jstree", $.proxy(function (e) {
+ if(this._get_node(e.target).hasClass("jstree-checked")) { this.uncheck_node(e.target); }
+ else { this.check_node(e.target); }
+ if(this.data.ui) { this.save_selected(); }
+ if(this.data.cookies) { this.save_cookie("select_node"); }
+ e.preventDefault();
+ }, this));
+ },
+ __destroy : function () {
+ this.get_container().find(".jstree-checkbox").remove();
+ },
+ _fn : {
+ _prepare_checkboxes : function (obj) {
+ obj = !obj || obj == -1 ? this.get_container() : this._get_node(obj);
+ var c, _this = this, t;
+ obj.each(function () {
+ t = $(this);
+ c = t.is("li") && t.hasClass("jstree-checked") ? "jstree-checked" : "jstree-unchecked";
+ t.find("a").not(":has(.jstree-checkbox)").prepend("<ins class='jstree-checkbox'>&#160;</ins>").parent().not(".jstree-checked, .jstree-unchecked").addClass(c);
+ });
+ if(obj.is("li")) { this._repair_state(obj); }
+ else { obj.find("> ul > li").each(function () { _this._repair_state(this); }); }
+ },
+ change_state : function (obj, state) {
+ obj = this._get_node(obj);
+ state = (state === false || state === true) ? state : obj.hasClass("jstree-checked");
+ if(state) { obj.find("li").andSelf().removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked"); }
+ else {
+ obj.find("li").andSelf().removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked");
+ if(this.data.ui) { this.data.ui.last_selected = obj; }
+ this.data.checkbox.last_selected = obj;
+ }
+ obj.parentsUntil(".jstree", "li").each(function () {
+ var $this = $(this);
+ if(state) {
+ if($this.children("ul").children(".jstree-checked, .jstree-undetermined").length) {
+ $this.parentsUntil(".jstree", "li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");
+ return false;
+ }
+ else {
+ $this.removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked");
+ }
+ }
+ else {
+ if($this.children("ul").children(".jstree-unchecked, .jstree-undetermined").length) {
+ $this.parentsUntil(".jstree", "li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");
+ return false;
+ }
+ else {
+ $this.removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked");
+ }
+ }
+ });
+ if(this.data.ui) { this.data.ui.selected = this.get_checked(); }
+ this.__callback(obj);
+ },
+ check_node : function (obj) {
+ this.change_state(obj, false);
+ },
+ uncheck_node : function (obj) {
+ this.change_state(obj, true);
+ },
+ check_all : function () {
+ var _this = this;
+ this.get_container().children("ul").children("li").each(function () {
+ _this.check_node(this, false);
+ });
+ },
+ uncheck_all : function () {
+ var _this = this;
+ this.get_container().children("ul").children("li").each(function () {
+ _this.change_state(this, true);
+ });
+ },
+
+ is_checked : function(obj) {
+ obj = this._get_node(obj);
+ return obj.length ? obj.is(".jstree-checked") : false;
+ },
+ get_checked : function (obj) {
+ obj = !obj || obj === -1 ? this.get_container() : this._get_node(obj);
+ return obj.find("> ul > .jstree-checked, .jstree-undetermined > ul > .jstree-checked");
+ },
+ get_unchecked : function (obj) {
+ obj = !obj || obj === -1 ? this.get_container() : this._get_node(obj);
+ return obj.find("> ul > .jstree-unchecked, .jstree-undetermined > ul > .jstree-unchecked");
+ },
+
+ show_checkboxes : function () { this.get_container().children("ul").removeClass("jstree-no-checkboxes"); },
+ hide_checkboxes : function () { this.get_container().children("ul").addClass("jstree-no-checkboxes"); },
+
+ _repair_state : function (obj) {
+ obj = this._get_node(obj);
+ if(!obj.length) { return; }
+ var a = obj.find("> ul > .jstree-checked").length,
+ b = obj.find("> ul > .jstree-undetermined").length,
+ c = obj.find("> ul > li").length;
+
+ if(c === 0) { if(obj.hasClass("jstree-undetermined")) { this.check_node(obj); } }
+ else if(a === 0 && b === 0) { this.uncheck_node(obj); }
+ else if(a === c) { this.check_node(obj); }
+ else {
+ obj.parentsUntil(".jstree","li").removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");
+ }
+ },
+ reselect : function () {
+ if(this.data.ui) {
+ var _this = this,
+ s = this.data.ui.to_select;
+ s = $.map($.makeArray(s), function (n) { return "#" + n.toString().replace(/^#/,"").replace('\\/','/').replace('/','\\/'); });
+ this.deselect_all();
+ $.each(s, function (i, val) { _this.check_node(val); });
+ this.__callback();
+ }
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree XML 1.0
+ * The XML data store. Datastores are build by overriding the `load_node` and `_is_loaded` functions.
+ */
+(function ($) {
+ $.vakata.xslt = function (xml, xsl, callback) {
+ var rs = "", xm, xs, processor, support;
+ if(document.recalc) {
+ xm = document.createElement('xml');
+ xs = document.createElement('xml');
+ xm.innerHTML = xml;
+ xs.innerHTML = xsl;
+ $("body").append(xm).append(xs);
+ setTimeout( (function (xm, xs, callback) {
+ return function () {
+ callback.call(null, xm.transformNode(xs.XMLDocument));
+ setTimeout( (function (xm, xs) { return function () { jQuery("body").remove(xm).remove(xs); }; })(xm, xs), 200);
+ };
+ }) (xm, xs, callback), 100);
+ return true;
+ }
+ if(typeof window.DOMParser !== "undefined" && typeof window.XMLHttpRequest !== "undefined" && typeof window.XSLTProcessor !== "undefined") {
+ processor = new XSLTProcessor();
+ support = $.isFunction(processor.transformDocument) ? (typeof window.XMLSerializer !== "undefined") : true;
+ if(!support) { return false; }
+ xml = new DOMParser().parseFromString(xml, "text/xml");
+ xsl = new DOMParser().parseFromString(xsl, "text/xml");
+ if($.isFunction(processor.transformDocument)) {
+ rs = document.implementation.createDocument("", "", null);
+ processor.transformDocument(xml, xsl, rs, null);
+ callback.call(null, XMLSerializer().serializeToString(rs));
+ return true;
+ }
+ else {
+ processor.importStylesheet(xsl);
+ rs = processor.transformToFragment(xml, document);
+ callback.call(null, $("<div>").append(rs).html());
+ return true;
+ }
+ }
+ return false;
+ };
+ var xsl = {
+ 'nest' : '<?xml version="1.0" encoding="utf-8" ?>' +
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >' +
+ '<xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/html" />' +
+ '<xsl:template match="/">' +
+ ' <xsl:call-template name="nodes">' +
+ ' <xsl:with-param name="node" select="/root" />' +
+ ' </xsl:call-template>' +
+ '</xsl:template>' +
+ '<xsl:template name="nodes">' +
+ ' <xsl:param name="node" />' +
+ ' <ul>' +
+ ' <xsl:for-each select="$node/item">' +
+ ' <xsl:variable name="children" select="count(./item) &gt; 0" />' +
+ ' <li>' +
+ ' <xsl:attribute name="class">' +
+ ' <xsl:if test="position() = last()">jstree-last </xsl:if>' +
+ ' <xsl:choose>' +
+ ' <xsl:when test="@state = \'open\'">jstree-open </xsl:when>' +
+ ' <xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when>' +
+ ' <xsl:otherwise>jstree-leaf </xsl:otherwise>' +
+ ' </xsl:choose>' +
+ ' <xsl:value-of select="@class" />' +
+ ' </xsl:attribute>' +
+ ' <xsl:for-each select="@*">' +
+ ' <xsl:if test="name() != \'class\' and name() != \'state\' and name() != \'hasChildren\'">' +
+ ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' +
+ ' </xsl:if>' +
+ ' </xsl:for-each>' +
+ ' <ins class="jstree-icon"><xsl:text>&#xa0;</xsl:text></ins>' +
+ ' <xsl:for-each select="content/name">' +
+ ' <a>' +
+ ' <xsl:attribute name="href">' +
+ ' <xsl:choose>' +
+ ' <xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when>' +
+ ' <xsl:otherwise>#</xsl:otherwise>' +
+ ' </xsl:choose>' +
+ ' </xsl:attribute>' +
+ ' <xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute>' +
+ ' <xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute>' +
+ ' <xsl:for-each select="@*">' +
+ ' <xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'">' +
+ ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' +
+ ' </xsl:if>' +
+ ' </xsl:for-each>' +
+ ' <ins>' +
+ ' <xsl:attribute name="class">jstree-icon ' +
+ ' <xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if>' +
+ ' </xsl:attribute>' +
+ ' <xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if>' +
+ ' <xsl:text>&#xa0;</xsl:text>' +
+ ' </ins>' +
+ ' <xsl:value-of select="current()" />' +
+ ' </a>' +
+ ' </xsl:for-each>' +
+ ' <xsl:if test="$children or @hasChildren"><xsl:call-template name="nodes"><xsl:with-param name="node" select="current()" /></xsl:call-template></xsl:if>' +
+ ' </li>' +
+ ' </xsl:for-each>' +
+ ' </ul>' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>',
+
+ 'flat' : '<?xml version="1.0" encoding="utf-8" ?>' +
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >' +
+ '<xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/xml" />' +
+ '<xsl:template match="/">' +
+ ' <ul>' +
+ ' <xsl:for-each select="//item[not(@parent_id) or @parent_id=0 or not(@parent_id = //item/@id)]">' + /* the last `or` may be removed */
+ ' <xsl:call-template name="nodes">' +
+ ' <xsl:with-param name="node" select="." />' +
+ ' <xsl:with-param name="is_last" select="number(position() = last())" />' +
+ ' </xsl:call-template>' +
+ ' </xsl:for-each>' +
+ ' </ul>' +
+ '</xsl:template>' +
+ '<xsl:template name="nodes">' +
+ ' <xsl:param name="node" />' +
+ ' <xsl:param name="is_last" />' +
+ ' <xsl:variable name="children" select="count(//item[@parent_id=$node/attribute::id]) &gt; 0" />' +
+ ' <li>' +
+ ' <xsl:attribute name="class">' +
+ ' <xsl:if test="$is_last = true()">jstree-last </xsl:if>' +
+ ' <xsl:choose>' +
+ ' <xsl:when test="@state = \'open\'">jstree-open </xsl:when>' +
+ ' <xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when>' +
+ ' <xsl:otherwise>jstree-leaf </xsl:otherwise>' +
+ ' </xsl:choose>' +
+ ' <xsl:value-of select="@class" />' +
+ ' </xsl:attribute>' +
+ ' <xsl:for-each select="@*">' +
+ ' <xsl:if test="name() != \'parent_id\' and name() != \'hasChildren\' and name() != \'class\' and name() != \'state\'">' +
+ ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' +
+ ' </xsl:if>' +
+ ' </xsl:for-each>' +
+ ' <ins class="jstree-icon"><xsl:text>&#xa0;</xsl:text></ins>' +
+ ' <xsl:for-each select="content/name">' +
+ ' <a>' +
+ ' <xsl:attribute name="href">' +
+ ' <xsl:choose>' +
+ ' <xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when>' +
+ ' <xsl:otherwise>#</xsl:otherwise>' +
+ ' </xsl:choose>' +
+ ' </xsl:attribute>' +
+ ' <xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute>' +
+ ' <xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute>' +
+ ' <xsl:for-each select="@*">' +
+ ' <xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'">' +
+ ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' +
+ ' </xsl:if>' +
+ ' </xsl:for-each>' +
+ ' <ins>' +
+ ' <xsl:attribute name="class">jstree-icon ' +
+ ' <xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if>' +
+ ' </xsl:attribute>' +
+ ' <xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if>' +
+ ' <xsl:text>&#xa0;</xsl:text>' +
+ ' </ins>' +
+ ' <xsl:value-of select="current()" />' +
+ ' </a>' +
+ ' </xsl:for-each>' +
+ ' <xsl:if test="$children">' +
+ ' <ul>' +
+ ' <xsl:for-each select="//item[@parent_id=$node/attribute::id]">' +
+ ' <xsl:call-template name="nodes">' +
+ ' <xsl:with-param name="node" select="." />' +
+ ' <xsl:with-param name="is_last" select="number(position() = last())" />' +
+ ' </xsl:call-template>' +
+ ' </xsl:for-each>' +
+ ' </ul>' +
+ ' </xsl:if>' +
+ ' </li>' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>'
+ };
+ $.jstree.plugin("xml_data", {
+ defaults : {
+ data : false,
+ ajax : false,
+ xsl : "flat",
+ clean_node : false,
+ correct_state : true
+ },
+ _fn : {
+ load_node : function (obj, s_call, e_call) { var _this = this; this.load_node_xml(obj, function () { _this.__callback({ "obj" : obj }); s_call.call(this); }, e_call); },
+ _is_loaded : function (obj) {
+ var s = this._get_settings().xml_data;
+ obj = this._get_node(obj);
+ return obj == -1 || !obj || !s.ajax || obj.is(".jstree-open, .jstree-leaf") || obj.children("ul").children("li").size() > 0;
+ },
+ load_node_xml : function (obj, s_call, e_call) {
+ var s = this.get_settings().xml_data,
+ error_func = function () {},
+ success_func = function () {};
+
+ obj = this._get_node(obj);
+ if(obj && obj !== -1) {
+ if(obj.data("jstree-is-loading")) { return; }
+ else { obj.data("jstree-is-loading",true); }
+ }
+ switch(!0) {
+ case (!s.data && !s.ajax): throw "Neither data nor ajax settings supplied.";
+ case (!!s.data && !s.ajax) || (!!s.data && !!s.ajax && (!obj || obj === -1)):
+ if(!obj || obj == -1) {
+ this.parse_xml(s.data, $.proxy(function (d) {
+ if(d) {
+ d = d.replace(/ ?xmlns="[^"]*"/ig, "");
+ if(d.length > 10) {
+ d = $(d);
+ this.get_container().children("ul").empty().append(d.children());
+ if(s.clean_node) { this.clean_node(obj); }
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ else {
+ if(s.correct_state) {
+ this.get_container().children("ul").empty();
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ }, this));
+ }
+ break;
+ case (!s.data && !!s.ajax) || (!!s.data && !!s.ajax && obj && obj !== -1):
+ error_func = function (x, t, e) {
+ var ef = this.get_settings().xml_data.ajax.error;
+ if(ef) { ef.call(this, x, t, e); }
+ if(obj !== -1 && obj.length) {
+ obj.children(".jstree-loading").removeClass("jstree-loading");
+ obj.data("jstree-is-loading",false);
+ if(t === "success" && s.correct_state) { obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf"); }
+ }
+ else {
+ if(t === "success" && s.correct_state) { this.get_container().children("ul").empty(); }
+ }
+ if(e_call) { e_call.call(this); }
+ };
+ success_func = function (d, t, x) {
+ d = x.responseText;
+ var sf = this.get_settings().xml_data.ajax.success;
+ if(sf) { d = sf.call(this,d,t,x) || d; }
+ if(d == "") {
+ return error_func.call(this, x, t, "");
+ }
+ this.parse_xml(d, $.proxy(function (d) {
+ if(d) {
+ d = d.replace(/ ?xmlns="[^"]*"/ig, "");
+ if(d.length > 10) {
+ d = $(d);
+ if(obj === -1 || !obj) { this.get_container().children("ul").empty().append(d.children()); }
+ else { obj.children(".jstree-loading").removeClass("jstree-loading"); obj.append(d); obj.data("jstree-is-loading",false); }
+ if(s.clean_node) { this.clean_node(obj); }
+ if(s_call) { s_call.call(this); }
+ }
+ else {
+ if(obj && obj !== -1) {
+ obj.children(".jstree-loading").removeClass("jstree-loading");
+ obj.data("jstree-is-loading",false);
+ if(s.correct_state) {
+ obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ else {
+ if(s.correct_state) {
+ this.get_container().children("ul").empty();
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ }
+ }
+ }, this));
+ };
+ s.ajax.context = this;
+ s.ajax.error = error_func;
+ s.ajax.success = success_func;
+ if(!s.ajax.dataType) { s.ajax.dataType = "xml"; }
+ if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, obj); }
+ if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, obj); }
+ $.ajax(s.ajax);
+ break;
+ }
+ },
+ parse_xml : function (xml, callback) {
+ var s = this._get_settings().xml_data;
+ $.vakata.xslt(xml, xsl[s.xsl], callback);
+ },
+ get_xml : function (tp, obj, li_attr, a_attr, is_callback) {
+ var result = "",
+ s = this._get_settings(),
+ _this = this,
+ tmp1, tmp2, li, a, lang;
+ if(!tp) { tp = "flat"; }
+ if(!is_callback) { is_callback = 0; }
+ obj = this._get_node(obj);
+ if(!obj || obj === -1) { obj = this.get_container().find("> ul > li"); }
+ li_attr = $.isArray(li_attr) ? li_attr : [ "id", "class" ];
+ if(!is_callback && this.data.types && $.inArray(s.types.type_attr, li_attr) === -1) { li_attr.push(s.types.type_attr); }
+
+ a_attr = $.isArray(a_attr) ? a_attr : [ ];
+
+ if(!is_callback) { result += "<root>"; }
+ obj.each(function () {
+ result += "<item";
+ li = $(this);
+ $.each(li_attr, function (i, v) { result += " " + v + "=\"" + (li.attr(v) || "").replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"") + "\""; });
+ if(li.hasClass("jstree-open")) { result += " state=\"open\""; }
+ if(li.hasClass("jstree-closed")) { result += " state=\"closed\""; }
+ if(tp === "flat") { result += " parent_id=\"" + is_callback + "\""; }
+ result += ">";
+ result += "<content>";
+ a = li.children("a");
+ a.each(function () {
+ tmp1 = $(this);
+ lang = false;
+ result += "<name";
+ if($.inArray("languages", s.plugins) !== -1) {
+ $.each(s.languages, function (k, z) {
+ if(tmp1.hasClass(z)) { result += " lang=\"" + z + "\""; lang = z; return false; }
+ });
+ }
+ if(a_attr.length) {
+ $.each(a_attr, function (k, z) {
+ result += " " + z + "=\"" + (tmp1.attr(z) || "").replace(/jstree[^ ]*|$/ig,'') + "\"";
+ });
+ }
+ if(tmp1.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"").length) {
+ result += ' icon="' + tmp1.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"") + '"';
+ }
+ if(tmp1.children("ins").get(0).style.backgroundImage.length) {
+ result += ' icon="' + tmp1.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")","") + '"';
+ }
+ result += ">";
+ result += "<![CDATA[" + _this.get_text(tmp1, lang) + "]]>";
+ result += "</name>";
+ });
+ result += "</content>";
+ tmp2 = li[0].id;
+ li = li.find("> ul > li");
+ if(li.length) { tmp2 = _this.get_xml(tp, li, li_attr, a_attr, tmp2); }
+ else { tmp2 = ""; }
+ if(tp == "nest") { result += tmp2; }
+ result += "</item>";
+ if(tp == "flat") { result += tmp2; }
+ });
+ if(!is_callback) { result += "</root>"; }
+ return result;
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree search plugin 1.0
+ * Enables both sync and async search on the tree
+ * DOES NOT WORK WITH JSON PROGRESSIVE RENDER
+ */
+(function ($) {
+ $.expr[':'].jstree_contains = function(a,i,m){
+ return (a.textContent || a.innerText || "").toLowerCase().indexOf(m[3].toLowerCase())>=0;
+ };
+ $.jstree.plugin("search", {
+ __init : function () {
+ this.data.search.str = "";
+ this.data.search.result = $();
+ },
+ defaults : {
+ ajax : false, // OR ajax object
+ case_insensitive : false
+ },
+ _fn : {
+ search : function (str, skip_async) {
+ if(str === "") { return; }
+ var s = this.get_settings().search,
+ t = this,
+ error_func = function () { },
+ success_func = function () { };
+ this.data.search.str = str;
+
+ if(!skip_async && s.ajax !== false && this.get_container().find(".jstree-closed:eq(0)").length > 0) {
+ this.search.supress_callback = true;
+ error_func = function () { };
+ success_func = function (d, t, x) {
+ var sf = this.get_settings().search.ajax.success;
+ if(sf) { d = sf.call(this,d,t,x) || d; }
+ this.data.search.to_open = d;
+ this._search_open();
+ };
+ s.ajax.context = this;
+ s.ajax.error = error_func;
+ s.ajax.success = success_func;
+ if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, str); }
+ if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, str); }
+ if(!s.ajax.data) { s.ajax.data = { "search_string" : str }; }
+ if(!s.ajax.dataType || /^json/.exec(s.ajax.dataType)) { s.ajax.dataType = "json"; }
+ $.ajax(s.ajax);
+ return;
+ }
+ if(this.data.search.result.length) { this.clear_search(); }
+ this.data.search.result = this.get_container().find("a" + (this.data.languages ? "." + this.get_lang() : "" ) + ":" + (s.case_insensitive ? "jstree_contains" : "contains") + "(" + this.data.search.str + ")");
+ this.data.search.result.addClass("jstree-search").parents(".jstree-closed").each(function () {
+ t.open_node(this, false, true);
+ });
+ this.__callback({ nodes : this.data.search.result, str : str });
+ },
+ clear_search : function (str) {
+ this.data.search.result.removeClass("jstree-search");
+ this.__callback(this.data.search.result);
+ this.data.search.result = $();
+ },
+ _search_open : function (is_callback) {
+ var _this = this,
+ done = true,
+ current = [],
+ remaining = [];
+ if(this.data.search.to_open.length) {
+ $.each(this.data.search.to_open, function (i, val) {
+ if(val == "#") { return true; }
+ if($(val).length && $(val).is(".jstree-closed")) { current.push(val); }
+ else { remaining.push(val); }
+ });
+ if(current.length) {
+ this.data.search.to_open = remaining;
+ $.each(current, function (i, val) {
+ _this.open_node(val, function () { _this._search_open(true); });
+ });
+ done = false;
+ }
+ }
+ if(done) { this.search(this.data.search.str, true); }
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree contextmenu plugin 1.0
+ */
+(function ($) {
+ $.vakata.context = {
+ cnt : $("<div id='vakata-contextmenu'/>"),
+ vis : false,
+ tgt : false,
+ par : false,
+ func : false,
+ data : false,
+ show : function (s, t, x, y, d, p) {
+ var html = $.vakata.context.parse(s), h, w;
+ if(!html) { return; }
+ $.vakata.context.vis = true;
+ $.vakata.context.tgt = t;
+ $.vakata.context.par = p || t || null;
+ $.vakata.context.data = d || null;
+ $.vakata.context.cnt
+ .html(html)
+ .css({ "visibility" : "hidden", "display" : "block", "left" : 0, "top" : 0 });
+ h = $.vakata.context.cnt.height();
+ w = $.vakata.context.cnt.width();
+ if(x + w > $(document).width()) {
+ x = $(document).width() - (w + 5);
+ $.vakata.context.cnt.find("li > ul").addClass("right");
+ }
+ if(y + h > $(document).height()) {
+ y = y - (h + t[0].offsetHeight);
+ $.vakata.context.cnt.find("li > ul").addClass("bottom");
+ }
+
+ $.vakata.context.cnt
+ .css({ "left" : x, "top" : y })
+ .find("li:has(ul)")
+ .bind("mouseenter", function (e) {
+ var w = $(document).width(),
+ h = $(document).height(),
+ ul = $(this).children("ul").show();
+ if(w !== $(document).width()) { ul.toggleClass("right"); }
+ if(h !== $(document).height()) { ul.toggleClass("bottom"); }
+ })
+ .bind("mouseleave", function (e) {
+ $(this).children("ul").hide();
+ })
+ .end()
+ .css({ "visibility" : "visible" })
+ .show();
+ $(document).triggerHandler("context_show.vakata");
+ },
+ hide : function () {
+ $.vakata.context.vis = false;
+ $.vakata.context.cnt.attr("class","").hide();
+ $(document).triggerHandler("context_hide.vakata");
+ },
+ parse : function (s, is_callback) {
+ if(!s) { return false; }
+ var str = "",
+ tmp = false,
+ was_sep = true;
+ if(!is_callback) { $.vakata.context.func = {}; }
+ str += "<ul>";
+ $.each(s, function (i, val) {
+ if(!val) { return true; }
+ $.vakata.context.func[i] = val.action;
+ if(!was_sep && val.separator_before) {
+ str += "<li class='vakata-separator vakata-separator-before'></li>";
+ }
+ was_sep = false;
+ str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";
+ if(val.icon && val.icon.indexOf("/") === -1) { str += " class='" + val.icon + "' "; }
+ if(val.icon && val.icon.indexOf("/") !== -1) { str += " style='background:url(" + val.icon + ") center center no-repeat;' "; }
+ str += ">&#160;</ins><a href='#' rel='" + i + "'>";
+ if(val.submenu) {
+ str += "<span style='float:right;'>&raquo;</span>";
+ }
+ str += val.label + "</a>";
+ if(val.submenu) {
+ tmp = $.vakata.context.parse(val.submenu, true);
+ if(tmp) { str += tmp; }
+ }
+ str += "</li>";
+ if(val.separator_after) {
+ str += "<li class='vakata-separator vakata-separator-after'></li>";
+ was_sep = true;
+ }
+ });
+ str = str.replace(/<li class\='vakata-separator vakata-separator-after'\><\/li\>$/,"");
+ str += "</ul>";
+ return str.length > 10 ? str : false;
+ },
+ exec : function (i) {
+ if($.isFunction($.vakata.context.func[i])) {
+ $.vakata.context.func[i].call($.vakata.context.data, $.vakata.context.par);
+ return true;
+ }
+ else { return false; }
+ }
+ };
+ $(function () {
+ var css_string = '' +
+ '#vakata-contextmenu { display:none; position:absolute; margin:0; padding:0; min-width:180px; background:#ebebeb; border:1px solid silver; z-index:10000; *width:180px; } ' +
+ '#vakata-contextmenu ul { min-width:180px; *width:180px; } ' +
+ '#vakata-contextmenu ul, #vakata-contextmenu li { margin:0; padding:0; list-style-type:none; display:block; } ' +
+ '#vakata-contextmenu li { line-height:20px; min-height:20px; position:relative; padding:0px; } ' +
+ '#vakata-contextmenu li a { padding:1px 6px; line-height:17px; display:block; text-decoration:none; margin:1px 1px 0 1px; } ' +
+ '#vakata-contextmenu li ins { float:left; width:16px; height:16px; text-decoration:none; margin-right:2px; } ' +
+ '#vakata-contextmenu li a:hover, #vakata-contextmenu li.vakata-hover > a { background:gray; color:white; } ' +
+ '#vakata-contextmenu li ul { display:none; position:absolute; top:-2px; left:100%; background:#ebebeb; border:1px solid gray; } ' +
+ '#vakata-contextmenu .right { right:100%; left:auto; } ' +
+ '#vakata-contextmenu .bottom { bottom:-1px; top:auto; } ' +
+ '#vakata-contextmenu li.vakata-separator { min-height:0; height:1px; line-height:1px; font-size:1px; overflow:hidden; margin:0 2px; background:silver; /* border-top:1px solid #fefefe; */ padding:0; } ';
+ $.vakata.css.add_sheet({ str : css_string });
+ $.vakata.context.cnt
+ .delegate("a","click", function (e) { e.preventDefault(); })
+ .delegate("a","mouseup", function (e) {
+ if(!$(this).parent().hasClass("jstree-contextmenu-disabled") && $.vakata.context.exec($(this).attr("rel"))) {
+ $.vakata.context.hide();
+ }
+ else { $(this).blur(); }
+ })
+ .delegate("a","mouseover", function () {
+ $.vakata.context.cnt.find(".vakata-hover").removeClass("vakata-hover");
+ })
+ .appendTo("body");
+ $(document).bind("mousedown", function (e) { if($.vakata.context.vis && !$.contains($.vakata.context.cnt[0], e.target)) { $.vakata.context.hide(); } });
+ if(typeof $.hotkeys !== "undefined") {
+ $(document)
+ .bind("keydown", "up", function (e) {
+ if($.vakata.context.vis) {
+ var o = $.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").prevAll("li:not(.vakata-separator)").first();
+ if(!o.length) { o = $.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").last(); }
+ o.addClass("vakata-hover");
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ }
+ })
+ .bind("keydown", "down", function (e) {
+ if($.vakata.context.vis) {
+ var o = $.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").nextAll("li:not(.vakata-separator)").first();
+ if(!o.length) { o = $.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").first(); }
+ o.addClass("vakata-hover");
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ }
+ })
+ .bind("keydown", "right", function (e) {
+ if($.vakata.context.vis) {
+ $.vakata.context.cnt.find(".vakata-hover").children("ul").show().children("li:not(.vakata-separator)").removeClass("vakata-hover").first().addClass("vakata-hover");
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ }
+ })
+ .bind("keydown", "left", function (e) {
+ if($.vakata.context.vis) {
+ $.vakata.context.cnt.find(".vakata-hover").children("ul").hide().children(".vakata-separator").removeClass("vakata-hover");
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ }
+ })
+ .bind("keydown", "esc", function (e) {
+ $.vakata.context.hide();
+ e.preventDefault();
+ })
+ .bind("keydown", "space", function (e) {
+ $.vakata.context.cnt.find(".vakata-hover").last().children("a").click();
+ e.preventDefault();
+ });
+ }
+ });
+
+ $.jstree.plugin("contextmenu", {
+ __init : function () {
+ this.get_container()
+ .delegate("a", "contextmenu.jstree", $.proxy(function (e) {
+ e.preventDefault();
+ this.show_contextmenu(e.currentTarget, e.pageX, e.pageY);
+ }, this))
+ .bind("destroy.jstree", $.proxy(function () {
+ if(this.data.contextmenu) {
+ $.vakata.context.hide();
+ }
+ }, this));
+ $(document).bind("context_hide.vakata", $.proxy(function () { this.data.contextmenu = false; }, this));
+ },
+ defaults : {
+ select_node : false, // requires UI plugin
+ show_at_node : true,
+ items : { // Could be a function that should return an object like this one
+ "create" : {
+ "separator_before" : false,
+ "separator_after" : true,
+ "label" : "Create",
+ "action" : function (obj) { this.create(obj); }
+ },
+ "rename" : {
+ "separator_before" : false,
+ "separator_after" : false,
+ "label" : "Rename",
+ "action" : function (obj) { this.rename(obj); }
+ },
+ "remove" : {
+ "separator_before" : false,
+ "icon" : false,
+ "separator_after" : false,
+ "label" : "Delete",
+ "action" : function (obj) { this.remove(obj); }
+ },
+ "ccp" : {
+ "separator_before" : true,
+ "icon" : false,
+ "separator_after" : false,
+ "label" : "Edit",
+ "action" : false,
+ "submenu" : {
+ "cut" : {
+ "separator_before" : false,
+ "separator_after" : false,
+ "label" : "Cut",
+ "action" : function (obj) { this.cut(obj); }
+ },
+ "copy" : {
+ "separator_before" : false,
+ "icon" : false,
+ "separator_after" : false,
+ "label" : "Copy",
+ "action" : function (obj) { this.copy(obj); }
+ },
+ "paste" : {
+ "separator_before" : false,
+ "icon" : false,
+ "separator_after" : false,
+ "label" : "Paste",
+ "action" : function (obj) { this.paste(obj); }
+ }
+ }
+ }
+ }
+ },
+ _fn : {
+ show_contextmenu : function (obj, x, y) {
+ obj = this._get_node(obj);
+ var s = this.get_settings().contextmenu,
+ a = obj.children("a:visible:eq(0)"),
+ o = false;
+ if(s.select_node && this.data.ui && !this.is_selected(obj)) {
+ this.deselect_all();
+ this.select_node(obj, true);
+ }
+ if(s.show_at_node || typeof x === "undefined" || typeof y === "undefined") {
+ o = a.offset();
+ x = o.left;
+ y = o.top + this.data.core.li_height;
+ }
+ if($.isFunction(s.items)) { s.items = s.items.call(this, obj); }
+ this.data.contextmenu = true;
+ $.vakata.context.show(s.items, a, x, y, this, obj);
+ if(this.data.themes) { $.vakata.context.cnt.attr("class", "jstree-" + this.data.themes.theme + "-context"); }
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree types plugin 1.0
+ * Adds support types of nodes
+ * You can set an attribute on each li node, that represents its type.
+ * According to the type setting the node may get custom icon/validation rules
+ */
+(function ($) {
+ $.jstree.plugin("types", {
+ __init : function () {
+ var s = this._get_settings().types;
+ this.data.types.attach_to = [];
+ this.get_container()
+ .bind("init.jstree", $.proxy(function () {
+ var types = s.types,
+ attr = s.type_attr,
+ icons_css = "",
+ _this = this;
+
+ $.each(types, function (i, tp) {
+ $.each(tp, function (k, v) {
+ if(!/^(max_depth|max_children|icon|valid_children)$/.test(k)) { _this.data.types.attach_to.push(k); }
+ });
+ if(!tp.icon) { return true; }
+ if( tp.icon.image || tp.icon.position) {
+ if(i == "default") { icons_css += '.jstree-' + _this.get_index() + ' a > .jstree-icon { '; }
+ else { icons_css += '.jstree-' + _this.get_index() + ' li[' + attr + '=' + i + '] > a > .jstree-icon { '; }
+ if(tp.icon.image) { icons_css += ' background-image:url(' + tp.icon.image + '); '; }
+ if(tp.icon.position){ icons_css += ' background-position:' + tp.icon.position + '; '; }
+ else { icons_css += ' background-position:0 0; '; }
+ icons_css += '} ';
+ }
+ });
+ if(icons_css != "") { $.vakata.css.add_sheet({ 'str' : icons_css }); }
+ }, this))
+ .bind("before.jstree", $.proxy(function (e, data) {
+ if($.inArray(data.func, this.data.types.attach_to) !== -1) {
+ var s = this._get_settings().types.types,
+ t = this._get_type(data.args[0]);
+ if(
+ (
+ (s[t] && typeof s[t][data.func] !== "undefined") ||
+ (s["default"] && typeof s["default"][data.func] !== "undefined")
+ ) && !this._check(data.func, data.args[0])
+ ) {
+ e.stopImmediatePropagation();
+ return false;
+ }
+ }
+ }, this));
+ },
+ defaults : {
+ // defines maximum number of root nodes (-1 means unlimited, -2 means disable max_children checking)
+ max_children : -1,
+ // defines the maximum depth of the tree (-1 means unlimited, -2 means disable max_depth checking)
+ max_depth : -1,
+ // defines valid node types for the root nodes
+ valid_children : "all",
+
+ // where is the type stores (the rel attribute of the LI element)
+ type_attr : "rel",
+ // a list of types
+ types : {
+ // the default type
+ "default" : {
+ "max_children" : -1,
+ "max_depth" : -1,
+ "valid_children": "all"
+
+ // Bound functions - you can bind any other function here (using boolean or function)
+ //"select_node" : true,
+ //"open_node" : true,
+ //"close_node" : true,
+ //"create_node" : true,
+ //"delete_node" : true
+ }
+ }
+ },
+ _fn : {
+ _get_type : function (obj) {
+ obj = this._get_node(obj);
+ return (!obj || !obj.length) ? false : obj.attr(this._get_settings().types.type_attr) || "default";
+ },
+ set_type : function (str, obj) {
+ obj = this._get_node(obj);
+ return (!obj.length || !str) ? false : obj.attr(this._get_settings().types.type_attr, str);
+ },
+ _check : function (rule, obj, opts) {
+ var v = false, t = this._get_type(obj), d = 0, _this = this, s = this._get_settings().types;
+ if(obj === -1) {
+ if(!!s[rule]) { v = s[rule]; }
+ else { return; }
+ }
+ else {
+ if(t === false) { return; }
+ if(!!s.types[t] && !!s.types[t][rule]) { v = s.types[t][rule]; }
+ else if(!!s.types["default"] && !!s.types["default"][rule]) { v = s.types["default"][rule]; }
+ }
+ if($.isFunction(v)) { v = v.call(this, obj); }
+ if(rule === "max_depth" && obj !== -1 && opts !== false && s.max_depth !== -2 && v !== 0) {
+ // also include the node itself - otherwise if root node it is not checked
+ this._get_node(obj).children("a:eq(0)").parentsUntil(".jstree","li").each(function (i) {
+ // check if current depth already exceeds global tree depth
+ if(s.max_depth !== -1 && s.max_depth - (i + 1) <= 0) { v = 0; return false; }
+ d = (i === 0) ? v : _this._check(rule, this, false);
+ // check if current node max depth is already matched or exceeded
+ if(d !== -1 && d - (i + 1) <= 0) { v = 0; return false; }
+ // otherwise - set the max depth to the current value minus current depth
+ if(d >= 0 && (d - (i + 1) < v || v < 0) ) { v = d - (i + 1); }
+ // if the global tree depth exists and it minus the nodes calculated so far is less than `v` or `v` is unlimited
+ if(s.max_depth >= 0 && (s.max_depth - (i + 1) < v || v < 0) ) { v = s.max_depth - (i + 1); }
+ });
+ }
+ return v;
+ },
+ check_move : function () {
+ if(!this.__call_old()) { return false; }
+ var m = this._get_move(),
+ s = m.rt._get_settings().types,
+ mc = m.rt._check("max_children", m.cr),
+ md = m.rt._check("max_depth", m.cr),
+ vc = m.rt._check("valid_children", m.cr),
+ ch = 0, d = 1, t;
+
+ if(vc === "none") { return false; }
+ if($.isArray(vc) && m.ot && m.ot._get_type) {
+ m.o.each(function () {
+ if($.inArray(m.ot._get_type(this), vc) === -1) { d = false; return false; }
+ });
+ if(d === false) { return false; }
+ }
+ if(s.max_children !== -2 && mc !== -1) {
+ ch = m.cr === -1 ? this.get_container().children("> ul > li").not(m.o).length : m.cr.children("> ul > li").not(m.o).length;
+ if(ch + m.o.length > mc) { return false; }
+ }
+ if(s.max_depth !== -2 && md !== -1) {
+ d = 0;
+ if(md === 0) { return false; }
+ if(typeof m.o.d === "undefined") {
+ // TODO: deal with progressive rendering and async when checking max_depth (how to know the depth of the moved node)
+ t = m.o;
+ while(t.length > 0) {
+ t = t.find("> ul > li");
+ d ++;
+ }
+ m.o.d = d;
+ }
+ if(md - m.o.d < 0) { return false; }
+ }
+ return true;
+ },
+ create_node : function (obj, position, js, callback, is_loaded, skip_check) {
+ if(!skip_check && (is_loaded || this._is_loaded(obj))) {
+ var p = (position && position.match(/^before|after$/i) && obj !== -1) ? this._get_parent(obj) : this._get_node(obj),
+ s = this._get_settings().types,
+ mc = this._check("max_children", p),
+ md = this._check("max_depth", p),
+ vc = this._check("valid_children", p),
+ ch;
+ if(!js) { js = {}; }
+ if(vc === "none") { return false; }
+ if($.isArray(vc)) {
+ if(!js.attr || !js.attr[s.type_attr]) {
+ if(!js.attr) { js.attr = {}; }
+ js.attr[s.type_attr] = vc[0];
+ }
+ else {
+ if($.inArray(js.attr[s.type_attr], vc) === -1) { return false; }
+ }
+ }
+ if(s.max_children !== -2 && mc !== -1) {
+ ch = p === -1 ? this.get_container().children("> ul > li").length : p.children("> ul > li").length;
+ if(ch + 1 > mc) { return false; }
+ }
+ if(s.max_depth !== -2 && md !== -1 && (md - 1) < 0) { return false; }
+ }
+ return this.__call_old(true, obj, position, js, callback, is_loaded, skip_check);
+ }
+ }
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree HTML data 1.0
+ * The HTML data store. Datastores are build by replacing the `load_node` and `_is_loaded` functions.
+ */
+(function ($) {
+ $.jstree.plugin("html_data", {
+ __init : function () {
+ // this used to use html() and clean the whitespace, but this way any attached data was lost
+ this.data.html_data.original_container_html = this.get_container().find(" > ul > li").clone(true);
+ // remove white space from LI node - otherwise nodes appear a bit to the right
+ this.data.html_data.original_container_html.find("li").andSelf().contents().filter(function() { return this.nodeType == 3; }).remove();
+ },
+ defaults : {
+ data : false,
+ ajax : false,
+ correct_state : true
+ },
+ _fn : {
+ load_node : function (obj, s_call, e_call) { var _this = this; this.load_node_html(obj, function () { _this.__callback({ "obj" : obj }); s_call.call(this); }, e_call); },
+ _is_loaded : function (obj) {
+ obj = this._get_node(obj);
+ return obj == -1 || !obj || !this._get_settings().html_data.ajax || obj.is(".jstree-open, .jstree-leaf") || obj.children("ul").children("li").size() > 0;
+ },
+ load_node_html : function (obj, s_call, e_call) {
+ var d,
+ s = this.get_settings().html_data,
+ error_func = function () {},
+ success_func = function () {};
+ obj = this._get_node(obj);
+ if(obj && obj !== -1) {
+ if(obj.data("jstree-is-loading")) { return; }
+ else { obj.data("jstree-is-loading",true); }
+ }
+ switch(!0) {
+ case (!s.data && !s.ajax):
+ if(!obj || obj == -1) {
+ this.get_container()
+ .children("ul").empty()
+ .append(this.data.html_data.original_container_html)
+ .find("li, a").filter(function () { return this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'>&#160;</ins>").end()
+ .filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon");
+ this.clean_node();
+ }
+ if(s_call) { s_call.call(this); }
+ break;
+ case (!!s.data && !s.ajax) || (!!s.data && !!s.ajax && (!obj || obj === -1)):
+ if(!obj || obj == -1) {
+ d = $(s.data);
+ if(!d.is("ul")) { d = $("<ul>").append(d); }
+ this.get_container()
+ .children("ul").empty().append(d.children())
+ .find("li, a").filter(function () { return this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'>&#160;</ins>").end()
+ .filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon");
+ this.clean_node();
+ }
+ if(s_call) { s_call.call(this); }
+ break;
+ case (!s.data && !!s.ajax) || (!!s.data && !!s.ajax && obj && obj !== -1):
+ obj = this._get_node(obj);
+ error_func = function (x, t, e) {
+ var ef = this.get_settings().html_data.ajax.error;
+ if(ef) { ef.call(this, x, t, e); }
+ if(obj != -1 && obj.length) {
+ obj.children(".jstree-loading").removeClass("jstree-loading");
+ obj.data("jstree-is-loading",false);
+ if(t === "success" && s.correct_state) { obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf"); }
+ }
+ else {
+ if(t === "success" && s.correct_state) { this.get_container().children("ul").empty(); }
+ }
+ if(e_call) { e_call.call(this); }
+ };
+ success_func = function (d, t, x) {
+ var sf = this.get_settings().html_data.ajax.success;
+ if(sf) { d = sf.call(this,d,t,x) || d; }
+ if(d == "") {
+ return error_func.call(this, x, t, "");
+ }
+ if(d) {
+ d = $(d);
+ if(!d.is("ul")) { d = $("<ul>").append(d); }
+ if(obj == -1 || !obj) { this.get_container().children("ul").empty().append(d.children()).find("li, a").filter(function () { return this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'>&#160;</ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); }
+ else { obj.children(".jstree-loading").removeClass("jstree-loading"); obj.append(d).find("li, a").filter(function () { return this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'>&#160;</ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); obj.data("jstree-is-loading",false); }
+ this.clean_node(obj);
+ if(s_call) { s_call.call(this); }
+ }
+ else {
+ if(obj && obj !== -1) {
+ obj.children(".jstree-loading").removeClass("jstree-loading");
+ obj.data("jstree-is-loading",false);
+ if(s.correct_state) {
+ obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ else {
+ if(s.correct_state) {
+ this.get_container().children("ul").empty();
+ if(s_call) { s_call.call(this); }
+ }
+ }
+ }
+ };
+ s.ajax.context = this;
+ s.ajax.error = error_func;
+ s.ajax.success = success_func;
+ if(!s.ajax.dataType) { s.ajax.dataType = "html"; }
+ if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, obj); }
+ if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, obj); }
+ $.ajax(s.ajax);
+ break;
+ }
+ }
+ }
+ });
+ // include the HTML data plugin by default
+ $.jstree.defaults.plugins.push("html_data");
+})(jQuery);
+//*/
+
+/*
+ * jsTree themeroller plugin 1.0
+ * Adds support for jQuery UI themes. Include this at the end of your plugins list, also make sure "themes" is not included.
+ */
+(function ($) {
+ $.jstree.plugin("themeroller", {
+ __init : function () {
+ var s = this._get_settings().themeroller;
+ this.get_container()
+ .addClass("ui-widget-content")
+ .delegate("a","mouseenter.jstree", function () {
+ $(this).addClass(s.item_h);
+ })
+ .delegate("a","mouseleave.jstree", function () {
+ $(this).removeClass(s.item_h);
+ })
+ .bind("open_node.jstree create_node.jstree", $.proxy(function (e, data) {
+ this._themeroller(data.rslt.obj);
+ }, this))
+ .bind("loaded.jstree refresh.jstree", $.proxy(function (e) {
+ this._themeroller();
+ }, this))
+ .bind("close_node.jstree", $.proxy(function (e, data) {
+ data.rslt.obj.children("ins").removeClass(s.opened).addClass(s.closed);
+ }, this))
+ .bind("select_node.jstree", $.proxy(function (e, data) {
+ data.rslt.obj.children("a").addClass(s.item_a);
+ }, this))
+ .bind("deselect_node.jstree deselect_all.jstree", $.proxy(function (e, data) {
+ this.get_container()
+ .find("." + s.item_a).removeClass(s.item_a).end()
+ .find(".jstree-clicked").addClass(s.item_a);
+ }, this))
+ .bind("move_node.jstree", $.proxy(function (e, data) {
+ this._themeroller(data.rslt.o);
+ }, this));
+ },
+ __destroy : function () {
+ var s = this._get_settings().themeroller,
+ c = [ "ui-icon" ];
+ $.each(s, function (i, v) {
+ v = v.split(" ");
+ if(v.length) { c = c.concat(v); }
+ });
+ this.get_container()
+ .removeClass("ui-widget-content")
+ .find("." + c.join(", .")).removeClass(c.join(" "));
+ },
+ _fn : {
+ _themeroller : function (obj) {
+ var s = this._get_settings().themeroller;
+ obj = !obj || obj == -1 ? this.get_container() : this._get_node(obj).parent();
+ obj
+ .find("li.jstree-closed > ins.jstree-icon").removeClass(s.opened).addClass("ui-icon " + s.closed).end()
+ .find("li.jstree-open > ins.jstree-icon").removeClass(s.closed).addClass("ui-icon " + s.opened).end()
+ .find("a").addClass(s.item)
+ .children("ins.jstree-icon").addClass("ui-icon " + s.item_icon);
+ }
+ },
+ defaults : {
+ "opened" : "ui-icon-triangle-1-se",
+ "closed" : "ui-icon-triangle-1-e",
+ "item" : "ui-state-default",
+ "item_h" : "ui-state-hover",
+ "item_a" : "ui-state-active",
+ "item_icon" : "ui-icon-folder-collapsed"
+ }
+ });
+ $(function() {
+ var css_string = '.jstree .ui-icon { overflow:visible; } .jstree a { padding:0 2px; }';
+ $.vakata.css.add_sheet({ str : css_string });
+ });
+})(jQuery);
+//*/
+
+/*
+ * jsTree unique plugin 1.0
+ * Forces different names amongst siblings (still a bit experimental)
+ * NOTE: does not check language versions (it will not be possible to have nodes with the same title, even in different languages)
+ */
+(function ($) {
+ $.jstree.plugin("unique", {
+ __init : function () {
+ this.get_container()
+ .bind("before.jstree", $.proxy(function (e, data) {
+ var nms = [], res = true, p, t;
+ if(data.func == "move_node") {
+ // obj, ref, position, is_copy, is_prepared, skip_check
+ if(data.args[4] === true) {
+ if(data.args[0].o && data.args[0].o.length) {
+ data.args[0].o.children("a").each(function () { nms.push($(this).text().replace(/^\s+/g,"")); });
+ res = this._check_unique(nms, data.args[0].np.find("> ul > li").not(data.args[0].o));
+ }
+ }
+ }
+ if(data.func == "create_node") {
+ // obj, position, js, callback, is_loaded
+ if(data.args[4] || this._is_loaded(data.args[0])) {
+ p = this._get_node(data.args[0]);
+ if(data.args[1] && (data.args[1] === "before" || data.args[1] === "after")) {
+ p = this._get_parent(data.args[0]);
+ if(!p || p === -1) { p = this.get_container(); }
+ }
+ if(typeof data.args[2] === "string") { nms.push(data.args[2]); }
+ else if(!data.args[2] || !data.args[2].data) { nms.push(this._get_settings().core.strings.new_node); }
+ else { nms.push(data.args[2].data); }
+ res = this._check_unique(nms, p.find("> ul > li"));
+ }
+ }
+ if(data.func == "rename_node") {
+ // obj, val
+ nms.push(data.args[1]);
+ t = this._get_node(data.args[0]);
+ p = this._get_parent(t);
+ if(!p || p === -1) { p = this.get_container(); }
+ res = this._check_unique(nms, p.find("> ul > li").not(t));
+ }
+ if(!res) {
+ e.stopPropagation();
+ return false;
+ }
+ }, this));
+ },
+ _fn : {
+ _check_unique : function (nms, p) {
+ var cnms = [];
+ p.children("a").each(function () { cnms.push($(this).text().replace(/^\s+/g,"")); });
+ if(!cnms.length || !nms.length) { return true; }
+ cnms = cnms.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",");
+ if((cnms.length + nms.length) != cnms.concat(nms).sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",").length) {
+ return false;
+ }
+ return true;
+ },
+ check_move : function () {
+ if(!this.__call_old()) { return false; }
+ var p = this._get_move(), nms = [];
+ if(p.o && p.o.length) {
+ p.o.children("a").each(function () { nms.push($(this).text().replace(/^\s+/g,"")); });
+ return this._check_unique(nms, p.np.find("> ul > li").not(p.o));
+ }
+ return true;
+ }
+ }
+ });
+})(jQuery);
+//*/ \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/jquery.jstree.min.js b/www/js/jstree-1.0-rc2/jquery.jstree.min.js
new file mode 100644
index 0000000..4443655
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/jquery.jstree.min.js
@@ -0,0 +1 @@
+"use strict";(function($){$.vakata={};$.vakata.css={get_css:function(rule_name,delete_flag,sheet){rule_name=rule_name.toLowerCase();var css_rules=sheet.cssRules||sheet.rules,j=0;do{if(css_rules.length&&j>css_rules.length+5){return false}if(css_rules[j].selectorText&&css_rules[j].selectorText.toLowerCase()==rule_name){if(delete_flag===true){if(sheet.removeRule){sheet.removeRule(j)}if(sheet.deleteRule){sheet.deleteRule(j)}return true}else{return css_rules[j]}}}while(css_rules[++j]);return false},add_css:function(rule_name,sheet){if($.jstree.css.get_css(rule_name,false,sheet)){return false}if(sheet.insertRule){sheet.insertRule(rule_name+" { }",0)}else{sheet.addRule(rule_name,null,0)}return $.vakata.css.get_css(rule_name)},remove_css:function(rule_name,sheet){return $.vakata.css.get_css(rule_name,true,sheet)},add_sheet:function(opts){var tmp;if(opts.str){tmp=document.createElement("style");tmp.setAttribute("type","text/css");if(tmp.styleSheet){document.getElementsByTagName("head")[0].appendChild(tmp);tmp.styleSheet.cssText=opts.str}else{tmp.appendChild(document.createTextNode(opts.str));document.getElementsByTagName("head")[0].appendChild(tmp)}return tmp.sheet||tmp.styleSheet}if(opts.url){if(document.createStyleSheet){try{tmp=document.createStyleSheet(opts.url)}catch(e){}}else{tmp=document.createElement("link");tmp.rel="stylesheet";tmp.type="text/css";tmp.media="all";tmp.href=opts.url;document.getElementsByTagName("head")[0].appendChild(tmp);return tmp.styleSheet}}}}})(jQuery);(function($){var instances=[],focused_instance=-1,plugins={},prepared_move={},is_ie6=false;$.fn.jstree=function(settings){var isMethodCall=(typeof settings=="string"),args=Array.prototype.slice.call(arguments,1),returnValue=this;if(!isMethodCall&&$.meta){args.push($.metadata.get(this).jstree)}settings=!isMethodCall&&args.length?$.extend.apply(null,[true,settings].concat(args)):settings;if(isMethodCall&&settings.substring(0,1)=="_"){return returnValue}if(isMethodCall){this.each(function(){var instance=instances[$.data(this,"jstree-instance-id")],methodValue=(instance&&$.isFunction(instance[settings]))?instance[settings].apply(instance,args):instance;if(typeof methodValue!=="undefined"&&(settings.indexOf("is_"===0)||(methodValue!==true&&methodValue!==false))){returnValue=methodValue;return false}})}else{this.each(function(){var instance_id=$.data(this,"jstree-instance-id"),s=false;if(typeof instance_id!=="undefined"&&instances[instance_id]){instances[instance_id].destroy()}instance_id=parseInt(instances.push({}),10)-1;$.data(this,"jstree-instance-id",instance_id);if(!settings){settings={}}settings.plugins=$.isArray(settings.plugins)?settings.plugins:$.jstree.defaults.plugins;if($.inArray("core",settings.plugins)===-1){settings.plugins.unshift("core")}s=$.extend(true,{},$.jstree.defaults,settings);s.plugins=settings.plugins;$.each(plugins,function(i,val){if($.inArray(i,s.plugins)===-1){s[i]=null;delete s[i]}});instances[instance_id]=new $.jstree._instance(instance_id,$(this).addClass("jstree jstree-"+instance_id),s);$.each(instances[instance_id]._get_settings().plugins,function(i,val){instances[instance_id].data[val]={}});$.each(instances[instance_id]._get_settings().plugins,function(i,val){if(plugins[val]){plugins[val].__init.apply(instances[instance_id])}});instances[instance_id].init()})}return returnValue};$.jstree={defaults:{plugins:[]},_focused:function(){return instances[focused_instance]||null},_reference:function(needle){if(instances[needle]){return instances[needle]}var o=$(needle);if(!o.length&&typeof needle==="string"){o=$("#"+needle)}if(!o.length){return null}return instances[o.closest(".jstree").data("jstree-instance-id")]||null},_instance:function(index,container,settings){this.data={core:{}};this.get_settings=function(){return $.extend(true,{},settings)};this._get_settings=function(){return settings};this.get_index=function(){return index};this.get_container=function(){return container};this._set_settings=function(s){settings=$.extend(true,{},settings,s)}},_fn:{},plugin:function(pname,pdata){pdata=$.extend({},{__init:$.noop,__destroy:$.noop,_fn:{},defaults:false},pdata);plugins[pname]=pdata;$.jstree.defaults[pname]=pdata.defaults;$.each(pdata._fn,function(i,val){val.plugin=pname;val.old=$.jstree._fn[i];$.jstree._fn[i]=function(){var rslt,func=val,args=Array.prototype.slice.call(arguments),evnt=new $.Event("before.jstree"),rlbk=false;do{if(func&&func.plugin&&$.inArray(func.plugin,this._get_settings().plugins)!==-1){break}func=func.old}while(func);if(!func){return}rslt=this.get_container().triggerHandler(evnt,{func:i,inst:this,args:args});if(rslt===false){return}if(typeof rslt!=="undefined"){args=rslt}if(i.indexOf("_")===0){rslt=func.apply(this,args)}else{rslt=func.apply($.extend({},this,{__callback:function(data){this.get_container().triggerHandler(i+".jstree",{inst:this,args:args,rslt:data,rlbk:rlbk})},__rollback:function(){rlbk=this.get_rollback();return rlbk},__call_old:function(replace_arguments){return func.old.apply(this,(replace_arguments?Array.prototype.slice.call(arguments,1):args))}}),args)}return rslt};$.jstree._fn[i].old=val.old;$.jstree._fn[i].plugin=pname})},rollback:function(rb){if(rb){if(!$.isArray(rb)){rb=[rb]}$.each(rb,function(i,val){instances[val.i].set_rollback(val.h,val.d)})}}};$.jstree._fn=$.jstree._instance.prototype={};$(function(){var u=navigator.userAgent.toLowerCase(),v=(u.match(/.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],css_string=".jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } .jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; } .jstree-rtl li { margin-left:0; margin-right:18px; } .jstree > ul > li { margin-left:0px; } .jstree-rtl > ul > li { margin-right:0px; } .jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } .jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } .jstree a:focus { outline: none; } .jstree a > ins { height:16px; width:16px; } .jstree a > .jstree-icon { margin-right:3px; } .jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; } li.jstree-open > ul { display:block; } li.jstree-closed > ul { display:none; } ";if(/msie/.test(u)&&parseInt(v,10)==6){is_ie6=true;css_string+=".jstree li { height:18px; margin-left:0; margin-right:0; } .jstree li li { margin-left:18px; } .jstree-rtl li li { margin-left:0px; margin-right:18px; } li.jstree-open ul { display:block; } li.jstree-closed ul { display:none !important; } .jstree li a { display:inline; border-width:0 !important; padding:0px 2px !important; } .jstree li a ins { height:16px; width:16px; margin-right:3px; } .jstree-rtl li a ins { margin-right:0px; margin-left:3px; } "}if(/msie/.test(u)&&parseInt(v,10)==7){css_string+=".jstree li a { border-width:0 !important; padding:0px 2px !important; } "}$.vakata.css.add_sheet({str:css_string})});$.jstree.plugin("core",{__init:function(){this.data.core.to_open=$.map($.makeArray(this.get_settings().core.initially_open),function(n){return"#"+n.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/")})},defaults:{html_titles:false,animation:500,initially_open:[],rtl:false,strings:{loading:"Loading ...",new_node:"New node"}},_fn:{init:function(){this.set_focus();if(this._get_settings().core.rtl){this.get_container().addClass("jstree-rtl").css("direction","rtl")}this.get_container().html("<ul><li class='jstree-last jstree-leaf'><ins>&#160;</ins><a class='jstree-loading' href='#'><ins class='jstree-icon'>&#160;</ins>"+this._get_settings().core.strings.loading+"</a></li></ul>");this.data.core.li_height=this.get_container().find("ul li.jstree-closed, ul li.jstree-leaf").eq(0).height()||18;this.get_container().delegate("li > ins","click.jstree",$.proxy(function(event){var trgt=$(event.target);if(trgt.is("ins")&&event.pageY-trgt.offset().top<this.data.core.li_height){this.toggle_node(trgt)}},this)).bind("mousedown.jstree",$.proxy(function(){this.set_focus()},this)).bind("dblclick.jstree",function(event){var sel;if(document.selection&&document.selection.empty){document.selection.empty()}else{if(window.getSelection){sel=window.getSelection();try{sel.removeAllRanges();sel.collapse()}catch(err){}}}});this.__callback();this.load_node(-1,function(){this.loaded();this.reopen()})},destroy:function(){var i,n=this.get_index(),s=this._get_settings(),_this=this;$.each(s.plugins,function(i,val){try{plugins[val].__destroy.apply(_this)}catch(err){}});this.__callback();if(this.is_focused()){for(i in instances){if(instances.hasOwnProperty(i)&&i!=n){instances[i].set_focus();break}}}if(n===focused_instance){focused_instance=-1}this.get_container().unbind(".jstree").undelegate(".jstree").removeData("jstree-instance-id").find("[class^='jstree']").andSelf().attr("class",function(){return this.className.replace(/jstree[^ ]*|$/ig,"")});instances[n]=null;delete instances[n]},save_opened:function(){var _this=this;this.data.core.to_open=[];this.get_container().find(".jstree-open").each(function(){_this.data.core.to_open.push("#"+this.id.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/"))});this.__callback(_this.data.core.to_open)},reopen:function(is_callback){var _this=this,done=true,current=[],remaining=[];if(!is_callback){this.data.core.reopen=false;this.data.core.refreshing=true}if(this.data.core.to_open.length){$.each(this.data.core.to_open,function(i,val){if(val=="#"){return true}if($(val).length&&$(val).is(".jstree-closed")){current.push(val)}else{remaining.push(val)}});if(current.length){this.data.core.to_open=remaining;$.each(current,function(i,val){_this.open_node(val,function(){_this.reopen(true)},true)});done=false}}if(done){if(this.data.core.reopen){clearTimeout(this.data.core.reopen)}this.data.core.reopen=setTimeout(function(){_this.__callback({},_this)},50);this.data.core.refreshing=false}},refresh:function(obj){var _this=this;this.save_opened();if(!obj){obj=-1}obj=this._get_node(obj);if(!obj){obj=-1}if(obj!==-1){obj.children("UL").remove()}this.load_node(obj,function(){_this.__callback({obj:obj});_this.reopen()})},loaded:function(){this.__callback()},set_focus:function(){var f=$.jstree._focused();if(f&&f!==this){f.get_container().removeClass("jstree-focused")}if(f!==this){this.get_container().addClass("jstree-focused");focused_instance=this.get_index()}this.__callback()},is_focused:function(){return focused_instance==this.get_index()},_get_node:function(obj){var $obj=$(obj,this.get_container());if($obj.is(".jstree")||obj==-1){return -1}$obj=$obj.closest("li",this.get_container());return $obj.length?$obj:false},_get_next:function(obj,strict){obj=this._get_node(obj);if(obj===-1){return this.get_container().find("> ul > li:first-child")}if(!obj.length){return false}if(strict){return(obj.nextAll("li").size()>0)?obj.nextAll("li:eq(0)"):false}if(obj.hasClass("jstree-open")){return obj.find("li:eq(0)")}else{if(obj.nextAll("li").size()>0){return obj.nextAll("li:eq(0)")}else{return obj.parentsUntil(".jstree","li").next("li").eq(0)}}},_get_prev:function(obj,strict){obj=this._get_node(obj);if(obj===-1){return this.get_container().find("> ul > li:last-child")}if(!obj.length){return false}if(strict){return(obj.prevAll("li").length>0)?obj.prevAll("li:eq(0)"):false}if(obj.prev("li").length){obj=obj.prev("li").eq(0);while(obj.hasClass("jstree-open")){obj=obj.children("ul:eq(0)").children("li:last")}return obj}else{var o=obj.parentsUntil(".jstree","li:eq(0)");return o.length?o:false}},_get_parent:function(obj){obj=this._get_node(obj);if(obj==-1||!obj.length){return false}var o=obj.parentsUntil(".jstree","li:eq(0)");return o.length?o:-1},_get_children:function(obj){obj=this._get_node(obj);if(obj===-1){return this.get_container().children("ul:eq(0)").children("li")}if(!obj.length){return false}return obj.children("ul:eq(0)").children("li")},get_path:function(obj,id_mode){var p=[],_this=this;obj=this._get_node(obj);if(obj===-1||!obj||!obj.length){return false}obj.parentsUntil(".jstree","li").each(function(){p.push(id_mode?this.id:_this.get_text(this))});p.reverse();p.push(id_mode?obj.attr("id"):this.get_text(obj));return p},is_open:function(obj){obj=this._get_node(obj);return obj&&obj!==-1&&obj.hasClass("jstree-open")},is_closed:function(obj){obj=this._get_node(obj);return obj&&obj!==-1&&obj.hasClass("jstree-closed")},is_leaf:function(obj){obj=this._get_node(obj);return obj&&obj!==-1&&obj.hasClass("jstree-leaf")},open_node:function(obj,callback,skip_animation){obj=this._get_node(obj);if(!obj.length){return false}if(!obj.hasClass("jstree-closed")){if(callback){callback.call()}return false}var s=skip_animation||is_ie6?0:this._get_settings().core.animation,t=this;if(!this._is_loaded(obj)){obj.children("a").addClass("jstree-loading");this.load_node(obj,function(){t.open_node(obj,callback,skip_animation)},callback)}else{if(s){obj.children("ul").css("display","none")}obj.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading");if(s){obj.children("ul").stop(true).slideDown(s,function(){this.style.display=""})}this.__callback({obj:obj});if(callback){callback.call()}}},close_node:function(obj,skip_animation){obj=this._get_node(obj);var s=skip_animation||is_ie6?0:this._get_settings().core.animation;if(!obj.length||!obj.hasClass("jstree-open")){return false}if(s){obj.children("ul").attr("style","display:block !important")}obj.removeClass("jstree-open").addClass("jstree-closed");if(s){obj.children("ul").stop(true).slideUp(s,function(){this.style.display=""})}this.__callback({obj:obj})},toggle_node:function(obj){obj=this._get_node(obj);if(obj.hasClass("jstree-closed")){return this.open_node(obj)}if(obj.hasClass("jstree-open")){return this.close_node(obj)}},open_all:function(obj,original_obj){obj=obj?this._get_node(obj):this.get_container();if(!obj||obj===-1){obj=this.get_container()}if(original_obj){obj=obj.find("li.jstree-closed")}else{original_obj=obj;if(obj.is(".jstree-closed")){obj=obj.find("li.jstree-closed").andSelf()}else{obj=obj.find("li.jstree-closed")}}var _this=this;obj.each(function(){var __this=this;if(!_this._is_loaded(this)){_this.open_node(this,function(){_this.open_all(__this,original_obj)},true)}else{_this.open_node(this,false,true)}});if(original_obj.find("li.jstree-closed").length===0){this.__callback({obj:original_obj})}},close_all:function(obj){var _this=this;obj=obj?this._get_node(obj):this.get_container();if(!obj||obj===-1){obj=this.get_container()}obj.find("li.jstree-open").andSelf().each(function(){_this.close_node(this)});this.__callback({obj:obj})},clean_node:function(obj){obj=obj&&obj!=-1?$(obj):this.get_container();obj=obj.is("li")?obj.find("li").andSelf():obj.find("li");obj.removeClass("jstree-last").filter("li:last-child").addClass("jstree-last").end().filter(":has(li)").not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed");obj.not(".jstree-open, .jstree-closed").addClass("jstree-leaf").children("ul").remove();this.__callback({obj:obj})},get_rollback:function(){this.__callback();return{i:this.get_index(),h:this.get_container().children("ul").clone(true),d:this.data}},set_rollback:function(html,data){this.get_container().empty().append(html);this.data=data;this.__callback()},load_node:function(obj,s_call,e_call){this.__callback({obj:obj})},_is_loaded:function(obj){return true},create_node:function(obj,position,js,callback,is_loaded){obj=this._get_node(obj);position=typeof position==="undefined"?"last":position;var d=$("<li>"),s=this._get_settings().core,tmp;if(obj!==-1&&!obj.length){return false}if(!is_loaded&&!this._is_loaded(obj)){this.load_node(obj,function(){this.create_node(obj,position,js,callback,true)});return false}this.__rollback();if(typeof js==="string"){js={data:js}}if(!js){js={}}if(js.attr){d.attr(js.attr)}if(js.state){d.addClass("jstree-"+js.state)}if(!js.data){js.data=s.strings.new_node}if(!$.isArray(js.data)){tmp=js.data;js.data=[];js.data.push(tmp)}$.each(js.data,function(i,m){tmp=$("<a>");if($.isFunction(m)){m=m.call(this,js)}if(typeof m=="string"){tmp.attr("href","#")[s.html_titles?"html":"text"](m)}else{if(!m.attr){m.attr={}}if(!m.attr.href){m.attr.href="#"}tmp.attr(m.attr)[s.html_titles?"html":"text"](m.title);if(m.language){tmp.addClass(m.language)}}tmp.prepend("<ins class='jstree-icon'>&#160;</ins>");if(m.icon){if(m.icon.indexOf("/")===-1){tmp.children("ins").addClass(m.icon)}else{tmp.children("ins").css("background","url('"+m.icon+"') center center no-repeat")}}d.append(tmp)});d.prepend("<ins class='jstree-icon'>&#160;</ins>");if(obj===-1){obj=this.get_container();if(position==="before"){position="first"}if(position==="after"){position="last"}}switch(position){case"before":obj.before(d);tmp=this._get_parent(obj);break;case"after":obj.after(d);tmp=this._get_parent(obj);break;case"inside":case"first":if(!obj.children("ul").length){obj.append("<ul>")}obj.children("ul").prepend(d);tmp=obj;break;case"last":if(!obj.children("ul").length){obj.append("<ul>")}obj.children("ul").append(d);tmp=obj;break;default:if(!obj.children("ul").length){obj.append("<ul>")}if(!position){position=0}tmp=obj.children("ul").children("li").eq(position);if(tmp.length){tmp.before(d)}else{obj.children("ul").append(d)}tmp=obj;break}if(tmp===-1||tmp.get(0)===this.get_container().get(0)){tmp=-1}this.clean_node(tmp);this.__callback({obj:d,parent:tmp});if(callback){callback.call(this,d)}return d},get_text:function(obj){obj=this._get_node(obj);if(!obj.length){return false}var s=this._get_settings().core.html_titles;obj=obj.children("a:eq(0)");if(s){obj=obj.clone();obj.children("INS").remove();return obj.html()}else{obj=obj.contents().filter(function(){return this.nodeType==3})[0];return obj.nodeValue}},set_text:function(obj,val){obj=this._get_node(obj);if(!obj.length){return false}obj=obj.children("a:eq(0)");if(this._get_settings().core.html_titles){var tmp=obj.children("INS").clone();obj.html(val).prepend(tmp);this.__callback({obj:obj,name:val});return true}else{obj=obj.contents().filter(function(){return this.nodeType==3})[0];this.__callback({obj:obj,name:val});return(obj.nodeValue=val)}},rename_node:function(obj,val){obj=this._get_node(obj);this.__rollback();if(obj&&obj.length&&this.set_text.apply(this,Array.prototype.slice.call(arguments))){this.__callback({obj:obj,name:val})}},delete_node:function(obj){obj=this._get_node(obj);if(!obj.length){return false}this.__rollback();var p=this._get_parent(obj),prev=this._get_prev(obj);obj=obj.remove();if(p!==-1&&p.find("> ul > li").length===0){p.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}this.clean_node(p);this.__callback({obj:obj,prev:prev});return obj},prepare_move:function(o,r,pos,cb,is_cb){var p={};p.ot=$.jstree._reference(p.o)||this;p.o=p.ot._get_node(o);p.r=r===-1?-1:this._get_node(r);p.p=(typeof p==="undefined")?"last":pos;if(!is_cb&&prepared_move.o&&prepared_move.o[0]===p.o[0]&&prepared_move.r[0]===p.r[0]&&prepared_move.p===p.p){this.__callback(prepared_move);if(cb){cb.call(this,prepared_move)}return}p.ot=$.jstree._reference(p.o)||this;p.rt=r===-1?p.ot:$.jstree._reference(p.r)||this;if(p.r===-1){p.cr=-1;switch(p.p){case"first":case"before":case"inside":p.cp=0;break;case"after":case"last":p.cp=p.rt.get_container().find(" > ul > li").length;break;default:p.cp=p.p;break}}else{if(!/^(before|after)$/.test(p.p)&&!this._is_loaded(p.r)){return this.load_node(p.r,function(){this.prepare_move(o,r,pos,cb,true)})}switch(p.p){case"before":p.cp=p.r.index();p.cr=p.rt._get_parent(p.r);break;case"after":p.cp=p.r.index()+1;p.cr=p.rt._get_parent(p.r);break;case"inside":case"first":p.cp=0;p.cr=p.r;break;case"last":p.cp=p.r.find(" > ul > li").length;p.cr=p.r;break;default:p.cp=p.p;p.cr=p.r;break}}p.np=p.cr==-1?p.rt.get_container():p.cr;p.op=p.ot._get_parent(p.o);p.or=p.np.find(" > ul > li:nth-child("+(p.cp+1)+")");prepared_move=p;this.__callback(prepared_move);if(cb){cb.call(this,prepared_move)}},check_move:function(){var obj=prepared_move,ret=true;if(obj.or[0]===obj.o[0]){return false}obj.o.each(function(){if(obj.r.parentsUntil(".jstree").andSelf().filter("li").index(this)!==-1){ret=false;return false}});return ret},move_node:function(obj,ref,position,is_copy,is_prepared,skip_check){if(!is_prepared){return this.prepare_move(obj,ref,position,function(p){this.move_node(p,false,false,is_copy,true,skip_check)})}if(!skip_check&&!this.check_move()){return false}this.__rollback();var o=false;if(is_copy){o=obj.o.clone();o.find("*[id]").andSelf().each(function(){if(this.id){this.id="copy_"+this.id}})}else{o=obj.o}if(obj.or.length){obj.or.before(o)}else{if(!obj.np.children("ul").length){$("<ul>").appendTo(obj.np)}obj.np.children("ul:eq(0)").append(o)}try{obj.ot.clean_node(obj.op);obj.rt.clean_node(obj.np);if(!obj.op.find("> ul > li").length){obj.op.removeClass("jstree-open jstree-closed").addClass("jstree-leaf").children("ul").remove()}}catch(e){}if(is_copy){prepared_move.cy=true;prepared_move.oc=o}this.__callback(prepared_move);return prepared_move},_get_move:function(){return prepared_move}}})})(jQuery);(function($){$.jstree.plugin("ui",{__init:function(){this.data.ui.selected=$();this.data.ui.last_selected=false;this.data.ui.hovered=null;this.data.ui.to_select=this.get_settings().ui.initially_select;this.get_container().delegate("a","click.jstree",$.proxy(function(event){event.preventDefault();this.select_node(event.currentTarget,true,event)},this)).delegate("a","mouseenter.jstree",$.proxy(function(event){this.hover_node(event.target)},this)).delegate("a","mouseleave.jstree",$.proxy(function(event){this.dehover_node(event.target)},this)).bind("reopen.jstree",$.proxy(function(){this.reselect()},this)).bind("get_rollback.jstree",$.proxy(function(){this.dehover_node();this.save_selected()},this)).bind("set_rollback.jstree",$.proxy(function(){this.reselect()},this)).bind("close_node.jstree",$.proxy(function(event,data){var s=this._get_settings().ui,obj=this._get_node(data.rslt.obj),clk=(obj&&obj.length)?obj.children("ul").find(".jstree-clicked"):$(),_this=this;if(s.selected_parent_close===false||!clk.length){return}clk.each(function(){_this.deselect_node(this);if(s.selected_parent_close==="select_parent"){_this.select_node(obj)}})},this)).bind("delete_node.jstree",$.proxy(function(event,data){var s=this._get_settings().ui.select_prev_on_delete,obj=this._get_node(data.rslt.obj),clk=(obj&&obj.length)?obj.find(".jstree-clicked"):[],_this=this;clk.each(function(){_this.deselect_node(this)});if(s&&clk.length){this.select_node(data.rslt.prev)}},this)).bind("move_node.jstree",$.proxy(function(event,data){if(data.rslt.cy){data.rslt.oc.find(".jstree-clicked").removeClass("jstree-clicked")}},this))},defaults:{select_limit:-1,select_multiple_modifier:"ctrl",selected_parent_close:"select_parent",select_prev_on_delete:true,disable_selecting_children:false,initially_select:[]},_fn:{_get_node:function(obj,allow_multiple){if(typeof obj==="undefined"||obj===null){return allow_multiple?this.data.ui.selected:this.data.ui.last_selected}var $obj=$(obj,this.get_container());if($obj.is(".jstree")||obj==-1){return -1}$obj=$obj.closest("li",this.get_container());return $obj.length?$obj:false},save_selected:function(){var _this=this;this.data.ui.to_select=[];this.data.ui.selected.each(function(){_this.data.ui.to_select.push("#"+this.id.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/"))});this.__callback(this.data.ui.to_select)},reselect:function(){var _this=this,s=this.data.ui.to_select;s=$.map($.makeArray(s),function(n){return"#"+n.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/")});this.deselect_all();$.each(s,function(i,val){if(val&&val!=="#"){_this.select_node(val)}});this.__callback()},refresh:function(obj){this.save_selected();return this.__call_old()},hover_node:function(obj){obj=this._get_node(obj);if(!obj.length){return false}if(!obj.hasClass("jstree-hovered")){this.dehover_node()}this.data.ui.hovered=obj.children("a").addClass("jstree-hovered").parent();this.__callback({obj:obj})},dehover_node:function(){var obj=this.data.ui.hovered,p;if(!obj||!obj.length){return false}p=obj.children("a").removeClass("jstree-hovered").parent();if(this.data.ui.hovered[0]===p[0]){this.data.ui.hovered=null}this.__callback({obj:obj})},select_node:function(obj,check,e){obj=this._get_node(obj);if(obj==-1||!obj||!obj.length){return false}var s=this._get_settings().ui,is_multiple=(s.select_multiple_modifier=="on"||(s.select_multiple_modifier!==false&&e&&e[s.select_multiple_modifier+"Key"])),is_selected=this.is_selected(obj),proceed=true;if(check){if(s.disable_selecting_children&&is_multiple&&obj.parents("li",this.get_container()).children(".jstree-clicked").length){return false}proceed=false;switch(!0){case (is_selected&&!is_multiple):this.deselect_all();is_selected=false;proceed=true;break;case (!is_selected&&!is_multiple):if(s.select_limit==-1||s.select_limit>0){this.deselect_all();proceed=true}break;case (is_selected&&is_multiple):this.deselect_node(obj);break;case (!is_selected&&is_multiple):if(s.select_limit==-1||this.data.ui.selected.length+1<=s.select_limit){proceed=true}break}}if(proceed&&!is_selected){obj.children("a").addClass("jstree-clicked");this.data.ui.selected=this.data.ui.selected.add(obj);this.data.ui.last_selected=obj;this.__callback({obj:obj})}},deselect_node:function(obj){obj=this._get_node(obj);if(!obj.length){return false}if(this.is_selected(obj)){obj.children("a").removeClass("jstree-clicked");this.data.ui.selected=this.data.ui.selected.not(obj);if(this.data.ui.last_selected.get(0)===obj.get(0)){this.data.ui.last_selected=this.data.ui.selected.eq(0)}this.__callback({obj:obj})}},toggle_select:function(obj){obj=this._get_node(obj);if(!obj.length){return false}if(this.is_selected(obj)){this.deselect_node(obj)}else{this.select_node(obj)}},is_selected:function(obj){return this.data.ui.selected.index(this._get_node(obj))>=0},get_selected:function(context){return context?$(context).find(".jstree-clicked").parent():this.data.ui.selected},deselect_all:function(context){if(context){$(context).find(".jstree-clicked").removeClass("jstree-clicked")}else{this.get_container().find(".jstree-clicked").removeClass("jstree-clicked")}this.data.ui.selected=$([]);this.data.ui.last_selected=false;this.__callback()}}});$.jstree.defaults.plugins.push("ui")})(jQuery);(function($){$.jstree.plugin("crrm",{__init:function(){this.get_container().bind("move_node.jstree",$.proxy(function(e,data){if(this._get_settings().crrm.move.open_onmove){var t=this;data.rslt.np.parentsUntil(".jstree").andSelf().filter(".jstree-closed").each(function(){t.open_node(this,false,true)})}},this))},defaults:{input_width_limit:200,move:{always_copy:false,open_onmove:true,default_position:"last",check_move:function(m){return true}}},_fn:{_show_input:function(obj,callback){obj=this._get_node(obj);var rtl=this._get_settings().core.rtl,w=this._get_settings().crrm.input_width_limit,w1=obj.children("ins").width(),w2=obj.find("> a:visible > ins").width()*obj.find("> a:visible > ins").length,t=this.get_text(obj),h1=$("<div>",{css:{position:"absolute",top:"-200px",left:(rtl?"0px":"-1000px"),visibility:"hidden"}}).appendTo("body"),h2=obj.css("position","relative").append($("<input>",{value:t,css:{padding:"0",border:"1px solid silver",position:"absolute",left:(rtl?"auto":(w1+w2+4)+"px"),right:(rtl?(w1+w2+4)+"px":"auto"),top:"0px",height:(this.data.core.li_height-2)+"px",lineHeight:(this.data.core.li_height-2)+"px",width:"150px"},blur:$.proxy(function(){var i=obj.children("input"),v=i.val();if(v===""){v=t}i.remove();this.set_text(obj,t);this.rename_node(obj,v);callback.call(this,obj,v,t);obj.css("position","")},this),keyup:function(event){var key=event.keyCode||event.which;if(key==27){this.value=t;this.blur();return}else{if(key==13){this.blur();return}else{h2.width(Math.min(h1.text("pW"+this.value).width(),w))}}}})).children("input");this.set_text(obj,"");h1.css({fontFamily:h2.css("fontFamily")||"",fontSize:h2.css("fontSize")||"",fontWeight:h2.css("fontWeight")||"",fontStyle:h2.css("fontStyle")||"",fontStretch:h2.css("fontStretch")||"",fontVariant:h2.css("fontVariant")||"",letterSpacing:h2.css("letterSpacing")||"",wordSpacing:h2.css("wordSpacing")||""});h2.width(Math.min(h1.text("pW"+h2[0].value).width(),w))[0].select()},rename:function(obj){obj=this._get_node(obj);this.__rollback();var f=this.__callback;this._show_input(obj,function(obj,new_name,old_name){f.call(this,{obj:obj,new_name:new_name,old_name:old_name})})},create:function(obj,position,js,callback,skip_rename){var t,_this=this;obj=this._get_node(obj);if(!obj){obj=-1}this.__rollback();t=this.create_node(obj,position,js,function(t){var p=this._get_parent(t),pos=$(t).index();if(callback){callback.call(this,t)}if(p.length&&p.hasClass("jstree-closed")){this.open_node(p,false,true)}if(!skip_rename){this._show_input(t,function(obj,new_name,old_name){_this.__callback({obj:obj,name:new_name,parent:p,position:pos})})}else{_this.__callback({obj:t,name:this.get_text(t),parent:p,position:pos})}});return t},remove:function(obj){obj=this._get_node(obj,true);this.__rollback();this.delete_node(obj);this.__callback({obj:obj})},check_move:function(){if(!this.__call_old()){return false}var s=this._get_settings().crrm.move;if(!s.check_move.call(this,this._get_move())){return false}return true},move_node:function(obj,ref,position,is_copy,is_prepared,skip_check){var s=this._get_settings().crrm.move;if(!is_prepared){if(!position){position=s.default_position}if(position==="inside"&&!s.default_position.match(/^(before|after)$/)){position=s.default_position}return this.__call_old(true,obj,ref,position,is_copy,false,skip_check)}if(s.always_copy===true||(s.always_copy==="multitree"&&obj.rt.get_index()!==obj.ot.get_index())){is_copy=true}this.__call_old(true,obj,ref,position,is_copy,true,skip_check)},cut:function(obj){obj=this._get_node(obj);this.data.crrm.cp_nodes=false;this.data.crrm.ct_nodes=false;if(!obj||!obj.length){return false}this.data.crrm.ct_nodes=obj},copy:function(obj){obj=this._get_node(obj);this.data.crrm.cp_nodes=false;this.data.crrm.ct_nodes=false;if(!obj||!obj.length){return false}this.data.crrm.cp_nodes=obj},paste:function(obj){obj=this._get_node(obj);if(!obj||!obj.length){return false}if(!this.data.crrm.ct_nodes&&!this.data.crrm.cp_nodes){return false}if(this.data.crrm.ct_nodes){this.move_node(this.data.crrm.ct_nodes,obj)}if(this.data.crrm.cp_nodes){this.move_node(this.data.crrm.cp_nodes,obj,false,true)}this.data.crrm.cp_nodes=false;this.data.crrm.ct_nodes=false}}});$.jstree.defaults.plugins.push("crrm")})(jQuery);(function($){var themes_loaded=[];$.jstree._themes=false;$.jstree.plugin("themes",{__init:function(){this.get_container().bind("init.jstree",$.proxy(function(){var s=this._get_settings().themes;this.data.themes.dots=s.dots;this.data.themes.icons=s.icons;this.set_theme(s.theme,s.url)},this)).bind("loaded.jstree",$.proxy(function(){if(!this.data.themes.dots){this.hide_dots()}else{this.show_dots()}if(!this.data.themes.icons){this.hide_icons()}else{this.show_icons()}},this))},defaults:{theme:"default",url:false,dots:true,icons:true},_fn:{set_theme:function(theme_name,theme_url){if(!theme_name){return false}if(!theme_url){theme_url=$.jstree._themes+theme_name+"/style.css"}if($.inArray(theme_url,themes_loaded)==-1){$.vakata.css.add_sheet({url:theme_url,rel:"jstree"});themes_loaded.push(theme_url)}if(this.data.themes.theme!=theme_name){this.get_container().removeClass("jstree-"+this.data.themes.theme);this.data.themes.theme=theme_name}this.get_container().addClass("jstree-"+theme_name);if(!this.data.themes.dots){this.hide_dots()}else{this.show_dots()}if(!this.data.themes.icons){this.hide_icons()}else{this.show_icons()}this.__callback()},get_theme:function(){return this.data.themes.theme},show_dots:function(){this.data.themes.dots=true;this.get_container().children("ul").removeClass("jstree-no-dots")},hide_dots:function(){this.data.themes.dots=false;this.get_container().children("ul").addClass("jstree-no-dots")},toggle_dots:function(){if(this.data.themes.dots){this.hide_dots()}else{this.show_dots()}},show_icons:function(){this.data.themes.icons=true;this.get_container().children("ul").removeClass("jstree-no-icons")},hide_icons:function(){this.data.themes.icons=false;this.get_container().children("ul").addClass("jstree-no-icons")},toggle_icons:function(){if(this.data.themes.icons){this.hide_icons()}else{this.show_icons()}}}});$(function(){if($.jstree._themes===false){$("script").each(function(){if(this.src.toString().match(/jquery\.jstree[^\/]*?\.js(\?.*)?$/)){$.jstree._themes=this.src.toString().replace(/jquery\.jstree[^\/]*?\.js(\?.*)?$/,"")+"themes/";return false}})}if($.jstree._themes===false){$.jstree._themes="themes/"}});$.jstree.defaults.plugins.push("themes")})(jQuery);(function($){var bound=[];function exec(i,event){var f=$.jstree._focused(),tmp;if(f&&f.data&&f.data.hotkeys&&f.data.hotkeys.enabled){tmp=f._get_settings().hotkeys[i];if(tmp){return tmp.call(f,event)}}}$.jstree.plugin("hotkeys",{__init:function(){if(typeof $.hotkeys==="undefined"){throw"jsTree hotkeys: jQuery hotkeys plugin not included."}if(!this.data.ui){throw"jsTree hotkeys: jsTree UI plugin not included."}$.each(this._get_settings().hotkeys,function(i,val){if($.inArray(i,bound)==-1){$(document).bind("keydown",i,function(event){return exec(i,event)});bound.push(i)}});this.enable_hotkeys()},defaults:{up:function(){var o=this.data.ui.hovered||this.data.ui.last_selected||-1;this.hover_node(this._get_prev(o));return false},down:function(){var o=this.data.ui.hovered||this.data.ui.last_selected||-1;this.hover_node(this._get_next(o));return false},left:function(){var o=this.data.ui.hovered||this.data.ui.last_selected;if(o){if(o.hasClass("jstree-open")){this.close_node(o)}else{this.hover_node(this._get_prev(o))}}return false},right:function(){var o=this.data.ui.hovered||this.data.ui.last_selected;if(o&&o.length){if(o.hasClass("jstree-closed")){this.open_node(o)}else{this.hover_node(this._get_next(o))}}return false},space:function(){if(this.data.ui.hovered){this.data.ui.hovered.children("a:eq(0)").click()}return false},"ctrl+space":function(event){event.type="click";if(this.data.ui.hovered){this.data.ui.hovered.children("a:eq(0)").trigger(event)}return false},f2:function(){this.rename(this.data.ui.hovered||this.data.ui.last_selected)},del:function(){this.remove(this.data.ui.hovered||this._get_node(null))}},_fn:{enable_hotkeys:function(){this.data.hotkeys.enabled=true},disable_hotkeys:function(){this.data.hotkeys.enabled=false}}})})(jQuery);(function($){$.jstree.plugin("json_data",{defaults:{data:false,ajax:false,correct_state:true,progressive_render:false},_fn:{load_node:function(obj,s_call,e_call){var _this=this;this.load_node_json(obj,function(){_this.__callback({obj:obj});s_call.call(this)},e_call)},_is_loaded:function(obj){var s=this._get_settings().json_data,d;obj=this._get_node(obj);if(obj&&obj!==-1&&s.progressive_render&&!obj.is(".jstree-open, .jstree-leaf")&&obj.children("ul").children("li").length===0&&obj.data("jstree-children")){d=this._parse_json(obj.data("jstree-children"));if(d){obj.append(d);$.removeData(obj,"jstree-children")}this.clean_node(obj);return true}return obj==-1||!obj||!s.ajax||obj.is(".jstree-open, .jstree-leaf")||obj.children("ul").children("li").size()>0},load_node_json:function(obj,s_call,e_call){var s=this.get_settings().json_data,d,error_func=function(){},success_func=function(){};obj=this._get_node(obj);if(obj&&obj!==-1){if(obj.data("jstree-is-loading")){return}else{obj.data("jstree-is-loading",true)}}switch(!0){case (!s.data&&!s.ajax):throw"Neither data nor ajax settings supplied.";case (!!s.data&&!s.ajax)||(!!s.data&&!!s.ajax&&(!obj||obj===-1)):if(!obj||obj==-1){d=this._parse_json(s.data);if(d){this.get_container().children("ul").empty().append(d.children());this.clean_node()}else{if(s.correct_state){this.get_container().children("ul").empty()}}}if(s_call){s_call.call(this)}break;case (!s.data&&!!s.ajax)||(!!s.data&&!!s.ajax&&obj&&obj!==-1):error_func=function(x,t,e){var ef=this.get_settings().json_data.ajax.error;if(ef){ef.call(this,x,t,e)}if(obj!=-1&&obj.length){obj.children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false);if(t==="success"&&s.correct_state){obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}}else{if(t==="success"&&s.correct_state){this.get_container().children("ul").empty()}}if(e_call){e_call.call(this)}};success_func=function(d,t,x){var sf=this.get_settings().json_data.ajax.success;if(sf){d=sf.call(this,d,t,x)||d}if(d===""||(!$.isArray(d)&&!$.isPlainObject(d))){return error_func.call(this,x,t,"")}d=this._parse_json(d);if(d){if(obj===-1||!obj){this.get_container().children("ul").empty().append(d.children())}else{obj.append(d).children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false)}this.clean_node(obj);if(s_call){s_call.call(this)}}else{if(obj===-1||!obj){if(s.correct_state){this.get_container().children("ul").empty();if(s_call){s_call.call(this)}}}else{obj.children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false);if(s.correct_state){obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");if(s_call){s_call.call(this)}}}}};s.ajax.context=this;s.ajax.error=error_func;s.ajax.success=success_func;if(!s.ajax.dataType){s.ajax.dataType="json"}if($.isFunction(s.ajax.url)){s.ajax.url=s.ajax.url.call(this,obj)}if($.isFunction(s.ajax.data)){s.ajax.data=s.ajax.data.call(this,obj)}$.ajax(s.ajax);break}},_parse_json:function(js,is_callback){var d=false,p=this._get_settings(),s=p.json_data,t=p.core.html_titles,tmp,i,j,ul1,ul2;if(!js){return d}if($.isFunction(js)){js=js.call(this)}if($.isArray(js)){d=$();if(!js.length){return false}for(i=0,j=js.length;i<j;i++){tmp=this._parse_json(js[i],true);if(tmp.length){d=d.add(tmp)}}}else{if(typeof js=="string"){js={data:js}}if(!js.data&&js.data!==""){return d}d=$("<li>");if(js.attr){d.attr(js.attr)}if(js.metadata){d.data("jstree",js.metadata)}if(js.state){d.addClass("jstree-"+js.state)}if(!$.isArray(js.data)){tmp=js.data;js.data=[];js.data.push(tmp)}$.each(js.data,function(i,m){tmp=$("<a>");if($.isFunction(m)){m=m.call(this,js)}if(typeof m=="string"){tmp.attr("href","#")[t?"html":"text"](m)}else{if(!m.attr){m.attr={}}if(!m.attr.href){m.attr.href="#"}tmp.attr(m.attr)[t?"html":"text"](m.title);if(m.language){tmp.addClass(m.language)}}tmp.prepend("<ins class='jstree-icon'>&#160;</ins>");if(!m.icon&&js.icon){m.icon=js.icon}if(m.icon){if(m.icon.indexOf("/")===-1){tmp.children("ins").addClass(m.icon)}else{tmp.children("ins").css("background","url('"+m.icon+"') center center no-repeat")}}d.append(tmp)});d.prepend("<ins class='jstree-icon'>&#160;</ins>");if(js.children){if(s.progressive_render&&js.state!=="open"){d.addClass("jstree-closed").data("jstree-children",js.children)}else{if($.isFunction(js.children)){js.children=js.children.call(this,js)}if($.isArray(js.children)&&js.children.length){tmp=this._parse_json(js.children,true);if(tmp.length){ul2=$("<ul>");ul2.append(tmp);d.append(ul2)}}}}}if(!is_callback){ul1=$("<ul>");ul1.append(d);d=ul1}return d},get_json:function(obj,li_attr,a_attr,is_callback){var result=[],s=this._get_settings(),_this=this,tmp1,tmp2,li,a,t,lang;obj=this._get_node(obj);if(!obj||obj===-1){obj=this.get_container().find("> ul > li")}li_attr=$.isArray(li_attr)?li_attr:["id","class"];if(!is_callback&&this.data.types){li_attr.push(s.types.type_attr)}a_attr=$.isArray(a_attr)?a_attr:[];obj.each(function(){li=$(this);tmp1={data:[]};if(li_attr.length){tmp1.attr={}}$.each(li_attr,function(i,v){tmp2=li.attr(v);if(tmp2&&tmp2.length&&tmp2.replace(/jstree[^ ]*|$/ig,"").length){tmp1.attr[v]=tmp2.replace(/jstree[^ ]*|$/ig,"")}});if(li.hasClass("jstree-open")){tmp1.state="open"}if(li.hasClass("jstree-closed")){tmp1.state="closed"}a=li.children("a");a.each(function(){t=$(this);if(a_attr.length||$.inArray("languages",s.plugins)!==-1||t.children("ins").get(0).style.backgroundImage.length||(t.children("ins").get(0).className&&t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").length)){lang=false;if($.inArray("languages",s.plugins)!==-1&&$.isArray(s.languages)&&s.languages.length){$.each(s.languages,function(l,lv){if(t.hasClass(lv)){lang=lv;return false}})}tmp2={attr:{},title:_this.get_text(t,lang)};$.each(a_attr,function(k,z){tmp1.attr[z]=(t.attr(z)||"").replace(/jstree[^ ]*|$/ig,"")});$.each(s.languages,function(k,z){if(t.hasClass(z)){tmp2.language=z;return true}});if(t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"").length){tmp2.icon=t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"")}if(t.children("ins").get(0).style.backgroundImage.length){tmp2.icon=t.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")","")}}else{tmp2=_this.get_text(t)}if(a.length>1){tmp1.data.push(tmp2)}else{tmp1.data=tmp2}});li=li.find("> ul > li");if(li.length){tmp1.children=_this.get_json(li,li_attr,a_attr,true)}result.push(tmp1)});return result}}})})(jQuery);(function($){$.jstree.plugin("languages",{__init:function(){this._load_css()},defaults:[],_fn:{set_lang:function(i){var langs=this._get_settings().languages,st=false,selector=".jstree-"+this.get_index()+" a";if(!$.isArray(langs)||langs.length===0){return false}if($.inArray(i,langs)==-1){if(!!langs[i]){i=langs[i]}else{return false}}if(i==this.data.languages.current_language){return true}st=$.vakata.css.get_css(selector+"."+this.data.languages.current_language,false,this.data.languages.language_css);if(st!==false){st.style.display="none"}st=$.vakata.css.get_css(selector+"."+i,false,this.data.languages.language_css);if(st!==false){st.style.display=""}this.data.languages.current_language=i;this.__callback(i);return true},get_lang:function(){return this.data.languages.current_language},get_text:function(obj,lang){obj=this._get_node(obj)||this.data.ui.last_selected;if(!obj.size()){return false}var langs=this._get_settings().languages,s=this._get_settings().core.html_titles;if($.isArray(langs)&&langs.length){lang=(lang&&$.inArray(lang,langs)!=-1)?lang:this.data.languages.current_language;obj=obj.children("a."+lang)}else{obj=obj.children("a:eq(0)")}if(s){obj=obj.clone();obj.children("INS").remove();return obj.html()}else{obj=obj.contents().filter(function(){return this.nodeType==3})[0];return obj.nodeValue}},set_text:function(obj,val,lang){obj=this._get_node(obj)||this.data.ui.last_selected;if(!obj.size()){return false}var langs=this._get_settings().languages,s=this._get_settings().core.html_titles,tmp;if($.isArray(langs)&&langs.length){lang=(lang&&$.inArray(lang,langs)!=-1)?lang:this.data.languages.current_language;obj=obj.children("a."+lang)}else{obj=obj.children("a:eq(0)")}if(s){tmp=obj.children("INS").clone();obj.html(val).prepend(tmp);this.__callback({obj:obj,name:val,lang:lang});return true}else{obj=obj.contents().filter(function(){return this.nodeType==3})[0];this.__callback({obj:obj,name:val,lang:lang});return(obj.nodeValue=val)}},_load_css:function(){var langs=this._get_settings().languages,str="/* languages css */",selector=".jstree-"+this.get_index()+" a",ln;if($.isArray(langs)&&langs.length){this.data.languages.current_language=langs[0];for(ln=0;ln<langs.length;ln++){str+=selector+"."+langs[ln]+" {";if(langs[ln]!=this.data.languages.current_language){str+=" display:none; "}str+=" } "}this.data.languages.language_css=$.vakata.css.add_sheet({str:str})}},create_node:function(obj,position,js,callback){var t=this.__call_old(true,obj,position,js,function(t){var langs=this._get_settings().languages,a=t.children("a"),ln;if($.isArray(langs)&&langs.length){for(ln=0;ln<langs.length;ln++){if(!a.is("."+langs[ln])){t.append(a.eq(0).clone().removeClass(langs.join(" ")).addClass(langs[ln]))}}a.not("."+langs.join(", .")).remove()}if(callback){callback.call(this,t)}});return t}}})})(jQuery);(function($){$.jstree.plugin("cookies",{__init:function(){if(typeof $.cookie==="undefined"){throw"jsTree cookie: jQuery cookie plugin not included."}var s=this._get_settings().cookies,tmp;if(!!s.save_opened){tmp=$.cookie(s.save_opened);if(tmp&&tmp.length){this.data.core.to_open=tmp.split(",")}}if(!!s.save_selected){tmp=$.cookie(s.save_selected);if(tmp&&tmp.length&&this.data.ui){this.data.ui.to_select=tmp.split(",")}}this.get_container().one((this.data.ui?"reselect":"reopen")+".jstree",$.proxy(function(){this.get_container().bind("open_node.jstree close_node.jstree select_node.jstree deselect_node.jstree",$.proxy(function(e){if(this._get_settings().cookies.auto_save){this.save_cookie((e.handleObj.namespace+e.handleObj.type).replace("jstree",""))}},this))},this))},defaults:{save_opened:"jstree_open",save_selected:"jstree_select",auto_save:true,cookie_options:{}},_fn:{save_cookie:function(c){if(this.data.core.refreshing){return}var s=this._get_settings().cookies;if(!c){if(s.save_opened){this.save_opened();$.cookie(s.save_opened,this.data.core.to_open.join(","),s.cookie_options)}if(s.save_selected&&this.data.ui){this.save_selected();$.cookie(s.save_selected,this.data.ui.to_select.join(","),s.cookie_options)}return}switch(c){case"open_node":case"close_node":if(!!s.save_opened){this.save_opened();$.cookie(s.save_opened,this.data.core.to_open.join(","),s.cookie_options)}break;case"select_node":case"deselect_node":if(!!s.save_selected&&this.data.ui){this.save_selected();$.cookie(s.save_selected,this.data.ui.to_select.join(","),s.cookie_options)}break}}}});$.jstree.defaults.plugins.push("cookies")})(jQuery);(function($){$.jstree.plugin("sort",{__init:function(){this.get_container().bind("load_node.jstree",$.proxy(function(e,data){var obj=this._get_node(data.rslt.obj);obj=obj===-1?this.get_container().children("ul"):obj.children("ul");this.sort(obj)},this)).bind("rename_node.jstree",$.proxy(function(e,data){this.sort(data.rslt.obj.parent())},this)).bind("move_node.jstree",$.proxy(function(e,data){var m=data.rslt.np==-1?this.get_container():data.rslt.np;this.sort(m.children("ul"))},this))},defaults:function(a,b){return this.get_text(a)>this.get_text(b)?1:-1},_fn:{sort:function(obj){var s=this._get_settings().sort,t=this;obj.append($.makeArray(obj.children("li")).sort($.proxy(s,t)));obj.find("> li > ul").each(function(){t.sort($(this))});this.clean_node(obj)}}})})(jQuery);(function($){var o=false,r=false,m=false,sli=false,sti=false,dir1=false,dir2=false;$.vakata.dnd={is_down:false,is_drag:false,helper:false,scroll_spd:10,init_x:0,init_y:0,threshold:5,user_data:{},drag_start:function(e,data,html){if($.vakata.dnd.is_drag){$.vakata.drag_stop({})}try{e.currentTarget.unselectable="on";e.currentTarget.onselectstart=function(){return false};if(e.currentTarget.style){e.currentTarget.style.MozUserSelect="none"}}catch(err){}$.vakata.dnd.init_x=e.pageX;$.vakata.dnd.init_y=e.pageY;$.vakata.dnd.user_data=data;$.vakata.dnd.is_down=true;$.vakata.dnd.helper=$("<div id='vakata-dragged'>").html(html).css("opacity","0.75");$(document).bind("mousemove",$.vakata.dnd.drag);$(document).bind("mouseup",$.vakata.dnd.drag_stop);return false},drag:function(e){if(!$.vakata.dnd.is_down){return}if(!$.vakata.dnd.is_drag){if(Math.abs(e.pageX-$.vakata.dnd.init_x)>5||Math.abs(e.pageY-$.vakata.dnd.init_y)>5){$.vakata.dnd.helper.appendTo("body");$.vakata.dnd.is_drag=true;$(document).triggerHandler("drag_start.vakata",{event:e,data:$.vakata.dnd.user_data})}else{return}}if(e.type==="mousemove"){var d=$(document),t=d.scrollTop(),l=d.scrollLeft();if(e.pageY-t<20){if(sti&&dir1==="down"){clearInterval(sti);sti=false}if(!sti){dir1="up";sti=setInterval(function(){$(document).scrollTop($(document).scrollTop()-$.vakata.dnd.scroll_spd)},150)}}else{if(sti&&dir1==="up"){clearInterval(sti);sti=false}}if($(window).height()-(e.pageY-t)<20){if(sti&&dir1==="up"){clearInterval(sti);sti=false}if(!sti){dir1="down";sti=setInterval(function(){$(document).scrollTop($(document).scrollTop()+$.vakata.dnd.scroll_spd)},150)}}else{if(sti&&dir1==="down"){clearInterval(sti);sti=false}}if(e.pageX-l<20){if(sli&&dir2==="right"){clearInterval(sli);sli=false}if(!sli){dir2="left";sli=setInterval(function(){$(document).scrollLeft($(document).scrollLeft()-$.vakata.dnd.scroll_spd)},150)}}else{if(sli&&dir2==="left"){clearInterval(sli);sli=false}}if($(window).width()-(e.pageX-l)<20){if(sli&&dir2==="left"){clearInterval(sli);sli=false}if(!sli){dir2="right";sli=setInterval(function(){$(document).scrollLeft($(document).scrollLeft()+$.vakata.dnd.scroll_spd)},150)}}else{if(sli&&dir2==="right"){clearInterval(sli);sli=false}}}$.vakata.dnd.helper.css({left:(e.pageX+5)+"px",top:(e.pageY+10)+"px"});$(document).triggerHandler("drag.vakata",{event:e,data:$.vakata.dnd.user_data})},drag_stop:function(e){$(document).unbind("mousemove",$.vakata.dnd.drag);$(document).unbind("mouseup",$.vakata.dnd.drag_stop);$(document).triggerHandler("drag_stop.vakata",{event:e,data:$.vakata.dnd.user_data});$.vakata.dnd.helper.remove();$.vakata.dnd.init_x=0;$.vakata.dnd.init_y=0;$.vakata.dnd.user_data={};$.vakata.dnd.is_down=false;$.vakata.dnd.is_drag=false}};$(function(){var css_string="#vakata-dragged { display:block; margin:0 0 0 0; padding:4px 4px 4px 24px; position:absolute; top:-2000px; line-height:16px; z-index:10000; } ";$.vakata.css.add_sheet({str:css_string})});$.jstree.plugin("dnd",{__init:function(){this.data.dnd={active:false,after:false,inside:false,before:false,off:false,prepared:false,w:0,to1:false,to2:false,cof:false,cw:false,ch:false,i1:false,i2:false};this.get_container().bind("mouseenter.jstree",$.proxy(function(){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree&&this.data.themes){m.attr("class","jstree-"+this.data.themes.theme);$.vakata.dnd.helper.attr("class","jstree-dnd-helper jstree-"+this.data.themes.theme)}},this)).bind("mouseleave.jstree",$.proxy(function(){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree){if(this.data.dnd.i1){clearInterval(this.data.dnd.i1)}if(this.data.dnd.i2){clearInterval(this.data.dnd.i2)}}},this)).bind("mousemove.jstree",$.proxy(function(e){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree){var cnt=this.get_container()[0];if(e.pageX+24>this.data.dnd.cof.left+this.data.dnd.cw){if(this.data.dnd.i1){clearInterval(this.data.dnd.i1)}this.data.dnd.i1=setInterval($.proxy(function(){this.scrollLeft+=$.vakata.dnd.scroll_spd},cnt),100)}else{if(e.pageX-24<this.data.dnd.cof.left){if(this.data.dnd.i1){clearInterval(this.data.dnd.i1)}this.data.dnd.i1=setInterval($.proxy(function(){this.scrollLeft-=$.vakata.dnd.scroll_spd},cnt),100)}else{if(this.data.dnd.i1){clearInterval(this.data.dnd.i1)}}}if(e.pageY+24>this.data.dnd.cof.top+this.data.dnd.ch){if(this.data.dnd.i2){clearInterval(this.data.dnd.i2)}this.data.dnd.i2=setInterval($.proxy(function(){this.scrollTop+=$.vakata.dnd.scroll_spd},cnt),100)}else{if(e.pageY-24<this.data.dnd.cof.top){if(this.data.dnd.i2){clearInterval(this.data.dnd.i2)}this.data.dnd.i2=setInterval($.proxy(function(){this.scrollTop-=$.vakata.dnd.scroll_spd},cnt),100)}else{if(this.data.dnd.i2){clearInterval(this.data.dnd.i2)}}}}},this)).delegate("a","mousedown.jstree",$.proxy(function(e){if(e.which===1){this.start_drag(e.currentTarget,e);return false}},this)).delegate("a","mouseenter.jstree",$.proxy(function(e){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree){this.dnd_enter(e.currentTarget)}},this)).delegate("a","mousemove.jstree",$.proxy(function(e){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree){if(typeof this.data.dnd.off.top==="undefined"){this.data.dnd.off=$(e.target).offset()}this.data.dnd.w=(e.pageY-(this.data.dnd.off.top||0))%this.data.core.li_height;if(this.data.dnd.w<0){this.data.dnd.w+=this.data.core.li_height}this.dnd_show()}},this)).delegate("a","mouseleave.jstree",$.proxy(function(e){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree){this.data.dnd.after=false;this.data.dnd.before=false;this.data.dnd.inside=false;$.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");m.hide();if(r&&r[0]===e.target.parentNode){if(this.data.dnd.to1){clearTimeout(this.data.dnd.to1);this.data.dnd.to1=false}if(this.data.dnd.to2){clearTimeout(this.data.dnd.to2);this.data.dnd.to2=false}}}},this)).delegate("a","mouseup.jstree",$.proxy(function(e){if($.vakata.dnd.is_drag&&$.vakata.dnd.user_data.jstree){this.dnd_finish(e)}},this));$(document).bind("drag_stop.vakata",$.proxy(function(){this.data.dnd.after=false;this.data.dnd.before=false;this.data.dnd.inside=false;this.data.dnd.off=false;this.data.dnd.prepared=false;this.data.dnd.w=false;this.data.dnd.to1=false;this.data.dnd.to2=false;this.data.dnd.active=false;this.data.dnd.foreign=false;if(m){m.css({top:"-2000px"})}},this)).bind("drag_start.vakata",$.proxy(function(e,data){if(data.data.jstree){var et=$(data.event.target);if(et.closest(".jstree").hasClass("jstree-"+this.get_index())){this.dnd_enter(et)}}},this));var s=this._get_settings().dnd;if(s.drag_target){$(document).delegate(s.drag_target,"mousedown.jstree",$.proxy(function(e){o=e.target;$.vakata.dnd.drag_start(e,{jstree:true,obj:e.target},"<ins class='jstree-icon'></ins>"+$(e.target).text());if(this.data.themes){m.attr("class","jstree-"+this.data.themes.theme);$.vakata.dnd.helper.attr("class","jstree-dnd-helper jstree-"+this.data.themes.theme)}$.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");var cnt=this.get_container();this.data.dnd.cof=cnt.offset();this.data.dnd.cw=parseInt(cnt.width(),10);this.data.dnd.ch=parseInt(cnt.height(),10);this.data.dnd.foreign=true;return false},this))}if(s.drop_target){$(document).delegate(s.drop_target,"mouseenter.jstree",$.proxy(function(e){if(this.data.dnd.active&&this._get_settings().dnd.drop_check.call(this,{o:o,r:$(e.target)})){$.vakata.dnd.helper.children("ins").attr("class","jstree-ok")}},this)).delegate(s.drop_target,"mouseleave.jstree",$.proxy(function(e){if(this.data.dnd.active){$.vakata.dnd.helper.children("ins").attr("class","jstree-invalid")}},this)).delegate(s.drop_target,"mouseup.jstree",$.proxy(function(e){if(this.data.dnd.active&&$.vakata.dnd.helper.children("ins").hasClass("jstree-ok")){this._get_settings().dnd.drop_finish.call(this,{o:o,r:$(e.target)})}},this))}},defaults:{copy_modifier:"ctrl",check_timeout:200,open_timeout:500,drop_target:".jstree-drop",drop_check:function(data){return true},drop_finish:$.noop,drag_target:".jstree-draggable",drag_finish:$.noop,drag_check:function(data){return{after:false,before:false,inside:true}}},_fn:{dnd_prepare:function(){if(!r||!r.length){return}this.data.dnd.off=r.offset();if(this._get_settings().core.rtl){this.data.dnd.off.right=this.data.dnd.off.left+r.width()}if(this.data.dnd.foreign){var a=this._get_settings().dnd.drag_check.call(this,{o:o,r:r});this.data.dnd.after=a.after;this.data.dnd.before=a.before;this.data.dnd.inside=a.inside;this.data.dnd.prepared=true;return this.dnd_show()}this.prepare_move(o,r,"before");this.data.dnd.before=this.check_move();this.prepare_move(o,r,"after");this.data.dnd.after=this.check_move();if(this._is_loaded(r)){this.prepare_move(o,r,"inside");this.data.dnd.inside=this.check_move()}else{this.data.dnd.inside=false}this.data.dnd.prepared=true;return this.dnd_show()},dnd_show:function(){if(!this.data.dnd.prepared){return}var o=["before","inside","after"],r=false,rtl=this._get_settings().core.rtl,pos;if(this.data.dnd.w<this.data.core.li_height/3){o=["before","inside","after"]}else{if(this.data.dnd.w<=this.data.core.li_height*2/3){o=this.data.dnd.w<this.data.core.li_height/2?["inside","before","after"]:["inside","after","before"]}else{o=["after","inside","before"]}}$.each(o,$.proxy(function(i,val){if(this.data.dnd[val]){$.vakata.dnd.helper.children("ins").attr("class","jstree-ok");r=val;return false}},this));if(r===false){$.vakata.dnd.helper.children("ins").attr("class","jstree-invalid")}pos=rtl?(this.data.dnd.off.right-18):(this.data.dnd.off.left+10);switch(r){case"before":m.css({left:pos+"px",top:(this.data.dnd.off.top-6)+"px"}).show();break;case"after":m.css({left:pos+"px",top:(this.data.dnd.off.top+this.data.core.li_height-7)+"px"}).show();break;case"inside":m.css({left:pos+(rtl?-4:4)+"px",top:(this.data.dnd.off.top+this.data.core.li_height/2-5)+"px"}).show();break;default:m.hide();break}return r},dnd_open:function(){this.data.dnd.to2=false;this.open_node(r,$.proxy(this.dnd_prepare,this),true)},dnd_finish:function(e){if(this.data.dnd.foreign){if(this.data.dnd.after||this.data.dnd.before||this.data.dnd.inside){this._get_settings().dnd.drag_finish.call(this,{o:o,r:r})}}else{this.dnd_prepare();this.move_node(o,r,this.dnd_show(),e[this._get_settings().dnd.copy_modifier+"Key"])}o=false;r=false;m.hide()},dnd_enter:function(obj){var s=this._get_settings().dnd;this.data.dnd.prepared=false;r=this._get_node(obj);if(s.check_timeout){if(this.data.dnd.to1){clearTimeout(this.data.dnd.to1)}this.data.dnd.to1=setTimeout($.proxy(this.dnd_prepare,this),s.check_timeout)}else{this.dnd_prepare()}if(s.open_timeout){if(this.data.dnd.to2){clearTimeout(this.data.dnd.to2)}if(r&&r.length&&r.hasClass("jstree-closed")){this.data.dnd.to2=setTimeout($.proxy(this.dnd_open,this),s.open_timeout)}}else{if(r&&r.length&&r.hasClass("jstree-closed")){this.dnd_open()}}},start_drag:function(obj,e){o=this._get_node(obj);if(this.data.ui&&this.is_selected(o)){o=this._get_node(null,true)}$.vakata.dnd.drag_start(e,{jstree:true,obj:o},"<ins class='jstree-icon'></ins>"+(o.length>1?"Multiple selection":this.get_text(o)));if(this.data.themes){m.attr("class","jstree-"+this.data.themes.theme);$.vakata.dnd.helper.attr("class","jstree-dnd-helper jstree-"+this.data.themes.theme)}var cnt=this.get_container();this.data.dnd.cof=cnt.children("ul").offset();this.data.dnd.cw=parseInt(cnt.width(),10);this.data.dnd.ch=parseInt(cnt.height(),10);this.data.dnd.active=true}}});$(function(){var css_string="#vakata-dragged ins { display:block; text-decoration:none; width:16px; height:16px; margin:0 0 0 0; padding:0; position:absolute; top:4px; left:4px; } #vakata-dragged .jstree-ok { background:green; } #vakata-dragged .jstree-invalid { background:red; } #jstree-marker { padding:0; margin:0; line-height:12px; font-size:1px; overflow:hidden; height:12px; width:8px; position:absolute; top:-30px; z-index:10000; background-repeat:no-repeat; display:none; background-color:silver; } ";$.vakata.css.add_sheet({str:css_string});m=$("<div>").attr({id:"jstree-marker"}).hide().appendTo("body");$(document).bind("drag_start.vakata",function(e,data){if(data.data.jstree){m.show()}});$(document).bind("drag_stop.vakata",function(e,data){if(data.data.jstree){m.hide()}})})})(jQuery);(function($){$.jstree.plugin("checkbox",{__init:function(){this.select_node=this.deselect_node=this.deselect_all=$.noop;this.get_selected=this.get_checked;this.get_container().bind("open_node.jstree create_node.jstree clean_node.jstree",$.proxy(function(e,data){this._prepare_checkboxes(data.rslt.obj)},this)).bind("loaded.jstree",$.proxy(function(e){this._prepare_checkboxes()},this)).delegate("a","click.jstree",$.proxy(function(e){if(this._get_node(e.target).hasClass("jstree-checked")){this.uncheck_node(e.target)}else{this.check_node(e.target)}if(this.data.ui){this.save_selected()}if(this.data.cookies){this.save_cookie("select_node")}e.preventDefault()},this))},__destroy:function(){this.get_container().find(".jstree-checkbox").remove()},_fn:{_prepare_checkboxes:function(obj){obj=!obj||obj==-1?this.get_container():this._get_node(obj);var c,_this=this,t;obj.each(function(){t=$(this);c=t.is("li")&&t.hasClass("jstree-checked")?"jstree-checked":"jstree-unchecked";t.find("a").not(":has(.jstree-checkbox)").prepend("<ins class='jstree-checkbox'>&#160;</ins>").parent().not(".jstree-checked, .jstree-unchecked").addClass(c)});if(obj.is("li")){this._repair_state(obj)}else{obj.find("> ul > li").each(function(){_this._repair_state(this)})}},change_state:function(obj,state){obj=this._get_node(obj);state=(state===false||state===true)?state:obj.hasClass("jstree-checked");if(state){obj.find("li").andSelf().removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked")}else{obj.find("li").andSelf().removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked");if(this.data.ui){this.data.ui.last_selected=obj}this.data.checkbox.last_selected=obj}obj.parentsUntil(".jstree","li").each(function(){var $this=$(this);if(state){if($this.children("ul").children(".jstree-checked, .jstree-undetermined").length){$this.parentsUntil(".jstree","li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");return false}else{$this.removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked")}}else{if($this.children("ul").children(".jstree-unchecked, .jstree-undetermined").length){$this.parentsUntil(".jstree","li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");return false}else{$this.removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked")}}});if(this.data.ui){this.data.ui.selected=this.get_checked()}this.__callback(obj)},check_node:function(obj){this.change_state(obj,false)},uncheck_node:function(obj){this.change_state(obj,true)},check_all:function(){var _this=this;this.get_container().children("ul").children("li").each(function(){_this.check_node(this,false)})},uncheck_all:function(){var _this=this;this.get_container().children("ul").children("li").each(function(){_this.change_state(this,true)})},is_checked:function(obj){obj=this._get_node(obj);return obj.length?obj.is(".jstree-checked"):false},get_checked:function(obj){obj=!obj||obj===-1?this.get_container():this._get_node(obj);return obj.find("> ul > .jstree-checked, .jstree-undetermined > ul > .jstree-checked")},get_unchecked:function(obj){obj=!obj||obj===-1?this.get_container():this._get_node(obj);return obj.find("> ul > .jstree-unchecked, .jstree-undetermined > ul > .jstree-unchecked")},show_checkboxes:function(){this.get_container().children("ul").removeClass("jstree-no-checkboxes")},hide_checkboxes:function(){this.get_container().children("ul").addClass("jstree-no-checkboxes")},_repair_state:function(obj){obj=this._get_node(obj);if(!obj.length){return}var a=obj.find("> ul > .jstree-checked").length,b=obj.find("> ul > .jstree-undetermined").length,c=obj.find("> ul > li").length;if(c===0){if(obj.hasClass("jstree-undetermined")){this.check_node(obj)}}else{if(a===0&&b===0){this.uncheck_node(obj)}else{if(a===c){this.check_node(obj)}else{obj.parentsUntil(".jstree","li").removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined")}}}},reselect:function(){if(this.data.ui){var _this=this,s=this.data.ui.to_select;s=$.map($.makeArray(s),function(n){return"#"+n.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/")});this.deselect_all();$.each(s,function(i,val){_this.check_node(val)});this.__callback()}}}})})(jQuery);(function($){$.vakata.xslt=function(xml,xsl,callback){var rs="",xm,xs,processor,support;if(document.recalc){xm=document.createElement("xml");xs=document.createElement("xml");xm.innerHTML=xml;xs.innerHTML=xsl;$("body").append(xm).append(xs);setTimeout((function(xm,xs,callback){return function(){callback.call(null,xm.transformNode(xs.XMLDocument));setTimeout((function(xm,xs){return function(){jQuery("body").remove(xm).remove(xs)}})(xm,xs),200)}})(xm,xs,callback),100);return true}if(typeof window.DOMParser!=="undefined"&&typeof window.XMLHttpRequest!=="undefined"&&typeof window.XSLTProcessor!=="undefined"){processor=new XSLTProcessor();support=$.isFunction(processor.transformDocument)?(typeof window.XMLSerializer!=="undefined"):true;if(!support){return false}xml=new DOMParser().parseFromString(xml,"text/xml");xsl=new DOMParser().parseFromString(xsl,"text/xml");if($.isFunction(processor.transformDocument)){rs=document.implementation.createDocument("","",null);processor.transformDocument(xml,xsl,rs,null);callback.call(null,XMLSerializer().serializeToString(rs));return true}else{processor.importStylesheet(xsl);rs=processor.transformToFragment(xml,document);callback.call(null,$("<div>").append(rs).html());return true}}return false};var xsl={nest:'<?xml version="1.0" encoding="utf-8" ?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ><xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/html" /><xsl:template match="/"> <xsl:call-template name="nodes"> <xsl:with-param name="node" select="/root" /> </xsl:call-template></xsl:template><xsl:template name="nodes"> <xsl:param name="node" /> <ul> <xsl:for-each select="$node/item"> <xsl:variable name="children" select="count(./item) &gt; 0" /> <li> <xsl:attribute name="class"> <xsl:if test="position() = last()">jstree-last </xsl:if> <xsl:choose> <xsl:when test="@state = \'open\'">jstree-open </xsl:when> <xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when> <xsl:otherwise>jstree-leaf </xsl:otherwise> </xsl:choose> <xsl:value-of select="@class" /> </xsl:attribute> <xsl:for-each select="@*"> <xsl:if test="name() != \'class\' and name() != \'state\' and name() != \'hasChildren\'"> <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute> </xsl:if> </xsl:for-each> <ins class="jstree-icon"><xsl:text>&#xa0;</xsl:text></ins> <xsl:for-each select="content/name"> <a> <xsl:attribute name="href"> <xsl:choose> <xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when> <xsl:otherwise>#</xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute> <xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute> <xsl:for-each select="@*"> <xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'"> <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute> </xsl:if> </xsl:for-each> <ins> <xsl:attribute name="class">jstree-icon <xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if> </xsl:attribute> <xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if> <xsl:text>&#xa0;</xsl:text> </ins> <xsl:value-of select="current()" /> </a> </xsl:for-each> <xsl:if test="$children or @hasChildren"><xsl:call-template name="nodes"><xsl:with-param name="node" select="current()" /></xsl:call-template></xsl:if> </li> </xsl:for-each> </ul></xsl:template></xsl:stylesheet>',flat:'<?xml version="1.0" encoding="utf-8" ?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ><xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/xml" /><xsl:template match="/"> <ul> <xsl:for-each select="//item[not(@parent_id) or @parent_id=0 or not(@parent_id = //item/@id)]"> <xsl:call-template name="nodes"> <xsl:with-param name="node" select="." /> <xsl:with-param name="is_last" select="number(position() = last())" /> </xsl:call-template> </xsl:for-each> </ul></xsl:template><xsl:template name="nodes"> <xsl:param name="node" /> <xsl:param name="is_last" /> <xsl:variable name="children" select="count(//item[@parent_id=$node/attribute::id]) &gt; 0" /> <li> <xsl:attribute name="class"> <xsl:if test="$is_last = true()">jstree-last </xsl:if> <xsl:choose> <xsl:when test="@state = \'open\'">jstree-open </xsl:when> <xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when> <xsl:otherwise>jstree-leaf </xsl:otherwise> </xsl:choose> <xsl:value-of select="@class" /> </xsl:attribute> <xsl:for-each select="@*"> <xsl:if test="name() != \'parent_id\' and name() != \'hasChildren\' and name() != \'class\' and name() != \'state\'"> <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute> </xsl:if> </xsl:for-each> <ins class="jstree-icon"><xsl:text>&#xa0;</xsl:text></ins> <xsl:for-each select="content/name"> <a> <xsl:attribute name="href"> <xsl:choose> <xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when> <xsl:otherwise>#</xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute> <xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute> <xsl:for-each select="@*"> <xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'"> <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute> </xsl:if> </xsl:for-each> <ins> <xsl:attribute name="class">jstree-icon <xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if> </xsl:attribute> <xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if> <xsl:text>&#xa0;</xsl:text> </ins> <xsl:value-of select="current()" /> </a> </xsl:for-each> <xsl:if test="$children"> <ul> <xsl:for-each select="//item[@parent_id=$node/attribute::id]"> <xsl:call-template name="nodes"> <xsl:with-param name="node" select="." /> <xsl:with-param name="is_last" select="number(position() = last())" /> </xsl:call-template> </xsl:for-each> </ul> </xsl:if> </li></xsl:template></xsl:stylesheet>'};$.jstree.plugin("xml_data",{defaults:{data:false,ajax:false,xsl:"flat",clean_node:false,correct_state:true},_fn:{load_node:function(obj,s_call,e_call){var _this=this;this.load_node_xml(obj,function(){_this.__callback({obj:obj});s_call.call(this)},e_call)},_is_loaded:function(obj){var s=this._get_settings().xml_data;obj=this._get_node(obj);return obj==-1||!obj||!s.ajax||obj.is(".jstree-open, .jstree-leaf")||obj.children("ul").children("li").size()>0},load_node_xml:function(obj,s_call,e_call){var s=this.get_settings().xml_data,error_func=function(){},success_func=function(){};obj=this._get_node(obj);if(obj&&obj!==-1){if(obj.data("jstree-is-loading")){return}else{obj.data("jstree-is-loading",true)}}switch(!0){case (!s.data&&!s.ajax):throw"Neither data nor ajax settings supplied.";case (!!s.data&&!s.ajax)||(!!s.data&&!!s.ajax&&(!obj||obj===-1)):if(!obj||obj==-1){this.parse_xml(s.data,$.proxy(function(d){if(d){d=d.replace(/ ?xmlns="[^"]*"/ig,"");if(d.length>10){d=$(d);this.get_container().children("ul").empty().append(d.children());if(s.clean_node){this.clean_node(obj)}if(s_call){s_call.call(this)}}}else{if(s.correct_state){this.get_container().children("ul").empty();if(s_call){s_call.call(this)}}}},this))}break;case (!s.data&&!!s.ajax)||(!!s.data&&!!s.ajax&&obj&&obj!==-1):error_func=function(x,t,e){var ef=this.get_settings().xml_data.ajax.error;if(ef){ef.call(this,x,t,e)}if(obj!==-1&&obj.length){obj.children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false);if(t==="success"&&s.correct_state){obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}}else{if(t==="success"&&s.correct_state){this.get_container().children("ul").empty()}}if(e_call){e_call.call(this)}};success_func=function(d,t,x){d=x.responseText;var sf=this.get_settings().xml_data.ajax.success;if(sf){d=sf.call(this,d,t,x)||d}if(d==""){return error_func.call(this,x,t,"")}this.parse_xml(d,$.proxy(function(d){if(d){d=d.replace(/ ?xmlns="[^"]*"/ig,"");if(d.length>10){d=$(d);if(obj===-1||!obj){this.get_container().children("ul").empty().append(d.children())}else{obj.children(".jstree-loading").removeClass("jstree-loading");obj.append(d);obj.data("jstree-is-loading",false)}if(s.clean_node){this.clean_node(obj)}if(s_call){s_call.call(this)}}else{if(obj&&obj!==-1){obj.children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false);if(s.correct_state){obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");if(s_call){s_call.call(this)}}}else{if(s.correct_state){this.get_container().children("ul").empty();if(s_call){s_call.call(this)}}}}}},this))};s.ajax.context=this;s.ajax.error=error_func;s.ajax.success=success_func;if(!s.ajax.dataType){s.ajax.dataType="xml"}if($.isFunction(s.ajax.url)){s.ajax.url=s.ajax.url.call(this,obj)}if($.isFunction(s.ajax.data)){s.ajax.data=s.ajax.data.call(this,obj)}$.ajax(s.ajax);break}},parse_xml:function(xml,callback){var s=this._get_settings().xml_data;$.vakata.xslt(xml,xsl[s.xsl],callback)},get_xml:function(tp,obj,li_attr,a_attr,is_callback){var result="",s=this._get_settings(),_this=this,tmp1,tmp2,li,a,lang;if(!tp){tp="flat"}if(!is_callback){is_callback=0}obj=this._get_node(obj);if(!obj||obj===-1){obj=this.get_container().find("> ul > li")}li_attr=$.isArray(li_attr)?li_attr:["id","class"];if(!is_callback&&this.data.types&&$.inArray(s.types.type_attr,li_attr)===-1){li_attr.push(s.types.type_attr)}a_attr=$.isArray(a_attr)?a_attr:[];if(!is_callback){result+="<root>"}obj.each(function(){result+="<item";li=$(this);$.each(li_attr,function(i,v){result+=" "+v+'="'+(li.attr(v)||"").replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"")+'"'});if(li.hasClass("jstree-open")){result+=' state="open"'}if(li.hasClass("jstree-closed")){result+=' state="closed"'}if(tp==="flat"){result+=' parent_id="'+is_callback+'"'}result+=">";result+="<content>";a=li.children("a");a.each(function(){tmp1=$(this);lang=false;result+="<name";if($.inArray("languages",s.plugins)!==-1){$.each(s.languages,function(k,z){if(tmp1.hasClass(z)){result+=' lang="'+z+'"';lang=z;return false}})}if(a_attr.length){$.each(a_attr,function(k,z){result+=" "+z+'="'+(tmp1.attr(z)||"").replace(/jstree[^ ]*|$/ig,"")+'"'})}if(tmp1.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"").length){result+=' icon="'+tmp1.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"")+'"'}if(tmp1.children("ins").get(0).style.backgroundImage.length){result+=' icon="'+tmp1.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")","")+'"'}result+=">";result+="<![CDATA["+_this.get_text(tmp1,lang)+"]]>";result+="</name>"});result+="</content>";tmp2=li[0].id;li=li.find("> ul > li");if(li.length){tmp2=_this.get_xml(tp,li,li_attr,a_attr,tmp2)}else{tmp2=""}if(tp=="nest"){result+=tmp2}result+="</item>";if(tp=="flat"){result+=tmp2}});if(!is_callback){result+="</root>"}return result}}})})(jQuery);(function($){$.expr[":"].jstree_contains=function(a,i,m){return(a.textContent||a.innerText||"").toLowerCase().indexOf(m[3].toLowerCase())>=0};$.jstree.plugin("search",{__init:function(){this.data.search.str="";this.data.search.result=$()},defaults:{ajax:false,case_insensitive:false},_fn:{search:function(str,skip_async){if(str===""){return}var s=this.get_settings().search,t=this,error_func=function(){},success_func=function(){};this.data.search.str=str;if(!skip_async&&s.ajax!==false&&this.get_container().find(".jstree-closed:eq(0)").length>0){this.search.supress_callback=true;error_func=function(){};success_func=function(d,t,x){var sf=this.get_settings().search.ajax.success;if(sf){d=sf.call(this,d,t,x)||d}this.data.search.to_open=d;this._search_open()};s.ajax.context=this;s.ajax.error=error_func;s.ajax.success=success_func;if($.isFunction(s.ajax.url)){s.ajax.url=s.ajax.url.call(this,str)}if($.isFunction(s.ajax.data)){s.ajax.data=s.ajax.data.call(this,str)}if(!s.ajax.data){s.ajax.data={search_string:str}}if(!s.ajax.dataType||/^json/.exec(s.ajax.dataType)){s.ajax.dataType="json"}$.ajax(s.ajax);return}if(this.data.search.result.length){this.clear_search()}this.data.search.result=this.get_container().find("a"+(this.data.languages?"."+this.get_lang():"")+":"+(s.case_insensitive?"jstree_contains":"contains")+"("+this.data.search.str+")");this.data.search.result.addClass("jstree-search").parents(".jstree-closed").each(function(){t.open_node(this,false,true)});this.__callback({nodes:this.data.search.result,str:str})},clear_search:function(str){this.data.search.result.removeClass("jstree-search");this.__callback(this.data.search.result);this.data.search.result=$()},_search_open:function(is_callback){var _this=this,done=true,current=[],remaining=[];if(this.data.search.to_open.length){$.each(this.data.search.to_open,function(i,val){if(val=="#"){return true}if($(val).length&&$(val).is(".jstree-closed")){current.push(val)}else{remaining.push(val)}});if(current.length){this.data.search.to_open=remaining;$.each(current,function(i,val){_this.open_node(val,function(){_this._search_open(true)})});done=false}}if(done){this.search(this.data.search.str,true)}}}})})(jQuery);(function($){$.vakata.context={cnt:$("<div id='vakata-contextmenu'/>"),vis:false,tgt:false,par:false,func:false,data:false,show:function(s,t,x,y,d,p){var html=$.vakata.context.parse(s),h,w;if(!html){return}$.vakata.context.vis=true;$.vakata.context.tgt=t;$.vakata.context.par=p||t||null;$.vakata.context.data=d||null;$.vakata.context.cnt.html(html).css({visibility:"hidden",display:"block",left:0,top:0});h=$.vakata.context.cnt.height();w=$.vakata.context.cnt.width();if(x+w>$(document).width()){x=$(document).width()-(w+5);$.vakata.context.cnt.find("li > ul").addClass("right")}if(y+h>$(document).height()){y=y-(h+t[0].offsetHeight);$.vakata.context.cnt.find("li > ul").addClass("bottom")}$.vakata.context.cnt.css({left:x,top:y}).find("li:has(ul)").bind("mouseenter",function(e){var w=$(document).width(),h=$(document).height(),ul=$(this).children("ul").show();if(w!==$(document).width()){ul.toggleClass("right")}if(h!==$(document).height()){ul.toggleClass("bottom")}}).bind("mouseleave",function(e){$(this).children("ul").hide()}).end().css({visibility:"visible"}).show();$(document).triggerHandler("context_show.vakata")},hide:function(){$.vakata.context.vis=false;$.vakata.context.cnt.attr("class","").hide();$(document).triggerHandler("context_hide.vakata")},parse:function(s,is_callback){if(!s){return false}var str="",tmp=false,was_sep=true;if(!is_callback){$.vakata.context.func={}}str+="<ul>";$.each(s,function(i,val){if(!val){return true}$.vakata.context.func[i]=val.action;if(!was_sep&&val.separator_before){str+="<li class='vakata-separator vakata-separator-before'></li>"}was_sep=false;str+="<li class='"+(val._class||"")+(val._disabled?" jstree-contextmenu-disabled ":"")+"'><ins ";if(val.icon&&val.icon.indexOf("/")===-1){str+=" class='"+val.icon+"' "}if(val.icon&&val.icon.indexOf("/")!==-1){str+=" style='background:url("+val.icon+") center center no-repeat;' "}str+=">&#160;</ins><a href='#' rel='"+i+"'>";if(val.submenu){str+="<span style='float:right;'>&raquo;</span>"}str+=val.label+"</a>";if(val.submenu){tmp=$.vakata.context.parse(val.submenu,true);if(tmp){str+=tmp}}str+="</li>";if(val.separator_after){str+="<li class='vakata-separator vakata-separator-after'></li>";was_sep=true}});str=str.replace(/<li class\='vakata-separator vakata-separator-after'\><\/li\>$/,"");str+="</ul>";return str.length>10?str:false},exec:function(i){if($.isFunction($.vakata.context.func[i])){$.vakata.context.func[i].call($.vakata.context.data,$.vakata.context.par);return true}else{return false}}};$(function(){var css_string="#vakata-contextmenu { display:none; position:absolute; margin:0; padding:0; min-width:180px; background:#ebebeb; border:1px solid silver; z-index:10000; *width:180px; } #vakata-contextmenu ul { min-width:180px; *width:180px; } #vakata-contextmenu ul, #vakata-contextmenu li { margin:0; padding:0; list-style-type:none; display:block; } #vakata-contextmenu li { line-height:20px; min-height:20px; position:relative; padding:0px; } #vakata-contextmenu li a { padding:1px 6px; line-height:17px; display:block; text-decoration:none; margin:1px 1px 0 1px; } #vakata-contextmenu li ins { float:left; width:16px; height:16px; text-decoration:none; margin-right:2px; } #vakata-contextmenu li a:hover, #vakata-contextmenu li.vakata-hover > a { background:gray; color:white; } #vakata-contextmenu li ul { display:none; position:absolute; top:-2px; left:100%; background:#ebebeb; border:1px solid gray; } #vakata-contextmenu .right { right:100%; left:auto; } #vakata-contextmenu .bottom { bottom:-1px; top:auto; } #vakata-contextmenu li.vakata-separator { min-height:0; height:1px; line-height:1px; font-size:1px; overflow:hidden; margin:0 2px; background:silver; /* border-top:1px solid #fefefe; */ padding:0; } ";$.vakata.css.add_sheet({str:css_string});$.vakata.context.cnt.delegate("a","click",function(e){e.preventDefault()}).delegate("a","mouseup",function(e){if(!$(this).parent().hasClass("jstree-contextmenu-disabled")&&$.vakata.context.exec($(this).attr("rel"))){$.vakata.context.hide()}else{$(this).blur()}}).delegate("a","mouseover",function(){$.vakata.context.cnt.find(".vakata-hover").removeClass("vakata-hover")}).appendTo("body");$(document).bind("mousedown",function(e){if($.vakata.context.vis&&!$.contains($.vakata.context.cnt[0],e.target)){$.vakata.context.hide()}});if(typeof $.hotkeys!=="undefined"){$(document).bind("keydown","up",function(e){if($.vakata.context.vis){var o=$.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").prevAll("li:not(.vakata-separator)").first();if(!o.length){o=$.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").last()}o.addClass("vakata-hover");e.stopImmediatePropagation();e.preventDefault()}}).bind("keydown","down",function(e){if($.vakata.context.vis){var o=$.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").nextAll("li:not(.vakata-separator)").first();if(!o.length){o=$.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").first()}o.addClass("vakata-hover");e.stopImmediatePropagation();e.preventDefault()}}).bind("keydown","right",function(e){if($.vakata.context.vis){$.vakata.context.cnt.find(".vakata-hover").children("ul").show().children("li:not(.vakata-separator)").removeClass("vakata-hover").first().addClass("vakata-hover");e.stopImmediatePropagation();e.preventDefault()}}).bind("keydown","left",function(e){if($.vakata.context.vis){$.vakata.context.cnt.find(".vakata-hover").children("ul").hide().children(".vakata-separator").removeClass("vakata-hover");e.stopImmediatePropagation();e.preventDefault()}}).bind("keydown","esc",function(e){$.vakata.context.hide();e.preventDefault()}).bind("keydown","space",function(e){$.vakata.context.cnt.find(".vakata-hover").last().children("a").click();e.preventDefault()})}});$.jstree.plugin("contextmenu",{__init:function(){this.get_container().delegate("a","contextmenu.jstree",$.proxy(function(e){e.preventDefault();this.show_contextmenu(e.currentTarget,e.pageX,e.pageY)},this)).bind("destroy.jstree",$.proxy(function(){if(this.data.contextmenu){$.vakata.context.hide()}},this));$(document).bind("context_hide.vakata",$.proxy(function(){this.data.contextmenu=false},this))},defaults:{select_node:false,show_at_node:true,items:{create:{separator_before:false,separator_after:true,label:"Create",action:function(obj){this.create(obj)}},rename:{separator_before:false,separator_after:false,label:"Rename",action:function(obj){this.rename(obj)}},remove:{separator_before:false,icon:false,separator_after:false,label:"Delete",action:function(obj){this.remove(obj)}},ccp:{separator_before:true,icon:false,separator_after:false,label:"Edit",action:false,submenu:{cut:{separator_before:false,separator_after:false,label:"Cut",action:function(obj){this.cut(obj)}},copy:{separator_before:false,icon:false,separator_after:false,label:"Copy",action:function(obj){this.copy(obj)}},paste:{separator_before:false,icon:false,separator_after:false,label:"Paste",action:function(obj){this.paste(obj)}}}}}},_fn:{show_contextmenu:function(obj,x,y){obj=this._get_node(obj);var s=this.get_settings().contextmenu,a=obj.children("a:visible:eq(0)"),o=false;if(s.select_node&&this.data.ui&&!this.is_selected(obj)){this.deselect_all();this.select_node(obj,true)}if(s.show_at_node||typeof x==="undefined"||typeof y==="undefined"){o=a.offset();x=o.left;y=o.top+this.data.core.li_height}if($.isFunction(s.items)){s.items=s.items.call(this,obj)}this.data.contextmenu=true;$.vakata.context.show(s.items,a,x,y,this,obj);if(this.data.themes){$.vakata.context.cnt.attr("class","jstree-"+this.data.themes.theme+"-context")}}}})})(jQuery);(function($){$.jstree.plugin("types",{__init:function(){var s=this._get_settings().types;this.data.types.attach_to=[];this.get_container().bind("init.jstree",$.proxy(function(){var types=s.types,attr=s.type_attr,icons_css="",_this=this;$.each(types,function(i,tp){$.each(tp,function(k,v){if(!/^(max_depth|max_children|icon|valid_children)$/.test(k)){_this.data.types.attach_to.push(k)}});if(!tp.icon){return true}if(tp.icon.image||tp.icon.position){if(i=="default"){icons_css+=".jstree-"+_this.get_index()+" a > .jstree-icon { "}else{icons_css+=".jstree-"+_this.get_index()+" li["+attr+"="+i+"] > a > .jstree-icon { "}if(tp.icon.image){icons_css+=" background-image:url("+tp.icon.image+"); "}if(tp.icon.position){icons_css+=" background-position:"+tp.icon.position+"; "}else{icons_css+=" background-position:0 0; "}icons_css+="} "}});if(icons_css!=""){$.vakata.css.add_sheet({str:icons_css})}},this)).bind("before.jstree",$.proxy(function(e,data){if($.inArray(data.func,this.data.types.attach_to)!==-1){var s=this._get_settings().types.types,t=this._get_type(data.args[0]);if(((s[t]&&typeof s[t][data.func]!=="undefined")||(s["default"]&&typeof s["default"][data.func]!=="undefined"))&&!this._check(data.func,data.args[0])){e.stopImmediatePropagation();return false}}},this))},defaults:{max_children:-1,max_depth:-1,valid_children:"all",type_attr:"rel",types:{"default":{max_children:-1,max_depth:-1,valid_children:"all"}}},_fn:{_get_type:function(obj){obj=this._get_node(obj);return(!obj||!obj.length)?false:obj.attr(this._get_settings().types.type_attr)||"default"},set_type:function(str,obj){obj=this._get_node(obj);return(!obj.length||!str)?false:obj.attr(this._get_settings().types.type_attr,str)},_check:function(rule,obj,opts){var v=false,t=this._get_type(obj),d=0,_this=this,s=this._get_settings().types;if(obj===-1){if(!!s[rule]){v=s[rule]}else{return}}else{if(t===false){return}if(!!s.types[t]&&!!s.types[t][rule]){v=s.types[t][rule]}else{if(!!s.types["default"]&&!!s.types["default"][rule]){v=s.types["default"][rule]}}}if($.isFunction(v)){v=v.call(this,obj)}if(rule==="max_depth"&&obj!==-1&&opts!==false&&s.max_depth!==-2&&v!==0){this._get_node(obj).children("a:eq(0)").parentsUntil(".jstree","li").each(function(i){if(s.max_depth!==-1&&s.max_depth-(i+1)<=0){v=0;return false}d=(i===0)?v:_this._check(rule,this,false);if(d!==-1&&d-(i+1)<=0){v=0;return false}if(d>=0&&(d-(i+1)<v||v<0)){v=d-(i+1)}if(s.max_depth>=0&&(s.max_depth-(i+1)<v||v<0)){v=s.max_depth-(i+1)}})}return v},check_move:function(){if(!this.__call_old()){return false}var m=this._get_move(),s=m.rt._get_settings().types,mc=m.rt._check("max_children",m.cr),md=m.rt._check("max_depth",m.cr),vc=m.rt._check("valid_children",m.cr),ch=0,d=1,t;if(vc==="none"){return false}if($.isArray(vc)&&m.ot&&m.ot._get_type){m.o.each(function(){if($.inArray(m.ot._get_type(this),vc)===-1){d=false;return false}});if(d===false){return false}}if(s.max_children!==-2&&mc!==-1){ch=m.cr===-1?this.get_container().children("> ul > li").not(m.o).length:m.cr.children("> ul > li").not(m.o).length;if(ch+m.o.length>mc){return false}}if(s.max_depth!==-2&&md!==-1){d=0;if(md===0){return false}if(typeof m.o.d==="undefined"){t=m.o;while(t.length>0){t=t.find("> ul > li");d++}m.o.d=d}if(md-m.o.d<0){return false}}return true},create_node:function(obj,position,js,callback,is_loaded,skip_check){if(!skip_check&&(is_loaded||this._is_loaded(obj))){var p=(position&&position.match(/^before|after$/i)&&obj!==-1)?this._get_parent(obj):this._get_node(obj),s=this._get_settings().types,mc=this._check("max_children",p),md=this._check("max_depth",p),vc=this._check("valid_children",p),ch;if(!js){js={}}if(vc==="none"){return false}if($.isArray(vc)){if(!js.attr||!js.attr[s.type_attr]){if(!js.attr){js.attr={}}js.attr[s.type_attr]=vc[0]}else{if($.inArray(js.attr[s.type_attr],vc)===-1){return false}}}if(s.max_children!==-2&&mc!==-1){ch=p===-1?this.get_container().children("> ul > li").length:p.children("> ul > li").length;if(ch+1>mc){return false}}if(s.max_depth!==-2&&md!==-1&&(md-1)<0){return false}}return this.__call_old(true,obj,position,js,callback,is_loaded,skip_check)}}})})(jQuery);(function($){$.jstree.plugin("html_data",{__init:function(){this.data.html_data.original_container_html=this.get_container().find(" > ul > li").clone(true);this.data.html_data.original_container_html.find("li").andSelf().contents().filter(function(){return this.nodeType==3}).remove()},defaults:{data:false,ajax:false,correct_state:true},_fn:{load_node:function(obj,s_call,e_call){var _this=this;this.load_node_html(obj,function(){_this.__callback({obj:obj});s_call.call(this)},e_call)},_is_loaded:function(obj){obj=this._get_node(obj);return obj==-1||!obj||!this._get_settings().html_data.ajax||obj.is(".jstree-open, .jstree-leaf")||obj.children("ul").children("li").size()>0},load_node_html:function(obj,s_call,e_call){var d,s=this.get_settings().html_data,error_func=function(){},success_func=function(){};obj=this._get_node(obj);if(obj&&obj!==-1){if(obj.data("jstree-is-loading")){return}else{obj.data("jstree-is-loading",true)}}switch(!0){case (!s.data&&!s.ajax):if(!obj||obj==-1){this.get_container().children("ul").empty().append(this.data.html_data.original_container_html).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'>&#160;</ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon");this.clean_node()}if(s_call){s_call.call(this)}break;case (!!s.data&&!s.ajax)||(!!s.data&&!!s.ajax&&(!obj||obj===-1)):if(!obj||obj==-1){d=$(s.data);if(!d.is("ul")){d=$("<ul>").append(d)}this.get_container().children("ul").empty().append(d.children()).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'>&#160;</ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon");this.clean_node()}if(s_call){s_call.call(this)}break;case (!s.data&&!!s.ajax)||(!!s.data&&!!s.ajax&&obj&&obj!==-1):obj=this._get_node(obj);error_func=function(x,t,e){var ef=this.get_settings().html_data.ajax.error;if(ef){ef.call(this,x,t,e)}if(obj!=-1&&obj.length){obj.children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false);if(t==="success"&&s.correct_state){obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}}else{if(t==="success"&&s.correct_state){this.get_container().children("ul").empty()}}if(e_call){e_call.call(this)}};success_func=function(d,t,x){var sf=this.get_settings().html_data.ajax.success;if(sf){d=sf.call(this,d,t,x)||d}if(d==""){return error_func.call(this,x,t,"")}if(d){d=$(d);if(!d.is("ul")){d=$("<ul>").append(d)}if(obj==-1||!obj){this.get_container().children("ul").empty().append(d.children()).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'>&#160;</ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon")}else{obj.children(".jstree-loading").removeClass("jstree-loading");obj.append(d).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'>&#160;</ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon");obj.data("jstree-is-loading",false)}this.clean_node(obj);if(s_call){s_call.call(this)}}else{if(obj&&obj!==-1){obj.children(".jstree-loading").removeClass("jstree-loading");obj.data("jstree-is-loading",false);if(s.correct_state){obj.removeClass("jstree-open jstree-closed").addClass("jstree-leaf");if(s_call){s_call.call(this)}}}else{if(s.correct_state){this.get_container().children("ul").empty();if(s_call){s_call.call(this)}}}}};s.ajax.context=this;s.ajax.error=error_func;s.ajax.success=success_func;if(!s.ajax.dataType){s.ajax.dataType="html"}if($.isFunction(s.ajax.url)){s.ajax.url=s.ajax.url.call(this,obj)}if($.isFunction(s.ajax.data)){s.ajax.data=s.ajax.data.call(this,obj)}$.ajax(s.ajax);break}}}});$.jstree.defaults.plugins.push("html_data")})(jQuery);(function($){$.jstree.plugin("themeroller",{__init:function(){var s=this._get_settings().themeroller;this.get_container().addClass("ui-widget-content").delegate("a","mouseenter.jstree",function(){$(this).addClass(s.item_h)}).delegate("a","mouseleave.jstree",function(){$(this).removeClass(s.item_h)}).bind("open_node.jstree create_node.jstree",$.proxy(function(e,data){this._themeroller(data.rslt.obj)},this)).bind("loaded.jstree refresh.jstree",$.proxy(function(e){this._themeroller()},this)).bind("close_node.jstree",$.proxy(function(e,data){data.rslt.obj.children("ins").removeClass(s.opened).addClass(s.closed)},this)).bind("select_node.jstree",$.proxy(function(e,data){data.rslt.obj.children("a").addClass(s.item_a)},this)).bind("deselect_node.jstree deselect_all.jstree",$.proxy(function(e,data){this.get_container().find("."+s.item_a).removeClass(s.item_a).end().find(".jstree-clicked").addClass(s.item_a)},this)).bind("move_node.jstree",$.proxy(function(e,data){this._themeroller(data.rslt.o)},this))},__destroy:function(){var s=this._get_settings().themeroller,c=["ui-icon"];$.each(s,function(i,v){v=v.split(" ");if(v.length){c=c.concat(v)}});this.get_container().removeClass("ui-widget-content").find("."+c.join(", .")).removeClass(c.join(" "))},_fn:{_themeroller:function(obj){var s=this._get_settings().themeroller;obj=!obj||obj==-1?this.get_container():this._get_node(obj).parent();obj.find("li.jstree-closed > ins.jstree-icon").removeClass(s.opened).addClass("ui-icon "+s.closed).end().find("li.jstree-open > ins.jstree-icon").removeClass(s.closed).addClass("ui-icon "+s.opened).end().find("a").addClass(s.item).children("ins.jstree-icon").addClass("ui-icon "+s.item_icon)}},defaults:{opened:"ui-icon-triangle-1-se",closed:"ui-icon-triangle-1-e",item:"ui-state-default",item_h:"ui-state-hover",item_a:"ui-state-active",item_icon:"ui-icon-folder-collapsed"}});$(function(){var css_string=".jstree .ui-icon { overflow:visible; } .jstree a { padding:0 2px; }";$.vakata.css.add_sheet({str:css_string})})})(jQuery);(function($){$.jstree.plugin("unique",{__init:function(){this.get_container().bind("before.jstree",$.proxy(function(e,data){var nms=[],res=true,p,t;if(data.func=="move_node"){if(data.args[4]===true){if(data.args[0].o&&data.args[0].o.length){data.args[0].o.children("a").each(function(){nms.push($(this).text().replace(/^\s+/g,""))});res=this._check_unique(nms,data.args[0].np.find("> ul > li").not(data.args[0].o))}}}if(data.func=="create_node"){if(data.args[4]||this._is_loaded(data.args[0])){p=this._get_node(data.args[0]);if(data.args[1]&&(data.args[1]==="before"||data.args[1]==="after")){p=this._get_parent(data.args[0]);if(!p||p===-1){p=this.get_container()}}if(typeof data.args[2]==="string"){nms.push(data.args[2])}else{if(!data.args[2]||!data.args[2].data){nms.push(this._get_settings().core.strings.new_node)}else{nms.push(data.args[2].data)}}res=this._check_unique(nms,p.find("> ul > li"))}}if(data.func=="rename_node"){nms.push(data.args[1]);t=this._get_node(data.args[0]);p=this._get_parent(t);if(!p||p===-1){p=this.get_container()}res=this._check_unique(nms,p.find("> ul > li").not(t))}if(!res){e.stopPropagation();return false}},this))},_fn:{_check_unique:function(nms,p){var cnms=[];p.children("a").each(function(){cnms.push($(this).text().replace(/^\s+/g,""))});if(!cnms.length||!nms.length){return true}cnms=cnms.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",");if((cnms.length+nms.length)!=cnms.concat(nms).sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",").length){return false}return true},check_move:function(){if(!this.__call_old()){return false}var p=this._get_move(),nms=[];if(p.o&&p.o.length){p.o.children("a").each(function(){nms.push($(this).text().replace(/^\s+/g,""))});return this._check_unique(nms,p.np.find("> ul > li").not(p.o))}return true}}})})(jQuery); \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/themes/apple/bg.jpg b/www/js/jstree-1.0-rc2/themes/apple/bg.jpg
new file mode 100644
index 0000000..3aad05d
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/apple/bg.jpg
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/apple/d.png b/www/js/jstree-1.0-rc2/themes/apple/d.png
new file mode 100644
index 0000000..2463ba6
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/apple/d.png
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/apple/dot_for_ie.gif b/www/js/jstree-1.0-rc2/themes/apple/dot_for_ie.gif
new file mode 100644
index 0000000..c0cc5fd
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/apple/dot_for_ie.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/apple/style.css b/www/js/jstree-1.0-rc2/themes/apple/style.css
new file mode 100644
index 0000000..8f1b3de
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/apple/style.css
@@ -0,0 +1,60 @@
+/*
+ * jsTree apple theme 1.0
+ * Supported features: dots/no-dots, icons/no-icons, focused, loading
+ * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
+ */
+
+.jstree-apple > ul { background:url("bg.jpg") left top repeat; }
+.jstree-apple li,
+.jstree-apple ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-apple li { background-position:-90px 0; background-repeat:repeat-y; }
+.jstree-apple li.jstree-last { background:transparent; }
+.jstree-apple .jstree-open > ins { background-position:-72px 0; }
+.jstree-apple .jstree-closed > ins { background-position:-54px 0; }
+.jstree-apple .jstree-leaf > ins { background-position:-36px 0; }
+
+.jstree-apple a { border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px; text-shadow:1px 1px 1px white; }
+.jstree-apple .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 3px 0 1px; text-shadow:1px 1px 1px silver; }
+.jstree-apple .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 3px 0 1px; }
+.jstree-apple a .jstree-icon { background-position:-56px -20px; }
+.jstree-apple a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
+
+.jstree-apple.jstree-focused { background:white; }
+
+.jstree-apple .jstree-no-dots li,
+.jstree-apple .jstree-no-dots .jstree-leaf > ins { background:transparent; }
+.jstree-apple .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
+.jstree-apple .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
+
+.jstree-apple .jstree-no-icons a .jstree-icon { display:none; }
+
+.jstree-apple .jstree-search { font-style:italic; }
+
+.jstree-apple .jstree-no-icons .jstree-checkbox { display:inline-block; }
+.jstree-apple .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
+.jstree-apple .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
+.jstree-apple .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
+.jstree-apple .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
+.jstree-apple .jstree-checked > a > .checkbox:hover { background-position:-38px -37px; }
+.jstree-apple .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
+.jstree-apple .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
+
+#vakata-dragged.jstree-apple ins { background:transparent !important; }
+#vakata-dragged.jstree-apple .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-apple .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-apple { background:url("d.png") -41px -57px no-repeat !important; }
+
+.jstree-apple a.jstree-search { color:aqua; }
+
+#vakata-contextmenu.jstree-apple-context,
+#vakata-contextmenu.jstree-apple-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
+#vakata-contextmenu.jstree-apple-context li { }
+#vakata-contextmenu.jstree-apple-context a { color:black; }
+#vakata-contextmenu.jstree-apple-context a:hover,
+#vakata-contextmenu.jstree-apple-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
+#vakata-contextmenu.jstree-apple-context li.jstree-contextmenu-disabled a,
+#vakata-contextmenu.jstree-apple-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
+#vakata-contextmenu.jstree-apple-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
+#vakata-contextmenu.jstree-apple-context li ul { margin-left:-4px; }
+
+/* TODO: IE6 support - the `>` selectors */ \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/themes/apple/throbber.gif b/www/js/jstree-1.0-rc2/themes/apple/throbber.gif
new file mode 100644
index 0000000..5b33f7e
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/apple/throbber.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/classic/d.png b/www/js/jstree-1.0-rc2/themes/classic/d.png
new file mode 100644
index 0000000..275daec
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/classic/d.png
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/classic/dot_for_ie.gif b/www/js/jstree-1.0-rc2/themes/classic/dot_for_ie.gif
new file mode 100644
index 0000000..c0cc5fd
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/classic/dot_for_ie.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/classic/style.css b/www/js/jstree-1.0-rc2/themes/classic/style.css
new file mode 100644
index 0000000..bb15730
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/classic/style.css
@@ -0,0 +1,59 @@
+/*
+ * jsTree classic theme 1.0
+ * Supported features: dots/no-dots, icons/no-icons, focused, loading
+ * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
+ */
+
+.jstree-classic li,
+.jstree-classic ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-classic li { background-position:-90px 0; background-repeat:repeat-y; }
+.jstree-classic li.jstree-last { background:transparent; }
+.jstree-classic .jstree-open > ins { background-position:-72px 0; }
+.jstree-classic .jstree-closed > ins { background-position:-54px 0; }
+.jstree-classic .jstree-leaf > ins { background-position:-36px 0; }
+
+.jstree-classic .jstree-hovered { background:#e7f4f9; border:1px solid #e7f4f9; padding:0 2px 0 1px; }
+.jstree-classic .jstree-clicked { background:navy; border:1px solid navy; padding:0 2px 0 1px; color:white; }
+.jstree-classic a .jstree-icon { background-position:-56px -19px; }
+.jstree-classic .jstree-open > a .jstree-icon { background-position:-56px -36px; }
+.jstree-classic a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
+
+.jstree-classic.jstree-focused { background:white; }
+
+.jstree-classic .jstree-no-dots li,
+.jstree-classic .jstree-no-dots .jstree-leaf > ins { background:transparent; }
+.jstree-classic .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
+.jstree-classic .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
+
+.jstree-classic .jstree-no-icons a .jstree-icon { display:none; }
+
+.jstree-classic .jstree-search { font-style:italic; }
+
+.jstree-classic .jstree-no-icons .jstree-checkbox { display:inline-block; }
+.jstree-classic .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
+.jstree-classic .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
+.jstree-classic .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
+.jstree-classic .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
+.jstree-classic .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
+.jstree-classic .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
+.jstree-classic .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
+
+#vakata-dragged.jstree-classic ins { background:transparent !important; }
+#vakata-dragged.jstree-classic .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-classic .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-classic { background:url("d.png") -41px -57px no-repeat !important; }
+
+.jstree-classic a.jstree-search { color:aqua; }
+
+#vakata-contextmenu.jstree-classic-context,
+#vakata-contextmenu.jstree-classic-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
+#vakata-contextmenu.jstree-classic-context li { }
+#vakata-contextmenu.jstree-classic-context a { color:black; }
+#vakata-contextmenu.jstree-classic-context a:hover,
+#vakata-contextmenu.jstree-classic-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
+#vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a,
+#vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
+#vakata-contextmenu.jstree-classic-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
+#vakata-contextmenu.jstree-classic-context li ul { margin-left:-4px; }
+
+/* TODO: IE6 support - the `>` selectors */ \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/themes/classic/throbber.gif b/www/js/jstree-1.0-rc2/themes/classic/throbber.gif
new file mode 100644
index 0000000..5b33f7e
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/classic/throbber.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default-rtl/d.gif b/www/js/jstree-1.0-rc2/themes/default-rtl/d.gif
new file mode 100644
index 0000000..d85aba0
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default-rtl/d.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default-rtl/d.png b/www/js/jstree-1.0-rc2/themes/default-rtl/d.png
new file mode 100644
index 0000000..5179cf6
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default-rtl/d.png
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default-rtl/dots.gif b/www/js/jstree-1.0-rc2/themes/default-rtl/dots.gif
new file mode 100644
index 0000000..0043364
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default-rtl/dots.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default-rtl/style.css b/www/js/jstree-1.0-rc2/themes/default-rtl/style.css
new file mode 100644
index 0000000..3ad0727
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default-rtl/style.css
@@ -0,0 +1,83 @@
+/*
+ * jsTree default-rtl theme 1.0
+ * Supported features: dots/no-dots, icons/no-icons, focused, loading
+ * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
+ */
+
+.jstree-default-rtl li,
+.jstree-default-rtl ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-default-rtl li { background-position:-90px 0; background-repeat:repeat-y; }
+.jstree-default-rtl li.jstree-last { background:transparent; }
+.jstree-default-rtl .jstree-open > ins { background-position:-72px 0; }
+.jstree-default-rtl .jstree-closed > ins { background-position:-54px 0; }
+.jstree-default-rtl .jstree-leaf > ins { background-position:-36px 0; }
+
+.jstree-default-rtl .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
+.jstree-default-rtl .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
+.jstree-default-rtl a .jstree-icon { background-position:-56px -19px; }
+.jstree-default-rtl a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
+
+.jstree-default-rtl.jstree-focused { background:#ffffee; }
+
+.jstree-default-rtl .jstree-no-dots li,
+.jstree-default-rtl .jstree-no-dots .jstree-leaf > ins { background:transparent; }
+.jstree-default-rtl .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
+.jstree-default-rtl .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
+
+.jstree-default-rtl .jstree-no-icons a .jstree-icon { display:none; }
+
+.jstree-default-rtl .jstree-search { font-style:italic; }
+
+.jstree-default-rtl .jstree-no-icons .jstree-checkbox { display:inline-block; }
+.jstree-default-rtl .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
+.jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
+.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
+.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
+.jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
+.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
+.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
+
+#vakata-dragged.jstree-default-rtl ins { background:transparent !important; }
+#vakata-dragged.jstree-default-rtl .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-default-rtl .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-default-rtl { background:url("d.png") -41px -57px no-repeat !important; }
+
+.jstree-default-rtl a.jstree-search { color:aqua; }
+
+#vakata-contextmenu.jstree-default-rtl-context,
+#vakata-contextmenu.jstree-default-rtl-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
+#vakata-contextmenu.jstree-default-rtl-context li { }
+#vakata-contextmenu.jstree-default-rtl-context a { color:black; }
+#vakata-contextmenu.jstree-default-rtl-context a:hover,
+#vakata-contextmenu.jstree-default-rtl-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
+#vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a,
+#vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
+#vakata-contextmenu.jstree-default-rtl-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
+#vakata-contextmenu.jstree-default-rtl-context li ul { margin-left:-4px; }
+
+/* IE6 BEGIN */
+.jstree-default-rtl li,
+.jstree-default-rtl ins,
+#vakata-dragged.jstree-default-rtl .jstree-invalid,
+#vakata-dragged.jstree-default-rtl .jstree-ok,
+#jstree-marker.jstree-default-rtl { _background-image:url("d.gif"); }
+.jstree-default-rtl .jstree-open ins { _background-position:-72px 0; }
+.jstree-default-rtl .jstree-closed ins { _background-position:-54px 0; }
+.jstree-default-rtl .jstree-leaf ins { _background-position:-36px 0; }
+.jstree-default-rtl a ins.jstree-icon { _background-position:-56px -19px; }
+#vakata-contextmenu.jstree-default-rtl-context ins { _display:none; }
+#vakata-contextmenu.jstree-default-rtl-context li { _zoom:1; }
+.jstree-default-rtl .jstree-undetermined a .jstree-checkbox { _background-position:-18px -19px; }
+.jstree-default-rtl .jstree-checked a .jstree-checkbox { _background-position:-36px -19px; }
+.jstree-default-rtl .jstree-unchecked a .jstree-checkbox { _background-position:0px -19px; }
+/* IE6 END */
+
+/* RTL part */
+.jstree-default-rtl .jstree-hovered, .jstree-default-rtl .jstree-clicked { padding:0 1px 0 2px; }
+.jstree-default-rtl li { background-image:url("dots.gif"); background-position: 100% 0px; }
+.jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-36px -19px; margin-left:2px; }
+.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:0px -19px; margin-left:2px; }
+.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-18px -19px; margin-left:2px; }
+.jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-36px -37px; }
+.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:0px -37px; }
+.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-18px -37px; } \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/themes/default-rtl/throbber.gif b/www/js/jstree-1.0-rc2/themes/default-rtl/throbber.gif
new file mode 100644
index 0000000..5b33f7e
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default-rtl/throbber.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default/d.gif b/www/js/jstree-1.0-rc2/themes/default/d.gif
new file mode 100644
index 0000000..0e958d3
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default/d.gif
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default/d.png b/www/js/jstree-1.0-rc2/themes/default/d.png
new file mode 100644
index 0000000..8540175
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default/d.png
Binary files differ
diff --git a/www/js/jstree-1.0-rc2/themes/default/style.css b/www/js/jstree-1.0-rc2/themes/default/style.css
new file mode 100644
index 0000000..01a0889
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default/style.css
@@ -0,0 +1,73 @@
+/*
+ * jsTree default theme 1.0
+ * Supported features: dots/no-dots, icons/no-icons, focused, loading
+ * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
+ */
+
+.jstree-default li,
+.jstree-default ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-default li { background-position:-90px 0; background-repeat:repeat-y; }
+.jstree-default li.jstree-last { background:transparent; }
+.jstree-default .jstree-open > ins { background-position:-72px 0; }
+.jstree-default .jstree-closed > ins { background-position:-54px 0; }
+.jstree-default .jstree-leaf > ins { background-position:-36px 0; }
+
+.jstree-default .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
+.jstree-default .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
+.jstree-default a .jstree-icon { background-position:-56px -19px; }
+.jstree-default a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
+
+.jstree-default.jstree-focused { background:#ffffee; }
+
+.jstree-default .jstree-no-dots li,
+.jstree-default .jstree-no-dots .jstree-leaf > ins { background:transparent; }
+.jstree-default .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
+.jstree-default .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
+
+.jstree-default .jstree-no-icons a .jstree-icon { display:none; }
+
+.jstree-default .jstree-search { font-style:italic; }
+
+.jstree-default .jstree-no-icons .jstree-checkbox { display:inline-block; }
+.jstree-default .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
+.jstree-default .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
+.jstree-default .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
+.jstree-default .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
+.jstree-default .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
+.jstree-default .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
+.jstree-default .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
+
+#vakata-dragged.jstree-default ins { background:transparent !important; }
+#vakata-dragged.jstree-default .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-default .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-default { background:url("d.png") -41px -57px no-repeat !important; }
+
+.jstree-default a.jstree-search { color:aqua; }
+
+#vakata-contextmenu.jstree-default-context,
+#vakata-contextmenu.jstree-default-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
+#vakata-contextmenu.jstree-default-context li { }
+#vakata-contextmenu.jstree-default-context a { color:black; }
+#vakata-contextmenu.jstree-default-context a:hover,
+#vakata-contextmenu.jstree-default-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
+#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a,
+#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
+#vakata-contextmenu.jstree-default-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
+#vakata-contextmenu.jstree-default-context li ul { margin-left:-4px; }
+
+/* IE6 BEGIN */
+.jstree-default li,
+.jstree-default ins,
+#vakata-dragged.jstree-default .jstree-invalid,
+#vakata-dragged.jstree-default .jstree-ok,
+#jstree-marker.jstree-default { _background-image:url("d.gif"); }
+.jstree-default .jstree-open ins { _background-position:-72px 0; }
+.jstree-default .jstree-closed ins { _background-position:-54px 0; }
+.jstree-default .jstree-leaf ins { _background-position:-36px 0; }
+.jstree-default a ins.jstree-icon { _background-position:-56px -19px; }
+#vakata-contextmenu.jstree-default-context ins { _display:none; }
+#vakata-contextmenu.jstree-default-context li { _zoom:1; }
+.jstree-default .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; }
+.jstree-default .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; }
+.jstree-default .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; }
+/* IE6 END */ \ No newline at end of file
diff --git a/www/js/jstree-1.0-rc2/themes/default/throbber.gif b/www/js/jstree-1.0-rc2/themes/default/throbber.gif
new file mode 100644
index 0000000..5b33f7e
--- /dev/null
+++ b/www/js/jstree-1.0-rc2/themes/default/throbber.gif
Binary files differ
diff --git a/www/jsScuttle.php b/www/jsScuttle.php
new file mode 100644
index 0000000..de97fd2
--- /dev/null
+++ b/www/jsScuttle.php
@@ -0,0 +1,199 @@
+<?php
+$GLOBALS['saveInLastUrl'] = false;
+$httpContentType = 'text/javascript';
+require_once 'www-header.php';
+require_once 'SemanticScuttle/functions.php';
+$theme = new SemanticScuttle_Model_Theme($GLOBALS['theme']);
+$player_root = ROOT .'includes/player/';
+?>
+
+function _playerAdd(anchor) {
+ var url = anchor.href;
+ var code = '<object type="application/x-shockwave-flash" data="<?php echo $player_root ?>musicplayer_f6.swf?song_url=' + url +'&amp;b_bgcolor=ffffff&amp;b_fgcolor=000000&amp;b_colors=0000ff,0000ff,ff0000,ff0000&buttons=<?php echo $player_root ?>load.swf,<?php echo $player_root ?>play.swf,<?php echo $player_root ?>stop.swf,<?php echo $player_root ?>error.swf" width="14" height="14">';
+ var code = code + '<param name="movie" value="<?php echo $player_root ?>musicplayer.swf?song_url=' + url +'&amp;b_bgcolor=ffffff&amp;b_fgcolor=000000&amp;b_colors=0000ff,0000ff,ff0000,ff0000&amp;buttons=<?php echo $player_root ?>load.swf,<?php echo $player_root ?>play.swf,<?php echo $player_root ?>stop.swf,<?php echo $player_root ?>error.swf" />';
+ var code = code + '</object>';
+ anchor.parentNode.innerHTML = code +' '+ anchor.parentNode.innerHTML;
+}
+
+String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g, '');
+};
+
+var deleted = false;
+function deleteBookmark(ele, input){
+ var confirmDelete = "<span><?php echo T_('Are you sure?') ?> <a href=\"#\" onclick=\"deleteConfirmed(this, " + input + ", \'\'); return false;\"><?php echo T_('Yes'); ?></a> - <a href=\"#\" onclick=\"deleteCancelled(this); return false;\"><?php echo T_('No'); ?></a></span>";
+ ele.style.display = 'none';
+ ele.parentNode.innerHTML = ele.parentNode.innerHTML + confirmDelete;
+}
+
+function deleteCancelled(ele) {
+ var del = previousElement(ele.parentNode);
+ del.style.display = 'inline';
+ ele.parentNode.parentNode.removeChild(ele.parentNode);
+ return false;
+}
+
+function deleteConfirmed(ele, input, response) {
+ //if (deleted == false) {
+ deleted = ele.parentNode.parentNode.parentNode.parentNode;
+ //}
+ var post = deleted;
+ post.className = 'xfolkentry deleted';
+ if (response != '') {
+ post.style.display = 'none';
+ deleted = false;
+ } else {
+ loadXMLDocProc('<?php echo ROOT; ?>ajaxDelete.php?id=' + input);
+ post.style.display = 'none';
+ }
+}
+
+function previousElement(ele) {
+ ele = ele.previousSibling;
+ while (ele.nodeType != 1) {
+ ele = ele.previousSibling;
+ }
+ return ele;
+}
+
+function isAvailable(input, response){
+ var usernameField = document.getElementById("username");
+ var username = usernameField.value;
+ username = username.toLowerCase();
+ username = username.trim();
+ var availability = document.getElementById("availability");
+ if (username != '') {
+ usernameField.style.backgroundImage = 'url(<?php echo $theme->resource('images/loading.gif'); ?>)';
+ if (response != '') {
+ usernameField.style.backgroundImage = 'none';
+ if (response == 'true') {
+ availability.className = 'available';
+ availability.innerHTML = '<?php echo T_('Available'); ?>';
+ } else {
+ availability.className = 'not-available';
+ availability.innerHTML = '<?php echo T_('Not Available'); ?>';
+ }
+ } else {
+ loadXMLDoc('<?php echo ROOT; ?>ajaxIsAvailable.php?username=' + username);
+ }
+ }
+}
+
+function useAddress(ele) {
+ var address = ele.value;
+ if (address != '') {
+ if (address.indexOf(':') < 0) {
+ address = 'http:\/\/' + address;
+ }
+ getTitle(address, null);
+ ele.value = address;
+ }
+}
+
+/**
+ * Makes an ajax call to PHP script to generate an new Private Key
+ *
+ * @param input Calling object
+ * @param response Response object that returned value is placed
+ *
+ * @return boolean Returns false to halt execution after call
+ */
+function getNewPrivateKey(input, response){
+ var pk = document.getElementById('pPrivateKey');
+ if (response != null) {
+ pk.value = response.trim();
+ } else {
+ loadXMLDocProc('<?php echo ROOT; ?>ajaxGetNewPrivateKey.php');
+ }
+ return false;
+}
+
+function getTitle(input, response){
+ var title = document.getElementById('titleField');
+ if (title.value == '') {
+ title.style.backgroundImage = 'url(<?php echo $theme->resource('images/loading.gif');?>)';
+ if (response != null) {
+ title.style.backgroundImage = 'none';
+ title.value = response;
+ } else if (input.indexOf('http') > -1) {
+ loadXMLDocProc('<?php echo ROOT; ?>ajaxGetTitle.php?url=' + input);
+ } else {
+ return false;
+ }
+ }
+}
+
+var xmlhttp;
+function loadXMLDocProc(url) {
+ loadXMLDoc(url, processStateChange);
+}
+function vote(bookmark, vote) {
+ if (vote == 1) {
+ vote = 'for';
+ } else {
+ vote = 'against';
+ }
+ loadXMLDoc(
+ '<?php echo ROOT; ?>ajaxVote.php/' + vote + '/' + bookmark,
+ processVotingResult
+ );
+}
+function loadXMLDoc(url, callback) {
+ // Native
+ if (window.XMLHttpRequest) {
+ xmlhttp = new XMLHttpRequest();
+ xmlhttp.onreadystatechange = callback;
+ xmlhttp.open("GET", url, true);
+ xmlhttp.send(null);
+ // ActiveX
+ } else if (window.ActiveXObject) {
+ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+ if (xmlhttp) {
+ xmlhttp.onreadystatechange = processStateChange;
+ xmlhttp.open("GET", url, true);
+ xmlhttp.send();
+ }
+ }
+}
+
+function processStateChange() {
+ if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
+ response = xmlhttp.responseXML.documentElement;
+ method = response.getElementsByTagName('method')[0].firstChild.data;
+ result = response.getElementsByTagName('result')[0].firstChild.data;
+ eval(method + '(\'\', result)');
+ }
+}
+function processVotingResult() {
+ if (xmlhttp.readyState != 4 || xmlhttp.status != 200) {
+ return;
+ }
+ var response = xmlhttp.responseXML.documentElement;
+ var bookmark = response.getElementsByTagName('bookmark')[0]
+ .firstChild.nodeValue;
+ var bmnode = document.getElementById('bmv-'+bookmark);
+
+ bmnode.parentNode.replaceChild(
+ xmlhttp.responseXML.importNode(
+ response.getElementsByTagName('html')[0].firstChild,
+ true
+ ),
+ bmnode
+ );
+}
+
+function playerLoad() {
+ var anchors = document.getElementsByTagName('a');
+ var anchors_length = anchors.length;
+ for (var i = 0; i < anchors_length; i++) {
+ if (anchors[i].className == 'taggedlink' && anchors[i].href.match(/\.mp3$/i)) {
+ _playerAdd(anchors[i]);
+ }
+ }
+}
+
+function addAnchor(anchorForm, where) {
+ var whereZone = document.getElementById(where);
+ whereZone.value = whereZone.value + anchorForm;
+ document.getElementById(where).focus();
+}
diff --git a/www/login.php b/www/login.php
new file mode 100644
index 0000000..2a61224
--- /dev/null
+++ b/www/login.php
@@ -0,0 +1,62 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+
+/* Service creation: only useful services are created */
+// No specific services
+
+
+/* Managing all possible inputs */
+isset($_POST['keeppass']) ? define('POST_KEEPPASS', $_POST['keeppass']): define('POST_KEEPPASS', '');
+isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', '');
+isset($_POST['username']) ? define('POST_USERNAME', $_POST['username']): define('POST_USERNAME', '');
+isset($_POST['password']) ? define('POST_PASSWORD', $_POST['password']): define('POST_PASSWORD', '');
+isset($_POST['query']) ? define('POST_QUERY', $_POST['query']): define('POST_QUERY', '');
+
+$keeppass = (POST_KEEPPASS=='yes')?true:false;
+
+$login = false;
+if (POST_SUBMITTED!='' && POST_USERNAME!='' && POST_PASSWORD!='') {
+ $posteduser = trim(utf8_strtolower(POST_USERNAME));
+ $login = $userservice->login($posteduser, POST_PASSWORD, $keeppass);
+ if ($login) {
+ if (POST_QUERY)
+ header('Location: '. createURL('bookmarks', $posteduser .'?'. POST_QUERY));
+ else
+ header('Location: '. createURL('bookmarks', $posteduser));
+ } else {
+ $tplVars['error'] = T_('The details you have entered are incorrect. Please try again.');
+ }
+}
+if (!$login) {
+ if ($userservice->isLoggedOn()) {
+ $cUser = $userservice->getCurrentObjectUser();
+ header('Location: '. createURL('bookmarks', strtolower($cUser->getUsername())));
+ }
+
+ $tplVars['subtitle'] = T_('Log In');
+ $tplVars['formaction'] = createURL('login');
+ $tplVars['querystring'] = filter($_SERVER['QUERY_STRING']);
+ $templateservice->loadTemplate('login.tpl', $tplVars);
+}
+?>
diff --git a/www/password.php b/www/password.php
new file mode 100644
index 0000000..77a58ce
--- /dev/null
+++ b/www/password.php
@@ -0,0 +1,78 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2005 Scuttle project
+ https://sourceforge.net/projects/scuttle/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+// No specific services
+
+/* Managing all possible inputs */
+isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', '');
+isset($_POST['username']) ? define('POST_USERNAME', $_POST['username']): define('POST_USERNAME', '');
+isset($_POST['email']) ? define('POST_EMAIL', $_POST['email']): define('POST_EMAIL', '');
+
+// IF SUBMITTED
+if (POST_SUBMITTED != '') {
+
+ // NO USERNAME
+ if (!POST_USERNAME) {
+ $tplVars['error'] = T_('You must enter your username.');
+
+ // NO E-MAIL
+ } elseif (!POST_EMAIL) {
+ $tplVars['error'] = T_('You must enter your <abbr title="electronic mail">e-mail</abbr> address.');
+
+ // USERNAME AND E-MAIL
+ } else {
+
+ // NO MATCH
+ $userinfo = $userservice->getObjectUserByUsername(POST_USERNAME);
+ if ($userinfo == NULL) {
+ $tplVars['error'] = T_('No matches found for that username.');
+
+ } elseif (POST_EMAIL != $userinfo->getEmail()) {
+ $tplVars['error'] = T_('No matches found for that combination of username and <abbr title="electronic mail">e-mail</abbr> address.');
+
+ // MATCH
+ } else {
+
+ // GENERATE AND STORE PASSWORD
+ $password = $userservice->generatePassword($userinfo->getId());
+ if (!($password = $userservice->generatePassword($userinfo->getId()))) {
+ $tplVars['error'] = T_('There was an error while generating your new password. Please try again.');
+
+ } else {
+ // SEND E-MAIL
+ $message = T_('Your new password is:') ."\n". $password ."\n\n". T_('To keep your bookmarks secure, you should change this password in your profile the next time you log in.');
+ $message = wordwrap($message, 70);
+ $headers = 'From: '. $adminemail;
+ $mail = mail(POST_EMAIL, sprintf(T_('%s Account Information'), $sitename), $message);
+
+ $tplVars['msg'] = sprintf(T_('New password generated and sent to %s'), POST_EMAIL);
+ }
+ }
+ }
+}
+
+$templatename = 'password.tpl';
+$tplVars['subtitle'] = T_('Forgotten Password');
+$tplVars['formaction'] = createURL('password');
+$templateservice->loadTemplate($templatename, $tplVars);
+?>
diff --git a/www/permalink.php b/www/permalink.php
new file mode 100644
index 0000000..d389294
--- /dev/null
+++ b/www/permalink.php
@@ -0,0 +1,122 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing all possible inputs */
+isset($_GET['action']) ? define('GET_ACTION', $_GET['action']): define('GET_ACTION', '');
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+
+// Logout action
+if (GET_ACTION == "logout") {
+ $userservice->logout();
+ $tplVars['currentUser'] = null;
+ $tplvars['msg'] = T_('You have now logged out');
+}
+
+
+// Header variables
+$tplVars['loadjs'] = true;
+$tplVars['rsschannels'] = array(
+ array(
+ sprintf(T_('%s: Recent bookmarks'), $sitename),
+ createURL('rss') . '?sort=' . getSortOrder()
+ )
+);
+
+if ($userservice->isLoggedOn()) {
+ if ($userservice->isPrivateKeyValid($currentUser->getPrivateKey())) {
+ $currentUsername = $currentUser->getUsername();
+ array_push(
+ $tplVars['rsschannels'],
+ array(
+ sprintf(
+ T_('%s: Recent bookmarks (+private %s)'),
+ $sitename, $currentUsername
+ ),
+ createURL('rss')
+ . '?sort=' . getSortOrder()
+ . '&privateKey=' . $currentUser->getPrivateKey()
+ )
+ );
+ }
+}
+
+if ($usecache) {
+ // Generate hash for caching on
+ $hashtext = $_SERVER['REQUEST_URI'];
+ if ($userservice->isLoggedOn()) {
+ $hashtext .= $userservice->getCurrentUserID();
+ }
+ $hash = md5($hashtext);
+
+ // Cache for 15 minutes
+ $cacheservice->Start($hash, 900);
+}
+
+// Pagination
+$perpage = getPerPageCount($currentUser);
+if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+} else {
+ $page = 0;
+ $start = 0;
+}
+
+$tplVars['page'] = $page;
+$tplVars['start'] = $start;
+$tplVars['popCount'] = 30;
+$tplVars['sidebar_blocks'] = $GLOBALS["index_sidebar_blocks"];
+$tplVars['range'] = 'all';
+$tplVars['pagetitle'] = T_('Store, share and tag your favourite links');
+$tplVars['subtitle'] = T_('All Bookmarks');
+$tplVars['bookmarkCount'] = $start + 1;
+
+//$bookmarks = $bookmarkservice->getBookmarks($start, $perpage, NULL, NULL, NULL, getSortOrder(), NULL, 0, NULL);
+list ($url, $bid) = explode('/', $_SERVER['PATH_INFO']);
+$bookmark = $bookmarkservice->getBookmark($bid, TRUE);
+$bookmarks = array('bookmarks' => array($bookmark));
+$tplVars['pagetitle'] = $bookmark['bTitle'];
+
+$tplVars['total'] = $bookmarks['total'];
+$tplVars['bookmarks'] = $bookmarks['bookmarks'];
+$tplVars['cat_url'] = createURL('bookmarks', '%1$s/%2$s');
+//$tplVars['cat_url'] = createURL('tags', '%2$s');
+$tplVars['nav_url'] = createURL('index', '%3$s');
+$tplVars['summarizeLinkedTags'] = true;
+$tplVars['pageName'] = PAGE_BOOKMARKS;
+$tplVars['user'] = '';
+$tplVars['currenttag'] = '';
+
+$templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/player/error.swf b/www/player/error.swf
new file mode 100644
index 0000000..fc440f3
--- /dev/null
+++ b/www/player/error.swf
Binary files differ
diff --git a/www/player/license.txt b/www/player/license.txt
new file mode 100644
index 0000000..2111a50
--- /dev/null
+++ b/www/player/license.txt
@@ -0,0 +1,10 @@
+Copyright (c) 2005, Fabricio Zuardi
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/www/player/load.swf b/www/player/load.swf
new file mode 100644
index 0000000..6da4ce6
--- /dev/null
+++ b/www/player/load.swf
Binary files differ
diff --git a/www/player/musicplayer_f6.swf b/www/player/musicplayer_f6.swf
new file mode 100644
index 0000000..2eb5d57
--- /dev/null
+++ b/www/player/musicplayer_f6.swf
Binary files differ
diff --git a/www/player/play.swf b/www/player/play.swf
new file mode 100644
index 0000000..95415a6
--- /dev/null
+++ b/www/player/play.swf
Binary files differ
diff --git a/www/player/stop.swf b/www/player/stop.swf
new file mode 100644
index 0000000..e66556d
--- /dev/null
+++ b/www/player/stop.swf
Binary files differ
diff --git a/www/populartags.php b/www/populartags.php
new file mode 100644
index 0000000..20b1b53
--- /dev/null
+++ b/www/populartags.php
@@ -0,0 +1,84 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$b2tservice =SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+@list($url, $user) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+if ($usecache) {
+ // Generate hash for caching on
+ $hashtext = $_SERVER['REQUEST_URI'];
+ if ($userservice->isLoggedOn()) {
+ $hashtext .= $currentUser->getId();
+ if ($currentUser->getUsername() == $user) {
+ $hashtext .= $user;
+ }
+ }
+ $hash = md5($hashtext);
+
+ // Cache for an hour
+ $cacheservice->Start($hash, 3600);
+}
+
+// Header variables
+$pagetitle = T_('Popular Tags');
+
+if (isset($user) && $user != '') {
+
+ $userid = $userservice->getIdFromUser($user);
+ if($userid == NULL) {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ //throw a 404 error
+ exit();
+ }
+
+ $pagetitle .= ': '. ucfirst($user);
+} else {
+ $userid = NULL;
+}
+
+$tags = $b2tservice->getPopularTags($userid, 150);
+$tplVars['tags'] = $b2tservice->tagCloud($tags, 5, 90, 225, getSortOrder('alphabet_asc'));
+$tplVars['user'] = $user;
+
+if (isset($userid)) {
+ $tplVars['cat_url'] = createURL('bookmarks', '%s/%s');
+} else {
+ $tplVars['cat_url'] = createURL('tags', '%2$s');
+}
+
+$tplVars['sidebar_blocks'] = array('linked');
+$tplVars['pagetitle'] = $pagetitle;
+$tplVars['subtitle'] = $pagetitle;
+$tplVars['loadjs'] = true;
+
+$templateservice->loadTemplate('tags.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/profile.php b/www/profile.php
new file mode 100644
index 0000000..6a4222e
--- /dev/null
+++ b/www/profile.php
@@ -0,0 +1,191 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+// No specific services
+$tplVars['loadjs'] = true;
+
+/* Managing all possible inputs */
+isset($_POST['submittedPK']) ? define('POST_SUBMITTEDPK', $_POST['submittedPK']): define('POST_SUBMITTEDPK', '');
+isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', '');
+isset($_POST['pPass']) ? define('POST_PASS', $_POST['pPass']): define('POST_PASS', '');
+isset($_POST['pPassConf']) ? define('POST_PASSCONF', $_POST['pPassConf']): define('POST_PASSCONF', '');
+isset($_POST['pName']) ? define('POST_NAME', $_POST['pName']): define('POST_NAME', '');
+isset($_POST['pPrivateKey']) ? define('POST_PRIVATEKEY', $_POST['pPrivateKey']): define('POST_PRIVATEKEY', '');
+isset($_POST['pEnablePrivateKey']) ? define('POST_ENABLEPRIVATEKEY', $_POST['pEnablePrivateKey']): define('POST_ENABLEPRIVATEKEY', '');
+isset($_POST['pMail']) ? define('POST_MAIL', $_POST['pMail']): define('POST_MAIL', '');
+isset($_POST['pPage']) ? define('POST_PAGE', $_POST['pPage']): define('POST_PAGE', '');
+isset($_POST['pDesc']) ? define('POST_DESC', $_POST['pDesc']): define('POST_DESC', '');
+
+isset($_POST['token']) ? define('POST_TOKEN', $_POST['token']): define('POST_TOKEN', '');
+isset($_SESSION['token']) ? define('SESSION_TOKEN', $_SESSION['token']): define('SESSION_TOKEN', '');
+isset($_SESSION['token_stamp']) ? define('SESSION_TOKENSTAMP', $_SESSION['token_stamp']): define('SESSION_TOKENSTAMP', '');
+
+
+@list($url, $user) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+if ($user) {
+
+ if (is_int($user)) {
+ $userid = intval($user);
+ } else {
+ $user = urldecode($user);
+ $userinfo = $userservice->getObjectUserByUsername($user);
+ if ($userinfo == NULL) {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+ } else {
+ $userid = $userinfo->getId();
+ }
+ }
+} else {
+ $tplVars['error'] = T_('Username was not specified');
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+}
+
+$tplVars['privateKeyIsEnabled'] = '';
+if ($userservice->isLoggedOn() && $user == $currentUser->getUsername()) {
+ $title = T_('My Profile');
+ $tplVars['privateKey'] = $currentUser->getPrivateKey(true);
+
+ if ($userservice->isPrivateKeyValid($currentUser->getPrivateKey())) {
+ $tplVars['privateKeyIsEnabled'] = 'checked="checked"';
+ } else {
+ $tplVars['privateKeyIsEnabled'] = '';
+ }
+} else {
+ $title = T_('Profile') .': '. $user;
+ $tplVars['privateKey'] = '';
+}
+$tplVars['pagetitle'] = $title;
+$tplVars['subtitle'] = $title;
+
+$tplVars['user'] = $user;
+$tplVars['userid'] = $userid;
+
+/* Update Private Key */
+if (POST_SUBMITTEDPK!='' && $currentUser->getId() == $userid) {
+ $userinfo = $userservice->getObjectUserByUsername($user);
+ $tplVars['privateKey'] = $userservice->getNewPrivateKey();
+}
+
+if (POST_SUBMITTED!='' && $currentUser->getId() == $userid) {
+ $error = false;
+ $detPass = trim(POST_PASS);
+ $detPassConf = trim(POST_PASSCONF);
+ $detName = trim(POST_NAME);
+ $detPrivateKey = trim(POST_PRIVATEKEY);
+ $detEnablePrivateKey = trim(POST_ENABLEPRIVATEKEY);
+ $detMail = trim(POST_MAIL);
+ $detPage = trim(POST_PAGE);
+ $detDesc = filter(POST_DESC);
+
+ // manage token preventing from CSRF vulnaribilities
+ if ( SESSION_TOKEN == ''
+ || time() - SESSION_TOKENSTAMP > 600 //limit token lifetime, optionnal
+ || SESSION_TOKEN != POST_TOKEN) {
+ $error = true;
+ $tplVars['error'] = T_('Invalid Token');
+ }
+
+ if ($detPass != $detPassConf) {
+ $error = true;
+ $tplVars['error'] = T_('Password and confirmation do not match.');
+ }
+ if ($detPass != "" && strlen($detPass) < 6) {
+ $error = true;
+ $tplVars['error'] = T_('Password must be at least 6 characters long.');
+ }
+ if (!$userservice->isValidEmail($detMail)) {
+ $error = true;
+ $tplVars['error'] = T_('E-mail address is not valid.');
+ }
+ if (!$error) {
+ if (!$userservice->updateUser($userid, $detPass, $detName, $detMail, $detPage, $detDesc, $detPrivateKey, $detEnablePrivateKey)) {
+ $tplVars['error'] = T_('An error occurred while saving your changes.');
+ } else {
+ $tplVars['msg'] = T_('Changes saved.');
+ }
+ }
+ $userinfo = $userservice->getObjectUserByUsername($user);
+ $tplVars['privateKey'] = $userinfo->getPrivateKey(true);
+ if ($userservice->isPrivateKeyValid($userinfo->getPrivateKey())) {
+ $tplVars['privateKeyIsEnabled'] = 'checked="checked"';
+ } else {
+ $tplVars['privateKeyIsEnabled'] = '';
+ }
+}
+
+if (!$userservice->isLoggedOn() || $currentUser->getId() != $userid) {
+ $templatename = 'profile.tpl.php';
+} else {
+ $scert = SemanticScuttle_Service_Factory::get('User_SslClientCert');
+
+ if (isset($_POST['action']) && $_POST['action'] == 'registerCurrentCert') {
+ if (!$scert->hasValidCert()) {
+ $tplVars['error'] = T_('You do not have a valid SSL client certificate');
+ } else if (false !== $scert->getUserIdFromCert()) {
+ $tplVars['error'] = T_('This certificate is already registered');
+ } else if (false === $scert->registerCurrentCertificate($currentUser->getId())) {
+ $tplVars['error'] = T_('Failed to register SSL client certificate.');
+ } else {
+ $tplVars['msg'] = T_('SSL client certificate registered.');
+ }
+ } else if (isset($_POST['action']) && $_POST['action'] == 'deleteClientCert'
+ && isset($_POST['certId'])
+ ) {
+ $certId = (int)$_POST['certId'];
+ $cert = $scert->getCert($certId);
+
+ if ($cert === null) {
+ $tplVars['error'] = T_('Certificate not found.');
+ } else if ($cert->uId != $currentUser->getId()) {
+ $tplVars['error'] = T_('The certificate does not belong to you.');
+ } else if (false === $scert->delete($certId)) {
+ $tplVars['error'] = T_('Failed to delete SSL client certificate.');
+ } else {
+ $tplVars['msg'] = T_('SSL client certificate deleted.');
+ }
+ }
+
+ //Token Init
+ $_SESSION['token'] = md5(uniqid(rand(), true));
+ $_SESSION['token_stamp'] = time();
+
+ $templatename = 'editprofile.tpl.php';
+
+ $tplVars['formaction'] = createURL('profile', $user);
+ $tplVars['token'] = $_SESSION['token'];
+
+ $tplVars['sslClientCerts'] = $scert->getUserCerts($currentUser->getId());
+ $tplVars['currentCert'] = null;
+ if ($scert->hasValidCert()) {
+ $tplVars['currentCert'] = SemanticScuttle_Model_User_SslClientCert::fromCurrentCert();
+ }
+}
+
+$tplVars['objectUser'] = $userinfo;
+$templateservice->loadTemplate($templatename, $tplVars);
+?>
diff --git a/www/register.php b/www/register.php
new file mode 100644
index 0000000..a607c17
--- /dev/null
+++ b/www/register.php
@@ -0,0 +1,107 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Marcus Campbell
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+if (!$GLOBALS['enableRegistration']) {
+ header('HTTP/1.0 501 Not implemented');
+ echo 'Registration is disabled';
+ exit(1);
+}
+
+/* Service creation: only useful services are created */
+// No specific services
+
+/* Managing all possible inputs */
+isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', '');
+isset($_POST['username']) ? define('POST_USERNAME', $_POST['username']): define('POST_USERNAME', '');
+isset($_POST['password']) ? define('POST_PASS', $_POST['password']): define('POST_PASS', '');
+isset($_POST['password2']) ? define('POST_PASS2', $_POST['password2']): define('POST_PASS2', '');
+if (isset($_POST['email'])) {
+ define('POST_MAIL', $_POST['email']);
+} else if (isset($_SERVER['SSL_CLIENT_S_DN_Email'])) {
+ define('POST_MAIL', $_SERVER['SSL_CLIENT_S_DN_Email']);
+} else {
+ define('POST_MAIL', '');
+}
+isset($_POST['antispamAnswer']) ? define('POST_ANTISPAMANSWER', $_POST['antispamAnswer']): define('POST_ANTISPAMANSWER', '');
+
+
+if (POST_SUBMITTED != '') {
+ $posteduser = trim(utf8_strtolower(POST_USERNAME));
+
+ // Check if form is incomplete
+ if (!($posteduser) || POST_PASS == '' || POST_MAIL == '') {
+ $tplVars['error'] = T_('You <em>must</em> enter a username, password and e-mail address.');
+
+ // Check if passwords match
+ } elseif (POST_PASS != POST_PASS2) {
+ $tplVars['error'] = T_('Those passwords do not match.');
+
+ // Check if username is reserved
+ } elseif ($userservice->isReserved($posteduser)) {
+ $tplVars['error'] = T_('This username has been reserved, please make another choice.');
+
+ // Check if username already exists
+ } elseif ($userservice->getUserByUsername($posteduser)) {
+ $tplVars['error'] = T_('This username already exists, please make another choice.');
+
+ // Check if username is valid (length, authorized characters)
+ } elseif (!$userservice->isValidUsername($posteduser)) {
+ $tplVars['error'] = T_('This username is not valid (too short, too long, forbidden characters...), please make another choice.');
+
+ // Check if e-mail address is valid
+ } elseif (!$userservice->isValidEmail(POST_MAIL)) {
+ $tplVars['error'] = T_('E-mail address is not valid. Please try again.');
+
+ // Check if antispam answer is valid (doesn't take into account spaces and uppercase)
+ } elseif (strcasecmp(str_replace(' ', '', POST_ANTISPAMANSWER), str_replace(' ', '', $GLOBALS['antispamAnswer'])) != 0) {
+ $tplVars['error'] = T_('Antispam answer is not valid. Please try again.');
+
+ // Register details
+ } else {
+ $uId = $userservice->addUser($posteduser, POST_PASS, POST_MAIL);
+ if ($uId !== false) {
+ if (isset($_SERVER['SSL_CLIENT_VERIFY'])
+ && $_SERVER['SSL_CLIENT_VERIFY'] == 'SUCCESS'
+ ) {
+ $ssl = SemanticScuttle_Service_Factory::get('User_SslClientCert');
+ $ssl->registerCurrentCertificate($uId);
+ $ssl->updateProfileFromCurentCert($uId);
+ }
+ // Log in with new username
+ $login = $userservice->login($posteduser, POST_PASS);
+ if ($login) {
+ header('Location: '. createURL('bookmarks', $posteduser));
+ }
+ $tplVars['msg'] = T_('You have successfully registered. Enjoy!');
+ } else {
+ $tplVars['error'] = T_('Registration failed. Please try again.');
+ }
+ }
+}
+
+$tplVars['antispamQuestion'] = $GLOBALS['antispamQuestion'];
+$tplVars['loadjs'] = true;
+$tplVars['subtitle'] = T_('Register');
+$tplVars['formaction'] = createURL('register');
+$templateservice->loadTemplate('register.tpl', $tplVars);
+?>
diff --git a/www/rss.php b/www/rss.php
new file mode 100644
index 0000000..6f376ec
--- /dev/null
+++ b/www/rss.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * RSS output of the latest posts.
+ *
+ * Parameter:
+ * - count=15
+ * Sets the number of RSS entries to export
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+$httpContentType = 'application/rss+xml';
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice = SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice = SemanticScuttle_Service_Factory::get('Cache');
+
+if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO']) > 1) {
+ $parts = explode('/', $_SERVER['PATH_INFO']);
+ if (count($parts) == 3) {
+ list($url, $user, $cat) = $parts;
+ } else {
+ list($url, $user) = $parts;
+ $cat = null;
+ }
+} else {
+ $url = '';
+ $user = '';
+ $cat = null;
+}
+
+if ($usecache) {
+ // Generate hash for caching on
+ $hashtext = $_SERVER['REQUEST_URI'];
+ if ($userservice->isLoggedOn()) {
+ $hashtext .= $userservice->getCurrentUserID();
+ if ($currentUser->getUsername() == $user) {
+ $hashtext .= $user;
+ }
+ }
+ $hash = md5($hashtext);
+
+ // Cache for an hour
+ $cacheservice->Start($hash, 3600);
+}
+
+if (isset($_GET['count'])) {
+ $rssEntries = (int)$_GET['count'];
+}
+if (!isset($rssEntries) || $rssEntries <= 0) {
+ $rssEntries = $defaultRssEntries;
+} else if ($rssEntries > $maxRssEntries) {
+ $rssEntries = $maxRssEntries;
+}
+
+$privateKey = null;
+if (isset($_GET['privateKey'])) {
+ $privateKey = $_GET['privateKey'];
+}
+
+$userid = null;
+$watchlist = null;
+$pagetitle = '';
+if ($user && $user != 'all') {
+ if ($user == 'watchlist') {
+ $user = $cat;
+ $cat = null;
+ $watchlist = true;
+ }
+ if (is_int($user)) {
+ $userid = intval($user);
+ } else {
+ if ($userinfo = $userservice->getUserByUsername($user)) {
+ $userid = $userinfo[$userservice->getFieldName('primary')];
+ /* if user is not logged in and has valid privateKey */
+ if (!$userservice->isLoggedOn()) {
+ if ($privateKey != null) {
+ if (!$userservice->loginPrivateKey($privateKey)) {
+ $tplVars['error'] = sprintf(T_('Failed to Autenticate User with username %s using private key'), $user);
+ header('Content-type: text/html; charset=utf-8');
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ //throw a 404 error
+ exit();
+ }
+ }
+ }
+
+ } else {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ header('Content-type: text/html; charset=utf-8');
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ //throw a 404 error
+ exit();
+ }
+ }
+ $pagetitle .= ": ". $user;
+} else {
+ if ($privateKey != null) {
+ if (!$userservice->loginPrivateKey($privateKey)) {
+ $tplVars['error'] = sprintf(T_('Failed to Autenticate User with username %s using private key'), $user);
+ header('Content-type: text/html; charset=utf-8');
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ //throw a 404 error
+ exit();
+ }
+ } else {
+ $userid = null;
+ }
+}
+
+if ($cat) {
+ $pagetitle .= ": ". str_replace('+', ' + ', $cat);
+}
+
+$tplVars['feedtitle'] = filter($GLOBALS['sitename'] . (isset($pagetitle) ? $pagetitle : ''));
+$tplVars['pagelink'] = addProtocolToUrl(ROOT);
+$tplVars['feedlink'] = addProtocolToUrl(ROOT) . 'rss?sort=' . getSortOrder();
+$tplVars['feeddescription'] = sprintf(T_('Recent bookmarks posted to %s'), $GLOBALS['sitename']);
+
+$bookmarks = $bookmarkservice->getBookmarks(
+ 0, $rssEntries, $userid, $cat,
+ null, getSortOrder(), $watchlist,
+ null, null, null
+);
+
+$bookmarks_tmp = filter($bookmarks['bookmarks']);
+
+$bookmarks_tpl = array();
+$latestdate = null;
+$guidBaseUrl = addProtocolToUrl(ROOT) . '#';
+foreach ($bookmarks_tmp as $key => $row) {
+ $_link = $row['bAddress'];
+ // Redirection option
+ if ($GLOBALS['useredir']) {
+ $_link = $GLOBALS['url_redir'] . $_link;
+ }
+ if ($row['bDatetime'] > $latestdate) {
+ $latestdate = $row['bDatetime'];
+ }
+ $_pubdate = gmdate('r', strtotime($row['bDatetime']));
+
+ $bookmarks_tpl[] = array(
+ 'title' => $row['bTitle'],
+ 'link' => $_link,
+ 'description' => $row['bDescription'],
+ 'creator' => SemanticScuttle_Model_UserArray::getName($row),
+ 'pubdate' => $_pubdate,
+ 'tags' => $row['tags'],
+ 'guid' => $guidBaseUrl . $row['bId'],
+ );
+}
+unset($bookmarks_tmp);
+unset($bookmarks);
+$tplVars['bookmarks'] = $bookmarks_tpl;
+$tplVars['feedlastupdate'] = date('r', strtotime($latestdate));
+
+$templateservice->loadTemplate('rss.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/search.php b/www/search.php
new file mode 100644
index 0000000..e4977ad
--- /dev/null
+++ b/www/search.php
@@ -0,0 +1,155 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2005 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Managing all possible inputs */
+isset($_POST['terms']) ? define('POST_TERMS', $_POST['terms']): define('POST_TERMS', '');
+isset($_POST['range']) ? define('POST_RANGE', $_POST['range']): define('POST_RANGE', '');
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+
+// POST
+if (POST_TERMS != '') {
+ // Redirect to GET
+ header(
+ 'Location: '
+ . createURL('search', POST_RANGE .'/'. filter(POST_TERMS, 'url'))
+ );
+ exit();
+
+}
+
+/* Service creation: only useful services are created */
+$bookmarkservice = SemanticScuttle_Service_Factory::get('Bookmark');
+$searchhistoryservice = SemanticScuttle_Service_Factory::get('SearchHistory');
+
+/* Managing current logged user */
+$currentUserId = $userservice->getCurrentUserId();
+
+
+$exploded = isset($_SERVER['PATH_INFO'])
+ ? explode('/', $_SERVER['PATH_INFO']) : null;
+if(count($exploded) == 4) {
+ list($url, $range, $terms, $page) = $exploded;
+} else if (count($exploded) == 2) {
+ list($url, $range) = $exploded;
+ $terms = $page = NULL;
+} else {
+ list($url, $range, $terms) = $exploded;
+ $page = NULL;
+}
+//some OpenSearch clients need that
+$terms = urldecode($terms);
+
+$tplVars['loadjs'] = true;
+
+// Pagination
+$perpage = getPerPageCount($currentUser);
+if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+} else {
+ $page = 0;
+ $start = 0;
+}
+
+$s_user = NULL;
+$s_start = NULL;
+$s_end = NULL;
+$s_watchlist = NULL;
+
+// No search terms
+if (is_null($terms)) {
+ $tplVars['subtitle'] = T_('Search Bookmarks');
+ $s_end = date('Y-m-d H:i:s', strtotime('tomorrow'));
+ $s_start = date('Y-m-d H:i:s', strtotime($s_end .' -'. $defaultRecentDays .' days'));
+
+ // Search terms
+} else {
+ $tplVars['subtitle'] = T_('Search Results');
+ $selected = ' selected="selected"';
+
+ switch ($range) {
+ case 'all':
+ $tplVars['select_all'] = $selected;
+ $s_user = NULL;
+ break;
+ case 'watchlist':
+ $tplVars['select_watchlist'] = $selected;
+ $s_user = $currentUserId;
+ $s_watchlist = true;
+ break;
+ default:
+ $s_user = $range;
+ break;
+ }
+
+ if (isset($s_user)) {
+ $tplVars['user'] = $range;
+ $s_user = $userservice->getIdFromUser($s_user);
+ if($s_user == NULL) {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $s_user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+ }
+
+ }
+}
+$bookmarks = $bookmarkservice->getBookmarks(
+ $start, $perpage, $s_user, NULL, $terms, getSortOrder(),
+ $s_watchlist, $s_start, $s_end
+);
+
+// Save search
+$searchhistoryservice->addSearch(
+ $terms, $range, $bookmarks['total'], $currentUserId
+);
+
+if (isset($_GET['lucky']) && $_GET['lucky']
+ && isset($bookmarks['bookmarks'][0])
+) {
+ $url = $bookmarks['bookmarks'][0]['bAddress'];
+ header('Location: ' . $url);
+ exit();
+}
+
+if ($GLOBALS['enableGoogleCustomSearch']) {
+ $tplVars['tipMsg'] = T_('Unsatisfied? You can also try our ')
+ . '<a href="' . createUrl('gsearch/index') . '">Google Custom Search page</a>.';
+}
+$tplVars['rsschannels'] = array();
+$tplVars['page'] = $page;
+$tplVars['start'] = $start;
+$tplVars['popCount'] = 25;
+$tplVars['sidebar_blocks'] = array('search', 'recent', 'menu2');
+$tplVars['range'] = $range;
+$tplVars['terms'] = $terms;
+$tplVars['pagetitle'] = T_('Search Bookmarks');
+$tplVars['bookmarkCount'] = $start + 1;
+$tplVars['total'] = $bookmarks['total'];
+$tplVars['bookmarks'] = $bookmarks['bookmarks'];
+$tplVars['cat_url'] = createURL('tags', '%2$s');
+$tplVars['nav_url'] = createURL('search', $range .'/'. $terms .'/%3$s');
+
+$templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+?>
diff --git a/www/tag2tagadd.php b/www/tag2tagadd.php
new file mode 100644
index 0000000..3f4af8c
--- /dev/null
+++ b/www/tag2tagadd.php
@@ -0,0 +1,73 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$tag2tagservice = SemanticScuttle_Service_Factory :: get('Tag2Tag');
+
+/* Managing all possible inputs */
+isset($_POST['confirm']) ? define('POST_CONFIRM', $_POST['confirm']): define('POST_CONFIRM', '');
+isset($_POST['cancel']) ? define('POST_CANCEL', $_POST['cancel']): define('POST_CANCEL', '');
+isset($_POST['tag1']) ? define('POST_TAG1', $_POST['tag1']): define('POST_TAG1', '');
+isset($_POST['linkType']) ? define('POST_LINKTYPE', $_POST['linkType']): define('POST_LINKTYPE', '');
+isset($_POST['tag2']) ? define('POST_TAG2', $_POST['tag2']): define('POST_TAG2', '');
+
+
+//permissions
+if(!$userservice->isLoggedOn()) {
+ $tplVars['error'] = T_('Permission denied.');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+}
+
+/* Managing path info */
+if (isset($_SERVER['PATH_INFO'])) {
+ list ($url, $tag1) = explode('/', $_SERVER['PATH_INFO']);
+} else {
+ $url = $tag1 = null;
+}
+
+if (POST_CONFIRM != '') {
+ $tag1 = POST_TAG1;
+ $linkType = POST_LINKTYPE;
+ $tag2 = POST_TAG2;
+ if ($tag2tagservice->addLinkedTags($tag1, $tag2, $linkType, $currentUser->getId())) {
+ $tplVars['msg'] = T_('Tag link created');
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername()));
+ } else {
+ $tplVars['error'] = T_('Failed to create the link');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+ }
+} elseif (POST_CANCEL) {
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername() .'/'. $tag1));
+}
+
+$tplVars['links'] = $tag2tagservice->getLinks($currentUser->getId());
+
+$tplVars['tag1'] = $tag1;
+$tplVars['tag2'] = '';
+$tplVars['subtitle'] = T_('Add Tag Link') .': '. $tag1;
+$tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $tag1;
+$tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+$templateservice->loadTemplate('tag2tagadd.tpl', $tplVars);
+?>
diff --git a/www/tag2tagdelete.php b/www/tag2tagdelete.php
new file mode 100644
index 0000000..06fea98
--- /dev/null
+++ b/www/tag2tagdelete.php
@@ -0,0 +1,85 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$tag2tagservice = SemanticScuttle_Service_Factory :: get('Tag2Tag');
+
+/* Managing all possible inputs */
+isset($_POST['confirm']) ? define('POST_CONFIRM', $_POST['confirm']): define('POST_CONFIRM', '');
+isset($_POST['cancel']) ? define('POST_CANCEL', $_POST['cancel']): define('POST_CANCEL', '');
+isset($_POST['tag1']) ? define('POST_TAG1', $_POST['tag1']): define('POST_TAG1', '');
+isset($_POST['linkType']) ? define('POST_LINKTYPE', $_POST['linkType']): define('POST_LINKTYPE', '');
+isset($_POST['tag2']) ? define('POST_TAG2', $_POST['tag2']): define('POST_TAG2', '');
+
+isset($_SERVER['HTTP_REFERER']) ? define('HTTP_REFERER', $_SERVER['HTTP_REFERER']): define('HTTP_REFERER', '');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+//permissions
+if(!$userservice->isloggedOn()) {
+ $tplVars['error'] = T_('Permission denied.');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+}
+
+/* Managing path info */
+if(isset($_SERVER['PATH_INFO'])) {
+ $exploded = explode('/', $_SERVER['PATH_INFO']);
+ if(count($exploded) == 3) {
+ list ($url, $tag1, $tag2) = explode('/', $_SERVER['PATH_INFO']);
+ } else {
+ list ($url, $tag1) = explode('/', $_SERVER['PATH_INFO']);
+ $tag2 = '';
+ }
+} else {
+ $url = $tag1 = $tag2 = '';
+}
+
+
+
+if (POST_CONFIRM) {
+ $tag = POST_TAG1;
+ $linkType = POST_LINKTYPE;
+ $newTag = POST_TAG2;
+ if ($tag2tagservice->removeLinkedTags(POST_TAG1, POST_TAG2, POST_LINKTYPE, $currentUser->getId())) {
+ $tplVars['msg'] = T_('Tag link deleted');
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername().'/'.$tag));
+ } else {
+ $tplVars['error'] = T_('Failed to delete the link');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+ }
+} elseif (POST_CANCEL) {
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername() .'/'. $tag1));
+}
+
+$tplVars['links'] = $tag2tagservice->getLinks($currentUser->getId());
+
+$tplVars['tag1'] = $tag1;
+$tplVars['tag2'] = $tag2;
+$tplVars['subtitle'] = T_('Delete Link Between Tags') .': '. $tag1.' > '.$tag2;
+$tplVars['formaction'] = $_SERVER['SCRIPT_NAME'];
+$tplVars['referrer'] = HTTP_REFERER;
+$templateservice->loadTemplate('tag2tagdelete.tpl', $tplVars);
+?>
diff --git a/www/tag2tagedit.php b/www/tag2tagedit.php
new file mode 100644
index 0000000..a652d8a
--- /dev/null
+++ b/www/tag2tagedit.php
@@ -0,0 +1,60 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$tag2tagservice = SemanticScuttle_Service_Factory :: get('Tag2Tag');
+
+
+/* Managing all possible inputs */
+isset($_SERVER['HTTP_REFERER']) ? define('HTTP_REFERER', $_SERVER['HTTP_REFERER']): define('HTTP_REFERER', '');
+
+//permissions
+if(!$userservice->loggedOn()) {
+ $tplVars['error'] = T_('Permission denied.');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+}
+
+/* Managing path info */
+if(isset($_SERVER['PATH_INFO'])) {
+ $exploded = explode('/', $_SERVER['PATH_INFO']);
+ if(count($exploded) == 3) {
+ list ($url, $tag1, $tag2) = explode('/', $_SERVER['PATH_INFO']);
+ } else {
+ list ($url, $tag1) = explode('/', $_SERVER['PATH_INFO']);
+ $tag2 = '';
+ }
+} else {
+ $url = $tag1 = $tag2 = '';
+}
+
+$tplVars['links'] = $tag2tagservice->getLinks($userservice->getCurrentUserId());
+
+$tplVars['tag1'] = $tag1;
+$tplVars['tag2'] = $tag2;
+$tplVars['subtitle'] = T_('Edit Link Between Tags') .': '. $tag1.' > '.$tag2;
+$tplVars['formaddaction'] = createUrl('tag2tagadd');
+$tplVars['formdeleteaction'] = createUrl('tag2tagdelete');
+$tplVars['referrer'] = HTTP_REFERER;
+$templateservice->loadTemplate('tag2tagedit.tpl', $tplVars);
+?>
diff --git a/www/tagcommondescriptionedit.php b/www/tagcommondescriptionedit.php
new file mode 100644
index 0000000..979eca2
--- /dev/null
+++ b/www/tagcommondescriptionedit.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Edits the common tag description
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$b2tservice = SemanticScuttle_Service_Factory::get('Bookmark2Tag');
+$cdservice = SemanticScuttle_Service_Factory::get('CommonDescription');
+
+/* Managing all possible inputs */
+isset($_POST['confirm'])
+ ? define('POST_CONFIRM', $_POST['confirm'])
+ : define('POST_CONFIRM', '');
+isset($_POST['cancel'])
+ ? define('POST_CANCEL', $_POST['cancel'])
+ : define('POST_CANCEL', '');
+isset($_POST['description'])
+ ? define('POST_DESCRIPTION', $_POST['description'])
+ : define('POST_DESCRIPTION', '');
+isset($_POST['referrer'])
+ ? define('POST_REFERRER', $_POST['referrer'])
+ : define('POST_REFERRER', '');
+
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+/* Managing path info */
+list ($url, $tag) = explode('/', $_SERVER['PATH_INFO']);
+
+//permissions
+if (!$userservice->isLoggedOn()
+ || (!$GLOBALS['enableCommonTagDescriptionEditedByAll']
+ && !$currentUser->isAdmin()
+ )
+) {
+ $tplVars['error'] = T_('Permission denied.');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+}
+
+$template = 'tagcommondescriptionedit.tpl';
+
+if (POST_CONFIRM) {
+ if (strlen($tag) > 0
+ && $cdservice->addTagDescription($tag, stripslashes(POST_DESCRIPTION), $currentUser->getId(), time())
+ ) {
+ $tplVars['msg'] = T_('Tag common description updated');
+ if (POST_REFERRER) {
+ header('Location: '. POST_REFERRER);
+ }
+ } else {
+ $tplVars['error'] = T_('Failed to update the tag common description');
+ $template = 'error.500.tpl';
+ }
+} else if (POST_CANCEL) {
+ if (POST_REFERRER) {
+ header('Location: '. POST_REFERRER);
+ }
+}
+
+$tplVars['subtitle'] = T_('Edit Tag Common Description') .': '. $tag;
+$tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $tag;
+$tplVars['referrer'] = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
+$tplVars['tag'] = $tag;
+$tplVars['description'] = $cdservice->getLastTagDescription($tag);
+$templateservice->loadTemplate($template, $tplVars);
+?>
diff --git a/www/tagdelete.php b/www/tagdelete.php
new file mode 100644
index 0000000..1a237d4
--- /dev/null
+++ b/www/tagdelete.php
@@ -0,0 +1,58 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$b2tservice = SemanticScuttle_Service_Factory :: get('Bookmark2Tag');
+
+
+/* Managing all possible inputs */
+isset($_POST['confirm']) ? define('POST_CONFIRM', $_POST['confirm']): define('POST_CONFIRM', '');
+isset($_POST['cancel']) ? define('POST_CANCEL', $_POST['cancel']): define('POST_CANCEL', '');
+isset($_POST['referrer']) ? define('POST_REFERRER', $_POST['referrer']): define('POST_REFERRER', '');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+/* Managing path info */
+list ($url, $tag) = explode('/', $_SERVER['PATH_INFO']);
+
+
+
+if (POST_CONFIRM) {
+ if ($b2tservice->deleteTag($currentUser->getId(), $tag)) {
+ $tplVars['msg'] = T_('Tag deleted');
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername()));
+ } else {
+ $tplVars['error'] = T_('Failed to delete the tag');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+ }
+} elseif (POST_CANCEL) {
+ header('Location: '. POST_REFERRER);
+}
+
+$tplVars['subtitle'] = T_('Delete Tag') .': '. $tag;
+$tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $tag;
+$tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+$templateservice->loadTemplate('tagdelete.tpl', $tplVars);
+?>
diff --git a/www/tagedit.php b/www/tagedit.php
new file mode 100644
index 0000000..af490a7
--- /dev/null
+++ b/www/tagedit.php
@@ -0,0 +1,68 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2006 - 2007 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$tagservice = SemanticScuttle_Service_Factory :: get('Tag');
+
+/* Managing all possible inputs */
+isset($_POST['confirm']) ? define('POST_CONFIRM', $_POST['confirm']): define('POST_CONFIRM', '');
+isset($_POST['cancel']) ? define('POST_CANCEL', $_POST['cancel']): define('POST_CANCEL', '');
+isset($_POST['description']) ? define('POST_DESCRIPTION', $_POST['description']): define('POST_DESCRIPTION', '');
+isset($_POST['referrer']) ? define('POST_REFERRER', $_POST['referrer']): define('POST_REFERRER', '');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+/* Managing path info */
+list ($url, $tag) = explode('/', $_SERVER['PATH_INFO']);
+
+$template = 'tagedit.tpl';
+
+//permissions
+if(!$userservice->isLoggedOn()) {
+ $tplVars['error'] = T_('Permission denied.');
+ $templateservice->loadTemplate('error.500.tpl', $tplVars);
+ exit();
+}
+
+if (POST_CONFIRM) {
+ if ( strlen($tag)>0 &&
+ $tagservice->updateDescription($tag, $currentUser->getId(), stripslashes(POST_DESCRIPTION))
+ ) {
+ $tplVars['msg'] = T_('Tag description updated');
+ header('Location: '. POST_REFERRER);
+ } else {
+ $tplVars['error'] = T_('Failed to update the tag description');
+ $template = 'error.500.tpl';
+ }
+} elseif (POST_CANCEL) {
+ header('Location: '. POST_REFERRER);
+} else {
+ $tplVars['subtitle'] = T_('Edit Tag Description') .': '. $tag;
+ $tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $tag;
+ $tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+ $tplVars['tag'] = $tag;
+ $tplVars['description'] = $tagservice->getDescription($tag, $currentUser->getId());
+}
+$templateservice->loadTemplate($template, $tplVars);
+?>
diff --git a/www/tagrename.php b/www/tagrename.php
new file mode 100644
index 0000000..18e26ab
--- /dev/null
+++ b/www/tagrename.php
@@ -0,0 +1,78 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2006 - 2007 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$b2tservice = SemanticScuttle_Service_Factory :: get('Bookmark2Tag');
+$tagservice = SemanticScuttle_Service_Factory :: get('Tag');
+$tag2tagservice = SemanticScuttle_Service_Factory :: get('Tag2Tag');
+
+/* Managing all possible inputs */
+isset($_POST['confirm']) ? define('POST_CONFIRM', $_POST['confirm']): define('POST_CONFIRM', '');
+isset($_POST['cancel']) ? define('POST_CANCEL', $_POST['cancel']): define('POST_CANCEL', '');
+isset($_POST['old']) ? define('POST_OLD', $_POST['old']): define('POST_OLD', '');
+isset($_POST['new']) ? define('POST_NEW', $_POST['new']): define('POST_NEW', '');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+/* Managing path info */
+list ($url, $tag) = explode('/', $_SERVER['PATH_INFO']);
+//$tag = isset($_GET['query']) ? $_GET['query'] : NULL;
+$template = 'tagrename.tpl';
+
+if (POST_CONFIRM) {
+ if (trim(POST_OLD) != '') {
+ $old = trim(POST_OLD);
+ } else {
+ $old = NULL;
+ }
+
+ if (trim(POST_NEW) != '') {
+ $new = trim(POST_NEW);
+ } else {
+ $new = NULL;
+ }
+
+ if (
+ !is_null($old) &&
+ !is_null($new) &&
+ $tagservice->renameTag($currentUser->getId(), $old, $new) &&
+ $b2tservice->renameTag($currentUser->getId(), $old, $new) &&
+ $tag2tagservice->renameTag($currentUser->getId(), $old, $new)
+ ) {
+ $tplVars['msg'] = T_('Tag renamed');
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername()));
+ } else {
+ $tplVars['error'] = T_('Failed to rename the tag');
+ $template = 'error.500.tpl';
+ }
+} elseif (POST_CANCEL) {
+ header('Location: '. createURL('bookmarks', $currentUser->getUsername() .'/'. $tag));
+} else {
+ $tplVars['subtitle'] = T_('Rename Tag') .': '. $tag;
+ $tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $tag;
+ $tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+ $tplVars['old'] = $tag;
+}
+$templateservice->loadTemplate($template, $tplVars);
+?>
diff --git a/www/tags.php b/www/tags.php
new file mode 100644
index 0000000..3e2cd5f
--- /dev/null
+++ b/www/tags.php
@@ -0,0 +1,125 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing all possible inputs */
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+/* Managing path info */
+list($url, $cat) = explode('/', $_SERVER['PATH_INFO']);
+
+
+if (!$cat) {
+ header('Location: '. createURL('populartags'));
+ exit;
+}
+
+$titleTags = explode('+', filter($cat));
+$pagetitle = T_('Tags') .': ';
+for($i = 0; $i<count($titleTags);$i++) {
+ $pagetitle.= $titleTags[$i].'<a href="'.createUrl('tags', aggregateTags($titleTags, '+', $titleTags[$i])).'" title="'.T_('Remove the tag from the selection').'">*</a> + ';
+}
+$pagetitle = substr($pagetitle, 0, strlen($pagetitle) - strlen(' + '));
+
+
+//$cattitle = str_replace('+', ' + ', $cat);
+
+if ($usecache) {
+ // Generate hash for caching on
+ if ($userservice->isLoggedOn()) {
+ $hash = md5($_SERVER['REQUEST_URI'] . $currentUser->getId());
+ } else {
+ $hash = md5($_SERVER['REQUEST_URI']);
+ }
+
+ // Cache for 30 minutes
+ $cacheservice->Start($hash, 1800);
+}
+
+// Header variables
+$tplVars['pagetitle'] = T_('Tags') .': '. $cat;
+$tplVars['loadjs'] = true;
+$tplVars['rsschannels'] = array(
+ array(
+ sprintf(T_('%s: tagged with "%s"'), $sitename, $cat),
+ createURL('rss', 'all/' . filter($cat, 'url'))
+ . '?sort='.getSortOrder()
+ )
+);
+
+if ($userservice->isLoggedOn()) {
+ if ($userservice->isPrivateKeyValid($currentUser->getPrivateKey())) {
+ $currentUsername = $currentUser->getUsername();
+ array_push(
+ $tplVars['rsschannels'],
+ array(
+ sprintf(
+ T_('%s: tagged with "%s" (+private %s)'),
+ $sitename, $cat, $currentUsername
+ ),
+ createURL('rss', filter($currentUsername, 'url'))
+ . '?sort=' . getSortOrder()
+ . '&privateKey=' . $currentUser->getPrivateKey()
+ )
+ );
+ }
+}
+
+// Pagination
+$perpage = getPerPageCount($currentUser);
+if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+} else {
+ $page = 0;
+ $start = 0;
+}
+
+$tplVars['page'] = $page;
+$tplVars['start'] = $start;
+$tplVars['popCount'] = 25;
+$tplVars['currenttag'] = $cat;
+$tplVars['sidebar_blocks'] = array('linked', 'related', 'menu2');//array('linked', 'related', 'popular');
+$tplVars['subtitlehtml'] = $pagetitle;
+$tplVars['bookmarkCount'] = $start + 1;
+$bookmarks = $bookmarkservice->getBookmarks($start, $perpage, NULL, $cat, NULL, getSortOrder());
+$tplVars['total'] = $bookmarks['total'];
+$tplVars['bookmarks'] = $bookmarks['bookmarks'];
+$tplVars['cat_url'] = createURL('bookmarks', '%1$s/%2$s');
+$tplVars['nav_url'] = createURL('tags', '%2$s%3$s');
+
+$templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+
+?>
diff --git a/www/themes/default/icon.png b/www/themes/default/icon.png
new file mode 100644
index 0000000..be864a7
--- /dev/null
+++ b/www/themes/default/icon.png
Binary files differ
diff --git a/www/themes/default/images/b_edit.png b/www/themes/default/images/b_edit.png
new file mode 100644
index 0000000..05711a0
--- /dev/null
+++ b/www/themes/default/images/b_edit.png
Binary files differ
diff --git a/www/themes/default/images/bg_admin.png b/www/themes/default/images/bg_admin.png
new file mode 100644
index 0000000..c707779
--- /dev/null
+++ b/www/themes/default/images/bg_admin.png
Binary files differ
diff --git a/www/themes/default/images/bg_bar.png b/www/themes/default/images/bg_bar.png
new file mode 100644
index 0000000..7152ba1
--- /dev/null
+++ b/www/themes/default/images/bg_bar.png
Binary files differ
diff --git a/www/themes/default/images/bg_header.png b/www/themes/default/images/bg_header.png
new file mode 100644
index 0000000..6fa4161
--- /dev/null
+++ b/www/themes/default/images/bg_header.png
Binary files differ
diff --git a/www/themes/default/images/bg_sidebar.png b/www/themes/default/images/bg_sidebar.png
new file mode 100644
index 0000000..0edb153
--- /dev/null
+++ b/www/themes/default/images/bg_sidebar.png
Binary files differ
diff --git a/www/themes/default/images/loading.gif b/www/themes/default/images/loading.gif
new file mode 100644
index 0000000..859126e
--- /dev/null
+++ b/www/themes/default/images/loading.gif
Binary files differ
diff --git a/www/themes/default/images/logo-empty.png b/www/themes/default/images/logo-empty.png
new file mode 100644
index 0000000..4590098
--- /dev/null
+++ b/www/themes/default/images/logo-empty.png
Binary files differ
diff --git a/www/themes/default/images/logo.png b/www/themes/default/images/logo.png
new file mode 100644
index 0000000..4afe565
--- /dev/null
+++ b/www/themes/default/images/logo.png
Binary files differ
diff --git a/www/themes/default/images/logo_24.gif b/www/themes/default/images/logo_24.gif
new file mode 100644
index 0000000..d5af64d
--- /dev/null
+++ b/www/themes/default/images/logo_24.gif
Binary files differ
diff --git a/www/themes/default/images/rss.gif b/www/themes/default/images/rss.gif
new file mode 100644
index 0000000..094ba10
--- /dev/null
+++ b/www/themes/default/images/rss.gif
Binary files differ
diff --git a/www/themes/default/images/thumbs_up.orig.png b/www/themes/default/images/thumbs_up.orig.png
new file mode 100644
index 0000000..1a89d0e
--- /dev/null
+++ b/www/themes/default/images/thumbs_up.orig.png
Binary files differ
diff --git a/www/themes/default/images/vote-against-voted.png b/www/themes/default/images/vote-against-voted.png
new file mode 100644
index 0000000..740dfc8
--- /dev/null
+++ b/www/themes/default/images/vote-against-voted.png
Binary files differ
diff --git a/www/themes/default/images/vote-against.png b/www/themes/default/images/vote-against.png
new file mode 100644
index 0000000..f15ea4d
--- /dev/null
+++ b/www/themes/default/images/vote-against.png
Binary files differ
diff --git a/www/themes/default/images/vote-for-voted.png b/www/themes/default/images/vote-for-voted.png
new file mode 100644
index 0000000..5abd43e
--- /dev/null
+++ b/www/themes/default/images/vote-for-voted.png
Binary files differ
diff --git a/www/themes/default/images/vote-for.png b/www/themes/default/images/vote-for.png
new file mode 100644
index 0000000..5973ae9
--- /dev/null
+++ b/www/themes/default/images/vote-for.png
Binary files differ
diff --git a/www/themes/default/scuttle.css b/www/themes/default/scuttle.css
new file mode 100644
index 0000000..c9d7293
--- /dev/null
+++ b/www/themes/default/scuttle.css
@@ -0,0 +1,658 @@
+/* Watch out. This file bugs (in the header) when it is formated with Eclipse (Ctrl+Shift+F)*/
+
+/* BASE */
+
+* {
+ font-family: "trebuchet ms", tahoma, sans-serif;
+}
+a {
+ color: #47A;
+ text-decoration: none;
+}
+a:hover {
+ color: #258;
+ text-decoration: underline;
+}
+a img {
+ border: 0;
+}
+
+
+body {
+ margin: 0;
+ padding: 0;
+}
+input[type=text],
+input[type=password],
+select,
+textarea {
+ border: 1px solid #AAA;
+ padding: 0.1em;
+}
+input[type=text],
+input[type=password],
+textarea {
+ padding: 0.2em;
+}
+input[type=text]:focus,
+input[type=password]:focus,
+select:focus,
+textarea:focus {
+ border-color: #666;
+}
+p.tipMsg,
+p.error,
+p.success {
+ border: 1px solid;
+ font-size: small;
+ margin: 0.5em;
+ padding: 0.5em;
+ width: 70%;
+}
+p.tipMsg {
+ background: #FFFF99;
+ border-color: #CC9900;
+ color: #CC9900;
+}
+p.error {
+ background: #FCC;
+ border-color: #966;
+ color: #633;
+}
+p.success {
+ background: #CFC;
+ border-color: #696;
+ color: #363;
+}
+td#availability {
+ color: #285;
+ font-weight: bold;
+}
+td#availability.not-available {
+ color: #F00;
+}
+textarea {
+ font-size: small;
+ padding: 0.2em;
+}
+th {
+ padding-right: 1em;
+ text-align: right;
+}
+
+/* HEADER */
+
+div#header {
+ background: #FFF url('images/bg_header.png') bottom repeat-x;
+ border-bottom: 3px solid #9CD;
+ clear: both;
+}
+div#header:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+* html div#header {
+ height: 1%;
+}
+h1 {
+ float: left;
+ font-size: x-large;
+ font-weight: bold;
+ letter-spacing: 0.25em;
+ margin: 0;
+ padding: 1em;
+}
+html > body h1 {
+ background: url('images/logo.png') no-repeat 10px;
+ padding-left: 75px;
+}
+html > body div#header.popup h1 {
+ background: url('images/logo.png') no-repeat 10px;
+ background-size: 24px;
+ padding: 0.5em 0.5em 0.5em 50px;
+}
+/*html > body div#header #welcome {
+ position:absolute;
+ left:75px;
+}*/
+#welcome {
+ border: 10px solid #995;
+ width: 66%;
+ padding: 10px;
+ text-align:center;
+ font-size: medium;
+}
+#bottom {
+ padding-top: 10px;
+ text-align: center;
+ font-size: xx-small;
+}
+h1 a {
+ color: #000;
+}
+h1 a:hover {
+ color: #000;
+}
+h2 {
+ background: #666 url('images/bg_bar.png') center center repeat-x;
+ border-bottom: 3px solid #DDD;
+ clear: both;
+ color: #FFF;
+ font-size: medium;
+ letter-spacing: 0.1em;
+ margin: 0 0 1em 0;
+ padding: 0.5em 1em;
+}
+h2 a, h2 a:hover {
+ color: #DDD;
+}
+
+/*sup {
+ color: #77ABE0;
+ font-weight: bold;
+}*/
+
+/* NAVIGATION */
+
+ul#navigation {
+ list-style-type: none;
+ margin: 0;
+ padding: 1.75em 1em;
+ width: auto;
+}
+ul#navigation a {
+ font-size: medium;
+ font-weight: bold;
+ padding: 0.2em 0.5em;
+}
+ul#navigation a:hover {
+ background: #7AD;
+ color: #FFF;
+}
+ul#navigation li {
+ float: left;
+}
+ul#navigation li.access {
+ float: right;
+}
+
+/* BOOKMARKS */
+
+ol#bookmarks {
+ list-style-type: none;
+ margin: 0;
+ padding: 0 1em;
+ width: 70%;
+}
+html > body ol#bookmarks {
+ margin: 0 1em;
+ padding: 0;
+}
+img.thumbnail {
+ float: left;
+ padding: 1px;
+ margin-right: 6px;
+ margin-bottom:4px;
+ border:1px solid #AAA;
+}
+div.link a {
+ color: blue;
+ font-size: medium;
+}
+div.link a:visited {
+ color: purple;
+}
+div.address {
+ color: #285;
+}
+div.meta {
+ color: #285;
+}
+div.meta span {
+ color: #F00;
+}
+div.privateNote {
+ font-variant: small-caps;
+ font-size: small;
+ text-align: right;
+}
+li.xfolkentry {
+ border-bottom: 1px solid #DDD;
+ margin-bottom: 0;
+ padding: 1em 0.5em;
+ overflow: auto;
+}
+html > body li.xfolkentry {
+ border-bottom: 1px dotted #AAA;
+}
+li.xfolkentry div {
+ padding: 0.1em;
+}
+li.xfolkentry.deleted {
+ opacity: 0.5;
+}
+li.xfolkentry.private {
+ border-left: 3px solid #F00;
+}
+li.xfolkentry.shared {
+ border-left: 3px solid #FA0;
+}
+li.below-threshold > div {
+ border-right: 5px dotted #F00;
+}
+
+li.xfolkentry div div.description span.anchorBookmark {
+ /*font-family:monospace;*/
+}
+
+/* bookmark voting */
+.vote-badge {
+ float: left;
+ width: 3em;
+ margin: 5px;
+ border: 1px solid #CCCC88;
+ font-weight: bold;
+ /*border-radius: 10px;*/
+ -moz-border-radius: 10px;
+}
+.vote-badge span, .vote-badge a {
+ display: block;
+ text-align: center;
+}
+.vote-badge a:hover {
+ text-decoration: none;
+}
+.vote-badge .vote-for {
+ /*border-top-right-radius: 10px;*/
+ /*border-top-left-radius: 10px;*/
+ -moz-border-radius-topright: 10px;
+ -moz-border-radius-topleft: 10px;
+}
+.vote-badge .vote-against {
+ /*border-bottom-right-radius: 10px;*/
+ /*border-bottom-left-radius: 10px;*/
+ -moz-border-radius-bottomright: 10px;
+ -moz-border-radius-bottomleft: 10px;
+}
+.vote-badge a.vote-for:hover {
+ background-color: #ccffbb;
+}
+.vote-badge a.vote-against:hover {
+ background-color: #ffcccc;
+}
+.vote-badge-for .vote-for-inactive {
+ color: transparent;
+ background-color: #ccffbb;
+}
+.vote-badge-against .vote-against-inactive {
+ color: transparent;
+ background-color: #ffcccc;
+}
+
+/* bookmark voting horizontal */
+div.vote-horiz .voting {
+ font-weight: bold;
+}
+li.xfolkentry div.vote-horiz-for {
+/* border-left: 1em solid #ccffbb;*/
+/* padding-left: 0.5em;*/
+}
+li.xfolkentry div.vote-horiz-against {
+/* border-left: 1em solid #ffcccc;*/
+/* padding-left: 0.5em;*/
+}
+.vote-horiz a, .vote-horiz .vote-for-inactive,
+.vote-horiz .vote-against-inactive {
+ padding-left: 1.5em;
+ margin-left: 0.7em;
+}
+.vote-horiz a.vote-for:hover {
+ background-color: #ccffbb;
+}
+.vote-horiz a.vote-against:hover {
+ background-color: #ffcccc;
+}
+.vote-horiz .vote-for-inactive, .vote-horiz .vote-against-inactive {
+ color: #AAA;
+}
+.vote-horiz .vote-for {
+ background: url(images/vote-for.png) no-repeat;
+}
+.vote-horiz .vote-against {
+ background: url(images/vote-against.png) no-repeat;
+}
+.vote-horiz .vote-for-inactive {
+ background: url(images/vote-for-voted.png) no-repeat;
+}
+.vote-horiz .vote-against-inactive {
+ background: url(images/vote-against-voted.png) no-repeat;
+}
+
+
+/* SIDEBAR */
+
+div#sidebar {
+ font-size: small;
+ position: absolute;
+ right: 1em;
+ top: 10em;
+ width: 25%;
+}
+div#sidebar a {
+ color: #995;
+}
+div#sidebar a:hover {
+ color: #773;
+}
+div#sidebar>div {
+ background: #FFF url('images/bg_sidebar.png') bottom repeat-x;
+ border: 1px solid #CC8;
+ color: #555;
+ margin-bottom: 1em;
+}
+div#sidebar h2 {
+ background: transparent;
+ border: 0;
+ color: #995;
+ letter-spacing: 0;
+ margin: 0;
+ padding: 0.5em 0;
+}
+div#sidebar hr {
+ display: none;
+}
+div#sidebar p {
+ margin: 1em;
+}
+div#sidebar p.tags a {
+ margin: 0;
+}
+div#sidebar table {
+ margin: 0.5em 0.5em 0 0.5em;
+}
+div#sidebar table td {
+ padding-bottom: 0.25em;
+ padding-right: 0.5em;
+}
+/*
+div#sidebar ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0.5em;
+}
+div#sidebar ul li {
+ margin: 0.5em 0;
+}*/
+
+div#related {
+ padding: 0.5em;
+}
+
+.crossTags {
+ padding:0px 3px;
+ border: 1px solid #999955
+}
+
+div#sidebar>div#maintagsmenu {
+ padding: 0.5em;
+ border: 5px solid #999955
+}
+
+
+
+/* TAGS */
+
+p.commondescription {
+ padding-left:5px;
+ color: #555;
+ font-weight: bold;
+ font-size: medium;
+ border-left: 1px solid #CC8;
+ width: 70%;
+}
+
+p.tags {
+ line-height: 2.25em;
+ /*margin: 2em 10%;*/
+ text-align: justify;
+ vertical-align: middle;
+ width: 70%;
+}
+p.tags a,
+p.tags span {
+ color: #47A;
+ margin-right: 0.5em;
+}
+p.tags span:hover {
+ cursor: pointer;
+ text-decoration: underline;
+}
+p.tags span.selected {
+ background: #CEC;
+}
+
+.anchor {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+.treeTag {
+ color: #999955;
+}
+.treeTag:hover {
+ cursor: pointer;
+ text-decoration: underline;
+}
+.treeTag.selected {
+ background: #CEC;
+}
+
+/* PROFILE */
+
+table.profile th {
+ width: 10em;
+}
+
+table tr.ssl-current td {
+ background-color: #AFA;
+}
+
+/* OTHER GUFF */
+
+dd {
+ background: #CEC;
+ border-right: 4px solid #ACA;
+ color: #464;
+ padding: 6px;
+}
+dd a {
+ color: #464;
+}
+dd a:hover {
+ color: #000 !important;
+ text-decoration: underline !important;
+}
+
+dl {
+ font-size: small;
+ margin: 1em;
+ width: 70%;
+}
+dl#profile dd {
+ background: #CDE;
+ border-color: #ABC;
+ color: #247;
+}
+dl#profile dt {
+ background: #BCE;
+ border-color: #9AC;
+ color: #245;
+ display: block;
+ font-weight: bold;
+ padding: 6px;
+}
+dl#profile a {
+ color: #446;
+}
+dl#profile a:hover {
+ color: #000 !important;
+ text-decoration: underline !important;
+}
+dl#meta dd {
+ line-height: 1.5em;
+}
+dl#meta dt {
+ background: #BDB;
+ color: #353;
+ display: block;
+ font-weight: bold;
+ padding: 6px;
+}
+dt {
+ border-right: 4px solid #9B9;
+}
+dt a {
+ background: #BDB;
+ color: #353;
+ display: block;
+ font-weight: bold;
+ padding: 6px;
+}
+dt a:hover {
+ background: #ACA;
+ border: 0;
+}
+form {
+ margin: 0;
+}
+form#search {
+ margin-right: 0.75em;
+ color: #CCC;
+ font-size: small;
+ float: right;
+ margin: 0;
+ position: absolute;
+ right: 0;
+ top: 6.4em;
+}
+form label,
+form td,
+form th {
+ font-size: small;
+}
+form table {
+ margin: 0 1em;
+}
+h3 {
+ background: #DDD;
+ color: #555;
+ font-size: small;
+ letter-spacing: 0.2em;
+ margin: 2em 1em 1em 1em;
+ padding: 0.25em 0.75em;
+}
+li {
+ font-size: small;
+ margin-bottom: 0.5em;
+}
+p {
+ font-size: small;
+ margin: 1em;
+}
+p#sort {
+ /*color: #CCC;*/
+ font-size: small;
+ /*float: right;*/
+ /*margin: 0;*/
+ /*position: absolute;*/
+ right: 0;
+ top: 7em;
+}
+html > body p#sort {
+ /*margin-right: 0.75em;*/
+}
+p#sort a {
+ background: #AAA;
+ color: #555;
+ font-weight: normal;
+ margin-right: 0.5em;
+ padding: 0 1em;
+}
+html > body p#sort a {
+ margin-right: 0;
+}
+p#sort a:hover {
+ background: #CCC;
+ text-decoration: none !important;
+}
+p#sort span {
+ display: none;
+}
+
+p.backToTop{
+ text-align:center;
+}
+
+p.paging {
+ font-size: small;
+ margin-left: 1em;
+ text-align:center;
+}
+p.paging a,
+p.paging span.disable {
+ background: #888;
+ color: #FFF;
+ display: inline;
+ margin-right: 0.5em;
+ padding: 0.25em 1em;
+}
+p.paging a:hover {
+ background: #666;
+}
+p.paging span {
+ display: none;
+}
+p.paging span.disable {
+ background: #DDD;
+ color: #AAA;
+}
+div.collapsible p.tags {
+ line-height: 2.25em;
+ margin: 1em 2em;
+}
+th label {
+ padding-right: 1em;
+}
+ul {
+ margin-right: 1em;
+ width: 75%;
+}
+
+.adminBackground {
+ /*background: #FFFFFF url(images/logo_24.png) no-repeat scroll center right;*/
+ /*border-top: 1px solid;
+ border-color: #CC9900;*/
+}
+
+.adminBackground {
+ background: #FFF url('images/bg_admin.png') repeat-y top right;
+}
+
+a.bookmarklet {
+ padding: 2px;
+ background-color: #AAFAEE;
+}
+
+/* tree styles */
+#related-content.jstree-default.jstree-focused {
+ background: none !important;
+}
+
+
+/* add/edit bookmark */
+.ui-autocomplete {
+ width: 458px;
+}
diff --git a/www/themes/mono/icon.png b/www/themes/mono/icon.png
new file mode 100644
index 0000000..713f1eb
--- /dev/null
+++ b/www/themes/mono/icon.png
Binary files differ
diff --git a/www/themes/mono/images/b_edit.png b/www/themes/mono/images/b_edit.png
new file mode 100644
index 0000000..05711a0
--- /dev/null
+++ b/www/themes/mono/images/b_edit.png
Binary files differ
diff --git a/www/themes/mono/images/bg_admin.png b/www/themes/mono/images/bg_admin.png
new file mode 100644
index 0000000..c707779
--- /dev/null
+++ b/www/themes/mono/images/bg_admin.png
Binary files differ
diff --git a/www/themes/mono/images/bg_bar.png b/www/themes/mono/images/bg_bar.png
new file mode 100644
index 0000000..7152ba1
--- /dev/null
+++ b/www/themes/mono/images/bg_bar.png
Binary files differ
diff --git a/www/themes/mono/images/bg_header.png b/www/themes/mono/images/bg_header.png
new file mode 100644
index 0000000..6fa4161
--- /dev/null
+++ b/www/themes/mono/images/bg_header.png
Binary files differ
diff --git a/www/themes/mono/images/bg_sidebar.png b/www/themes/mono/images/bg_sidebar.png
new file mode 100644
index 0000000..0edb153
--- /dev/null
+++ b/www/themes/mono/images/bg_sidebar.png
Binary files differ
diff --git a/www/themes/mono/images/loading.gif b/www/themes/mono/images/loading.gif
new file mode 100644
index 0000000..859126e
--- /dev/null
+++ b/www/themes/mono/images/loading.gif
Binary files differ
diff --git a/www/themes/mono/images/logo-empty.png b/www/themes/mono/images/logo-empty.png
new file mode 100644
index 0000000..4590098
--- /dev/null
+++ b/www/themes/mono/images/logo-empty.png
Binary files differ
diff --git a/www/themes/mono/images/logo.png b/www/themes/mono/images/logo.png
new file mode 100644
index 0000000..4afe565
--- /dev/null
+++ b/www/themes/mono/images/logo.png
Binary files differ
diff --git a/www/themes/mono/images/logo_24.gif b/www/themes/mono/images/logo_24.gif
new file mode 100644
index 0000000..d5af64d
--- /dev/null
+++ b/www/themes/mono/images/logo_24.gif
Binary files differ
diff --git a/www/themes/mono/images/rss.gif b/www/themes/mono/images/rss.gif
new file mode 100644
index 0000000..094ba10
--- /dev/null
+++ b/www/themes/mono/images/rss.gif
Binary files differ
diff --git a/www/themes/mono/images/thumbs_up.orig.png b/www/themes/mono/images/thumbs_up.orig.png
new file mode 100644
index 0000000..1a89d0e
--- /dev/null
+++ b/www/themes/mono/images/thumbs_up.orig.png
Binary files differ
diff --git a/www/themes/mono/images/vote-against-voted.png b/www/themes/mono/images/vote-against-voted.png
new file mode 100644
index 0000000..740dfc8
--- /dev/null
+++ b/www/themes/mono/images/vote-against-voted.png
Binary files differ
diff --git a/www/themes/mono/images/vote-against.png b/www/themes/mono/images/vote-against.png
new file mode 100644
index 0000000..f15ea4d
--- /dev/null
+++ b/www/themes/mono/images/vote-against.png
Binary files differ
diff --git a/www/themes/mono/images/vote-for-voted.png b/www/themes/mono/images/vote-for-voted.png
new file mode 100644
index 0000000..5abd43e
--- /dev/null
+++ b/www/themes/mono/images/vote-for-voted.png
Binary files differ
diff --git a/www/themes/mono/images/vote-for.png b/www/themes/mono/images/vote-for.png
new file mode 100644
index 0000000..5973ae9
--- /dev/null
+++ b/www/themes/mono/images/vote-for.png
Binary files differ
diff --git a/www/themes/mono/scuttle.css b/www/themes/mono/scuttle.css
new file mode 100644
index 0000000..f90c4cd
--- /dev/null
+++ b/www/themes/mono/scuttle.css
@@ -0,0 +1,703 @@
+/* Watch out. This file bugs (in the header) when it is formated with Eclipse (Ctrl+Shift+F)*/
+
+/* BASE */
+
+* {
+ font-family: fixed, monospace;
+}
+a {
+ color: #005fff;
+ /*color: #5b5b5b;*/
+ text-decoration: none;
+}
+a:hover {
+ color: #258;
+ text-decoration: underline;
+}
+a img {
+ border: 0;
+}
+
+
+body {
+ margin: 0;
+ padding: 0;
+}
+input[type=text],
+input[type=password],
+select,
+textarea {
+ border: 1px solid #AAA;
+ padding: 0.1em;
+}
+input[type=text],
+input[type=password],
+textarea {
+ padding: 0.2em;
+}
+input[type=text]:focus,
+input[type=password]:focus,
+select:focus,
+textarea:focus {
+ border-color: #666;
+}
+p.tipMsg,
+p.error,
+p.success {
+ border: 1px solid;
+ font-size: small;
+ margin: 0.5em;
+ padding: 0.5em;
+ width: 70%;
+}
+p.tipMsg {
+ background: #FFFF99;
+ border-color: #CC9900;
+ color: #CC9900;
+}
+p.error {
+ background: #FCC;
+ border-color: #966;
+ color: #633;
+}
+p.success {
+ background: #CFC;
+ border-color: #696;
+ color: #363;
+}
+td#availability {
+ color: #285;
+ font-weight: bold;
+}
+td#availability.not-available {
+ color: #F00;
+}
+textarea {
+ font-size: small;
+ padding: 0.2em;
+}
+th {
+ padding-right: 1em;
+ text-align: right;
+}
+
+/* HEADER */
+
+/*
+div#header {
+ background: #FFF url('images/bg_header.png') bottom repeat-x;
+ border-bottom: 3px solid #9CD;
+ clear: both;
+}
+*/
+div#header:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+* html div#header {
+ height: 1%;
+}
+h1 {
+ float: left;
+ font-size: x-large;
+ font-weight: bold;
+ letter-spacing: 0.25em;
+ margin: 0;
+ padding: 1em;
+}
+
+/*
+html > body h1 {
+ background: url('images/logo.png') no-repeat 10px;
+ padding-left: 75px;
+}
+*/
+
+html > body div#header.popup h1 {
+ background: url('images/logo.png') no-repeat 10px;
+ background-size: 24px;
+ padding: 0.5em 0.5em 0.5em 50px;
+}
+/*html > body div#header #welcome {
+ position:absolute;
+ left:75px;
+}*/
+#welcome {
+ border: 10px solid #5b5b5b;
+ width: 66%;
+ padding: 10px;
+ text-align:center;
+ font-size: medium;
+}
+#bottom {
+ padding-top: 10px;
+ text-align: center;
+ font-size: xx-small;
+}
+h1 a {
+ color: #000;
+}
+h1 a:hover {
+ color: #000;
+}
+h2 {
+ /*background: #000 url('images/bg_bar.png') center center repeat-x;*/
+ background: #5b5b5b;
+ border-bottom: 3px solid #5b5b5b;
+ clear: both;
+ color: #FFF;
+ font-size: medium;
+ letter-spacing: 0.1em;
+ margin: 0 0 1em 0;
+ padding: 0.5em 1em;
+}
+h2 a, h2 a:hover {
+ color: #DDD;
+}
+
+/*sup {
+ color: #77ABE0;
+ font-weight: bold;
+}*/
+
+/* NAVIGATION */
+
+ul#navigation {
+ list-style-type: none;
+ margin: 0;
+ padding: 1.75em 1em;
+ width: auto;
+}
+ul#navigation a {
+ font-size: medium;
+ font-weight: bold;
+ padding: 0.2em 0.5em;
+}
+ul#navigation a:hover {
+ background: #7AD;
+ color: #FFF;
+}
+ul#navigation li {
+ float: left;
+}
+ul#navigation li.access {
+ float: right;
+}
+
+/* BOOKMARKS */
+
+ol#bookmarks {
+ list-style-type: none;
+ margin: 0;
+ padding: 0 1em;
+ width: 70%;
+}
+html > body ol#bookmarks {
+ margin: 0 1em;
+ padding: 0;
+}
+img.thumbnail {
+ float: left;
+ padding: 1px;
+ margin-right: 6px;
+ margin-bottom:4px;
+ border:1px solid #AAA;
+}
+div.link a {
+ /*color: #5b5b5b;*/
+ color: #005fff;
+ text-decoration: none;
+ font-size: medium;
+}
+div.link a:hover {
+ text-decoration: underline;
+}
+div.link a:visited {
+ color: purple;
+}
+div.address {
+ color: #5b5b5b;
+}
+div.meta {
+ color: #5b5b5b;
+}
+div.meta span {
+ color: #F00;
+}
+div.privateNote {
+ font-variant: small-caps;
+ font-size: small;
+ text-align: right;
+}
+li.xfolkentry {
+ border-bottom: 1px solid #DDD;
+ margin-bottom: 0;
+ padding: 1em 0.5em;
+ overflow: auto;
+}
+html > body li.xfolkentry {
+ border-bottom: 1px dotted #AAA;
+}
+li.xfolkentry div {
+ padding: 0.1em;
+}
+li.xfolkentry.deleted {
+ opacity: 0.5;
+}
+li.xfolkentry.private {
+ border-left: 3px solid #F00;
+}
+li.xfolkentry.shared {
+ border-left: 3px solid #FA0;
+}
+li.below-threshold > div {
+ border-right: 5px dotted #F00;
+}
+
+li.xfolkentry div div.description span.anchorBookmark {
+ /*font-family:monospace;*/
+}
+
+/* bookmark voting */
+.vote-badge {
+ float: left;
+ width: 3em;
+ margin: 5px;
+ border: 1px solid #CCCC88;
+ font-weight: bold;
+ /*border-radius: 10px;*/
+ -moz-border-radius: 10px;
+}
+.vote-badge span, .vote-badge a {
+ display: block;
+ text-align: center;
+}
+.vote-badge a:hover {
+ text-decoration: none;
+}
+.vote-badge .vote-for {
+ /*border-top-right-radius: 10px;*/
+ /*border-top-left-radius: 10px;*/
+ -moz-border-radius-topright: 10px;
+ -moz-border-radius-topleft: 10px;
+}
+.vote-badge .vote-against {
+ /*border-bottom-right-radius: 10px;*/
+ /*border-bottom-left-radius: 10px;*/
+ -moz-border-radius-bottomright: 10px;
+ -moz-border-radius-bottomleft: 10px;
+}
+.vote-badge a.vote-for:hover {
+ background-color: #ccffbb;
+}
+.vote-badge a.vote-against:hover {
+ background-color: #ffcccc;
+}
+.vote-badge-for .vote-for-inactive {
+ color: transparent;
+ background-color: #ccffbb;
+}
+.vote-badge-against .vote-against-inactive {
+ color: transparent;
+ background-color: #ffcccc;
+}
+
+/* bookmark voting horizontal */
+div.vote-horiz .voting {
+ font-weight: bold;
+}
+li.xfolkentry div.vote-horiz-for {
+/* border-left: 1em solid #ccffbb;*/
+/* padding-left: 0.5em;*/
+}
+li.xfolkentry div.vote-horiz-against {
+/* border-left: 1em solid #ffcccc;*/
+/* padding-left: 0.5em;*/
+}
+.vote-horiz a, .vote-horiz .vote-for-inactive,
+.vote-horiz .vote-against-inactive {
+ padding-left: 1.5em;
+ margin-left: 0.7em;
+}
+.vote-horiz a.vote-for:hover {
+ background-color: #ccffbb;
+}
+.vote-horiz a.vote-against:hover {
+ background-color: #ffcccc;
+}
+.vote-horiz .vote-for-inactive, .vote-horiz .vote-against-inactive {
+ color: #AAA;
+}
+.vote-horiz .vote-for {
+ background: url(images/vote-for.png) no-repeat;
+}
+.vote-horiz .vote-against {
+ background: url(images/vote-against.png) no-repeat;
+}
+.vote-horiz .vote-for-inactive {
+ background: url(images/vote-for-voted.png) no-repeat;
+}
+.vote-horiz .vote-against-inactive {
+ background: url(images/vote-against-voted.png) no-repeat;
+}
+
+
+/* SIDEBAR */
+
+div#sidebar {
+ font-size: small;
+ position: absolute;
+ right: 1em;
+ top: 10em;
+ width: 25%;
+}
+/*
+div#sidebar a {
+ color: #995;
+}
+*/
+div#sidebar a:hover {
+ color: #000;
+}
+div#sidebar>div {
+ /*background: #FFF url('images/bg_sidebar.png') bottom repeat-x;*/
+ border: 1px solid #000;
+ color: #555;
+ margin-bottom: 1em;
+}
+div#sidebar h2 {
+ background: transparent;
+ border: 0;
+ color: #000000;
+ letter-spacing: 0;
+ margin: 0;
+ padding: 0.5em 0;
+}
+div#sidebar hr {
+ display: none;
+}
+div#sidebar p {
+ margin: 1em;
+}
+div#sidebar p.tags a {
+ margin: 0;
+}
+div#sidebar table {
+ margin: 0.5em 0.5em 0 0.5em;
+}
+div#sidebar table td {
+ padding-bottom: 0.25em;
+ padding-right: 0.5em;
+}
+/*
+div#sidebar ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0.5em;
+}
+div#sidebar ul li {
+ margin: 0.5em 0;
+}*/
+
+div#related {
+ padding: 0.5em;
+}
+
+.crossTags {
+ padding:0px 3px;
+ border: 1px solid #000000;
+}
+
+div#sidebar>div#maintagsmenu {
+ padding: 0.5em;
+ border: 5px solid #999955
+}
+
+
+
+/* TAGS */
+
+p.commondescription {
+ padding-left:5px;
+ color: #555;
+ font-weight: bold;
+ font-size: medium;
+ border-left: 1px solid #CC8;
+ width: 70%;
+}
+
+p.tags {
+ line-height: 2.25em;
+ /*margin: 2em 10%;*/
+ text-align: justify;
+ vertical-align: middle;
+ width: 70%;
+}
+p.tags a,
+p.tags span {
+ color: #47A;
+ margin-right: 0.5em;
+}
+p.tags span:hover {
+ cursor: pointer;
+ text-decoration: underline;
+}
+p.tags span.selected {
+ background: #CEC;
+}
+
+.anchor {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+.treeTag {
+ color: #999955;
+}
+.treeTag:hover {
+ cursor: pointer;
+ text-decoration: underline;
+}
+.treeTag.selected {
+ background: #CEC;
+}
+
+/* PROFILE */
+
+table.profile th {
+ width: 10em;
+}
+
+table tr.ssl-current td {
+ background-color: #AFA;
+}
+
+/* OTHER GUFF */
+
+dd {
+ background: #CEC;
+ border-right: 4px solid #ACA;
+ color: #464;
+ padding: 6px;
+}
+dd a {
+ color: #464;
+}
+dd a:hover {
+ color: #000 !important;
+ text-decoration: underline !important;
+}
+
+dl {
+ font-size: small;
+ margin: 1em;
+ width: 70%;
+}
+dl#profile dd {
+ background: #CDE;
+ border-color: #ABC;
+ color: #247;
+}
+dl#profile dt {
+ background: #BCE;
+ border-color: #9AC;
+ color: #245;
+ display: block;
+ font-weight: bold;
+ padding: 6px;
+}
+dl#profile a {
+ color: #446;
+}
+dl#profile a:hover {
+ color: #000 !important;
+ text-decoration: underline !important;
+}
+dl#meta dd {
+ line-height: 1.5em;
+}
+dl#meta dt {
+ background: #BDB;
+ color: #353;
+ display: block;
+ font-weight: bold;
+ padding: 6px;
+}
+dt {
+ border-right: 4px solid #9B9;
+}
+dt a {
+ background: #BDB;
+ color: #353;
+ display: block;
+ font-weight: bold;
+ padding: 6px;
+}
+dt a:hover {
+ background: #ACA;
+ border: 0;
+}
+form {
+ margin: 0;
+}
+form#search {
+ margin-right: 0.75em;
+ color: #CCC;
+ font-size: small;
+ float: right;
+ margin: 0;
+ position: absolute;
+ right: 0;
+ top: 6.4em;
+}
+form label,
+form td,
+form th {
+ font-size: small;
+}
+form table {
+ margin: 0 1em;
+}
+h3 {
+ background: #DDD;
+ color: #555;
+ font-size: small;
+ letter-spacing: 0.2em;
+ margin: 2em 1em 1em 1em;
+ padding: 0.25em 0.75em;
+}
+li {
+ font-size: small;
+ margin-bottom: 0.5em;
+}
+p {
+ font-size: small;
+ margin: 1em;
+}
+p#sort {
+ /*color: #CCC;*/
+ font-size: small;
+ /*float: right;*/
+ /*margin: 0;*/
+ /*position: absolute;*/
+ right: 0;
+ top: 7em;
+}
+html > body p#sort {
+ /*margin-right: 0.75em;*/
+}
+p#sort a {
+ background: #AAA;
+ color: #555;
+ font-weight: normal;
+ margin-right: 0.5em;
+ padding: 0 1em;
+}
+html > body p#sort a {
+ margin-right: 0;
+}
+p#sort a:hover {
+ background: #CCC;
+ text-decoration: none !important;
+}
+p#sort span {
+ display: none;
+}
+
+p.backToTop{
+ text-align:center;
+}
+
+p.paging {
+ font-size: small;
+ margin-left: 1em;
+ text-align:center;
+}
+p.paging a,
+p.paging span.disable {
+ background: #888;
+ color: #FFF;
+ display: inline;
+ margin-right: 0.5em;
+ padding: 0.25em 1em;
+}
+p.paging a:hover {
+ background: #666;
+}
+p.paging span {
+ display: none;
+}
+p.paging span.disable {
+ background: #DDD;
+ color: #AAA;
+}
+div.collapsible p.tags {
+ line-height: 2.25em;
+ margin: 1em 2em;
+}
+th label {
+ padding-right: 1em;
+}
+ul {
+ margin-right: 1em;
+ width: 75%;
+}
+
+.adminBackground {
+ /*background: #FFFFFF url(images/logo_24.png) no-repeat scroll center right;*/
+ /*border-top: 1px solid;
+ border-color: #CC9900;*/
+}
+
+.adminBackground {
+ background: #FFF url('images/bg_admin.png') repeat-y top right;
+}
+
+a.bookmarklet {
+ padding: 2px;
+ background-color: #AAFAEE;
+}
+
+/* tree styles */
+#related-content.jstree-default.jstree-focused {
+ background: none !important;
+}
+
+
+/* add/edit bookmark */
+.ui-autocomplete {
+ width: 458px;
+}
+
+/* Mobile First behavior */
+
+#search, #sidebar {
+ visibility: hidden;
+ display: none;
+}
+
+ol#bookmarks {
+ width: 95%;
+}
+
+#welcome {
+ width: 80%;
+}
+
+@media only screen and (min-width : 800px) {
+ #search, #sidebar {
+ visibility: inherit;
+ display: inherit;
+ }
+
+ ol#bookmarks {
+ width: 70%;
+ }
+
+ #welcome {
+ width: 66%;
+ }
+}
diff --git a/www/themes/testdummy/images/logo.png b/www/themes/testdummy/images/logo.png
new file mode 100644
index 0000000..4a8f8ba
--- /dev/null
+++ b/www/themes/testdummy/images/logo.png
Binary files differ
diff --git a/www/themes/testdummy/scuttle.css b/www/themes/testdummy/scuttle.css
new file mode 100644
index 0000000..c62cd19
--- /dev/null
+++ b/www/themes/testdummy/scuttle.css
@@ -0,0 +1,12 @@
+@import url(../default/scuttle.css);
+
+body {
+ background-color: #FEA;
+}
+
+html > body h1 {
+ background: url('images/logo.png') no-repeat 10px;
+}
+div#header {
+ background: #FEA;
+}
diff --git a/www/upgrade.php b/www/upgrade.php
new file mode 100644
index 0000000..e9d9643
--- /dev/null
+++ b/www/upgrade.php
@@ -0,0 +1,61 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 - 2007 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+
+
+/////////////////
+// WARNING!
+// Comment the two lines of code below to make work the script.
+// You have to put // at the beginning of lines to comment them.
+// BEFORE upgrading, don't forget to make a BACKUP of your database.
+// AFTER upgrading, don't forget to UN-COMMENT the lines back.
+/////////////////
+
+echo "Please edit the 'upgrade.php' file into SemanticScuttle/ to start upgrading";
+exit();
+
+/////////////////
+// This part below will be executed once you comment the two lines above
+/////////////////
+require_once 'www-header.php';
+$tagstatservice = SemanticScuttle_Service_Factory :: get('TagStat');
+?>
+
+<h1>Upgrade</h1>
+<h2>From Scuttle 0.7.2 to SemanticScuttle 0.87</h2>
+<ul>
+ <li>1/ Make a <b>backup</b> of your database</li>
+ <li>2/ Create the missing tables
+ <ul>
+ <li>Open "tables.sql" (into your SemanticScuttle directory) with a text editor</li>
+ <li>Copy to your database the last three tables : sc_tags2tags, sc_tagsstats, sc_commondescription</li>
+ </ul>
+ </li>
+ <li>3/ Complete the upgrade by clicking on the following link : <a href="upgrade.php?action=upgrade">upgrade</a></li>
+</ul>
+<?php
+if($_GET['action']=="upgrade") {
+ // Update the stats
+ $tagstatservice->updateAllStat();
+ echo "Upgrade script executed: OK!<br/><br/>";
+ echo "For security reason, don't forget to uncomment back the first lines into \"upgrade.php\"<br/><br/>";
+ echo 'In case of problem during upgrade, please use our <a href="http://sourceforge.net/tracker/?group_id=211356&atid=1017431">sourceforge page</a> to inform us. Thank you.';
+}
+?>
diff --git a/www/users.php b/www/users.php
new file mode 100644
index 0000000..9d6c655
--- /dev/null
+++ b/www/users.php
@@ -0,0 +1,62 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+$pagetitle = T_('Users');
+
+if ($usecache) {
+ // Generate hash for caching on
+ if ($userservice->isLoggedOn()) {
+ $hash = md5($_SERVER['REQUEST_URI'] . $currentUser->getId());
+ } else {
+ $hash = md5($_SERVER['REQUEST_URI']);
+ }
+
+ // Cache for 30 minutes
+ $cacheservice->Start($hash, 1800);
+}
+
+// Header variables
+$tplVars['pagetitle'] = $pagetitle;
+$tplVars['loadjs'] = true;
+
+$tplVars['sidebar_blocks'] = array('recent', 'popular');
+$tplVars['subtitle'] = filter($pagetitle);
+
+$tplVars['users'] = $userservice->getUsers();
+//$tplVars['cat_url'] = createURL('tags', '%2$s');
+//$tplVars['nav_url'] = createURL('tags', '%2$s%3$s');
+
+$templateservice->loadTemplate('users.tpl', $tplVars);
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/vote.php b/www/vote.php
new file mode 100644
index 0000000..0f33d75
--- /dev/null
+++ b/www/vote.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * We do expect three parameters:
+ * - type (for/against)
+ * - bookmark id
+ * - session needs to contain the URL last visited
+ *
+ * vote/for/123
+ */
+require_once 'www-header.php';
+
+if (!$GLOBALS['enableVoting']) {
+ header('HTTP/1.0 501 Not implemented');
+ echo 'voting is disabled';
+ exit(1);
+}
+
+
+$us = SemanticScuttle_Service_Factory::get('User');
+$vs = SemanticScuttle_Service_Factory::get('Vote');
+
+if (!$us->isLoggedOn()) {
+ header('HTTP/1.0 400 Bad Request');
+ echo 'You need to be logged on to vote.';
+ exit(1);
+}
+$user = $us->getCurrentUser();
+$user = $user['uId'];
+
+if (!isset($_SERVER['PATH_INFO'])) {
+ //we got a problem
+ header('HTTP/1.0 500 Internal Server Error');
+ echo 'PATH_INFO not found';
+ exit(2);
+}
+
+//we should really use net_url_mapper here
+list($url, $type, $bookmark) = explode('/', $_SERVER['PATH_INFO']);
+
+if ($type != 'for' && $type != 'against') {
+ header('HTTP/1.0 400 Bad Request');
+ echo 'type has to be "for" or "against"';
+ exit(3);
+}
+if (!is_numeric($bookmark)) {
+ header('HTTP/1.0 400 Bad Request');
+ echo 'Bookmark must be numeric';
+ exit(4);
+}
+$bookmark = (int)$bookmark;
+
+if (!isset($GLOBALS['lastUrl']) || $GLOBALS['lastUrl'] == '') {
+ header('HTTP/1.0 412 Precondition failed');
+ echo 'Missing last URL in session';
+ exit(5);
+}
+$from = $GLOBALS['lastUrl'];
+
+
+$vs->vote($bookmark, $user, $type == 'for' ? 1 : -1);
+
+if (isset($GLOBALS['ajaxRequest']) && $GLOBALS['ajaxRequest']) {
+ //we are in ajax mode and return the badge in ajaxVote.php
+ return;
+}
+
+header('Location: ' . $from);
+?> \ No newline at end of file
diff --git a/www/watch.php b/www/watch.php
new file mode 100644
index 0000000..07d4127
--- /dev/null
+++ b/www/watch.php
@@ -0,0 +1,61 @@
+<?php
+/***************************************************************************
+ Copyright (C) 2004 - 2006 Scuttle project
+ http://sourceforge.net/projects/scuttle/
+ http://scuttle.org/
+
+ 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
+ ***************************************************************************/
+
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+//No specific services
+
+isset($_POST['contact']) ? define('POST_CONTACT', $_POST['contact']): define('POST_CONTACT', '');
+isset($_GET['contact']) ? define('GET_CONTACT', $_GET['contact']): define('GET_CONTACT', '');
+
+/* Managing path info */
+@list($url, $user) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+if($user=='' && POST_CONTACT != '') {
+ $user = POST_CONTACT;
+} elseif($user=='' && GET_CONTACT != '') {
+ $user = GET_CONTACT;
+}
+
+if ($userservice->isLoggedOn() && $user) {
+ $pagetitle = '';
+
+ $userid = $userservice->getIdFromUser($user);
+
+ if($userid == NULL) {
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+ }
+
+ $watched = $userservice->getWatchStatus($userid, $currentUser->getId());
+ $changed = $userservice->setWatchStatus($userid);
+
+ if ($watched) {
+ $tplVars['msg'] = T_('User removed from your watchlist');
+ } else {
+ $tplVars['msg'] = T_('User added to your watchlist');
+ }
+
+ header('Location: '. createURL('watchlist', $currentUser->getUsername()));
+}
+?>
diff --git a/www/watchlist.php b/www/watchlist.php
new file mode 100644
index 0000000..667cbe9
--- /dev/null
+++ b/www/watchlist.php
@@ -0,0 +1,128 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+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
+***************************************************************************/
+require_once 'www-header.php';
+
+/* Service creation: only useful services are created */
+$bookmarkservice =SemanticScuttle_Service_Factory::get('Bookmark');
+$cacheservice =SemanticScuttle_Service_Factory::get('Cache');
+
+/* Managing all possible inputs */
+isset($_GET['page']) ? define('GET_PAGE', $_GET['page']): define('GET_PAGE', 0);
+isset($_GET['sort']) ? define('GET_SORT', $_GET['sort']): define('GET_SORT', '');
+
+/* Managing current logged user */
+$currentUser = $userservice->getCurrentObjectUser();
+
+/* Managing path info */
+@list($url, $user, $page) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+
+if ($usecache) {
+ // Generate hash for caching on
+ if ($userservice->isLoggedOn()) {
+ if ($currentUser->getUsername() != $user) {
+ $cachehash = md5($_SERVER['REQUEST_URI'] . $currentUser->getUsername());
+
+ // Cache for 5 minutes
+ $cacheservice->Start($cachehash);
+ }
+ } else {
+ // Cache for 30 minutes
+ $cachehash = md5($_SERVER['REQUEST_URI']);
+ $cacheservice->Start($cachehash, 1800);
+ }
+}
+
+if ($user) {
+ if (is_int($user)) {
+ $userid = intval($user);
+ } else {
+ $userinfo = $userservice->getObjectUserByUsername($user);
+ if ($userinfo == NULL ) {
+ // Throw a 404 error
+ $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+ } else {
+ $userid = $userinfo->getId();
+ }
+ }
+}
+
+// Header variables
+$tplVars['loadjs'] = true;
+
+if ($user) {
+ $tplVars['user'] = $user;
+ $tplVars['userid'] = $userid;
+ $tplVars['userinfo'] = $userinfo;
+
+ // Pagination
+ $perpage = getPerPageCount($currentUser);
+ if (intval(GET_PAGE) > 1) {
+ $page = intval(GET_PAGE);
+ $start = ($page - 1) * $perpage;
+ } else {
+ $page = 0;
+ $start = 0;
+ }
+
+ // Set template vars
+ $tplVars['currenttag'] = '';
+ $tplVars['page'] = $page;
+ $tplVars['start'] = $start;
+ $tplVars['bookmarkCount'] = $start + 1;
+
+ $bookmarks = $bookmarkservice->getBookmarks($start, $perpage, $userid, NULL, NULL, getSortOrder(), true);
+
+ $tplVars['sidebar_blocks'] = array('watchlist');
+ $tplVars['watched'] = true;
+ $tplVars['total'] = $bookmarks['total'];
+ $tplVars['bookmarks'] = $bookmarks['bookmarks'];
+ $tplVars['cat_url'] = createURL('tags', '%2$s');
+ $tplVars['nav_url'] = createURL('watchlist', '%s/%s%s');
+
+ if ($userservice->isLoggedOn() && $user == $currentUser->getUsername()) {
+ $title = T_('My Watchlist');
+ } else {
+ $title = T_('Watchlist') .': '. $user;
+ }
+ $tplVars['pagetitle'] = $title;
+ $tplVars['subtitle'] = $title;
+ $tplVars['range'] = 'watchlist';
+ $tplVars['pageName'] = PAGE_WATCHLIST;
+
+ $tplVars['rsschannels'] = array(
+ array(filter($sitename .': '. $title), createURL('rss', 'watchlist/'. filter($user, 'url')))
+ );
+
+ $templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+} else {
+ $tplVars['error'] = T_('Username was not specified');
+ $templateservice->loadTemplate('error.404.tpl', $tplVars);
+ exit();
+}
+
+if ($usecache) {
+ // Cache output if existing copy has expired
+ $cacheservice->End($hash);
+}
+?>
diff --git a/www/widgetUWA.php b/www/widgetUWA.php
new file mode 100644
index 0000000..25ec898
--- /dev/null
+++ b/www/widgetUWA.php
@@ -0,0 +1,234 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:widget="http://www.netvibes.com/ns/">
+<head>
+
+<title>SemanticScuttle</title>
+
+<meta name="author" content="Benjamin HKB (inspired by Florent Solt)" />
+<meta name="description" content="" />
+<meta name="keywords" content="semanticscuttle delicious del.icio.us" />
+
+<meta name="apiVersion" content="1.0" />
+<meta name="inline" content="true" />
+<meta name="autoRefresh" content="20" />
+<meta name="debugMode" content="false" />
+
+<link rel="icon" href="http://semanticscuttle.sourceforge.net/icon.png"/>
+
+<link rel="stylesheet" type="text/css" href="http://www.netvibes.com/themes/uwa/style.css" />
+<script type="text/javascript" src="http://www.netvibes.com/js/UWA/load.js.php?env=Standalone"></script>
+
+<widget:preferences>
+ <preference name="website" label="URL of the SemanticScuttle website" type="text" defaultValue="http://example.com" />
+ <preference name="websiteTitle" label="Website's title" type="text" defaultValue="Website Name" />
+ <preference name="account" label="User" type="text" defaultValue="" />
+ <preference name="tags" label="Tags (optionally separated by +)" type="text" defaultValue="tag1+tag2" />
+ <preference name="thumb" label="Show thumbnails" type="boolean" defaultValue="true" />
+ <preference name="limit" type="range" label="Number of items to display" defaultValue="20" step="1" min="1" max="50" />
+ <preference name="offset" type="hidden" defaultValue="0" />
+</widget:preferences>
+
+<style type="text/css">
+.container ul {
+ padding: 0px;
+}
+.container .clear {
+ clear: both;
+ height: 1px;
+ font-size: 1px;
+ line-height: 1px;
+}
+.container li {
+ background-image: none;
+ padding: 2px;
+ clear: both;
+ margin: 0px;
+}
+.container li a {
+ font-weight: bold;
+}
+.container li em {
+ display: block;
+}
+.container li img {
+ display: block;
+ float: left;
+ margin-right: 4px;
+ margin-bottom: 4px;
+}
+.container li img.thumb {
+ width: 60px;
+ height: 45px;
+ border: 1px solid #ccc;
+}
+.container li.cal {
+ padding-left: 20px;
+ background-image: url(http://www.netvibes.com/img/icons/time.png);
+ background-repeat: no-repeat;
+ background-position: left 1em;
+ line-height: 16px;
+ margin-bottom: 0.5em;
+ padding-top: 1em;
+ font-size: 1.3em;
+ border-bottom: 1px dotted #aaa;
+}
+
+</style>
+
+<script type="text/javascript">
+if (document.location && document.location.hostname == 'mymodules.local') {
+ UWA.proxies.ajax = 'ajaxProxy.php';
+}
+
+widget.months = {
+ 1:_('January'), '2':_('February'), 3:_('March'), 4:_('April'), 5:_('May'), 6:_('June'),
+ 7:_('July'), 8:_('August'), 9:_('September'), 10:_('October'), 11:_('November'), 12:_('December')
+}
+
+widget.onLoad = function() {
+ widget.onRefresh();
+}
+
+widget.onRefresh = function() {
+ widget.setBody( _("Loading ... (if loading is too long, check preferences: URL must be exact, user and tags must exist.)") );
+ var website = widget.getValue('website');
+ if(website.charAt(website.length-1) != '/') {
+ website = website +'/';
+ }
+
+ var websiteTitle = widget.getValue('websiteTitle');
+
+ var account = widget.getValue('account');
+ if(account == undefined || account == '') {
+ account = 'all';
+ }
+ var tags = widget.getValue('tags');
+ if(tags != undefined && tags != '') {
+ tags = '/' + tags;
+ var reg=new RegExp(" ", "g");
+ tags = tags.replace(reg, "%20");
+ }
+ if (website == undefined || website == 'http://example.com/') {
+ widget.setBody(_("Edit the widget please."));
+ } else {
+ //widget.setBody(website + 'rss/' + account + tags);
+ var title = '<a href="' + website + '">' + websiteTitle +'</a>';
+ title+= ' / <a href="http://sourceforge.net/projects/semanticscuttle/">'+ 'SemanticScuttle</a>';
+ widget.setTitle(title);
+ UWA.Data.getFeed(website + 'rss/' + account + tags, widget.onData);
+ }
+}
+
+widget.onData = function(data) {
+ widget.items = data.items;
+ //widget.log(data.items);
+ widget.display();
+}
+
+widget.display = function() {
+ // Main container
+ var container = widget.createElement('div');
+ container.className = 'container';
+
+ // Prev date
+ var prev_date = null;
+
+ // Items
+ var ul = widget.createElement('ul');
+ var limit = parseInt(widget.getValue('limit'));
+ var offset = parseInt(widget.getValue('offset'));
+ for (var i=offset; i<widget.items.length && i<limit+offset; i++) {
+
+ var date = new Date(widget.items[i].date)
+
+ // Cal
+ if (prev_date == null ||
+ date.getMonth() != prev_date.getMonth() ||
+ date.getDate() != prev_date.getDate() ||
+ date.getYear() != prev_date.getYear()) {
+ prev_date = date;
+ var cal = widget.createElement('li');
+ cal.className = 'cal';
+
+ var relative_to = new Date();
+ var delta = parseInt((relative_to.getTime() - date.getTime()) / 1000);
+ if (delta < 24*60*60) {
+ cal.appendText(_('Today'));
+ } else if (delta < 48*60*60) {
+ cal.appendText(_('Yesterday'));
+ } else {
+ cal.appendText(date.getDate() + ' ' + widget.months[date.getMonth() + 1])
+ }
+ ul.appendChild(cal);
+ }
+
+ // Li
+ var li = widget.createElement('li');
+ if (i%2 == 1) li.className = 'even';
+
+ if (widget.getValue('thumb') == 'true') {
+ // Img
+ var img = widget.createElement('img');
+ img.src = "http://open.thumbshots.org/image.pxf?url=" + widget.items[i].link.replace(/^(http:\/\/[^\/]+).*$/, '$1');
+ img.className = 'thumb';
+ li.appendChild(img);
+ } else {
+ var img = widget.createElement('img');
+ img.src = 'http://semanticscuttle.sourceforge.net/bookmark.gif'
+ li.appendChild(img);
+ }
+
+ // Title
+ var title = widget.createElement('a');
+ title.setContent(widget.items[i].title);
+ title.href = widget.items[i].link;
+ if(widget.items[i].content == '') {
+ widget.items[i].content = _("No description");
+ }
+ title.title= widget.items[i].content;
+ li.appendChild(title);
+
+ // // Tags
+ // console.log(widget.items[i]);
+ // var tagsText = widget.items[i].subject;
+ // if (tagsText != null) {
+ // tags = widget.createElement('em');
+ // tags.appendText(tagsText.nodeValue);
+ // li.appendChild(tags);
+ // }
+
+ // Clear
+ var clear = widget.createElement('div');
+ clear.className = 'clear';
+ li.appendChild(clear);
+
+ ul.appendChild(li);
+ }
+ container.appendChild(ul);
+
+ // Pager
+ var pager = widget.createElement('div');
+
+ var pager_ctrl = new UWA.Controls.Pager( {
+ module: this,
+ limit: widget.getValue('limit'),
+ offset: widget.getValue('offset'),
+ dataArray: widget.items
+ } );
+
+ pager_ctrl.onChange = function(newOffset) {
+ widget.setValue('offset', newOffset);
+ widget.display();
+ }
+ pager.setContent(pager_ctrl.getContent());
+ container.appendChild(pager);
+
+ widget.setBody(container);
+}
+
+</script>
+
+</head>
+<body>
+</body>
+</html>
diff --git a/www/www-header.php b/www/www-header.php
new file mode 100644
index 0000000..cc5a5ae
--- /dev/null
+++ b/www/www-header.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Base file that every file in www/ should include.
+ * Loads all other SemanticScuttle files.
+ *
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Eric Dane <ericdane@users.sourceforge.net>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+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