aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Kuzminski <marcin@python-works.com>2012-09-02 21:19:54 +0200
committerMarcin Kuzminski <marcin@python-works.com>2012-09-02 21:19:54 +0200
commit39b3e37caee03e86823694f1f8706e937e4c7c0c (patch)
treee81ddcf0759306951012cff7be39b587907d2d90
parent9db094e33c53fd77360b54ac99db2d76d403f710 (diff)
parentebf543dc52a96b70c16ff008c7142e38feb0b158 (diff)
Merge beta branch into stable
--HG-- rename : rhodecode/lib/backup_manager.py => rhodecode/bin/rhodecode_backup.py
-rw-r--r--.hgignore2
-rw-r--r--.travis.yml38
-rw-r--r--CONTRIBUTORS7
-rwxr-xr-xREADME.rst19
-rw-r--r--development.ini51
-rw-r--r--docs/api/api.rst401
-rwxr-xr-xdocs/changelog.rst60
-rw-r--r--docs/conf.py4
-rw-r--r--docs/contributing.rst3
-rw-r--r--docs/index.rst3
-rw-r--r--docs/installation.rst19
-rw-r--r--docs/installation_win.rst244
-rwxr-xr-xdocs/setup.rst57
-rw-r--r--docs/usage/general.rst26
-rw-r--r--docs/usage/git_support.rst17
-rw-r--r--docs/usage/locking.rst41
-rw-r--r--docs/usage/performance.rst50
-rw-r--r--docs/usage/troubleshooting.rst70
-rw-r--r--init.d/supervisord.conf51
-rw-r--r--production.ini51
-rw-r--r--requires.txt14
-rw-r--r--rhodecode/__init__.py43
-rw-r--r--rhodecode/bin/__init__.py0
-rwxr-xr-xrhodecode/bin/rhodecode_api.py249
-rwxr-xr-x[-rw-r--r--]rhodecode/bin/rhodecode_backup.py (renamed from rhodecode/lib/backup_manager.py)6
-rw-r--r--rhodecode/config/deployment.ini_tmpl55
-rw-r--r--rhodecode/config/environment.py17
-rw-r--r--rhodecode/config/post_receive_tmpl.py31
-rw-r--r--rhodecode/config/pre_receive_tmpl.py31
-rw-r--r--rhodecode/config/rcextensions/__init__.py4
-rw-r--r--rhodecode/config/rcextensions/make_rcextensions.py6
-rw-r--r--rhodecode/config/routing.py175
-rw-r--r--rhodecode/controllers/admin/admin.py2
-rw-r--r--rhodecode/controllers/admin/ldap_settings.py5
-rw-r--r--rhodecode/controllers/admin/notifications.py40
-rw-r--r--rhodecode/controllers/admin/permissions.py33
-rw-r--r--rhodecode/controllers/admin/repos.py131
-rw-r--r--rhodecode/controllers/admin/repos_groups.py54
-rw-r--r--rhodecode/controllers/admin/settings.py282
-rw-r--r--rhodecode/controllers/admin/users.py189
-rw-r--r--rhodecode/controllers/admin/users_groups.py124
-rw-r--r--rhodecode/controllers/api/__init__.py52
-rw-r--r--rhodecode/controllers/api/api.py757
-rw-r--r--rhodecode/controllers/branches.py3
-rw-r--r--rhodecode/controllers/changelog.py31
-rw-r--r--rhodecode/controllers/changeset.py68
-rw-r--r--rhodecode/controllers/compare.py133
-rw-r--r--rhodecode/controllers/feed.py63
-rw-r--r--rhodecode/controllers/files.py102
-rw-r--r--rhodecode/controllers/forks.py16
-rw-r--r--rhodecode/controllers/home.py5
-rw-r--r--rhodecode/controllers/journal.py130
-rw-r--r--rhodecode/controllers/login.py47
-rw-r--r--rhodecode/controllers/pullrequests.py404
-rw-r--r--rhodecode/controllers/search.py49
-rw-r--r--rhodecode/controllers/settings.py12
-rw-r--r--rhodecode/controllers/summary.py21
-rw-r--r--rhodecode/i18n/en/LC_MESSAGES/rhodecode.po3087
-rw-r--r--rhodecode/i18n/fr/LC_MESSAGES/rhodecode.mobin0 -> 45107 bytes
-rw-r--r--rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po4047
-rw-r--r--rhodecode/i18n/how_to20
-rw-r--r--rhodecode/i18n/ja/LC_MESSAGES/rhodecode.mobin0 -> 53308 bytes
-rw-r--r--rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po3929
-rw-r--r--rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.mobin35095 -> 42556 bytes
-rw-r--r--rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po3522
-rw-r--r--rhodecode/i18n/rhodecode.pot3015
-rw-r--r--rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.mobin0 -> 23719 bytes
-rw-r--r--rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po4022
-rw-r--r--rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po3435
-rw-r--r--rhodecode/lib/auth.py34
-rw-r--r--rhodecode/lib/auth_ldap.py9
-rw-r--r--rhodecode/lib/base.py124
-rw-r--r--rhodecode/lib/celerylib/__init__.py2
-rw-r--r--rhodecode/lib/celerylib/tasks.py32
-rw-r--r--rhodecode/lib/cleanup.py142
-rw-r--r--rhodecode/lib/compat.py191
-rw-r--r--rhodecode/lib/db_manage.py208
-rw-r--r--rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py1
-rw-r--r--rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py1
-rwxr-xr-xrhodecode/lib/dbmigrate/schema/db_1_2_0.py6
-rw-r--r--rhodecode/lib/dbmigrate/schema/db_1_3_0.py1302
-rw-r--r--rhodecode/lib/dbmigrate/schema/db_1_4_0.py28
-rw-r--r--rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py186
-rw-r--r--rhodecode/lib/diffs.py182
-rw-r--r--rhodecode/lib/exceptions.py19
-rw-r--r--rhodecode/lib/ext_json.py41
-rw-r--r--rhodecode/lib/graphmod.py127
-rw-r--r--rhodecode/lib/helpers.py263
-rw-r--r--rhodecode/lib/hooks.py220
-rw-r--r--rhodecode/lib/indexers/__init__.py96
-rw-r--r--rhodecode/lib/indexers/daemon.py334
-rw-r--r--rhodecode/lib/markup_renderer.py17
-rw-r--r--rhodecode/lib/middleware/https_fixup.py27
-rw-r--r--rhodecode/lib/middleware/pygrack.py200
-rw-r--r--rhodecode/lib/middleware/simplegit.py103
-rw-r--r--rhodecode/lib/middleware/simplehg.py66
-rw-r--r--rhodecode/lib/pidlock.py5
-rw-r--r--rhodecode/lib/profiler.py9
-rw-r--r--rhodecode/lib/rcmail/message.py7
-rw-r--r--rhodecode/lib/rcmail/response.py8
-rw-r--r--rhodecode/lib/rcmail/smtp_mailer.py17
-rw-r--r--rhodecode/lib/rcmail/utils.py19
-rw-r--r--rhodecode/lib/subprocessio.py409
-rw-r--r--rhodecode/lib/utils.py158
-rw-r--r--rhodecode/lib/utils2.py111
-rw-r--r--rhodecode/lib/vcs/__init__.py2
-rw-r--r--rhodecode/lib/vcs/backends/git/changeset.py105
-rw-r--r--rhodecode/lib/vcs/backends/git/inmemory.py7
-rw-r--r--rhodecode/lib/vcs/backends/git/repository.py202
-rw-r--r--rhodecode/lib/vcs/backends/hg/changeset.py12
-rw-r--r--rhodecode/lib/vcs/backends/hg/inmemory.py5
-rw-r--r--rhodecode/lib/vcs/backends/hg/repository.py20
-rw-r--r--rhodecode/lib/vcs/backends/hg/workdir.py2
-rw-r--r--rhodecode/lib/vcs/nodes.py14
-rw-r--r--rhodecode/lib/vcs/utils/hgcompat.py3
-rw-r--r--rhodecode/lib/vcs/utils/lazy.py3
-rw-r--r--rhodecode/lib/vcs/utils/paths.py3
-rw-r--r--rhodecode/model/__init__.py47
-rw-r--r--rhodecode/model/changeset_status.py189
-rw-r--r--rhodecode/model/comment.py168
-rwxr-xr-xrhodecode/model/db.py779
-rw-r--r--rhodecode/model/forms.py804
-rw-r--r--rhodecode/model/notification.py104
-rw-r--r--rhodecode/model/permission.py34
-rw-r--r--rhodecode/model/pull_request.py249
-rw-r--r--rhodecode/model/repo.py171
-rw-r--r--rhodecode/model/repo_permission.py19
-rw-r--r--rhodecode/model/repos_group.py50
-rw-r--r--rhodecode/model/scm.py215
-rw-r--r--rhodecode/model/user.py266
-rw-r--r--rhodecode/model/users_group.py22
-rw-r--r--rhodecode/model/validators.py676
-rw-r--r--rhodecode/public/css/codemirror.css145
-rw-r--r--rhodecode/public/css/style.css278
-rw-r--r--rhodecode/public/images/arrow_right_64.pngbin0 -> 3105 bytes
-rw-r--r--rhodecode/public/images/icons/flag_status_approved.pngbin0 -> 672 bytes
-rw-r--r--rhodecode/public/images/icons/flag_status_not_reviewed.pngbin0 -> 3519 bytes
-rw-r--r--rhodecode/public/images/icons/flag_status_rejected.pngbin0 -> 665 bytes
-rw-r--r--rhodecode/public/images/icons/flag_status_under_review.pngbin0 -> 671 bytes
-rw-r--r--rhodecode/public/js/codemirror.js2949
-rw-r--r--rhodecode/public/js/native.history.js1
-rw-r--r--rhodecode/public/js/rhodecode.js728
-rw-r--r--rhodecode/public/js/yui.2.9.js1015
-rw-r--r--rhodecode/templates/admin/admin_log.html9
-rw-r--r--rhodecode/templates/admin/ldap/ldap.html2
-rw-r--r--rhodecode/templates/admin/notifications/notifications.html45
-rw-r--r--rhodecode/templates/admin/notifications/notifications_data.html15
-rw-r--r--rhodecode/templates/admin/notifications/show_notification.html2
-rw-r--r--rhodecode/templates/admin/permissions/permissions.html13
-rw-r--r--rhodecode/templates/admin/repos/repo_add_base.html13
-rw-r--r--rhodecode/templates/admin/repos/repo_edit.html65
-rw-r--r--rhodecode/templates/admin/repos/repo_edit_perms.html52
-rw-r--r--rhodecode/templates/admin/repos/repos.html171
-rw-r--r--rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html43
-rw-r--r--rhodecode/templates/admin/repos_groups/repos_groups_add.html2
-rw-r--r--rhodecode/templates/admin/repos_groups/repos_groups_edit.html14
-rw-r--r--rhodecode/templates/admin/repos_groups/repos_groups_show.html2
-rw-r--r--rhodecode/templates/admin/settings/hooks.html2
-rw-r--r--rhodecode/templates/admin/settings/settings.html109
-rw-r--r--rhodecode/templates/admin/users/user_add.html6
-rw-r--r--rhodecode/templates/admin/users/user_edit.html137
-rw-r--r--rhodecode/templates/admin/users/user_edit_my_account.html203
-rw-r--r--rhodecode/templates/admin/users/user_edit_my_account_form.html85
-rw-r--r--rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html41
-rw-r--r--rhodecode/templates/admin/users/user_edit_my_account_repos.html46
-rw-r--r--rhodecode/templates/admin/users/users.html156
-rw-r--r--rhodecode/templates/admin/users_groups/users_group_add.html2
-rw-r--r--rhodecode/templates/admin/users_groups/users_group_edit.html163
-rw-r--r--rhodecode/templates/admin/users_groups/users_groups.html4
-rw-r--r--rhodecode/templates/base/base.html11
-rw-r--r--rhodecode/templates/base/root.html38
-rw-r--r--rhodecode/templates/bookmarks/bookmarks.html4
-rw-r--r--rhodecode/templates/bookmarks/bookmarks_data.html2
-rw-r--r--rhodecode/templates/branches/branches.html21
-rw-r--r--rhodecode/templates/branches/branches_data.html10
-rw-r--r--rhodecode/templates/changelog/changelog.html51
-rw-r--r--rhodecode/templates/changelog/changelog_details.html14
-rw-r--r--rhodecode/templates/changeset/changeset.html49
-rw-r--r--rhodecode/templates/changeset/changeset_comment_block.html2
-rw-r--r--rhodecode/templates/changeset/changeset_file_comment.html103
-rw-r--r--rhodecode/templates/changeset/changeset_range.html22
-rw-r--r--rhodecode/templates/changeset/diff_block.html34
-rw-r--r--rhodecode/templates/compare/compare_cs.html27
-rw-r--r--rhodecode/templates/compare/compare_diff.html77
-rw-r--r--rhodecode/templates/data_table/_dt_elements.html (renamed from rhodecode/templates/_data_table/_dt_elements.html)31
-rw-r--r--rhodecode/templates/email_templates/changeset_comment.html6
-rwxr-xr-xrhodecode/templates/errors/error_document.html8
-rw-r--r--rhodecode/templates/files/file_diff.html4
-rw-r--r--rhodecode/templates/files/files.html112
-rw-r--r--rhodecode/templates/files/files_add.html4
-rw-r--r--rhodecode/templates/files/files_browser.html8
-rw-r--r--rhodecode/templates/files/files_edit.html4
-rw-r--r--rhodecode/templates/files/files_source.html34
-rw-r--r--rhodecode/templates/files/files_ypjax.html2
-rw-r--r--rhodecode/templates/followers/followers.html4
-rw-r--r--rhodecode/templates/followers/followers_data.html4
-rw-r--r--rhodecode/templates/forks/fork.html20
-rw-r--r--rhodecode/templates/forks/forks.html4
-rw-r--r--rhodecode/templates/forks/forks_data.html6
-rw-r--r--rhodecode/templates/index_base.html20
-rw-r--r--rhodecode/templates/journal/journal.html21
-rw-r--r--rhodecode/templates/journal/journal_data.html6
-rw-r--r--rhodecode/templates/journal/public_journal.html54
-rw-r--r--rhodecode/templates/login.html2
-rw-r--r--rhodecode/templates/password_reset.html2
-rw-r--r--rhodecode/templates/pullrequests/pullrequest.html189
-rw-r--r--rhodecode/templates/pullrequests/pullrequest_show.html195
-rw-r--r--rhodecode/templates/pullrequests/pullrequest_show_all.html42
-rw-r--r--rhodecode/templates/register.html6
-rw-r--r--rhodecode/templates/repo_switcher_list.html6
-rw-r--r--rhodecode/templates/search/search.html33
-rw-r--r--rhodecode/templates/search/search_commit.html45
-rw-r--r--rhodecode/templates/settings/repo_settings.html17
-rw-r--r--rhodecode/templates/shortlog/shortlog.html4
-rw-r--r--rhodecode/templates/shortlog/shortlog_data.html9
-rw-r--r--rhodecode/templates/summary/summary.html30
-rw-r--r--rhodecode/templates/switch_to_list.html4
-rw-r--r--rhodecode/templates/tags/tags.html4
-rw-r--r--rhodecode/templates/tags/tags_data.html2
-rw-r--r--rhodecode/tests/__init__.py67
-rw-r--r--rhodecode/tests/api/__init__.py0
-rw-r--r--rhodecode/tests/api/api_base.py990
-rw-r--r--rhodecode/tests/api/test_api_git.py7
-rw-r--r--rhodecode/tests/api/test_api_hg.py7
-rw-r--r--rhodecode/tests/functional/test_admin_notifications.py40
-rw-r--r--rhodecode/tests/functional/test_admin_repos.py262
-rw-r--r--rhodecode/tests/functional/test_admin_settings.py226
-rw-r--r--rhodecode/tests/functional/test_admin_users.py237
-rw-r--r--rhodecode/tests/functional/test_admin_users_groups.py108
-rw-r--r--rhodecode/tests/functional/test_changelog.py28
-rw-r--r--rhodecode/tests/functional/test_changeset_comments.py14
-rw-r--r--rhodecode/tests/functional/test_compare.py189
-rw-r--r--rhodecode/tests/functional/test_files.py23
-rw-r--r--rhodecode/tests/functional/test_forks.py94
-rw-r--r--rhodecode/tests/functional/test_home.py43
-rw-r--r--rhodecode/tests/functional/test_login.py212
-rw-r--r--rhodecode/tests/functional/test_pullrequests.py9
-rw-r--r--rhodecode/tests/functional/test_search.py93
-rw-r--r--rhodecode/tests/functional/test_summary.py48
-rw-r--r--rhodecode/tests/models/__init__.py0
-rw-r--r--rhodecode/tests/models/test_notifications.py187
-rw-r--r--rhodecode/tests/models/test_permissions.py438
-rw-r--r--rhodecode/tests/models/test_repos_groups.py172
-rw-r--r--rhodecode/tests/models/test_users.py124
-rw-r--r--rhodecode/tests/nose_parametrized.py238
-rwxr-xr-xrhodecode/tests/scripts/create_rc.sh12
-rwxr-xr-xrhodecode/tests/scripts/mem_watch (renamed from rhodecode/tests/mem_watch)0
-rw-r--r--rhodecode/tests/scripts/test_concurency.py (renamed from rhodecode/tests/_test_concurency.py)4
-rwxr-xr-xrhodecode/tests/scripts/test_crawler.py (renamed from rhodecode/tests/rhodecode_crawler.py)0
-rwxr-xr-xrhodecode/tests/scripts/test_vcs_operations.py425
-rwxr-xr-xrhodecode/tests/test_hg_operations.py401
-rw-r--r--rhodecode/tests/test_libs.py63
-rw-r--r--rhodecode/tests/test_models.py715
-rw-r--r--rhodecode/tests/test_validators.py246
-rw-r--r--rhodecode/tests/vcs/__init__.py56
-rw-r--r--rhodecode/tests/vcs/aconfig10
-rw-r--r--rhodecode/tests/vcs/base.py111
-rw-r--r--rhodecode/tests/vcs/conf.py62
-rw-r--r--rhodecode/tests/vcs/test_archives.py108
-rw-r--r--rhodecode/tests/vcs/test_branches.py118
-rw-r--r--rhodecode/tests/vcs/test_changesets.py344
-rw-r--r--rhodecode/tests/vcs/test_filenodes_unicode_path.py49
-rw-r--r--rhodecode/tests/vcs/test_getitem.py44
-rw-r--r--rhodecode/tests/vcs/test_getslice.py56
-rw-r--r--rhodecode/tests/vcs/test_git.py702
-rw-r--r--rhodecode/tests/vcs/test_hg.py557
-rw-r--r--rhodecode/tests/vcs/test_inmemchangesets.py340
-rw-r--r--rhodecode/tests/vcs/test_nodes.py183
-rw-r--r--rhodecode/tests/vcs/test_repository.py215
-rw-r--r--rhodecode/tests/vcs/test_tags.py61
-rw-r--r--rhodecode/tests/vcs/test_utils.py279
-rw-r--r--rhodecode/tests/vcs/test_utils_filesize.py26
-rw-r--r--rhodecode/tests/vcs/test_vcs.py84
-rw-r--r--rhodecode/tests/vcs/test_workdirs.py90
-rw-r--r--rhodecode/tests/vcs/utils.py97
-rw-r--r--rhodecode/tests/vcs_test_git.tar.gzbin0 -> 1135049 bytes
-rw-r--r--rhodecode/tests/vcs_test_hg.tar.gzbin327161 -> 237244 bytes
-rw-r--r--rhodecode/websetup.py2
-rw-r--r--setup.cfg2
-rw-r--r--setup.py79
-rw-r--r--test.ini28
-rw-r--r--tox.ini112
282 files changed, 45882 insertions, 10130 deletions
diff --git a/.hgignore b/.hgignore
index f1067489..a8a6e0fe 100644
--- a/.hgignore
+++ b/.hgignore
@@ -2,6 +2,7 @@ syntax: glob
*.pyc
*.swp
*.sqlite
+*.tox
*.egg-info
*.egg
@@ -20,3 +21,4 @@ syntax: regexp
^RhodeCode\.egg-info$
^rc\.ini$
^fabfile.py
+^\.rhodecode$
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..b2964220
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,38 @@
+language: python
+python:
+ - "2.5"
+ - "2.6"
+ - "2.7"
+
+env:
+ - TEST_DB=sqlite:////tmp/rhodecode_test.sqlite
+ - TEST_DB=mysql://root@127.0.0.1/rhodecode_test
+ - TEST_DB=postgresql://postgres@127.0.0.1/rhodecode_test
+
+# command to install dependencies
+before_script:
+ - mysql -e 'create database rhodecode_test;'
+ - psql -c 'create database rhodecode_test;' -U postgres
+ - git --version
+
+before_install:
+ - sudo apt-get remove git
+ - sudo add-apt-repository ppa:pdoes/ppa -y
+ - sudo apt-get update -y
+ - sudo apt-get install git -y
+
+install:
+ - pip install mysql-python psycopg2 mock unittest2
+ - pip install . --use-mirrors
+
+# command to run tests
+script: nosetests
+
+notifications:
+ email:
+ - marcinkuz@gmail.com
+ irc: "irc.freenode.org#rhodecode"
+
+branches:
+ only:
+ - dev
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index fd47faa2..23159bde 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -18,4 +18,9 @@ List of contributors to RhodeCode project:
Aras Pranckevicius <aras@unity3d.com>
Tony Bussieres <t.bussieres@gmail.com>
Erwin Kroon <e.kroon@smartmetersolutions.nl>
- nansenat16 <nansenat16@null.tw> \ No newline at end of file
+ nansenat16 <nansenat16@null.tw>
+ Vincent Duvert <vincent@duvert.net>
+ Takumi IINO <trot.thunder@gmail.com>
+ Indra Talip <indra.talip@gmail.com>
+ James Rhodes <jrhodes@redpointsoftware.com.au>
+ Dominik Ruf <dominikruf@gmail.com> \ No newline at end of file
diff --git a/README.rst b/README.rst
index 541acb3f..f25f0e3f 100755
--- a/README.rst
+++ b/README.rst
@@ -72,25 +72,26 @@ RhodeCode Features
Each request can be logged and authenticated.
- Runs on threads unlike hgweb. You can make multiple pulls/pushes simultaneous.
Supports http/https and LDAP
-- Full permissions (private/read/write/admin) and authentication per project.
- One account for web interface and mercurial_ push/pull/clone operations.
+- Full permissions (private/read/write/admin) for each repository, additional
+ explicit forking and repository permissions.
- Have built in users groups for easier permission management
- Repository groups let you group repos and manage them easier.
- Users can fork other users repo. RhodeCode have also compare view to see
combined changeset for all changeset made within single push.
- Build in commit-api let's you add, edit and commit files right from RhodeCode
interface using simple editor or upload form for binaries.
+- Powerfull pull-request driven review system with inline commenting, and
+ changeset statuses, notification system.
+- Importing SVN repositories from remote locations into RhodeCode.
- Mako templates let's you customize the look and feel of the application.
- Beautiful diffs, annotations and source code browsing all colored by pygments.
- Raw diffs are made in git-diff format, including git_ binary-patches
+ Raw diffs are made in git-diff format, including GIT_ binary-patches
- Mercurial_ branch graph and yui-flot powered graphs with zooming and statistics
- Admin interface with user/permission management. Admin activity journal, logs
pulls, pushes, forks, registrations and other actions made by all users.
- Server side forks. It is possible to fork a project and modify it freely
without breaking the main repository. You can even write Your own hooks
and install them
-- code review with notification system, inline commenting, all parsed using
- rst syntax
- rst and markdown README support for repositories
- Full text search powered by Whoosh on the source files, and file names.
Build in indexing daemons, with optional incremental index build
@@ -110,8 +111,9 @@ Incoming / Plans
----------------
- Finer granular permissions per branch, repo group or subrepo
-- pull requests and web based merges
-- per line file history
+- Pull requests with web based merges
+- Per line file history
+- Simple issue tracker
- SSH based authentication with server side key management
- Commit based built in wiki system
- More statistics and graph (global annotation + some more statistics)
@@ -131,7 +133,8 @@ Listed bellow are various support resources that should help.
.. note::
- Please try to read the documentation before posting any issues
+ Please try to read the documentation before posting any issues, especially
+ the **troubleshooting section**
- Join the `Google group <http://groups.google.com/group/rhodecode>`_ and ask
any questions.
diff --git a/development.ini b/development.ini
index 4cef3478..7737cd9e 100644
--- a/development.ini
+++ b/development.ini
@@ -30,22 +30,31 @@ pdebug = false
[server:main]
##nr of threads to spawn
-threadpool_workers = 5
+#threadpool_workers = 5
##max request before thread respawn
-threadpool_max_requests = 10
+#threadpool_max_requests = 10
##option to use threads of process
-use_threadpool = true
+#use_threadpool = true
-use = egg:Paste#http
+#use = egg:Paste#http
+use = egg:waitress#main
host = 0.0.0.0
port = 5000
+[filter:proxy-prefix]
+# prefix middleware for rc
+use = egg:PasteDeploy#prefix
+prefix = /<your-prefix>
+
[app:main]
use = egg:rhodecode
+#filter-with = proxy-prefix
full_stack = true
static_files = true
+# Optional Languages
+# en, fr, ja, pt_BR, zh_CN, zh_TW
lang = en
cache_dir = %(here)s/data
index_dir = %(here)s/data/index
@@ -54,6 +63,15 @@ cut_off_limit = 256000
force_https = false
commit_parse_limit = 25
use_gravatar = true
+
+## alternative_gravatar_url allows you to use your own avatar server application
+## the following parts of the URL will be replaced
+## {email} user email
+## {md5email} md5 hash of the user email (like at gravatar.com)
+## {size} size of the image that is expected from the server application
+#alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
+#alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
+
container_auth_enabled = false
proxypass_auth_enabled = false
default_encoding = utf8
@@ -78,7 +96,8 @@ default_encoding = utf8
issue_pat = (?:\s*#)(\d+)
## server url to the issue, each {id} will be replaced with match
-## fetched from the regex and {repo} is replaced with repository name
+## fetched from the regex and {repo} is replaced with full repository name
+## including groups {repo_name} is replaced with just name of repo
issue_server_link = https://myissueserver.com/{repo}/issue/{id}
@@ -165,30 +184,34 @@ beaker.cache.sql_cache_long.key_length = 256
## The storage uses the Container API
## that is also used by the cache system.
-## db session example
-
+## db session ##
#beaker.session.type = ext:database
#beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
#beaker.session.table_name = db_session
-## encrypted cookie session, good for many instances
+## encrypted cookie client side session, good for many instances ##
#beaker.session.type = cookie
-beaker.session.type = file
+## file based cookies (default) ##
+#beaker.session.type = file
+
+
beaker.session.key = rhodecode
-# secure cookie requires AES python libraries
+## secure cookie requires AES python libraries ##
#beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
#beaker.session.validate_key = 9712sds2212c--zxc123
-beaker.session.timeout = 36000
+## sets session as invalid if it haven't been accessed for given amount of time
+beaker.session.timeout = 2592000
beaker.session.httponly = true
+#beaker.session.cookie_path = /<your-prefix>
-## uncomment for https secure cookie
+## uncomment for https secure cookie ##
beaker.session.secure = false
-##auto save the session to not to use .save()
+## auto save the session to not to use .save() ##
beaker.session.auto = False
-##true exire at browser close
+## default cookie expiration time in seconds `true` expire at browser close ##
#beaker.session.cookie_expires = 3600
diff --git a/docs/api/api.rst b/docs/api/api.rst
index a5958930..2eabeb73 100644
--- a/docs/api/api.rst
+++ b/docs/api/api.rst
@@ -7,7 +7,7 @@ API
Starting from RhodeCode version 1.2 a simple API was implemented.
There's a single schema for calling all api methods. API is implemented
-with JSON protocol both ways. An url to send API request in RhodeCode is
+with JSON protocol both ways. An url to send API request to RhodeCode is
<your_server>/_admin/api
API ACCESS FOR WEB VIEWS
@@ -59,6 +59,47 @@ All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
calling api *error* key from response will contain failure description
and result will be null.
+
+API CLIENT
+++++++++++
+
+From version 1.4 RhodeCode adds a script that allows to easily
+communicate with API. After installing RhodeCode a `rhodecode-api` script
+will be available.
+
+To get started quickly simply run::
+
+ rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
+
+This will create a file named .config in the directory you executed it storing
+json config file with credentials. You can skip this step and always provide
+both of the arguments to be able to communicate with server
+
+
+after that simply run any api command for example get_repo::
+
+ rhodecode-api get_repo
+
+ calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
+ rhodecode said:
+ {'error': 'Missing non optional `repoid` arg in JSON DATA',
+ 'id': 75,
+ 'result': None}
+
+Ups looks like we forgot to add an argument
+
+Let's try again now giving the repoid as parameters::
+
+ rhodecode-api get_repo repoid:rhodecode
+
+ calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
+ rhodecode said:
+ {'error': None,
+ 'id': 39,
+ 'result': <json data...>}
+
+
+
API METHODS
+++++++++++
@@ -76,12 +117,64 @@ INPUT::
api_key : "<api_key>"
method : "pull"
args : {
- "repo_name" : "<reponame>"
+ "repoid" : "<reponame or repo_id>"
+ }
+
+OUTPUT::
+
+ id : <id_given_in_input>
+ result : "Pulled from `<reponame>`"
+ error : null
+
+
+rescan_repos
+------------
+
+Dispatch rescan repositories action. If remove_obsolete is set
+RhodeCode will delete repos that are in database but not in the filesystem.
+This command can be executed only using api_key belonging to user with admin
+rights.
+
+INPUT::
+
+ id : <id_for_response>
+ api_key : "<api_key>"
+ method : "rescan_repos"
+ args : {
+ "remove_obsolete" : "<boolean = Optional(False)>"
}
OUTPUT::
- result : "Pulled from <reponame>"
+ id : <id_given_in_input>
+ result : "{'added': [<list of names of added repos>],
+ 'removed': [<list of names of removed repos>]}"
+ error : null
+
+
+lock
+----
+
+Set locking state on given repository by given user.
+This command can be executed only using api_key belonging to user with admin
+rights.
+
+INPUT::
+
+ id : <id_for_response>
+ api_key : "<api_key>"
+ method : "lock"
+ args : {
+ "repoid" : "<reponame or repo_id>"
+ "userid" : "<user_id or username>",
+ "locked" : "<bool true|false>"
+
+ }
+
+OUTPUT::
+
+ id : <id_given_in_input>
+ result : "User `<username>` set lock state for repo `<reponame>` to `true|false`"
error : null
@@ -104,13 +197,15 @@ INPUT::
OUTPUT::
+ id : <id_given_in_input>
result: None if user does not exist or
{
- "id" : "<id>",
+ "user_id" : "<user_id>",
"username" : "<username>",
"firstname": "<firstname>",
"lastname" : "<lastname>",
"email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
"active" : "<bool>",
"admin" :  "<bool>",
"ldap_dn" : "<ldap_dn>",
@@ -143,13 +238,15 @@ INPUT::
OUTPUT::
+ id : <id_given_in_input>
result: [
{
- "id" : "<id>",
+ "user_id" : "<user_id>",
"username" : "<username>",
"firstname": "<firstname>",
"lastname" : "<lastname>",
"email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
"active" : "<bool>",
"admin" :  "<bool>",
"ldap_dn" : "<ldap_dn>",
@@ -174,20 +271,32 @@ INPUT::
method : "create_user"
args : {
"username" : "<username>",
- "password" : "<password>",
"email" : "<useremail>",
- "firstname" : "<firstname> = None",
- "lastname" : "<lastname> = None",
- "active" : "<bool> = True",
- "admin" : "<bool> = False",
- "ldap_dn" : "<ldap_dn> = None"
+ "password" : "<password>",
+ "firstname" : "<firstname> = Optional(None)",
+ "lastname" : "<lastname> = Optional(None)",
+ "active" : "<bool> = Optional(True)",
+ "admin" : "<bool> = Optional(False)",
+ "ldap_dn" : "<ldap_dn> = Optional(None)"
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "id" : "<new_user_id>",
- "msg" : "created new user <username>"
+ "msg" : "created new user `<username>`",
+ "user": {
+ "user_id" : "<user_id>",
+ "username" : "<username>",
+ "firstname": "<firstname>",
+ "lastname" : "<lastname>",
+ "email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
+ "active" : "<bool>",
+ "admin" :  "<bool>",
+ "ldap_dn" : "<ldap_dn>",
+ "last_login": "<last_login>",
+ },
}
error: null
@@ -195,7 +304,7 @@ OUTPUT::
update_user
-----------
-updates current one if such user exists. This command can
+updates given user if such user exists. This command can
be executed only using api_key belonging to user with admin rights.
@@ -206,21 +315,60 @@ INPUT::
method : "update_user"
args : {
"userid" : "<user_id or username>",
- "username" : "<username>",
- "password" : "<password>",
- "email" : "<useremail>",
- "firstname" : "<firstname>",
- "lastname" : "<lastname>",
- "active" : "<bool>",
- "admin" : "<bool>",
- "ldap_dn" : "<ldap_dn>"
+ "username" : "<username> = Optional",
+ "email" : "<useremail> = Optional",
+ "password" : "<password> = Optional",
+ "firstname" : "<firstname> = Optional",
+ "lastname" : "<lastname> = Optional",
+ "active" : "<bool> = Optional",
+ "admin" : "<bool> = Optional",
+ "ldap_dn" : "<ldap_dn> = Optional"
+ }
+
+OUTPUT::
+
+ id : <id_given_in_input>
+ result: {
+ "msg" : "updated user ID:<userid> <username>",
+ "user": {
+ "user_id" : "<user_id>",
+ "username" : "<username>",
+ "firstname": "<firstname>",
+ "lastname" : "<lastname>",
+ "email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
+ "active" : "<bool>",
+ "admin" :  "<bool>",
+ "ldap_dn" : "<ldap_dn>",
+ "last_login": "<last_login>",
+ },
+ }
+ error: null
+
+
+delete_user
+-----------
+
+
+deletes givenuser if such user exists. This command can
+be executed only using api_key belonging to user with admin rights.
+
+
+INPUT::
+
+ id : <id_for_response>
+ api_key : "<api_key>"
+ method : "delete_user"
+ args : {
+ "userid" : "<user_id or username>",
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "id" : "<edited_user_id>",
- "msg" : "updated user <username>"
+ "msg" : "deleted user ID:<userid> <username>",
+ "user": null
}
error: null
@@ -238,25 +386,29 @@ INPUT::
api_key : "<api_key>"
method : "get_users_group"
args : {
- "group_name" : "<name>"
+ "usersgroupid" : "<users group id or name>"
}
OUTPUT::
+ id : <id_given_in_input>
result : None if group not exist
{
- "id" : "<id>",
- "group_name" : "<groupname>",
- "active": "<bool>",
+ "users_group_id" : "<id>",
+ "group_name" : "<groupname>",
+ "active": "<bool>",
"members" : [
- { "id" : "<userid>",
+ {
+ "user_id" : "<user_id>",
"username" : "<username>",
"firstname": "<firstname>",
"lastname" : "<lastname>",
"email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
"active" : "<bool>",
"admin" :  "<bool>",
- "ldap" : "<ldap_dn>"
+ "ldap_dn" : "<ldap_dn>",
+ "last_login": "<last_login>",
},
]
@@ -280,25 +432,29 @@ INPUT::
OUTPUT::
+ id : <id_given_in_input>
result : [
{
- "id" : "<id>",
- "group_name" : "<groupname>",
- "active": "<bool>",
- "members" : [
- {
- "id" : "<userid>",
- "username" : "<username>",
- "firstname": "<firstname>",
- "lastname" : "<lastname>",
- "email" : "<email>",
- "active" : "<bool>",
- "admin" :  "<bool>",
- "ldap" : "<ldap_dn>"
- },
- …
- ]
- }
+ "users_group_id" : "<id>",
+ "group_name" : "<groupname>",
+ "active": "<bool>",
+ "members" : [
+ {
+ "user_id" : "<user_id>",
+ "username" : "<username>",
+ "firstname": "<firstname>",
+ "lastname" : "<lastname>",
+ "email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
+ "active" : "<bool>",
+ "admin" :  "<bool>",
+ "ldap_dn" : "<ldap_dn>",
+ "last_login": "<last_login>",
+ },
+ …
+ ]
+ },
+ …
]
error : null
@@ -317,14 +473,34 @@ INPUT::
method : "create_users_group"
args: {
"group_name": "<groupname>",
- "active":"<bool> = True"
+ "active":"<bool> = Optional(True)"
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "id": "<newusersgroupid>",
- "msg": "created new users group <groupname>"
+ "msg": "created new users group `<groupname>`",
+ "users_group": {
+ "users_group_id" : "<id>",
+ "group_name" : "<groupname>",
+ "active": "<bool>",
+ "members" : [
+ {
+ "user_id" : "<user_id>",
+ "username" : "<username>",
+ "firstname": "<firstname>",
+ "lastname" : "<lastname>",
+ "email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
+ "active" : "<bool>",
+ "admin" :  "<bool>",
+ "ldap_dn" : "<ldap_dn>",
+ "last_login": "<last_login>",
+ },
+ …
+ ]
+ },
}
error: null
@@ -343,16 +519,16 @@ INPUT::
api_key : "<api_key>"
method : "add_user_users_group"
args: {
- "group_name" : "<groupname>",
- "username" : "<username>"
+ "usersgroupid" : "<users group id or name>",
+ "userid" : "<user_id or username>",
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "id": "<newusersgroupmemberid>",
"success": True|False # depends on if member is in group
- "msg": "added member <username> to users group <groupname> |
+ "msg": "added member `<username>` to users group `<groupname>` |
User is already in that group"
}
error: null
@@ -372,12 +548,13 @@ INPUT::
api_key : "<api_key>"
method : "remove_user_from_users_group"
args: {
- "group_name" : "<groupname>",
- "username" : "<username>"
+ "usersgroupid" : "<users group id or name>",
+ "userid" : "<user_id or username>",
}
OUTPUT::
+ id : <id_given_in_input>
result: {
"success": True|False, # depends on if member is in group
"msg": "removed member <username> from users group <groupname> |
@@ -405,23 +582,32 @@ INPUT::
OUTPUT::
+ id : <id_given_in_input>
result: None if repository does not exist or
{
- "id" : "<id>",
+ "repo_id" : "<repo_id>",
"repo_name" : "<reponame>"
- "type" : "<type>",
+ "repo_type" : "<repo_type>",
+ "clone_uri" : "<clone_uri>",
+ "private": : "<bool>",
+ "created_on" : "<datetimecreated>",
"description" : "<description>",
+ "landing_rev": "<landing_rev>",
+ "owner": "<repo_owner>",
+ "fork_of": "<name_of_fork_parent>",
"members" : [
{
"type": "user",
- "id" : "<userid>",
- "username" : "<username>",
- "firstname": "<firstname>",
- "lastname" : "<lastname>",
- "email" : "<email>",
- "active" : "<bool>",
- "admin" :  "<bool>",
- "ldap" : "<ldap_dn>",
+ "user_id" : "<user_id>",
+ "username" : "<username>",
+ "firstname": "<firstname>",
+ "lastname" : "<lastname>",
+ "email" : "<email>",
+ "emails": "<list_of_all_additional_emails>",
+ "active" : "<bool>",
+ "admin" :  "<bool>",
+ "ldap_dn" : "<ldap_dn>",
+ "last_login": "<last_login>",
"permission" : "repository.(read|write|admin)"
},
@@ -454,12 +640,19 @@ INPUT::
OUTPUT::
+ id : <id_given_in_input>
result: [
{
- "id" : "<id>",
+ "repo_id" : "<repo_id>",
"repo_name" : "<reponame>"
- "type" : "<type>",
- "description" : "<description>"
+ "repo_type" : "<repo_type>",
+ "clone_uri" : "<clone_uri>",
+ "private": : "<bool>",
+ "created_on" : "<datetimecreated>",
+ "description" : "<description>",
+ "landing_rev": "<landing_rev>",
+ "owner": "<repo_owner>",
+ "fork_of": "<name_of_fork_parent>",
},
]
@@ -481,14 +674,15 @@ INPUT::
api_key : "<api_key>"
method : "get_repo_nodes"
args: {
- "repo_name" : "<reponame>",
+ "repoid" : "<reponame or repo_id>"
"revision" : "<revision>",
"root_path" : "<root_path>",
- "ret_type" : "<ret_type>" = 'all'
+ "ret_type" : "<ret_type> = Optional('all')"
}
OUTPUT::
+ id : <id_given_in_input>
result: [
{
"name" : "<name>"
@@ -516,18 +710,31 @@ INPUT::
method : "create_repo"
args: {
"repo_name" : "<reponame>",
- "owner_name" : "<ownername>",
- "description" : "<description> = ''",
- "repo_type" : "<type> = 'hg'",
- "private" : "<bool> = False",
- "clone_uri" : "<clone_uri> = None",
+ "owner" : "<onwer_name_or_id>",
+ "repo_type" : "<repo_type>",
+ "description" : "<description> = Optional('')",
+ "private" : "<bool> = Optional(False)",
+ "clone_uri" : "<clone_uri> = Optional(None)",
+ "landing_rev" : "<landing_rev> = Optional('tip')",
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "id": "<newrepoid>",
- "msg": "Created new repository <reponame>",
+ "msg": "Created new repository `<reponame>`",
+ "repo": {
+ "repo_id" : "<repo_id>",
+ "repo_name" : "<reponame>"
+ "repo_type" : "<repo_type>",
+ "clone_uri" : "<clone_uri>",
+ "private": : "<bool>",
+ "created_on" : "<datetimecreated>",
+ "description" : "<description>",
+ "landing_rev": "<landing_rev>",
+ "owner": "<repo_owner>",
+ "fork_of": "<name_of_fork_parent>",
+ },
}
error: null
@@ -545,13 +752,15 @@ INPUT::
api_key : "<api_key>"
method : "delete_repo"
args: {
- "repo_name" : "<reponame>",
+ "repoid" : "<reponame or repo_id>"
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "msg": "Deleted repository <reponame>",
+ "msg": "Deleted repository `<reponame>`",
+ "success": true
}
error: null
@@ -570,15 +779,17 @@ INPUT::
api_key : "<api_key>"
method : "grant_user_permission"
args: {
- "repo_name" : "<reponame>",
- "username" : "<username>",
+ "repoid" : "<reponame or repo_id>"
+ "userid" : "<username or user_id>"
"perm" : "(repository.(none|read|write|admin))",
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "msg" : "Granted perm: <perm> for user: <username> in repo: <reponame>"
+ "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
+ "success": true
}
error: null
@@ -596,14 +807,16 @@ INPUT::
api_key : "<api_key>"
method : "revoke_user_permission"
args: {
- "repo_name" : "<reponame>",
- "username" : "<username>",
+ "repoid" : "<reponame or repo_id>"
+ "userid" : "<username or user_id>"
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "msg" : "Revoked perm for user: <suername> in repo: <reponame>"
+ "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
+ "success": true
}
error: null
@@ -622,15 +835,17 @@ INPUT::
api_key : "<api_key>"
method : "grant_users_group_permission"
args: {
- "repo_name" : "<reponame>",
- "group_name" : "<usersgroupname>",
+ "repoid" : "<reponame or repo_id>"
+ "usersgroupid" : "<users group id or name>"
"perm" : "(repository.(none|read|write|admin))",
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "msg" : "Granted perm: <perm> for group: <usersgroupname> in repo: <reponame>"
+ "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
+ "success": true
}
error: null
@@ -647,13 +862,15 @@ INPUT::
api_key : "<api_key>"
method : "revoke_users_group_permission"
args: {
- "repo_name" : "<reponame>",
- "users_group" : "<usersgroupname>",
+ "repoid" : "<reponame or repo_id>"
+ "usersgroupid" : "<users group id or name>"
}
OUTPUT::
+ id : <id_given_in_input>
result: {
- "msg" : "Revoked perm for group: <usersgroupname> in repo: <reponame>"
+ "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
+ "success": true
}
error: null \ No newline at end of file
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 2af4ab9a..04781314 100755
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,66 @@
Changelog
=========
+1.4.0 (**2012-09-03**)
+----------------------
+
+news
+++++
+
+- new codereview system
+- email map, allowing users to have multiple email addresses mapped into
+ their accounts
+- improved git-hook system. Now all actions for git are logged into journal
+ including pushed revisions, user and IP address
+- changed setup-app into setup-rhodecode and added default options to it.
+- new git repos are created as bare now by default
+- #464 added links to groups in permission box
+- #465 mentions autocomplete inside comments boxes
+- #469 added --update-only option to whoosh to re-index only given list
+ of repos in index
+- rhodecode-api CLI client
+- new git http protocol replaced buggy dulwich implementation.
+ Now based on pygrack & gitweb
+- Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
+ reformated based on user suggestions. Additional rss/atom feeds for user
+ journal
+- various i18n improvements
+- #478 permissions overview for admin in user edit view
+- File view now displays small gravatars off all authors of given file
+- Implemented landing revisions. Each repository will get landing_rev attribute
+ that defines 'default' revision/branch for generating readme files
+- Implemented #509, RhodeCode enforces SSL for push/pulling if requested at
+ earliest possible call.
+- Import remote svn repositories to mercurial using hgsubversion.
+- Fixed #508 RhodeCode now has a option to explicitly set forking permissions
+- RhodeCode can use alternative server for generating avatar icons
+- implemented repositories locking. Pull locks, push unlocks. Also can be done
+ via API calls
+- #538 form for permissions can handle multiple users at once
+
+fixes
++++++
+
+- improved translations
+- fixes issue #455 Creating an archive generates an exception on Windows
+- fixes #448 Download ZIP archive keeps file in /tmp open and results
+ in out of disk space
+- fixes issue #454 Search results under Windows include proceeding
+ backslash
+- fixed issue #450. Rhodecode no longer will crash when bad revision is
+ present in journal data.
+- fix for issue #417, git execution was broken on windows for certain
+ commands.
+- fixed #413. Don't disable .git directory for bare repos on deleting
+- fixed issue #459. Changed the way of obtaining logger in reindex task.
+- fixed #453 added ID field in whoosh SCHEMA that solves the issue of
+ reindexing modified files
+- fixed #481 rhodecode emails are sent without Date header
+- fixed #458 wrong count when no repos are present
+- fixed issue #492 missing `\ No newline at end of file` test at the end of
+ new chunk in html diff
+- full text search now works also for commit messages
+
1.3.6 (**2012-05-17**)
----------------------
diff --git a/docs/conf.py b/docs/conf.py
index c8963047..74c68a5a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -54,8 +54,8 @@ copyright = u'%s, Marcin Kuzminski' % (datetime.datetime.now().year)
# The short X.Y version.
root = os.path.dirname(os.path.dirname(__file__))
sys.path.append(root)
-from rhodecode import get_version, __version__
-version = get_version()
+from rhodecode import __version__
+version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 9f1b7e6c..1cda23fc 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -27,7 +27,8 @@ enviroment.
After finishing your changes make sure all tests passes ok. You can run
-the testsuite running nosetest from the project root.
+the testsuite running ``nosetest`` from the project root, or if you use tox
+run tox for python2.5-2.7 with multiple database test.
| Thank you for any contributions!
| Marcin
diff --git a/docs/index.rst b/docs/index.rst
index 189fd857..e56e2bbd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,9 +21,12 @@ Users Guide
usage/general
usage/git_support
+ usage/performance
+ usage/locking
usage/statistics
usage/backup
usage/debugging
+ usage/troubleshooting
**Develop**
diff --git a/docs/installation.rst b/docs/installation.rst
index 50b9ce8b..16cf23b9 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -25,13 +25,18 @@ Or::
pip install rhodecode
If you prefer to install RhodeCode manually simply grab latest release from
-http://pypi.python.org/pypi/rhodecode, decompress the archive and run::
+http://pypi.python.org/pypi/RhodeCode, decompress the archive and run::
python setup.py install
+Step by step installation example for Windows
+---------------------------------------------
-Step by step installation example
----------------------------------
+:ref:`installation_win`
+
+
+Step by step installation example for Linux
+-------------------------------------------
For installing RhodeCode i highly recommend using separate virtualenv_. This
@@ -41,7 +46,7 @@ python and making things less problematic when doing system python updates.
- Assuming you have installed virtualenv_ create a new virtual environment
using virtualenv command::
- virtualenv --no-site-packages /var/www/rhodecode-venv
+ virtualenv --no-site-packages /opt/rhodecode-venv
.. note:: Using ``--no-site-packages`` when generating your
@@ -54,10 +59,10 @@ python and making things less problematic when doing system python updates.
Python's "main" site-packages dir.
-- this will install new virtualenv_ into `/var/www/rhodecode-venv`.
+- this will install new virtualenv_ into `/opt/rhodecode-venv`.
- Activate the virtualenv_ by running::
- source /var/www/rhodecode-venv/bin/activate
+ source /opt/rhodecode-venv/bin/activate
.. note:: If you're using UNIX, *do not* use ``sudo`` to run the
``virtualenv`` script. It's perfectly acceptable (and desirable)
@@ -66,7 +71,7 @@ python and making things less problematic when doing system python updates.
- Make a folder for rhodecode data files, and configuration somewhere on the
filesystem. For example::
- mkdir /var/www/rhodecode
+ mkdir /opt/rhodecode
- Go into the created directory run this command to install rhodecode::
diff --git a/docs/installation_win.rst b/docs/installation_win.rst
new file mode 100644
index 00000000..9a42f57e
--- /dev/null
+++ b/docs/installation_win.rst
@@ -0,0 +1,244 @@
+.. _installation_win:
+
+
+Step by step Installation for Windows
+=====================================
+
+
+RhodeCode step-by-step install Guide for Windows
+
+Target OS: Windows XP SP3 English (Clean installation)
++ All Windows Updates until 24-may-2012
+
+Step1 - Install Visual Studio 2008 Express
+------------------------------------------
+
+
+Optional: You can also install MingW, but VS2008 installation is easier
+
+Download "Visual C++ 2008 Express Edition with SP1" from:
+http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express
+(if not found or relocated, google for "visual studio 2008 express" for
+updated link)
+
+You can also download full ISO file for offline installation, just
+choose "All - Offline Install ISO image file" in the previous page and
+choose "Visual C++ 2008 Express" when installing.
+
+
+.. note::
+
+ Silverlight Runtime and SQL Server 2008 Express Edition are not
+ required, you can uncheck them
+
+
+Step2 - Install Python
+----------------------
+
+Install Python 2.x.y (x >= 5) x86 version (32bit). DO NOT USE A 3.x version.
+Download Python 2.x.y from:
+http://www.python.org/download/
+
+Choose "Windows Installer" (32bit version) not "Windows X86-64
+Installer". While writing this guide, the latest version was v2.7.3.
+Remember the specific major and minor version installed, because it will
+be needed in the next step. In this case, it is "2.7".
+
+
+Step3 - Install Win32py extensions
+----------------------------------
+
+Download pywin32 from:
+http://sourceforge.net/projects/pywin32/files/
+
+- Click on "pywin32" folder
+- Click on the first folder (in this case, Build 217, maybe newer when you try)
+- Choose the file ending with ".win32-py2.x.exe" -> x being the minor
+ version of Python you installed (in this case, 7)
+ When writing this guide, the file was:
+ http://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/pywin32-217.win32-py2.7.exe/download
+
+
+Step4 - Python BIN
+------------------
+
+Add Python BIN folder to the path
+
+You have to add the Python folder to the path, you can do it manually
+(editing "PATH" environment variable) or using Windows Support Tools
+that came preinstalled in Vista/7 and can be installed in Windows XP.
+
+- Using support tools on WINDOWS XP:
+ If you use Windows XP you can install them using Windows XP CD and
+ navigating to \SUPPORT\TOOLS. There, execute Setup.EXE (not MSI).
+ Afterwards, open a CMD and type::
+
+ SETX PATH "%PATH%;[your-python-path]" -M
+
+ Close CMD (the path variable will be updated then)
+
+- Using support tools on WINDOWS Vista/7:
+
+ Open a CMD and type::
+
+ SETX PATH "%PATH%;[your-python-path]" /M
+
+ Please substitute [your-python-path] with your Python installation path.
+ Typically: C:\\Python27
+
+
+Step5 - RhodeCode folder structure
+----------------------------------
+
+Create a RhodeCode folder structure
+
+This is only a example to install RhodeCode, you can of course change
+it. However, this guide will follow the proposed structure, so please
+later adapt the paths if you change them. My recommendation is to use
+folders with NO SPACES. But you can try if you are brave...
+
+Create the following folder structure::
+
+ C:\RhodeCode
+ C:\RhodeCode\Bin
+ C:\RhodeCode\Env
+ C:\RhodeCode\Repos
+
+
+Step6 - Install virtualenv
+---------------------------
+
+Install Virtual Env for Python
+
+Navigate to: http://www.virtualenv.org/en/latest/index.html#installation
+Right click on "virtualenv.py" file and choose "Save link as...".
+Download to C:\\RhodeCode (or whatever you want)
+(the file is located at
+https://raw.github.com/pypa/virtualenv/master/virtualenv.py)
+
+Create a virtual Python environment in C:\\RhodeCode\\Env (or similar). To
+do so, open a CMD (Python Path should be included in Step3), navigate
+where you downloaded "virtualenv.py", and write::
+
+ python virtualenv.py C:\RhodeCode\Env
+
+(--no-site-packages is now the default behaviour of virtualenv, no need
+to include it)
+
+
+Step7 - Install RhodeCode
+-------------------------
+
+Finally, install RhodeCode
+
+Close previously opened command prompt/s, and open a Visual Studio 2008
+Command Prompt (**IMPORTANT!!**). To do so, go to Start Menu, and then open
+"Microsoft Visual C++ 2008 Express Edition" -> "Visual Studio Tools" ->
+"Visual Studio 2008 Command Prompt"
+
+In that CMD (loaded with VS2008 PATHs) type::
+
+ cd C:\RhodeCode\Env\Scripts (or similar)
+ activate
+
+The prompt will change into "(Env) C:\\RhodeCode\\Env\\Scripts" or similar
+(depending of your folder structure). Then type::
+
+ pip install rhodecode
+
+(long step, please wait until fully complete)
+
+Some warnings will appear, don't worry as they are normal.
+
+
+Step8 - Configuring RhodeCode
+-----------------------------
+
+
+steps taken from http://packages.python.org/RhodeCode/setup.html
+
+You have to use the same Visual Studio 2008 command prompt as Step7, so
+if you closed it reopen it following the same commands (including the
+"activate" one). When ready, just type::
+
+ cd C:\RhodeCode\Bin
+ paster make-config RhodeCode production.ini
+
+Then, you must edit production.ini to fit your needs (ip address, ip
+port, mail settings, database, whatever). I recommend using NotePad++
+(free) or similar text editor, as it handles well the EndOfLine
+character differences between Unix and Windows
+(http://notepad-plus-plus.org/)
+
+For the sake of simplicity lets run it with the default settings. After
+your edits (if any), in the previous Command Prompt, type::
+
+ paster setup-rhodecode production.ini
+
+(this time a NEW database will be installed, you must follow a different
+step to later UPGRADE to a newer RhodeCode version)
+
+The script will ask you for confirmation about creating a NEW database,
+answer yes (y)
+The script will ask you for repository path, answer C:\\RhodeCode\\Repos
+(or similar)
+The script will ask you for admin username and password, answer "admin"
++ "123456" (or whatever you want)
+The script will ask you for admin mail, answer "admin@xxxx.com" (or
+whatever you want)
+
+If you make some mistake and the script does not end, don't worry, start
+it again.
+
+
+Step9 - Running RhodeCode
+-------------------------
+
+
+In the previous command prompt, being in the C:\\RhodeCode\\Bin folder,
+just type::
+
+ paster serve production.ini
+
+Open yout web server, and go to http://127.0.0.1:5000
+
+It works!! :-)
+
+Remark:
+If it does not work first time, just Ctrl-C the CMD process and start it
+again. Don't forget the "http://" in Internet Explorer
+
+
+
+What this Guide does not cover:
+
+- Installing Celery
+- Running RhodeCode as Windows Service. You can investigate here:
+
+ - http://pypi.python.org/pypi/wsgisvc
+ - http://ryrobes.com/python/running-python-scripts-as-a-windows-service/
+ - http://wiki.pylonshq.com/display/pylonscookbook/How+to+run+Pylons+as+a+Windows+service
+
+- Using Apache. You can investigate here:
+
+ - https://groups.google.com/group/rhodecode/msg/c433074e813ffdc4
+
+
+Upgrading
+=========
+
+Stop running RhodeCode
+Open a CommandPrompt like in Step7 (VS2008 path + activate) and type::
+
+ easy_install -U rhodecode
+ cd \RhodeCode\Bin
+
+{ backup your production.ini file now} ::
+
+ paster make-config RhodeCode production.ini
+
+(check changes and update your production.ini accordingly) ::
+
+ paster upgrade-db production.ini (update database)
+
+Full steps in http://packages.python.org/RhodeCode/upgrade.html \ No newline at end of file
diff --git a/docs/setup.rst b/docs/setup.rst
index 6d389828..1d68300d 100755
--- a/docs/setup.rst
+++ b/docs/setup.rst
@@ -34,6 +34,11 @@ entering this "root" path ``setup-rhodecode`` will also prompt you for a usernam
and password for the initial admin account which ``setup-rhodecode`` sets
up for you.
+setup process can be fully automated, example for lazy::
+
+ paster setup-rhodecode production.ini --user=marcink --password=secret --email=marcin@rhodecode.org --repos=/home/marcink/my_repos
+
+
- The ``setup-rhodecode`` command will create all of the needed tables and an
admin account. When choosing a root path you can either use a new empty
location, or a location which already contains existing repositories. If you
@@ -527,6 +532,18 @@ Sample config for nginx using proxy::
access_log /var/log/nginx/rhodecode.access.log;
error_log /var/log/nginx/rhodecode.error.log;
+ # uncomment if you have nginx with chunking module compiled
+ # fixes the issues of having to put postBuffer data for large git
+ # pushes
+ #chunkin on;
+ #error_page 411 = @my_411_error;
+ #location @my_411_error {
+ # chunkin_resume;
+ #}
+
+ # uncomment if you want to serve static files by nginx
+ #root /path/to/installation/rhodecode/public;
+
location / {
try_files $uri @rhode;
}
@@ -682,43 +699,9 @@ environment.
Other configuration files
-------------------------
-Some example init.d scripts can be found here, for debian and gentoo:
-
-https://rhodecode.org/rhodecode/files/tip/init.d
-
-
-Troubleshooting
----------------
-
-:Q: **Missing static files?**
-:A: Make sure either to set the `static_files = true` in the .ini file or
- double check the root path for your http setup. It should point to
- for example:
- /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
-
-|
-
-:Q: **Can't install celery/rabbitmq**
-:A: Don't worry RhodeCode works without them too. No extra setup is required.
-
-|
-
-:Q: **Long lasting push timeouts?**
-:A: Make sure you set a longer timeouts in your proxy/fcgi settings, timeouts
- are caused by https server and not RhodeCode.
-
-|
-
-:Q: **Large pushes timeouts?**
-:A: Make sure you set a proper max_body_size for the http server.
-
-|
-
-:Q: **Apache doesn't pass basicAuth on pull/push?**
-:A: Make sure you added `WSGIPassAuthorization true`.
+Some example init.d scripts can be found in init.d directory::
-For further questions search the `Issues tracker`_, or post a message in the
-`google group rhodecode`_
+ https://secure.rhodecode.org/rhodecode/files/beta/init.d
.. _virtualenv: http://pypi.python.org/pypi/virtualenv
.. _python: http://www.python.org/
@@ -729,4 +712,4 @@ For further questions search the `Issues tracker`_, or post a message in the
.. _mercurial-server: http://www.lshift.net/mercurial-server.html
.. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
.. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
-.. _google group rhodecode: http://groups.google.com/group/rhodecode
+.. _google group rhodecode: http://groups.google.com/group/rhodecode \ No newline at end of file
diff --git a/docs/usage/general.rst b/docs/usage/general.rst
index 5978fdc1..e2991145 100644
--- a/docs/usage/general.rst
+++ b/docs/usage/general.rst
@@ -62,7 +62,6 @@ the _<ID> syntax can be used anywhere in the system so urls with repo_name
for changelogs, files and other can be exchanged with _<ID> syntax.
-
Mailing
-------
@@ -82,4 +81,27 @@ Trending source files
Trending source files are calculated based on pre defined dict of known
types and extensions. If You miss some extension or Would like to scan some
custom files it's possible to add new types in `LANGUAGES_EXTENSIONS_MAP` dict
-located in `/rhodecode/lib/celerylib/tasks.py` \ No newline at end of file
+located in `/rhodecode/lib/celerylib/tasks.py`
+
+
+Cloning remote repositories
+---------------------------
+
+RhodeCode has an ability to clone remote repos from given remote locations.
+Currently it support following options:
+
+- hg -> hg clone
+- svn -> hg clone
+- git -> git clone
+
+
+.. note::
+
+ - *`svn -> hg` cloning requires `hgsubversion` library to be installed.*
+
+If you need to clone repositories that are protected via basic auth, you
+might pass the url with stored credentials inside eg.
+`http://user:passw@remote.server/repo, RhodeCode will try to login and clone
+using given credentials. Please take a note that they will be stored as
+plaintext inside the database. RhodeCode will remove auth info when showing the
+clone url in summary page.
diff --git a/docs/usage/git_support.rst b/docs/usage/git_support.rst
index af523e85..19193a75 100644
--- a/docs/usage/git_support.rst
+++ b/docs/usage/git_support.rst
@@ -5,11 +5,13 @@ GIT support
===========
-Git support in RhodeCode 1.3 was enabled by default.
+Git support in RhodeCode 1.3 was enabled by default. You need to have a git
+client installed on the machine to make git fully work.
+
Although There are some limitations on git usage.
-- No hooks are runned for git push/pull actions.
-- logs in action journals don't have git operations
+- hooks that are executed on pull/push are not *real* hooks, they are
+ just emulating the behavior, and are executed **BEFORE** action takes place.
- large pushes needs http server with chunked encoding support.
if you plan to use git you need to run RhodeCode with some
@@ -17,14 +19,19 @@ http server that supports chunked encoding which git http protocol uses,
i recommend using waitress_ or gunicorn_ (linux only) for `paste` wsgi app
replacement.
-To use waitress simply change change the following in the .ini file::
+To use, simply change change the following in the .ini file::
use = egg:Paste#http
-To::
+to::
use = egg:waitress#main
+or::
+
+ use = egg:gunicorn#main
+
+
And comment out bellow options::
threadpool_workers =
diff --git a/docs/usage/locking.rst b/docs/usage/locking.rst
new file mode 100644
index 00000000..5ed4c359
--- /dev/null
+++ b/docs/usage/locking.rst
@@ -0,0 +1,41 @@
+.. _locking:
+
+===================================
+RhodeCode repository locking system
+===================================
+
+
+| Repos with **locking function=disabled** is the default, that's how repos work
+ today.
+| Repos with **locking function=enabled** behaves like follows:
+
+Repos have a state called `locked` that can be true or false.
+The hg/git commands `hg/git clone`, `hg/git pull`, and `hg/git push`
+influence this state:
+
+- The command `hg/git pull <repo>` will lock that repo (locked=true)
+ if the user has write/admin permissions on this repo
+
+- The command `hg/git clone <repo>` will lock that repo (locked=true) if the
+ user has write/admin permissions on this repo
+
+
+RhodeCode will remember the user id who locked the repo
+only this specific user can unlock the repo (locked=false) by calling
+
+- `hg/git push <repo>`
+
+every other command on that repo from this user and
+every command from any other user will result in http return code 423 (locked)
+
+
+additionally the http error includes the <user> that locked the repo
+(e.g. “repository <repo> locked by user <user>”)
+
+
+So the scenario of use for repos with `locking function` enabled is that
+every initial clone and every pull gives users (with write permission)
+the exclusive right to do a push.
+
+
+Each repo can be manually unlocked by admin from the repo settings menu. \ No newline at end of file
diff --git a/docs/usage/performance.rst b/docs/usage/performance.rst
new file mode 100644
index 00000000..364c58b6
--- /dev/null
+++ b/docs/usage/performance.rst
@@ -0,0 +1,50 @@
+.. _performance:
+
+================================
+Optimizing RhodeCode Performance
+================================
+
+When serving large amount of big repositories RhodeCode can start
+performing slower than expected. Because of demanding nature of handling large
+amount of data from version control systems here are some tips how to get
+the best performance.
+
+* RhodeCode will perform better on machines with faster disks (SSD/SAN). It's
+ more important to have faster disk than faster CPU.
+
+* Slowness on initial page can be easily fixed by grouping repositories, and/or
+ increasing cache size (see below)
+
+
+Follow these few steps to improve performance of RhodeCode system.
+
+
+1. Increase cache
+
+ in the .ini file::
+
+ beaker.cache.sql_cache_long.expire=3600 <-- set this to higher number
+
+ This option affects the cache expiration time for main page. Having
+ few hundreds of repositories on main page can sometimes make the system
+ to behave slow when cache expires for all of them. Increasing `expire`
+ option to day (86400) or a week (604800) will improve general response
+ times for the main page. RhodeCode has an intelligent cache expiration
+ system and it will expire cache for repositories that had been changed.
+
+2. Switch from sqlite to postgres or mysql
+
+ sqlite is a good option when having small load on the system. But due to
+ locking issues with sqlite, it's not recommended to use it for larger
+ setup. Switching to mysql or postgres will result in a immediate
+ performance increase.
+
+3. Scale RhodeCode horizontally
+
+ - running two or more instances on the same server can speed up things a lot
+ - load balance using round robin or ip hash
+ - you need to handle consistent user session storage by switching to
+ db sessions, client side sessions or sharing session data folder across
+ instances. See http://beaker.readthedocs.org/ docs for details.
+ - remember that each instance needs it's own .ini file and unique
+ `instance_id` set in them \ No newline at end of file
diff --git a/docs/usage/troubleshooting.rst b/docs/usage/troubleshooting.rst
new file mode 100644
index 00000000..c19d1ad4
--- /dev/null
+++ b/docs/usage/troubleshooting.rst
@@ -0,0 +1,70 @@
+.. _troubleshooting:
+
+
+===============
+Troubleshooting
+===============
+
+:Q: **Missing static files?**
+:A: Make sure either to set the `static_files = true` in the .ini file or
+ double check the root path for your http setup. It should point to
+ for example:
+ /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
+
+|
+
+:Q: **Can't install celery/rabbitmq?**
+:A: Don't worry RhodeCode works without them too. No extra setup is required.
+ Try out great celery docs for further help.
+
+|
+
+:Q: **Long lasting push timeouts?**
+:A: Make sure you set a longer timeouts in your proxy/fcgi settings, timeouts
+ are caused by https server and not RhodeCode.
+
+|
+
+:Q: **Large pushes timeouts?**
+:A: Make sure you set a proper max_body_size for the http server. Very often
+ Apache, Nginx or other http servers kill the connection due to to large
+ body.
+
+|
+
+:Q: **Apache doesn't pass basicAuth on pull/push?**
+:A: Make sure you added `WSGIPassAuthorization true`.
+
+|
+
+:Q: **Git fails on push/pull?**
+:A: Make sure you're using an wsgi http server that can handle chunked encoding
+ such as `waitress` or `gunicorn`
+
+|
+
+:Q: **How i use hooks in RhodeCode?**
+:A: It's easy if they are python hooks just use advanced link in hooks section
+ in Admin panel, that works only for Mercurial. If you want to use githooks,
+ just install proper one in repository eg. create file in
+ `/gitrepo/hooks/pre-receive`. You can also use RhodeCode-extensions to
+ connect to callback hooks, for both Git and Mercurial.
+
+|
+
+:Q: **RhodeCode is slow for me, how can i make it faster?**
+:A: See the :ref:`performance` section
+
+For further questions search the `Issues tracker`_, or post a message in the
+`google group rhodecode`_
+
+.. _virtualenv: http://pypi.python.org/pypi/virtualenv
+.. _python: http://www.python.org/
+.. _mercurial: http://mercurial.selenic.com/
+.. _celery: http://celeryproject.org/
+.. _rabbitmq: http://www.rabbitmq.com/
+.. _python-ldap: http://www.python-ldap.org/
+.. _mercurial-server: http://www.lshift.net/mercurial-server.html
+.. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
+.. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
+.. _google group rhodecode: http://groups.google.com/group/rhodecode \ No newline at end of file
diff --git a/init.d/supervisord.conf b/init.d/supervisord.conf
new file mode 100644
index 00000000..99bbec9b
--- /dev/null
+++ b/init.d/supervisord.conf
@@ -0,0 +1,51 @@
+; RhodeCode Supervisord
+; ##########################
+; for help see http://supervisord.org/configuration.html
+; ##########################
+
+[inet_http_server] ; inet (TCP) server disabled by default
+port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface)
+;username=user ; (default is no username (open server))
+;password=123 ; (default is no password (open server))
+
+[supervisord]
+logfile=/%(here)s/supervisord_rhodecode.log ; (main log file;default $CWD/supervisord.log)
+logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
+logfile_backups=10 ; (num of main logfile rotation backups;default 10)
+loglevel=info ; (log level;default info; others: debug,warn,trace)
+pidfile=/%(here)s/supervisord_rhodecode.pid ; (supervisord pidfile;default supervisord.pid)
+nodaemon=true ; (start in foreground if true;default false)
+minfds=1024 ; (min. avail startup file descriptors;default 1024)
+minprocs=200 ; (min. avail process descriptors;default 200)
+umask=022 ; (process file creation umask;default 022)
+user=marcink ; (default is current user, required if root)
+;identifier=supervisor ; (supervisord identifier, default is 'supervisor')
+;directory=/tmp ; (default is not to cd during start)
+;nocleanup=true ; (don't clean up tempfiles at start;default false)
+;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP)
+environment=HOME=/home/marcink ; (key value pairs to add to environment)
+;strip_ansi=false ; (strip ansi escape codes in logs; def. false)
+
+; the below section must remain in the config file for RPC
+; (supervisorctl/web interface) to work, additional interfaces may be
+; added by defining them in separate rpcinterface: sections
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+[supervisorctl]
+serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
+;username=user ; should be same as http_username if set
+;password=123 ; should be same as http_password if set
+;prompt=mysupervisor ; cmd line prompt (default "supervisor")
+;history_file=~/.sc_history ; use readline history if available
+
+
+; restart with supervisorctl restart rhodecode:*
+[program:rhodecode]
+numprocs = 1
+numprocs_start = 5000 # possible should match ports
+directory=/home/marcink/rhodecode-dir
+command = /home/marcink/v-env/bin/paster serve rc.ini
+process_name = %(program_name)s_%(process_num)04d
+redirect_stderr=true
+stdout_logfile=/%(here)s/rhodecode.log \ No newline at end of file
diff --git a/production.ini b/production.ini
index b127c784..45811e71 100644
--- a/production.ini
+++ b/production.ini
@@ -30,22 +30,31 @@ pdebug = false
[server:main]
##nr of threads to spawn
-threadpool_workers = 5
+#threadpool_workers = 5
##max request before thread respawn
-threadpool_max_requests = 10
+#threadpool_max_requests = 10
##option to use threads of process
-use_threadpool = true
+#use_threadpool = true
-use = egg:Paste#http
+#use = egg:Paste#http
+use = egg:waitress#main
host = 127.0.0.1
port = 8001
+[filter:proxy-prefix]
+# prefix middleware for rc
+use = egg:PasteDeploy#prefix
+prefix = /<your-prefix>
+
[app:main]
use = egg:rhodecode
+#filter-with = proxy-prefix
full_stack = true
static_files = true
+# Optional Languages
+# en, fr, ja, pt_BR, zh_CN, zh_TW
lang = en
cache_dir = %(here)s/data
index_dir = %(here)s/data/index
@@ -54,6 +63,15 @@ cut_off_limit = 256000
force_https = false
commit_parse_limit = 50
use_gravatar = true
+
+## alternative_gravatar_url allows you to use your own avatar server application
+## the following parts of the URL will be replaced
+## {email} user email
+## {md5email} md5 hash of the user email (like at gravatar.com)
+## {size} size of the image that is expected from the server application
+#alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
+#alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
+
container_auth_enabled = false
proxypass_auth_enabled = false
default_encoding = utf8
@@ -78,7 +96,8 @@ default_encoding = utf8
issue_pat = (?:\s*#)(\d+)
## server url to the issue, each {id} will be replaced with match
-## fetched from the regex and {repo} is replaced with repository name
+## fetched from the regex and {repo} is replaced with full repository name
+## including groups {repo_name} is replaced with just name of repo
issue_server_link = https://myissueserver.com/{repo}/issue/{id}
@@ -165,30 +184,34 @@ beaker.cache.sql_cache_long.key_length = 256
## The storage uses the Container API
## that is also used by the cache system.
-## db session example
-
+## db session ##
#beaker.session.type = ext:database
#beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
#beaker.session.table_name = db_session
-## encrypted cookie session, good for many instances
+## encrypted cookie client side session, good for many instances ##
#beaker.session.type = cookie
-beaker.session.type = file
+## file based cookies (default) ##
+#beaker.session.type = file
+
+
beaker.session.key = rhodecode
-# secure cookie requires AES python libraries
+## secure cookie requires AES python libraries ##
#beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
#beaker.session.validate_key = 9712sds2212c--zxc123
-beaker.session.timeout = 36000
+## sets session as invalid if it haven't been accessed for given amount of time
+beaker.session.timeout = 2592000
beaker.session.httponly = true
+#beaker.session.cookie_path = /<your-prefix>
-## uncomment for https secure cookie
+## uncomment for https secure cookie ##
beaker.session.secure = false
-##auto save the session to not to use .save()
+## auto save the session to not to use .save() ##
beaker.session.auto = False
-##true exire at browser close
+## default cookie expiration time in seconds `true` expire at browser close ##
#beaker.session.cookie_expires = 3600
diff --git a/requires.txt b/requires.txt
index e223f8c7..0cad4ca5 100644
--- a/requires.txt
+++ b/requires.txt
@@ -1,18 +1,20 @@
+waitress==0.8.1
+webob==1.0.8
Pylons==1.0.0
-Beaker==1.6.3
+Beaker==1.6.4
WebHelpers==1.3
formencode==1.2.4
-SQLAlchemy==0.7.6
-Mako==0.7.0
-pygments>=1.4
+SQLAlchemy==0.7.8
+Mako==0.7.2
+pygments>=1.5
whoosh>=2.4.0,<2.5
celery>=2.2.5,<2.3
babel
python-dateutil>=1.5.0,<2.0.0
dulwich>=0.8.5,<0.9.0
-webob==1.0.8
markdown==2.1.1
docutils==0.8.1
simplejson==2.5.2
+mock
py-bcrypt
-mercurial>=2.2.1,<2.3 \ No newline at end of file
+mercurial>=2.3.0,<2.4 \ No newline at end of file
diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py
index 209c0b39..2f5a1067 100644
--- a/rhodecode/__init__.py
+++ b/rhodecode/__init__.py
@@ -26,7 +26,7 @@
import sys
import platform
-VERSION = (1, 3, 6)
+VERSION = (1, 4, 0)
try:
from rhodecode.lib import get_current_revision
@@ -38,50 +38,19 @@ except ImportError:
__version__ = ('.'.join((str(each) for each in VERSION[:3])) +
'.'.join(VERSION[3:]))
-__dbversion__ = 5 # defines current db version for migrations
+__dbversion__ = 6 # defines current db version for migrations
__platform__ = platform.system()
__license__ = 'GPLv3'
__py_version__ = sys.version_info
+__author__ = 'Marcin Kuzminski'
+__url__ = 'http://rhodecode.org'
PLATFORM_WIN = ('Windows')
-PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
+PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS') #depracated
is_windows = __platform__ in PLATFORM_WIN
-is_unix = __platform__ in PLATFORM_OTHERS
+is_unix = not is_windows
-requirements = [
- "Pylons==1.0.0",
- "Beaker==1.6.3",
- "WebHelpers==1.3",
- "formencode==1.2.4",
- "SQLAlchemy==0.7.6",
- "Mako==0.7.0",
- "pygments>=1.4",
- "whoosh>=2.4.0,<2.5",
- "celery>=2.2.5,<2.3",
- "babel",
- "python-dateutil>=1.5.0,<2.0.0",
- "dulwich>=0.8.5,<0.9.0",
- "webob==1.0.8",
- "markdown==2.1.1",
- "docutils==0.8.1",
- "simplejson==2.5.2",
-]
-
-if __py_version__ < (2, 6):
- requirements.append("pysqlite")
-
-if is_windows:
- requirements.append("mercurial>=2.2.1,<2.3")
-else:
- requirements.append("py-bcrypt")
- requirements.append("mercurial>=2.2.1,<2.3")
-
-
-def get_version():
- """Returns shorter version (digit parts only) as string."""
-
- return '.'.join((str(each) for each in VERSION[:3]))
BACKENDS = {
'hg': 'Mercurial repository',
diff --git a/rhodecode/bin/__init__.py b/rhodecode/bin/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rhodecode/bin/__init__.py
diff --git a/rhodecode/bin/rhodecode_api.py b/rhodecode/bin/rhodecode_api.py
new file mode 100755
index 00000000..9a15d33a
--- /dev/null
+++ b/rhodecode/bin/rhodecode_api.py
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.bin.backup_manager
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Api CLI client for RhodeCode
+
+ :created_on: Jun 3, 2012
+ :author: marcink
+ :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+from __future__ import with_statement
+import os
+import sys
+import random
+import urllib2
+import pprint
+import argparse
+
+try:
+ from rhodecode.lib.ext_json import json
+except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ import json
+
+
+CONFIG_NAME = '.rhodecode'
+FORMAT_PRETTY = 'pretty'
+FORMAT_JSON = 'json'
+
+
+class RcConf(object):
+ """
+ RhodeCode config for API
+
+ conf = RcConf()
+ conf['key']
+
+ """
+
+ def __init__(self, config_location=None, autoload=True, autocreate=False,
+ config=None):
+ self._conf_name = CONFIG_NAME if not config_location else config_location
+ self._conf = {}
+ if autocreate:
+ self.make_config(config)
+ if autoload:
+ self._conf = self.load_config()
+
+ def __getitem__(self, key):
+ return self._conf[key]
+
+ def __nonzero__(self):
+ if self._conf:
+ return True
+ return False
+
+ def __eq__(self):
+ return self._conf.__eq__()
+
+ def __repr__(self):
+ return 'RcConf<%s>' % self._conf.__repr__()
+
+ def make_config(self, config):
+ """
+ Saves given config as a JSON dump in the _conf_name location
+
+ :param config:
+ :type config:
+ """
+ update = False
+ if os.path.exists(self._conf_name):
+ update = True
+ with open(self._conf_name, 'wb') as f:
+ json.dump(config, f, indent=4)
+
+ if update:
+ sys.stdout.write('Updated config in %s\n' % self._conf_name)
+ else:
+ sys.stdout.write('Created new config in %s\n' % self._conf_name)
+
+ def update_config(self, new_config):
+ """
+ Reads the JSON config updates it's values with new_config and
+ saves it back as JSON dump
+
+ :param new_config:
+ """
+ config = {}
+ try:
+ with open(self._conf_name, 'rb') as conf:
+ config = json.load(conf)
+ except IOError, e:
+ sys.stderr.write(str(e) + '\n')
+
+ config.update(new_config)
+ self.make_config(config)
+
+ def load_config(self):
+ """
+ Loads config from file and returns loaded JSON object
+ """
+ try:
+ with open(self._conf_name, 'rb') as conf:
+ return json.load(conf)
+ except IOError, e:
+ #sys.stderr.write(str(e) + '\n')
+ pass
+
+
+def api_call(apikey, apihost, format, method=None, **kw):
+ """
+ Api_call wrapper for RhodeCode
+
+ :param apikey:
+ :param apihost:
+ :param format: formatting, pretty means prints and pprint of json
+ json returns unparsed json
+ :param method:
+ """
+ def _build_data(random_id):
+ """
+ Builds API data with given random ID
+
+ :param random_id:
+ :type random_id:
+ """
+ return {
+ "id": random_id,
+ "api_key": apikey,
+ "method": method,
+ "args": kw
+ }
+
+ if not method:
+ raise Exception('please specify method name !')
+ id_ = random.randrange(1, 9999)
+ req = urllib2.Request('%s/_admin/api' % apihost,
+ data=json.dumps(_build_data(id_)),
+ headers={'content-type': 'text/plain'})
+ if format == FORMAT_PRETTY:
+ sys.stdout.write('calling %s to %s \n' % (req.get_data(), apihost))
+ ret = urllib2.urlopen(req)
+ raw_json = ret.read()
+ json_data = json.loads(raw_json)
+ id_ret = json_data['id']
+ _formatted_json = pprint.pformat(json_data)
+ if id_ret == id_:
+ if format == FORMAT_JSON:
+ sys.stdout.write(str(raw_json))
+ else:
+ sys.stdout.write('rhodecode returned:\n%s\n' % (_formatted_json))
+
+ else:
+ raise Exception('something went wrong. '
+ 'ID mismatch got %s, expected %s | %s' % (
+ id_ret, id_, _formatted_json))
+
+
+def argparser(argv):
+ usage = (
+ "rhodecode_api [-h] [--format=FORMAT] [--apikey=APIKEY] [--apihost=APIHOST] "
+ " [--config=CONFIG] "
+ "_create_config or METHOD <key:val> <key2:val> ..."
+ )
+
+ parser = argparse.ArgumentParser(description='RhodeCode API cli',
+ usage=usage)
+
+ ## config
+ group = parser.add_argument_group('config')
+ group.add_argument('--apikey', help='api access key')
+ group.add_argument('--apihost', help='api host')
+ group.add_argument('--config', help='config file')
+
+ group = parser.add_argument_group('API')
+ group.add_argument('method', metavar='METHOD', type=str,
+ help='API method name to call followed by key:value attributes',
+ )
+ group.add_argument('--format', dest='format', type=str,
+ help='output format default: `pretty` can '
+ 'be also `%s`' % FORMAT_JSON,
+ default=FORMAT_PRETTY
+ )
+ args, other = parser.parse_known_args()
+ return parser, args, other
+
+
+def main(argv=None):
+ """
+ Main execution function for cli
+
+ :param argv:
+ :type argv:
+ """
+ if argv is None:
+ argv = sys.argv
+
+ conf = None
+ parser, args, other = argparser(argv)
+
+ api_credentials_given = (args.apikey and args.apihost)
+ if args.method == '_create_config':
+ if not api_credentials_given:
+ raise parser.error('_create_config requires --apikey and --apihost')
+ conf = RcConf(config_location=args.config,
+ autocreate=True, config={'apikey': args.apikey,
+ 'apihost': args.apihost})
+
+ if not conf:
+ conf = RcConf(config_location=args.config, autoload=True)
+ if not conf:
+ if not api_credentials_given:
+ parser.error('Could not find config file and missing '
+ '--apikey or --apihost in params')
+
+ apikey = args.apikey or conf['apikey']
+ host = args.apihost or conf['apihost']
+ method = args.method
+ if method == '_create_config':
+ sys.exit()
+
+ try:
+ margs = dict(map(lambda s: s.split(':', 1), other))
+ except:
+ sys.stderr.write('Error parsing arguments \n')
+ sys.exit()
+
+ api_call(apikey, host, args.format, method, **margs)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/rhodecode/lib/backup_manager.py b/rhodecode/bin/rhodecode_backup.py
index 65cc8e38..1b3188c7 100644..100755
--- a/rhodecode/lib/backup_manager.py
+++ b/rhodecode/bin/rhodecode_backup.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""
- rhodecode.lib.backup_manager
+ rhodecode.bin.backup_manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Mercurial repositories backup manager, it allows to backups all
+ Repositories backup manager, it allows to backups all
repositories and send it to backup server using RSA key via ssh.
:created_on: Feb 28, 2010
@@ -39,7 +39,7 @@ logging.basicConfig(level=logging.DEBUG,
class BackupManager(object):
def __init__(self, repos_location, rsa_key, backup_server):
today = datetime.datetime.now().weekday() + 1
- self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
+ self.backup_file_name = "rhodecode_repos.%s.tar.gz" % today
self.id_rsa_path = self.get_id_rsa(rsa_key)
self.repos_path = self.get_repos_path(repos_location)
diff --git a/rhodecode/config/deployment.ini_tmpl b/rhodecode/config/deployment.ini_tmpl
index 057951f3..33befff4 100644
--- a/rhodecode/config/deployment.ini_tmpl
+++ b/rhodecode/config/deployment.ini_tmpl
@@ -30,22 +30,31 @@ pdebug = false
[server:main]
##nr of threads to spawn
-threadpool_workers = 5
+#threadpool_workers = 5
##max request before thread respawn
-threadpool_max_requests = 10
+#threadpool_max_requests = 10
##option to use threads of process
-use_threadpool = true
+#use_threadpool = true
-use = egg:Paste#http
+#use = egg:Paste#http
+use = egg:waitress#main
host = 127.0.0.1
port = 5000
+[filter:proxy-prefix]
+# prefix middleware for rc
+use = egg:PasteDeploy#prefix
+prefix = /<your-prefix>
+
[app:main]
use = egg:rhodecode
+#filter-with = proxy-prefix
full_stack = true
static_files = true
+# Optional Languages
+# en, fr, ja, pt_BR, zh_CN, zh_TW
lang = en
cache_dir = %(here)s/data
index_dir = %(here)s/data/index
@@ -54,6 +63,15 @@ cut_off_limit = 256000
force_https = false
commit_parse_limit = 50
use_gravatar = true
+
+## alternative_gravatar_url allows you to use your own avatar server application
+## the following parts of the URL will be replaced
+## {email} user email
+## {md5email} md5 hash of the user email (like at gravatar.com)
+## {size} size of the image that is expected from the server application
+#alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
+#alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
+
container_auth_enabled = false
proxypass_auth_enabled = false
default_encoding = utf8
@@ -78,7 +96,8 @@ default_encoding = utf8
issue_pat = (?:\s*#)(\d+)
## server url to the issue, each {id} will be replaced with match
-## fetched from the regex and {repo} is replaced with repository name
+## fetched from the regex and {repo} is replaced with full repository name
+## including groups {repo_name} is replaced with just name of repo
issue_server_link = https://myissueserver.com/{repo}/issue/{id}
@@ -165,30 +184,34 @@ beaker.cache.sql_cache_long.key_length = 256
## The storage uses the Container API
## that is also used by the cache system.
-## db session example
-
+## db session ##
#beaker.session.type = ext:database
#beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
#beaker.session.table_name = db_session
-## encrypted cookie session, good for many instances
+## encrypted cookie client side session, good for many instances ##
#beaker.session.type = cookie
-beaker.session.type = file
+## file based cookies (default) ##
+#beaker.session.type = file
+
+
beaker.session.key = rhodecode
-# secure cookie requires AES python libraries
-#beaker.session.encrypt_key = ${app_instance_secret}
-#beaker.session.validate_key = ${app_instance_secret}
-beaker.session.timeout = 36000
+## secure cookie requires AES python libraries ##
+#beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
+#beaker.session.validate_key = 9712sds2212c--zxc123
+## sets session as invalid if it haven't been accessed for given amount of time
+beaker.session.timeout = 2592000
beaker.session.httponly = true
+#beaker.session.cookie_path = /<your-prefix>
-## uncomment for https secure cookie
+## uncomment for https secure cookie ##
beaker.session.secure = false
-##auto save the session to not to use .save()
+## auto save the session to not to use .save() ##
beaker.session.auto = False
-##true exire at browser close
+## default cookie expiration time in seconds `true` expire at browser close ##
#beaker.session.cookie_expires = 3600
diff --git a/rhodecode/config/environment.py b/rhodecode/config/environment.py
index fb85005d..2bbeaa3f 100644
--- a/rhodecode/config/environment.py
+++ b/rhodecode/config/environment.py
@@ -72,19 +72,28 @@ def load_environment(global_conf, app_conf, initial=False):
config['pylons.strict_tmpl_context'] = True
test = os.path.split(config['__file__'])[-1] == 'test.ini'
if test:
+ if os.environ.get('TEST_DB'):
+ # swap config if we pass enviroment variable
+ config['sqlalchemy.db1.url'] = os.environ.get('TEST_DB')
+
from rhodecode.lib.utils import create_test_env, create_test_index
from rhodecode.tests import TESTS_TMP_PATH
- create_test_env(TESTS_TMP_PATH, config)
- create_test_index(TESTS_TMP_PATH, config, True)
+ # set RC_NO_TMP_PATH=1 to disable re-creating the database and
+ # test repos
+ if not int(os.environ.get('RC_NO_TMP_PATH', 0)):
+ create_test_env(TESTS_TMP_PATH, config)
+ # set RC_WHOOSH_TEST_DISABLE=1 to disable whoosh index during tests
+ if not int(os.environ.get('RC_WHOOSH_TEST_DISABLE', 0)):
+ create_test_index(TESTS_TMP_PATH, config, True)
# MULTIPLE DB configs
# Setup the SQLAlchemy database engine
sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
-
init_model(sa_engine_db1)
repos_path = make_ui('db').configitems('paths')[0][1]
- repo2db_mapper(ScmModel().repo_scan(repos_path))
+ repo2db_mapper(ScmModel().repo_scan(repos_path),
+ remove_obsolete=False, install_git_hook=False)
set_available_permissions(config)
config['base_path'] = repos_path
set_rhodecode_config(config)
diff --git a/rhodecode/config/post_receive_tmpl.py b/rhodecode/config/post_receive_tmpl.py
new file mode 100644
index 00000000..d246c78c
--- /dev/null
+++ b/rhodecode/config/post_receive_tmpl.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+import os
+import sys
+
+try:
+ import rhodecode
+ RC_HOOK_VER = '_TMPL_'
+ os.environ['RC_HOOK_VER'] = RC_HOOK_VER
+ from rhodecode.lib.hooks import handle_git_post_receive
+except ImportError:
+ rhodecode = None
+
+
+def main():
+ if rhodecode is None:
+ # exit with success if we cannot import rhodecode !!
+ # this allows simply push to this repo even without
+ # rhodecode
+ sys.exit(0)
+
+ repo_path = os.path.abspath('.')
+ push_data = sys.stdin.readlines()
+ # os.environ is modified here by a subprocess call that
+ # runs git and later git executes this hook.
+ # Environ get's some additional info from rhodecode system
+ # like IP or username from basic-auth
+ handle_git_post_receive(repo_path, push_data, os.environ)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/rhodecode/config/pre_receive_tmpl.py b/rhodecode/config/pre_receive_tmpl.py
new file mode 100644
index 00000000..e306367d
--- /dev/null
+++ b/rhodecode/config/pre_receive_tmpl.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+import os
+import sys
+
+try:
+ import rhodecode
+ RC_HOOK_VER = '_TMPL_'
+ os.environ['RC_HOOK_VER'] = RC_HOOK_VER
+ from rhodecode.lib.hooks import handle_git_pre_receive
+except ImportError:
+ rhodecode = None
+
+
+def main():
+ if rhodecode is None:
+ # exit with success if we cannot import rhodecode !!
+ # this allows simply push to this repo even without
+ # rhodecode
+ sys.exit(0)
+
+ repo_path = os.path.abspath('.')
+ push_data = sys.stdin.readlines()
+ # os.environ is modified here by a subprocess call that
+ # runs git and later git executes this hook.
+ # Environ get's some additional info from rhodecode system
+ # like IP or username from basic-auth
+ handle_git_pre_receive(repo_path, push_data, os.environ)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/rhodecode/config/rcextensions/__init__.py b/rhodecode/config/rcextensions/__init__.py
index 96572626..7c79fe68 100644
--- a/rhodecode/config/rcextensions/__init__.py
+++ b/rhodecode/config/rcextensions/__init__.py
@@ -1,6 +1,7 @@
# Additional mappings that are not present in the pygments lexers
# used for building stats
-# format is {'ext':'Name'} eg. {'py':'Python'}
+# format is {'ext':['Names']} eg. {'py':['Python']} note: there can be
+# more than one name for extension
# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
# build by pygments
EXTRA_MAPPINGS = {}
@@ -39,6 +40,7 @@ def _crhook(*args, **kwargs):
:param group_id:
:param created_by:
"""
+
return 0
CREATE_REPO_HOOK = _crhook
diff --git a/rhodecode/config/rcextensions/make_rcextensions.py b/rhodecode/config/rcextensions/make_rcextensions.py
index af8839f1..09a86209 100644
--- a/rhodecode/config/rcextensions/make_rcextensions.py
+++ b/rhodecode/config/rcextensions/make_rcextensions.py
@@ -54,7 +54,7 @@ class MakeRcExt(BasePasterCommand):
logging.config.fileConfig(self.path_to_ini_file)
from pylons import config
- def _make_file(ext_file):
+ def _make_file(ext_file, tmpl):
bdir = os.path.split(ext_file)[0]
if not os.path.isdir(bdir):
os.makedirs(bdir)
@@ -71,11 +71,11 @@ class MakeRcExt(BasePasterCommand):
msg = ('Extension file already exists, do you want '
'to overwrite it ? [y/n]')
if ask_ok(msg):
- _make_file(ext_file)
+ _make_file(ext_file, tmpl)
else:
log.info('nothing done...')
else:
- _make_file(ext_file)
+ _make_file(ext_file, tmpl)
def update_parser(self):
pass
diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py
index f3cc8d86..a3e7f29d 100644
--- a/rhodecode/config/routing.py
+++ b/rhodecode/config/routing.py
@@ -69,7 +69,7 @@ def make_map(config):
rmap.connect('home', '/', controller='home', action='index')
rmap.connect('repo_switcher', '/repos', controller='home',
action='repo_switcher')
- rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
+ rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
controller='home', action='branch_tag_switcher')
rmap.connect('bugtracker',
"http://bitbucket.org/marcinkuzminski/rhodecode/issues",
@@ -93,52 +93,54 @@ def make_map(config):
action="new", conditions=dict(method=["GET"]))
m.connect("formatted_new_repo", "/repos/new.{format}",
action="new", conditions=dict(method=["GET"]))
- m.connect("/repos/{repo_name:.*}",
+ m.connect("/repos/{repo_name:.*?}",
action="update", conditions=dict(method=["PUT"],
function=check_repo))
- m.connect("/repos/{repo_name:.*}",
+ m.connect("/repos/{repo_name:.*?}",
action="delete", conditions=dict(method=["DELETE"],
function=check_repo))
- m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
+ m.connect("edit_repo", "/repos/{repo_name:.*?}/edit",
action="edit", conditions=dict(method=["GET"],
function=check_repo))
- m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
+ m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
action="edit", conditions=dict(method=["GET"],
function=check_repo))
- m.connect("repo", "/repos/{repo_name:.*}",
+ m.connect("repo", "/repos/{repo_name:.*?}",
action="show", conditions=dict(method=["GET"],
function=check_repo))
- m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
+ m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
action="show", conditions=dict(method=["GET"],
function=check_repo))
#ajax delete repo perm user
- m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
+ m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}",
action="delete_perm_user",
conditions=dict(method=["DELETE"], function=check_repo))
#ajax delete repo perm users_group
m.connect('delete_repo_users_group',
- "/repos_delete_users_group/{repo_name:.*}",
+ "/repos_delete_users_group/{repo_name:.*?}",
action="delete_perm_users_group",
conditions=dict(method=["DELETE"], function=check_repo))
#settings actions
- m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
+ m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
action="repo_stats", conditions=dict(method=["DELETE"],
function=check_repo))
- m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
+ m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
action="repo_cache", conditions=dict(method=["DELETE"],
function=check_repo))
- m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
+ m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
action="repo_public_journal", conditions=dict(method=["PUT"],
function=check_repo))
- m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
+ m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
action="repo_pull", conditions=dict(method=["PUT"],
function=check_repo))
- m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
+ m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
action="repo_as_fork", conditions=dict(method=["PUT"],
function=check_repo))
-
+ m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
+ action="repo_locking", conditions=dict(method=["PUT"],
+ function=check_repo))
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/repos_groups') as m:
m.connect("repos_groups", "/repos_groups",
@@ -157,9 +159,8 @@ def make_map(config):
m.connect("delete_repos_group", "/repos_groups/{id}",
action="delete", conditions=dict(method=["DELETE"],
function=check_int))
- m.connect("edit_repos_group", "/repos_groups/{id}/edit",
- action="edit", conditions=dict(method=["GET"],
- function=check_int))
+ m.connect("edit_repos_group", "/repos_groups/{id:.*?}/edit",
+ action="edit", conditions=dict(method=["GET"],))
m.connect("formatted_edit_repos_group",
"/repos_groups/{id}.{format}/edit",
action="edit", conditions=dict(method=["GET"],
@@ -212,8 +213,12 @@ def make_map(config):
#EXTRAS USER ROUTES
m.connect("user_perm", "/users_perm/{id}",
action="update_perm", conditions=dict(method=["PUT"]))
+ m.connect("user_emails", "/users_emails/{id}",
+ action="add_email", conditions=dict(method=["PUT"]))
+ m.connect("user_emails_delete", "/users_emails/{id}",
+ action="delete_email", conditions=dict(method=["DELETE"]))
- #ADMIN USERS REST ROUTES
+ #ADMIN USERS GROUPS REST ROUTES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/users_groups') as m:
m.connect("users_groups", "/users_groups",
@@ -292,6 +297,10 @@ def make_map(config):
action="my_account_update", conditions=dict(method=["PUT"]))
m.connect("admin_settings_create_repository", "/create_repository",
action="create_repository", conditions=dict(method=["GET"]))
+ m.connect("admin_settings_my_repos", "/my_account/repos",
+ action="my_account_my_repos", conditions=dict(method=["GET"]))
+ m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
+ action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
#NOTIFICATION REST ROUTES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
@@ -337,15 +346,27 @@ def make_map(config):
m.connect('api', '/api')
#USER JOURNAL
- rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
+ rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
+ controller='journal', action='index')
+ rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
+ controller='journal', action='journal_rss')
+ rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
+ controller='journal', action='journal_atom')
rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
controller='journal', action="public_journal")
- rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
+ rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
+ controller='journal', action="public_journal_rss")
+
+ rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
controller='journal', action="public_journal_rss")
rmap.connect('public_journal_atom',
+ '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
+ action="public_journal_atom")
+
+ rmap.connect('public_journal_atom_old',
'%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
action="public_journal_atom")
@@ -374,18 +395,18 @@ def make_map(config):
controller='login', action='password_reset_confirmation')
#FEEDS
- rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
+ rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
controller='feed', action='rss',
conditions=dict(function=check_repo))
- rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
+ rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
controller='feed', action='atom',
conditions=dict(function=check_repo))
#==========================================================================
# REPOSITORY ROUTES
#==========================================================================
- rmap.connect('summary_home', '/{repo_name:.*}',
+ rmap.connect('summary_home', '/{repo_name:.*?}',
controller='summary',
conditions=dict(function=check_repo))
@@ -393,114 +414,166 @@ def make_map(config):
controller='admin/repos_groups', action="show_by_name",
conditions=dict(function=check_group))
- rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
+ rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
controller='changeset', revision='tip',
conditions=dict(function=check_repo))
rmap.connect('changeset_comment',
- '/{repo_name:.*}/changeset/{revision}/comment',
+ '/{repo_name:.*?}/changeset/{revision}/comment',
controller='changeset', revision='tip', action='comment',
conditions=dict(function=check_repo))
rmap.connect('changeset_comment_delete',
- '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
+ '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
controller='changeset', action='delete_comment',
conditions=dict(function=check_repo, method=["DELETE"]))
rmap.connect('raw_changeset_home',
- '/{repo_name:.*}/raw-changeset/{revision}',
+ '/{repo_name:.*?}/raw-changeset/{revision}',
controller='changeset', action='raw_changeset',
revision='tip', conditions=dict(function=check_repo))
- rmap.connect('summary_home', '/{repo_name:.*}/summary',
+ rmap.connect('compare_url',
+ '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref}...{other_ref_type}@{other_ref}',
+ controller='compare', action='index',
+ conditions=dict(function=check_repo),
+ requirements=dict(
+ org_ref_type='(branch|book|tag|rev|org_ref_type)',
+ other_ref_type='(branch|book|tag|rev|other_ref_type)')
+ )
+
+ rmap.connect('pullrequest_home',
+ '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
+ action='index', conditions=dict(function=check_repo,
+ method=["GET"]))
+
+ rmap.connect('pullrequest',
+ '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
+ action='create', conditions=dict(function=check_repo,
+ method=["POST"]))
+
+ rmap.connect('pullrequest_show',
+ '/{repo_name:.*?}/pull-request/{pull_request_id}',
+ controller='pullrequests',
+ action='show', conditions=dict(function=check_repo,
+ method=["GET"]))
+ rmap.connect('pullrequest_update',
+ '/{repo_name:.*?}/pull-request/{pull_request_id}',
+ controller='pullrequests',
+ action='update', conditions=dict(function=check_repo,
+ method=["PUT"]))
+ rmap.connect('pullrequest_delete',
+ '/{repo_name:.*?}/pull-request/{pull_request_id}',
+ controller='pullrequests',
+ action='delete', conditions=dict(function=check_repo,
+ method=["DELETE"]))
+
+ rmap.connect('pullrequest_show_all',
+ '/{repo_name:.*?}/pull-request',
+ controller='pullrequests',
+ action='show_all', conditions=dict(function=check_repo,
+ method=["GET"]))
+
+ rmap.connect('pullrequest_comment',
+ '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
+ controller='pullrequests',
+ action='comment', conditions=dict(function=check_repo,
+ method=["POST"]))
+
+ rmap.connect('pullrequest_comment_delete',
+ '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
+ controller='pullrequests', action='delete_comment',
+ conditions=dict(function=check_repo, method=["DELETE"]))
+
+ rmap.connect('summary_home', '/{repo_name:.*?}/summary',
controller='summary', conditions=dict(function=check_repo))
- rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
+ rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
controller='shortlog', conditions=dict(function=check_repo))
- rmap.connect('branches_home', '/{repo_name:.*}/branches',
+ rmap.connect('branches_home', '/{repo_name:.*?}/branches',
controller='branches', conditions=dict(function=check_repo))
- rmap.connect('tags_home', '/{repo_name:.*}/tags',
+ rmap.connect('tags_home', '/{repo_name:.*?}/tags',
controller='tags', conditions=dict(function=check_repo))
- rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
+ rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
controller='bookmarks', conditions=dict(function=check_repo))
- rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
+ rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
controller='changelog', conditions=dict(function=check_repo))
- rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
+ rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
controller='changelog', action='changelog_details',
conditions=dict(function=check_repo))
- rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
+ rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
controller='files', revision='tip', f_path='',
conditions=dict(function=check_repo))
- rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
+ rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
controller='files', action='diff', revision='tip', f_path='',
conditions=dict(function=check_repo))
rmap.connect('files_rawfile_home',
- '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
+ '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
controller='files', action='rawfile', revision='tip',
f_path='', conditions=dict(function=check_repo))
rmap.connect('files_raw_home',
- '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
+ '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
controller='files', action='raw', revision='tip', f_path='',
conditions=dict(function=check_repo))
rmap.connect('files_annotate_home',
- '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
+ '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
controller='files', action='index', revision='tip',
f_path='', annotate=True, conditions=dict(function=check_repo))
rmap.connect('files_edit_home',
- '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
+ '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
controller='files', action='edit', revision='tip',
f_path='', conditions=dict(function=check_repo))
rmap.connect('files_add_home',
- '/{repo_name:.*}/add/{revision}/{f_path:.*}',
+ '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
controller='files', action='add', revision='tip',
f_path='', conditions=dict(function=check_repo))
- rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
+ rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
controller='files', action='archivefile',
conditions=dict(function=check_repo))
rmap.connect('files_nodelist_home',
- '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
+ '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
controller='files', action='nodelist',
conditions=dict(function=check_repo))
- rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
+ rmap.connect('repo_settings_delete', '/{repo_name:.*?}/settings',
controller='settings', action="delete",
conditions=dict(method=["DELETE"], function=check_repo))
- rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
+ rmap.connect('repo_settings_update', '/{repo_name:.*?}/settings',
controller='settings', action="update",
conditions=dict(method=["PUT"], function=check_repo))
- rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
+ rmap.connect('repo_settings_home', '/{repo_name:.*?}/settings',
controller='settings', action='index',
conditions=dict(function=check_repo))
- rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
+ rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
controller='forks', action='fork_create',
conditions=dict(function=check_repo, method=["POST"]))
- rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
+ rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
controller='forks', action='fork',
conditions=dict(function=check_repo))
- rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
+ rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
controller='forks', action='forks',
conditions=dict(function=check_repo))
- rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
+ rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
controller='followers', action='followers',
conditions=dict(function=check_repo))
diff --git a/rhodecode/controllers/admin/admin.py b/rhodecode/controllers/admin/admin.py
index 3e14e761..8ab0527e 100644
--- a/rhodecode/controllers/admin/admin.py
+++ b/rhodecode/controllers/admin/admin.py
@@ -45,7 +45,7 @@ class AdminController(BaseController):
@HasPermissionAllDecorator('hg.admin')
def index(self):
- users_log = self.sa.query(UserLog)\
+ users_log = UserLog.query()\
.options(joinedload(UserLog.user))\
.options(joinedload(UserLog.repository))\
.order_by(UserLog.action_date.desc())
diff --git a/rhodecode/controllers/admin/ldap_settings.py b/rhodecode/controllers/admin/ldap_settings.py
index 18c94d25..79a55d62 100644
--- a/rhodecode/controllers/admin/ldap_settings.py
+++ b/rhodecode/controllers/admin/ldap_settings.py
@@ -40,6 +40,7 @@ from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.exceptions import LdapImportError
from rhodecode.model.forms import LdapSettingsForm
from rhodecode.model.db import RhodeCodeSetting
+from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
@@ -119,9 +120,9 @@ class LdapSettingsController(BaseController):
v = ldap_active
setting = RhodeCodeSetting.get_by_name(k)
setting.app_settings_value = v
- self.sa.add(setting)
+ Session().add(setting)
- self.sa.commit()
+ Session().commit()
h.flash(_('Ldap settings updated successfully'),
category='success')
if not ldap_active:
diff --git a/rhodecode/controllers/admin/notifications.py b/rhodecode/controllers/admin/notifications.py
index 03b96540..9162ea9f 100644
--- a/rhodecode/controllers/admin/notifications.py
+++ b/rhodecode/controllers/admin/notifications.py
@@ -60,19 +60,33 @@ class NotificationsController(BaseController):
"""GET /_admin/notifications: All items in the collection"""
# url('notifications')
c.user = self.rhodecode_user
- notif = NotificationModel().get_for_user(self.rhodecode_user.user_id)
+ notif = NotificationModel().get_for_user(self.rhodecode_user.user_id,
+ filter_=request.GET.getall('type'))
p = int(request.params.get('page', 1))
c.notifications = Page(notif, page=p, items_per_page=10)
+ c.pull_request_type = Notification.TYPE_PULL_REQUEST
+ c.comment_type = [Notification.TYPE_CHANGESET_COMMENT,
+ Notification.TYPE_PULL_REQUEST_COMMENT]
+
+ _current_filter = request.GET.getall('type')
+ c.current_filter = 'all'
+ if _current_filter == [c.pull_request_type]:
+ c.current_filter = 'pull_request'
+ elif _current_filter == c.comment_type:
+ c.current_filter = 'comment'
+
return render('admin/notifications/notifications.html')
def mark_all_read(self):
if request.environ.get('HTTP_X_PARTIAL_XHR'):
nm = NotificationModel()
# mark all read
- nm.mark_all_read_for_user(self.rhodecode_user.user_id)
- Session.commit()
+ nm.mark_all_read_for_user(self.rhodecode_user.user_id,
+ filter_=request.GET.getall('type'))
+ Session().commit()
c.user = self.rhodecode_user
- notif = nm.get_for_user(self.rhodecode_user.user_id)
+ notif = nm.get_for_user(self.rhodecode_user.user_id,
+ filter_=request.GET.getall('type'))
c.notifications = Page(notif, page=1, items_per_page=10)
return render('admin/notifications/notifications_data.html')
@@ -92,6 +106,18 @@ class NotificationsController(BaseController):
# h.form(url('notification', notification_id=ID),
# method='put')
# url('notification', notification_id=ID)
+ try:
+ no = Notification.get(notification_id)
+ owner = lambda: (no.notifications_to_users.user.user_id
+ == c.rhodecode_user.user_id)
+ if h.HasPermissionAny('hg.admin')() or owner:
+ NotificationModel().mark_read(c.rhodecode_user.user_id, no)
+ Session().commit()
+ return 'ok'
+ except Exception:
+ Session.rollback()
+ log.error(traceback.format_exc())
+ return 'fail'
def delete(self, notification_id):
"""DELETE /_admin/notifications/id: Delete an existing item"""
@@ -106,9 +132,9 @@ class NotificationsController(BaseController):
no = Notification.get(notification_id)
owner = lambda: (no.notifications_to_users.user.user_id
== c.rhodecode_user.user_id)
- if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
+ if h.HasPermissionAny('hg.admin')() or owner:
NotificationModel().delete(c.rhodecode_user.user_id, no)
- Session.commit()
+ Session().commit()
return 'ok'
except Exception:
Session.rollback()
@@ -132,7 +158,7 @@ class NotificationsController(BaseController):
if unotification:
if unotification.read is False:
unotification.mark_as_read()
- Session.commit()
+ Session().commit()
c.notification = no
return render('admin/notifications/show_notification.html')
diff --git a/rhodecode/controllers/admin/permissions.py b/rhodecode/controllers/admin/permissions.py
index 490d0ea1..29a38467 100644
--- a/rhodecode/controllers/admin/permissions.py
+++ b/rhodecode/controllers/admin/permissions.py
@@ -71,6 +71,15 @@ class PermissionsController(BaseController):
self.create_choices = [('hg.create.none', _('Disabled')),
('hg.create.repository', _('Enabled'))]
+ self.fork_choices = [('hg.fork.none', _('Disabled')),
+ ('hg.fork.repository', _('Enabled'))]
+
+ # set the global template variables
+ c.perms_choices = self.perms_choices
+ c.register_choices = self.register_choices
+ c.create_choices = self.create_choices
+ c.fork_choices = self.fork_choices
+
def index(self, format='html'):
"""GET /permissions: All items in the collection"""
# url('permissions')
@@ -96,20 +105,18 @@ class PermissionsController(BaseController):
_form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
[x[0] for x in self.register_choices],
- [x[0] for x in self.create_choices])()
+ [x[0] for x in self.create_choices],
+ [x[0] for x in self.fork_choices])()
try:
form_result = _form.to_python(dict(request.POST))
form_result.update({'perm_user_name': id})
permission_model.update(form_result)
- Session.commit()
+ Session().commit()
h.flash(_('Default permissions updated successfully'),
category='success')
except formencode.Invalid, errors:
- c.perms_choices = self.perms_choices
- c.register_choices = self.register_choices
- c.create_choices = self.create_choices
defaults = errors.value
return htmlfill.render(
@@ -141,10 +148,8 @@ class PermissionsController(BaseController):
def edit(self, id, format='html'):
"""GET /permissions/id/edit: Form to edit an existing item"""
#url('edit_permission', id=ID)
- c.perms_choices = self.perms_choices
- c.register_choices = self.register_choices
- c.create_choices = self.create_choices
+ #this form can only edit default user permissions
if id == 'default':
default_user = User.get_by_username('default')
defaults = {'_method': 'put',
@@ -160,10 +165,14 @@ class PermissionsController(BaseController):
if p.permission.permission_name.startswith('hg.create.'):
defaults['default_create'] = p.permission.permission_name
+ if p.permission.permission_name.startswith('hg.fork.'):
+ defaults['default_fork'] = p.permission.permission_name
+
return htmlfill.render(
- render('admin/permissions/permissions.html'),
- defaults=defaults,
- encoding="UTF-8",
- force_defaults=True,)
+ render('admin/permissions/permissions.html'),
+ defaults=defaults,
+ encoding="UTF-8",
+ force_defaults=True,
+ )
else:
return redirect(url('admin_home'))
diff --git a/rhodecode/controllers/admin/repos.py b/rhodecode/controllers/admin/repos.py
index 0db1da7f..1b763010 100644
--- a/rhodecode/controllers/admin/repos.py
+++ b/rhodecode/controllers/admin/repos.py
@@ -28,12 +28,13 @@ import traceback
import formencode
from formencode import htmlfill
-from paste.httpexceptions import HTTPInternalServerError
+from webob.exc import HTTPInternalServerError
from pylons import request, session, tmpl_context as c, url
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from sqlalchemy.exc import IntegrityError
+import rhodecode
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
@@ -45,6 +46,7 @@ from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
from rhodecode.model.forms import RepoForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.repo import RepoModel
+from rhodecode.lib.compat import json
log = logging.getLogger(__name__)
@@ -70,6 +72,8 @@ class ReposController(BaseController):
repo_model = RepoModel()
c.users_array = repo_model.get_users_js()
c.users_groups_array = repo_model.get_users_groups_js()
+ choices, c.landing_revs = ScmModel().get_repo_landing_revs()
+ c.landing_revs_choices = choices
def __load_data(self, repo_name=None):
"""
@@ -91,6 +95,9 @@ class ReposController(BaseController):
return redirect(url('repos'))
+ choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
+ c.landing_revs_choices = choices
+
c.default_user_id = User.get_by_username('default').user_id
c.in_public_journal = UserFollowing.query()\
.filter(UserFollowing.user_id == c.default_user_id)\
@@ -115,7 +122,10 @@ class ReposController(BaseController):
c.repos_list = [('', _('--REMOVE FORK--'))]
c.repos_list += [(x.repo_id, x.repo_name) for x in
- Repository.query().order_by(Repository.repo_name).all()]
+ Repository.query().order_by(Repository.repo_name).all()
+ if x.repo_id != c.repo_info.repo_id]
+
+ defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
return defaults
@HasPermissionAllDecorator('hg.admin')
@@ -123,9 +133,45 @@ class ReposController(BaseController):
"""GET /repos: All items in the collection"""
# url('repos')
- c.repos_list = ScmModel().get_repos(Repository.query()
- .order_by(Repository.repo_name)
- .all(), sort_key='name_sort')
+ c.repos_list = Repository.query()\
+ .order_by(Repository.repo_name)\
+ .all()
+
+ repos_data = []
+ total_records = len(c.repos_list)
+
+ _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
+ template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
+
+ quick_menu = lambda repo_name: (template.get_def("quick_menu")
+ .render(repo_name, _=_, h=h, c=c))
+ repo_lnk = lambda name, rtype, private, fork_of: (
+ template.get_def("repo_name")
+ .render(name, rtype, private, fork_of, short_name=False,
+ admin=True, _=_, h=h, c=c))
+
+ repo_actions = lambda repo_name: (template.get_def("repo_actions")
+ .render(repo_name, _=_, h=h, c=c))
+
+ for repo in c.repos_list:
+ repos_data.append({
+ "menu": quick_menu(repo.repo_name),
+ "raw_name": repo.repo_name,
+ "name": repo_lnk(repo.repo_name, repo.repo_type,
+ repo.private, repo.fork),
+ "desc": repo.description,
+ "owner": repo.user.username,
+ "action": repo_actions(repo.repo_name),
+ })
+
+ c.data = json.dumps({
+ "totalRecords": total_records,
+ "startIndex": 0,
+ "sort": "name",
+ "dir": "asc",
+ "records": repos_data
+ })
+
return render('admin/repos/repos.html')
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
@@ -137,9 +183,11 @@ class ReposController(BaseController):
self.__load_defaults()
form_result = {}
try:
- form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
+ form_result = RepoForm(repo_groups=c.repo_groups_choices,
+ landing_revs=c.landing_revs_choices)()\
.to_python(dict(request.POST))
- RepoModel().create(form_result, self.rhodecode_user)
+ new_repo = RepoModel().create(form_result,
+ self.rhodecode_user.user_id)
if form_result['clone_uri']:
h.flash(_('created repository %s from %s') \
% (form_result['repo_name'], form_result['clone_uri']),
@@ -151,11 +199,13 @@ class ReposController(BaseController):
if request.POST.get('user_created'):
# created by regular non admin user
action_logger(self.rhodecode_user, 'user_created_repo',
- form_result['repo_name_full'], '', self.sa)
+ form_result['repo_name_full'], self.ip_addr,
+ self.sa)
else:
action_logger(self.rhodecode_user, 'admin_created_repo',
- form_result['repo_name_full'], '', self.sa)
- Session.commit()
+ form_result['repo_name_full'], self.ip_addr,
+ self.sa)
+ Session().commit()
except formencode.Invalid, errors:
c.new_repo = errors.value['repo_name']
@@ -177,9 +227,9 @@ class ReposController(BaseController):
msg = _('error occurred during creation of repository %s') \
% form_result.get('repo_name')
h.flash(msg, category='error')
- if request.POST.get('user_created'):
- return redirect(url('home'))
- return redirect(url('repos'))
+ return redirect(url('repos'))
+ #redirect to our new repo !
+ return redirect(url('summary_home', repo_name=new_repo.repo_name))
@HasPermissionAllDecorator('hg.admin')
def new(self, format='html'):
@@ -202,18 +252,23 @@ class ReposController(BaseController):
self.__load_defaults()
repo_model = RepoModel()
changed_name = repo_name
+ #override the choices with extracted revisions !
+ choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
+ c.landing_revs_choices = choices
+
_form = RepoForm(edit=True, old_data={'repo_name': repo_name},
- repo_groups=c.repo_groups_choices)()
+ repo_groups=c.repo_groups_choices,
+ landing_revs=c.landing_revs_choices)()
try:
form_result = _form.to_python(dict(request.POST))
repo = repo_model.update(repo_name, form_result)
invalidate_cache('get_repo_cached_%s' % repo_name)
- h.flash(_('Repository %s updated successfully' % repo_name),
+ h.flash(_('Repository %s updated successfully') % repo_name,
category='success')
changed_name = repo.repo_name
action_logger(self.rhodecode_user, 'admin_updated_repo',
- changed_name, '', self.sa)
- Session.commit()
+ changed_name, self.ip_addr, self.sa)
+ Session().commit()
except formencode.Invalid, errors:
defaults = self.__load_data(repo_name)
defaults.update(errors.value)
@@ -253,11 +308,11 @@ class ReposController(BaseController):
return redirect(url('repos'))
try:
action_logger(self.rhodecode_user, 'admin_deleted_repo',
- repo_name, '', self.sa)
+ repo_name, self.ip_addr, self.sa)
repo_model.delete(repo)
invalidate_cache('get_repo_cached_%s' % repo_name)
h.flash(_('deleted repository %s') % repo_name, category='success')
- Session.commit()
+ Session().commit()
except IntegrityError, e:
if e.message.find('repositories_fork_id_fkey') != -1:
log.error(traceback.format_exc())
@@ -287,7 +342,7 @@ class ReposController(BaseController):
try:
RepoModel().revoke_user_permission(repo=repo_name,
user=request.POST['user_id'])
- Session.commit()
+ Session().commit()
except Exception:
log.error(traceback.format_exc())
h.flash(_('An error occurred during deletion of repository user'),
@@ -306,7 +361,7 @@ class ReposController(BaseController):
RepoModel().revoke_users_group_permission(
repo=repo_name, group_name=request.POST['users_group_id']
)
- Session.commit()
+ Session().commit()
except Exception:
log.error(traceback.format_exc())
h.flash(_('An error occurred during deletion of repository'
@@ -324,8 +379,9 @@ class ReposController(BaseController):
try:
RepoModel().delete_stats(repo_name)
- Session.commit()
+ Session().commit()
except Exception, e:
+ log.error(traceback.format_exc())
h.flash(_('An error occurred during deletion of repository stats'),
category='error')
return redirect(url('edit_repo', repo_name=repo_name))
@@ -340,13 +396,34 @@ class ReposController(BaseController):
try:
ScmModel().mark_for_invalidation(repo_name)
- Session.commit()
+ Session().commit()
except Exception, e:
+ log.error(traceback.format_exc())
h.flash(_('An error occurred during cache invalidation'),
category='error')
return redirect(url('edit_repo', repo_name=repo_name))
@HasPermissionAllDecorator('hg.admin')
+ def repo_locking(self, repo_name):
+ """
+ Unlock repository when it is locked !
+
+ :param repo_name:
+ """
+
+ try:
+ repo = Repository.get_by_repo_name(repo_name)
+ if request.POST.get('set_lock'):
+ Repository.lock(repo, c.rhodecode_user.user_id)
+ elif request.POST.get('set_unlock'):
+ Repository.unlock(repo)
+ except Exception, e:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during unlocking'),
+ category='error')
+ return redirect(url('edit_repo', repo_name=repo_name))
+
+ @HasPermissionAllDecorator('hg.admin')
def repo_public_journal(self, repo_name):
"""
Set's this repository to be visible in public journal,
@@ -364,7 +441,7 @@ class ReposController(BaseController):
self.scm_model.toggle_following_repo(repo_id, user_id)
h.flash(_('Updated repository visibility in public journal'),
category='success')
- Session.commit()
+ Session().commit()
except:
h.flash(_('An error occurred during setting this'
' repository in public journal'),
@@ -403,11 +480,11 @@ class ReposController(BaseController):
repo = ScmModel().mark_as_fork(repo_name, fork_id,
self.rhodecode_user.username)
fork = repo.fork.repo_name if repo.fork else _('Nothing')
- Session.commit()
- h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
+ Session().commit()
+ h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
category='success')
except Exception, e:
- raise
+ log.error(traceback.format_exc())
h.flash(_('An error occurred during this operation'),
category='error')
diff --git a/rhodecode/controllers/admin/repos_groups.py b/rhodecode/controllers/admin/repos_groups.py
index a3588f82..343af433 100644
--- a/rhodecode/controllers/admin/repos_groups.py
+++ b/rhodecode/controllers/admin/repos_groups.py
@@ -44,7 +44,7 @@ from rhodecode.model.repos_group import ReposGroupModel
from rhodecode.model.forms import ReposGroupForm
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
-from webob.exc import HTTPInternalServerError
+from webob.exc import HTTPInternalServerError, HTTPNotFound
log = logging.getLogger(__name__)
@@ -74,11 +74,8 @@ class ReposGroupsController(BaseController):
:param group_id:
"""
self.__load_defaults()
-
- repo_group = RepoGroup.get(group_id)
-
+ repo_group = RepoGroup.get_or_404(group_id)
data = repo_group.get_dict()
-
data['group_name'] = repo_group.name
# fill repository users
@@ -115,7 +112,7 @@ class ReposGroupsController(BaseController):
group_description=form_result['group_description'],
parent=form_result['group_parent_id']
)
- Session.commit()
+ Session().commit()
h.flash(_('created repos group %s') \
% form_result['group_name'], category='success')
#TODO: in futureaction_logger(, '', '', '', self.sa)
@@ -162,7 +159,7 @@ class ReposGroupsController(BaseController):
try:
form_result = repos_group_form.to_python(dict(request.POST))
ReposGroupModel().update(id, form_result)
- Session.commit()
+ Session().commit()
h.flash(_('updated repos group %s') \
% form_result['group_name'], category='success')
#TODO: in futureaction_logger(, '', '', '', self.sa)
@@ -179,7 +176,7 @@ class ReposGroupsController(BaseController):
h.flash(_('error occurred during update of repos group %s') \
% request.POST.get('group_name'), category='error')
- return redirect(url('repos_groups'))
+ return redirect(url('edit_repos_group', id=id))
@HasPermissionAnyDecorator('hg.admin')
def delete(self, id):
@@ -195,17 +192,18 @@ class ReposGroupsController(BaseController):
repos = gr.repositories.all()
if repos:
h.flash(_('This group contains %s repositores and cannot be '
- 'deleted' % len(repos)),
+ 'deleted') % len(repos),
category='error')
return redirect(url('repos_groups'))
try:
ReposGroupModel().delete(id)
- Session.commit()
- h.flash(_('removed repos group %s' % gr.group_name), category='success')
+ Session().commit()
+ h.flash(_('removed repos group %s') % gr.group_name,
+ category='success')
#TODO: in future action_logger(, '', '', '', self.sa)
except IntegrityError, e:
- if e.message.find('groups_group_parent_id_fkey') != -1:
+ if str(e.message).find('groups_group_parent_id_fkey') != -1:
log.error(traceback.format_exc())
h.flash(_('Cannot delete this group it still contains '
'subgroups'),
@@ -213,12 +211,12 @@ class ReposGroupsController(BaseController):
else:
log.error(traceback.format_exc())
h.flash(_('error occurred during deletion of repos '
- 'group %s' % gr.group_name), category='error')
+ 'group %s') % gr.group_name, category='error')
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occurred during deletion of repos '
- 'group %s' % gr.group_name), category='error')
+ 'group %s') % gr.group_name, category='error')
return redirect(url('repos_groups'))
@@ -234,7 +232,7 @@ class ReposGroupsController(BaseController):
ReposGroupModel().revoke_user_permission(
repos_group=group_name, user=request.POST['user_id']
)
- Session.commit()
+ Session().commit()
except Exception:
log.error(traceback.format_exc())
h.flash(_('An error occurred during deletion of group user'),
@@ -254,7 +252,7 @@ class ReposGroupsController(BaseController):
repos_group=group_name,
group_name=request.POST['users_group_id']
)
- Session.commit()
+ Session().commit()
except Exception:
log.error(traceback.format_exc())
h.flash(_('An error occurred during deletion of group'
@@ -268,8 +266,10 @@ class ReposGroupsController(BaseController):
the group by id view instead
"""
group_name = group_name.rstrip('/')
- id_ = RepoGroup.get_by_group_name(group_name).group_id
- return self.show(id_)
+ id_ = RepoGroup.get_by_group_name(group_name)
+ if id_:
+ return self.show(id_.group_id)
+ raise HTTPNotFound
@HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
'group.admin')
@@ -277,12 +277,9 @@ class ReposGroupsController(BaseController):
"""GET /repos_groups/id: Show a specific item"""
# url('repos_group', id=ID)
- c.group = RepoGroup.get(id)
+ c.group = RepoGroup.get_or_404(id)
- if c.group:
- c.group_repos = c.group.repositories.all()
- else:
- return redirect(url('home'))
+ c.group_repos = c.group.repositories.all()
#overwrite our cached list with current filter
gr_filter = c.group_repos
@@ -292,7 +289,7 @@ class ReposGroupsController(BaseController):
c.repo_cnt = 0
- c.groups = self.sa.query(RepoGroup).order_by(RepoGroup.group_name)\
+ c.groups = RepoGroup.query().order_by(RepoGroup.group_name)\
.filter(RepoGroup.group_parent_id == id).all()
return render('admin/repos_groups/repos_groups.html')
@@ -302,13 +299,12 @@ class ReposGroupsController(BaseController):
"""GET /repos_groups/id/edit: Form to edit an existing item"""
# url('edit_repos_group', id=ID)
- id_ = int(id)
-
- c.repos_group = RepoGroup.get(id_)
- defaults = self.__load_data(id_)
+ c.repos_group = ReposGroupModel()._get_repos_group(id)
+ defaults = self.__load_data(c.repos_group.group_id)
# we need to exclude this group from the group list for editing
- c.repo_groups = filter(lambda x: x[0] != id_, c.repo_groups)
+ c.repo_groups = filter(lambda x: x[0] != c.repos_group.group_id,
+ c.repo_groups)
return htmlfill.render(
render('admin/repos_groups/repos_groups_edit.html'),
diff --git a/rhodecode/controllers/admin/settings.py b/rhodecode/controllers/admin/settings.py
index 29a770cd..bd10e087 100644
--- a/rhodecode/controllers/admin/settings.py
+++ b/rhodecode/controllers/admin/settings.py
@@ -43,9 +43,9 @@ from rhodecode.lib.celerylib import tasks, run_task
from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
set_rhodecode_config, repo_name_slug
from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
- RhodeCodeSetting
+ RhodeCodeSetting, PullRequest, PullRequestReviewers
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
- ApplicationUiSettingsForm
+ ApplicationUiSettingsForm, ApplicationVisualisationForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.user import UserModel
from rhodecode.model.db import User
@@ -79,7 +79,7 @@ class SettingsController(BaseController):
# url('admin_settings')
defaults = RhodeCodeSetting.get_app_settings()
- defaults.update(self.get_hg_ui_settings())
+ defaults.update(self._get_hg_ui_settings())
return htmlfill.render(
render('admin/settings/settings.html'),
@@ -107,6 +107,7 @@ class SettingsController(BaseController):
# h.form(url('admin_setting', setting_id=ID),
# method='put')
# url('admin_setting', setting_id=ID)
+
if setting_id == 'mapping':
rm_obsolete = request.POST.get('destroy', False)
log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
@@ -119,122 +120,160 @@ class SettingsController(BaseController):
h.flash(_('Repositories successfully'
' rescanned added: %s,removed: %s') % (added, removed),
- category='success')
+ category='success')
if setting_id == 'whoosh':
- repo_location = self.get_hg_ui_settings()['paths_root_path']
+ repo_location = self._get_hg_ui_settings()['paths_root_path']
full_index = request.POST.get('full_index', False)
run_task(tasks.whoosh_index, repo_location, full_index)
-
h.flash(_('Whoosh reindex task scheduled'), category='success')
+
if setting_id == 'global':
application_form = ApplicationSettingsForm()()
try:
form_result = application_form.to_python(dict(request.POST))
+ except formencode.Invalid, errors:
+ return htmlfill.render(
+ render('admin/settings/settings.html'),
+ defaults=errors.value,
+ errors=errors.error_dict or {},
+ prefix_error=False,
+ encoding="UTF-8"
+ )
- try:
- hgsettings1 = RhodeCodeSetting.get_by_name('title')
- hgsettings1.app_settings_value = \
- form_result['rhodecode_title']
-
- hgsettings2 = RhodeCodeSetting.get_by_name('realm')
- hgsettings2.app_settings_value = \
- form_result['rhodecode_realm']
-
- hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
- hgsettings3.app_settings_value = \
- form_result['rhodecode_ga_code']
-
- self.sa.add(hgsettings1)
- self.sa.add(hgsettings2)
- self.sa.add(hgsettings3)
- self.sa.commit()
- set_rhodecode_config(config)
- h.flash(_('Updated application settings'),
- category='success')
+ try:
+ sett1 = RhodeCodeSetting.get_by_name_or_create('title')
+ sett1.app_settings_value = form_result['rhodecode_title']
+ Session().add(sett1)
+
+ sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
+ sett2.app_settings_value = form_result['rhodecode_realm']
+ Session().add(sett2)
- except Exception:
- log.error(traceback.format_exc())
- h.flash(_('error occurred during updating '
- 'application settings'),
- category='error')
+ sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
+ sett3.app_settings_value = form_result['rhodecode_ga_code']
+ Session().add(sett3)
- self.sa.rollback()
+ Session().commit()
+ set_rhodecode_config(config)
+ h.flash(_('Updated application settings'), category='success')
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('error occurred during updating '
+ 'application settings'),
+ category='error')
+
+ if setting_id == 'visual':
+
+ application_form = ApplicationVisualisationForm()()
+ try:
+ form_result = application_form.to_python(dict(request.POST))
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/settings/settings.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
- encoding="UTF-8")
+ encoding="UTF-8"
+ )
- if setting_id == 'mercurial':
+ try:
+ sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
+ sett1.app_settings_value = \
+ form_result['rhodecode_show_public_icon']
+
+ sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
+ sett2.app_settings_value = \
+ form_result['rhodecode_show_private_icon']
+
+ sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
+ sett3.app_settings_value = \
+ form_result['rhodecode_stylify_metatags']
+
+ Session().add(sett1)
+ Session().add(sett2)
+ Session().add(sett3)
+ Session().commit()
+ set_rhodecode_config(config)
+ h.flash(_('Updated visualisation settings'),
+ category='success')
+
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('error occurred during updating '
+ 'visualisation settings'),
+ category='error')
+
+ if setting_id == 'vcs':
application_form = ApplicationUiSettingsForm()()
try:
form_result = application_form.to_python(dict(request.POST))
-
- try:
-
- hgsettings1 = self.sa.query(RhodeCodeUi)\
- .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
- hgsettings1.ui_value = form_result['web_push_ssl']
-
- hgsettings2 = self.sa.query(RhodeCodeUi)\
- .filter(RhodeCodeUi.ui_key == '/').one()
- hgsettings2.ui_value = form_result['paths_root_path']
-
- #HOOKS
- hgsettings3 = self.sa.query(RhodeCodeUi)\
- .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
- hgsettings3.ui_active = \
- bool(form_result['hooks_changegroup_update'])
-
- hgsettings4 = self.sa.query(RhodeCodeUi)\
- .filter(RhodeCodeUi.ui_key ==
- 'changegroup.repo_size').one()
- hgsettings4.ui_active = \
- bool(form_result['hooks_changegroup_repo_size'])
-
- hgsettings5 = self.sa.query(RhodeCodeUi)\
- .filter(RhodeCodeUi.ui_key ==
- 'pretxnchangegroup.push_logger').one()
- hgsettings5.ui_active = \
- bool(form_result['hooks_pretxnchangegroup'
- '_push_logger'])
-
- hgsettings6 = self.sa.query(RhodeCodeUi)\
- .filter(RhodeCodeUi.ui_key ==
- 'preoutgoing.pull_logger').one()
- hgsettings6.ui_active = \
- bool(form_result['hooks_preoutgoing_pull_logger'])
-
- self.sa.add(hgsettings1)
- self.sa.add(hgsettings2)
- self.sa.add(hgsettings3)
- self.sa.add(hgsettings4)
- self.sa.add(hgsettings5)
- self.sa.add(hgsettings6)
- self.sa.commit()
-
- h.flash(_('Updated mercurial settings'),
- category='success')
-
- except:
- log.error(traceback.format_exc())
- h.flash(_('error occurred during updating '
- 'application settings'), category='error')
-
- self.sa.rollback()
-
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/settings/settings.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
- encoding="UTF-8")
+ encoding="UTF-8"
+ )
+
+ try:
+ # fix namespaces for hooks and extensions
+ _f = lambda s: s.replace('.', '_')
+
+ sett = RhodeCodeUi.get_by_key('push_ssl')
+ sett.ui_value = form_result['web_push_ssl']
+ Session().add(sett)
+
+ sett = RhodeCodeUi.get_by_key('/')
+ sett.ui_value = form_result['paths_root_path']
+ Session().add(sett)
+
+ #HOOKS
+ sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
+ sett.ui_active = form_result[_f('hooks_%s' %
+ RhodeCodeUi.HOOK_UPDATE)]
+ Session().add(sett)
+
+ sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
+ sett.ui_active = form_result[_f('hooks_%s' %
+ RhodeCodeUi.HOOK_REPO_SIZE)]
+ Session().add(sett)
+
+ sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
+ sett.ui_active = form_result[_f('hooks_%s' %
+ RhodeCodeUi.HOOK_PUSH)]
+ Session().add(sett)
+
+ sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
+ sett.ui_active = form_result[_f('hooks_%s' %
+ RhodeCodeUi.HOOK_PULL)]
+
+ Session().add(sett)
+
+ ## EXTENSIONS
+ sett = RhodeCodeUi.get_by_key('largefiles')
+ sett.ui_active = form_result[_f('extensions_largefiles')]
+ Session().add(sett)
+
+ sett = RhodeCodeUi.get_by_key('hgsubversion')
+ sett.ui_active = form_result[_f('extensions_hgsubversion')]
+ Session().add(sett)
+
+# sett = RhodeCodeUi.get_by_key('hggit')
+# sett.ui_active = form_result[_f('extensions_hggit')]
+# Session().add(sett)
+
+ Session().commit()
+
+ h.flash(_('Updated VCS settings'), category='success')
+
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('error occurred during updating '
+ 'application settings'), category='error')
if setting_id == 'hooks':
ui_key = request.POST.get('new_hook_ui_key')
@@ -256,8 +295,8 @@ class SettingsController(BaseController):
if update:
h.flash(_('Updated hooks'), category='success')
- self.sa.commit()
- except:
+ Session().commit()
+ except Exception:
log.error(traceback.format_exc())
h.flash(_('error occurred during hook creation'),
category='error')
@@ -293,7 +332,7 @@ class SettingsController(BaseController):
if setting_id == 'hooks':
hook_id = request.POST.get('hook_id')
RhodeCodeUi.delete(hook_id)
- self.sa.commit()
+ Session().commit()
@HasPermissionAllDecorator('hg.admin')
def show(self, setting_id, format='html'):
@@ -326,7 +365,7 @@ class SettingsController(BaseController):
# url('admin_settings_my_account')
c.user = User.get(self.rhodecode_user.user_id)
- all_repos = self.sa.query(Repository)\
+ all_repos = Session().query(Repository)\
.filter(Repository.user_id == c.user.user_id)\
.order_by(func.lower(Repository.repo_name)).all()
@@ -338,13 +377,16 @@ class SettingsController(BaseController):
return redirect(url('users'))
defaults = c.user.get_dict()
- return htmlfill.render(
- render('admin/users/user_edit_my_account.html'),
+
+ c.form = htmlfill.render(
+ render('admin/users/user_edit_my_account_form.html'),
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
+ return render('admin/users/user_edit_my_account.html')
+ @NotAnonymous()
def my_account_update(self):
"""PUT /_admin/my_account_update: Update an existing item"""
# Forms posted to this method should contain a hidden field:
@@ -353,32 +395,27 @@ class SettingsController(BaseController):
# h.form(url('admin_settings_my_account_update'),
# method='put')
# url('admin_settings_my_account_update', id=ID)
- user_model = UserModel()
uid = self.rhodecode_user.user_id
+ email = self.rhodecode_user.email
_form = UserForm(edit=True,
- old_data={'user_id': uid,
- 'email': self.rhodecode_user.email})()
+ old_data={'user_id': uid, 'email': email})()
form_result = {}
try:
form_result = _form.to_python(dict(request.POST))
- user_model.update_my_account(uid, form_result)
+ UserModel().update_my_account(uid, form_result)
h.flash(_('Your account was updated successfully'),
category='success')
- Session.commit()
+ Session().commit()
except formencode.Invalid, errors:
c.user = User.get(self.rhodecode_user.user_id)
- all_repos = self.sa.query(Repository)\
- .filter(Repository.user_id == c.user.user_id)\
- .order_by(func.lower(Repository.repo_name))\
- .all()
- c.user_repos = ScmModel().get_repos(all_repos)
-
- return htmlfill.render(
- render('admin/users/user_edit_my_account.html'),
+
+ c.form = htmlfill.render(
+ render('admin/users/user_edit_my_account_form.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
+ return render('admin/users/user_edit_my_account.html')
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occurred during update of user %s') \
@@ -387,20 +424,43 @@ class SettingsController(BaseController):
return redirect(url('my_account'))
@NotAnonymous()
+ def my_account_my_repos(self):
+ all_repos = Session().query(Repository)\
+ .filter(Repository.user_id == self.rhodecode_user.user_id)\
+ .order_by(func.lower(Repository.repo_name))\
+ .all()
+ c.user_repos = ScmModel().get_repos(all_repos)
+ return render('admin/users/user_edit_my_account_repos.html')
+
+ @NotAnonymous()
+ def my_account_my_pullrequests(self):
+ c.my_pull_requests = PullRequest.query()\
+ .filter(PullRequest.user_id==
+ self.rhodecode_user.user_id)\
+ .all()
+ c.participate_in_pull_requests = \
+ [x.pull_request for x in PullRequestReviewers.query()\
+ .filter(PullRequestReviewers.user_id==
+ self.rhodecode_user.user_id)\
+ .all()]
+ return render('admin/users/user_edit_my_account_pullrequests.html')
+
+ @NotAnonymous()
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
def create_repository(self):
"""GET /_admin/create_repository: Form to create a new item"""
c.repo_groups = RepoGroup.groups_choices()
c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
+ choices, c.landing_revs = ScmModel().get_repo_landing_revs()
new_repo = request.GET.get('repo', '')
c.new_repo = repo_name_slug(new_repo)
return render('admin/repos/repo_add_create_repository.html')
- def get_hg_ui_settings(self):
- ret = self.sa.query(RhodeCodeUi).all()
+ def _get_hg_ui_settings(self):
+ ret = RhodeCodeUi.query().all()
if not ret:
raise Exception('Could not get application ui settings !')
@@ -414,7 +474,7 @@ class SettingsController(BaseController):
if k.find('.') != -1:
k = k.replace('.', '_')
- if each.ui_section == 'hooks':
+ if each.ui_section in ['hooks', 'extensions']:
v = each.ui_active
settings[each.ui_section + '_' + k] = v
diff --git a/rhodecode/controllers/admin/users.py b/rhodecode/controllers/admin/users.py
index 82d4f127..7ab20b71 100644
--- a/rhodecode/controllers/admin/users.py
+++ b/rhodecode/controllers/admin/users.py
@@ -26,22 +26,28 @@
import logging
import traceback
import formencode
+from pylons import response
from formencode import htmlfill
from pylons import request, session, tmpl_context as c, url, config
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
+import rhodecode
from rhodecode.lib.exceptions import DefaultUserException, \
UserOwnsReposException
from rhodecode.lib import helpers as h
-from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
+from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
+ AuthUser
from rhodecode.lib.base import BaseController, render
-from rhodecode.model.db import User, Permission
+from rhodecode.model.db import User, UserEmailMap
from rhodecode.model.forms import UserForm
from rhodecode.model.user import UserModel
from rhodecode.model.meta import Session
+from rhodecode.lib.utils import action_logger
+from rhodecode.lib.compat import json
+from rhodecode.lib.utils2 import datetime_to_time, str2bool
log = logging.getLogger(__name__)
@@ -64,7 +70,49 @@ class UsersController(BaseController):
"""GET /users: All items in the collection"""
# url('users')
- c.users_list = self.sa.query(User).all()
+ c.users_list = User.query().order_by(User.username).all()
+
+ users_data = []
+ total_records = len(c.users_list)
+ _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
+ template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
+
+ grav_tmpl = lambda user_email, size: (
+ template.get_def("user_gravatar")
+ .render(user_email, size, _=_, h=h, c=c))
+
+ user_lnk = lambda user_id, username: (
+ template.get_def("user_name")
+ .render(user_id, username, _=_, h=h, c=c))
+
+ user_actions = lambda user_id, username: (
+ template.get_def("user_actions")
+ .render(user_id, username, _=_, h=h, c=c))
+
+ for user in c.users_list:
+
+ users_data.append({
+ "gravatar": grav_tmpl(user. email, 24),
+ "raw_username": user.username,
+ "username": user_lnk(user.user_id, user.username),
+ "firstname": user.name,
+ "lastname": user.lastname,
+ "last_login": h.fmt_date(user.last_login),
+ "last_login_raw": datetime_to_time(user.last_login),
+ "active": h.bool2icon(user.active),
+ "admin": h.bool2icon(user.admin),
+ "ldap": h.bool2icon(bool(user.ldap_dn)),
+ "action": user_actions(user.user_id, user.username),
+ })
+
+ c.data = json.dumps({
+ "totalRecords": total_records,
+ "startIndex": 0,
+ "sort": None,
+ "dir": "asc",
+ "records": users_data
+ })
+
return render('admin/users/users.html')
def create(self):
@@ -76,10 +124,12 @@ class UsersController(BaseController):
try:
form_result = user_form.to_python(dict(request.POST))
user_model.create(form_result)
- h.flash(_('created user %s') % form_result['username'],
+ usr = form_result['username']
+ action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
+ None, self.ip_addr, self.sa)
+ h.flash(_('created user %s') % usr,
category='success')
- Session.commit()
- #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
+ Session().commit()
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/users/user_add.html'),
@@ -108,22 +158,31 @@ class UsersController(BaseController):
# url('user', id=ID)
user_model = UserModel()
c.user = user_model.get(id)
-
+ c.perm_user = AuthUser(user_id=id)
_form = UserForm(edit=True, old_data={'user_id': id,
'email': c.user.email})()
form_result = {}
try:
form_result = _form.to_python(dict(request.POST))
user_model.update(id, form_result)
+ usr = form_result['username']
+ action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
+ None, self.ip_addr, self.sa)
h.flash(_('User updated successfully'), category='success')
- Session.commit()
+ Session().commit()
except formencode.Invalid, errors:
+ c.user_email_map = UserEmailMap.query()\
+ .filter(UserEmailMap.user == c.user).all()
+ defaults = errors.value
e = errors.error_dict or {}
- perm = Permission.get_by_key('hg.create.repository')
- e.update({'create_repo_perm': user_model.has_perm(id, perm)})
+ defaults.update({
+ 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
+ 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
+ '_method': 'put'
+ })
return htmlfill.render(
render('admin/users/user_edit.html'),
- defaults=errors.value,
+ defaults=defaults,
errors=e,
prefix_error=False,
encoding="UTF-8")
@@ -131,8 +190,7 @@ class UsersController(BaseController):
log.error(traceback.format_exc())
h.flash(_('error occurred during update of user %s') \
% form_result.get('username'), category='error')
-
- return redirect(url('users'))
+ return redirect(url('edit_user', id=id))
def delete(self, id):
"""DELETE /users/id: Delete an existing item"""
@@ -142,10 +200,10 @@ class UsersController(BaseController):
# h.form(url('delete_user', id=ID),
# method='delete')
# url('user', id=ID)
- user_model = UserModel()
+ usr = User.get_or_404(id)
try:
- user_model.delete(id)
- Session.commit()
+ UserModel().delete(usr)
+ Session().commit()
h.flash(_('successfully deleted user'), category='success')
except (UserOwnsReposException, DefaultUserException), e:
h.flash(e, category='warning')
@@ -162,19 +220,24 @@ class UsersController(BaseController):
def edit(self, id, format='html'):
"""GET /users/id/edit: Form to edit an existing item"""
# url('edit_user', id=ID)
- c.user = User.get(id)
- if not c.user:
- return redirect(url('users'))
+ c.user = User.get_or_404(id)
+
if c.user.username == 'default':
h.flash(_("You can't edit this user"), category='warning')
return redirect(url('users'))
+
+ c.perm_user = AuthUser(user_id=id)
c.user.permissions = {}
c.granted_permissions = UserModel().fill_perms(c.user)\
.permissions['global']
-
+ c.user_email_map = UserEmailMap.query()\
+ .filter(UserEmailMap.user == c.user).all()
+ user_model = UserModel()
defaults = c.user.get_dict()
- perm = Permission.get_by_key('hg.create.repository')
- defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
+ defaults.update({
+ 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
+ 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
+ })
return htmlfill.render(
render('admin/users/user_edit.html'),
@@ -186,26 +249,72 @@ class UsersController(BaseController):
def update_perm(self, id):
"""PUT /users_perm/id: Update an existing item"""
# url('user_perm', id=ID, method='put')
+ usr = User.get_or_404(id)
+ grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
+ grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
+ inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
- grant_perm = request.POST.get('create_repo_perm', False)
user_model = UserModel()
- if grant_perm:
- perm = Permission.get_by_key('hg.create.none')
- user_model.revoke_perm(id, perm)
+ try:
+ usr.inherit_default_permissions = inherit_perms
+ Session().add(usr)
- perm = Permission.get_by_key('hg.create.repository')
- user_model.grant_perm(id, perm)
- h.flash(_("Granted 'repository create' permission to user"),
- category='success')
- Session.commit()
- else:
- perm = Permission.get_by_key('hg.create.repository')
- user_model.revoke_perm(id, perm)
-
- perm = Permission.get_by_key('hg.create.none')
- user_model.grant_perm(id, perm)
- h.flash(_("Revoked 'repository create' permission to user"),
- category='success')
- Session.commit()
+ if grant_create_perm:
+ user_model.revoke_perm(usr, 'hg.create.none')
+ user_model.grant_perm(usr, 'hg.create.repository')
+ h.flash(_("Granted 'repository create' permission to user"),
+ category='success')
+ else:
+ user_model.revoke_perm(usr, 'hg.create.repository')
+ user_model.grant_perm(usr, 'hg.create.none')
+ h.flash(_("Revoked 'repository create' permission to user"),
+ category='success')
+
+ if grant_fork_perm:
+ user_model.revoke_perm(usr, 'hg.fork.none')
+ user_model.grant_perm(usr, 'hg.fork.repository')
+ h.flash(_("Granted 'repository fork' permission to user"),
+ category='success')
+ else:
+ user_model.revoke_perm(usr, 'hg.fork.repository')
+ user_model.grant_perm(usr, 'hg.fork.none')
+ h.flash(_("Revoked 'repository fork' permission to user"),
+ category='success')
+
+ Session().commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during permissions saving'),
+ category='error')
+ return redirect(url('edit_user', id=id))
+
+ def add_email(self, id):
+ """POST /user_emails:Add an existing item"""
+ # url('user_emails', id=ID, method='put')
+
+ #TODO: validation and form !!!
+ email = request.POST.get('new_email')
+ user_model = UserModel()
+
+ try:
+ user_model.add_extra_email(id, email)
+ Session().commit()
+ h.flash(_("Added email %s to user") % email, category='success')
+ except formencode.Invalid, error:
+ msg = error.error_dict['email']
+ h.flash(msg, category='error')
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during email saving'),
+ category='error')
+ return redirect(url('edit_user', id=id))
+
+ def delete_email(self, id):
+ """DELETE /user_emails_delete/id: Delete an existing item"""
+ # url('user_emails_delete', id=ID, method='delete')
+ user_model = UserModel()
+ user_model.delete_extra_email(id, request.POST.get('del_email'))
+ Session().commit()
+ h.flash(_("Removed email from user"), category='success')
return redirect(url('edit_user', id=id))
diff --git a/rhodecode/controllers/admin/users_groups.py b/rhodecode/controllers/admin/users_groups.py
index b47d6e7a..85337aae 100644
--- a/rhodecode/controllers/admin/users_groups.py
+++ b/rhodecode/controllers/admin/users_groups.py
@@ -34,15 +34,16 @@ from pylons.i18n.translation import _
from rhodecode.lib import helpers as h
from rhodecode.lib.exceptions import UsersGroupsAssignedException
-from rhodecode.lib.utils2 import safe_unicode
+from rhodecode.lib.utils2 import safe_unicode, str2bool
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.model.users_group import UsersGroupModel
-from rhodecode.model.db import User, UsersGroup, Permission, UsersGroupToPerm
+from rhodecode.model.db import User, UsersGroup
from rhodecode.model.forms import UsersGroupForm
from rhodecode.model.meta import Session
+from rhodecode.lib.utils import action_logger
log = logging.getLogger(__name__)
@@ -64,7 +65,7 @@ class UsersGroupsController(BaseController):
def index(self, format='html'):
"""GET /users_groups: All items in the collection"""
# url('users_groups')
- c.users_groups_list = self.sa.query(UsersGroup).all()
+ c.users_groups_list = UsersGroup().query().all()
return render('admin/users_groups/users_groups.html')
def create(self):
@@ -76,10 +77,12 @@ class UsersGroupsController(BaseController):
form_result = users_group_form.to_python(dict(request.POST))
UsersGroupModel().create(name=form_result['users_group_name'],
active=form_result['users_group_active'])
- h.flash(_('created users group %s') \
- % form_result['users_group_name'], category='success')
- #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
- Session.commit()
+ gr = form_result['users_group_name']
+ action_logger(self.rhodecode_user,
+ 'admin_created_users_group:%s' % gr,
+ None, self.ip_addr, self.sa)
+ h.flash(_('created users group %s') % gr, category='success')
+ Session().commit()
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/users_groups/users_group_add.html'),
@@ -114,7 +117,7 @@ class UsersGroupsController(BaseController):
c.group_members_obj]
c.available_members = [(x.user_id, x.username) for x in
- self.sa.query(User).all()]
+ User.query().all()]
available_members = [safe_unicode(x[0]) for x in c.available_members]
@@ -125,21 +128,27 @@ class UsersGroupsController(BaseController):
try:
form_result = users_group_form.to_python(request.POST)
UsersGroupModel().update(c.users_group, form_result)
- h.flash(_('updated users group %s') \
- % form_result['users_group_name'],
- category='success')
- #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
- Session.commit()
+ gr = form_result['users_group_name']
+ action_logger(self.rhodecode_user,
+ 'admin_updated_users_group:%s' % gr,
+ None, self.ip_addr, self.sa)
+ h.flash(_('updated users group %s') % gr, category='success')
+ Session().commit()
except formencode.Invalid, errors:
+ ug_model = UsersGroupModel()
+ defaults = errors.value
e = errors.error_dict or {}
-
- perm = Permission.get_by_key('hg.create.repository')
- e.update({'create_repo_perm':
- UsersGroupModel().has_perm(id, perm)})
+ defaults.update({
+ 'create_repo_perm': ug_model.has_perm(id,
+ 'hg.create.repository'),
+ 'fork_repo_perm': ug_model.has_perm(id,
+ 'hg.fork.repository'),
+ '_method': 'put'
+ })
return htmlfill.render(
render('admin/users_groups/users_group_edit.html'),
- defaults=errors.value,
+ defaults=defaults,
errors=e,
prefix_error=False,
encoding="UTF-8")
@@ -148,7 +157,7 @@ class UsersGroupsController(BaseController):
h.flash(_('error occurred during update of users group %s') \
% request.POST.get('users_group_name'), category='error')
- return redirect(url('users_groups'))
+ return redirect(url('edit_users_group', id=id))
def delete(self, id):
"""DELETE /users_groups/id: Delete an existing item"""
@@ -158,10 +167,10 @@ class UsersGroupsController(BaseController):
# h.form(url('users_group', id=ID),
# method='delete')
# url('users_group', id=ID)
-
+ usr_gr = UsersGroup.get_or_404(id)
try:
- UsersGroupModel().delete(id)
- Session.commit()
+ UsersGroupModel().delete(usr_gr)
+ Session().commit()
h.flash(_('successfully deleted users group'), category='success')
except UsersGroupsAssignedException, e:
h.flash(e, category='error')
@@ -179,20 +188,23 @@ class UsersGroupsController(BaseController):
"""GET /users_groups/id/edit: Form to edit an existing item"""
# url('edit_users_group', id=ID)
- c.users_group = self.sa.query(UsersGroup).get(id)
- if not c.users_group:
- return redirect(url('users_groups'))
+ c.users_group = UsersGroup.get_or_404(id)
c.users_group.permissions = {}
c.group_members_obj = [x.user for x in c.users_group.members]
c.group_members = [(x.user_id, x.username) for x in
c.group_members_obj]
c.available_members = [(x.user_id, x.username) for x in
- self.sa.query(User).all()]
+ User.query().all()]
+ ug_model = UsersGroupModel()
defaults = c.users_group.get_dict()
- perm = Permission.get_by_key('hg.create.repository')
- defaults.update({'create_repo_perm':
- UsersGroupModel().has_perm(c.users_group, perm)})
+ defaults.update({
+ 'create_repo_perm': ug_model.has_perm(c.users_group,
+ 'hg.create.repository'),
+ 'fork_repo_perm': ug_model.has_perm(c.users_group,
+ 'hg.fork.repository'),
+ })
+
return htmlfill.render(
render('admin/users_groups/users_group_edit.html'),
defaults=defaults,
@@ -204,25 +216,43 @@ class UsersGroupsController(BaseController):
"""PUT /users_perm/id: Update an existing item"""
# url('users_group_perm', id=ID, method='put')
- grant_perm = request.POST.get('create_repo_perm', False)
+ users_group = UsersGroup.get_or_404(id)
+ grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
+ grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
+ inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
- if grant_perm:
- perm = Permission.get_by_key('hg.create.none')
- UsersGroupModel().revoke_perm(id, perm)
+ usersgroup_model = UsersGroupModel()
- perm = Permission.get_by_key('hg.create.repository')
- UsersGroupModel().grant_perm(id, perm)
- h.flash(_("Granted 'repository create' permission to user"),
- category='success')
-
- Session.commit()
- else:
- perm = Permission.get_by_key('hg.create.repository')
- UsersGroupModel().revoke_perm(id, perm)
+ try:
+ users_group.inherit_default_permissions = inherit_perms
+ Session().add(users_group)
+
+ if grant_create_perm:
+ usersgroup_model.revoke_perm(id, 'hg.create.none')
+ usersgroup_model.grant_perm(id, 'hg.create.repository')
+ h.flash(_("Granted 'repository create' permission to users group"),
+ category='success')
+ else:
+ usersgroup_model.revoke_perm(id, 'hg.create.repository')
+ usersgroup_model.grant_perm(id, 'hg.create.none')
+ h.flash(_("Revoked 'repository create' permission to users group"),
+ category='success')
+
+ if grant_fork_perm:
+ usersgroup_model.revoke_perm(id, 'hg.fork.none')
+ usersgroup_model.grant_perm(id, 'hg.fork.repository')
+ h.flash(_("Granted 'repository fork' permission to users group"),
+ category='success')
+ else:
+ usersgroup_model.revoke_perm(id, 'hg.fork.repository')
+ usersgroup_model.grant_perm(id, 'hg.fork.none')
+ h.flash(_("Revoked 'repository fork' permission to users group"),
+ category='success')
+
+ Session().commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during permissions saving'),
+ category='error')
- perm = Permission.get_by_key('hg.create.none')
- UsersGroupModel().grant_perm(id, perm)
- h.flash(_("Revoked 'repository create' permission to user"),
- category='success')
- Session.commit()
return redirect(url('edit_users_group', id=id))
diff --git a/rhodecode/controllers/api/__init__.py b/rhodecode/controllers/api/__init__.py
index 9a12559f..13fd7033 100644
--- a/rhodecode/controllers/api/__init__.py
+++ b/rhodecode/controllers/api/__init__.py
@@ -30,6 +30,7 @@ import logging
import types
import urllib
import traceback
+import time
from rhodecode.lib.compat import izip_longest, json
@@ -43,6 +44,8 @@ HTTPBadRequest, HTTPError
from rhodecode.model.db import User
from rhodecode.lib.auth import AuthUser
+from rhodecode.lib.base import _get_ip_addr, _get_access_path
+from rhodecode.lib.utils2 import safe_unicode
log = logging.getLogger('JSONRPC')
@@ -57,15 +60,16 @@ class JSONRPCError(BaseException):
return str(self.message)
-def jsonrpc_error(message, code=None):
+def jsonrpc_error(message, retid=None, code=None):
"""
Generate a Response object with a JSON-RPC error body
"""
from pylons.controllers.util import Response
- resp = Response(body=json.dumps(dict(id=None, result=None, error=message)),
- status=code,
- content_type='application/json')
- return resp
+ return Response(
+ body=json.dumps(dict(id=retid, result=None, error=message)),
+ status=code,
+ content_type='application/json'
+ )
class JSONRPCController(WSGIController):
@@ -94,9 +98,12 @@ class JSONRPCController(WSGIController):
Parse the request body as JSON, look up the method on the
controller and if it exists, dispatch to it.
"""
+ start = time.time()
+ self._req_id = None
if 'CONTENT_LENGTH' not in environ:
log.debug("No Content-Length")
- return jsonrpc_error(message="No Content-Length in request")
+ return jsonrpc_error(retid=self._req_id,
+ message="No Content-Length in request")
else:
length = environ['CONTENT_LENGTH'] or 0
length = int(environ['CONTENT_LENGTH'])
@@ -104,7 +111,8 @@ class JSONRPCController(WSGIController):
if length == 0:
log.debug("Content-Length is 0")
- return jsonrpc_error(message="Content-Length is 0")
+ return jsonrpc_error(retid=self._req_id,
+ message="Content-Length is 0")
raw_body = environ['wsgi.input'].read(length)
@@ -112,7 +120,8 @@ class JSONRPCController(WSGIController):
json_body = json.loads(urllib.unquote_plus(raw_body))
except ValueError, e:
# catch JSON errors Here
- return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
+ return jsonrpc_error(retid=self._req_id,
+ message="JSON parse error ERR:%s RAW:%r" \
% (e, urllib.unquote_plus(raw_body)))
# check AUTH based on API KEY
@@ -126,22 +135,26 @@ class JSONRPCController(WSGIController):
self._request_params)
)
except KeyError, e:
- return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
+ return jsonrpc_error(retid=self._req_id,
+ message='Incorrect JSON query missing %s' % e)
# check if we can find this session using api_key
try:
u = User.get_by_api_key(self._req_api_key)
if u is None:
- return jsonrpc_error(message='Invalid API KEY')
+ return jsonrpc_error(retid=self._req_id,
+ message='Invalid API KEY')
auth_u = AuthUser(u.user_id, self._req_api_key)
except Exception, e:
- return jsonrpc_error(message='Invalid API KEY')
+ return jsonrpc_error(retid=self._req_id,
+ message='Invalid API KEY')
self._error = None
try:
self._func = self._find_method()
except AttributeError, e:
- return jsonrpc_error(message=str(e))
+ return jsonrpc_error(retid=self._req_id,
+ message=str(e))
# now that we have a method, add self._req_params to
# self.kargs and dispatch control to WGIController
@@ -164,9 +177,12 @@ class JSONRPCController(WSGIController):
USER_SESSION_ATTR = 'apiuser'
if USER_SESSION_ATTR not in arglist:
- return jsonrpc_error(message='This method [%s] does not support '
- 'authentication (missing %s param)' %
- (self._func.__name__, USER_SESSION_ATTR))
+ return jsonrpc_error(
+ retid=self._req_id,
+ message='This method [%s] does not support '
+ 'authentication (missing %s param)' % (
+ self._func.__name__, USER_SESSION_ATTR)
+ )
# get our arglist and check if we provided them as args
for arg, default in func_kwargs.iteritems():
@@ -179,6 +195,7 @@ class JSONRPCController(WSGIController):
# NotImplementedType (default_empty)
if (default == default_empty and arg not in self._request_params):
return jsonrpc_error(
+ retid=self._req_id,
message=(
'Missing non optional `%s` arg in JSON DATA' % arg
)
@@ -205,7 +222,10 @@ class JSONRPCController(WSGIController):
headers.append(('Content-Length', str(len(output[0]))))
replace_header(headers, 'Content-Type', 'application/json')
start_response(status[0], headers, exc_info[0])
-
+ log.info('IP: %s Request to %s time: %.3fs' % (
+ _get_ip_addr(environ),
+ safe_unicode(_get_access_path(environ)), time.time() - start)
+ )
return output
def _dispatch_call(self):
diff --git a/rhodecode/controllers/api/api.py b/rhodecode/controllers/api/api.py
index 60b5ce22..b88e8d41 100644
--- a/rhodecode/controllers/api/api.py
+++ b/rhodecode/controllers/api/api.py
@@ -31,18 +31,100 @@ import logging
from rhodecode.controllers.api import JSONRPCController, JSONRPCError
from rhodecode.lib.auth import HasPermissionAllDecorator, \
HasPermissionAnyDecorator, PasswordGenerator, AuthUser
-
+from rhodecode.lib.utils import map_groups, repo2db_mapper
from rhodecode.model.meta import Session
from rhodecode.model.scm import ScmModel
-from rhodecode.model.db import User, UsersGroup, Repository
from rhodecode.model.repo import RepoModel
from rhodecode.model.user import UserModel
from rhodecode.model.users_group import UsersGroupModel
-from rhodecode.lib.utils import map_groups
+from rhodecode.model.permission import PermissionModel
+from rhodecode.model.db import Repository
log = logging.getLogger(__name__)
+class Optional(object):
+ """
+ Defines an optional parameter::
+
+ param = param.getval() if isinstance(param, Optional) else param
+ param = param() if isinstance(param, Optional) else param
+
+ is equivalent of::
+
+ param = Optional.extract(param)
+
+ """
+ def __init__(self, type_):
+ self.type_ = type_
+
+ def __repr__(self):
+ return '<Optional:%s>' % self.type_.__repr__()
+
+ def __call__(self):
+ return self.getval()
+
+ def getval(self):
+ """
+ returns value from this Optional instance
+ """
+ return self.type_
+
+ @classmethod
+ def extract(cls, val):
+ if isinstance(val, cls):
+ return val.getval()
+ return val
+
+
+def get_user_or_error(userid):
+ """
+ Get user by id or name or return JsonRPCError if not found
+
+ :param userid:
+ """
+ user = UserModel().get_user(userid)
+ if user is None:
+ raise JSONRPCError("user `%s` does not exist" % userid)
+ return user
+
+
+def get_repo_or_error(repoid):
+ """
+ Get repo by id or name or return JsonRPCError if not found
+
+ :param userid:
+ """
+ repo = RepoModel().get_repo(repoid)
+ if repo is None:
+ raise JSONRPCError('repository `%s` does not exist' % (repoid))
+ return repo
+
+
+def get_users_group_or_error(usersgroupid):
+ """
+ Get users group by id or name or return JsonRPCError if not found
+
+ :param userid:
+ """
+ users_group = UsersGroupModel().get_group(usersgroupid)
+ if users_group is None:
+ raise JSONRPCError('users group `%s` does not exist' % usersgroupid)
+ return users_group
+
+
+def get_perm_or_error(permid):
+ """
+ Get permission by id or name or return JsonRPCError if not found
+
+ :param userid:
+ """
+ perm = PermissionModel().get_permission_by_name(permid)
+ if perm is None:
+ raise JSONRPCError('permission `%s` does not exist' % (permid))
+ return perm
+
+
class ApiController(JSONRPCController):
"""
API Controller
@@ -60,23 +142,74 @@ class ApiController(JSONRPCController):
"""
@HasPermissionAllDecorator('hg.admin')
- def pull(self, apiuser, repo_name):
+ def pull(self, apiuser, repoid):
"""
Dispatch pull action on given repo
+ :param apiuser:
+ :param repoid:
+ """
+
+ repo = get_repo_or_error(repoid)
- :param user:
- :param repo_name:
+ try:
+ ScmModel().pull_changes(repo.repo_name,
+ self.rhodecode_user.username)
+ return 'Pulled from `%s`' % repo.repo_name
+ except Exception:
+ log.error(traceback.format_exc())
+ raise JSONRPCError(
+ 'Unable to pull changes from `%s`' % repo.repo_name
+ )
+
+ @HasPermissionAllDecorator('hg.admin')
+ def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
"""
+ Dispatch rescan repositories action. If remove_obsolete is set
+ than also delete repos that are in database but not in the filesystem.
+ aka "clean zombies"
- if Repository.is_valid(repo_name) is False:
- raise JSONRPCError('Unknown repo "%s"' % repo_name)
+ :param apiuser:
+ :param remove_obsolete:
+ """
try:
- ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
- return 'Pulled from %s' % repo_name
+ rm_obsolete = Optional.extract(remove_obsolete)
+ added, removed = repo2db_mapper(ScmModel().repo_scan(),
+ remove_obsolete=rm_obsolete)
+ return {'added': added, 'removed': removed}
except Exception:
- raise JSONRPCError('Unable to pull changes from "%s"' % repo_name)
+ log.error(traceback.format_exc())
+ raise JSONRPCError(
+ 'Error occurred during rescan repositories action'
+ )
+
+ @HasPermissionAllDecorator('hg.admin')
+ def lock(self, apiuser, repoid, userid, locked):
+ """
+ Set locking state on particular repository by given user
+
+ :param apiuser:
+ :param repoid:
+ :param userid:
+ :param locked:
+ """
+ repo = get_repo_or_error(repoid)
+ user = get_user_or_error(userid)
+ locked = bool(locked)
+ try:
+ if locked:
+ Repository.lock(repo, user.user_id)
+ else:
+ Repository.unlock(repo)
+
+ return ('User `%s` set lock state for repo `%s` to `%s`'
+ % (user.username, repo.repo_name, locked))
+ except Exception:
+ log.error(traceback.format_exc())
+ raise JSONRPCError(
+ 'Error occurred locking repository `%s`' % repo.repo_name
+ )
@HasPermissionAllDecorator('hg.admin')
def get_user(self, apiuser, userid):
@@ -84,25 +217,13 @@ class ApiController(JSONRPCController):
Get a user by username
:param apiuser:
- :param username:
+ :param userid:
"""
- user = UserModel().get_user(userid)
- if user is None:
- return user
-
- return dict(
- id=user.user_id,
- username=user.username,
- firstname=user.name,
- lastname=user.lastname,
- email=user.email,
- active=user.active,
- admin=user.admin,
- ldap_dn=user.ldap_dn,
- last_login=user.last_login,
- permissions=AuthUser(user_id=user.user_id).permissions
- )
+ user = get_user_or_error(userid)
+ data = user.get_api_data()
+ data['permissions'] = AuthUser(user_id=user.user_id).permissions
+ return data
@HasPermissionAllDecorator('hg.admin')
def get_users(self, apiuser):
@@ -113,124 +234,150 @@ class ApiController(JSONRPCController):
"""
result = []
- for user in User.getAll():
- result.append(
- dict(
- id=user.user_id,
- username=user.username,
- firstname=user.name,
- lastname=user.lastname,
- email=user.email,
- active=user.active,
- admin=user.admin,
- ldap_dn=user.ldap_dn,
- last_login=user.last_login,
- )
- )
+ for user in UserModel().get_all():
+ result.append(user.get_api_data())
return result
@HasPermissionAllDecorator('hg.admin')
- def create_user(self, apiuser, username, email, password, firstname=None,
- lastname=None, active=True, admin=False, ldap_dn=None):
+ def create_user(self, apiuser, username, email, password,
+ firstname=Optional(None), lastname=Optional(None),
+ active=Optional(True), admin=Optional(False),
+ ldap_dn=Optional(None)):
"""
Create new user
:param apiuser:
:param username:
- :param password:
:param email:
- :param name:
+ :param password:
+ :param firstname:
:param lastname:
:param active:
:param admin:
:param ldap_dn:
"""
- if User.get_by_username(username):
- raise JSONRPCError("user %s already exist" % username)
- if User.get_by_email(email, case_insensitive=True):
- raise JSONRPCError("email %s already exist" % email)
+ if UserModel().get_by_username(username):
+ raise JSONRPCError("user `%s` already exist" % username)
+
+ if UserModel().get_by_email(email, case_insensitive=True):
+ raise JSONRPCError("email `%s` already exist" % email)
- if ldap_dn:
+ if Optional.extract(ldap_dn):
# generate temporary password if ldap_dn
password = PasswordGenerator().gen_password(length=8)
try:
- usr = UserModel().create_or_update(
- username, password, email, firstname,
- lastname, active, admin, ldap_dn
+ user = UserModel().create_or_update(
+ username=Optional.extract(username),
+ password=Optional.extract(password),
+ email=Optional.extract(email),
+ firstname=Optional.extract(firstname),
+ lastname=Optional.extract(lastname),
+ active=Optional.extract(active),
+ admin=Optional.extract(admin),
+ ldap_dn=Optional.extract(ldap_dn)
)
- Session.commit()
+ Session().commit()
return dict(
- id=usr.user_id,
- msg='created new user %s' % username
+ msg='created new user `%s`' % username,
+ user=user.get_api_data()
)
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to create user %s' % username)
+ raise JSONRPCError('failed to create user `%s`' % username)
@HasPermissionAllDecorator('hg.admin')
- def update_user(self, apiuser, userid, username, password, email,
- firstname, lastname, active, admin, ldap_dn):
+ def update_user(self, apiuser, userid, username=Optional(None),
+ email=Optional(None), firstname=Optional(None),
+ lastname=Optional(None), active=Optional(None),
+ admin=Optional(None), ldap_dn=Optional(None),
+ password=Optional(None)):
"""
Updates given user
:param apiuser:
+ :param userid:
:param username:
- :param password:
:param email:
- :param name:
+ :param firstname:
:param lastname:
:param active:
:param admin:
:param ldap_dn:
+ :param password:
"""
- if not UserModel().get_user(userid):
- raise JSONRPCError("user %s does not exist" % username)
+
+ user = get_user_or_error(userid)
+
+ # call function and store only updated arguments
+ updates = {}
+
+ def store_update(attr, name):
+ if not isinstance(attr, Optional):
+ updates[name] = attr
try:
- usr = UserModel().create_or_update(
- username, password, email, firstname,
- lastname, active, admin, ldap_dn
+
+ store_update(username, 'username')
+ store_update(password, 'password')
+ store_update(email, 'email')
+ store_update(firstname, 'name')
+ store_update(lastname, 'lastname')
+ store_update(active, 'active')
+ store_update(admin, 'admin')
+ store_update(ldap_dn, 'ldap_dn')
+
+ user = UserModel().update_user(user, **updates)
+ Session().commit()
+ return dict(
+ msg='updated user ID:%s %s' % (user.user_id, user.username),
+ user=user.get_api_data()
)
- Session.commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ raise JSONRPCError('failed to update user `%s`' % userid)
+
+ @HasPermissionAllDecorator('hg.admin')
+ def delete_user(self, apiuser, userid):
+ """"
+ Deletes an user
+
+ :param apiuser:
+ :param userid:
+ """
+ user = get_user_or_error(userid)
+
+ try:
+ UserModel().delete(userid)
+ Session().commit()
return dict(
- id=usr.user_id,
- msg='updated user %s' % username
+ msg='deleted user ID:%s %s' % (user.user_id, user.username),
+ user=None
)
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to update user %s' % username)
+ raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
+ user.username))
@HasPermissionAllDecorator('hg.admin')
- def get_users_group(self, apiuser, group_name):
+ def get_users_group(self, apiuser, usersgroupid):
""""
- Get users group by name
+ Get users group by name or id
:param apiuser:
- :param group_name:
+ :param usersgroupid:
"""
+ users_group = get_users_group_or_error(usersgroupid)
- users_group = UsersGroup.get_by_group_name(group_name)
- if not users_group:
- return None
+ data = users_group.get_api_data()
members = []
for user in users_group.members:
user = user.user
- members.append(dict(id=user.user_id,
- username=user.username,
- firstname=user.name,
- lastname=user.lastname,
- email=user.email,
- active=user.active,
- admin=user.admin,
- ldap=user.ldap_dn))
-
- return dict(id=users_group.users_group_id,
- group_name=users_group.users_group_name,
- active=users_group.users_group_active,
- members=members)
+ members.append(user.get_api_data())
+ data['members'] = members
+ return data
@HasPermissionAllDecorator('hg.admin')
def get_users_groups(self, apiuser):
@@ -241,107 +388,96 @@ class ApiController(JSONRPCController):
"""
result = []
- for users_group in UsersGroup.getAll():
- members = []
- for user in users_group.members:
- user = user.user
- members.append(dict(id=user.user_id,
- username=user.username,
- firstname=user.name,
- lastname=user.lastname,
- email=user.email,
- active=user.active,
- admin=user.admin,
- ldap=user.ldap_dn))
-
- result.append(dict(id=users_group.users_group_id,
- group_name=users_group.users_group_name,
- active=users_group.users_group_active,
- members=members))
+ for users_group in UsersGroupModel().get_all():
+ result.append(users_group.get_api_data())
return result
@HasPermissionAllDecorator('hg.admin')
- def create_users_group(self, apiuser, group_name, active=True):
+ def create_users_group(self, apiuser, group_name, active=Optional(True)):
"""
Creates an new usergroup
+ :param apiuser:
:param group_name:
:param active:
"""
- if self.get_users_group(apiuser, group_name):
- raise JSONRPCError("users group %s already exist" % group_name)
+ if UsersGroupModel().get_by_name(group_name):
+ raise JSONRPCError("users group `%s` already exist" % group_name)
try:
+ active = Optional.extract(active)
ug = UsersGroupModel().create(name=group_name, active=active)
- Session.commit()
- return dict(id=ug.users_group_id,
- msg='created new users group %s' % group_name)
+ Session().commit()
+ return dict(
+ msg='created new users group `%s`' % group_name,
+ users_group=ug.get_api_data()
+ )
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to create group %s' % group_name)
+ raise JSONRPCError('failed to create group `%s`' % group_name)
@HasPermissionAllDecorator('hg.admin')
- def add_user_to_users_group(self, apiuser, group_name, username):
+ def add_user_to_users_group(self, apiuser, usersgroupid, userid):
""""
Add a user to a users group
:param apiuser:
- :param group_name:
- :param username:
+ :param usersgroupid:
+ :param userid:
"""
+ user = get_user_or_error(userid)
+ users_group = get_users_group_or_error(usersgroupid)
try:
- users_group = UsersGroup.get_by_group_name(group_name)
- if not users_group:
- raise JSONRPCError('unknown users group %s' % group_name)
-
- user = User.get_by_username(username)
- if user is None:
- raise JSONRPCError('unknown user %s' % username)
-
ugm = UsersGroupModel().add_user_to_group(users_group, user)
success = True if ugm != True else False
- msg = 'added member %s to users group %s' % (username, group_name)
+ msg = 'added member `%s` to users group `%s`' % (
+ user.username, users_group.users_group_name
+ )
msg = msg if success else 'User is already in that group'
- Session.commit()
+ Session().commit()
return dict(
- id=ugm.users_group_member_id if ugm != True else None,
success=success,
msg=msg
)
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to add users group member')
+ raise JSONRPCError(
+ 'failed to add member to users group `%s`' % (
+ users_group.users_group_name
+ )
+ )
@HasPermissionAllDecorator('hg.admin')
- def remove_user_from_users_group(self, apiuser, group_name, username):
+ def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
"""
Remove user from a group
- :param apiuser
- :param group_name
- :param username
+ :param apiuser:
+ :param usersgroupid:
+ :param userid:
"""
+ user = get_user_or_error(userid)
+ users_group = get_users_group_or_error(usersgroupid)
try:
- users_group = UsersGroup.get_by_group_name(group_name)
- if not users_group:
- raise JSONRPCError('unknown users group %s' % group_name)
-
- user = User.get_by_username(username)
- if user is None:
- raise JSONRPCError('unknown user %s' % username)
-
- success = UsersGroupModel().remove_user_from_group(users_group, user)
- msg = 'removed member %s from users group %s' % (username, group_name)
+ success = UsersGroupModel().remove_user_from_group(users_group,
+ user)
+ msg = 'removed member `%s` from users group `%s`' % (
+ user.username, users_group.users_group_name
+ )
msg = msg if success else "User wasn't in group"
- Session.commit()
+ Session().commit()
return dict(success=success, msg=msg)
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to remove user from group')
+ raise JSONRPCError(
+ 'failed to remove member from users group `%s`' % (
+ users_group.users_group_name
+ )
+ )
@HasPermissionAnyDecorator('hg.admin')
def get_repo(self, apiuser, repoid):
@@ -349,51 +485,30 @@ class ApiController(JSONRPCController):
Get repository by name
:param apiuser:
- :param repo_name:
+ :param repoid:
"""
-
- repo = RepoModel().get_repo(repoid)
- if repo is None:
- raise JSONRPCError('unknown repository %s' % repo)
+ repo = get_repo_or_error(repoid)
members = []
for user in repo.repo_to_perm:
perm = user.permission.permission_name
user = user.user
- members.append(
- dict(
- type="user",
- id=user.user_id,
- username=user.username,
- firstname=user.name,
- lastname=user.lastname,
- email=user.email,
- active=user.active,
- admin=user.admin,
- ldap=user.ldap_dn,
- permission=perm
- )
- )
+ user_data = user.get_api_data()
+ user_data['type'] = "user"
+ user_data['permission'] = perm
+ members.append(user_data)
+
for users_group in repo.users_group_to_perm:
perm = users_group.permission.permission_name
users_group = users_group.users_group
- members.append(
- dict(
- type="users_group",
- id=users_group.users_group_id,
- name=users_group.users_group_name,
- active=users_group.users_group_active,
- permission=perm
- )
- )
+ users_group_data = users_group.get_api_data()
+ users_group_data['type'] = "users_group"
+ users_group_data['permission'] = perm
+ members.append(users_group_data)
- return dict(
- id=repo.repo_id,
- repo_name=repo.repo_name,
- type=repo.repo_type,
- description=repo.description,
- members=members
- )
+ data = repo.get_api_data()
+ data['members'] = members
+ return data
@HasPermissionAnyDecorator('hg.admin')
def get_repos(self, apiuser):
@@ -404,19 +519,12 @@ class ApiController(JSONRPCController):
"""
result = []
- for repository in Repository.getAll():
- result.append(
- dict(
- id=repository.repo_id,
- repo_name=repository.repo_name,
- type=repository.repo_type,
- description=repository.description
- )
- )
+ for repo in RepoModel().get_all():
+ result.append(repo.get_api_data())
return result
@HasPermissionAnyDecorator('hg.admin')
- def get_repo_nodes(self, apiuser, repo_name, revision, root_path,
+ def get_repo_nodes(self, apiuser, repoid, revision, root_path,
ret_type='all'):
"""
returns a list of nodes and it's children
@@ -424,13 +532,14 @@ class ApiController(JSONRPCController):
to show only files or dirs
:param apiuser:
- :param repo_name: name of repository
+ :param repoid: name or id of repository
:param revision: revision for which listing should be done
:param root_path: path from which start displaying
:param ret_type: return type 'all|files|dirs' nodes
"""
+ repo = get_repo_or_error(repoid)
try:
- _d, _f = ScmModel().get_nodes(repo_name, revision, root_path,
+ _d, _f = ScmModel().get_nodes(repo, revision, root_path,
flat=False)
_map = {
'all': _d + _f,
@@ -440,218 +549,264 @@ class ApiController(JSONRPCController):
return _map[ret_type]
except KeyError:
raise JSONRPCError('ret_type must be one of %s' % _map.keys())
- except Exception, e:
- raise JSONRPCError(e)
+ except Exception:
+ log.error(traceback.format_exc())
+ raise JSONRPCError(
+ 'failed to get repo: `%s` nodes' % repo.repo_name
+ )
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
- def create_repo(self, apiuser, repo_name, owner_name, description='',
- repo_type='hg', private=False, clone_uri=None):
+ def create_repo(self, apiuser, repo_name, owner, repo_type,
+ description=Optional(''), private=Optional(False),
+ clone_uri=Optional(None), landing_rev=Optional('tip')):
"""
Create repository, if clone_url is given it makes a remote clone
+ if repo_name is withina group name the groups will be created
+ automatically if they aren't present
:param apiuser:
:param repo_name:
- :param owner_name:
- :param description:
+ :param onwer:
:param repo_type:
+ :param description:
:param private:
:param clone_uri:
+ :param landing_rev:
"""
+ owner = get_user_or_error(owner)
- try:
- owner = User.get_by_username(owner_name)
- if owner is None:
- raise JSONRPCError('unknown user %s' % owner_name)
+ if RepoModel().get_by_repo_name(repo_name):
+ raise JSONRPCError("repo `%s` already exist" % repo_name)
- if Repository.get_by_repo_name(repo_name):
- raise JSONRPCError("repo %s already exist" % repo_name)
+ private = Optional.extract(private)
+ clone_uri = Optional.extract(clone_uri)
+ description = Optional.extract(description)
+ landing_rev = Optional.extract(landing_rev)
- groups = repo_name.split(Repository.url_sep())
- real_name = groups[-1]
- # create structure of groups
+ try:
+ # create structure of groups and return the last group
group = map_groups(repo_name)
- repo = RepoModel().create(
- dict(
- repo_name=real_name,
- repo_name_full=repo_name,
- description=description,
- private=private,
- repo_type=repo_type,
- repo_group=group.group_id if group else None,
- clone_uri=clone_uri
- ),
- owner
+ repo = RepoModel().create_repo(
+ repo_name=repo_name,
+ repo_type=repo_type,
+ description=description,
+ owner=owner,
+ private=private,
+ clone_uri=clone_uri,
+ repos_group=group,
+ landing_rev=landing_rev,
)
- Session.commit()
+
+ Session().commit()
return dict(
- id=repo.repo_id,
- msg="Created new repository %s" % repo.repo_name
+ msg="Created new repository `%s`" % (repo.repo_name),
+ repo=repo.get_api_data()
)
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to create repository %s' % repo_name)
+ raise JSONRPCError('failed to create repository `%s`' % repo_name)
@HasPermissionAnyDecorator('hg.admin')
- def delete_repo(self, apiuser, repo_name):
+ def fork_repo(self, apiuser, repoid, fork_name, owner,
+ description=Optional(''), copy_permissions=Optional(False),
+ private=Optional(False), landing_rev=Optional('tip')):
+ repo = get_repo_or_error(repoid)
+ repo_name = repo.repo_name
+ owner = get_user_or_error(owner)
+
+ _repo = RepoModel().get_by_repo_name(fork_name)
+ if _repo:
+ type_ = 'fork' if _repo.fork else 'repo'
+ raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
+
+ try:
+ # create structure of groups and return the last group
+ group = map_groups(fork_name)
+
+ form_data = dict(
+ repo_name=fork_name,
+ repo_name_full=fork_name,
+ repo_group=group,
+ repo_type=repo.repo_type,
+ description=Optional.extract(description),
+ private=Optional.extract(private),
+ copy_permissions=Optional.extract(copy_permissions),
+ landing_rev=Optional.extract(landing_rev),
+ update_after_clone=False,
+ fork_parent_id=repo.repo_id,
+ )
+ RepoModel().create_fork(form_data, cur_user=owner)
+ return dict(
+ msg='Created fork of `%s` as `%s`' % (repo.repo_name,
+ fork_name),
+ success=True # cannot return the repo data here since fork
+ # cann be done async
+ )
+ except Exception:
+ log.error(traceback.format_exc())
+ raise JSONRPCError(
+ 'failed to fork repository `%s` as `%s`' % (repo_name,
+ fork_name)
+ )
+
+ @HasPermissionAnyDecorator('hg.admin')
+ def delete_repo(self, apiuser, repoid):
"""
Deletes a given repository
- :param repo_name:
+ :param apiuser:
+ :param repoid:
"""
- if not Repository.get_by_repo_name(repo_name):
- raise JSONRPCError("repo %s does not exist" % repo_name)
+ repo = get_repo_or_error(repoid)
+
try:
- RepoModel().delete(repo_name)
- Session.commit()
+ RepoModel().delete(repo)
+ Session().commit()
return dict(
- msg='Deleted repository %s' % repo_name
+ msg='Deleted repository `%s`' % repo.repo_name,
+ success=True
)
except Exception:
log.error(traceback.format_exc())
- raise JSONRPCError('failed to delete repository %s' % repo_name)
+ raise JSONRPCError(
+ 'failed to delete repository `%s`' % repo.repo_name
+ )
@HasPermissionAnyDecorator('hg.admin')
- def grant_user_permission(self, apiuser, repo_name, username, perm):
+ def grant_user_permission(self, apiuser, repoid, userid, perm):
"""
Grant permission for user on given repository, or update existing one
if found
- :param repo_name:
- :param username:
+ :param repoid:
+ :param userid:
:param perm:
"""
+ repo = get_repo_or_error(repoid)
+ user = get_user_or_error(userid)
+ perm = get_perm_or_error(perm)
try:
- repo = Repository.get_by_repo_name(repo_name)
- if repo is None:
- raise JSONRPCError('unknown repository %s' % repo)
-
- user = User.get_by_username(username)
- if user is None:
- raise JSONRPCError('unknown user %s' % username)
RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
- Session.commit()
+ Session().commit()
return dict(
- msg='Granted perm: %s for user: %s in repo: %s' % (
- perm, username, repo_name
- )
+ msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
+ perm.permission_name, user.username, repo.repo_name
+ ),
+ success=True
)
except Exception:
log.error(traceback.format_exc())
raise JSONRPCError(
- 'failed to edit permission %(repo)s for %(user)s' % dict(
- user=username, repo=repo_name
+ 'failed to edit permission for user: `%s` in repo: `%s`' % (
+ userid, repoid
)
)
@HasPermissionAnyDecorator('hg.admin')
- def revoke_user_permission(self, apiuser, repo_name, username):
+ def revoke_user_permission(self, apiuser, repoid, userid):
"""
Revoke permission for user on given repository
- :param repo_name:
- :param username:
+ :param apiuser:
+ :param repoid:
+ :param userid:
"""
+ repo = get_repo_or_error(repoid)
+ user = get_user_or_error(userid)
try:
- repo = Repository.get_by_repo_name(repo_name)
- if repo is None:
- raise JSONRPCError('unknown repository %s' % repo)
- user = User.get_by_username(username)
- if user is None:
- raise JSONRPCError('unknown user %s' % username)
+ RepoModel().revoke_user_permission(repo=repo, user=user)
- RepoModel().revoke_user_permission(repo=repo_name, user=username)
-
- Session.commit()
+ Session().commit()
return dict(
- msg='Revoked perm for user: %s in repo: %s' % (
- username, repo_name
- )
+ msg='Revoked perm for user: `%s` in repo: `%s`' % (
+ user.username, repo.repo_name
+ ),
+ success=True
)
except Exception:
log.error(traceback.format_exc())
raise JSONRPCError(
- 'failed to edit permission %(repo)s for %(user)s' % dict(
- user=username, repo=repo_name
+ 'failed to edit permission for user: `%s` in repo: `%s`' % (
+ userid, repoid
)
)
@HasPermissionAnyDecorator('hg.admin')
- def grant_users_group_permission(self, apiuser, repo_name, group_name, perm):
+ def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
+ perm):
"""
Grant permission for users group on given repository, or update
existing one if found
- :param repo_name:
- :param group_name:
+ :param apiuser:
+ :param repoid:
+ :param usersgroupid:
:param perm:
"""
+ repo = get_repo_or_error(repoid)
+ perm = get_perm_or_error(perm)
+ users_group = get_users_group_or_error(usersgroupid)
try:
- repo = Repository.get_by_repo_name(repo_name)
- if repo is None:
- raise JSONRPCError('unknown repository %s' % repo)
-
- user_group = UsersGroup.get_by_group_name(group_name)
- if user_group is None:
- raise JSONRPCError('unknown users group %s' % user_group)
-
- RepoModel().grant_users_group_permission(repo=repo_name,
- group_name=group_name,
+ RepoModel().grant_users_group_permission(repo=repo,
+ group_name=users_group,
perm=perm)
- Session.commit()
+ Session().commit()
return dict(
- msg='Granted perm: %s for group: %s in repo: %s' % (
- perm, group_name, repo_name
- )
+ msg='Granted perm: `%s` for users group: `%s` in '
+ 'repo: `%s`' % (
+ perm.permission_name, users_group.users_group_name,
+ repo.repo_name
+ ),
+ success=True
)
except Exception:
+ print traceback.format_exc()
log.error(traceback.format_exc())
raise JSONRPCError(
- 'failed to edit permission %(repo)s for %(usersgr)s' % dict(
- usersgr=group_name, repo=repo_name
+ 'failed to edit permission for users group: `%s` in '
+ 'repo: `%s`' % (
+ usersgroupid, repo.repo_name
)
)
@HasPermissionAnyDecorator('hg.admin')
- def revoke_users_group_permission(self, apiuser, repo_name, group_name):
+ def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
"""
Revoke permission for users group on given repository
- :param repo_name:
- :param group_name:
+ :param apiuser:
+ :param repoid:
+ :param usersgroupid:
"""
+ repo = get_repo_or_error(repoid)
+ users_group = get_users_group_or_error(usersgroupid)
try:
- repo = Repository.get_by_repo_name(repo_name)
- if repo is None:
- raise JSONRPCError('unknown repository %s' % repo)
-
- user_group = UsersGroup.get_by_group_name(group_name)
- if user_group is None:
- raise JSONRPCError('unknown users group %s' % user_group)
-
- RepoModel().revoke_users_group_permission(repo=repo_name,
- group_name=group_name)
+ RepoModel().revoke_users_group_permission(repo=repo,
+ group_name=users_group)
- Session.commit()
+ Session().commit()
return dict(
- msg='Revoked perm for group: %s in repo: %s' % (
- group_name, repo_name
- )
+ msg='Revoked perm for users group: `%s` in repo: `%s`' % (
+ users_group.users_group_name, repo.repo_name
+ ),
+ success=True
)
except Exception:
log.error(traceback.format_exc())
raise JSONRPCError(
- 'failed to edit permission %(repo)s for %(usersgr)s' % dict(
- usersgr=group_name, repo=repo_name
+ 'failed to edit permission for users group: `%s` in '
+ 'repo: `%s`' % (
+ users_group.users_group_name, repo.repo_name
)
)
diff --git a/rhodecode/controllers/branches.py b/rhodecode/controllers/branches.py
index eead9f51..75726da4 100644
--- a/rhodecode/controllers/branches.py
+++ b/rhodecode/controllers/branches.py
@@ -24,14 +24,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
+import binascii
from pylons import tmpl_context as c
-import binascii
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.utils2 import safe_unicode
+
log = logging.getLogger(__name__)
diff --git a/rhodecode/controllers/changelog.py b/rhodecode/controllers/changelog.py
index fe989078..4afdbf0d 100644
--- a/rhodecode/controllers/changelog.py
+++ b/rhodecode/controllers/changelog.py
@@ -26,7 +26,6 @@
import logging
import traceback
-from mercurial import graphmod
from pylons import request, url, session, tmpl_context as c
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
@@ -36,9 +35,8 @@ from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.helpers import RepoPage
from rhodecode.lib.compat import json
-
+from rhodecode.lib.graphmod import _colored, _dagwalker
from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError
-from rhodecode.model.db import Repository
log = logging.getLogger(__name__)
@@ -60,13 +58,13 @@ class ChangelogController(BaseRepoController):
int_size = int(request.params.get('size'))
except ValueError:
int_size = default
- int_size = int_size if int_size <= limit else limit
- c.size = int_size
+ c.size = max(min(int_size, limit), 1)
session['changelog_size'] = c.size
session.save()
else:
c.size = int(session.get('changelog_size', default))
-
+ # min size must be 1
+ c.size = max(c.size, 1)
p = int(request.params.get('page', 1))
branch_name = request.params.get('branch', None)
try:
@@ -83,8 +81,8 @@ class ChangelogController(BaseRepoController):
items_per_page=c.size, branch=branch_name)
collection = list(c.pagination)
page_revisions = [x.raw_id for x in collection]
- c.comments = c.rhodecode_db_repo.comments(page_revisions)
-
+ c.comments = c.rhodecode_db_repo.get_comments(page_revisions)
+ c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
log.error(traceback.format_exc())
h.flash(str(e), category='warning')
@@ -118,18 +116,9 @@ class ChangelogController(BaseRepoController):
data = []
revs = [x.revision for x in collection]
- if repo.alias == 'git':
- for _ in revs:
- vtx = [0, 1]
- edges = [[0, 0, 1]]
- data.append(['', vtx, edges])
-
- elif repo.alias == 'hg':
- dag = graphmod.dagwalker(repo._repo, revs)
- c.dag = graphmod.colored(dag, repo._repo)
- for (id, type, ctx, vtx, edges) in c.dag:
- if type != graphmod.CHANGESET:
- continue
- data.append(['', vtx, edges])
+ dag = _dagwalker(repo, revs, repo.alias)
+ dag = _colored(dag)
+ for (id, type, ctx, vtx, edges) in dag:
+ data.append(['', vtx, edges])
c.jsdata = json.dumps(data)
diff --git a/rhodecode/controllers/changeset.py b/rhodecode/controllers/changeset.py
index 27953497..919ec411 100644
--- a/rhodecode/controllers/changeset.py
+++ b/rhodecode/controllers/changeset.py
@@ -40,13 +40,17 @@ from rhodecode.lib.vcs.nodes import FileNode
import rhodecode.lib.helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
-from rhodecode.lib.utils import EmptyChangeset
+from rhodecode.lib.utils import action_logger
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib import diffs
-from rhodecode.model.db import ChangesetComment
+from rhodecode.model.db import ChangesetComment, ChangesetStatus
from rhodecode.model.comment import ChangesetCommentsModel
+from rhodecode.model.changeset_status import ChangesetStatusModel
from rhodecode.model.meta import Session
from rhodecode.lib.diffs import wrapped_diff
+from rhodecode.model.repo import RepoModel
+from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
log = logging.getLogger(__name__)
@@ -165,6 +169,9 @@ class ChangesetController(BaseRepoController):
def __before__(self):
super(ChangesetController, self).__before__()
c.affected_files_cut_off = 60
+ repo_model = RepoModel()
+ c.users_array = repo_model.get_users_js()
+ c.users_groups_array = repo_model.get_users_groups_js()
def index(self, revision):
@@ -201,18 +208,24 @@ class ChangesetController(BaseRepoController):
cumulative_diff = 0
c.cut_off = False # defines if cut off limit is reached
-
+ c.changeset_statuses = ChangesetStatus.STATUSES
c.comments = []
+ c.statuses = []
c.inline_comments = []
c.inline_cnt = 0
# Iterate over ranges (default changeset view is always one changeset)
for changeset in c.cs_ranges:
+
+ c.statuses.extend([ChangesetStatusModel()\
+ .get_status(c.rhodecode_db_repo.repo_id,
+ changeset.raw_id)])
+
c.comments.extend(ChangesetCommentsModel()\
.get_comments(c.rhodecode_db_repo.repo_id,
- changeset.raw_id))
+ revision=changeset.raw_id))
inlines = ChangesetCommentsModel()\
.get_inline_comments(c.rhodecode_db_repo.repo_id,
- changeset.raw_id)
+ revision=changeset.raw_id)
c.inline_comments.extend(inlines)
c.changes[changeset.raw_id] = []
try:
@@ -284,7 +297,7 @@ class ChangesetController(BaseRepoController):
)
# count inline comments
- for path, lines in c.inline_comments:
+ for __, lines in c.inline_comments:
for comments in lines.values():
c.inline_cnt += len(comments)
@@ -361,15 +374,48 @@ class ChangesetController(BaseRepoController):
@jsonify
def comment(self, repo_name, revision):
+ status = request.POST.get('changeset_status')
+ change_status = request.POST.get('change_changeset_status')
+
comm = ChangesetCommentsModel().create(
text=request.POST.get('text'),
- repo_id=c.rhodecode_db_repo.repo_id,
- user_id=c.rhodecode_user.user_id,
+ repo=c.rhodecode_db_repo.repo_id,
+ user=c.rhodecode_user.user_id,
revision=revision,
f_path=request.POST.get('f_path'),
- line_no=request.POST.get('line')
+ line_no=request.POST.get('line'),
+ status_change=(ChangesetStatus.get_status_lbl(status)
+ if status and change_status else None)
)
- Session.commit()
+
+ # get status if set !
+ if status and change_status:
+ # if latest status was from pull request and it's closed
+ # disallow changing status !
+ # dont_allow_on_closed_pull_request = True !
+
+ try:
+ ChangesetStatusModel().set_status(
+ c.rhodecode_db_repo.repo_id,
+ status,
+ c.rhodecode_user.user_id,
+ comm,
+ revision=revision,
+ dont_allow_on_closed_pull_request=True
+ )
+ except StatusChangeOnClosedPullRequestError:
+ log.error(traceback.format_exc())
+ msg = _('Changing status on a changeset associated with'
+ 'a closed pull request is not allowed')
+ h.flash(msg, category='warning')
+ return redirect(h.url('changeset_home', repo_name=repo_name,
+ revision=revision))
+ action_logger(self.rhodecode_user,
+ 'user_commented_revision:%s' % revision,
+ c.rhodecode_db_repo, self.ip_addr, self.sa)
+
+ Session().commit()
+
if not request.environ.get('HTTP_X_PARTIAL_XHR'):
return redirect(h.url('changeset_home', repo_name=repo_name,
revision=revision))
@@ -391,7 +437,7 @@ class ChangesetController(BaseRepoController):
owner = lambda: co.author.user_id == c.rhodecode_user.user_id
if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
ChangesetCommentsModel().delete(comment=co)
- Session.commit()
+ Session().commit()
return True
else:
raise HTTPForbidden()
diff --git a/rhodecode/controllers/compare.py b/rhodecode/controllers/compare.py
new file mode 100644
index 00000000..f3f2b352
--- /dev/null
+++ b/rhodecode/controllers/compare.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.controllers.compare
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ compare controller for pylons showoing differences between two
+ repos, branches, bookmarks or tips
+
+ :created_on: May 6, 2012
+ :author: marcink
+ :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+import logging
+import traceback
+
+from webob.exc import HTTPNotFound
+from pylons import request, response, session, tmpl_context as c, url
+from pylons.controllers.util import abort, redirect
+from pylons.i18n.translation import _
+
+from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
+from rhodecode.lib import helpers as h
+from rhodecode.lib.base import BaseRepoController, render
+from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from rhodecode.lib import diffs
+
+from rhodecode.model.db import Repository
+from rhodecode.model.pull_request import PullRequestModel
+
+log = logging.getLogger(__name__)
+
+
+class CompareController(BaseRepoController):
+
+ @LoginRequired()
+ @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
+ 'repository.admin')
+ def __before__(self):
+ super(CompareController, self).__before__()
+
+ def __get_cs_or_redirect(self, rev, repo, redirect_after=True):
+ """
+ Safe way to get changeset if error occur it redirects to changeset with
+ proper message
+
+ :param rev: revision to fetch
+ :param repo: repo instance
+ """
+
+ try:
+ type_, rev = rev
+ return repo.scm_instance.get_changeset(rev)
+ except EmptyRepositoryError, e:
+ if not redirect_after:
+ return None
+ h.flash(h.literal(_('There are no changesets yet')),
+ category='warning')
+ redirect(url('summary_home', repo_name=repo.repo_name))
+
+ except RepositoryError, e:
+ log.error(traceback.format_exc())
+ h.flash(str(e), category='warning')
+ redirect(h.url('summary_home', repo_name=repo.repo_name))
+
+ def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
+
+ org_repo = c.rhodecode_db_repo.repo_name
+ org_ref = (org_ref_type, org_ref)
+ other_ref = (other_ref_type, other_ref)
+ other_repo = request.GET.get('repo', org_repo)
+
+ c.swap_url = h.url('compare_url', repo_name=other_repo,
+ org_ref_type=other_ref[0], org_ref=other_ref[1],
+ other_ref_type=org_ref[0], other_ref=org_ref[1],
+ repo=org_repo)
+
+ c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
+ c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
+
+ if c.org_repo is None or c.other_repo is None:
+ log.error('Could not found repo %s or %s' % (org_repo, other_repo))
+ raise HTTPNotFound
+
+ if c.org_repo.scm_instance.alias != 'hg':
+ log.error('Review not available for GIT REPOS')
+ raise HTTPNotFound
+
+ self.__get_cs_or_redirect(rev=org_ref, repo=org_repo)
+ self.__get_cs_or_redirect(rev=other_ref, repo=other_repo)
+
+ c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
+ org_repo, org_ref, other_repo, other_ref
+ )
+
+ c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
+ c.cs_ranges])
+ c.target_repo = c.repo_name
+ # defines that we need hidden inputs with changesets
+ c.as_form = request.GET.get('as_form', False)
+ if request.environ.get('HTTP_X_PARTIAL_XHR'):
+ return render('compare/compare_cs.html')
+
+ c.org_ref = org_ref[1]
+ c.other_ref = other_ref[1]
+ # diff needs to have swapped org with other to generate proper diff
+ _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
+ discovery_data)
+ diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
+ _parsed = diff_processor.prepare()
+
+ c.files = []
+ c.changes = {}
+
+ for f in _parsed:
+ fid = h.FID('', f['filename'])
+ c.files.append([fid, f['operation'], f['filename'], f['stats']])
+ diff = diff_processor.as_html(enable_comments=False, diff_lines=[f])
+ c.changes[fid] = [f['operation'], f['filename'], diff]
+
+ return render('compare/compare_diff.html')
diff --git a/rhodecode/controllers/feed.py b/rhodecode/controllers/feed.py
index 1c15c7cf..10874e9e 100644
--- a/rhodecode/controllers/feed.py
+++ b/rhodecode/controllers/feed.py
@@ -28,11 +28,12 @@ import logging
from pylons import url, response, tmpl_context as c
from pylons.i18n.translation import _
-from rhodecode.lib.utils2 import safe_unicode
+from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
+
+from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController
-
-from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
+from rhodecode.lib.diffs import DiffProcessor
log = logging.getLogger(__name__)
@@ -49,31 +50,36 @@ class FeedController(BaseRepoController):
self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
self.language = 'en-us'
self.ttl = "5"
- self.feed_nr = 10
+ self.feed_nr = 20
def _get_title(self, cs):
- return "R%s:%s - %s" % (
- cs.revision, cs.short_id, cs.message
+ return "%s" % (
+ h.shorter(cs.message, 160)
)
def __changes(self, cs):
changes = []
- a = [safe_unicode(n.path) for n in cs.added]
- if a:
- changes.append('\nA ' + '\nA '.join(a))
-
- m = [safe_unicode(n.path) for n in cs.changed]
- if m:
- changes.append('\nM ' + '\nM '.join(m))
-
- d = [safe_unicode(n.path) for n in cs.removed]
- if d:
- changes.append('\nD ' + '\nD '.join(d))
-
- changes.append('</pre>')
-
- return ''.join(changes)
+ diffprocessor = DiffProcessor(cs.diff())
+ stats = diffprocessor.prepare(inline_diff=False)
+ for st in stats:
+ st.update({'added': st['stats'][0],
+ 'removed': st['stats'][1]})
+ changes.append('\n %(operation)s %(filename)s '
+ '(%(added)s lines added, %(removed)s lines removed)'
+ % st)
+ return changes
+
+ def __get_desc(self, cs):
+ desc_msg = []
+ desc_msg.append('%s %s %s:<br/>' % (cs.author, _('commited on'),
+ h.fmt_date(cs.date)))
+ desc_msg.append('<pre>')
+ desc_msg.append(cs.message)
+ desc_msg.append('\n')
+ desc_msg.extend(self.__changes(cs))
+ desc_msg.append('</pre>')
+ return desc_msg
def atom(self, repo_name):
"""Produce an atom-1.0 feed via feedgenerator module"""
@@ -87,15 +93,13 @@ class FeedController(BaseRepoController):
)
for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
- desc_msg = []
- desc_msg.append('%s - %s<br/><pre>' % (cs.author, cs.date))
- desc_msg.append(self.__changes(cs))
-
feed.add_item(title=self._get_title(cs),
link=url('changeset_home', repo_name=repo_name,
revision=cs.raw_id, qualified=True),
author_name=cs.author,
- description=''.join(desc_msg))
+ description=''.join(self.__get_desc(cs)),
+ pubdate=cs.date,
+ )
response.content_type = feed.mime_type
return feed.writeString('utf-8')
@@ -112,15 +116,12 @@ class FeedController(BaseRepoController):
)
for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
- desc_msg = []
- desc_msg.append('%s - %s<br/><pre>' % (cs.author, cs.date))
- desc_msg.append(self.__changes(cs))
-
feed.add_item(title=self._get_title(cs),
link=url('changeset_home', repo_name=repo_name,
revision=cs.raw_id, qualified=True),
author_name=cs.author,
- description=''.join(desc_msg),
+ description=''.join(self.__get_desc(cs)),
+ pubdate=cs.date,
)
response.content_type = feed.mime_type
diff --git a/rhodecode/controllers/files.py b/rhodecode/controllers/files.py
index b493657a..1d52fba1 100644
--- a/rhodecode/controllers/files.py
+++ b/rhodecode/controllers/files.py
@@ -32,7 +32,6 @@ from pylons import request, response, tmpl_context as c, url
from pylons.i18n.translation import _
from pylons.controllers.util import redirect
from pylons.decorators import jsonify
-from paste.fileapp import FileApp, _FileIter
from rhodecode.lib import diffs
from rhodecode.lib import helpers as h
@@ -41,7 +40,7 @@ from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
-from rhodecode.lib.utils import EmptyChangeset
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
from rhodecode.lib.vcs.conf import settings
from rhodecode.lib.vcs.exceptions import RepositoryError, \
ChangesetDoesNotExistError, EmptyRepositoryError, \
@@ -61,7 +60,6 @@ log = logging.getLogger(__name__)
class FilesController(BaseRepoController):
- @LoginRequired()
def __before__(self):
super(FilesController, self).__before__()
c.cut_off_limit = self.cut_off_limit
@@ -83,8 +81,8 @@ class FilesController(BaseRepoController):
url_ = url('files_add_home',
repo_name=c.repo_name,
revision=0, f_path='')
- add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
- h.flash(h.literal(_('There are no files yet %s' % add_new)),
+ add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
+ h.flash(h.literal(_('There are no files yet %s') % add_new),
category='warning')
redirect(h.url('summary_home', repo_name=repo_name))
@@ -113,6 +111,7 @@ class FilesController(BaseRepoController):
return file_node
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def index(self, repo_name, revision, f_path, annotate=False):
@@ -154,16 +153,25 @@ class FilesController(BaseRepoController):
c.file = c.changeset.get_node(f_path)
if c.file.is_file():
- c.file_history = self._get_node_history(c.changeset, f_path)
+ _hist = c.changeset.get_file_history(f_path)
+ c.file_history = self._get_node_history(c.changeset, f_path,
+ _hist)
+ c.authors = []
+ for a in set([x.author for x in _hist]):
+ c.authors.append((h.email(a), h.person(a)))
else:
- c.file_history = []
+ c.authors = c.file_history = []
except RepositoryError, e:
h.flash(str(e), category='warning')
redirect(h.url('files_home', repo_name=repo_name,
- revision=revision))
+ revision='tip'))
+
+ if request.environ.get('HTTP_X_PARTIAL_XHR'):
+ return render('files/files_ypjax.html')
return render('files/files.html')
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def rawfile(self, repo_name, revision, f_path):
@@ -176,6 +184,7 @@ class FilesController(BaseRepoController):
response.content_type = file_node.mimetype
return file_node.content
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def raw(self, repo_name, revision, f_path):
@@ -222,8 +231,18 @@ class FilesController(BaseRepoController):
response.content_type = mimetype
return file_node.content
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
def edit(self, repo_name, revision, f_path):
+ repo = Repository.get_by_repo_name(repo_name)
+ if repo.enable_locking and repo.locked[0]:
+ h.flash(_('This repository is has been locked by %s on %s')
+ % (h.person_by_id(repo.locked[0]),
+ h.fmt_date(h.time_to_datetime(repo.locked[1]))),
+ 'warning')
+ return redirect(h.url('files_home',
+ repo_name=repo_name, revision='tip'))
+
r_post = request.POST
c.cs = self.__get_cs_or_redirect(revision, repo_name)
@@ -260,7 +279,7 @@ class FilesController(BaseRepoController):
user=self.rhodecode_user,
author=author, message=message,
content=content, f_path=f_path)
- h.flash(_('Successfully committed to %s' % f_path),
+ h.flash(_('Successfully committed to %s') % f_path,
category='success')
except Exception:
@@ -271,8 +290,19 @@ class FilesController(BaseRepoController):
return render('files/files_edit.html')
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
def add(self, repo_name, revision, f_path):
+
+ repo = Repository.get_by_repo_name(repo_name)
+ if repo.enable_locking and repo.locked[0]:
+ h.flash(_('This repository is has been locked by %s on %s')
+ % (h.person_by_id(repo.locked[0]),
+ h.fmt_date(h.time_to_datetime(repo.locked[1]))),
+ 'warning')
+ return redirect(h.url('files_home',
+ repo_name=repo_name, revision='tip'))
+
r_post = request.POST
c.cs = self.__get_cs_or_redirect(revision, repo_name,
redirect_after=False)
@@ -313,7 +343,7 @@ class FilesController(BaseRepoController):
user=self.rhodecode_user,
author=author, message=message,
content=content, f_path=node_path)
- h.flash(_('Successfully committed to %s' % node_path),
+ h.flash(_('Successfully committed to %s') % node_path,
category='success')
except NodeAlreadyExistsError, e:
h.flash(_(e), category='error')
@@ -325,6 +355,7 @@ class FilesController(BaseRepoController):
return render('files/files_add.html')
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def archivefile(self, repo_name, fname):
@@ -361,27 +392,28 @@ class FilesController(BaseRepoController):
except (ImproperArchiveTypeError, KeyError):
return _('Unknown archive type')
- fd, _archive_name = tempfile.mkstemp(suffix='rcarchive')
- with open(_archive_name, 'wb') as f:
- cs.fill_archive(stream=f, kind=fileformat, subrepos=subrepos)
-
- content_disposition = 'attachment; filename=%s-%s%s' \
- % (repo_name, revision[:12], ext)
- content_length = os.path.getsize(_archive_name)
-
- headers = [('Content-Disposition', str(content_disposition)),
- ('Content-Type', str(content_type)),
- ('Content-Length', str(content_length))]
-
- class _DestroyingFileWrapper(_FileIter):
- def close(self):
- self.file.close
- os.remove(self.file.name)
-
- request.environ['wsgi.file_wrapper'] = _DestroyingFileWrapper
- fapp = FileApp(_archive_name, headers=headers)
- return fapp(request.environ, self.start_response)
+ fd, archive = tempfile.mkstemp()
+ t = open(archive, 'wb')
+ cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
+ t.close()
+
+ def get_chunked_archive(archive):
+ stream = open(archive, 'rb')
+ while True:
+ data = stream.read(16 * 1024)
+ if not data:
+ stream.close()
+ os.close(fd)
+ os.remove(archive)
+ break
+ yield data
+
+ response.content_disposition = str('attachment; filename=%s-%s%s' \
+ % (repo_name, revision[:12], ext))
+ response.content_type = str(content_type)
+ return get_chunked_archive(archive)
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def diff(self, repo_name, f_path):
@@ -454,8 +486,9 @@ class FilesController(BaseRepoController):
return render('files/file_diff.html')
- def _get_node_history(self, cs, f_path):
- changesets = cs.get_file_history(f_path)
+ def _get_node_history(self, cs, f_path, changesets=None):
+ if changesets is None:
+ changesets = cs.get_file_history(f_path)
hist_l = []
changesets_group = ([], _("Changesets"))
@@ -479,12 +512,13 @@ class FilesController(BaseRepoController):
return hist_l
- @jsonify
+ @LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
+ @jsonify
def nodelist(self, repo_name, revision, f_path):
if request.environ.get('HTTP_X_PARTIAL_XHR'):
cs = self.__get_cs_or_redirect(revision, repo_name)
_d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
flat=False)
- return _d + _f
+ return {'nodes': _d + _f}
diff --git a/rhodecode/controllers/forks.py b/rhodecode/controllers/forks.py
index b5c047ec..9096b9a5 100644
--- a/rhodecode/controllers/forks.py
+++ b/rhodecode/controllers/forks.py
@@ -35,11 +35,13 @@ import rhodecode.lib.helpers as h
from rhodecode.lib.helpers import Page
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
- NotAnonymous, HasRepoPermissionAny
+ NotAnonymous, HasRepoPermissionAny, HasPermissionAllDecorator,\
+ HasPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
from rhodecode.model.repo import RepoModel
from rhodecode.model.forms import RepoForkForm
+from rhodecode.model.scm import ScmModel
log = logging.getLogger(__name__)
@@ -53,6 +55,8 @@ class ForksController(BaseRepoController):
def __load_defaults(self):
c.repo_groups = RepoGroup.groups_choices()
c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
+ choices, c.landing_revs = ScmModel().get_repo_landing_revs()
+ c.landing_revs_choices = choices
def __load_data(self, repo_name=None):
"""
@@ -120,6 +124,7 @@ class ForksController(BaseRepoController):
return render('/forks/forks.html')
@NotAnonymous()
+ @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def fork(self, repo_name):
@@ -142,24 +147,23 @@ class ForksController(BaseRepoController):
force_defaults=False
)
-
@NotAnonymous()
+ @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def fork_create(self, repo_name):
self.__load_defaults()
c.repo_info = Repository.get_by_repo_name(repo_name)
_form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
- repo_groups=c.repo_groups_choices,)()
+ repo_groups=c.repo_groups_choices,
+ landing_revs=c.landing_revs_choices)()
form_result = {}
try:
form_result = _form.to_python(dict(request.POST))
- # add org_path of repo so we can do a clone from it later
- form_result['org_path'] = c.repo_info.repo_name
# create fork is done sometimes async on celery, db transaction
# management is handled there.
- RepoModel().create_fork(form_result, self.rhodecode_user)
+ RepoModel().create_fork(form_result, self.rhodecode_user.user_id)
h.flash(_('forked %s repository as %s') \
% (repo_name, form_result['repo_name']),
category='success')
diff --git a/rhodecode/controllers/home.py b/rhodecode/controllers/home.py
index 90afa2b1..93aa74e4 100644
--- a/rhodecode/controllers/home.py
+++ b/rhodecode/controllers/home.py
@@ -26,7 +26,7 @@
import logging
from pylons import tmpl_context as c, request
-from paste.httpexceptions import HTTPBadRequest
+from webob.exc import HTTPBadRequest
from rhodecode.lib.auth import LoginRequired
from rhodecode.lib.base import BaseController, render
@@ -51,7 +51,8 @@ class HomeController(BaseController):
if request.is_xhr:
all_repos = Repository.query().order_by(Repository.repo_name).all()
c.repos_list = self.scm_model.get_repos(all_repos,
- sort_key='name_sort')
+ sort_key='name_sort',
+ simple=True)
return render('/repo_switcher_list.html')
else:
return HTTPBadRequest()
diff --git a/rhodecode/controllers/journal.py b/rhodecode/controllers/journal.py
index 4a55da95..3d6da9e8 100644
--- a/rhodecode/controllers/journal.py
+++ b/rhodecode/controllers/journal.py
@@ -30,7 +30,7 @@ from sqlalchemy.orm import joinedload
from webhelpers.paginate import Page
from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
-from paste.httpexceptions import HTTPBadRequest
+from webob.exc import HTTPBadRequest
from pylons import request, tmpl_context as c, response, url
from pylons.i18n.translation import _
@@ -49,8 +49,6 @@ class JournalController(BaseController):
def __before__(self):
super(JournalController, self).__before__()
- self.rhodecode_user = self.rhodecode_user
- self.title = _('%s public journal %s feed') % (c.rhodecode_name, '%s')
self.language = 'en-us'
self.ttl = "5"
self.feed_nr = 20
@@ -84,6 +82,30 @@ class JournalController(BaseController):
return c.journal_data
return render('journal/journal.html')
+ @LoginRequired(api_access=True)
+ @NotAnonymous()
+ def journal_atom(self):
+ """
+ Produce an atom-1.0 feed via feedgenerator module
+ """
+ following = self.sa.query(UserFollowing)\
+ .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
+ .options(joinedload(UserFollowing.follows_repository))\
+ .all()
+ return self._atom_feed(following, public=False)
+
+ @LoginRequired(api_access=True)
+ @NotAnonymous()
+ def journal_rss(self):
+ """
+ Produce an rss feed via feedgenerator module
+ """
+ following = self.sa.query(UserFollowing)\
+ .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
+ .options(joinedload(UserFollowing.follows_repository))\
+ .all()
+ return self._rss_feed(following, public=False)
+
def _get_daily_aggregate(self, journal):
groups = []
for k, g in groupby(journal, lambda x: x.action_as_day):
@@ -173,33 +195,36 @@ class JournalController(BaseController):
return c.journal_data
return render('journal/public_journal.html')
- @LoginRequired(api_access=True)
- def public_journal_atom(self):
- """
- Produce an atom-1.0 feed via feedgenerator module
- """
- c.following = self.sa.query(UserFollowing)\
- .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
- .options(joinedload(UserFollowing.follows_repository))\
- .all()
-
- journal = self._get_journal_data(c.following)
+ def _atom_feed(self, repos, public=True):
+ journal = self._get_journal_data(repos)
+ if public:
+ _link = url('public_journal_atom', qualified=True)
+ _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
+ 'atom feed')
+ else:
+ _link = url('journal_atom', qualified=True)
+ _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'atom feed')
- feed = Atom1Feed(title=self.title % 'atom',
- link=url('public_journal_atom', qualified=True),
- description=_('Public journal'),
+ feed = Atom1Feed(title=_desc,
+ link=_link,
+ description=_desc,
language=self.language,
ttl=self.ttl)
for entry in journal[:self.feed_nr]:
- #tmpl = h.action_parser(entry)[0]
- action, action_extra = h.action_parser(entry, feed=True)
- title = "%s - %s %s" % (entry.user.short_contact, action,
+ action, action_extra, ico = h.action_parser(entry, feed=True)
+ title = "%s - %s %s" % (entry.user.short_contact, action(),
entry.repository.repo_name)
desc = action_extra()
+ _url = None
+ if entry.repository is not None:
+ _url = url('changelog_home',
+ repo_name=entry.repository.repo_name,
+ qualified=True)
+
feed.add_item(title=title,
pubdate=entry.action_date,
- link=url('', qualified=True),
+ link=_url or url('', qualified=True),
author_email=entry.user.email,
author_name=entry.user.full_contact,
description=desc)
@@ -207,36 +232,63 @@ class JournalController(BaseController):
response.content_type = feed.mime_type
return feed.writeString('utf-8')
- @LoginRequired(api_access=True)
- def public_journal_rss(self):
- """
- Produce an rss2 feed via feedgenerator module
- """
- c.following = self.sa.query(UserFollowing)\
- .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
- .options(joinedload(UserFollowing.follows_repository))\
- .all()
-
- journal = self._get_journal_data(c.following)
+ def _rss_feed(self, repos, public=True):
+ journal = self._get_journal_data(repos)
+ if public:
+ _link = url('public_journal_atom', qualified=True)
+ _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
+ 'rss feed')
+ else:
+ _link = url('journal_atom', qualified=True)
+ _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'rss feed')
- feed = Rss201rev2Feed(title=self.title % 'rss',
- link=url('public_journal_rss', qualified=True),
- description=_('Public journal'),
+ feed = Rss201rev2Feed(title=_desc,
+ link=_link,
+ description=_desc,
language=self.language,
ttl=self.ttl)
for entry in journal[:self.feed_nr]:
- #tmpl = h.action_parser(entry)[0]
- action, action_extra = h.action_parser(entry, feed=True)
- title = "%s - %s %s" % (entry.user.short_contact, action,
+ action, action_extra, ico = h.action_parser(entry, feed=True)
+ title = "%s - %s %s" % (entry.user.short_contact, action(),
entry.repository.repo_name)
desc = action_extra()
+ _url = None
+ if entry.repository is not None:
+ _url = url('changelog_home',
+ repo_name=entry.repository.repo_name,
+ qualified=True)
+
feed.add_item(title=title,
pubdate=entry.action_date,
- link=url('', qualified=True),
+ link=_url or url('', qualified=True),
author_email=entry.user.email,
author_name=entry.user.full_contact,
description=desc)
response.content_type = feed.mime_type
return feed.writeString('utf-8')
+
+ @LoginRequired(api_access=True)
+ def public_journal_atom(self):
+ """
+ Produce an atom-1.0 feed via feedgenerator module
+ """
+ c.following = self.sa.query(UserFollowing)\
+ .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
+ .options(joinedload(UserFollowing.follows_repository))\
+ .all()
+
+ return self._atom_feed(c.following)
+
+ @LoginRequired(api_access=True)
+ def public_journal_rss(self):
+ """
+ Produce an rss2 feed via feedgenerator module
+ """
+ c.following = self.sa.query(UserFollowing)\
+ .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
+ .options(joinedload(UserFollowing.follows_repository))\
+ .all()
+
+ return self._rss_feed(c.following)
diff --git a/rhodecode/controllers/login.py b/rhodecode/controllers/login.py
index f997f2e0..1e75bb49 100644
--- a/rhodecode/controllers/login.py
+++ b/rhodecode/controllers/login.py
@@ -25,9 +25,11 @@
import logging
import formencode
+import datetime
+import urlparse
from formencode import htmlfill
-
+from webob.exc import HTTPFound
from pylons.i18n.translation import _
from pylons.controllers.util import abort, redirect
from pylons import request, response, session, tmpl_context as c, url
@@ -51,7 +53,7 @@ class LoginController(BaseController):
def index(self):
# redirect if already logged in
- c.came_from = request.GET.get('came_from', None)
+ c.came_from = request.GET.get('came_from')
if self.rhodecode_user.is_authenticated \
and self.rhodecode_user.username != 'default':
@@ -62,6 +64,7 @@ class LoginController(BaseController):
# import Login Form validator class
login_form = LoginForm()
try:
+ session.invalidate()
c.form_result = login_form.to_python(dict(request.POST))
# form checks for username/password, now we're authenticated
username = c.form_result['username']
@@ -70,22 +73,46 @@ class LoginController(BaseController):
auth_user.set_authenticated()
cs = auth_user.get_cookie_store()
session['rhodecode_user'] = cs
+ user.update_lastlogin()
+ Session().commit()
+
# If they want to be remembered, update the cookie
if c.form_result['remember'] is not False:
- session.cookie_expires = False
- session._set_cookie_values()
- session._update_cookie_out()
+ _year = (datetime.datetime.now() +
+ datetime.timedelta(seconds=60 * 60 * 24 * 365))
+ session._set_cookie_expires(_year)
+
session.save()
log.info('user %s is now authenticated and stored in '
'session, session attrs %s' % (username, cs))
- user.update_lastlogin()
- Session.commit()
+ # dumps session attrs back to cookie
+ session._update_cookie_out()
+
+ # we set new cookie
+ headers = None
+ if session.request['set_cookie']:
+ # send set-cookie headers back to response to update cookie
+ headers = [('Set-Cookie', session.request['cookie_out'])]
+
+ allowed_schemes = ['http', 'https']
if c.came_from:
- return redirect(c.came_from)
+ parsed = urlparse.urlparse(c.came_from)
+ server_parsed = urlparse.urlparse(url.current())
+ if parsed.scheme and parsed.scheme not in allowed_schemes:
+ log.error(
+ 'Suspicious URL scheme detected %s for url %s' %
+ (parsed.scheme, parsed))
+ c.came_from = url('home')
+ elif server_parsed.netloc != parsed.netloc:
+ log.error('Suspicious NETLOC detected %s for url %s'
+ 'server url is: %s' %
+ (parsed.netloc, parsed, server_parsed))
+ c.came_from = url('home')
+ raise HTTPFound(location=c.came_from, headers=headers)
else:
- return redirect(url('home'))
+ raise HTTPFound(location=url('home'), headers=headers)
except formencode.Invalid, errors:
return htmlfill.render(
@@ -115,7 +142,7 @@ class LoginController(BaseController):
UserModel().create_registration(form_result)
h.flash(_('You have successfully registered into rhodecode'),
category='success')
- Session.commit()
+ Session().commit()
return redirect(url('login_home'))
except formencode.Invalid, errors:
diff --git a/rhodecode/controllers/pullrequests.py b/rhodecode/controllers/pullrequests.py
new file mode 100644
index 00000000..6e30f1d6
--- /dev/null
+++ b/rhodecode/controllers/pullrequests.py
@@ -0,0 +1,404 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.controllers.pullrequests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ pull requests controller for rhodecode for initializing pull requests
+
+ :created_on: May 7, 2012
+ :author: marcink
+ :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+import logging
+import traceback
+import formencode
+
+from webob.exc import HTTPNotFound, HTTPForbidden
+from collections import defaultdict
+from itertools import groupby
+
+from pylons import request, response, session, tmpl_context as c, url
+from pylons.controllers.util import abort, redirect
+from pylons.i18n.translation import _
+from pylons.decorators import jsonify
+
+from rhodecode.lib.compat import json
+from rhodecode.lib.base import BaseRepoController, render
+from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
+ NotAnonymous
+from rhodecode.lib import helpers as h
+from rhodecode.lib import diffs
+from rhodecode.lib.utils import action_logger
+from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
+ ChangesetComment
+from rhodecode.model.pull_request import PullRequestModel
+from rhodecode.model.meta import Session
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.comment import ChangesetCommentsModel
+from rhodecode.model.changeset_status import ChangesetStatusModel
+from rhodecode.model.forms import PullRequestForm
+
+log = logging.getLogger(__name__)
+
+
+class PullrequestsController(BaseRepoController):
+
+ @LoginRequired()
+ @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
+ 'repository.admin')
+ def __before__(self):
+ super(PullrequestsController, self).__before__()
+ repo_model = RepoModel()
+ c.users_array = repo_model.get_users_js()
+ c.users_groups_array = repo_model.get_users_groups_js()
+
+ def _get_repo_refs(self, repo):
+ hist_l = []
+
+ branches_group = ([('branch:%s:%s' % (k, v), k) for
+ k, v in repo.branches.iteritems()], _("Branches"))
+ bookmarks_group = ([('book:%s:%s' % (k, v), k) for
+ k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
+ tags_group = ([('tag:%s:%s' % (k, v), k) for
+ k, v in repo.tags.iteritems()], _("Tags"))
+
+ hist_l.append(bookmarks_group)
+ hist_l.append(branches_group)
+ hist_l.append(tags_group)
+
+ return hist_l
+
+ def show_all(self, repo_name):
+ c.pull_requests = PullRequestModel().get_all(repo_name)
+ c.repo_name = repo_name
+ return render('/pullrequests/pullrequest_show_all.html')
+
+ @NotAnonymous()
+ def index(self):
+ org_repo = c.rhodecode_db_repo
+
+ if org_repo.scm_instance.alias != 'hg':
+ log.error('Review not available for GIT REPOS')
+ raise HTTPNotFound
+
+ other_repos_info = {}
+
+ c.org_refs = self._get_repo_refs(c.rhodecode_repo)
+ c.org_repos = []
+ c.other_repos = []
+ c.org_repos.append((org_repo.repo_name, '%s/%s' % (
+ org_repo.user.username, c.repo_name))
+ )
+
+ # add org repo to other so we can open pull request agains itself
+ c.other_repos.extend(c.org_repos)
+
+ c.default_pull_request = org_repo.repo_name
+ c.default_revs = self._get_repo_refs(org_repo.scm_instance)
+ #add orginal repo
+ other_repos_info[org_repo.repo_name] = {
+ 'gravatar': h.gravatar_url(org_repo.user.email, 24),
+ 'description': org_repo.description,
+ 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
+ }
+
+ #gather forks and add to this list
+ for fork in org_repo.forks:
+ c.other_repos.append((fork.repo_name, '%s/%s' % (
+ fork.user.username, fork.repo_name))
+ )
+ other_repos_info[fork.repo_name] = {
+ 'gravatar': h.gravatar_url(fork.user.email, 24),
+ 'description': fork.description,
+ 'revs': h.select('other_ref', '',
+ self._get_repo_refs(fork.scm_instance),
+ class_='refs')
+ }
+ #add parents of this fork also
+ if org_repo.parent:
+ c.default_pull_request = org_repo.parent.repo_name
+ c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
+ org_repo.parent.user.username,
+ org_repo.parent.repo_name))
+ )
+ other_repos_info[org_repo.parent.repo_name] = {
+ 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
+ 'description': org_repo.parent.description,
+ 'revs': h.select('other_ref', '',
+ self._get_repo_refs(org_repo.parent.scm_instance),
+ class_='refs')
+ }
+
+ c.other_repos_info = json.dumps(other_repos_info)
+ c.review_members = [org_repo.user]
+ return render('/pullrequests/pullrequest.html')
+
+ @NotAnonymous()
+ def create(self, repo_name):
+ try:
+ _form = PullRequestForm()().to_python(request.POST)
+ except formencode.Invalid, errors:
+ log.error(traceback.format_exc())
+ if errors.error_dict.get('revisions'):
+ msg = 'Revisions: %s' % errors.error_dict['revisions']
+ elif errors.error_dict.get('pullrequest_title'):
+ msg = _('Pull request requires a title with min. 3 chars')
+ else:
+ msg = _('error during creation of pull request')
+
+ h.flash(msg, 'error')
+ return redirect(url('pullrequest_home', repo_name=repo_name))
+
+ org_repo = _form['org_repo']
+ org_ref = _form['org_ref']
+ other_repo = _form['other_repo']
+ other_ref = _form['other_ref']
+ revisions = _form['revisions']
+ reviewers = _form['review_members']
+
+ title = _form['pullrequest_title']
+ description = _form['pullrequest_desc']
+
+ try:
+ pull_request = PullRequestModel().create(
+ self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
+ other_ref, revisions, reviewers, title, description
+ )
+ Session().commit()
+ h.flash(_('Successfully opened new pull request'),
+ category='success')
+ except Exception:
+ h.flash(_('Error occurred during sending pull request'),
+ category='error')
+ log.error(traceback.format_exc())
+ return redirect(url('pullrequest_home', repo_name=repo_name))
+
+ return redirect(url('pullrequest_show', repo_name=other_repo,
+ pull_request_id=pull_request.pull_request_id))
+
+ @NotAnonymous()
+ @jsonify
+ def update(self, repo_name, pull_request_id):
+ pull_request = PullRequest.get_or_404(pull_request_id)
+ if pull_request.is_closed():
+ raise HTTPForbidden()
+ #only owner or admin can update it
+ owner = pull_request.author.user_id == c.rhodecode_user.user_id
+ if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
+ reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
+ request.POST.get('reviewers_ids', '').split(',')))
+
+ PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
+ Session.commit()
+ return True
+ raise HTTPForbidden()
+
+ @NotAnonymous()
+ @jsonify
+ def delete(self, repo_name, pull_request_id):
+ pull_request = PullRequest.get_or_404(pull_request_id)
+ #only owner can delete it !
+ if pull_request.author.user_id == c.rhodecode_user.user_id:
+ PullRequestModel().delete(pull_request)
+ Session().commit()
+ h.flash(_('Successfully deleted pull request'),
+ category='success')
+ return redirect(url('admin_settings_my_account'))
+ raise HTTPForbidden()
+
+ def _load_compare_data(self, pull_request, enable_comments=True):
+ """
+ Load context data needed for generating compare diff
+
+ :param pull_request:
+ :type pull_request:
+ """
+
+ org_repo = pull_request.org_repo
+ (org_ref_type,
+ org_ref_name,
+ org_ref_rev) = pull_request.org_ref.split(':')
+
+ other_repo = pull_request.other_repo
+ (other_ref_type,
+ other_ref_name,
+ other_ref_rev) = pull_request.other_ref.split(':')
+
+ # despite opening revisions for bookmarks/branches/tags, we always
+ # convert this to rev to prevent changes after book or branch change
+ org_ref = ('rev', org_ref_rev)
+ other_ref = ('rev', other_ref_rev)
+
+ c.org_repo = org_repo
+ c.other_repo = other_repo
+
+ c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
+ org_repo, org_ref, other_repo, other_ref
+ )
+
+ c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
+ c.cs_ranges])
+ # defines that we need hidden inputs with changesets
+ c.as_form = request.GET.get('as_form', False)
+
+ c.org_ref = org_ref[1]
+ c.other_ref = other_ref[1]
+ # diff needs to have swapped org with other to generate proper diff
+ _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
+ discovery_data)
+ diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
+ _parsed = diff_processor.prepare()
+
+ c.files = []
+ c.changes = {}
+
+ for f in _parsed:
+ fid = h.FID('', f['filename'])
+ c.files.append([fid, f['operation'], f['filename'], f['stats']])
+ diff = diff_processor.as_html(enable_comments=enable_comments,
+ diff_lines=[f])
+ c.changes[fid] = [f['operation'], f['filename'], diff]
+
+ def show(self, repo_name, pull_request_id):
+ repo_model = RepoModel()
+ c.users_array = repo_model.get_users_js()
+ c.users_groups_array = repo_model.get_users_groups_js()
+ c.pull_request = PullRequest.get_or_404(pull_request_id)
+
+ cc_model = ChangesetCommentsModel()
+ cs_model = ChangesetStatusModel()
+ _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
+ pull_request=c.pull_request,
+ with_revisions=True)
+
+ cs_statuses = defaultdict(list)
+ for st in _cs_statuses:
+ cs_statuses[st.author.username] += [st]
+
+ c.pull_request_reviewers = []
+ c.pull_request_pending_reviewers = []
+ for o in c.pull_request.reviewers:
+ st = cs_statuses.get(o.user.username, None)
+ if st:
+ sorter = lambda k: k.version
+ st = [(x, list(y)[0])
+ for x, y in (groupby(sorted(st, key=sorter), sorter))]
+ else:
+ c.pull_request_pending_reviewers.append(o.user)
+ c.pull_request_reviewers.append([o.user, st])
+
+ # pull_requests repo_name we opened it against
+ # ie. other_repo must match
+ if repo_name != c.pull_request.other_repo.repo_name:
+ raise HTTPNotFound
+
+ # load compare data into template context
+ enable_comments = not c.pull_request.is_closed()
+ self._load_compare_data(c.pull_request, enable_comments=enable_comments)
+
+ # inline comments
+ c.inline_cnt = 0
+ c.inline_comments = cc_model.get_inline_comments(
+ c.rhodecode_db_repo.repo_id,
+ pull_request=pull_request_id)
+ # count inline comments
+ for __, lines in c.inline_comments:
+ for comments in lines.values():
+ c.inline_cnt += len(comments)
+ # comments
+ c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
+ pull_request=pull_request_id)
+
+ # changeset(pull-request) status
+ c.current_changeset_status = cs_model.calculate_status(
+ c.pull_request_reviewers
+ )
+ c.changeset_statuses = ChangesetStatus.STATUSES
+ c.target_repo = c.pull_request.org_repo.repo_name
+ return render('/pullrequests/pullrequest_show.html')
+
+ @NotAnonymous()
+ @jsonify
+ def comment(self, repo_name, pull_request_id):
+ pull_request = PullRequest.get_or_404(pull_request_id)
+ if pull_request.is_closed():
+ raise HTTPForbidden()
+
+ status = request.POST.get('changeset_status')
+ change_status = request.POST.get('change_changeset_status')
+
+ comm = ChangesetCommentsModel().create(
+ text=request.POST.get('text'),
+ repo=c.rhodecode_db_repo.repo_id,
+ user=c.rhodecode_user.user_id,
+ pull_request=pull_request_id,
+ f_path=request.POST.get('f_path'),
+ line_no=request.POST.get('line'),
+ status_change=(ChangesetStatus.get_status_lbl(status)
+ if status and change_status else None)
+ )
+
+ # get status if set !
+ if status and change_status:
+ ChangesetStatusModel().set_status(
+ c.rhodecode_db_repo.repo_id,
+ status,
+ c.rhodecode_user.user_id,
+ comm,
+ pull_request=pull_request_id
+ )
+ action_logger(self.rhodecode_user,
+ 'user_commented_pull_request:%s' % pull_request_id,
+ c.rhodecode_db_repo, self.ip_addr, self.sa)
+
+ if request.POST.get('save_close'):
+ PullRequestModel().close_pull_request(pull_request_id)
+ action_logger(self.rhodecode_user,
+ 'user_closed_pull_request:%s' % pull_request_id,
+ c.rhodecode_db_repo, self.ip_addr, self.sa)
+
+ Session().commit()
+
+ if not request.environ.get('HTTP_X_PARTIAL_XHR'):
+ return redirect(h.url('pullrequest_show', repo_name=repo_name,
+ pull_request_id=pull_request_id))
+
+ data = {
+ 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
+ }
+ if comm:
+ c.co = comm
+ data.update(comm.get_dict())
+ data.update({'rendered_text':
+ render('changeset/changeset_comment_block.html')})
+
+ return data
+
+ @NotAnonymous()
+ @jsonify
+ def delete_comment(self, repo_name, comment_id):
+ co = ChangesetComment.get(comment_id)
+ if co.pull_request.is_closed():
+ #don't allow deleting comments on closed pull request
+ raise HTTPForbidden()
+
+ owner = lambda: co.author.user_id == c.rhodecode_user.user_id
+ if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
+ ChangesetCommentsModel().delete(comment=co)
+ Session().commit()
+ return True
+ else:
+ raise HTTPForbidden()
diff --git a/rhodecode/controllers/search.py b/rhodecode/controllers/search.py
index f75246dc..2b73c1df 100644
--- a/rhodecode/controllers/search.py
+++ b/rhodecode/controllers/search.py
@@ -3,7 +3,7 @@
rhodecode.controllers.search
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Search controller for rhodecode
+ Search controller for RhodeCode
:created_on: Aug 7, 2010
:author: marcink
@@ -30,7 +30,8 @@ from pylons import request, config, tmpl_context as c
from rhodecode.lib.auth import LoginRequired
from rhodecode.lib.base import BaseController, render
-from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
+from rhodecode.lib.indexers import CHGSETS_SCHEMA, SCHEMA, CHGSET_IDX_NAME, \
+ IDX_NAME, WhooshResultWrapper
from webhelpers.paginate import Page
from webhelpers.util import update_params
@@ -38,6 +39,7 @@ from webhelpers.util import update_params
from whoosh.index import open_dir, EmptyIndexError
from whoosh.qparser import QueryParser, QueryParserError
from whoosh.query import Phrase, Wildcard, Term, Prefix
+from rhodecode.model.repo import RepoModel
log = logging.getLogger(__name__)
@@ -53,25 +55,41 @@ class SearchController(BaseController):
c.formated_results = []
c.runtime = ''
c.cur_query = request.GET.get('q', None)
- c.cur_type = request.GET.get('type', 'source')
+ c.cur_type = request.GET.get('type', 'content')
c.cur_search = search_type = {'content': 'content',
- 'commit': 'content',
+ 'commit': 'message',
'path': 'path',
- 'repository': 'repository'}\
- .get(c.cur_type, 'content')
+ 'repository': 'repository'
+ }.get(c.cur_type, 'content')
+
+ index_name = {
+ 'content': IDX_NAME,
+ 'commit': CHGSET_IDX_NAME,
+ 'path': IDX_NAME
+ }.get(c.cur_type, IDX_NAME)
+
+ schema_defn = {
+ 'content': SCHEMA,
+ 'commit': CHGSETS_SCHEMA,
+ 'path': SCHEMA
+ }.get(c.cur_type, SCHEMA)
+
+ log.debug('IDX: %s' % index_name)
+ log.debug('SCHEMA: %s' % schema_defn)
if c.cur_query:
cur_query = c.cur_query.lower()
+ log.debug(cur_query)
if c.cur_query:
p = int(request.params.get('page', 1))
highlight_items = set()
try:
idx = open_dir(config['app_conf']['index_dir'],
- indexname=IDX_NAME)
+ indexname=index_name)
searcher = idx.searcher()
- qp = QueryParser(search_type, schema=SCHEMA)
+ qp = QueryParser(search_type, schema=schema_defn)
if c.repo_name:
cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
try:
@@ -83,13 +101,13 @@ class SearchController(BaseController):
highlight_items.add(query.text)
else:
for i in query.all_terms():
- if i[0] == 'content':
+ if i[0] in ['content', 'message']:
highlight_items.add(i[1])
matcher = query.matcher(searcher)
- log.debug(query)
- log.debug(highlight_items)
+ log.debug('query: %s' % query)
+ log.debug('hl terms: %s' % highlight_items)
results = searcher.search(query)
res_ln = len(results)
c.runtime = '%s results (%.3f seconds)' % (
@@ -98,11 +116,11 @@ class SearchController(BaseController):
def url_generator(**kw):
return update_params("?q=%s&type=%s" \
- % (c.cur_query, c.cur_search), **kw)
-
+ % (c.cur_query, c.cur_type), **kw)
+ repo_location = RepoModel().repos_path
c.formated_results = Page(
- ResultWrapper(search_type, searcher, matcher,
- highlight_items),
+ WhooshResultWrapper(search_type, searcher, matcher,
+ highlight_items, repo_location),
page=p,
item_count=res_ln,
items_per_page=10,
@@ -121,6 +139,5 @@ class SearchController(BaseController):
log.error(traceback.format_exc())
c.runtime = _('An error occurred during this search operation')
-
# Return a rendered template
return render('/search/search.html')
diff --git a/rhodecode/controllers/settings.py b/rhodecode/controllers/settings.py
index 06bbd927..eb734a90 100644
--- a/rhodecode/controllers/settings.py
+++ b/rhodecode/controllers/settings.py
@@ -43,6 +43,7 @@ from rhodecode.model.forms import RepoSettingsForm
from rhodecode.model.repo import RepoModel
from rhodecode.model.db import RepoGroup
from rhodecode.model.meta import Session
+from rhodecode.model.scm import ScmModel
log = logging.getLogger(__name__)
@@ -60,6 +61,8 @@ class SettingsController(BaseRepoController):
repo_model = RepoModel()
c.users_array = repo_model.get_users_js()
c.users_groups_array = repo_model.get_users_groups_js()
+ choices, c.landing_revs = ScmModel().get_repo_landing_revs()
+ c.landing_revs_choices = choices
@HasRepoPermissionAllDecorator('repository.admin')
def index(self, repo_name):
@@ -94,17 +97,18 @@ class SettingsController(BaseRepoController):
_form = RepoSettingsForm(edit=True,
old_data={'repo_name': repo_name},
- repo_groups=c.repo_groups_choices)()
+ repo_groups=c.repo_groups_choices,
+ landing_revs=c.landing_revs_choices)()
try:
form_result = _form.to_python(dict(request.POST))
repo_model.update(repo_name, form_result)
invalidate_cache('get_repo_cached_%s' % repo_name)
- h.flash(_('Repository %s updated successfully' % repo_name),
+ h.flash(_('Repository %s updated successfully') % repo_name,
category='success')
changed_name = form_result['repo_name_full']
action_logger(self.rhodecode_user, 'user_updated_repo',
- changed_name, '', self.sa)
+ changed_name, self.ip_addr, self.sa)
Session.commit()
except formencode.Invalid, errors:
c.repo_info = repo_model.get_by_repo_name(repo_name)
@@ -145,7 +149,7 @@ class SettingsController(BaseRepoController):
return redirect(url('home'))
try:
action_logger(self.rhodecode_user, 'user_deleted_repo',
- repo_name, '', self.sa)
+ repo_name, self.ip_addr, self.sa)
repo_model.delete(repo)
invalidate_cache('get_repo_cached_%s' % repo_name)
h.flash(_('deleted repository %s') % repo_name, category='success')
diff --git a/rhodecode/controllers/summary.py b/rhodecode/controllers/summary.py
index baec386a..72dcd5d0 100644
--- a/rhodecode/controllers/summary.py
+++ b/rhodecode/controllers/summary.py
@@ -45,12 +45,13 @@ from rhodecode.model.db import Statistics, CacheInvalidation
from rhodecode.lib.utils2 import safe_unicode
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
-from rhodecode.lib.utils import EmptyChangeset
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
from rhodecode.lib.markup_renderer import MarkupRenderer
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.celerylib.tasks import get_commits_stats
from rhodecode.lib.helpers import RepoPage
from rhodecode.lib.compat import json, OrderedDict
+from rhodecode.lib.vcs.nodes import FileNode
log = logging.getLogger(__name__)
@@ -179,27 +180,31 @@ class SummaryController(BaseRepoController):
if c.enable_downloads:
c.download_options = self._get_download_links(c.rhodecode_repo)
- c.readme_data, c.readme_file = self.__get_readme_data(
- c.rhodecode_db_repo.repo_name, c.rhodecode_repo
- )
+ c.readme_data, c.readme_file = \
+ self.__get_readme_data(c.rhodecode_db_repo)
return render('summary/summary.html')
- def __get_readme_data(self, repo_name, repo):
+ def __get_readme_data(self, db_repo):
+ repo_name = db_repo.repo_name
@cache_region('long_term')
def _get_readme_from_cache(key):
readme_data = None
readme_file = None
- log.debug('Fetching readme file')
+ log.debug('Looking for README file')
try:
- cs = repo.get_changeset() # fetches TIP
+ # get's the landing revision! or tip if fails
+ cs = db_repo.get_landing_changeset()
renderer = MarkupRenderer()
for f in README_FILES:
try:
readme = cs.get_node(f)
+ if not isinstance(readme, FileNode):
+ continue
readme_file = f
+ log.debug('Found README file `%s` rendering...' %
+ readme_file)
readme_data = renderer.render(readme.content, f)
- log.debug('Found readme %s' % readme_file)
break
except NodeDoesNotExistError:
continue
diff --git a/rhodecode/i18n/en/LC_MESSAGES/rhodecode.po b/rhodecode/i18n/en/LC_MESSAGES/rhodecode.po
index 5496b490..2b04bbed 100644
--- a/rhodecode/i18n/en/LC_MESSAGES/rhodecode.po
+++ b/rhodecode/i18n/en/LC_MESSAGES/rhodecode.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: rhodecode 0.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2011-09-14 15:50-0300\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\n"
"PO-Revision-Date: 2011-02-25 19:13+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: en <LL@li.org>\n"
@@ -17,20 +17,36 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-#: rhodecode/controllers/changeset.py:108
-#: rhodecode/controllers/changeset.py:149
-#: rhodecode/controllers/changeset.py:216
-#: rhodecode/controllers/changeset.py:229
+#: rhodecode/controllers/changelog.py:94
+msgid "All Branches"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:157
+#, python-format
+msgid "%s line context"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:333
+#: rhodecode/controllers/changeset.py:348 rhodecode/lib/diffs.py:70
msgid "binary file"
msgstr ""
-#: rhodecode/controllers/changeset.py:123
-#: rhodecode/controllers/changeset.py:168
-msgid "Changeset is to big and was cut off, see raw changeset instead"
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is "
+"not allowed"
msgstr ""
-#: rhodecode/controllers/changeset.py:159
-msgid "Diff is to big and was cut off, see raw diff instead"
+#: rhodecode/controllers/compare.py:69
+msgid "There are no changesets yet"
msgstr ""
#: rhodecode/controllers/error.py:69
@@ -59,138 +75,197 @@ msgid ""
"fulfilling the request."
msgstr ""
-#: rhodecode/controllers/feed.py:48
+#: rhodecode/controllers/feed.py:49
#, python-format
msgid "Changes on %s repository"
msgstr ""
-#: rhodecode/controllers/feed.py:49
+#: rhodecode/controllers/feed.py:50
#, python-format
msgid "%s %s feed"
msgstr ""
-#: rhodecode/controllers/files.py:72
-msgid "There are no files yet"
+#: rhodecode/controllers/feed.py:75
+msgid "commited on"
+msgstr ""
+
+#: rhodecode/controllers/files.py:84
+msgid "click here to add new file"
msgstr ""
-#: rhodecode/controllers/files.py:262
+#: rhodecode/controllers/files.py:85
+#, python-format
+msgid "There are no files yet %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:266
#, python-format
msgid "Edited %s via RhodeCode"
msgstr ""
-#: rhodecode/controllers/files.py:267
-#: rhodecode/templates/files/file_diff.html:40
+#: rhodecode/controllers/files.py:271
msgid "No changes"
msgstr ""
-#: rhodecode/controllers/files.py:278
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
#, python-format
msgid "Successfully committed to %s"
msgstr ""
-#: rhodecode/controllers/files.py:283
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
msgid "Error occurred during commit"
msgstr ""
-#: rhodecode/controllers/files.py:308
+#: rhodecode/controllers/files.py:318
+#, python-format
+msgid "Added %s via RhodeCode"
+msgstr ""
+
+#: rhodecode/controllers/files.py:332
+msgid "No content"
+msgstr ""
+
+#: rhodecode/controllers/files.py:336
+msgid "No filename"
+msgstr ""
+
+#: rhodecode/controllers/files.py:378
msgid "downloads disabled"
msgstr ""
-#: rhodecode/controllers/files.py:313
+#: rhodecode/controllers/files.py:389
#, python-format
msgid "Unknown revision %s"
msgstr ""
-#: rhodecode/controllers/files.py:315
+#: rhodecode/controllers/files.py:391
msgid "Empty repository"
msgstr ""
-#: rhodecode/controllers/files.py:317
+#: rhodecode/controllers/files.py:393
msgid "Unknown archive type"
msgstr ""
-#: rhodecode/controllers/files.py:385 rhodecode/controllers/files.py:398
-msgid "Binary file"
-msgstr ""
-
-#: rhodecode/controllers/files.py:417
-#: rhodecode/templates/changeset/changeset_range.html:4
-#: rhodecode/templates/changeset/changeset_range.html:12
-#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
msgid "Changesets"
msgstr ""
-#: rhodecode/controllers/files.py:418 rhodecode/controllers/summary.py:175
-#: rhodecode/templates/branches/branches.html:5
-#: rhodecode/templates/summary/summary.html:690
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
msgid "Branches"
msgstr ""
-#: rhodecode/controllers/files.py:419 rhodecode/controllers/summary.py:176
-#: rhodecode/templates/summary/summary.html:679
-#: rhodecode/templates/tags/tags.html:5
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
msgid "Tags"
msgstr ""
-#: rhodecode/controllers/journal.py:50
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
#, python-format
-msgid "%s public journal %s feed"
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the file system please run the application again in order to rescan "
+"repositories"
msgstr ""
-#: rhodecode/controllers/journal.py:178 rhodecode/controllers/journal.py:212
-#: rhodecode/templates/admin/repos/repo_edit.html:171
-#: rhodecode/templates/base/base.html:50
-msgid "Public journal"
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr ""
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+msgid "public journal"
msgstr ""
-#: rhodecode/controllers/login.py:111
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr ""
+
+#: rhodecode/controllers/login.py:143
msgid "You have successfully registered into rhodecode"
msgstr ""
-#: rhodecode/controllers/login.py:133
+#: rhodecode/controllers/login.py:164
msgid "Your password reset link was sent"
msgstr ""
-#: rhodecode/controllers/login.py:155
+#: rhodecode/controllers/login.py:184
msgid ""
"Your password reset was successful, new password has been sent to your "
"email"
msgstr ""
-#: rhodecode/controllers/search.py:109
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+msgid "Bookmarks"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+msgid "error during creation of pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:181
+msgid "Successfully opened new pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:184
+msgid "Error occurred during sending pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:217
+msgid "Successfully deleted pull request"
+msgstr ""
+
+#: rhodecode/controllers/search.py:131
msgid "Invalid search query. Try quoting it."
msgstr ""
-#: rhodecode/controllers/search.py:114
+#: rhodecode/controllers/search.py:136
msgid "There is no index to search in. Please run whoosh indexer"
msgstr ""
-#: rhodecode/controllers/search.py:118
+#: rhodecode/controllers/search.py:140
msgid "An error occurred during this search operation"
msgstr ""
-#: rhodecode/controllers/settings.py:61 rhodecode/controllers/settings.py:171
-#, python-format
-msgid ""
-"%s repository is not mapped to db perhaps it was created or renamed from "
-"the file system please run the application again in order to rescan "
-"repositories"
-msgstr ""
-
-#: rhodecode/controllers/settings.py:109
-#: rhodecode/controllers/admin/repos.py:239
+#: rhodecode/controllers/settings.py:107
+#: rhodecode/controllers/admin/repos.py:266
#, python-format
msgid "Repository %s updated successfully"
msgstr ""
-#: rhodecode/controllers/settings.py:126
-#: rhodecode/controllers/admin/repos.py:257
+#: rhodecode/controllers/settings.py:125
+#: rhodecode/controllers/admin/repos.py:284
#, python-format
msgid "error occurred during update of repository %s"
msgstr ""
-#: rhodecode/controllers/settings.py:144
-#: rhodecode/controllers/admin/repos.py:275
+#: rhodecode/controllers/settings.py:143
+#: rhodecode/controllers/admin/repos.py:302
#, python-format
msgid ""
"%s repository is not mapped to db perhaps it was moved or renamed from "
@@ -198,111 +273,102 @@ msgid ""
"repositories"
msgstr ""
-#: rhodecode/controllers/settings.py:156
-#: rhodecode/controllers/admin/repos.py:287
+#: rhodecode/controllers/settings.py:155
+#: rhodecode/controllers/admin/repos.py:314
#, python-format
msgid "deleted repository %s"
msgstr ""
#: rhodecode/controllers/settings.py:159
-#: rhodecode/controllers/admin/repos.py:297
-#: rhodecode/controllers/admin/repos.py:303
+#: rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
#, python-format
msgid "An error occurred during deletion of %s"
msgstr ""
-#: rhodecode/controllers/settings.py:193
-#, python-format
-msgid "forked %s repository as %s"
-msgstr ""
-
-#: rhodecode/controllers/settings.py:211
-#, python-format
-msgid "An error occurred during repository forking %s"
-msgstr ""
-
-#: rhodecode/controllers/summary.py:123
+#: rhodecode/controllers/summary.py:138
msgid "No data loaded yet"
msgstr ""
-#: rhodecode/controllers/summary.py:126
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
msgid "Statistics are disabled for this repository"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:49
+#: rhodecode/controllers/admin/ldap_settings.py:50
msgid "BASE"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:50
+#: rhodecode/controllers/admin/ldap_settings.py:51
msgid "ONELEVEL"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:51
+#: rhodecode/controllers/admin/ldap_settings.py:52
msgid "SUBTREE"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:55
+#: rhodecode/controllers/admin/ldap_settings.py:56
msgid "NEVER"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:56
+#: rhodecode/controllers/admin/ldap_settings.py:57
msgid "ALLOW"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:57
+#: rhodecode/controllers/admin/ldap_settings.py:58
msgid "TRY"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:58
+#: rhodecode/controllers/admin/ldap_settings.py:59
msgid "DEMAND"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:59
+#: rhodecode/controllers/admin/ldap_settings.py:60
msgid "HARD"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:63
+#: rhodecode/controllers/admin/ldap_settings.py:64
msgid "No encryption"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:64
+#: rhodecode/controllers/admin/ldap_settings.py:65
msgid "LDAPS connection"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:65
+#: rhodecode/controllers/admin/ldap_settings.py:66
msgid "START_TLS on LDAP connection"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:115
+#: rhodecode/controllers/admin/ldap_settings.py:126
msgid "Ldap settings updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:120
+#: rhodecode/controllers/admin/ldap_settings.py:130
msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:134
+#: rhodecode/controllers/admin/ldap_settings.py:147
msgid "error occurred during update of ldap settings"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:56
+#: rhodecode/controllers/admin/permissions.py:59
msgid "None"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:57
+#: rhodecode/controllers/admin/permissions.py:60
msgid "Read"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:58
+#: rhodecode/controllers/admin/permissions.py:61
msgid "Write"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:59
+#: rhodecode/controllers/admin/permissions.py:62
#: rhodecode/templates/admin/ldap/ldap.html:9
#: rhodecode/templates/admin/permissions/permissions.html:9
#: rhodecode/templates/admin/repos/repo_add.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:9
-#: rhodecode/templates/admin/repos/repos.html:10
+#: rhodecode/templates/admin/repos/repos.html:9
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
@@ -310,550 +376,871 @@ msgstr ""
#: rhodecode/templates/admin/settings/settings.html:9
#: rhodecode/templates/admin/users/user_add.html:8
#: rhodecode/templates/admin/users/user_edit.html:9
-#: rhodecode/templates/admin/users/user_edit.html:110
+#: rhodecode/templates/admin/users/user_edit.html:122
#: rhodecode/templates/admin/users/users.html:9
#: rhodecode/templates/admin/users_groups/users_group_add.html:8
#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
#: rhodecode/templates/admin/users_groups/users_groups.html:9
-#: rhodecode/templates/base/base.html:279
-#: rhodecode/templates/base/base.html:366
-#: rhodecode/templates/base/base.html:368
-#: rhodecode/templates/base/base.html:370
+#: rhodecode/templates/base/base.html:197
+#: rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339
+#: rhodecode/templates/base/base.html:341
msgid "Admin"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/controllers/admin/permissions.py:65
msgid "disabled"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:64
+#: rhodecode/controllers/admin/permissions.py:67
msgid "allowed with manual account activation"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:66
+#: rhodecode/controllers/admin/permissions.py:69
msgid "allowed with automatic account activation"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:68
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
msgid "Disabled"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:69
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
msgid "Enabled"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:102
+#: rhodecode/controllers/admin/permissions.py:116
msgid "Default permissions updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:119
+#: rhodecode/controllers/admin/permissions.py:130
msgid "error occurred during update of permissions"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:96
-#, python-format
-msgid ""
-"%s repository is not mapped to db perhaps it was created or renamed from "
-"the filesystem please run the application again in order to rescan "
-"repositories"
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:172
+#: rhodecode/controllers/admin/repos.py:192
#, python-format
msgid "created repository %s from %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:176
+#: rhodecode/controllers/admin/repos.py:196
#, python-format
msgid "created repository %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:205
+#: rhodecode/controllers/admin/repos.py:227
#, python-format
msgid "error occurred during creation of repository %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:292
+#: rhodecode/controllers/admin/repos.py:319
#, python-format
msgid "Cannot delete %s it still contains attached forks"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:320
+#: rhodecode/controllers/admin/repos.py:348
msgid "An error occurred during deletion of repository user"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:335
+#: rhodecode/controllers/admin/repos.py:367
msgid "An error occurred during deletion of repository users groups"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:352
+#: rhodecode/controllers/admin/repos.py:385
msgid "An error occurred during deletion of repository stats"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:367
+#: rhodecode/controllers/admin/repos.py:402
msgid "An error occurred during cache invalidation"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:387
+#: rhodecode/controllers/admin/repos.py:422
+msgid "An error occurred during unlocking"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:442
msgid "Updated repository visibility in public journal"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:390
+#: rhodecode/controllers/admin/repos.py:446
msgid "An error occurred during setting this repository in public journal"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:395 rhodecode/model/forms.py:53
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
msgid "Token mismatch"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:408
+#: rhodecode/controllers/admin/repos.py:464
msgid "Pulled from remote location"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:410
+#: rhodecode/controllers/admin/repos.py:466
msgid "An error occurred during pull from remote location"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:83
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:484
+#, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:488
+msgid "An error occurred during this operation"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:116
#, python-format
msgid "created repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:96
+#: rhodecode/controllers/admin/repos_groups.py:129
#, python-format
msgid "error occurred during creation of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:130
+#: rhodecode/controllers/admin/repos_groups.py:163
#, python-format
msgid "updated repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:143
+#: rhodecode/controllers/admin/repos_groups.py:176
#, python-format
msgid "error occurred during update of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:164
+#: rhodecode/controllers/admin/repos_groups.py:194
#, python-format
msgid "This group contains %s repositores and cannot be deleted"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:171
+#: rhodecode/controllers/admin/repos_groups.py:202
#, python-format
msgid "removed repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:175
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
#, python-format
msgid "error occurred during deletion of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:109
+#: rhodecode/controllers/admin/repos_groups.py:238
+msgid "An error occurred during deletion of group user"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:121
#, python-format
msgid "Repositories successfully rescanned added: %s,removed: %s"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:118
+#: rhodecode/controllers/admin/settings.py:129
msgid "Whoosh reindex task scheduled"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:143
+#: rhodecode/controllers/admin/settings.py:160
msgid "Updated application settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:148
-#: rhodecode/controllers/admin/settings.py:215
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
msgid "error occurred during updating application settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:210
-msgid "Updated mercurial settings"
+#: rhodecode/controllers/admin/settings.py:200
+msgid "Updated visualisation settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:205
+msgid "error occurred during updating visualisation settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:271
+msgid "Updated VCS settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:236
+#: rhodecode/controllers/admin/settings.py:285
msgid "Added new hook"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:247
+#: rhodecode/controllers/admin/settings.py:297
msgid "Updated hooks"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:251
+#: rhodecode/controllers/admin/settings.py:301
msgid "error occurred during hook creation"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:310
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:375
msgid "You can't edit this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:339
+#: rhodecode/controllers/admin/settings.py:406
msgid "Your account was updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:359
-#: rhodecode/controllers/admin/users.py:130
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
#, python-format
msgid "error occurred during update of user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:78
+#: rhodecode/controllers/admin/users.py:130
#, python-format
msgid "created user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:90
+#: rhodecode/controllers/admin/users.py:142
#, python-format
msgid "error occurred during creation of user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:116
+#: rhodecode/controllers/admin/users.py:171
msgid "User updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/users.py:146
+#: rhodecode/controllers/admin/users.py:207
msgid "successfully deleted user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:150
+#: rhodecode/controllers/admin/users.py:212
msgid "An error occurred during deletion of user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:166
+#: rhodecode/controllers/admin/users.py:226
msgid "You can't edit this user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:195
-#: rhodecode/controllers/admin/users_groups.py:202
+#: rhodecode/controllers/admin/users.py:266
msgid "Granted 'repository create' permission to user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:204
-#: rhodecode/controllers/admin/users_groups.py:211
+#: rhodecode/controllers/admin/users.py:271
msgid "Revoked 'repository create' permission to user"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:74
+#: rhodecode/controllers/admin/users.py:277
+msgid "Granted 'repository fork' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:282
+msgid "Revoked 'repository fork' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+msgid "An error occurred during permissions saving"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:309
+msgid "An error occurred during email saving"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:319
+msgid "Removed email from user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:84
#, python-format
msgid "created users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:86
+#: rhodecode/controllers/admin/users_groups.py:95
#, python-format
msgid "error occurred during creation of users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:119
+#: rhodecode/controllers/admin/users_groups.py:135
#, python-format
msgid "updated users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:138
+#: rhodecode/controllers/admin/users_groups.py:157
#, python-format
msgid "error occurred during update of users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:154
+#: rhodecode/controllers/admin/users_groups.py:174
msgid "successfully deleted users group"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:158
+#: rhodecode/controllers/admin/users_groups.py:179
msgid "An error occurred during deletion of users group"
msgstr ""
-#: rhodecode/lib/__init__.py:279
-msgid "year"
+#: rhodecode/controllers/admin/users_groups.py:233
+msgid "Granted 'repository create' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:280
-msgid "month"
+#: rhodecode/controllers/admin/users_groups.py:238
+msgid "Revoked 'repository create' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:281
-msgid "day"
+#: rhodecode/controllers/admin/users_groups.py:244
+msgid "Granted 'repository fork' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:282
-msgid "hour"
+#: rhodecode/controllers/admin/users_groups.py:249
+msgid "Revoked 'repository fork' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:283
-msgid "minute"
+#: rhodecode/lib/auth.py:499
+msgid "You need to be a registered user to perform this action"
msgstr ""
-#: rhodecode/lib/__init__.py:284
-msgid "second"
+#: rhodecode/lib/auth.py:540
+msgid "You need to be a signed in to view this page"
msgstr ""
-#: rhodecode/lib/__init__.py:293
-msgid "ago"
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
msgstr ""
-#: rhodecode/lib/__init__.py:296
-msgid "just now"
+#: rhodecode/lib/diffs.py:96
+msgid "No changes detected"
msgstr ""
-#: rhodecode/lib/auth.py:377
-msgid "You need to be a registered user to perform this action"
-msgstr ""
-
-#: rhodecode/lib/auth.py:421
-msgid "You need to be a signed in to view this page"
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
msgstr ""
-#: rhodecode/lib/helpers.py:307
+#: rhodecode/lib/helpers.py:484
msgid "True"
msgstr ""
-#: rhodecode/lib/helpers.py:311
+#: rhodecode/lib/helpers.py:488
msgid "False"
msgstr ""
-#: rhodecode/lib/helpers.py:352
+#: rhodecode/lib/helpers.py:532
+msgid "Changeset not found"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:555
#, python-format
msgid "Show all combined changesets %s->%s"
msgstr ""
-#: rhodecode/lib/helpers.py:356
+#: rhodecode/lib/helpers.py:561
msgid "compare view"
msgstr ""
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:581
msgid "and"
msgstr ""
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:582
#, python-format
msgid "%s more"
msgstr ""
-#: rhodecode/lib/helpers.py:367 rhodecode/templates/changelog/changelog.html:14
-#: rhodecode/templates/changelog/changelog.html:39
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
msgid "revisions"
msgstr ""
-#: rhodecode/lib/helpers.py:385
+#: rhodecode/lib/helpers.py:606
msgid "fork name "
msgstr ""
-#: rhodecode/lib/helpers.py:388
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:626
msgid "[deleted] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:389 rhodecode/lib/helpers.py:393
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
msgid "[created] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:390 rhodecode/lib/helpers.py:394
+#: rhodecode/lib/helpers.py:630
+msgid "[created] repository as fork"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
msgid "[forked] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:391 rhodecode/lib/helpers.py:395
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
msgid "[updated] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:392
+#: rhodecode/lib/helpers.py:636
msgid "[delete] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:396
+#: rhodecode/lib/helpers.py:644
+msgid "[created] user"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:646
+msgid "[updated] user"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:648
+msgid "[created] users group"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:650
+msgid "[updated] users group"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:652
+msgid "[commented] on revision in repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:654
+msgid "[commented] on pull request for"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:656
+msgid "[closed] pull request for"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:658
msgid "[pushed] into"
msgstr ""
-#: rhodecode/lib/helpers.py:397
-msgid "[committed via RhodeCode] into"
+#: rhodecode/lib/helpers.py:660
+msgid "[committed via RhodeCode] into repository"
msgstr ""
-#: rhodecode/lib/helpers.py:398
-msgid "[pulled from remote] into"
+#: rhodecode/lib/helpers.py:662
+msgid "[pulled from remote] into repository"
msgstr ""
-#: rhodecode/lib/helpers.py:399
+#: rhodecode/lib/helpers.py:664
msgid "[pulled] from"
msgstr ""
-#: rhodecode/lib/helpers.py:400
+#: rhodecode/lib/helpers.py:666
msgid "[started following] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:401
+#: rhodecode/lib/helpers.py:668
msgid "[stopped following] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:577
+#: rhodecode/lib/helpers.py:840
#, python-format
msgid " and %s more"
msgstr ""
-#: rhodecode/lib/helpers.py:581
+#: rhodecode/lib/helpers.py:844
msgid "No Files"
msgstr ""
-#: rhodecode/model/forms.py:66
-msgid "Invalid username"
+#: rhodecode/lib/utils2.py:335
+#, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:336
+#, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:337
+#, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:338
+#, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:339
+#, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:340
+#, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:355
+#, python-format
+msgid "%s ago"
msgstr ""
-#: rhodecode/model/forms.py:75
-msgid "This username already exists"
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
msgstr ""
-#: rhodecode/model/forms.py:79
-msgid ""
-"Username may only contain alphanumeric characters underscores, periods or"
-" dashes and must begin with alphanumeric character"
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
msgstr ""
-#: rhodecode/model/forms.py:94
-msgid "Invalid group name"
+#: rhodecode/lib/celerylib/tasks.py:269
+msgid "password reset link"
msgstr ""
-#: rhodecode/model/forms.py:104
-msgid "This users group already exists"
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
msgstr ""
-#: rhodecode/model/forms.py:110
-msgid ""
-"Group name may only contain alphanumeric characters underscores, periods "
-"or dashes and must begin with alphanumeric character"
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
msgstr ""
-#: rhodecode/model/forms.py:132
-msgid "Cannot assign this group as parent"
+#: rhodecode/model/db.py:1140
+msgid "Repository no access"
msgstr ""
-#: rhodecode/model/forms.py:148
-msgid "This group already exists"
+#: rhodecode/model/db.py:1141
+msgid "Repository read access"
msgstr ""
-#: rhodecode/model/forms.py:164 rhodecode/model/forms.py:172
-#: rhodecode/model/forms.py:180
-msgid "Invalid characters in password"
+#: rhodecode/model/db.py:1142
+msgid "Repository write access"
msgstr ""
-#: rhodecode/model/forms.py:191
-msgid "Passwords do not match"
+#: rhodecode/model/db.py:1143
+msgid "Repository admin access"
msgstr ""
-#: rhodecode/model/forms.py:196
-msgid "invalid password"
+#: rhodecode/model/db.py:1145
+msgid "Repositories Group no access"
msgstr ""
-#: rhodecode/model/forms.py:197
-msgid "invalid user name"
+#: rhodecode/model/db.py:1146
+msgid "Repositories Group read access"
msgstr ""
-#: rhodecode/model/forms.py:198
-msgid "Your account is disabled"
+#: rhodecode/model/db.py:1147
+msgid "Repositories Group write access"
msgstr ""
-#: rhodecode/model/forms.py:233
-msgid "This username is not valid"
+#: rhodecode/model/db.py:1148
+msgid "Repositories Group admin access"
msgstr ""
-#: rhodecode/model/forms.py:245
-msgid "This repository name is disallowed"
+#: rhodecode/model/db.py:1150
+msgid "RhodeCode Administrator"
msgstr ""
-#: rhodecode/model/forms.py:266
-#, python-format
-msgid "This repository already exists in group \"%s\""
+#: rhodecode/model/db.py:1151
+msgid "Repository creation disabled"
msgstr ""
-#: rhodecode/model/forms.py:274
-msgid "This repository already exists"
+#: rhodecode/model/db.py:1152
+msgid "Repository creation enabled"
msgstr ""
-#: rhodecode/model/forms.py:312 rhodecode/model/forms.py:319
-msgid "invalid clone url"
+#: rhodecode/model/db.py:1153
+msgid "Repository forking disabled"
msgstr ""
-#: rhodecode/model/forms.py:322
-msgid "Invalid clone url, provide a valid clone http\\s url"
+#: rhodecode/model/db.py:1154
+msgid "Repository forking enabled"
msgstr ""
-#: rhodecode/model/forms.py:334
-msgid "Fork have to be the same type as original"
+#: rhodecode/model/db.py:1155
+msgid "Register disabled"
msgstr ""
-#: rhodecode/model/forms.py:341
-msgid "This username or users group name is not valid"
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
msgstr ""
-#: rhodecode/model/forms.py:403
-msgid "This is not a valid path"
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
msgstr ""
-#: rhodecode/model/forms.py:416
-msgid "This e-mail address is already taken"
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
msgstr ""
-#: rhodecode/model/forms.py:427
-msgid "This e-mail address doesn't exist."
+#: rhodecode/model/db.py:1580
+msgid "Approved"
msgstr ""
-#: rhodecode/model/forms.py:447
-msgid ""
-"The LDAP Login attribute of the CN must be specified - this is the name "
-"of the attribute that is equivalent to 'username'"
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
msgstr ""
-#: rhodecode/model/forms.py:466
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
+msgstr ""
+
+#: rhodecode/model/forms.py:43
msgid "Please enter a login"
msgstr ""
-#: rhodecode/model/forms.py:467
+#: rhodecode/model/forms.py:44
#, python-format
msgid "Enter a value %(min)i characters long or more"
msgstr ""
-#: rhodecode/model/forms.py:475
+#: rhodecode/model/forms.py:52
msgid "Please enter a password"
msgstr ""
-#: rhodecode/model/forms.py:476
+#: rhodecode/model/forms.py:53
#, python-format
msgid "Enter %(min)i characters or more"
msgstr ""
-#: rhodecode/model/user.py:145
-msgid "[RhodeCode] New User registration"
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr ""
+
+#: rhodecode/model/notification.py:221
+msgid "sent message"
+msgstr ""
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr ""
+
+#: rhodecode/model/notification.py:223
+msgid "registered in RhodeCode"
+msgstr ""
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
+msgstr ""
+
+#: rhodecode/model/notification.py:225
+msgid "commented on pull request"
msgstr ""
-#: rhodecode/model/user.py:157 rhodecode/model/user.py:179
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+msgid "latest tip"
+msgstr ""
+
+#: rhodecode/model/user.py:230
+msgid "new user registration"
+msgstr ""
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
msgid "You can't Edit this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/model/user.py:201
+#: rhodecode/model/user.py:323
msgid "You can't remove this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/model/user.py:204
+#: rhodecode/model/user.py:329
+#, python-format
+msgid ""
+"user \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories. %s"
+msgstr ""
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, python-format
+msgid "Username \"%(username)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr ""
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with alphanumeric character"
+msgstr ""
+
+#: rhodecode/model/validators.py:114
+#, python-format
+msgid "Username %(username)s is not valid"
+msgstr ""
+
+#: rhodecode/model/validators.py:133
+msgid "Invalid users group name"
+msgstr ""
+
+#: rhodecode/model/validators.py:134
+#, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr ""
+
+#: rhodecode/model/validators.py:174
+msgid "Cannot assign this group as parent"
+msgstr ""
+
+#: rhodecode/model/validators.py:175
+#, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:177
+#, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:235
+msgid "Invalid characters (non-ascii) in password"
+msgstr ""
+
+#: rhodecode/model/validators.py:250
+msgid "Passwords do not match"
+msgstr ""
+
+#: rhodecode/model/validators.py:267
+msgid "invalid password"
+msgstr ""
+
+#: rhodecode/model/validators.py:268
+msgid "invalid user name"
+msgstr ""
+
+#: rhodecode/model/validators.py:269
+msgid "Your account is disabled"
+msgstr ""
+
+#: rhodecode/model/validators.py:313
+#, python-format
+msgid "Repository name %(repo)s is disallowed"
+msgstr ""
+
+#: rhodecode/model/validators.py:315
+#, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:316
#, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr ""
+
+#: rhodecode/model/validators.py:318
+#, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:431
+msgid "invalid clone url"
+msgstr ""
+
+#: rhodecode/model/validators.py:432
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
+msgstr ""
+
+#: rhodecode/model/validators.py:457
+msgid "Fork have to be the same type as parent"
+msgstr ""
+
+#: rhodecode/model/validators.py:478
+msgid "This username or users group name is not valid"
+msgstr ""
+
+#: rhodecode/model/validators.py:562
+msgid "This is not a valid path"
+msgstr ""
+
+#: rhodecode/model/validators.py:577
+msgid "This e-mail address is already taken"
+msgstr ""
+
+#: rhodecode/model/validators.py:597
+#, python-format
+msgid "e-mail \"%(email)s\" does not exist."
+msgstr ""
+
+#: rhodecode/model/validators.py:634
msgid ""
-"This user still owns %s repositories and cannot be removed. Switch owners"
-" or remove those repositories"
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
+msgstr ""
+
+#: rhodecode/model/validators.py:653
+#, python-format
+msgid "Revisions %(revs)s are already part of pull request or have set status"
msgstr ""
-#: rhodecode/templates/index.html:4
+#: rhodecode/templates/index.html:3
msgid "Dashboard"
msgstr ""
-#: rhodecode/templates/index_base.html:22
-#: rhodecode/templates/admin/users/user_edit_my_account.html:102
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
msgid "quick filter..."
msgstr ""
-#: rhodecode/templates/index_base.html:23
-#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
msgid "repositories"
msgstr ""
-#: rhodecode/templates/index_base.html:29
-#: rhodecode/templates/admin/repos/repos.html:22
-msgid "ADD NEW REPOSITORY"
+#: rhodecode/templates/index_base.html:13
+#: rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
msgstr ""
-#: rhodecode/templates/index_base.html:41
+#: rhodecode/templates/index_base.html:29
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
@@ -862,158 +1249,158 @@ msgstr ""
msgid "Group name"
msgstr ""
-#: rhodecode/templates/index_base.html:42
-#: rhodecode/templates/index_base.html:73
-#: rhodecode/templates/admin/repos/repo_add_base.html:44
-#: rhodecode/templates/admin/repos/repo_edit.html:64
-#: rhodecode/templates/admin/repos/repos.html:31
+#: rhodecode/templates/index_base.html:30
+#: rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142
+#: rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
-#: rhodecode/templates/settings/repo_fork.html:40
-#: rhodecode/templates/settings/repo_settings.html:40
-#: rhodecode/templates/summary/summary.html:92
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
msgid "Description"
msgstr ""
-#: rhodecode/templates/index_base.html:53
+#: rhodecode/templates/index_base.html:40
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
msgid "Repositories group"
msgstr ""
-#: rhodecode/templates/index_base.html:72
+#: rhodecode/templates/index_base.html:70
+#: rhodecode/templates/index_base.html:166
#: rhodecode/templates/admin/repos/repo_add_base.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:32
-#: rhodecode/templates/admin/repos/repos.html:30
-#: rhodecode/templates/admin/users/user_edit_my_account.html:117
-#: rhodecode/templates/files/files_browser.html:157
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
#: rhodecode/templates/settings/repo_settings.html:31
-#: rhodecode/templates/summary/summary.html:31
-#: rhodecode/templates/summary/summary.html:107
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36
+#: rhodecode/templates/tags/tags_data.html:6
msgid "Name"
msgstr ""
-#: rhodecode/templates/index_base.html:74
-#: rhodecode/templates/admin/repos/repos.html:32
-#: rhodecode/templates/summary/summary.html:114
+#: rhodecode/templates/index_base.html:72
msgid "Last change"
msgstr ""
-#: rhodecode/templates/index_base.html:75
-#: rhodecode/templates/admin/repos/repos.html:33
+#: rhodecode/templates/index_base.html:73
+#: rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
msgid "Tip"
msgstr ""
-#: rhodecode/templates/index_base.html:76
-#: rhodecode/templates/admin/repos/repo_edit.html:97
+#: rhodecode/templates/index_base.html:74
+#: rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
msgid "Owner"
msgstr ""
-#: rhodecode/templates/index_base.html:77
-#: rhodecode/templates/journal/public_journal.html:20
-#: rhodecode/templates/summary/summary.html:180
-#: rhodecode/templates/summary/summary.html:183
+#: rhodecode/templates/index_base.html:75
+#: rhodecode/templates/summary/summary.html:48
+#: rhodecode/templates/summary/summary.html:51
msgid "RSS"
msgstr ""
-#: rhodecode/templates/index_base.html:78
-#: rhodecode/templates/journal/public_journal.html:23
-#: rhodecode/templates/summary/summary.html:181
-#: rhodecode/templates/summary/summary.html:184
+#: rhodecode/templates/index_base.html:76
msgid "Atom"
msgstr ""
-#: rhodecode/templates/index_base.html:87
-#: rhodecode/templates/index_base.html:89
-#: rhodecode/templates/index_base.html:91
-#: rhodecode/templates/base/base.html:209
-#: rhodecode/templates/base/base.html:211
-#: rhodecode/templates/base/base.html:213
-#: rhodecode/templates/summary/summary.html:4
-msgid "Summary"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:95
-#: rhodecode/templates/index_base.html:97
-#: rhodecode/templates/index_base.html:99
-#: rhodecode/templates/base/base.html:225
-#: rhodecode/templates/base/base.html:227
-#: rhodecode/templates/base/base.html:229
-#: rhodecode/templates/changelog/changelog.html:6
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "Changelog"
+#: rhodecode/templates/index_base.html:110
+#: rhodecode/templates/index_base.html:112
+#, python-format
+msgid "Subscribe to %s rss feed"
msgstr ""
-#: rhodecode/templates/index_base.html:103
-#: rhodecode/templates/index_base.html:105
-#: rhodecode/templates/index_base.html:107
-#: rhodecode/templates/base/base.html:268
-#: rhodecode/templates/base/base.html:270
-#: rhodecode/templates/base/base.html:272
-#: rhodecode/templates/files/files.html:4
-msgid "Files"
+#: rhodecode/templates/index_base.html:117
+#: rhodecode/templates/index_base.html:119
+#, python-format
+msgid "Subscribe to %s atom feed"
msgstr ""
-#: rhodecode/templates/index_base.html:116
-#: rhodecode/templates/admin/repos/repos.html:42
-#: rhodecode/templates/admin/users/user_edit_my_account.html:127
-#: rhodecode/templates/summary/summary.html:48
-msgid "Mercurial repository"
+#: rhodecode/templates/index_base.html:140
+msgid "Group Name"
msgstr ""
-#: rhodecode/templates/index_base.html:118
-#: rhodecode/templates/admin/repos/repos.html:44
-#: rhodecode/templates/admin/users/user_edit_my_account.html:129
-#: rhodecode/templates/summary/summary.html:51
-msgid "Git repository"
+#: rhodecode/templates/index_base.html:158
+#: rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
msgstr ""
-#: rhodecode/templates/index_base.html:123
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
-#: rhodecode/templates/journal/journal.html:53
-#: rhodecode/templates/summary/summary.html:56
-msgid "private repository"
+#: rhodecode/templates/index_base.html:159
+#: rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
msgstr ""
-#: rhodecode/templates/index_base.html:125
-#: rhodecode/templates/journal/journal.html:55
-#: rhodecode/templates/summary/summary.html:58
-msgid "public repository"
+#: rhodecode/templates/index_base.html:169
+msgid "Last Change"
msgstr ""
-#: rhodecode/templates/index_base.html:133
-#: rhodecode/templates/base/base.html:291
-#: rhodecode/templates/settings/repo_fork.html:13
-msgid "fork"
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
msgstr ""
-#: rhodecode/templates/index_base.html:134
-#: rhodecode/templates/admin/repos/repos.html:60
-#: rhodecode/templates/admin/users/user_edit_my_account.html:143
-#: rhodecode/templates/summary/summary.html:69
-#: rhodecode/templates/summary/summary.html:71
-msgid "Fork of"
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
msgstr ""
-#: rhodecode/templates/index_base.html:155
-#: rhodecode/templates/admin/repos/repos.html:73
-msgid "No changesets yet"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:161
-#: rhodecode/templates/index_base.html:163
-#, python-format
-msgid "Subscribe to %s rss feed"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:168
-#: rhodecode/templates/index_base.html:170
-#, python-format
-msgid "Subscribe to %s atom feed"
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+msgid "Loading..."
msgstr ""
#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
-#: rhodecode/templates/base/base.html:38
msgid "Sign In"
msgstr ""
@@ -1024,25 +1411,29 @@ msgstr ""
#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
#: rhodecode/templates/admin/admin_log.html:5
#: rhodecode/templates/admin/users/user_add.html:32
-#: rhodecode/templates/admin/users/user_edit.html:47
-#: rhodecode/templates/admin/users/user_edit_my_account.html:45
-#: rhodecode/templates/base/base.html:15
-#: rhodecode/templates/summary/summary.html:106
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
msgid "Username"
msgstr ""
#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
#: rhodecode/templates/admin/ldap/ldap.html:46
#: rhodecode/templates/admin/users/user_add.html:41
-#: rhodecode/templates/base/base.html:24
+#: rhodecode/templates/base/base.html:92
msgid "Password"
msgstr ""
+#: rhodecode/templates/login.html:50
+msgid "Remember me"
+msgstr ""
+
#: rhodecode/templates/login.html:60
msgid "Forgot your password ?"
msgstr ""
-#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:35
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
msgid "Don't have an account ?"
msgstr ""
@@ -1079,24 +1470,24 @@ msgid "Re-enter password"
msgstr ""
#: rhodecode/templates/register.html:47
-#: rhodecode/templates/admin/users/user_add.html:50
-#: rhodecode/templates/admin/users/user_edit.html:74
-#: rhodecode/templates/admin/users/user_edit_my_account.html:63
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
msgid "First Name"
msgstr ""
#: rhodecode/templates/register.html:56
-#: rhodecode/templates/admin/users/user_add.html:59
-#: rhodecode/templates/admin/users/user_edit.html:83
-#: rhodecode/templates/admin/users/user_edit_my_account.html:72
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
msgid "Last Name"
msgstr ""
#: rhodecode/templates/register.html:65
-#: rhodecode/templates/admin/users/user_add.html:68
-#: rhodecode/templates/admin/users/user_edit.html:92
-#: rhodecode/templates/admin/users/user_edit_my_account.html:81
-#: rhodecode/templates/summary/summary.html:108
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
msgid "Email"
msgstr ""
@@ -1108,20 +1499,59 @@ msgstr ""
msgid "Your account must wait for activation by administrator"
msgstr ""
-#: rhodecode/templates/repo_switcher_list.html:14
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
msgid "Private repository"
msgstr ""
-#: rhodecode/templates/repo_switcher_list.html:19
+#: rhodecode/templates/repo_switcher_list.html:16
msgid "Public repository"
msgstr ""
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+msgid "There are no bookmarks yet"
+msgstr ""
+
#: rhodecode/templates/admin/admin.html:5
#: rhodecode/templates/admin/admin.html:9
msgid "Admin journal"
msgstr ""
#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
msgid "Action"
msgstr ""
@@ -1130,6 +1560,11 @@ msgid "Repository"
msgstr ""
#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37
+#: rhodecode/templates/tags/tags_data.html:7
msgid "Date"
msgstr ""
@@ -1137,7 +1572,7 @@ msgstr ""
msgid "From IP"
msgstr ""
-#: rhodecode/templates/admin/admin_log.html:52
+#: rhodecode/templates/admin/admin_log.html:53
msgid "No actions yet"
msgstr ""
@@ -1214,23 +1649,64 @@ msgid "E-mail Attribute"
msgstr ""
#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
#: rhodecode/templates/admin/settings/hooks.html:73
-#: rhodecode/templates/admin/users/user_edit.html:117
-#: rhodecode/templates/admin/users/user_edit.html:142
-#: rhodecode/templates/admin/users/user_edit_my_account.html:89
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:263
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
msgid "Save"
msgstr ""
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+#, fuzzy
+msgid "Comments"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254
+#: rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+msgid "No notifications here yet"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+msgid "Show notification"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+msgid "Notifications"
+msgstr ""
+
#: rhodecode/templates/admin/permissions/permissions.html:5
msgid "Permissions administration"
msgstr ""
#: rhodecode/templates/admin/permissions/permissions.html:11
-#: rhodecode/templates/admin/repos/repo_edit.html:109
-#: rhodecode/templates/admin/users/user_edit.html:127
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:248
-#: rhodecode/templates/settings/repo_settings.html:58
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
msgid "Permissions"
msgstr ""
@@ -1266,6 +1742,11 @@ msgid "Repository creation"
msgstr ""
#: rhodecode/templates/admin/permissions/permissions.html:71
+msgid "Repository forking"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
msgid "set"
msgstr ""
@@ -1276,7 +1757,6 @@ msgstr ""
#: rhodecode/templates/admin/repos/repo_add.html:11
#: rhodecode/templates/admin/repos/repo_edit.html:11
-#: rhodecode/templates/admin/repos/repos.html:10
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
msgid "Repositories"
msgstr ""
@@ -1286,30 +1766,70 @@ msgid "add new"
msgstr ""
#: rhodecode/templates/admin/repos/repo_add_base.html:20
-#: rhodecode/templates/summary/summary.html:80
-#: rhodecode/templates/summary/summary.html:82
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
msgid "Clone from"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:28
-#: rhodecode/templates/admin/repos/repo_edit.html:48
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
msgid "Repository group"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:36
-#: rhodecode/templates/admin/repos/repo_edit.html:56
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+msgid "Optionaly select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
msgid "Type"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:52
-#: rhodecode/templates/admin/repos/repo_edit.html:73
-#: rhodecode/templates/settings/repo_fork.html:48
-#: rhodecode/templates/settings/repo_settings.html:49
-msgid "Private"
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+msgid "Type of repository to create."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+msgid "Landing revision"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:59
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
msgid "add"
msgstr ""
@@ -1323,183 +1843,269 @@ msgstr ""
#: rhodecode/templates/admin/repos/repo_edit.html:13
#: rhodecode/templates/admin/users/user_edit.html:13
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
-#: rhodecode/templates/files/files_annotate.html:49
-#: rhodecode/templates/files/files_source.html:20
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
msgid "edit"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
msgid "Clone uri"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:81
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
msgid "Enable statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
msgid "Enable downloads"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:127
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+msgid "Enable locking"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+msgid "Change owner of this repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
msgid "Administration"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:130
+#: rhodecode/templates/admin/repos/repo_edit.html:155
msgid "Statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Reset current statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Confirm to remove current statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:137
+#: rhodecode/templates/admin/repos/repo_edit.html:162
msgid "Fetched to rev"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:138
-msgid "Percentage of stats gathered"
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:147
+#: rhodecode/templates/admin/repos/repo_edit.html:171
msgid "Remote"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Pull changes from remote location"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Confirm to pull changes from remote side"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:162
+#: rhodecode/templates/admin/repos/repo_edit.html:186
msgid "Cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Invalidate repository cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Confirm to invalidate repository cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:177
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318
+#: rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
msgid "Remove from public journal"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:179
+#: rhodecode/templates/admin/repos/repo_edit.html:203
msgid "Add to public journal"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:185
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in "
+"public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+msgid "Locking"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Confirm to unlock repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "Confirm to lock repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+msgid "Repository is not locked"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+msgid "Set as fork of"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+msgid "Manually set this repository as a fork of another from the list"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
msgid "Delete"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:189
+#: rhodecode/templates/admin/repos/repo_edit.html:255
msgid "Remove this repository"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:189
-#: rhodecode/templates/admin/repos/repos.html:79
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
msgid "Confirm to delete this repository"
msgstr ""
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be "
+"unaccesible for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem "
+"please do it manually"
+msgstr ""
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
msgid "none"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
msgid "read"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
msgid "write"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
-#: rhodecode/templates/admin/users/users.html:38
-#: rhodecode/templates/base/base.html:296
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
msgid "admin"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
msgid "member"
msgstr ""
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+msgid "default"
+msgstr ""
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:53
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
msgid "revoke"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:75
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
msgid "Add another member"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:89
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
msgid "Failed to remove user"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:104
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
msgid "Failed to remove users group"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:205
-msgid "Group"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:206
-#: rhodecode/templates/admin/users_groups/users_groups.html:33
-msgid "members"
-msgstr ""
-
#: rhodecode/templates/admin/repos/repos.html:5
msgid "Repositories administration"
msgstr ""
-#: rhodecode/templates/admin/repos/repos.html:34
-#: rhodecode/templates/summary/summary.html:100
-msgid "Contact"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:35
-#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
-#: rhodecode/templates/admin/users/user_edit_my_account.html:119
-#: rhodecode/templates/admin/users/users.html:40
-#: rhodecode/templates/admin/users_groups/users_groups.html:35
-msgid "action"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:51
-#: rhodecode/templates/admin/users/user_edit_my_account.html:134
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
-msgid "private"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:53
-#: rhodecode/templates/admin/repos/repos.html:59
-#: rhodecode/templates/admin/users/user_edit_my_account.html:136
-#: rhodecode/templates/admin/users/user_edit_my_account.html:142
-#: rhodecode/templates/summary/summary.html:68
-msgid "public"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:79
-#: rhodecode/templates/admin/users/users.html:55
-msgid "delete"
-msgstr ""
-
#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
msgid "Groups"
msgstr ""
-#: rhodecode/templates/admin/repos_groups/repos_groups.html:13
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
msgid "with"
msgstr ""
@@ -1522,10 +2128,10 @@ msgid "Group parent"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
-#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
-#: rhodecode/templates/admin/users/user_add.html:85
+#: rhodecode/templates/admin/users/user_add.html:94
#: rhodecode/templates/admin/users_groups/users_group_add.html:49
#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
msgid "save"
msgstr ""
@@ -1537,6 +2143,12 @@ msgstr ""
msgid "edit repos group"
msgstr ""
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
msgid "Repositories groups administration"
msgstr ""
@@ -1546,11 +2158,26 @@ msgid "ADD NEW GROUP"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
-msgid "Number of repositories"
+msgid "Number of toplevel repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
-msgid "Confirm to delete this group"
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#, python-format
+msgid "Confirm to delete this group: %s"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
@@ -1564,7 +2191,6 @@ msgstr ""
#: rhodecode/templates/admin/settings/hooks.html:9
#: rhodecode/templates/admin/settings/settings.html:9
-#: rhodecode/templates/settings/repo_settings.html:5
#: rhodecode/templates/settings/repo_settings.html:13
msgid "Settings"
msgstr ""
@@ -1604,115 +2230,185 @@ msgstr ""
msgid "destroy old data"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:45
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete "
+"if `destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
msgid "Rescan repositories"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:51
+#: rhodecode/templates/admin/settings/settings.html:52
msgid "Whoosh indexing"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:59
+#: rhodecode/templates/admin/settings/settings.html:60
msgid "index build option"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:64
+#: rhodecode/templates/admin/settings/settings.html:65
msgid "build from scratch"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:70
+#: rhodecode/templates/admin/settings/settings.html:71
msgid "Reindex"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:76
+#: rhodecode/templates/admin/settings/settings.html:77
msgid "Global application settings"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:85
+#: rhodecode/templates/admin/settings/settings.html:86
msgid "Application name"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:94
+#: rhodecode/templates/admin/settings/settings.html:95
msgid "Realm text"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:103
+#: rhodecode/templates/admin/settings/settings.html:104
msgid "GA code"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:111
-#: rhodecode/templates/admin/settings/settings.html:177
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
msgid "Save settings"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:112
-#: rhodecode/templates/admin/settings/settings.html:178
-#: rhodecode/templates/admin/users/user_edit.html:118
-#: rhodecode/templates/admin/users/user_edit.html:143
-#: rhodecode/templates/admin/users/user_edit_my_account.html:90
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:264
-#: rhodecode/templates/files/files_edit.html:50
-msgid "Reset"
+#: rhodecode/templates/admin/settings/settings.html:119
+msgid "Visualisation settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:128
+msgid "Icons"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+msgid "Show private repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:144
+msgid "Meta-Tagging"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:118
-msgid "Mercurial settings"
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:127
+#: rhodecode/templates/admin/settings/settings.html:176
+msgid "VCS settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:185
msgid "Web"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:132
-msgid "require ssl for pushing"
+#: rhodecode/templates/admin/settings/settings.html:190
+msgid "require ssl for vcs operations"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:139
-msgid "Hooks"
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
+"will return HTTP Error 406: Not Acceptable"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:142
-msgid "advanced setup"
+#: rhodecode/templates/admin/settings/settings.html:198
+msgid "Hooks"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:147
+#: rhodecode/templates/admin/settings/settings.html:203
msgid "Update repository after push (hg update)"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:151
+#: rhodecode/templates/admin/settings/settings.html:207
msgid "Show repository size after push"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:155
+#: rhodecode/templates/admin/settings/settings.html:211
msgid "Log user push commands"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:159
+#: rhodecode/templates/admin/settings/settings.html:215
msgid "Log user pull commands"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:166
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:224
+msgid "Mercurial Extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
msgid "Repositories location"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:171
+#: rhodecode/templates/admin/settings/settings.html:250
msgid ""
"This a crucial application setting. If you are really sure you need to "
"change this, you must restart application in order to make this setting "
"take effect. Click this label to unlock."
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:172
+#: rhodecode/templates/admin/settings/settings.html:251
msgid "unlock"
msgstr ""
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a "
+"restart, and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:280
+msgid "Email to"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:288
+msgid "Send"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:297
+msgid "show"
+msgstr ""
+
#: rhodecode/templates/admin/users/user_add.html:5
msgid "Add user"
msgstr ""
#: rhodecode/templates/admin/users/user_add.html:10
#: rhodecode/templates/admin/users/user_edit.html:11
-#: rhodecode/templates/admin/users/users.html:9
msgid "Users"
msgstr ""
@@ -1720,8 +2416,12 @@ msgstr ""
msgid "add new user"
msgstr ""
-#: rhodecode/templates/admin/users/user_add.html:77
-#: rhodecode/templates/admin/users/user_edit.html:101
+#: rhodecode/templates/admin/users/user_add.html:50
+msgid "Password confirmation"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
#: rhodecode/templates/admin/users_groups/users_group_add.html:41
#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
msgid "Active"
@@ -1731,36 +2431,93 @@ msgstr ""
msgid "Edit user"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:33
-#: rhodecode/templates/admin/users/user_edit_my_account.html:32
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
msgid "Change your avatar at"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:34
-#: rhodecode/templates/admin/users/user_edit_my_account.html:33
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
msgid "Using"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:40
-#: rhodecode/templates/admin/users/user_edit_my_account.html:39
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
msgid "API key"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:56
+#: rhodecode/templates/admin/users/user_edit.html:59
msgid "LDAP DN"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:65
-#: rhodecode/templates/admin/users/user_edit_my_account.html:54
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
msgid "New password"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:135
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:256
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+msgid "Inherit default permissions"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
msgid "Create repositories"
msgstr ""
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+msgid "Fork repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+msgid "Nothing here yet"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+msgid "Permission"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+msgid "Edit Permission"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+msgid "Email addresses"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, python-format
+msgid "Confirm to delete this email: %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+msgid "New email address"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+msgid "Add"
+msgstr ""
+
#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
msgid "My account"
msgstr ""
@@ -1768,26 +2525,74 @@ msgstr ""
msgid "My Account"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:101
-msgid "My repositories"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+msgid "My permissions"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:107
-msgid "ADD REPOSITORY"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+msgid "My repos"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:118
-#: rhodecode/templates/branches/branches_data.html:7
-#: rhodecode/templates/shortlog/shortlog_data.html:8
-#: rhodecode/templates/tags/tags_data.html:7
-msgid "revision"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+msgid "My pull requests"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+msgid "Add repo"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, python-format
+msgid "Pull request #%s opened on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+msgid "Confirm to delete this pull request"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40
+#: rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
msgid "No repositories yet"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
msgid "create one now"
msgstr ""
@@ -1795,42 +2600,41 @@ msgstr ""
msgid "Users administration"
msgstr ""
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr ""
+
#: rhodecode/templates/admin/users/users.html:23
msgid "ADD NEW USER"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:33
+#: rhodecode/templates/admin/users/users.html:77
msgid "username"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:34
-#: rhodecode/templates/branches/branches_data.html:5
-#: rhodecode/templates/tags/tags_data.html:5
-msgid "name"
+#: rhodecode/templates/admin/users/users.html:80
+msgid "firstname"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:35
+#: rhodecode/templates/admin/users/users.html:81
msgid "lastname"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:36
+#: rhodecode/templates/admin/users/users.html:82
msgid "last login"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:37
+#: rhodecode/templates/admin/users/users.html:84
#: rhodecode/templates/admin/users_groups/users_groups.html:34
msgid "active"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:39
-#: rhodecode/templates/base/base.html:305
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
msgid "ldap"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:56
-msgid "Confirm to delete this user"
-msgstr ""
-
#: rhodecode/templates/admin/users_groups/users_group_add.html:5
msgid "Add users group"
msgstr ""
@@ -1872,6 +2676,10 @@ msgstr ""
msgid "Add all elements"
msgstr ""
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+msgid "Group members"
+msgstr ""
+
#: rhodecode/templates/admin/users_groups/users_groups.html:5
msgid "Users groups administration"
msgstr ""
@@ -1884,396 +2692,616 @@ msgstr ""
msgid "group name"
msgstr ""
-#: rhodecode/templates/base/base.html:32
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:100
msgid "Forgot password ?"
msgstr ""
-#: rhodecode/templates/base/base.html:57 rhodecode/templates/base/base.html:338
-#: rhodecode/templates/base/base.html:340
-#: rhodecode/templates/base/base.html:342
+#: rhodecode/templates/base/base.html:107
+msgid "Log In"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:122
+#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302
+#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8
+#: rhodecode/templates/tags/tags.html:11
msgid "Home"
msgstr ""
-#: rhodecode/templates/base/base.html:61 rhodecode/templates/base/base.html:347
-#: rhodecode/templates/base/base.html:349
-#: rhodecode/templates/base/base.html:351
+#: rhodecode/templates/base/base.html:123
+#: rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311
+#: rhodecode/templates/base/base.html:313
#: rhodecode/templates/journal/journal.html:4
-#: rhodecode/templates/journal/journal.html:17
+#: rhodecode/templates/journal/journal.html:21
#: rhodecode/templates/journal/public_journal.html:4
msgid "Journal"
msgstr ""
-#: rhodecode/templates/base/base.html:66
-msgid "Login"
-msgstr ""
-
-#: rhodecode/templates/base/base.html:68
+#: rhodecode/templates/base/base.html:125
msgid "Log Out"
msgstr ""
-#: rhodecode/templates/base/base.html:107
-msgid "Submit a bug"
-msgstr ""
-
-#: rhodecode/templates/base/base.html:141
+#: rhodecode/templates/base/base.html:144
msgid "Switch repository"
msgstr ""
-#: rhodecode/templates/base/base.html:143
+#: rhodecode/templates/base/base.html:146
msgid "Products"
msgstr ""
-#: rhodecode/templates/base/base.html:149
+#: rhodecode/templates/base/base.html:152
+#: rhodecode/templates/base/base.html:182
msgid "loading..."
msgstr ""
-#: rhodecode/templates/base/base.html:234
-#: rhodecode/templates/base/base.html:236
-#: rhodecode/templates/base/base.html:238
-msgid "Switch to"
-msgstr ""
-
-#: rhodecode/templates/base/base.html:242
-#: rhodecode/templates/branches/branches.html:13
-msgid "branches"
+#: rhodecode/templates/base/base.html:158
+#: rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
msgstr ""
-#: rhodecode/templates/base/base.html:249
-#: rhodecode/templates/branches/branches_data.html:52
-msgid "There are no branches yet"
+#: rhodecode/templates/base/base.html:166
+#: rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
msgstr ""
-#: rhodecode/templates/base/base.html:254
-#: rhodecode/templates/shortlog/shortlog_data.html:10
-#: rhodecode/templates/tags/tags.html:14
-msgid "tags"
+#: rhodecode/templates/base/base.html:175
+#: rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
msgstr ""
-#: rhodecode/templates/base/base.html:261
-#: rhodecode/templates/tags/tags_data.html:32
-msgid "There are no tags yet"
+#: rhodecode/templates/base/base.html:186
+#: rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
msgstr ""
-#: rhodecode/templates/base/base.html:277
-#: rhodecode/templates/base/base.html:281
-#: rhodecode/templates/files/files_annotate.html:40
-#: rhodecode/templates/files/files_source.html:11
+#: rhodecode/templates/base/base.html:195
+#: rhodecode/templates/base/base.html:199
msgid "Options"
msgstr ""
-#: rhodecode/templates/base/base.html:286
-#: rhodecode/templates/base/base.html:288
-#: rhodecode/templates/base/base.html:306
+#: rhodecode/templates/base/base.html:204
+#: rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
msgid "settings"
msgstr ""
-#: rhodecode/templates/base/base.html:292
-msgid "search"
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
msgstr ""
-#: rhodecode/templates/base/base.html:299
-msgid "journal"
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
msgstr ""
-#: rhodecode/templates/base/base.html:301
-msgid "repositories groups"
+#: rhodecode/templates/base/base.html:213
+msgid "search"
msgstr ""
-#: rhodecode/templates/base/base.html:302
-msgid "users"
+#: rhodecode/templates/base/base.html:222
+msgid "repositories groups"
msgstr ""
-#: rhodecode/templates/base/base.html:303
+#: rhodecode/templates/base/base.html:224
msgid "users groups"
msgstr ""
-#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/base/base.html:225
msgid "permissions"
msgstr ""
-#: rhodecode/templates/base/base.html:317
-#: rhodecode/templates/base/base.html:319
-#: rhodecode/templates/followers/followers.html:5
+#: rhodecode/templates/base/base.html:238
+#: rhodecode/templates/base/base.html:240
msgid "Followers"
msgstr ""
-#: rhodecode/templates/base/base.html:325
-#: rhodecode/templates/base/base.html:327
-#: rhodecode/templates/forks/forks.html:5
+#: rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248
msgid "Forks"
msgstr ""
-#: rhodecode/templates/base/base.html:356
-#: rhodecode/templates/base/base.html:358
-#: rhodecode/templates/base/base.html:360
-#: rhodecode/templates/search/search.html:4
-#: rhodecode/templates/search/search.html:24
-#: rhodecode/templates/search/search.html:46
+#: rhodecode/templates/base/base.html:327
+#: rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331
+#: rhodecode/templates/search/search.html:52
msgid "Search"
msgstr ""
-#: rhodecode/templates/base/root.html:57
-#: rhodecode/templates/journal/journal.html:48
-#: rhodecode/templates/summary/summary.html:36
+#: rhodecode/templates/base/root.html:42
+msgid "add another comment"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
msgid "Stop following this repository"
msgstr ""
-#: rhodecode/templates/base/root.html:66
-#: rhodecode/templates/summary/summary.html:40
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
msgid "Start following this repository"
msgstr ""
-#: rhodecode/templates/branches/branches_data.html:4
-#: rhodecode/templates/tags/tags_data.html:4
-msgid "date"
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr ""
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr ""
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39
+#: rhodecode/templates/tags/tags_data.html:8
+msgid "Author"
+msgstr ""
+
+#: rhodecode/templates/branches/branches.html:5
+#, python-format
+msgid "%s Branches"
+msgstr ""
+
+#: rhodecode/templates/branches/branches.html:29
+msgid "Compare branches"
+msgstr ""
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+msgid "Compare"
msgstr ""
#: rhodecode/templates/branches/branches_data.html:6
-#: rhodecode/templates/shortlog/shortlog_data.html:7
-#: rhodecode/templates/tags/tags_data.html:6
-msgid "author"
+msgid "name"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:7
+msgid "date"
msgstr ""
#: rhodecode/templates/branches/branches_data.html:8
-#: rhodecode/templates/shortlog/shortlog_data.html:11
-#: rhodecode/templates/tags/tags_data.html:8
-msgid "links"
+#: rhodecode/templates/shortlog/shortlog_data.html:8
+msgid "author"
msgstr ""
-#: rhodecode/templates/branches/branches_data.html:23
-#: rhodecode/templates/branches/branches_data.html:43
-#: rhodecode/templates/shortlog/shortlog_data.html:39
-#: rhodecode/templates/tags/tags_data.html:24
-msgid "changeset"
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
msgstr ""
-#: rhodecode/templates/branches/branches_data.html:25
-#: rhodecode/templates/branches/branches_data.html:45
-#: rhodecode/templates/files/files.html:12
-#: rhodecode/templates/shortlog/shortlog_data.html:41
-#: rhodecode/templates/summary/summary.html:233
-#: rhodecode/templates/tags/tags_data.html:26
-msgid "files"
+#: rhodecode/templates/branches/branches_data.html:10
+msgid "compare"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "showing "
+#: rhodecode/templates/changelog/changelog.html:6
+#, python-format
+msgid "%s Changelog"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "out of"
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
msgstr ""
#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+msgid "Compare fork"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:46
msgid "Show"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:50
-#: rhodecode/templates/changeset/changeset.html:42
-#: rhodecode/templates/summary/summary.html:609
-msgid "commit"
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:63
+#: rhodecode/templates/changelog/changelog.html:76
msgid "Affected number of files, click to show more details"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:67
-#: rhodecode/templates/changeset/changeset.html:66
-msgid "merge"
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+msgid "Changeset status"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:72
-#: rhodecode/templates/changeset/changeset.html:72
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
msgid "Parent"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:77
-#: rhodecode/templates/changeset/changeset.html:77
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
msgid "No parents"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:82
-#: rhodecode/templates/changeset/changeset.html:80
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
#: rhodecode/templates/files/files.html:29
-#: rhodecode/templates/files/files_annotate.html:25
+#: rhodecode/templates/files/files_add.html:33
#: rhodecode/templates/files/files_edit.html:33
#: rhodecode/templates/shortlog/shortlog_data.html:9
msgid "branch"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:86
-#: rhodecode/templates/changeset/changeset.html:83
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
msgid "tag"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:122
+#: rhodecode/templates/changelog/changelog.html:164
msgid "Show selected changes __S -> __E"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:172
-#: rhodecode/templates/shortlog/shortlog_data.html:61
+#: rhodecode/templates/changelog/changelog.html:255
msgid "There are no changes yet"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:2
-#: rhodecode/templates/changeset/changeset.html:55
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
msgid "removed"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:3
-#: rhodecode/templates/changeset/changeset.html:56
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
msgid "changed"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:4
-#: rhodecode/templates/changeset/changeset.html:57
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
msgid "added"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:6
-#: rhodecode/templates/changelog/changelog_details.html:7
#: rhodecode/templates/changelog/changelog_details.html:8
-#: rhodecode/templates/changeset/changeset.html:59
-#: rhodecode/templates/changeset/changeset.html:60
-#: rhodecode/templates/changeset/changeset.html:61
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
#, python-format
msgid "affected %s files"
msgstr ""
#: rhodecode/templates/changeset/changeset.html:6
+#, python-format
+msgid "%s Changeset"
+msgstr ""
+
#: rhodecode/templates/changeset/changeset.html:14
-#: rhodecode/templates/changeset/changeset.html:31
msgid "Changeset"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:32
-#: rhodecode/templates/changeset/changeset.html:121
-#: rhodecode/templates/changeset/changeset_range.html:78
-#: rhodecode/templates/files/file_diff.html:32
-#: rhodecode/templates/files/file_diff.html:42
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
msgid "raw diff"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:34
-#: rhodecode/templates/changeset/changeset.html:123
-#: rhodecode/templates/changeset/changeset_range.html:80
-#: rhodecode/templates/files/file_diff.html:34
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
msgid "download diff"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:90
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/changeset/changeset.html:103
#, python-format
-msgid "%s files affected with %s additions and %s deletions."
+msgid "%s files affected with %s insertions and %s deletions:"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:101
+#: rhodecode/templates/changeset/changeset.html:119
msgid "Changeset was too big and was cut off..."
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:119
-#: rhodecode/templates/changeset/changeset_range.html:76
-#: rhodecode/templates/files/file_diff.html:30
-msgid "diff"
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+msgid "Comment"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:132
-#: rhodecode/templates/changeset/changeset_range.html:89
-msgid "No changes in this file"
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
msgstr ""
-#: rhodecode/templates/changeset/changeset_range.html:30
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "You need to be logged in to comment."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "change status"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, python-format
+msgid "%s Changesets"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
msgid "Compare View"
msgstr ""
-#: rhodecode/templates/changeset/changeset_range.html:52
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
msgid "Files affected"
msgstr ""
-#: rhodecode/templates/errors/error_document.html:44
-#, python-format
-msgid "You will be redirected to %s in %s seconds"
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
msgstr ""
-#: rhodecode/templates/files/file_diff.html:4
-#: rhodecode/templates/files/file_diff.html:12
-msgid "File diff"
+#: rhodecode/templates/changeset/diff_block.html:27
+msgid "show inline comments"
msgstr ""
-#: rhodecode/templates/files/file_diff.html:42
-msgid "Diff is to big to display"
+#: rhodecode/templates/compare/compare_cs.html:5
+msgid "No changesets"
msgstr ""
-#: rhodecode/templates/files/files.html:37
-#: rhodecode/templates/files/files_annotate.html:31
-#: rhodecode/templates/files/files_edit.html:39
-msgid "Location"
+#: rhodecode/templates/compare/compare_diff.html:37
+msgid "Outgoing changesets"
msgstr ""
-#: rhodecode/templates/files/files.html:46
-msgid "Go back"
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
msgstr ""
-#: rhodecode/templates/files/files.html:47
-msgid "No files at given path"
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:4
-msgid "File annotate"
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:12
-msgid "annotate"
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:33
-#: rhodecode/templates/files/files_browser.html:160
-#: rhodecode/templates/files/files_source.html:2
-msgid "Revision"
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:36
-#: rhodecode/templates/files/files_browser.html:158
-#: rhodecode/templates/files/files_source.html:7
-msgid "Size"
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:38
-#: rhodecode/templates/files/files_browser.html:159
-#: rhodecode/templates/files/files_source.html:9
-msgid "Mimetype"
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, python-format
+msgid "Confirm to delete this user: %s"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:41
-msgid "show source"
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:43
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:14
-#: rhodecode/templates/files/files_source.html:51
-msgid "show as raw"
+#: rhodecode/templates/errors/error_document.html:46
+#, python-format
+msgid "You will be redirected to %s in %s seconds"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:45
-#: rhodecode/templates/files/files_source.html:16
-msgid "download as raw"
+#: rhodecode/templates/files/file_diff.html:4
+#, python-format
+msgid "%s File diff"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:54
-#: rhodecode/templates/files/files_source.html:25
-msgid "History"
+#: rhodecode/templates/files/file_diff.html:12
+msgid "File diff"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:73
-#: rhodecode/templates/files/files_source.html:46
+#: rhodecode/templates/files/files.html:4
+#: rhodecode/templates/files/files.html:72
#, python-format
-msgid "Binary file (%s)"
+msgid "%s files"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:51
-msgid "File is too big to display"
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, python-format
+msgid "%s Edit file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:19
+msgid "add file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:40
+msgid "Add new file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:45
+msgid "File Name"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+msgid "or"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+msgid "Upload file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:58
+msgid "Create new file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
msgstr ""
#: rhodecode/templates/files/files_browser.html:13
@@ -2296,57 +3324,161 @@ msgstr ""
msgid "search file list"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:32
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+msgid "add new file"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:35
msgid "Loading file list..."
msgstr ""
-#: rhodecode/templates/files/files_browser.html:111
-msgid "search truncated"
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:122
-msgid "no matching files"
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:161
-msgid "Last modified"
+#: rhodecode/templates/files/files_browser.html:50
+msgid "Last Revision"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:162
-msgid "Last commiter"
+#: rhodecode/templates/files/files_browser.html:51
+msgid "Last modified"
msgstr ""
-#: rhodecode/templates/files/files_edit.html:4
-msgid "Edit file"
+#: rhodecode/templates/files/files_browser.html:52
+msgid "Last commiter"
msgstr ""
#: rhodecode/templates/files/files_edit.html:19
msgid "edit file"
msgstr ""
-#: rhodecode/templates/files/files_edit.html:45
-#: rhodecode/templates/shortlog/shortlog_data.html:5
-msgid "commit message"
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr ""
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
msgstr ""
#: rhodecode/templates/files/files_edit.html:51
-msgid "Commit changes"
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
msgstr ""
-#: rhodecode/templates/files/files_source.html:12
-msgid "show annotation"
+#: rhodecode/templates/files/files_edit.html:54
+msgid "source"
+msgstr ""
+
+#: rhodecode/templates/files/files_edit.html:59
+msgid "Editing file"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:9
+msgid "diff to revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:10
+#, fuzzy
+msgid "show at revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:14
+#, fuzzy, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
msgstr ""
-#: rhodecode/templates/files/files_source.html:153
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:124
msgid "Selection link"
msgstr ""
+#: rhodecode/templates/files/files_ypjax.html:5
+msgid "annotation"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr ""
+
+#: rhodecode/templates/followers/followers.html:5
+#, python-format
+msgid "%s Followers"
+msgstr ""
+
#: rhodecode/templates/followers/followers.html:13
msgid "followers"
msgstr ""
#: rhodecode/templates/followers/followers_data.html:12
-msgid "Started following"
+msgid "Started following -"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:5
+#, python-format
+msgid "%s Fork"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:77
+msgid "Copy permissions"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr ""
+
+#: rhodecode/templates/forks/forks.html:5
+#, python-format
+msgid "%s Forks"
msgstr ""
#: rhodecode/templates/forks/forks.html:13
@@ -2357,184 +3489,395 @@ msgstr ""
msgid "forked"
msgstr ""
-#: rhodecode/templates/forks/forks_data.html:34
+#: rhodecode/templates/forks/forks_data.html:38
msgid "There are no forks yet"
msgstr ""
-#: rhodecode/templates/journal/journal.html:34
-msgid "Following"
+#: rhodecode/templates/journal/journal.html:13
+msgid "ATOM journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:14
+msgid "RSS journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+msgid "RSS feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
msgstr ""
#: rhodecode/templates/journal/journal.html:41
+msgid "Watched"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:46
+msgid "ADD"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:114
msgid "following user"
msgstr ""
-#: rhodecode/templates/journal/journal.html:41
+#: rhodecode/templates/journal/journal.html:114
msgid "user"
msgstr ""
-#: rhodecode/templates/journal/journal.html:65
+#: rhodecode/templates/journal/journal.html:147
msgid "You are not following any users or repositories"
msgstr ""
-#: rhodecode/templates/journal/journal_data.html:46
+#: rhodecode/templates/journal/journal_data.html:47
msgid "No entries yet"
msgstr ""
-#: rhodecode/templates/journal/public_journal.html:17
+#: rhodecode/templates/journal/public_journal.html:13
+msgid "ATOM public journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/public_journal.html:14
+msgid "RSS public journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/public_journal.html:21
msgid "Public Journal"
msgstr ""
-#: rhodecode/templates/search/search.html:7
-#: rhodecode/templates/search/search.html:26
-msgid "in repository: "
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+msgid "Detailed compare view"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+msgid "owner"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+msgid "Add reviewer to this pull request."
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+msgid "Create new pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+msgid "Title"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+msgid "description"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+msgid "Status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+msgid "Still not reviewed by"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+msgid "Created on"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+msgid "Compare view"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+msgid "Incoming changesets"
msgstr ""
-#: rhodecode/templates/search/search.html:9
-#: rhodecode/templates/search/search.html:28
-msgid "in all repositories"
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+msgid "all pull requests"
msgstr ""
-#: rhodecode/templates/search/search.html:42
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:8
+#, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, python-format
+msgid "Search in repository: %s"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+msgid "Search in all repositories"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:48
msgid "Search term"
msgstr ""
-#: rhodecode/templates/search/search.html:54
+#: rhodecode/templates/search/search.html:60
msgid "Search in"
msgstr ""
-#: rhodecode/templates/search/search.html:57
+#: rhodecode/templates/search/search.html:63
msgid "File contents"
msgstr ""
-#: rhodecode/templates/search/search.html:59
+#: rhodecode/templates/search/search.html:64
+msgid "Commit messages"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:65
msgid "File names"
msgstr ""
-#: rhodecode/templates/search/search_content.html:20
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
#: rhodecode/templates/search/search_path.html:15
msgid "Permission denied"
msgstr ""
-#: rhodecode/templates/settings/repo_fork.html:5
-msgid "Fork"
-msgstr ""
-
-#: rhodecode/templates/settings/repo_fork.html:31
-msgid "Fork name"
-msgstr ""
-
-#: rhodecode/templates/settings/repo_fork.html:55
-msgid "fork this repository"
+#: rhodecode/templates/settings/repo_settings.html:5
+#, python-format
+msgid "%s Settings"
msgstr ""
#: rhodecode/templates/shortlog/shortlog.html:5
-#: rhodecode/templates/summary/summary.html:666
-msgid "Shortlog"
+#, python-format
+msgid "%s Shortlog"
msgstr ""
#: rhodecode/templates/shortlog/shortlog.html:14
msgid "shortlog"
msgstr ""
-#: rhodecode/templates/shortlog/shortlog_data.html:6
+#: rhodecode/templates/shortlog/shortlog_data.html:7
msgid "age"
msgstr ""
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+msgid "No commit message"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+msgid "Existing repository?"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:4
+#, python-format
+msgid "%s Summary"
+msgstr ""
+
#: rhodecode/templates/summary/summary.html:12
msgid "summary"
msgstr ""
-#: rhodecode/templates/summary/summary.html:79
+#: rhodecode/templates/summary/summary.html:20
+#, python-format
+msgid "repo %s ATOM feed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:21
+#, python-format
+msgid "repo %s RSS feed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+msgid "ATOM"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:82
+#, python-format
+msgid "Non changable ID %s"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:95
msgid "remote clone"
msgstr ""
-#: rhodecode/templates/summary/summary.html:121
-msgid "by"
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
msgstr ""
-#: rhodecode/templates/summary/summary.html:128
+#: rhodecode/templates/summary/summary.html:130
msgid "Clone url"
msgstr ""
-#: rhodecode/templates/summary/summary.html:137
-msgid "Trending source files"
+#: rhodecode/templates/summary/summary.html:133
+msgid "Show by Name"
msgstr ""
-#: rhodecode/templates/summary/summary.html:146
-msgid "Download"
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
msgstr ""
-#: rhodecode/templates/summary/summary.html:150
-msgid "There are no downloads yet"
+#: rhodecode/templates/summary/summary.html:142
+msgid "Trending files"
msgstr ""
-#: rhodecode/templates/summary/summary.html:152
-msgid "Downloads are disabled for this repository"
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
msgstr ""
-#: rhodecode/templates/summary/summary.html:154
-#: rhodecode/templates/summary/summary.html:320
-msgid "enable"
+#: rhodecode/templates/summary/summary.html:158
+msgid "Download"
msgstr ""
#: rhodecode/templates/summary/summary.html:162
-#: rhodecode/templates/summary/summary.html:297
-#, python-format
-msgid "Download %s as %s"
+msgid "There are no downloads yet"
msgstr ""
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:164
+msgid "Downloads are disabled for this repository"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:170
+msgid "Download as zip"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:173
msgid "Check this to download archive with subrepos"
msgstr ""
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:173
msgid "with subrepos"
msgstr ""
-#: rhodecode/templates/summary/summary.html:176
-msgid "Feeds"
+#: rhodecode/templates/summary/summary.html:186
+msgid "Commit activity by day / author"
msgstr ""
-#: rhodecode/templates/summary/summary.html:257
-#: rhodecode/templates/summary/summary.html:684
-#: rhodecode/templates/summary/summary.html:695
-msgid "show more"
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
msgstr ""
-#: rhodecode/templates/summary/summary.html:312
-msgid "Commit activity by day / author"
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
msgstr ""
-#: rhodecode/templates/summary/summary.html:324
-msgid "Loaded in"
+#: rhodecode/templates/summary/summary.html:220
+msgid "Quick start"
msgstr ""
-#: rhodecode/templates/summary/summary.html:603
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:650
msgid "commits"
msgstr ""
-#: rhodecode/templates/summary/summary.html:604
+#: rhodecode/templates/summary/summary.html:651
msgid "files added"
msgstr ""
-#: rhodecode/templates/summary/summary.html:605
+#: rhodecode/templates/summary/summary.html:652
msgid "files changed"
msgstr ""
-#: rhodecode/templates/summary/summary.html:606
+#: rhodecode/templates/summary/summary.html:653
msgid "files removed"
msgstr ""
-#: rhodecode/templates/summary/summary.html:610
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:657
msgid "file added"
msgstr ""
-#: rhodecode/templates/summary/summary.html:611
+#: rhodecode/templates/summary/summary.html:658
msgid "file changed"
msgstr ""
-#: rhodecode/templates/summary/summary.html:612
+#: rhodecode/templates/summary/summary.html:659
msgid "file removed"
msgstr ""
+#: rhodecode/templates/tags/tags.html:5
+#, python-format
+msgid "%s Tags"
+msgstr ""
+
diff --git a/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.mo b/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.mo
new file mode 100644
index 00000000..c4d8e0f2
--- /dev/null
+++ b/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.mo
Binary files differ
diff --git a/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po b/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po
new file mode 100644
index 00000000..74c69ec4
--- /dev/null
+++ b/rhodecode/i18n/fr/LC_MESSAGES/rhodecode.po
@@ -0,0 +1,4047 @@
+# French translations for RhodeCode.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the RhodeCode project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: RhodeCode 1.1.5\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\n"
+"PO-Revision-Date: 2012-06-05 20:07+0100\n"
+"Last-Translator: Vincent Duvert <vincent@duvert.net>\n"
+"Language-Team: fr <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: rhodecode/controllers/changelog.py:94
+msgid "All Branches"
+msgstr "Toutes les branches"
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr "afficher les espaces et tabulations"
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr "ignorer les espaces et tabulations"
+
+#: rhodecode/controllers/changeset.py:157
+#, python-format
+msgid "%s line context"
+msgstr "afficher %s lignes de contexte"
+
+#: rhodecode/controllers/changeset.py:333
+#: rhodecode/controllers/changeset.py:348 rhodecode/lib/diffs.py:70
+msgid "binary file"
+msgstr "fichier binaire"
+
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is "
+"not allowed"
+msgstr ""
+
+#: rhodecode/controllers/compare.py:69
+#, fuzzy
+msgid "There are no changesets yet"
+msgstr "Il n’y a aucun changement pour le moment"
+
+#: rhodecode/controllers/error.py:69
+msgid "Home page"
+msgstr "Accueil"
+
+#: rhodecode/controllers/error.py:98
+msgid "The request could not be understood by the server due to malformed syntax."
+msgstr ""
+"Le serveur n’a pas pu interpréter la requête à cause d’une erreur de "
+"syntaxe"
+
+#: rhodecode/controllers/error.py:101
+msgid "Unauthorized access to resource"
+msgstr "Accès interdit à cet ressource"
+
+#: rhodecode/controllers/error.py:103
+msgid "You don't have permission to view this page"
+msgstr "Vous n’avez pas la permission de voir cette page"
+
+#: rhodecode/controllers/error.py:105
+msgid "The resource could not be found"
+msgstr "Ressource introuvable"
+
+#: rhodecode/controllers/error.py:107
+msgid ""
+"The server encountered an unexpected condition which prevented it from "
+"fulfilling the request."
+msgstr ""
+"La requête n’a pu être traitée en raison d’une erreur survenue sur le "
+"serveur."
+
+#: rhodecode/controllers/feed.py:49
+#, python-format
+msgid "Changes on %s repository"
+msgstr "Changements sur le dépôt %s"
+
+#: rhodecode/controllers/feed.py:50
+#, python-format
+msgid "%s %s feed"
+msgstr "Flux %s de %s"
+
+#: rhodecode/controllers/feed.py:75
+msgid "commited on"
+msgstr "a commité, le"
+
+#: rhodecode/controllers/files.py:84
+#, fuzzy
+msgid "click here to add new file"
+msgstr "Ajouter un fichier"
+
+#: rhodecode/controllers/files.py:85
+#, python-format
+msgid "There are no files yet %s"
+msgstr "Il n’y a pas encore de fichiers %s"
+
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:266
+#, python-format
+msgid "Edited %s via RhodeCode"
+msgstr "%s édité via RhodeCode"
+
+#: rhodecode/controllers/files.py:271
+msgid "No changes"
+msgstr "Aucun changement"
+
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
+#, python-format
+msgid "Successfully committed to %s"
+msgstr "Commit réalisé avec succès sur %s"
+
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
+msgid "Error occurred during commit"
+msgstr "Une erreur est survenue durant le commit"
+
+#: rhodecode/controllers/files.py:318
+#, python-format
+msgid "Added %s via RhodeCode"
+msgstr "%s ajouté par RhodeCode"
+
+#: rhodecode/controllers/files.py:332
+msgid "No content"
+msgstr "Aucun contenu"
+
+#: rhodecode/controllers/files.py:336
+msgid "No filename"
+msgstr "Aucun nom de fichier"
+
+#: rhodecode/controllers/files.py:378
+msgid "downloads disabled"
+msgstr "Les téléchargements sont désactivés"
+
+#: rhodecode/controllers/files.py:389
+#, python-format
+msgid "Unknown revision %s"
+msgstr "Révision %s inconnue."
+
+#: rhodecode/controllers/files.py:391
+msgid "Empty repository"
+msgstr "Dépôt vide."
+
+#: rhodecode/controllers/files.py:393
+msgid "Unknown archive type"
+msgstr "Type d’archive inconnu"
+
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
+msgid "Changesets"
+msgstr "Changesets"
+
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
+msgid "Branches"
+msgstr "Branches"
+
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
+msgid "Tags"
+msgstr "Tags"
+
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"Le dépôt %s n’est pas représenté dans la base de données. Il a "
+"probablement été créé ou renommé manuellement. Veuillez relancer "
+"l’application pour rescanner les dépôts."
+
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the file system please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"Le dépôt %s n’est pas représenté dans la base de données. Il a "
+"probablement été créé ou renommé manuellement. Veuillez relancer "
+"l’application pour rescanner les dépôts."
+
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr "dépôt %s forké en tant que %s"
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr "Une erreur est survenue durant le fork du dépôt %s."
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+#, fuzzy
+msgid "public journal"
+msgstr "Journal public"
+
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr "Journal"
+
+#: rhodecode/controllers/login.py:143
+msgid "You have successfully registered into rhodecode"
+msgstr "Vous vous êtes inscrits avec succès à RhodeCode"
+
+#: rhodecode/controllers/login.py:164
+msgid "Your password reset link was sent"
+msgstr "Un lien de rénitialisation de votre mot de passe vous a été envoyé."
+
+#: rhodecode/controllers/login.py:184
+msgid ""
+"Your password reset was successful, new password has been sent to your "
+"email"
+msgstr ""
+"Votre mot de passe a été réinitialisé. Votre nouveau mot de passe vous a "
+"été envoyé par e-mail."
+
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+#, fuzzy
+msgid "Bookmarks"
+msgstr "Signets"
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+msgid "error during creation of pull request"
+msgstr "erreur lors de la création de la demande traction"
+
+#: rhodecode/controllers/pullrequests.py:181
+#, fuzzy
+msgid "Successfully opened new pull request"
+msgstr "L’utilisateur a été supprimé avec succès."
+
+#: rhodecode/controllers/pullrequests.py:184
+#, fuzzy
+msgid "Error occurred during sending pull request"
+msgstr "Une erreur est survenue durant la création du dépôt %s."
+
+#: rhodecode/controllers/pullrequests.py:217
+#, fuzzy
+msgid "Successfully deleted pull request"
+msgstr "L’utilisateur a été supprimé avec succès."
+
+#: rhodecode/controllers/search.py:131
+msgid "Invalid search query. Try quoting it."
+msgstr "Requête invalide. Essayer de la mettre entre guillemets."
+
+#: rhodecode/controllers/search.py:136
+msgid "There is no index to search in. Please run whoosh indexer"
+msgstr ""
+"L’index de recherche n’est pas présent. Veuillez exécuter l’indexeur de "
+"code Whoosh."
+
+#: rhodecode/controllers/search.py:140
+msgid "An error occurred during this search operation"
+msgstr "Une erreur est survenue durant l’opération de recherche."
+
+#: rhodecode/controllers/settings.py:107
+#: rhodecode/controllers/admin/repos.py:266
+#, python-format
+msgid "Repository %s updated successfully"
+msgstr "Dépôt %s mis à jour avec succès."
+
+#: rhodecode/controllers/settings.py:125
+#: rhodecode/controllers/admin/repos.py:284
+#, python-format
+msgid "error occurred during update of repository %s"
+msgstr "Une erreur est survenue lors de la mise à jour du dépôt %s."
+
+#: rhodecode/controllers/settings.py:143
+#: rhodecode/controllers/admin/repos.py:302
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was moved or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"Le dépôt %s n’est pas représenté dans la base de données. Il a "
+"probablement été déplacé ou renommé manuellement. Veuillez relancer "
+"l’application pour rescanner les dépôts."
+
+#: rhodecode/controllers/settings.py:155
+#: rhodecode/controllers/admin/repos.py:314
+#, python-format
+msgid "deleted repository %s"
+msgstr "Dépôt %s supprimé"
+
+#: rhodecode/controllers/settings.py:159
+#: rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
+#, python-format
+msgid "An error occurred during deletion of %s"
+msgstr "Erreur pendant la suppression de %s"
+
+#: rhodecode/controllers/summary.py:138
+msgid "No data loaded yet"
+msgstr "Aucune donnée actuellement disponible."
+
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
+msgid "Statistics are disabled for this repository"
+msgstr "La mise à jour des statistiques est désactivée pour ce dépôt."
+
+#: rhodecode/controllers/admin/ldap_settings.py:50
+msgid "BASE"
+msgstr "Base"
+
+#: rhodecode/controllers/admin/ldap_settings.py:51
+msgid "ONELEVEL"
+msgstr "Un niveau"
+
+#: rhodecode/controllers/admin/ldap_settings.py:52
+msgid "SUBTREE"
+msgstr "Sous-arbre"
+
+#: rhodecode/controllers/admin/ldap_settings.py:56
+msgid "NEVER"
+msgstr "NEVER"
+
+#: rhodecode/controllers/admin/ldap_settings.py:57
+msgid "ALLOW"
+msgstr "Autoriser"
+
+#: rhodecode/controllers/admin/ldap_settings.py:58
+msgid "TRY"
+msgstr "TRY"
+
+#: rhodecode/controllers/admin/ldap_settings.py:59
+msgid "DEMAND"
+msgstr "DEMAND"
+
+#: rhodecode/controllers/admin/ldap_settings.py:60
+msgid "HARD"
+msgstr "HARD"
+
+#: rhodecode/controllers/admin/ldap_settings.py:64
+msgid "No encryption"
+msgstr "Pas de chiffrement"
+
+#: rhodecode/controllers/admin/ldap_settings.py:65
+msgid "LDAPS connection"
+msgstr "Connection LDAPS"
+
+#: rhodecode/controllers/admin/ldap_settings.py:66
+msgid "START_TLS on LDAP connection"
+msgstr "START_TLS à la connexion"
+
+#: rhodecode/controllers/admin/ldap_settings.py:126
+msgid "Ldap settings updated successfully"
+msgstr "Mise à jour réussie des réglages LDAP"
+
+#: rhodecode/controllers/admin/ldap_settings.py:130
+msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
+msgstr "Impossible d’activer LDAP. La bibliothèque « python-ldap » est manquante."
+
+#: rhodecode/controllers/admin/ldap_settings.py:147
+msgid "error occurred during update of ldap settings"
+msgstr "Une erreur est survenue durant la mise à jour des réglages du LDAP."
+
+#: rhodecode/controllers/admin/permissions.py:59
+msgid "None"
+msgstr "Aucun"
+
+#: rhodecode/controllers/admin/permissions.py:60
+msgid "Read"
+msgstr "Lire"
+
+#: rhodecode/controllers/admin/permissions.py:61
+msgid "Write"
+msgstr "Écrire"
+
+#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/templates/admin/ldap/ldap.html:9
+#: rhodecode/templates/admin/permissions/permissions.html:9
+#: rhodecode/templates/admin/repos/repo_add.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:9
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
+#: rhodecode/templates/admin/settings/hooks.html:9
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/admin/users/user_add.html:8
+#: rhodecode/templates/admin/users/user_edit.html:9
+#: rhodecode/templates/admin/users/user_edit.html:122
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/admin/users_groups/users_group_add.html:8
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+#: rhodecode/templates/base/base.html:197
+#: rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339
+#: rhodecode/templates/base/base.html:341
+msgid "Admin"
+msgstr "Administration"
+
+#: rhodecode/controllers/admin/permissions.py:65
+msgid "disabled"
+msgstr "Désactivé"
+
+#: rhodecode/controllers/admin/permissions.py:67
+msgid "allowed with manual account activation"
+msgstr "Autorisé avec activation manuelle du compte"
+
+#: rhodecode/controllers/admin/permissions.py:69
+msgid "allowed with automatic account activation"
+msgstr "Autorisé avec activation automatique du compte"
+
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
+msgid "Disabled"
+msgstr "Interdite"
+
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
+msgid "Enabled"
+msgstr "Autorisée"
+
+#: rhodecode/controllers/admin/permissions.py:116
+msgid "Default permissions updated successfully"
+msgstr "Permissions par défaut mises à jour avec succès"
+
+#: rhodecode/controllers/admin/permissions.py:130
+msgid "error occurred during update of permissions"
+msgstr "erreur pendant la mise à jour des permissions"
+
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
+msgstr "[Pas un fork]"
+
+#: rhodecode/controllers/admin/repos.py:192
+#, python-format
+msgid "created repository %s from %s"
+msgstr "Le dépôt %s a été créé depuis %s."
+
+#: rhodecode/controllers/admin/repos.py:196
+#, python-format
+msgid "created repository %s"
+msgstr "Le dépôt %s a été créé."
+
+#: rhodecode/controllers/admin/repos.py:227
+#, python-format
+msgid "error occurred during creation of repository %s"
+msgstr "Une erreur est survenue durant la création du dépôt %s."
+
+#: rhodecode/controllers/admin/repos.py:319
+#, python-format
+msgid "Cannot delete %s it still contains attached forks"
+msgstr "Impossible de supprimer le dépôt %s : Des forks y sont attachés."
+
+#: rhodecode/controllers/admin/repos.py:348
+msgid "An error occurred during deletion of repository user"
+msgstr "Une erreur est survenue durant la suppression de l’utilisateur du dépôt."
+
+#: rhodecode/controllers/admin/repos.py:367
+msgid "An error occurred during deletion of repository users groups"
+msgstr ""
+"Une erreur est survenue durant la suppression du groupe d’utilisateurs de"
+" ce dépôt."
+
+#: rhodecode/controllers/admin/repos.py:385
+msgid "An error occurred during deletion of repository stats"
+msgstr "Une erreur est survenue durant la suppression des statistiques du dépôt."
+
+#: rhodecode/controllers/admin/repos.py:402
+msgid "An error occurred during cache invalidation"
+msgstr "Une erreur est survenue durant l’invalidation du cache."
+
+#: rhodecode/controllers/admin/repos.py:422
+#, fuzzy
+msgid "An error occurred during unlocking"
+msgstr "Une erreur est survenue durant cette opération."
+
+#: rhodecode/controllers/admin/repos.py:442
+msgid "Updated repository visibility in public journal"
+msgstr "La visibilité du dépôt dans le journal public a été mise à jour."
+
+#: rhodecode/controllers/admin/repos.py:446
+msgid "An error occurred during setting this repository in public journal"
+msgstr ""
+"Une erreur est survenue durant la configuration du journal public pour ce"
+" dépôt."
+
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
+msgid "Token mismatch"
+msgstr "Jeton d’authentification incorrect."
+
+#: rhodecode/controllers/admin/repos.py:464
+msgid "Pulled from remote location"
+msgstr "Les changements distants ont été récupérés."
+
+#: rhodecode/controllers/admin/repos.py:466
+msgid "An error occurred during pull from remote location"
+msgstr "Une erreur est survenue durant le pull depuis la source distante."
+
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr "[Aucun dépôt]"
+
+#: rhodecode/controllers/admin/repos.py:484
+#, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr "Le dépôt %s a été marké comme fork de %s"
+
+#: rhodecode/controllers/admin/repos.py:488
+msgid "An error occurred during this operation"
+msgstr "Une erreur est survenue durant cette opération."
+
+#: rhodecode/controllers/admin/repos_groups.py:116
+#, python-format
+msgid "created repos group %s"
+msgstr "Le groupe de dépôts %s a été créé."
+
+#: rhodecode/controllers/admin/repos_groups.py:129
+#, python-format
+msgid "error occurred during creation of repos group %s"
+msgstr "Une erreur est survenue durant la création du groupe de dépôts %s."
+
+#: rhodecode/controllers/admin/repos_groups.py:163
+#, python-format
+msgid "updated repos group %s"
+msgstr "Le groupe de dépôts %s a été mis à jour."
+
+#: rhodecode/controllers/admin/repos_groups.py:176
+#, python-format
+msgid "error occurred during update of repos group %s"
+msgstr "Une erreur est survenue durant la mise à jour du groupe de dépôts %s."
+
+#: rhodecode/controllers/admin/repos_groups.py:194
+#, python-format
+msgid "This group contains %s repositores and cannot be deleted"
+msgstr "Ce groupe contient %s dépôts et ne peut être supprimé."
+
+#: rhodecode/controllers/admin/repos_groups.py:202
+#, python-format
+msgid "removed repos group %s"
+msgstr "Le groupe de dépôts %s a été supprimé."
+
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr "Impossible de supprimer ce groupe : Il contient des sous-groupes."
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
+#, python-format
+msgid "error occurred during deletion of repos group %s"
+msgstr "Une erreur est survenue durant la suppression du groupe de dépôts %s."
+
+#: rhodecode/controllers/admin/repos_groups.py:238
+msgid "An error occurred during deletion of group user"
+msgstr ""
+"Une erreur est survenue durant la suppression de l’utilisateur du groupe "
+"de dépôts."
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr ""
+"Une erreur est survenue durant la suppression du groupe d’utilisateurs du"
+" groupe de dépôts."
+
+#: rhodecode/controllers/admin/settings.py:121
+#, python-format
+msgid "Repositories successfully rescanned added: %s,removed: %s"
+msgstr "Après re-scan : %s ajouté(s), %s enlevé(s)"
+
+#: rhodecode/controllers/admin/settings.py:129
+msgid "Whoosh reindex task scheduled"
+msgstr "La tâche de réindexation Whoosh a été planifiée."
+
+#: rhodecode/controllers/admin/settings.py:160
+msgid "Updated application settings"
+msgstr "Réglages mis à jour"
+
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
+msgid "error occurred during updating application settings"
+msgstr "Une erreur est survenue durant la mise à jour des options."
+
+#: rhodecode/controllers/admin/settings.py:200
+#, fuzzy
+msgid "Updated visualisation settings"
+msgstr "Réglages mis à jour"
+
+#: rhodecode/controllers/admin/settings.py:205
+#, fuzzy
+msgid "error occurred during updating visualisation settings"
+msgstr "Une erreur est survenue durant la mise à jour des options."
+
+#: rhodecode/controllers/admin/settings.py:271
+#, fuzzy
+msgid "Updated VCS settings"
+msgstr "Réglages de Mercurial mis à jour"
+
+#: rhodecode/controllers/admin/settings.py:285
+msgid "Added new hook"
+msgstr "Le nouveau hook a été ajouté."
+
+#: rhodecode/controllers/admin/settings.py:297
+msgid "Updated hooks"
+msgstr "Hooks mis à jour"
+
+#: rhodecode/controllers/admin/settings.py:301
+msgid "error occurred during hook creation"
+msgstr "Une erreur est survenue durant la création du hook."
+
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr "La tâche d’e-mail a été créée."
+
+#: rhodecode/controllers/admin/settings.py:375
+msgid "You can't edit this user since it's crucial for entire application"
+msgstr ""
+"Vous ne pouvez pas éditer cet utilisateur ; il est nécessaire pour le bon"
+" fonctionnement de l’application."
+
+#: rhodecode/controllers/admin/settings.py:406
+msgid "Your account was updated successfully"
+msgstr "Votre compte a été mis à jour avec succès"
+
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
+#, python-format
+msgid "error occurred during update of user %s"
+msgstr "Une erreur est survenue durant la mise à jour de l’utilisateur %s."
+
+#: rhodecode/controllers/admin/users.py:130
+#, python-format
+msgid "created user %s"
+msgstr "utilisateur %s créé"
+
+#: rhodecode/controllers/admin/users.py:142
+#, python-format
+msgid "error occurred during creation of user %s"
+msgstr "Une erreur est survenue durant la création de l’utilisateur %s."
+
+#: rhodecode/controllers/admin/users.py:171
+msgid "User updated successfully"
+msgstr "L’utilisateur a été mis à jour avec succès."
+
+#: rhodecode/controllers/admin/users.py:207
+msgid "successfully deleted user"
+msgstr "L’utilisateur a été supprimé avec succès."
+
+#: rhodecode/controllers/admin/users.py:212
+msgid "An error occurred during deletion of user"
+msgstr "Une erreur est survenue durant la suppression de l’utilisateur."
+
+#: rhodecode/controllers/admin/users.py:226
+msgid "You can't edit this user"
+msgstr "Vous ne pouvez pas éditer cet utilisateur"
+
+#: rhodecode/controllers/admin/users.py:266
+msgid "Granted 'repository create' permission to user"
+msgstr "La permission de création de dépôts a été accordée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users.py:271
+msgid "Revoked 'repository create' permission to user"
+msgstr "La permission de création de dépôts a été révoquée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users.py:277
+#, fuzzy
+msgid "Granted 'repository fork' permission to user"
+msgstr "La permission de création de dépôts a été accordée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users.py:282
+#, fuzzy
+msgid "Revoked 'repository fork' permission to user"
+msgstr "La permission de création de dépôts a été révoquée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+#, fuzzy
+msgid "An error occurred during permissions saving"
+msgstr "Une erreur est survenue durant cette opération."
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:309
+#, fuzzy
+msgid "An error occurred during email saving"
+msgstr "Une erreur est survenue durant cette opération."
+
+#: rhodecode/controllers/admin/users.py:319
+#, fuzzy
+msgid "Removed email from user"
+msgstr "Le groupe de dépôts %s a été supprimé."
+
+#: rhodecode/controllers/admin/users_groups.py:84
+#, python-format
+msgid "created users group %s"
+msgstr "Le groupe d’utilisateurs %s a été créé."
+
+#: rhodecode/controllers/admin/users_groups.py:95
+#, python-format
+msgid "error occurred during creation of users group %s"
+msgstr "Une erreur est survenue durant la création du groupe d’utilisateurs %s."
+
+#: rhodecode/controllers/admin/users_groups.py:135
+#, python-format
+msgid "updated users group %s"
+msgstr "Le groupe d’utilisateurs %s a été mis à jour."
+
+#: rhodecode/controllers/admin/users_groups.py:157
+#, python-format
+msgid "error occurred during update of users group %s"
+msgstr "Une erreur est survenue durant la mise à jour du groupe d’utilisateurs %s."
+
+#: rhodecode/controllers/admin/users_groups.py:174
+msgid "successfully deleted users group"
+msgstr "Le groupe d’utilisateurs a été supprimé avec succès."
+
+#: rhodecode/controllers/admin/users_groups.py:179
+msgid "An error occurred during deletion of users group"
+msgstr "Une erreur est survenue lors de la suppression du groupe d’utilisateurs."
+
+#: rhodecode/controllers/admin/users_groups.py:233
+#, fuzzy
+msgid "Granted 'repository create' permission to users group"
+msgstr "La permission de création de dépôts a été accordée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users_groups.py:238
+#, fuzzy
+msgid "Revoked 'repository create' permission to users group"
+msgstr "La permission de création de dépôts a été révoquée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users_groups.py:244
+#, fuzzy
+msgid "Granted 'repository fork' permission to users group"
+msgstr "La permission de création de dépôts a été accordée à l’utilisateur."
+
+#: rhodecode/controllers/admin/users_groups.py:249
+#, fuzzy
+msgid "Revoked 'repository fork' permission to users group"
+msgstr "La permission de création de dépôts a été révoquée à l’utilisateur."
+
+#: rhodecode/lib/auth.py:499
+msgid "You need to be a registered user to perform this action"
+msgstr "Vous devez être un utilisateur enregistré pour effectuer cette action."
+
+#: rhodecode/lib/auth.py:540
+msgid "You need to be a signed in to view this page"
+msgstr "Vous devez être connecté pour visualiser cette page."
+
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
+msgstr ""
+"Cet ensemble de changements était trop gros pour être affiché et a été "
+"découpé, utilisez le menu « Diff » pour afficher les différences."
+
+#: rhodecode/lib/diffs.py:96
+msgid "No changes detected"
+msgstr "Aucun changement détecté."
+
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
+msgstr "%d/%m/%Y à %H:%M:%S"
+
+#: rhodecode/lib/helpers.py:484
+msgid "True"
+msgstr "Vrai"
+
+#: rhodecode/lib/helpers.py:488
+msgid "False"
+msgstr "Faux"
+
+#: rhodecode/lib/helpers.py:532
+msgid "Changeset not found"
+msgstr "Ensemble de changements non trouvé"
+
+#: rhodecode/lib/helpers.py:555
+#, python-format
+msgid "Show all combined changesets %s->%s"
+msgstr "Afficher les changements combinés %s->%s"
+
+#: rhodecode/lib/helpers.py:561
+msgid "compare view"
+msgstr "vue de comparaison"
+
+#: rhodecode/lib/helpers.py:581
+msgid "and"
+msgstr "et"
+
+#: rhodecode/lib/helpers.py:582
+#, python-format
+msgid "%s more"
+msgstr "%s de plus"
+
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
+msgid "revisions"
+msgstr "révisions"
+
+#: rhodecode/lib/helpers.py:606
+msgid "fork name "
+msgstr "Nom du fork"
+
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:626
+msgid "[deleted] repository"
+msgstr "[a supprimé] le dépôt"
+
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
+msgid "[created] repository"
+msgstr "[a créé] le dépôt"
+
+#: rhodecode/lib/helpers.py:630
+msgid "[created] repository as fork"
+msgstr "[a créé] le dépôt en tant que fork"
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
+msgid "[forked] repository"
+msgstr "[a forké] le dépôt"
+
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
+msgid "[updated] repository"
+msgstr "[a mis à jour] le dépôt"
+
+#: rhodecode/lib/helpers.py:636
+msgid "[delete] repository"
+msgstr "[a supprimé] le dépôt"
+
+#: rhodecode/lib/helpers.py:644
+msgid "[created] user"
+msgstr "[a créé] l’utilisateur"
+
+#: rhodecode/lib/helpers.py:646
+msgid "[updated] user"
+msgstr "[a mis à jour] l’utilisateur"
+
+#: rhodecode/lib/helpers.py:648
+msgid "[created] users group"
+msgstr "[a créé] le groupe d’utilisateurs"
+
+#: rhodecode/lib/helpers.py:650
+msgid "[updated] users group"
+msgstr "[a mis à jour] le groupe d’utilisateurs"
+
+#: rhodecode/lib/helpers.py:652
+msgid "[commented] on revision in repository"
+msgstr "[a commenté] une révision du dépôt"
+
+#: rhodecode/lib/helpers.py:654
+#, fuzzy
+msgid "[commented] on pull request for"
+msgstr "[a commenté] une révision du dépôt"
+
+#: rhodecode/lib/helpers.py:656
+#, fuzzy
+msgid "[closed] pull request for"
+msgstr "[a commenté] une révision du dépôt"
+
+#: rhodecode/lib/helpers.py:658
+msgid "[pushed] into"
+msgstr "[a pushé] dans"
+
+#: rhodecode/lib/helpers.py:660
+msgid "[committed via RhodeCode] into repository"
+msgstr "[a commité via RhodeCode] dans le dépôt"
+
+#: rhodecode/lib/helpers.py:662
+msgid "[pulled from remote] into repository"
+msgstr "[a pullé depuis un site distant] dans le dépôt"
+
+#: rhodecode/lib/helpers.py:664
+msgid "[pulled] from"
+msgstr "[a pullé] depuis"
+
+#: rhodecode/lib/helpers.py:666
+msgid "[started following] repository"
+msgstr "[suit maintenant] le dépôt"
+
+#: rhodecode/lib/helpers.py:668
+msgid "[stopped following] repository"
+msgstr "[ne suit plus] le dépôt"
+
+#: rhodecode/lib/helpers.py:840
+#, python-format
+msgid " and %s more"
+msgstr "et %s de plus"
+
+#: rhodecode/lib/helpers.py:844
+msgid "No Files"
+msgstr "Aucun fichier"
+
+#: rhodecode/lib/utils2.py:335
+#, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] "%d an"
+msgstr[1] "%d ans"
+
+#: rhodecode/lib/utils2.py:336
+#, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] "%d mois"
+msgstr[1] "%d mois"
+
+#: rhodecode/lib/utils2.py:337
+#, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] "%d jour"
+msgstr[1] "%d jours"
+
+#: rhodecode/lib/utils2.py:338
+#, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] "%d heure"
+msgstr[1] "%d heures"
+
+#: rhodecode/lib/utils2.py:339
+#, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] "%d minute"
+msgstr[1] "%d minutes"
+
+#: rhodecode/lib/utils2.py:340
+#, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] "%d seconde"
+msgstr[1] "%d secondes"
+
+#: rhodecode/lib/utils2.py:355
+#, python-format
+msgid "%s ago"
+msgstr "Il y a %s"
+
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
+msgstr "Il y a %s et %s"
+
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
+msgstr "à l’instant"
+
+#: rhodecode/lib/celerylib/tasks.py:269
+msgid "password reset link"
+msgstr "Réinitialisation du mot de passe"
+
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
+msgstr "à la ligne %s"
+
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
+msgstr "[Mention]"
+
+#: rhodecode/model/db.py:1140
+#, fuzzy
+msgid "Repository no access"
+msgstr "Dépôts"
+
+#: rhodecode/model/db.py:1141
+#, fuzzy
+msgid "Repository read access"
+msgstr "Ce dépôt existe déjà"
+
+#: rhodecode/model/db.py:1142
+#, fuzzy
+msgid "Repository write access"
+msgstr "Dépôts"
+
+#: rhodecode/model/db.py:1143
+#, fuzzy
+msgid "Repository admin access"
+msgstr "Dépôts"
+
+#: rhodecode/model/db.py:1145
+#, fuzzy
+msgid "Repositories Group no access"
+msgstr "Groupes de dépôts"
+
+#: rhodecode/model/db.py:1146
+#, fuzzy
+msgid "Repositories Group read access"
+msgstr "Groupes de dépôts"
+
+#: rhodecode/model/db.py:1147
+#, fuzzy
+msgid "Repositories Group write access"
+msgstr "Groupes de dépôts"
+
+#: rhodecode/model/db.py:1148
+#, fuzzy
+msgid "Repositories Group admin access"
+msgstr "Groupes de dépôts"
+
+#: rhodecode/model/db.py:1150
+#, fuzzy
+msgid "RhodeCode Administrator"
+msgstr "Administration des utilisateurs"
+
+#: rhodecode/model/db.py:1151
+#, fuzzy
+msgid "Repository creation disabled"
+msgstr "Création de dépôt"
+
+#: rhodecode/model/db.py:1152
+#, fuzzy
+msgid "Repository creation enabled"
+msgstr "Création de dépôt"
+
+#: rhodecode/model/db.py:1153
+#, fuzzy
+msgid "Repository forking disabled"
+msgstr "Création de dépôt"
+
+#: rhodecode/model/db.py:1154
+#, fuzzy
+msgid "Repository forking enabled"
+msgstr "Création de dépôt"
+
+#: rhodecode/model/db.py:1155
+#, fuzzy
+msgid "Register disabled"
+msgstr "Désactivé"
+
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
+msgstr ""
+
+#: rhodecode/model/db.py:1580
+#, fuzzy
+msgid "Approved"
+msgstr "Supprimés"
+
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
+msgstr ""
+
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
+msgstr ""
+
+#: rhodecode/model/forms.py:43
+msgid "Please enter a login"
+msgstr "Veuillez entrer un identifiant"
+
+#: rhodecode/model/forms.py:44
+#, python-format
+msgid "Enter a value %(min)i characters long or more"
+msgstr "Entrez une valeur d’au moins %(min)i caractères de long."
+
+#: rhodecode/model/forms.py:52
+msgid "Please enter a password"
+msgstr "Veuillez entrer un mot de passe"
+
+#: rhodecode/model/forms.py:53
+#, python-format
+msgid "Enter %(min)i characters or more"
+msgstr "Entrez au moins %(min)i caractères"
+
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr "a posté un commentaire sur le commit"
+
+#: rhodecode/model/notification.py:221
+msgid "sent message"
+msgstr "a envoyé un message"
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr "vous a mentioné"
+
+#: rhodecode/model/notification.py:223
+msgid "registered in RhodeCode"
+msgstr "s’est enregistré sur RhodeCode"
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
+msgstr ""
+
+#: rhodecode/model/notification.py:225
+#, fuzzy
+msgid "commented on pull request"
+msgstr "a posté un commentaire sur le commit"
+
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+#, fuzzy
+msgid "latest tip"
+msgstr "Dernière connexion"
+
+#: rhodecode/model/user.py:230
+msgid "new user registration"
+msgstr "Nouveau compte utilisateur enregistré"
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
+msgid "You can't Edit this user since it's crucial for entire application"
+msgstr ""
+"Vous ne pouvez pas éditer cet utilisateur ; il est nécessaire pour le bon"
+" fonctionnement de l’application."
+
+#: rhodecode/model/user.py:323
+msgid "You can't remove this user since it's crucial for entire application"
+msgstr ""
+"Vous ne pouvez pas supprimer cet utilisateur ; il est nécessaire pour le "
+"bon fonctionnement de l’application."
+
+#: rhodecode/model/user.py:329
+#, python-format
+msgid ""
+"user \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories. %s"
+msgstr ""
+"L’utilisateur « %s » possède %s dépôts et ne peut être supprimé. Changez "
+"les propriétaires de ces dépôts. %s"
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, fuzzy, python-format
+msgid "Username \"%(username)s\" already exists"
+msgstr "Ce nom \"%(username)s\" d’utilisateur existe déjà"
+
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr ""
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with alphanumeric character"
+msgstr ""
+"Le nom d’utilisateur peut contenir uniquement des caractères alpha-"
+"numériques ainsi que les caractères suivants : « _ . - ». Il doit "
+"commencer par un caractère alpha-numérique."
+
+#: rhodecode/model/validators.py:114
+#, python-format
+msgid "Username %(username)s is not valid"
+msgstr "%(username)s Nom d'utilisateur n'est pas valide"
+
+#: rhodecode/model/validators.py:133
+#, fuzzy
+msgid "Invalid users group name"
+msgstr "nom d’utilisateur invalide"
+
+#: rhodecode/model/validators.py:134
+#, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
+msgstr "Ce groupe \"%(usersgroup)s\" d’utilisateurs existe déjà."
+
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr ""
+"Le nom de groupe de dépôts peut contenir uniquement des caractères alpha-"
+"numériques ainsi que les caractères suivants : « _ . - ». Il doit "
+"commencer par un caractère alpha-numérique."
+
+#: rhodecode/model/validators.py:174
+msgid "Cannot assign this group as parent"
+msgstr "Impossible d’assigner ce groupe en tant que parent."
+
+#: rhodecode/model/validators.py:175
+#, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr "Ce nom d’utilisateur \"%(group_name)s\" existe déjà"
+
+#: rhodecode/model/validators.py:177
+#, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr "Dépôt avec le nom de \"%(group_name)s\" existe déjà"
+
+#: rhodecode/model/validators.py:235
+#, fuzzy
+msgid "Invalid characters (non-ascii) in password"
+msgstr "Caractères incorrects dans le mot de passe"
+
+#: rhodecode/model/validators.py:250
+msgid "Passwords do not match"
+msgstr "Les mots de passe ne correspondent pas."
+
+#: rhodecode/model/validators.py:267
+msgid "invalid password"
+msgstr "mot de passe invalide"
+
+#: rhodecode/model/validators.py:268
+msgid "invalid user name"
+msgstr "nom d’utilisateur invalide"
+
+#: rhodecode/model/validators.py:269
+msgid "Your account is disabled"
+msgstr "Votre compte est désactivé"
+
+#: rhodecode/model/validators.py:313
+#, python-format
+msgid "Repository name %(repo)s is disallowed"
+msgstr "Ce nom de dépôt %(repo)s est interdit"
+
+#: rhodecode/model/validators.py:315
+#, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr "Un dépôt portant %(repo)s ce nom existe déjà."
+
+#: rhodecode/model/validators.py:316
+#, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr "Ce dépôt \"%(repo)s\" existe déjà dans le groupe « \"%(group)s\" »."
+
+#: rhodecode/model/validators.py:318
+#, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
+msgstr "Un dépôt portant \"%(repo)s\" ce nom existe déjà."
+
+#: rhodecode/model/validators.py:431
+msgid "invalid clone url"
+msgstr "URL de clonage invalide."
+
+#: rhodecode/model/validators.py:432
+#, fuzzy
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
+msgstr ""
+"URL à cloner invalide. Veuillez fournir une URL valide commençant par "
+"http(s)."
+
+#: rhodecode/model/validators.py:457
+#, fuzzy
+msgid "Fork have to be the same type as parent"
+msgstr "Le fork doit être du même type que l’original"
+
+#: rhodecode/model/validators.py:478
+msgid "This username or users group name is not valid"
+msgstr "Ce nom d’utilisateur ou de groupe n’est pas valide."
+
+#: rhodecode/model/validators.py:562
+msgid "This is not a valid path"
+msgstr "Ceci n’est pas un chemin valide"
+
+#: rhodecode/model/validators.py:577
+msgid "This e-mail address is already taken"
+msgstr "Cette adresse e-mail est déjà enregistrée"
+
+#: rhodecode/model/validators.py:597
+#, python-format
+msgid "e-mail \"%(email)s\" does not exist."
+msgstr "Cette adresse e-mail \"%(email)s\" n’existe pas"
+
+#: rhodecode/model/validators.py:634
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
+msgstr ""
+"L’attribut Login du CN doit être spécifié. Cet attribut correspond au nom"
+" d’utilisateur."
+
+#: rhodecode/model/validators.py:653
+#, python-format
+msgid "Revisions %(revs)s are already part of pull request or have set status"
+msgstr ""
+
+#: rhodecode/templates/index.html:3
+msgid "Dashboard"
+msgstr "Tableau de bord"
+
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
+msgid "quick filter..."
+msgstr "Filtre rapide…"
+
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
+msgid "repositories"
+msgstr "Dépôts"
+
+#: rhodecode/templates/index_base.html:13
+#: rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
+msgstr "AJOUTER UN DÉPÔT"
+
+#: rhodecode/templates/index_base.html:29
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
+#: rhodecode/templates/admin/users_groups/users_group_add.html:32
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:33
+msgid "Group name"
+msgstr "Nom de groupe"
+
+#: rhodecode/templates/index_base.html:30
+#: rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142
+#: rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
+msgid "Description"
+msgstr "Description"
+
+#: rhodecode/templates/index_base.html:40
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
+msgid "Repositories group"
+msgstr "Groupe de dépôts"
+
+#: rhodecode/templates/index_base.html:70
+#: rhodecode/templates/index_base.html:166
+#: rhodecode/templates/admin/repos/repo_add_base.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:32
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
+#: rhodecode/templates/settings/repo_settings.html:31
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36
+#: rhodecode/templates/tags/tags_data.html:6
+msgid "Name"
+msgstr "Nom"
+
+#: rhodecode/templates/index_base.html:72
+msgid "Last change"
+msgstr "Dernière modification"
+
+#: rhodecode/templates/index_base.html:73
+#: rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
+msgid "Tip"
+msgstr "Sommet"
+
+#: rhodecode/templates/index_base.html:74
+#: rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
+msgid "Owner"
+msgstr "Propriétaire"
+
+#: rhodecode/templates/index_base.html:75
+#: rhodecode/templates/summary/summary.html:48
+#: rhodecode/templates/summary/summary.html:51
+msgid "RSS"
+msgstr "RSS"
+
+#: rhodecode/templates/index_base.html:76
+msgid "Atom"
+msgstr "Atom"
+
+#: rhodecode/templates/index_base.html:110
+#: rhodecode/templates/index_base.html:112
+#, python-format
+msgid "Subscribe to %s rss feed"
+msgstr "S’abonner au flux RSS de %s"
+
+#: rhodecode/templates/index_base.html:117
+#: rhodecode/templates/index_base.html:119
+#, python-format
+msgid "Subscribe to %s atom feed"
+msgstr "S’abonner au flux ATOM de %s"
+
+#: rhodecode/templates/index_base.html:140
+msgid "Group Name"
+msgstr "Nom du groupe"
+
+#: rhodecode/templates/index_base.html:158
+#: rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
+msgstr "Tri ascendant"
+
+#: rhodecode/templates/index_base.html:159
+#: rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
+msgstr "Tri descendant"
+
+#: rhodecode/templates/index_base.html:169
+msgid "Last Change"
+msgstr "Dernière modification"
+
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
+msgstr "Aucun élément n’a été trouvé."
+
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
+msgstr "Erreur d’intégrité des données."
+
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+msgid "Loading..."
+msgstr "Chargement…"
+
+#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
+msgid "Sign In"
+msgstr "Connexion"
+
+#: rhodecode/templates/login.html:21
+msgid "Sign In to"
+msgstr "Connexion à"
+
+#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
+#: rhodecode/templates/admin/admin_log.html:5
+#: rhodecode/templates/admin/users/user_add.html:32
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
+msgid "Username"
+msgstr "Nom d’utilisateur"
+
+#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
+#: rhodecode/templates/admin/ldap/ldap.html:46
+#: rhodecode/templates/admin/users/user_add.html:41
+#: rhodecode/templates/base/base.html:92
+msgid "Password"
+msgstr "Mot de passe"
+
+#: rhodecode/templates/login.html:50
+msgid "Remember me"
+msgstr "Se souvenir de moi"
+
+#: rhodecode/templates/login.html:60
+msgid "Forgot your password ?"
+msgstr "Mot de passe oublié ?"
+
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
+msgid "Don't have an account ?"
+msgstr "Vous n’avez pas de compte ?"
+
+#: rhodecode/templates/password_reset.html:5
+msgid "Reset your password"
+msgstr "Mot de passe oublié ?"
+
+#: rhodecode/templates/password_reset.html:11
+msgid "Reset your password to"
+msgstr "Réinitialiser votre mot de passe"
+
+#: rhodecode/templates/password_reset.html:21
+msgid "Email address"
+msgstr "Adresse e-mail"
+
+#: rhodecode/templates/password_reset.html:30
+msgid "Reset my password"
+msgstr "Réinitialiser mon mot de passe"
+
+#: rhodecode/templates/password_reset.html:31
+msgid "Password reset link will be send to matching email address"
+msgstr "Votre nouveau mot de passe sera envoyé à l’adresse correspondante."
+
+#: rhodecode/templates/register.html:5 rhodecode/templates/register.html:74
+msgid "Sign Up"
+msgstr "Inscription"
+
+#: rhodecode/templates/register.html:11
+msgid "Sign Up to"
+msgstr "Inscription à"
+
+#: rhodecode/templates/register.html:38
+msgid "Re-enter password"
+msgstr "Confirmation"
+
+#: rhodecode/templates/register.html:47
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
+msgid "First Name"
+msgstr "Prénom"
+
+#: rhodecode/templates/register.html:56
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
+msgid "Last Name"
+msgstr "Nom"
+
+#: rhodecode/templates/register.html:65
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
+msgid "Email"
+msgstr "E-mail"
+
+#: rhodecode/templates/register.html:76
+msgid "Your account will be activated right after registration"
+msgstr "Votre compte utilisateur sera actif dès la fin de l’enregistrement."
+
+#: rhodecode/templates/register.html:78
+msgid "Your account must wait for activation by administrator"
+msgstr "Votre compte utilisateur devra être activé par un administrateur."
+
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
+msgid "Private repository"
+msgstr "Dépôt privé"
+
+#: rhodecode/templates/repo_switcher_list.html:16
+msgid "Public repository"
+msgstr "Dépôt public"
+
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr "Branches"
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr "Aucune branche n’a été créée pour le moment."
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr "Tags"
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr "Aucun tag n’a été créé pour le moment."
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr "Signets"
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+msgid "There are no bookmarks yet"
+msgstr "Aucun signet n’a été créé."
+
+#: rhodecode/templates/admin/admin.html:5
+#: rhodecode/templates/admin/admin.html:9
+msgid "Admin journal"
+msgstr "Historique d’administration"
+
+#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
+msgid "Action"
+msgstr "Action"
+
+#: rhodecode/templates/admin/admin_log.html:7
+msgid "Repository"
+msgstr "Dépôt"
+
+#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37
+#: rhodecode/templates/tags/tags_data.html:7
+msgid "Date"
+msgstr "Date"
+
+#: rhodecode/templates/admin/admin_log.html:9
+msgid "From IP"
+msgstr "Depuis l’adresse IP"
+
+#: rhodecode/templates/admin/admin_log.html:53
+msgid "No actions yet"
+msgstr "Aucune action n’a été enregistrée pour le moment."
+
+#: rhodecode/templates/admin/ldap/ldap.html:5
+msgid "LDAP administration"
+msgstr "Administration LDAP"
+
+#: rhodecode/templates/admin/ldap/ldap.html:11
+msgid "Ldap"
+msgstr "LDAP"
+
+#: rhodecode/templates/admin/ldap/ldap.html:28
+msgid "Connection settings"
+msgstr "Options de connexion"
+
+#: rhodecode/templates/admin/ldap/ldap.html:30
+msgid "Enable LDAP"
+msgstr "Activer le LDAP"
+
+#: rhodecode/templates/admin/ldap/ldap.html:34
+msgid "Host"
+msgstr "Serveur"
+
+#: rhodecode/templates/admin/ldap/ldap.html:38
+msgid "Port"
+msgstr "Port"
+
+#: rhodecode/templates/admin/ldap/ldap.html:42
+msgid "Account"
+msgstr "Compte"
+
+#: rhodecode/templates/admin/ldap/ldap.html:50
+msgid "Connection security"
+msgstr "Connexion sécurisée"
+
+#: rhodecode/templates/admin/ldap/ldap.html:54
+msgid "Certificate Checks"
+msgstr "Vérif. des certificats"
+
+#: rhodecode/templates/admin/ldap/ldap.html:57
+msgid "Search settings"
+msgstr "Réglages de recherche"
+
+#: rhodecode/templates/admin/ldap/ldap.html:59
+msgid "Base DN"
+msgstr "Base de recherche"
+
+#: rhodecode/templates/admin/ldap/ldap.html:63
+msgid "LDAP Filter"
+msgstr "Filtre de recherche"
+
+#: rhodecode/templates/admin/ldap/ldap.html:67
+msgid "LDAP Search Scope"
+msgstr "Portée de recherche"
+
+#: rhodecode/templates/admin/ldap/ldap.html:70
+msgid "Attribute mappings"
+msgstr "Correspondance des attributs"
+
+#: rhodecode/templates/admin/ldap/ldap.html:72
+msgid "Login Attribute"
+msgstr "Attribut pour le nom d’utilisateur"
+
+#: rhodecode/templates/admin/ldap/ldap.html:76
+msgid "First Name Attribute"
+msgstr "Attribut pour le prénom"
+
+#: rhodecode/templates/admin/ldap/ldap.html:80
+msgid "Last Name Attribute"
+msgstr "Attribut pour le nom de famille"
+
+#: rhodecode/templates/admin/ldap/ldap.html:84
+msgid "E-mail Attribute"
+msgstr "Attribut pour l’e-mail"
+
+#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
+#: rhodecode/templates/admin/settings/hooks.html:73
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
+msgid "Save"
+msgstr "Enregistrer"
+
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr "Mes notifications"
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+#, fuzzy
+msgid "Comments"
+msgstr "commits"
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254
+#: rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr "Tout marquer comme lu"
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+msgid "No notifications here yet"
+msgstr "Aucune notification pour le moment."
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+msgid "Show notification"
+msgstr "Notification"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+msgid "Notifications"
+msgstr "Notifications"
+
+#: rhodecode/templates/admin/permissions/permissions.html:5
+msgid "Permissions administration"
+msgstr "Gestion des permissions"
+
+#: rhodecode/templates/admin/permissions/permissions.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
+msgid "Permissions"
+msgstr "Permissions"
+
+#: rhodecode/templates/admin/permissions/permissions.html:24
+msgid "Default permissions"
+msgstr "Permissions par défaut"
+
+#: rhodecode/templates/admin/permissions/permissions.html:31
+msgid "Anonymous access"
+msgstr "Accès anonyme"
+
+#: rhodecode/templates/admin/permissions/permissions.html:41
+msgid "Repository permission"
+msgstr "Permissions du dépôt"
+
+#: rhodecode/templates/admin/permissions/permissions.html:49
+msgid ""
+"All default permissions on each repository will be reset to choosen "
+"permission, note that all custom default permission on repositories will "
+"be lost"
+msgstr ""
+"Les permissions par défaut de chaque dépôt vont être remplacées par la "
+"permission choisie. Toutes les permissions par défaut des dépôts seront "
+"perdues."
+
+#: rhodecode/templates/admin/permissions/permissions.html:50
+msgid "overwrite existing settings"
+msgstr "Écraser les permissions existantes"
+
+#: rhodecode/templates/admin/permissions/permissions.html:55
+msgid "Registration"
+msgstr "Enregistrement"
+
+#: rhodecode/templates/admin/permissions/permissions.html:63
+msgid "Repository creation"
+msgstr "Création de dépôt"
+
+#: rhodecode/templates/admin/permissions/permissions.html:71
+#, fuzzy
+msgid "Repository forking"
+msgstr "Création de dépôt"
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
+msgid "set"
+msgstr "Définir"
+
+#: rhodecode/templates/admin/repos/repo_add.html:5
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:5
+msgid "Add repository"
+msgstr "Ajouter un dépôt"
+
+#: rhodecode/templates/admin/repos/repo_add.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:11
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
+msgid "Repositories"
+msgstr "Dépôts"
+
+#: rhodecode/templates/admin/repos/repo_add.html:13
+msgid "add new"
+msgstr "ajouter un nouveau"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:20
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
+msgid "Clone from"
+msgstr "Cloner depuis"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr "URL http(s) depuis laquelle le dépôt doit être cloné."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
+msgid "Repository group"
+msgstr "Groupe de dépôt"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+#, fuzzy
+msgid "Optionaly select a group to put this repository into."
+msgstr "Sélectionnez un groupe (optionel) dans lequel sera placé le dépôt."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
+msgid "Type"
+msgstr "Type"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+msgid "Type of repository to create."
+msgstr "Type de dépôt à créer."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+#, fuzzy
+msgid "Landing revision"
+msgstr "révision suivante"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr ""
+"Gardez cette description précise et concise. Utilisez un fichier README "
+"pour des descriptions plus détaillées."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr ""
+"Les dépôts privés sont visibles seulement par les utilisateurs ajoutés "
+"comme collaborateurs."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
+msgid "add"
+msgstr "Ajouter"
+
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:9
+msgid "add new repository"
+msgstr "ajouter un nouveau dépôt"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:5
+msgid "Edit repository"
+msgstr "Éditer le dépôt"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
+msgid "edit"
+msgstr "éditer"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
+msgid "Clone uri"
+msgstr "URL de clone"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr "Sélectionnez un groupe (optionel) dans lequel sera placé le dépôt."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
+msgid "Enable statistics"
+msgstr "Activer les statistiques"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr "Afficher les statistiques sur la page du dépôt."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
+msgid "Enable downloads"
+msgstr "Activer les téléchargements"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr "Afficher le menu de téléchargements sur la page du dépôt."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+#, fuzzy
+msgid "Enable locking"
+msgstr "Activer"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+msgid "Change owner of this repository."
+msgstr "Changer le propriétaire de ce dépôt."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr "Réinitialiser"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
+msgid "Administration"
+msgstr "Administration"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:155
+msgid "Statistics"
+msgstr "Statistiques"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:159
+msgid "Reset current statistics"
+msgstr "Réinitialiser les statistiques"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:159
+msgid "Confirm to remove current statistics"
+msgstr "Souhaitez-vous vraiment réinitialiser les statistiques de ce dépôt ?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:162
+msgid "Fetched to rev"
+msgstr "Parcouru jusqu’à la révision"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
+msgstr "Statistiques obtenues"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:171
+msgid "Remote"
+msgstr "Dépôt distant"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:175
+msgid "Pull changes from remote location"
+msgstr "Récupérer les changements depuis le site distant"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:175
+msgid "Confirm to pull changes from remote side"
+msgstr "Voulez-vous vraiment récupérer les changements depuis le site distant ?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:186
+msgid "Cache"
+msgstr "Cache"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:190
+msgid "Invalidate repository cache"
+msgstr "Invalider le cache du dépôt"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:190
+msgid "Confirm to invalidate repository cache"
+msgstr "Voulez-vous vraiment invalider le cache du dépôt ?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318
+#: rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr "Journal public"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
+msgid "Remove from public journal"
+msgstr "Supprimer du journal public"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:203
+msgid "Add to public journal"
+msgstr "Ajouter le dépôt au journal public"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in "
+"public journal"
+msgstr ""
+"Le descriptif des actions réalisées sur ce dépôt sera visible à tous "
+"depuis le journal public."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+#, fuzzy
+msgid "Locking"
+msgstr "Déverrouiller"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+#, fuzzy
+msgid "Confirm to unlock repository"
+msgstr "Voulez-vous vraiment supprimer ce dépôt ?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+#, fuzzy
+msgid "Confirm to lock repository"
+msgstr "Voulez-vous vraiment supprimer ce dépôt ?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+#, fuzzy
+msgid "Repository is not locked"
+msgstr "Dépôts"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+#, fuzzy
+msgid "Set as fork of"
+msgstr "Indiquer comme fork"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+#, fuzzy
+msgid "Manually set this repository as a fork of another from the list"
+msgstr "Permet d’indiquer manuellement que ce dépôt est un fork d’un autre dépôt."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
+msgid "Delete"
+msgstr "Supprimer"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+msgid "Remove this repository"
+msgstr "Supprimer ce dépôt"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
+msgid "Confirm to delete this repository"
+msgstr "Voulez-vous vraiment supprimer ce dépôt ?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be "
+"unaccesible for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem "
+"please do it manually"
+msgstr ""
+"Ce dépôt sera renommé de manière à le rendre inaccessible à RhodeCode et "
+"au système de gestion de versions.\n"
+"Si vous voulez le supprimer complètement, effectuez la suppression "
+"manuellement."
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
+msgid "none"
+msgstr "Aucune"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
+msgid "read"
+msgstr "Lecture"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
+msgid "write"
+msgstr "Écriture"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
+msgid "admin"
+msgstr "Administration"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
+msgid "member"
+msgstr "Membre"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr "Dépôt privé"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+msgid "default"
+msgstr "[Par défaut]"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
+msgid "revoke"
+msgstr "Révoquer"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
+msgid "Add another member"
+msgstr "Ajouter un utilisateur"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
+msgid "Failed to remove user"
+msgstr "Échec de suppression de l’utilisateur"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
+msgid "Failed to remove users group"
+msgstr "Erreur lors de la suppression du groupe d’utilisateurs."
+
+#: rhodecode/templates/admin/repos/repos.html:5
+msgid "Repositories administration"
+msgstr "Administration des dépôts"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
+msgid "Groups"
+msgstr "Groupes"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
+msgid "with"
+msgstr "comprenant"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:5
+msgid "Add repos group"
+msgstr "Créer un groupe de dépôt"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:10
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:10
+msgid "Repos groups"
+msgstr "Groupes de dépôts"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:12
+msgid "add new repos group"
+msgstr "Nouveau groupe de dépôt"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:50
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:50
+msgid "Group parent"
+msgstr "Parent du groupe"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
+#: rhodecode/templates/admin/users/user_add.html:94
+#: rhodecode/templates/admin/users_groups/users_group_add.html:49
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
+msgid "save"
+msgstr "Enregistrer"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:5
+msgid "Edit repos group"
+msgstr "Éditer le groupe de dépôt"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:12
+msgid "edit repos group"
+msgstr "Édition du groupe de dépôt"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
+msgid "Repositories groups administration"
+msgstr "Administration des groupes de dépôts"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:22
+msgid "ADD NEW GROUP"
+msgstr "AJOUTER UN NOUVEAU GROUPE"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
+msgid "Number of toplevel repositories"
+msgstr "Nombre de sous-dépôts"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
+msgstr "Action"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr "Supprimer"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#, python-format
+msgid "Confirm to delete this group: %s"
+msgstr "Voulez-vous vraiment supprimer le groupe « %s » ?"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
+msgid "There are no repositories groups yet"
+msgstr "Aucun groupe de dépôts n’a été créé pour le moment."
+
+#: rhodecode/templates/admin/settings/hooks.html:5
+#: rhodecode/templates/admin/settings/settings.html:5
+msgid "Settings administration"
+msgstr "Administration générale"
+
+#: rhodecode/templates/admin/settings/hooks.html:9
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/settings/repo_settings.html:13
+msgid "Settings"
+msgstr "Options"
+
+#: rhodecode/templates/admin/settings/hooks.html:24
+msgid "Built in hooks - read only"
+msgstr "Hooks prédéfinis (lecture seule)"
+
+#: rhodecode/templates/admin/settings/hooks.html:40
+msgid "Custom hooks"
+msgstr "Hooks personnalisés"
+
+#: rhodecode/templates/admin/settings/hooks.html:56
+msgid "remove"
+msgstr "Enlever"
+
+#: rhodecode/templates/admin/settings/hooks.html:88
+msgid "Failed to remove hook"
+msgstr "Erreur lors de la suppression du hook."
+
+#: rhodecode/templates/admin/settings/settings.html:24
+msgid "Remap and rescan repositories"
+msgstr "Ré-associer et re-scanner les dépôts"
+
+#: rhodecode/templates/admin/settings/settings.html:32
+msgid "rescan option"
+msgstr "Option de re-scan"
+
+#: rhodecode/templates/admin/settings/settings.html:38
+msgid ""
+"In case a repository was deleted from filesystem and there are leftovers "
+"in the database check this option to scan obsolete data in database and "
+"remove it."
+msgstr ""
+"Cochez cette option pour supprimer d’éventuelles données obsolètes "
+"(concernant des dépôts manuellement supprimés) de la base de données."
+
+#: rhodecode/templates/admin/settings/settings.html:39
+msgid "destroy old data"
+msgstr "Supprimer les données obsolètes"
+
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete "
+"if `destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
+msgid "Rescan repositories"
+msgstr "Re-scanner les dépôts"
+
+#: rhodecode/templates/admin/settings/settings.html:52
+msgid "Whoosh indexing"
+msgstr "Indexation Whoosh"
+
+#: rhodecode/templates/admin/settings/settings.html:60
+msgid "index build option"
+msgstr "Option d’indexation"
+
+#: rhodecode/templates/admin/settings/settings.html:65
+msgid "build from scratch"
+msgstr "Purger et reconstruire l’index"
+
+#: rhodecode/templates/admin/settings/settings.html:71
+msgid "Reindex"
+msgstr "Mettre à jour l’index"
+
+#: rhodecode/templates/admin/settings/settings.html:77
+msgid "Global application settings"
+msgstr "Réglages d’application globaux"
+
+#: rhodecode/templates/admin/settings/settings.html:86
+msgid "Application name"
+msgstr "Nom de l’application"
+
+#: rhodecode/templates/admin/settings/settings.html:95
+msgid "Realm text"
+msgstr "Texte du royaume"
+
+#: rhodecode/templates/admin/settings/settings.html:104
+msgid "GA code"
+msgstr "Code GA"
+
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
+msgid "Save settings"
+msgstr "Enregister les options"
+
+#: rhodecode/templates/admin/settings/settings.html:119
+#, fuzzy
+msgid "Visualisation settings"
+msgstr "Réglages d’application globaux"
+
+#: rhodecode/templates/admin/settings/settings.html:128
+#, fuzzy
+msgid "Icons"
+msgstr "Options"
+
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+#, fuzzy
+msgid "Show private repo icon on repositories"
+msgstr "Dépôt privé"
+
+#: rhodecode/templates/admin/settings/settings.html:144
+#, fuzzy
+msgid "Meta-Tagging"
+msgstr "Réglages"
+
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:176
+#, fuzzy
+msgid "VCS settings"
+msgstr "Réglages"
+
+#: rhodecode/templates/admin/settings/settings.html:185
+msgid "Web"
+msgstr "Web"
+
+#: rhodecode/templates/admin/settings/settings.html:190
+#, fuzzy
+msgid "require ssl for vcs operations"
+msgstr "SSL requis pour les pushs"
+
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
+"will return HTTP Error 406: Not Acceptable"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:198
+msgid "Hooks"
+msgstr "Hooks"
+
+#: rhodecode/templates/admin/settings/settings.html:203
+msgid "Update repository after push (hg update)"
+msgstr "Mettre à jour les dépôts après un push (hg update)"
+
+#: rhodecode/templates/admin/settings/settings.html:207
+msgid "Show repository size after push"
+msgstr "Afficher la taille du dépôt après un push"
+
+#: rhodecode/templates/admin/settings/settings.html:211
+msgid "Log user push commands"
+msgstr "Journaliser les commandes de push"
+
+#: rhodecode/templates/admin/settings/settings.html:215
+msgid "Log user pull commands"
+msgstr "Journaliser les commandes de pull"
+
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr "Avancé"
+
+#: rhodecode/templates/admin/settings/settings.html:224
+#, fuzzy
+msgid "Mercurial Extensions"
+msgstr "Dépôt Mercurial"
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
+msgid "Repositories location"
+msgstr "Emplacement des dépôts"
+
+#: rhodecode/templates/admin/settings/settings.html:250
+msgid ""
+"This a crucial application setting. If you are really sure you need to "
+"change this, you must restart application in order to make this setting "
+"take effect. Click this label to unlock."
+msgstr ""
+"Ce réglage ne devrait pas être modifié en temps normal. Si vous devez "
+"vraiment le faire, redémarrer l’application une fois le changement "
+"effectué. Cliquez sur ce texte pour déverrouiller."
+
+#: rhodecode/templates/admin/settings/settings.html:251
+msgid "unlock"
+msgstr "Déverrouiller"
+
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a "
+"restart, and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr "E-mail de test"
+
+#: rhodecode/templates/admin/settings/settings.html:280
+msgid "Email to"
+msgstr "Envoyer l’e-mail à"
+
+#: rhodecode/templates/admin/settings/settings.html:288
+msgid "Send"
+msgstr "Envoyer"
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr "Information système et paquets"
+
+#: rhodecode/templates/admin/settings/settings.html:297
+msgid "show"
+msgstr "Montrer"
+
+#: rhodecode/templates/admin/users/user_add.html:5
+msgid "Add user"
+msgstr "Ajouter un utilisateur"
+
+#: rhodecode/templates/admin/users/user_add.html:10
+#: rhodecode/templates/admin/users/user_edit.html:11
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: rhodecode/templates/admin/users/user_add.html:12
+msgid "add new user"
+msgstr "nouvel utilisateur"
+
+#: rhodecode/templates/admin/users/user_add.html:50
+msgid "Password confirmation"
+msgstr "Confirmation"
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
+#: rhodecode/templates/admin/users_groups/users_group_add.html:41
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
+msgid "Active"
+msgstr "Actif"
+
+#: rhodecode/templates/admin/users/user_edit.html:5
+msgid "Edit user"
+msgstr "Éditer l'utilisateur"
+
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
+msgid "Change your avatar at"
+msgstr "Vous pouvez changer votre avatar sur"
+
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
+msgid "Using"
+msgstr "en utilisant l’adresse"
+
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
+msgid "API key"
+msgstr "Clé d’API"
+
+#: rhodecode/templates/admin/users/user_edit.html:59
+msgid "LDAP DN"
+msgstr "DN LDAP"
+
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
+msgid "New password"
+msgstr "Nouveau mot de passe"
+
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr "Confirmation du nouveau mot de passe"
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+#, fuzzy
+msgid "Inherit default permissions"
+msgstr "Permissions par défaut"
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
+msgid "Create repositories"
+msgstr "Création de dépôts"
+
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+#, fuzzy
+msgid "Fork repositories"
+msgstr "Dépôts"
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+#, fuzzy
+msgid "Nothing here yet"
+msgstr "Aucune notification pour le moment."
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+msgid "Permission"
+msgstr "Permission"
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+#, fuzzy
+msgid "Edit Permission"
+msgstr "Permissions du dépôt"
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+#, fuzzy
+msgid "Email addresses"
+msgstr "Adresse e-mail"
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, fuzzy, python-format
+msgid "Confirm to delete this email: %s"
+msgstr "Voulez-vous vraiment supprimer l’utilisateur « %s » ?"
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+#, fuzzy
+msgid "New email address"
+msgstr "Adresse e-mail"
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+#, fuzzy
+msgid "Add"
+msgstr "Ajouter"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
+msgid "My account"
+msgstr "Mon compte"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:9
+msgid "My Account"
+msgstr "Mon compte"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+msgid "My permissions"
+msgstr "Mes permissions"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+msgid "My repos"
+msgstr "Mes dépôts"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+#, fuzzy
+msgid "My pull requests"
+msgstr "a posté un commentaire sur le commit"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+#, fuzzy
+msgid "Add repo"
+msgstr "ajouter un nouveau"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, python-format
+msgid "Pull request #%s opened on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+#, fuzzy
+msgid "Confirm to delete this pull request"
+msgstr "Voulez-vous vraiment supprimer ce dépôt ?"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40
+#: rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
+msgstr "Révision"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr "privé"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr "Voulez-vous vraiment supprimer le dépôt %s ?"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
+msgid "No repositories yet"
+msgstr "Aucun dépôt pour le moment"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
+msgid "create one now"
+msgstr "En créer un maintenant"
+
+#: rhodecode/templates/admin/users/users.html:5
+msgid "Users administration"
+msgstr "Administration des utilisateurs"
+
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr "Utilisateurs"
+
+#: rhodecode/templates/admin/users/users.html:23
+msgid "ADD NEW USER"
+msgstr "NOUVEL UTILISATEUR"
+
+#: rhodecode/templates/admin/users/users.html:77
+msgid "username"
+msgstr "Nom d’utilisateur"
+
+#: rhodecode/templates/admin/users/users.html:80
+#, fuzzy
+msgid "firstname"
+msgstr "Prénom"
+
+#: rhodecode/templates/admin/users/users.html:81
+msgid "lastname"
+msgstr "Nom de famille"
+
+#: rhodecode/templates/admin/users/users.html:82
+msgid "last login"
+msgstr "Dernière connexion"
+
+#: rhodecode/templates/admin/users/users.html:84
+#: rhodecode/templates/admin/users_groups/users_groups.html:34
+msgid "active"
+msgstr "Actif"
+
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
+msgid "ldap"
+msgstr "LDAP"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:5
+msgid "Add users group"
+msgstr "Ajouter un groupe d’utilisateur"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:10
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+msgid "Users groups"
+msgstr "Groupes d’utilisateurs"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:12
+msgid "add new users group"
+msgstr "Ajouter un nouveau groupe"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:5
+msgid "Edit users group"
+msgstr "Éditer le groupe d’utilisateurs"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:11
+msgid "UsersGroups"
+msgstr "UsersGroups"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:50
+msgid "Members"
+msgstr "Membres"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:58
+msgid "Choosen group members"
+msgstr "Membres du groupe"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:61
+msgid "Remove all elements"
+msgstr "Tout enlever"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:75
+msgid "Available members"
+msgstr "Membres disponibles"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:79
+msgid "Add all elements"
+msgstr "Tout ajouter"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+msgid "Group members"
+msgstr "Membres du groupe"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:5
+msgid "Users groups administration"
+msgstr "Gestion des groupes d’utilisateurs"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:23
+msgid "ADD NEW USER GROUP"
+msgstr "AJOUTER UN NOUVEAU GROUPE"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:32
+msgid "group name"
+msgstr "Nom du groupe"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr "Membres"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr "Voulez-vous vraiment supprimer le groupe d‘utilisateurs « %s » ?"
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr "Signaler un bogue"
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr "Connexion à votre compte"
+
+#: rhodecode/templates/base/base.html:100
+msgid "Forgot password ?"
+msgstr "Mot de passe oublié ?"
+
+#: rhodecode/templates/base/base.html:107
+msgid "Log In"
+msgstr "Connexion"
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr "Boîte de réception"
+
+#: rhodecode/templates/base/base.html:122
+#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302
+#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8
+#: rhodecode/templates/tags/tags.html:11
+msgid "Home"
+msgstr "Accueil"
+
+#: rhodecode/templates/base/base.html:123
+#: rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311
+#: rhodecode/templates/base/base.html:313
+#: rhodecode/templates/journal/journal.html:4
+#: rhodecode/templates/journal/journal.html:21
+#: rhodecode/templates/journal/public_journal.html:4
+msgid "Journal"
+msgstr "Historique"
+
+#: rhodecode/templates/base/base.html:125
+msgid "Log Out"
+msgstr "Se déconnecter"
+
+#: rhodecode/templates/base/base.html:144
+msgid "Switch repository"
+msgstr "Aller au dépôt"
+
+#: rhodecode/templates/base/base.html:146
+msgid "Products"
+msgstr "Produits"
+
+#: rhodecode/templates/base/base.html:152
+#: rhodecode/templates/base/base.html:182
+msgid "loading..."
+msgstr "Chargement…"
+
+#: rhodecode/templates/base/base.html:158
+#: rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
+msgstr "Résumé"
+
+#: rhodecode/templates/base/base.html:166
+#: rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
+msgstr "Historique"
+
+#: rhodecode/templates/base/base.html:175
+#: rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
+msgstr "Aller"
+
+#: rhodecode/templates/base/base.html:186
+#: rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
+msgstr "Fichiers"
+
+#: rhodecode/templates/base/base.html:195
+#: rhodecode/templates/base/base.html:199
+msgid "Options"
+msgstr "Options"
+
+#: rhodecode/templates/base/base.html:204
+#: rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
+msgid "settings"
+msgstr "Réglages"
+
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
+msgstr "Fork"
+
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:213
+msgid "search"
+msgstr "Rechercher"
+
+#: rhodecode/templates/base/base.html:222
+msgid "repositories groups"
+msgstr "Groupes de dépôts"
+
+#: rhodecode/templates/base/base.html:224
+msgid "users groups"
+msgstr "Groupes d’utilisateurs"
+
+#: rhodecode/templates/base/base.html:225
+msgid "permissions"
+msgstr "Permissions"
+
+#: rhodecode/templates/base/base.html:238
+#: rhodecode/templates/base/base.html:240
+msgid "Followers"
+msgstr "Followers"
+
+#: rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248
+msgid "Forks"
+msgstr "Forks"
+
+#: rhodecode/templates/base/base.html:327
+#: rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331
+#: rhodecode/templates/search/search.html:52
+msgid "Search"
+msgstr "Rechercher"
+
+#: rhodecode/templates/base/root.html:42
+msgid "add another comment"
+msgstr "Nouveau commentaire"
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
+msgid "Stop following this repository"
+msgstr "Arrêter de suivre ce dépôt"
+
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
+msgid "Start following this repository"
+msgstr "Suivre ce dépôt"
+
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr "Groupe"
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr "Résultats tronqués"
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr "Aucun fichier ne correspond"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr "Signets de %s"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39
+#: rhodecode/templates/tags/tags_data.html:8
+msgid "Author"
+msgstr "Auteur"
+
+#: rhodecode/templates/branches/branches.html:5
+#, python-format
+msgid "%s Branches"
+msgstr "Branches de %s"
+
+#: rhodecode/templates/branches/branches.html:29
+#, fuzzy
+msgid "Compare branches"
+msgstr "Branches"
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+#, fuzzy
+msgid "Compare"
+msgstr "vue de comparaison"
+
+#: rhodecode/templates/branches/branches_data.html:6
+msgid "name"
+msgstr "Prénom"
+
+#: rhodecode/templates/branches/branches_data.html:7
+msgid "date"
+msgstr "Date"
+
+#: rhodecode/templates/branches/branches_data.html:8
+#: rhodecode/templates/shortlog/shortlog_data.html:8
+msgid "author"
+msgstr "Auteur"
+
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
+msgstr "Révision"
+
+#: rhodecode/templates/branches/branches_data.html:10
+#, fuzzy
+msgid "compare"
+msgstr "vue de comparaison"
+
+#: rhodecode/templates/changelog/changelog.html:6
+#, python-format
+msgid "%s Changelog"
+msgstr "Historique de %s"
+
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] "Affichage de %d révision sur %d"
+msgstr[1] "Affichage de %d révisions sur %d"
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+#, fuzzy
+msgid "Compare fork"
+msgstr "vue de comparaison"
+
+#: rhodecode/templates/changelog/changelog.html:46
+msgid "Show"
+msgstr "Afficher"
+
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
+msgstr "montrer plus"
+
+#: rhodecode/templates/changelog/changelog.html:76
+msgid "Affected number of files, click to show more details"
+msgstr "Nombre de fichiers modifiés, cliquez pour plus de détails"
+
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+#, fuzzy
+msgid "Changeset status"
+msgstr "Changesets"
+
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
+msgid "Parent"
+msgstr "Parent"
+
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
+msgid "No parents"
+msgstr "Aucun parent"
+
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr "Fusion"
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
+#: rhodecode/templates/files/files.html:29
+#: rhodecode/templates/files/files_add.html:33
+#: rhodecode/templates/files/files_edit.html:33
+#: rhodecode/templates/shortlog/shortlog_data.html:9
+msgid "branch"
+msgstr "Branche"
+
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr "Signet"
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
+msgid "tag"
+msgstr "Tag"
+
+#: rhodecode/templates/changelog/changelog.html:164
+msgid "Show selected changes __S -> __E"
+msgstr "Afficher les changements sélections de __S à __E"
+
+#: rhodecode/templates/changelog/changelog.html:255
+msgid "There are no changes yet"
+msgstr "Il n’y a aucun changement pour le moment"
+
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
+msgid "removed"
+msgstr "Supprimés"
+
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
+msgid "changed"
+msgstr "Modifiés"
+
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
+msgid "added"
+msgstr "Ajoutés"
+
+#: rhodecode/templates/changelog/changelog_details.html:8
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
+#, python-format
+msgid "affected %s files"
+msgstr "%s fichiers affectés"
+
+#: rhodecode/templates/changeset/changeset.html:6
+#, python-format
+msgid "%s Changeset"
+msgstr "Changeset de %s"
+
+#: rhodecode/templates/changeset/changeset.html:14
+msgid "Changeset"
+msgstr "Changements"
+
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
+msgid "raw diff"
+msgstr "Diff brut"
+
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
+msgid "download diff"
+msgstr "Télécharger le diff"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d commentaire"
+msgstr[1] "%d commentaires"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] "(et %d en ligne)"
+msgstr[1] "(et %d en ligne)"
+
+#: rhodecode/templates/changeset/changeset.html:103
+#, python-format
+msgid "%s files affected with %s insertions and %s deletions:"
+msgstr "%s fichiers affectés avec %s insertions et %s suppressions :"
+
+#: rhodecode/templates/changeset/changeset.html:119
+msgid "Changeset was too big and was cut off..."
+msgstr "Cet ensemble de changements était trop important et a été découpé…"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr "Envoi…"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr "Commentaire sur la ligne {1}."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr ""
+"Les commentaires sont analysés avec la syntaxe %s, avec le support de la "
+"commande %s."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr ""
+"Utilisez @nomutilisateur dans ce texte pour envoyer une notification à "
+"l’utilisateur RhodeCode en question."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+msgid "Comment"
+msgstr "Commentaire"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
+msgstr "Masquer"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "You need to be logged in to comment."
+msgstr "Vous devez être connecté pour poster des commentaires."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr "Se connecter maintenant"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr "Laisser un commentaire"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+#, fuzzy
+msgid "change status"
+msgstr "Changesets"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, python-format
+msgid "%s Changesets"
+msgstr "Changesets de %s"
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
+msgid "Compare View"
+msgstr "Comparaison"
+
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
+msgid "Files affected"
+msgstr "Fichiers affectés"
+
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
+msgstr "Diff"
+
+#: rhodecode/templates/changeset/diff_block.html:27
+msgid "show inline comments"
+msgstr "Afficher les commentaires"
+
+#: rhodecode/templates/compare/compare_cs.html:5
+#, fuzzy
+msgid "No changesets"
+msgstr "Dépôt vide"
+
+#: rhodecode/templates/compare/compare_diff.html:37
+#, fuzzy
+msgid "Outgoing changesets"
+msgstr "Dépôt vide"
+
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
+msgstr "Fork"
+
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
+msgstr "Dépôt Mercurial"
+
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
+msgstr "Dépôt Git"
+
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
+msgstr "Dépôt public"
+
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
+msgstr "Fork de"
+
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
+msgstr "Dépôt vide"
+
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, python-format
+msgid "Confirm to delete this user: %s"
+msgstr "Voulez-vous vraiment supprimer l’utilisateur « %s » ?"
+
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
+msgstr "Ceci est une notification de RhodeCode."
+
+#: rhodecode/templates/errors/error_document.html:46
+#, python-format
+msgid "You will be redirected to %s in %s seconds"
+msgstr "Vous serez redirigé vers %s dans %s secondes."
+
+#: rhodecode/templates/files/file_diff.html:4
+#, python-format
+msgid "%s File diff"
+msgstr "Diff de fichier de %s"
+
+#: rhodecode/templates/files/file_diff.html:12
+msgid "File diff"
+msgstr "Diff de fichier"
+
+#: rhodecode/templates/files/files.html:4
+#: rhodecode/templates/files/files.html:72
+#, fuzzy, python-format
+msgid "%s files"
+msgstr "Fichiers de %s"
+
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr "Fichiers"
+
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, python-format
+msgid "%s Edit file"
+msgstr "Edition de fichier de %s"
+
+#: rhodecode/templates/files/files_add.html:19
+msgid "add file"
+msgstr "Ajouter un fichier"
+
+#: rhodecode/templates/files/files_add.html:40
+msgid "Add new file"
+msgstr "Ajouter un nouveau fichier"
+
+#: rhodecode/templates/files/files_add.html:45
+msgid "File Name"
+msgstr "Nom de fichier"
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+msgid "or"
+msgstr "ou"
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+msgid "Upload file"
+msgstr "Téléverser un fichier"
+
+#: rhodecode/templates/files/files_add.html:58
+msgid "Create new file"
+msgstr "Créer un nouveau fichier"
+
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr "Emplacement"
+
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr "Utilisez / pour séparer les répertoires"
+
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr "Message de commit"
+
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
+msgstr "Commiter les changements"
+
+#: rhodecode/templates/files/files_browser.html:13
+msgid "view"
+msgstr "voir"
+
+#: rhodecode/templates/files/files_browser.html:14
+msgid "previous revision"
+msgstr "révision précédente"
+
+#: rhodecode/templates/files/files_browser.html:16
+msgid "next revision"
+msgstr "révision suivante"
+
+#: rhodecode/templates/files/files_browser.html:23
+msgid "follow current branch"
+msgstr "Suivre la branche actuelle"
+
+#: rhodecode/templates/files/files_browser.html:27
+msgid "search file list"
+msgstr "Rechercher un fichier"
+
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+msgid "add new file"
+msgstr "Ajouter un fichier"
+
+#: rhodecode/templates/files/files_browser.html:35
+msgid "Loading file list..."
+msgstr "Chargement de la liste des fichiers…"
+
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
+msgstr "Taille"
+
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
+msgstr "Type MIME"
+
+#: rhodecode/templates/files/files_browser.html:50
+msgid "Last Revision"
+msgstr "Dernière révision"
+
+#: rhodecode/templates/files/files_browser.html:51
+msgid "Last modified"
+msgstr "Dernière modification"
+
+#: rhodecode/templates/files/files_browser.html:52
+msgid "Last commiter"
+msgstr "Dernier commiteur"
+
+#: rhodecode/templates/files/files_edit.html:19
+msgid "edit file"
+msgstr "Éditer le fichier"
+
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr "Afficher les annotations"
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
+msgstr "montrer le fichier brut"
+
+#: rhodecode/templates/files/files_edit.html:51
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
+msgstr "télécharger le fichier brut"
+
+#: rhodecode/templates/files/files_edit.html:54
+msgid "source"
+msgstr "Source"
+
+#: rhodecode/templates/files/files_edit.html:59
+msgid "Editing file"
+msgstr "Édition du fichier"
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr "Historique"
+
+#: rhodecode/templates/files/files_source.html:9
+#, fuzzy
+msgid "diff to revision"
+msgstr "révision suivante"
+
+#: rhodecode/templates/files/files_source.html:10
+#, fuzzy
+msgid "show at revision"
+msgstr "révision suivante"
+
+#: rhodecode/templates/files/files_source.html:14
+#, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] "%s Auteur"
+msgstr[1] "%s Auteurs"
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
+msgstr "montrer les sources"
+
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr "Fichier binaire (%s)"
+
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
+msgstr "Ce fichier est trop gros pour être affiché."
+
+#: rhodecode/templates/files/files_source.html:124
+msgid "Selection link"
+msgstr "Lien vers la sélection"
+
+#: rhodecode/templates/files/files_ypjax.html:5
+msgid "annotation"
+msgstr "annotation"
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr "Revenir en arrière"
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr "Aucun fichier à cet endroit"
+
+#: rhodecode/templates/followers/followers.html:5
+#, python-format
+msgid "%s Followers"
+msgstr "Followers de %s"
+
+#: rhodecode/templates/followers/followers.html:13
+msgid "followers"
+msgstr "followers"
+
+#: rhodecode/templates/followers/followers_data.html:12
+msgid "Started following -"
+msgstr "A commencé à suivre le dépôt :"
+
+#: rhodecode/templates/forks/fork.html:5
+#, python-format
+msgid "%s Fork"
+msgstr "Fork de %s"
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr "Nom du fork"
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr "Privé"
+
+#: rhodecode/templates/forks/fork.html:77
+msgid "Copy permissions"
+msgstr "Copier les permissions"
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr "MÀJ après le clonage"
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr "Forker ce dépôt"
+
+#: rhodecode/templates/forks/forks.html:5
+#, python-format
+msgid "%s Forks"
+msgstr "Forks de %s"
+
+#: rhodecode/templates/forks/forks.html:13
+msgid "forks"
+msgstr "forks"
+
+#: rhodecode/templates/forks/forks_data.html:17
+msgid "forked"
+msgstr "forké"
+
+#: rhodecode/templates/forks/forks_data.html:38
+msgid "There are no forks yet"
+msgstr "Il n’y a pas encore de forks."
+
+#: rhodecode/templates/journal/journal.html:13
+#, fuzzy
+msgid "ATOM journal feed"
+msgstr "%s — Flux %s du journal public"
+
+#: rhodecode/templates/journal/journal.html:14
+#, fuzzy
+msgid "RSS journal feed"
+msgstr "%s — Flux %s du journal public"
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr "Rafraîchir"
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+#, fuzzy
+msgid "RSS feed"
+msgstr "Flux %s de %s"
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:41
+msgid "Watched"
+msgstr "Surveillé"
+
+#: rhodecode/templates/journal/journal.html:46
+msgid "ADD"
+msgstr "AJOUTER"
+
+#: rhodecode/templates/journal/journal.html:114
+msgid "following user"
+msgstr "utilisateur suivant"
+
+#: rhodecode/templates/journal/journal.html:114
+msgid "user"
+msgstr "utilisateur"
+
+#: rhodecode/templates/journal/journal.html:147
+msgid "You are not following any users or repositories"
+msgstr "Vous ne suivez aucun utilisateur ou dépôt"
+
+#: rhodecode/templates/journal/journal_data.html:47
+msgid "No entries yet"
+msgstr "Aucune entrée pour le moment"
+
+#: rhodecode/templates/journal/public_journal.html:13
+#, fuzzy
+msgid "ATOM public journal feed"
+msgstr "%s — Flux %s du journal public"
+
+#: rhodecode/templates/journal/public_journal.html:14
+#, fuzzy
+msgid "RSS public journal feed"
+msgstr "%s — Flux %s du journal public"
+
+#: rhodecode/templates/journal/public_journal.html:21
+msgid "Public Journal"
+msgstr "Journal public"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+#, fuzzy
+msgid "Detailed compare view"
+msgstr "vue de comparaison"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+#, fuzzy
+msgid "owner"
+msgstr "Propriétaire"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+msgid "Add reviewer to this pull request."
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+#, fuzzy
+msgid "Create new pull request"
+msgstr "Créer un nouveau fichier"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+#, fuzzy
+msgid "Title"
+msgstr "Écriture"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+#, fuzzy
+msgid "description"
+msgstr "Description"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+#, fuzzy
+msgid "Status"
+msgstr "Changesets"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+msgid "Still not reviewed by"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+#, fuzzy
+msgid "Created on"
+msgstr "En créer un maintenant"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+#, fuzzy
+msgid "Compare view"
+msgstr "vue de comparaison"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+#, fuzzy
+msgid "Incoming changesets"
+msgstr "Dépôt vide"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+#, fuzzy
+msgid "all pull requests"
+msgstr "Créer un nouveau fichier"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr "dans \"%s\" le dépôt: %s"
+
+#: rhodecode/templates/search/search.html:8
+#, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr "Recherche \"%s\" dans tous les référentiels"
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, python-format
+msgid "Search in repository: %s"
+msgstr "dans le dépôt : %s"
+
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+#, fuzzy
+msgid "Search in all repositories"
+msgstr "dans tous les dépôts"
+
+#: rhodecode/templates/search/search.html:48
+msgid "Search term"
+msgstr "Termes de la recherches"
+
+#: rhodecode/templates/search/search.html:60
+msgid "Search in"
+msgstr "Rechercher dans"
+
+#: rhodecode/templates/search/search.html:63
+msgid "File contents"
+msgstr "Le contenu des fichiers"
+
+#: rhodecode/templates/search/search.html:64
+#, fuzzy
+msgid "Commit messages"
+msgstr "Message de commit"
+
+#: rhodecode/templates/search/search.html:65
+msgid "File names"
+msgstr "Les noms de fichiers"
+
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
+#: rhodecode/templates/search/search_path.html:15
+msgid "Permission denied"
+msgstr "Permission refusée"
+
+#: rhodecode/templates/settings/repo_settings.html:5
+#, python-format
+msgid "%s Settings"
+msgstr "Réglages de %s"
+
+#: rhodecode/templates/shortlog/shortlog.html:5
+#, python-format
+msgid "%s Shortlog"
+msgstr "Résumé de %s"
+
+#: rhodecode/templates/shortlog/shortlog.html:14
+msgid "shortlog"
+msgstr "Résumé"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:7
+msgid "age"
+msgstr "Âge"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+msgid "No commit message"
+msgstr "Pas de message de commit"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr "Ajouter ou téléverser des fichiers directement via RhodeCode…"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr "Pusher le nouveau dépôt"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+msgid "Existing repository?"
+msgstr "Le dépôt existe déjà ?"
+
+#: rhodecode/templates/summary/summary.html:4
+#, python-format
+msgid "%s Summary"
+msgstr "Résumé de %s"
+
+#: rhodecode/templates/summary/summary.html:12
+msgid "summary"
+msgstr "résumé"
+
+#: rhodecode/templates/summary/summary.html:20
+#, fuzzy, python-format
+msgid "repo %s ATOM feed"
+msgstr "S’abonner au flux ATOM de %s"
+
+#: rhodecode/templates/summary/summary.html:21
+#, fuzzy, python-format
+msgid "repo %s RSS feed"
+msgstr "S’abonner au flux RSS de %s"
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+msgid "ATOM"
+msgstr "ATOM"
+
+#: rhodecode/templates/summary/summary.html:82
+#, python-format
+msgid "Non changable ID %s"
+msgstr "Identifiant permanent : %s"
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr "publique"
+
+#: rhodecode/templates/summary/summary.html:95
+msgid "remote clone"
+msgstr "Clone distant"
+
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
+msgstr "Contact"
+
+#: rhodecode/templates/summary/summary.html:130
+msgid "Clone url"
+msgstr "URL de clone"
+
+#: rhodecode/templates/summary/summary.html:133
+msgid "Show by Name"
+msgstr "Afficher par nom"
+
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
+msgstr "Afficher par ID"
+
+#: rhodecode/templates/summary/summary.html:142
+msgid "Trending files"
+msgstr "Populaires"
+
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
+msgstr "Activer"
+
+#: rhodecode/templates/summary/summary.html:158
+msgid "Download"
+msgstr "Téléchargements"
+
+#: rhodecode/templates/summary/summary.html:162
+msgid "There are no downloads yet"
+msgstr "Il n’y a pas encore de téléchargements proposés."
+
+#: rhodecode/templates/summary/summary.html:164
+msgid "Downloads are disabled for this repository"
+msgstr "Les téléchargements sont désactivés pour ce dépôt."
+
+#: rhodecode/templates/summary/summary.html:170
+msgid "Download as zip"
+msgstr "Télécharger en ZIP"
+
+#: rhodecode/templates/summary/summary.html:173
+msgid "Check this to download archive with subrepos"
+msgstr "Télécharger une archive contenant également les sous-dépôts éventuels"
+
+#: rhodecode/templates/summary/summary.html:173
+msgid "with subrepos"
+msgstr "avec les sous-dépôts"
+
+#: rhodecode/templates/summary/summary.html:186
+msgid "Commit activity by day / author"
+msgstr "Activité de commit par jour et par auteur"
+
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
+msgstr "Statistiques obtenues :"
+
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
+msgstr "Résumé des changements"
+
+#: rhodecode/templates/summary/summary.html:220
+msgid "Quick start"
+msgstr "Démarrage rapide"
+
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr "Télécharger %s comme archive %s"
+
+#: rhodecode/templates/summary/summary.html:650
+msgid "commits"
+msgstr "commits"
+
+#: rhodecode/templates/summary/summary.html:651
+msgid "files added"
+msgstr "fichiers ajoutés"
+
+#: rhodecode/templates/summary/summary.html:652
+msgid "files changed"
+msgstr "fichiers modifiés"
+
+#: rhodecode/templates/summary/summary.html:653
+msgid "files removed"
+msgstr "fichiers supprimés"
+
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr "commit"
+
+#: rhodecode/templates/summary/summary.html:657
+msgid "file added"
+msgstr "fichier ajouté"
+
+#: rhodecode/templates/summary/summary.html:658
+msgid "file changed"
+msgstr "fichié modifié"
+
+#: rhodecode/templates/summary/summary.html:659
+msgid "file removed"
+msgstr "fichier supprimé"
+
+#: rhodecode/templates/tags/tags.html:5
+#, python-format
+msgid "%s Tags"
+msgstr "Tags de %s"
+
diff --git a/rhodecode/i18n/how_to b/rhodecode/i18n/how_to
index 7851c116..283f4a33 100644
--- a/rhodecode/i18n/how_to
+++ b/rhodecode/i18n/how_to
@@ -4,25 +4,27 @@
#this needs to be done on source codes, preferable default/stable branches
-python setup.py extract_messages -> get messages from project
-python setup.py init_catalog -l pl -> create a language directory for <pl> lang
-edit the new po file with poedit or any other editor
-python setup.py compile_catalog -> create translation files
+python setup.py extract_messages <- get messages from project
+python setup.py init_catalog -l pl <- create a language directory for <pl> lang
+#edit the new po file with poedit or any other editor
+msgfmt -f -c <updated_file.po> <- check format and errors
+python setup.py compile_catalog <- create translation files
#############
# to update #
#############
-python setup.py extract_messages -> get messages from project
-python setup.py update_catalog -> to update the translations
-edit the new updated po file with poedit
-python setup.py compile_catalog -> create translation files
+python setup.py extract_messages <- get messages from project
+python setup.py update_catalog <- to update the translations
+#edit the new updated po file with poedit
+msgfmt -f -c <updated_file.po> <- check format and errors
+python setup.py compile_catalog <- create translation files
###################
# change language #
###################
-`lang=en`
+`lang=pl`
in the .ini file \ No newline at end of file
diff --git a/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.mo b/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.mo
new file mode 100644
index 00000000..75399ccd
--- /dev/null
+++ b/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.mo
Binary files differ
diff --git a/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po b/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po
new file mode 100644
index 00000000..2f827739
--- /dev/null
+++ b/rhodecode/i18n/ja/LC_MESSAGES/rhodecode.po
@@ -0,0 +1,3929 @@
+# Japanese translations for RhodeCode.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the RhodeCode project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+# Takumi IINO <trot.thunder@gmail.com> 2012
+# WAKAYAMA Shirou <shirou.faw@gmail.com> 2012
+#
+# Mercurial Japanese Translation.
+# - http://selenic.com/repo/hg/file/stable/i18n/ja.po
+# ========================================
+msgid ""
+msgstr ""
+"Project-Id-Version: RhodeCode 1.2.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\n"
+"PO-Revision-Date: 2012-07-14 03:16+0900\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ja <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: rhodecode/controllers/changelog.py:94
+msgid "All Branches"
+msgstr "すべてのブランチ"
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr "空白を表示"
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr "空白を無視"
+
+#: rhodecode/controllers/changeset.py:157
+#, python-format
+msgid "%s line context"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:333
+#: rhodecode/controllers/changeset.py:348 rhodecode/lib/diffs.py:70
+msgid "binary file"
+msgstr "バイナリファイル"
+
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is "
+"not allowed"
+msgstr ""
+
+#: rhodecode/controllers/compare.py:69
+#, fuzzy
+msgid "There are no changesets yet"
+msgstr "まだ変更がありません"
+
+#: rhodecode/controllers/error.py:69
+msgid "Home page"
+msgstr "ホームページ"
+
+#: rhodecode/controllers/error.py:98
+msgid "The request could not be understood by the server due to malformed syntax."
+msgstr "形式が間違っているため、サーバーはリクエストを処理出来ませんでした"
+
+#: rhodecode/controllers/error.py:101
+msgid "Unauthorized access to resource"
+msgstr "リソースにアクセスする権限がありません"
+
+#: rhodecode/controllers/error.py:103
+msgid "You don't have permission to view this page"
+msgstr "このページを見る権限がありません"
+
+#: rhodecode/controllers/error.py:105
+msgid "The resource could not be found"
+msgstr "リソースが見つかりません"
+
+#: rhodecode/controllers/error.py:107
+msgid ""
+"The server encountered an unexpected condition which prevented it from "
+"fulfilling the request."
+msgstr ""
+
+#: rhodecode/controllers/feed.py:49
+#, python-format
+msgid "Changes on %s repository"
+msgstr "%s リポジトリでの変更"
+
+#: rhodecode/controllers/feed.py:50
+#, python-format
+msgid "%s %s feed"
+msgstr "%s %s フィード"
+
+#: rhodecode/controllers/feed.py:75
+msgid "commited on"
+msgstr "コミット"
+
+#: rhodecode/controllers/files.py:84
+#, fuzzy
+msgid "click here to add new file"
+msgstr "新しいファイルを追加"
+
+#: rhodecode/controllers/files.py:85
+#, python-format
+msgid "There are no files yet %s"
+msgstr "まだファイルがありません %s"
+
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:266
+#, python-format
+msgid "Edited %s via RhodeCode"
+msgstr "RhodeCode経由で %s を変更"
+
+#: rhodecode/controllers/files.py:271
+msgid "No changes"
+msgstr "変更点なし"
+
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
+#, python-format
+msgid "Successfully committed to %s"
+msgstr "%s へのコミットが成功しました"
+
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
+msgid "Error occurred during commit"
+msgstr "コミット中にエラーが発生しました"
+
+#: rhodecode/controllers/files.py:318
+#, python-format
+msgid "Added %s via RhodeCode"
+msgstr "RhodeCode経由で %s を追加"
+
+#: rhodecode/controllers/files.py:332
+msgid "No content"
+msgstr "内容がありません"
+
+#: rhodecode/controllers/files.py:336
+msgid "No filename"
+msgstr "ファイル名がありません"
+
+#: rhodecode/controllers/files.py:378
+msgid "downloads disabled"
+msgstr "ダウンロードは無効化されています"
+
+#: rhodecode/controllers/files.py:389
+#, python-format
+msgid "Unknown revision %s"
+msgstr "%s は未知のリビジョンです"
+
+#: rhodecode/controllers/files.py:391
+msgid "Empty repository"
+msgstr "空のリポジトリ"
+
+#: rhodecode/controllers/files.py:393
+msgid "Unknown archive type"
+msgstr "未知のアーカイブ種別です"
+
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
+msgid "Changesets"
+msgstr "チェンジセット"
+
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
+msgid "Branches"
+msgstr "ブランチ"
+
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
+msgid "Tags"
+msgstr "タグ"
+
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"%s "
+"リポジトリはDB内に見つかりませんでした。おそらくファイルシステム上で作られたか名前が変更されたためです。リポジトリをもう一度チェックするためにアプリケーションを立ち上げ直してください。"
+
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the file system please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"%s "
+"リポジトリはDB内に見つかりませんでした。おそらくファイルシステム上で作られたか名前が変更されたためです。リポジトリをもう一度チェックするためにアプリケーションを立ち上げ直してください。"
+
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr "リポジトリ %s を %s としてフォーク"
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr "リポジトリ %s のフォーク中にエラーが発生しました"
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+msgid "public journal"
+msgstr "公開ジャーナル"
+
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr "ジャーナル"
+
+#: rhodecode/controllers/login.py:143
+msgid "You have successfully registered into rhodecode"
+msgstr "rhodecodeへの登録を受け付けました"
+
+#: rhodecode/controllers/login.py:164
+msgid "Your password reset link was sent"
+msgstr "パスワードリセットのリンクを送信しました"
+
+#: rhodecode/controllers/login.py:184
+msgid ""
+"Your password reset was successful, new password has been sent to your "
+"email"
+msgstr "パスワードをリセットしました。新しいパスワードをあなたのメールアドレスに送りました"
+
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+msgid "Bookmarks"
+msgstr "ブックマーク"
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+#, fuzzy
+msgid "error during creation of pull request"
+msgstr "ユーザー %s の作成中にエラーが発生しました"
+
+#: rhodecode/controllers/pullrequests.py:181
+msgid "Successfully opened new pull request"
+msgstr "新しいプルリクエストを作成しました"
+
+#: rhodecode/controllers/pullrequests.py:184
+msgid "Error occurred during sending pull request"
+msgstr "プルリクエストの作成中にエラーが発生しました"
+
+#: rhodecode/controllers/pullrequests.py:217
+#, fuzzy
+msgid "Successfully deleted pull request"
+msgstr "新しいプルリクエストを作成しました"
+
+#: rhodecode/controllers/search.py:131
+msgid "Invalid search query. Try quoting it."
+msgstr "無効な検索クエリーです。\\\"で囲んで下さい"
+
+#: rhodecode/controllers/search.py:136
+msgid "There is no index to search in. Please run whoosh indexer"
+msgstr "検索するためのインデックスがありません。whooshでインデックスを作成して下さい"
+
+#: rhodecode/controllers/search.py:140
+msgid "An error occurred during this search operation"
+msgstr "検索を実行する際にエラーがおきました"
+
+#: rhodecode/controllers/settings.py:107
+#: rhodecode/controllers/admin/repos.py:266
+#, python-format
+msgid "Repository %s updated successfully"
+msgstr "リポジトリ %s の更新に成功しました"
+
+#: rhodecode/controllers/settings.py:125
+#: rhodecode/controllers/admin/repos.py:284
+#, python-format
+msgid "error occurred during update of repository %s"
+msgstr "リポジトリ %s の更新中にエラーが発生しました"
+
+#: rhodecode/controllers/settings.py:143
+#: rhodecode/controllers/admin/repos.py:302
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was moved or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"%s "
+"リポジトリはDB内に見つかりませんでした。おそらくファイルシステム上で作られたか名前が変更されたためです。リポジトリをもう一度チェックするためにアプリケーションを立ち上げ直してください。"
+
+#: rhodecode/controllers/settings.py:155
+#: rhodecode/controllers/admin/repos.py:314
+#, python-format
+msgid "deleted repository %s"
+msgstr "リポジトリ %s を削除しました"
+
+#: rhodecode/controllers/settings.py:159
+#: rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
+#, python-format
+msgid "An error occurred during deletion of %s"
+msgstr "リポジトリ %s の削除中にエラーが発生しました"
+
+#: rhodecode/controllers/summary.py:138
+msgid "No data loaded yet"
+msgstr ""
+
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
+msgid "Statistics are disabled for this repository"
+msgstr "このリポジトリの統計は無効化されています"
+
+#: rhodecode/controllers/admin/ldap_settings.py:50
+msgid "BASE"
+msgstr "BASE"
+
+#: rhodecode/controllers/admin/ldap_settings.py:51
+msgid "ONELEVEL"
+msgstr "ONELEVEL"
+
+#: rhodecode/controllers/admin/ldap_settings.py:52
+msgid "SUBTREE"
+msgstr "SUBTREE"
+
+#: rhodecode/controllers/admin/ldap_settings.py:56
+msgid "NEVER"
+msgstr "NEVER"
+
+#: rhodecode/controllers/admin/ldap_settings.py:57
+msgid "ALLOW"
+msgstr "ALLOW"
+
+#: rhodecode/controllers/admin/ldap_settings.py:58
+msgid "TRY"
+msgstr "TRY"
+
+#: rhodecode/controllers/admin/ldap_settings.py:59
+msgid "DEMAND"
+msgstr "DEMAND"
+
+#: rhodecode/controllers/admin/ldap_settings.py:60
+msgid "HARD"
+msgstr "HARD"
+
+#: rhodecode/controllers/admin/ldap_settings.py:64
+msgid "No encryption"
+msgstr "暗号化なし"
+
+#: rhodecode/controllers/admin/ldap_settings.py:65
+msgid "LDAPS connection"
+msgstr "LDAPS接続"
+
+#: rhodecode/controllers/admin/ldap_settings.py:66
+msgid "START_TLS on LDAP connection"
+msgstr "LDAP接続でSTART_TLSを使用"
+
+#: rhodecode/controllers/admin/ldap_settings.py:126
+msgid "Ldap settings updated successfully"
+msgstr "LDAP設定を更新しました"
+
+#: rhodecode/controllers/admin/ldap_settings.py:130
+msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
+msgstr "LDAPを有効にできませんでした。\"python-ldap\"ライブラリがありません。"
+
+#: rhodecode/controllers/admin/ldap_settings.py:147
+msgid "error occurred during update of ldap settings"
+msgstr "LDAP設定の更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/permissions.py:59
+msgid "None"
+msgstr "なし"
+
+#: rhodecode/controllers/admin/permissions.py:60
+msgid "Read"
+msgstr "読込"
+
+#: rhodecode/controllers/admin/permissions.py:61
+msgid "Write"
+msgstr "書込"
+
+#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/templates/admin/ldap/ldap.html:9
+#: rhodecode/templates/admin/permissions/permissions.html:9
+#: rhodecode/templates/admin/repos/repo_add.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:9
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
+#: rhodecode/templates/admin/settings/hooks.html:9
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/admin/users/user_add.html:8
+#: rhodecode/templates/admin/users/user_edit.html:9
+#: rhodecode/templates/admin/users/user_edit.html:122
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/admin/users_groups/users_group_add.html:8
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+#: rhodecode/templates/base/base.html:197
+#: rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339
+#: rhodecode/templates/base/base.html:341
+msgid "Admin"
+msgstr "管理"
+
+#: rhodecode/controllers/admin/permissions.py:65
+msgid "disabled"
+msgstr "無効にする"
+
+#: rhodecode/controllers/admin/permissions.py:67
+msgid "allowed with manual account activation"
+msgstr "手動でアカウントを有効にする"
+
+#: rhodecode/controllers/admin/permissions.py:69
+msgid "allowed with automatic account activation"
+msgstr "自動でアカウントを有効にする"
+
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
+msgid "Disabled"
+msgstr "無効"
+
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
+msgid "Enabled"
+msgstr "有効"
+
+#: rhodecode/controllers/admin/permissions.py:116
+msgid "Default permissions updated successfully"
+msgstr "デフォルトの権限を更新しました"
+
+#: rhodecode/controllers/admin/permissions.py:130
+msgid "error occurred during update of permissions"
+msgstr "権限の更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:192
+#, python-format
+msgid "created repository %s from %s"
+msgstr "リポジトリ %s を %s から作成"
+
+#: rhodecode/controllers/admin/repos.py:196
+#, python-format
+msgid "created repository %s"
+msgstr "リポジトリ %s を作成しました"
+
+#: rhodecode/controllers/admin/repos.py:227
+#, python-format
+msgid "error occurred during creation of repository %s"
+msgstr "リポジトリ %s を作成中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:319
+#, python-format
+msgid "Cannot delete %s it still contains attached forks"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:348
+msgid "An error occurred during deletion of repository user"
+msgstr "リポジトリユーザーの削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:367
+msgid "An error occurred during deletion of repository users groups"
+msgstr "リポジトリユーザーグループの削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:385
+msgid "An error occurred during deletion of repository stats"
+msgstr "リポジトリステートの削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:402
+msgid "An error occurred during cache invalidation"
+msgstr "キャッシュの無効化時にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:422
+#, fuzzy
+msgid "An error occurred during unlocking"
+msgstr "メールの保存時にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:442
+msgid "Updated repository visibility in public journal"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:446
+msgid "An error occurred during setting this repository in public journal"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
+msgid "Token mismatch"
+msgstr "トークンが合いません"
+
+#: rhodecode/controllers/admin/repos.py:464
+msgid "Pulled from remote location"
+msgstr "リモートから取得"
+
+#: rhodecode/controllers/admin/repos.py:466
+msgid "An error occurred during pull from remote location"
+msgstr "リモートから取得中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr "ありません"
+
+#: rhodecode/controllers/admin/repos.py:484
+#, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr "%s リポジトリを %s のフォークとして印をつける"
+
+#: rhodecode/controllers/admin/repos.py:488
+msgid "An error occurred during this operation"
+msgstr "操作中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:116
+#, python-format
+msgid "created repos group %s"
+msgstr "リポジトリグループ %s を作成しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:129
+#, python-format
+msgid "error occurred during creation of repos group %s"
+msgstr "リポジトリグループ %s を作成中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:163
+#, python-format
+msgid "updated repos group %s"
+msgstr "リポジトリグループ %s を更新しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:176
+#, python-format
+msgid "error occurred during update of repos group %s"
+msgstr "リポジトリグループ %s を更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:194
+#, python-format
+msgid "This group contains %s repositores and cannot be deleted"
+msgstr "このグループは %s リポジトリを含んでいるため削除出来ません"
+
+#: rhodecode/controllers/admin/repos_groups.py:202
+#, python-format
+msgid "removed repos group %s"
+msgstr "リポジトリグループ %s を削除しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr "サブグループを含んでいるため、このグループを削除できません"
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
+#, python-format
+msgid "error occurred during deletion of repos group %s"
+msgstr "リポジトリグループの削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:238
+msgid "An error occurred during deletion of group user"
+msgstr "グループユーザーを削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:121
+#, python-format
+msgid "Repositories successfully rescanned added: %s,removed: %s"
+msgstr "リポジトリを再度スキャンしました。 追加: %s 削除: %s"
+
+#: rhodecode/controllers/admin/settings.py:129
+msgid "Whoosh reindex task scheduled"
+msgstr "Whooshの再インデックスタスクを予定に入れました"
+
+#: rhodecode/controllers/admin/settings.py:160
+msgid "Updated application settings"
+msgstr "アプリケーション設定を更新しました"
+
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
+msgid "error occurred during updating application settings"
+msgstr "アプリケーション設定を更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/settings.py:200
+#, fuzzy
+msgid "Updated visualisation settings"
+msgstr "アプリケーション設定を更新しました"
+
+#: rhodecode/controllers/admin/settings.py:205
+#, fuzzy
+msgid "error occurred during updating visualisation settings"
+msgstr "アプリケーション設定を更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/settings.py:271
+#, fuzzy
+msgid "Updated VCS settings"
+msgstr "Mercurialの設定を更新しました"
+
+#: rhodecode/controllers/admin/settings.py:285
+msgid "Added new hook"
+msgstr "新しいフックを追加しました"
+
+#: rhodecode/controllers/admin/settings.py:297
+msgid "Updated hooks"
+msgstr "フックを更新しました"
+
+#: rhodecode/controllers/admin/settings.py:301
+msgid "error occurred during hook creation"
+msgstr "フックの作成時にエラーが発生しました"
+
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr "メールのタスクを作成しました"
+
+#: rhodecode/controllers/admin/settings.py:375
+msgid "You can't edit this user since it's crucial for entire application"
+msgstr "このユーザーを編集出来ません。このユーザーはアプリケーションにとって必要不可欠です。"
+
+#: rhodecode/controllers/admin/settings.py:406
+msgid "Your account was updated successfully"
+msgstr "アカウントを更新しました"
+
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
+#, python-format
+msgid "error occurred during update of user %s"
+msgstr "ユーザー %s の更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users.py:130
+#, python-format
+msgid "created user %s"
+msgstr "ユーザー %s を作成しました"
+
+#: rhodecode/controllers/admin/users.py:142
+#, python-format
+msgid "error occurred during creation of user %s"
+msgstr "ユーザー %s の作成中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users.py:171
+msgid "User updated successfully"
+msgstr "ユーザーの更新に成功しました"
+
+#: rhodecode/controllers/admin/users.py:207
+msgid "successfully deleted user"
+msgstr "ユーザーの削除に成功しました"
+
+#: rhodecode/controllers/admin/users.py:212
+msgid "An error occurred during deletion of user"
+msgstr "ユーザーの削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users.py:226
+msgid "You can't edit this user"
+msgstr "このユーザーは編集できません"
+
+#: rhodecode/controllers/admin/users.py:266
+msgid "Granted 'repository create' permission to user"
+msgstr "ユーザーに 'リポジトリ作成' 権限を与えました"
+
+#: rhodecode/controllers/admin/users.py:271
+msgid "Revoked 'repository create' permission to user"
+msgstr "ユーザーの 'リポジトリ作成' 権限を取り消しました"
+
+#: rhodecode/controllers/admin/users.py:277
+#, fuzzy
+msgid "Granted 'repository fork' permission to user"
+msgstr "ユーザーに 'リポジトリ作成' 権限を与えました"
+
+#: rhodecode/controllers/admin/users.py:282
+#, fuzzy
+msgid "Revoked 'repository fork' permission to user"
+msgstr "ユーザーの 'リポジトリ作成' 権限を取り消しました"
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+#, fuzzy
+msgid "An error occurred during permissions saving"
+msgstr "メールの保存時にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr "ユーザーにメール %s を追加しました"
+
+#: rhodecode/controllers/admin/users.py:309
+msgid "An error occurred during email saving"
+msgstr "メールの保存時にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users.py:319
+msgid "Removed email from user"
+msgstr "ユーザーからメールを削除しました"
+
+#: rhodecode/controllers/admin/users_groups.py:84
+#, python-format
+msgid "created users group %s"
+msgstr "ユーザーグループ %s を作成しました"
+
+#: rhodecode/controllers/admin/users_groups.py:95
+#, python-format
+msgid "error occurred during creation of users group %s"
+msgstr "ユーザーグループ %s の作成中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users_groups.py:135
+#, python-format
+msgid "updated users group %s"
+msgstr "ユーザーグループ %s を更新しました"
+
+#: rhodecode/controllers/admin/users_groups.py:157
+#, python-format
+msgid "error occurred during update of users group %s"
+msgstr "ユーザーグループ %s の更新中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users_groups.py:174
+msgid "successfully deleted users group"
+msgstr "ユーザーグループ"
+
+#: rhodecode/controllers/admin/users_groups.py:179
+msgid "An error occurred during deletion of users group"
+msgstr "ユーザーグループの削除中にエラーが発生しました"
+
+#: rhodecode/controllers/admin/users_groups.py:233
+#, fuzzy
+msgid "Granted 'repository create' permission to users group"
+msgstr "ユーザーに 'リポジトリ作成' 権限を与えました"
+
+#: rhodecode/controllers/admin/users_groups.py:238
+#, fuzzy
+msgid "Revoked 'repository create' permission to users group"
+msgstr "ユーザーの 'リポジトリ作成' 権限を取り消しました"
+
+#: rhodecode/controllers/admin/users_groups.py:244
+#, fuzzy
+msgid "Granted 'repository fork' permission to users group"
+msgstr "ユーザーに 'リポジトリ作成' 権限を与えました"
+
+#: rhodecode/controllers/admin/users_groups.py:249
+#, fuzzy
+msgid "Revoked 'repository fork' permission to users group"
+msgstr "ユーザーの 'リポジトリ作成' 権限を取り消しました"
+
+#: rhodecode/lib/auth.py:499
+msgid "You need to be a registered user to perform this action"
+msgstr "このアクションを実行するためには登録ユーザーである必要があります"
+
+#: rhodecode/lib/auth.py:540
+msgid "You need to be a signed in to view this page"
+msgstr "このページを閲覧するためにはサインインが必要です"
+
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
+msgstr ""
+
+#: rhodecode/lib/diffs.py:96
+msgid "No changes detected"
+msgstr "検出された変更はありません"
+
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
+msgstr "%a, %d %b %Y %H:%M:%S"
+
+#: rhodecode/lib/helpers.py:484
+msgid "True"
+msgstr "True"
+
+#: rhodecode/lib/helpers.py:488
+msgid "False"
+msgstr "False"
+
+#: rhodecode/lib/helpers.py:532
+msgid "Changeset not found"
+msgstr "リビジョンが見つかりません"
+
+#: rhodecode/lib/helpers.py:555
+#, python-format
+msgid "Show all combined changesets %s->%s"
+msgstr "%s から %s までのすべてのチェンジセットを表示"
+
+#: rhodecode/lib/helpers.py:561
+msgid "compare view"
+msgstr "比較の表示"
+
+#: rhodecode/lib/helpers.py:581
+msgid "and"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:582
+#, python-format
+msgid "%s more"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
+msgid "revisions"
+msgstr "リビジョン"
+
+#: rhodecode/lib/helpers.py:606
+msgid "fork name "
+msgstr "フォーク名 "
+
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr "プルリクエスト #%s"
+
+#: rhodecode/lib/helpers.py:626
+msgid "[deleted] repository"
+msgstr "リポジトリを[削除]"
+
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
+msgid "[created] repository"
+msgstr "リポジトリを[作成]"
+
+#: rhodecode/lib/helpers.py:630
+msgid "[created] repository as fork"
+msgstr "フォークしてリポジトリを[作成]"
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
+msgid "[forked] repository"
+msgstr "リポジトリを[フォーク]"
+
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
+msgid "[updated] repository"
+msgstr "リポジトリを[更新]"
+
+#: rhodecode/lib/helpers.py:636
+msgid "[delete] repository"
+msgstr "リポジトリを[削除]"
+
+#: rhodecode/lib/helpers.py:644
+msgid "[created] user"
+msgstr "ユーザーを[作成]"
+
+#: rhodecode/lib/helpers.py:646
+msgid "[updated] user"
+msgstr "ユーザーを[更新]"
+
+#: rhodecode/lib/helpers.py:648
+msgid "[created] users group"
+msgstr "ユーザーグループを[作成]"
+
+#: rhodecode/lib/helpers.py:650
+msgid "[updated] users group"
+msgstr "ユーザーグループを[更新]"
+
+#: rhodecode/lib/helpers.py:652
+msgid "[commented] on revision in repository"
+msgstr "リポジトリのリビジョンに[コメント]"
+
+#: rhodecode/lib/helpers.py:654
+#, fuzzy
+msgid "[commented] on pull request for"
+msgstr "プルリクエストに[コメント]"
+
+#: rhodecode/lib/helpers.py:656
+#, fuzzy
+msgid "[closed] pull request for"
+msgstr "プルリクエストに[コメント]"
+
+#: rhodecode/lib/helpers.py:658
+msgid "[pushed] into"
+msgstr "[プッシュ]"
+
+#: rhodecode/lib/helpers.py:660
+msgid "[committed via RhodeCode] into repository"
+msgstr "リポジトリに[RhodeCode経由でコミット]"
+
+#: rhodecode/lib/helpers.py:662
+msgid "[pulled from remote] into repository"
+msgstr "リポジトリに[リモートからプル]"
+
+#: rhodecode/lib/helpers.py:664
+msgid "[pulled] from"
+msgstr "[プル]"
+
+#: rhodecode/lib/helpers.py:666
+msgid "[started following] repository"
+msgstr "リポジトリの[フォローを開始]"
+
+#: rhodecode/lib/helpers.py:668
+msgid "[stopped following] repository"
+msgstr "リポジトリの[フォローを停止]"
+
+#: rhodecode/lib/helpers.py:840
+#, python-format
+msgid " and %s more"
+msgstr " と %s 以上"
+
+#: rhodecode/lib/helpers.py:844
+msgid "No Files"
+msgstr ""
+
+#: rhodecode/lib/utils2.py:335
+#, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] "%d 年"
+
+#: rhodecode/lib/utils2.py:336
+#, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] "%d ヶ月"
+
+#: rhodecode/lib/utils2.py:337
+#, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] "%d 日"
+
+#: rhodecode/lib/utils2.py:338
+#, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] "%d 時間"
+
+#: rhodecode/lib/utils2.py:339
+#, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] "%d 分"
+
+#: rhodecode/lib/utils2.py:340
+#, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] "%d 秒"
+
+#: rhodecode/lib/utils2.py:355
+#, python-format
+msgid "%s ago"
+msgstr "%s 前"
+
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
+msgstr "%s と %s 前"
+
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
+msgstr "ちょうどいま"
+
+#: rhodecode/lib/celerylib/tasks.py:269
+msgid "password reset link"
+msgstr "パスワードリセットのリンク"
+
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
+msgstr ""
+
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
+msgstr ""
+
+#: rhodecode/model/db.py:1140
+msgid "Repository no access"
+msgstr ""
+
+#: rhodecode/model/db.py:1141
+msgid "Repository read access"
+msgstr ""
+
+#: rhodecode/model/db.py:1142
+msgid "Repository write access"
+msgstr ""
+
+#: rhodecode/model/db.py:1143
+msgid "Repository admin access"
+msgstr ""
+
+#: rhodecode/model/db.py:1145
+msgid "Repositories Group no access"
+msgstr ""
+
+#: rhodecode/model/db.py:1146
+msgid "Repositories Group read access"
+msgstr ""
+
+#: rhodecode/model/db.py:1147
+msgid "Repositories Group write access"
+msgstr ""
+
+#: rhodecode/model/db.py:1148
+msgid "Repositories Group admin access"
+msgstr ""
+
+#: rhodecode/model/db.py:1150
+msgid "RhodeCode Administrator"
+msgstr ""
+
+#: rhodecode/model/db.py:1151
+msgid "Repository creation disabled"
+msgstr ""
+
+#: rhodecode/model/db.py:1152
+msgid "Repository creation enabled"
+msgstr ""
+
+#: rhodecode/model/db.py:1153
+msgid "Repository forking disabled"
+msgstr ""
+
+#: rhodecode/model/db.py:1154
+msgid "Repository forking enabled"
+msgstr ""
+
+#: rhodecode/model/db.py:1155
+msgid "Register disabled"
+msgstr ""
+
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
+msgstr "未レビュー"
+
+#: rhodecode/model/db.py:1580
+msgid "Approved"
+msgstr "承認"
+
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
+msgstr "却下"
+
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
+msgstr "レビュー中"
+
+#: rhodecode/model/forms.py:43
+msgid "Please enter a login"
+msgstr "ログイン名を入力してください"
+
+#: rhodecode/model/forms.py:44
+#, python-format
+msgid "Enter a value %(min)i characters long or more"
+msgstr "%(min)i 文字以上必要です"
+
+#: rhodecode/model/forms.py:52
+msgid "Please enter a password"
+msgstr "パスワードを入力してください"
+
+#: rhodecode/model/forms.py:53
+#, python-format
+msgid "Enter %(min)i characters or more"
+msgstr "%(min)i 文字以上必要です"
+
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr "コミットに対するコメント"
+
+#: rhodecode/model/notification.py:221
+msgid "sent message"
+msgstr ""
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr ""
+
+#: rhodecode/model/notification.py:223
+msgid "registered in RhodeCode"
+msgstr ""
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
+msgstr ""
+
+#: rhodecode/model/notification.py:225
+msgid "commented on pull request"
+msgstr ""
+
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+msgid "latest tip"
+msgstr "最新のtip"
+
+#: rhodecode/model/user.py:230
+msgid "new user registration"
+msgstr "新規ユーザー登録"
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
+msgid "You can't Edit this user since it's crucial for entire application"
+msgstr ""
+
+#: rhodecode/model/user.py:323
+msgid "You can't remove this user since it's crucial for entire application"
+msgstr ""
+
+#: rhodecode/model/user.py:329
+#, python-format
+msgid ""
+"user \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories. %s"
+msgstr ""
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, python-format
+msgid "Username \"%(username)s\" already exists"
+msgstr "ユーザー名 \"%(username)s\" はすでに使われています"
+
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr "ユーザー名 \"%(username)s\" は許可されていません"
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with alphanumeric character"
+msgstr "ユーザー名はアルファベット、アンダースコア(_)、ピリオド(.)、ダッシュ(-)しか使えません。また、アルファベットから始まる必要があります"
+
+#: rhodecode/model/validators.py:114
+#, python-format
+msgid "Username %(username)s is not valid"
+msgstr "ユーザー名 %(username)s は不正です"
+
+#: rhodecode/model/validators.py:133
+msgid "Invalid users group name"
+msgstr "不正なユーザーグループ名です"
+
+#: rhodecode/model/validators.py:134
+#, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
+msgstr "ユーザーグループ \"%(usersgroup)s\" はすでに存在します"
+
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr ""
+"ユーザーグループ名はアルファベット、アンダースコア(_)、ピリオド(.)、ダッシュ(-)しか使えません。また、アルファベットから始まる必要があります"
+" "
+
+#: rhodecode/model/validators.py:174
+msgid "Cannot assign this group as parent"
+msgstr "このグループは親にできません"
+
+#: rhodecode/model/validators.py:175
+#, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr "グループ \"%(group_name)s\" はすでに存在します"
+
+#: rhodecode/model/validators.py:177
+#, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr "グループ名 \"%(group_name)s\" を持つリポジトリはすでに存在します"
+
+#: rhodecode/model/validators.py:235
+msgid "Invalid characters (non-ascii) in password"
+msgstr "パスワードに利用出来ない文字列(non-ascii)です"
+
+#: rhodecode/model/validators.py:250
+msgid "Passwords do not match"
+msgstr "パスワードが一致しません"
+
+#: rhodecode/model/validators.py:267
+msgid "invalid password"
+msgstr "不正なパスワードです"
+
+#: rhodecode/model/validators.py:268
+msgid "invalid user name"
+msgstr "不正なユーザー名です"
+
+#: rhodecode/model/validators.py:269
+msgid "Your account is disabled"
+msgstr "アカウントは無効です"
+
+#: rhodecode/model/validators.py:313
+#, python-format
+msgid "Repository name %(repo)s is disallowed"
+msgstr "リポジトリ名 %(repo)s は許可されていません"
+
+#: rhodecode/model/validators.py:315
+#, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr "リポジトリ %(repo)s はすでに存在します"
+
+#: rhodecode/model/validators.py:316
+#, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr "リポジトリ \"%(repo)s\" は グループ \"%(group)s\" にすでに存在します"
+
+#: rhodecode/model/validators.py:318
+#, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
+msgstr "リポジトリグループ名 \"%(repo)s\" はすでに存在します"
+
+#: rhodecode/model/validators.py:431
+msgid "invalid clone url"
+msgstr "無効なクローンURIです"
+
+#: rhodecode/model/validators.py:432
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
+msgstr ""
+
+#: rhodecode/model/validators.py:457
+msgid "Fork have to be the same type as parent"
+msgstr "フォークは親と同じタイプの必要があります"
+
+#: rhodecode/model/validators.py:478
+msgid "This username or users group name is not valid"
+msgstr "ユーザー名かユーザーグループが不正です"
+
+#: rhodecode/model/validators.py:562
+msgid "This is not a valid path"
+msgstr "不正なパスです"
+
+#: rhodecode/model/validators.py:577
+msgid "This e-mail address is already taken"
+msgstr "このメールアドレスはすでに取得されています"
+
+#: rhodecode/model/validators.py:597
+#, python-format
+msgid "e-mail \"%(email)s\" does not exist."
+msgstr "メールアドレス \"%(email)s\" は存在しません"
+
+#: rhodecode/model/validators.py:634
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
+msgstr "LDAPのこのCNに対するログイン属性は必須です。 - これは \"ユーザー名\" と同じです"
+
+#: rhodecode/model/validators.py:653
+#, python-format
+msgid "Revisions %(revs)s are already part of pull request or have set status"
+msgstr ""
+
+#: rhodecode/templates/index.html:3
+msgid "Dashboard"
+msgstr "ダッシュボード"
+
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
+msgid "quick filter..."
+msgstr "クイックフィルタ..."
+
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
+msgid "repositories"
+msgstr "リポジトリ"
+
+#: rhodecode/templates/index_base.html:13
+#: rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
+msgstr "リポジトリの追加"
+
+#: rhodecode/templates/index_base.html:29
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
+#: rhodecode/templates/admin/users_groups/users_group_add.html:32
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:33
+msgid "Group name"
+msgstr "グループ名"
+
+#: rhodecode/templates/index_base.html:30
+#: rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142
+#: rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
+msgid "Description"
+msgstr "説明"
+
+#: rhodecode/templates/index_base.html:40
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
+msgid "Repositories group"
+msgstr "リポジトリグループ"
+
+#: rhodecode/templates/index_base.html:70
+#: rhodecode/templates/index_base.html:166
+#: rhodecode/templates/admin/repos/repo_add_base.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:32
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
+#: rhodecode/templates/settings/repo_settings.html:31
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36
+#: rhodecode/templates/tags/tags_data.html:6
+msgid "Name"
+msgstr "名前"
+
+#: rhodecode/templates/index_base.html:72
+msgid "Last change"
+msgstr "最後の変更時刻"
+
+#: rhodecode/templates/index_base.html:73
+#: rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
+msgid "Tip"
+msgstr "Tip"
+
+#: rhodecode/templates/index_base.html:74
+#: rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
+msgid "Owner"
+msgstr "所有者"
+
+#: rhodecode/templates/index_base.html:75
+#: rhodecode/templates/summary/summary.html:48
+#: rhodecode/templates/summary/summary.html:51
+msgid "RSS"
+msgstr "RSS"
+
+#: rhodecode/templates/index_base.html:76
+msgid "Atom"
+msgstr "Atom"
+
+#: rhodecode/templates/index_base.html:110
+#: rhodecode/templates/index_base.html:112
+#, python-format
+msgid "Subscribe to %s rss feed"
+msgstr "%s の RSS フィードを購読"
+
+#: rhodecode/templates/index_base.html:117
+#: rhodecode/templates/index_base.html:119
+#, python-format
+msgid "Subscribe to %s atom feed"
+msgstr "%s の ATOM フィードを購読"
+
+#: rhodecode/templates/index_base.html:140
+msgid "Group Name"
+msgstr "グループ名"
+
+#: rhodecode/templates/index_base.html:158
+#: rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
+msgstr "昇順で並び換え"
+
+#: rhodecode/templates/index_base.html:159
+#: rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
+msgstr "降順で並び替え"
+
+#: rhodecode/templates/index_base.html:169
+msgid "Last Change"
+msgstr "最後の変更点"
+
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
+msgstr "レコードが見つかりません"
+
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
+msgstr "データエラー"
+
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+msgid "Loading..."
+msgstr "読み込み中..."
+
+#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
+msgid "Sign In"
+msgstr "サインイン"
+
+#: rhodecode/templates/login.html:21
+msgid "Sign In to"
+msgstr ""
+
+#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
+#: rhodecode/templates/admin/admin_log.html:5
+#: rhodecode/templates/admin/users/user_add.html:32
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
+msgid "Username"
+msgstr "ユーザー名"
+
+#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
+#: rhodecode/templates/admin/ldap/ldap.html:46
+#: rhodecode/templates/admin/users/user_add.html:41
+#: rhodecode/templates/base/base.html:92
+msgid "Password"
+msgstr "パスワード"
+
+#: rhodecode/templates/login.html:50
+msgid "Remember me"
+msgstr "次回から自動的にサインイン"
+
+#: rhodecode/templates/login.html:60
+msgid "Forgot your password ?"
+msgstr "パスワードを忘れた場合はこちら"
+
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
+msgid "Don't have an account ?"
+msgstr "アカウントを持っていない場合はこちら"
+
+#: rhodecode/templates/password_reset.html:5
+msgid "Reset your password"
+msgstr "パスワードリセット"
+
+#: rhodecode/templates/password_reset.html:11
+msgid "Reset your password to"
+msgstr "パスワードリセット"
+
+#: rhodecode/templates/password_reset.html:21
+msgid "Email address"
+msgstr "メールアドレス"
+
+#: rhodecode/templates/password_reset.html:30
+msgid "Reset my password"
+msgstr "パスワードをリセットする"
+
+#: rhodecode/templates/password_reset.html:31
+msgid "Password reset link will be send to matching email address"
+msgstr "該当するメールアドレスにパスワードリセットのリンクを送信します"
+
+#: rhodecode/templates/register.html:5 rhodecode/templates/register.html:74
+msgid "Sign Up"
+msgstr "サインアップ"
+
+#: rhodecode/templates/register.html:11
+msgid "Sign Up to"
+msgstr "サインアップ"
+
+#: rhodecode/templates/register.html:38
+msgid "Re-enter password"
+msgstr "パスワード再入力"
+
+#: rhodecode/templates/register.html:47
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
+msgid "First Name"
+msgstr "名前"
+
+#: rhodecode/templates/register.html:56
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
+msgid "Last Name"
+msgstr "名字"
+
+#: rhodecode/templates/register.html:65
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
+msgid "Email"
+msgstr "メールアドレス"
+
+#: rhodecode/templates/register.html:76
+msgid "Your account will be activated right after registration"
+msgstr "アカウントは登録後にアクティブになります"
+
+#: rhodecode/templates/register.html:78
+msgid "Your account must wait for activation by administrator"
+msgstr "アカウントは管理者のアクティベーションを待つ必要があります"
+
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
+msgid "Private repository"
+msgstr "非公開リポジトリ"
+
+#: rhodecode/templates/repo_switcher_list.html:16
+msgid "Public repository"
+msgstr "公開リポジトリ"
+
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr "ブランチ"
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr "まだブランチがありません"
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr "タグ"
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr "まだタグがありません"
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr "ブックマーク"
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+msgid "There are no bookmarks yet"
+msgstr "まだブックマークがありません"
+
+#: rhodecode/templates/admin/admin.html:5
+#: rhodecode/templates/admin/admin.html:9
+msgid "Admin journal"
+msgstr "管理者ジャーナル"
+
+#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
+msgid "Action"
+msgstr "アクション"
+
+#: rhodecode/templates/admin/admin_log.html:7
+msgid "Repository"
+msgstr "リポジトリ"
+
+#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37
+#: rhodecode/templates/tags/tags_data.html:7
+msgid "Date"
+msgstr "日時"
+
+#: rhodecode/templates/admin/admin_log.html:9
+msgid "From IP"
+msgstr "アクセス元IPアドレス"
+
+#: rhodecode/templates/admin/admin_log.html:53
+msgid "No actions yet"
+msgstr "まだアクションがありません"
+
+#: rhodecode/templates/admin/ldap/ldap.html:5
+msgid "LDAP administration"
+msgstr "LDAP管理"
+
+#: rhodecode/templates/admin/ldap/ldap.html:11
+msgid "Ldap"
+msgstr "LDAP"
+
+#: rhodecode/templates/admin/ldap/ldap.html:28
+msgid "Connection settings"
+msgstr "接続設定"
+
+#: rhodecode/templates/admin/ldap/ldap.html:30
+msgid "Enable LDAP"
+msgstr "LDAPを有効にする"
+
+#: rhodecode/templates/admin/ldap/ldap.html:34
+msgid "Host"
+msgstr "ホスト"
+
+#: rhodecode/templates/admin/ldap/ldap.html:38
+msgid "Port"
+msgstr "ポート"
+
+#: rhodecode/templates/admin/ldap/ldap.html:42
+msgid "Account"
+msgstr "アカウント"
+
+#: rhodecode/templates/admin/ldap/ldap.html:50
+msgid "Connection security"
+msgstr "接続のセキュリティ"
+
+#: rhodecode/templates/admin/ldap/ldap.html:54
+msgid "Certificate Checks"
+msgstr "証明書チェック"
+
+#: rhodecode/templates/admin/ldap/ldap.html:57
+msgid "Search settings"
+msgstr "検索設定"
+
+#: rhodecode/templates/admin/ldap/ldap.html:59
+msgid "Base DN"
+msgstr "Base DN"
+
+#: rhodecode/templates/admin/ldap/ldap.html:63
+msgid "LDAP Filter"
+msgstr "LDAPフィルター"
+
+#: rhodecode/templates/admin/ldap/ldap.html:67
+msgid "LDAP Search Scope"
+msgstr "LDAP検索範囲"
+
+#: rhodecode/templates/admin/ldap/ldap.html:70
+msgid "Attribute mappings"
+msgstr "属性マッピング"
+
+#: rhodecode/templates/admin/ldap/ldap.html:72
+msgid "Login Attribute"
+msgstr "ログイン属性"
+
+#: rhodecode/templates/admin/ldap/ldap.html:76
+msgid "First Name Attribute"
+msgstr "名前(First Name)属性"
+
+#: rhodecode/templates/admin/ldap/ldap.html:80
+msgid "Last Name Attribute"
+msgstr "名字(Last Name)属性"
+
+#: rhodecode/templates/admin/ldap/ldap.html:84
+msgid "E-mail Attribute"
+msgstr "メールアドレス属性"
+
+#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
+#: rhodecode/templates/admin/settings/hooks.html:73
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
+msgid "Save"
+msgstr "保存"
+
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr "通知"
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr "すべて"
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+msgid "Comments"
+msgstr "コメント"
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254
+#: rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr "プルリクエスト"
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr "すべて既読にする"
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+msgid "No notifications here yet"
+msgstr "通知はまだありません"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+msgid "Show notification"
+msgstr "通知を表示"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+msgid "Notifications"
+msgstr "通知"
+
+#: rhodecode/templates/admin/permissions/permissions.html:5
+msgid "Permissions administration"
+msgstr "権限管理"
+
+#: rhodecode/templates/admin/permissions/permissions.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
+msgid "Permissions"
+msgstr "権限設定"
+
+#: rhodecode/templates/admin/permissions/permissions.html:24
+msgid "Default permissions"
+msgstr "デフォルトの権限"
+
+#: rhodecode/templates/admin/permissions/permissions.html:31
+msgid "Anonymous access"
+msgstr "匿名アクセス"
+
+#: rhodecode/templates/admin/permissions/permissions.html:41
+msgid "Repository permission"
+msgstr "リポジトリの権限"
+
+#: rhodecode/templates/admin/permissions/permissions.html:49
+msgid ""
+"All default permissions on each repository will be reset to choosen "
+"permission, note that all custom default permission on repositories will "
+"be lost"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:50
+msgid "overwrite existing settings"
+msgstr "現在の設定を上書きする"
+
+#: rhodecode/templates/admin/permissions/permissions.html:55
+msgid "Registration"
+msgstr "登録"
+
+#: rhodecode/templates/admin/permissions/permissions.html:63
+msgid "Repository creation"
+msgstr "リポジトリ作成"
+
+#: rhodecode/templates/admin/permissions/permissions.html:71
+#, fuzzy
+msgid "Repository forking"
+msgstr "リポジトリ作成"
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
+msgid "set"
+msgstr "保存"
+
+#: rhodecode/templates/admin/repos/repo_add.html:5
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:5
+msgid "Add repository"
+msgstr "リポジトリの追加"
+
+#: rhodecode/templates/admin/repos/repo_add.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:11
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
+msgid "Repositories"
+msgstr "リポジトリ"
+
+#: rhodecode/templates/admin/repos/repo_add.html:13
+msgid "add new"
+msgstr "新規追加"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:20
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
+msgid "Clone from"
+msgstr "クローン元"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr "オプション:クローンするリポジトリのHTTP[S]のURLを指定します"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
+msgid "Repository group"
+msgstr "リポジトリグループ"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+msgid "Optionaly select a group to put this repository into."
+msgstr "オプション:このリポジトリが属するグループを選択します"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
+msgid "Type"
+msgstr "リポジトリのタイプ"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+msgid "Type of repository to create."
+msgstr "作成するリポジトリのタイプを指定します"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+msgid "Landing revision"
+msgstr "ランディングリビジョン"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr "ファイルページ、ダウンロード、検索、READMEのデフォルトのリビジョンを指定します"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr "短く要点を絞ってください。長い説明にはREADMEファイルを利用してください。"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr "非公開リポジトリはコラボレーターとして明示的に追加された人でないと見つけられません"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
+msgid "add"
+msgstr "追加"
+
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:9
+msgid "add new repository"
+msgstr "新しいリポジトリの追加"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:5
+msgid "Edit repository"
+msgstr "リポジトリを編集"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
+msgid "edit"
+msgstr "編集"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
+msgid "Clone uri"
+msgstr "クローンURI"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr "オプション: このリポジトリを配置するグループを選択します"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
+msgid "Enable statistics"
+msgstr "統計を有効にする"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr "概要ページの統計ウィンドウを有効にします"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
+msgid "Enable downloads"
+msgstr "ダウンロードを有効にする"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr "概要ページのダウンロードメニューを有効にします"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+#, fuzzy
+msgid "Enable locking"
+msgstr "有効にする"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+msgid "Change owner of this repository."
+msgstr "リポジトリの所有者を変更"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr "リセット"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
+msgid "Administration"
+msgstr "管理"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:155
+msgid "Statistics"
+msgstr "統計"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:159
+msgid "Reset current statistics"
+msgstr "現在の統計情報をリセットする"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:159
+msgid "Confirm to remove current statistics"
+msgstr "現在の統計情報をリセットしてもよろしいですか"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:162
+msgid "Fetched to rev"
+msgstr "収集するリビジョン"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
+msgstr "収集した統計情報"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:171
+msgid "Remote"
+msgstr "リモート"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:175
+msgid "Pull changes from remote location"
+msgstr "リモートから変更を取り込む"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:175
+msgid "Confirm to pull changes from remote side"
+msgstr "リモートから変更を取り込んでもよろしいですか?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:186
+msgid "Cache"
+msgstr "キャッシュ"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:190
+msgid "Invalidate repository cache"
+msgstr "リポジトリのキャッシュを無効化"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:190
+msgid "Confirm to invalidate repository cache"
+msgstr "リポジトリのキャッシュを無効化してもよろしいですか?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318
+#: rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr "公開ジャーナル"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
+msgid "Remove from public journal"
+msgstr "公開ジャーナルから削除する"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:203
+msgid "Add to public journal"
+msgstr "公開ジャーナルに追加する"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in "
+"public journal"
+msgstr "公開ジャーナルでは、このリポジトリに対して行った操作のすべてが公開されます"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+#, fuzzy
+msgid "Locking"
+msgstr "変更可能にする"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+#, fuzzy
+msgid "Confirm to unlock repository"
+msgstr "このリポジトリを削除しますか?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+#, fuzzy
+msgid "Confirm to lock repository"
+msgstr "このリポジトリを削除しますか?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+#, fuzzy
+msgid "Repository is not locked"
+msgstr "リポジトリロケーション"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+msgid "Set as fork of"
+msgstr "フォーク元の設定"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+msgid "Manually set this repository as a fork of another from the list"
+msgstr "このリポジトリをリスト中の他のリポジトリのフォークとして、手動で設定します"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
+msgid "Delete"
+msgstr "削除"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+msgid "Remove this repository"
+msgstr "このリポジトリを削除"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
+msgid "Confirm to delete this repository"
+msgstr "このリポジトリを削除しますか?"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be "
+"unaccesible for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem "
+"please do it manually"
+msgstr ""
+"このリポジトリはRhodeCodeとVCSシステムからアクセスされないような名前に、特別な方法で変更されます。\n"
+"もし、ファイルシステムから完全に削除したい場合、手動で行ってください"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
+msgid "none"
+msgstr "なし"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
+msgid "read"
+msgstr "読込"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
+msgid "write"
+msgstr "書込"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
+msgid "admin"
+msgstr "管理"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
+msgid "member"
+msgstr "メンバー"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr "非公開リポジトリ"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+msgid "default"
+msgstr "default"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
+msgid "revoke"
+msgstr "取消"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
+msgid "Add another member"
+msgstr "別のメンバーを追加"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
+msgid "Failed to remove user"
+msgstr "ユーザーの削除に失敗しました"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
+msgid "Failed to remove users group"
+msgstr "ユーザーグループの削除に失敗しました"
+
+#: rhodecode/templates/admin/repos/repos.html:5
+msgid "Repositories administration"
+msgstr "リポジトリ管理"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
+msgid "Groups"
+msgstr "グループ"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
+msgid "with"
+msgstr "と"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:5
+msgid "Add repos group"
+msgstr "リポジトリグループを追加"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:10
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:10
+msgid "Repos groups"
+msgstr "リポジトリグループ"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:12
+msgid "add new repos group"
+msgstr "新しいリポジトリグループを追加"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:50
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:50
+msgid "Group parent"
+msgstr "親グループ"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
+#: rhodecode/templates/admin/users/user_add.html:94
+#: rhodecode/templates/admin/users_groups/users_group_add.html:49
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
+msgid "save"
+msgstr "保存"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:5
+msgid "Edit repos group"
+msgstr "リポジトリグループを編集"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:12
+msgid "edit repos group"
+msgstr "リポジトリグループを編集"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
+msgid "Repositories groups administration"
+msgstr "リポジトリグループ管理"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:22
+msgid "ADD NEW GROUP"
+msgstr "新しいグループを追加"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
+msgid "Number of toplevel repositories"
+msgstr "トップレベルリポジトリの数"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
+msgstr "アクション"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr "削除"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#, python-format
+msgid "Confirm to delete this group: %s"
+msgstr "グループ %s を削除してもよろしいですか"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
+msgid "There are no repositories groups yet"
+msgstr "まだリポジトリグループがありません"
+
+#: rhodecode/templates/admin/settings/hooks.html:5
+#: rhodecode/templates/admin/settings/settings.html:5
+msgid "Settings administration"
+msgstr "設定管理"
+
+#: rhodecode/templates/admin/settings/hooks.html:9
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/settings/repo_settings.html:13
+msgid "Settings"
+msgstr "設定"
+
+#: rhodecode/templates/admin/settings/hooks.html:24
+msgid "Built in hooks - read only"
+msgstr "組み込みフック - 読み込み専用"
+
+#: rhodecode/templates/admin/settings/hooks.html:40
+msgid "Custom hooks"
+msgstr "カスタムフック"
+
+#: rhodecode/templates/admin/settings/hooks.html:56
+msgid "remove"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/hooks.html:88
+msgid "Failed to remove hook"
+msgstr "フックの削除に失敗しました"
+
+#: rhodecode/templates/admin/settings/settings.html:24
+msgid "Remap and rescan repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:32
+msgid "rescan option"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:38
+msgid ""
+"In case a repository was deleted from filesystem and there are leftovers "
+"in the database check this option to scan obsolete data in database and "
+"remove it."
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:39
+msgid "destroy old data"
+msgstr "古いデータを削除する"
+
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete "
+"if `destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
+msgid "Rescan repositories"
+msgstr "リポジトリを再チェック"
+
+#: rhodecode/templates/admin/settings/settings.html:52
+msgid "Whoosh indexing"
+msgstr "Whooshインデックス"
+
+#: rhodecode/templates/admin/settings/settings.html:60
+msgid "index build option"
+msgstr "インデックス作成時の設定"
+
+#: rhodecode/templates/admin/settings/settings.html:65
+msgid "build from scratch"
+msgstr "一度削除してから再度インデックスを作成"
+
+#: rhodecode/templates/admin/settings/settings.html:71
+msgid "Reindex"
+msgstr "再インデックス"
+
+#: rhodecode/templates/admin/settings/settings.html:77
+msgid "Global application settings"
+msgstr "アプリケーション全体の設定"
+
+#: rhodecode/templates/admin/settings/settings.html:86
+msgid "Application name"
+msgstr "アプリケーション名"
+
+#: rhodecode/templates/admin/settings/settings.html:95
+msgid "Realm text"
+msgstr "Realmテキスト"
+
+#: rhodecode/templates/admin/settings/settings.html:104
+msgid "GA code"
+msgstr "GAコード"
+
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
+msgid "Save settings"
+msgstr "設定を保存"
+
+#: rhodecode/templates/admin/settings/settings.html:119
+#, fuzzy
+msgid "Visualisation settings"
+msgstr "アプリケーション全体の設定"
+
+#: rhodecode/templates/admin/settings/settings.html:128
+#, fuzzy
+msgid "Icons"
+msgstr "オプション"
+
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+#, fuzzy
+msgid "Show private repo icon on repositories"
+msgstr "非公開リポジトリ"
+
+#: rhodecode/templates/admin/settings/settings.html:144
+#, fuzzy
+msgid "Meta-Tagging"
+msgstr "設定"
+
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:176
+#, fuzzy
+msgid "VCS settings"
+msgstr "設定"
+
+#: rhodecode/templates/admin/settings/settings.html:185
+msgid "Web"
+msgstr "Web"
+
+#: rhodecode/templates/admin/settings/settings.html:190
+#, fuzzy
+msgid "require ssl for vcs operations"
+msgstr "プッシュにSSLを必須とする"
+
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
+"will return HTTP Error 406: Not Acceptable"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:198
+msgid "Hooks"
+msgstr "フック"
+
+#: rhodecode/templates/admin/settings/settings.html:203
+msgid "Update repository after push (hg update)"
+msgstr "プッシュ後にリポジトリをを更新する (hg update)"
+
+#: rhodecode/templates/admin/settings/settings.html:207
+msgid "Show repository size after push"
+msgstr "プッシュ後にリポジトリのサイズを表示する"
+
+#: rhodecode/templates/admin/settings/settings.html:211
+msgid "Log user push commands"
+msgstr "ユーザーのプッシュコマンドを記録する"
+
+#: rhodecode/templates/admin/settings/settings.html:215
+msgid "Log user pull commands"
+msgstr "ユーザーのプルコマンドを記録する"
+
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr "高度な設定"
+
+#: rhodecode/templates/admin/settings/settings.html:224
+#, fuzzy
+msgid "Mercurial Extensions"
+msgstr "Mercurialリポジトリ"
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
+msgid "Repositories location"
+msgstr "リポジトリロケーション"
+
+#: rhodecode/templates/admin/settings/settings.html:250
+msgid ""
+"This a crucial application setting. If you are really sure you need to "
+"change this, you must restart application in order to make this setting "
+"take effect. Click this label to unlock."
+msgstr "これはアプリケーションの重要な設定です。本当に変更が必要でしょうか。もし、変更した場合、変更を反映さ競るためにアプリケーションを再起動する必要があります。変更可能にするにはこのラベルをクリックして下さい"
+
+#: rhodecode/templates/admin/settings/settings.html:251
+msgid "unlock"
+msgstr "変更可能にする"
+
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a "
+"restart, and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr "テストメール"
+
+#: rhodecode/templates/admin/settings/settings.html:280
+msgid "Email to"
+msgstr "送信先"
+
+#: rhodecode/templates/admin/settings/settings.html:288
+msgid "Send"
+msgstr "送る"
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr "システム情報とパッケージ"
+
+#: rhodecode/templates/admin/settings/settings.html:297
+msgid "show"
+msgstr "表示"
+
+#: rhodecode/templates/admin/users/user_add.html:5
+msgid "Add user"
+msgstr "ユーザーを追加"
+
+#: rhodecode/templates/admin/users/user_add.html:10
+#: rhodecode/templates/admin/users/user_edit.html:11
+msgid "Users"
+msgstr "ユーザー"
+
+#: rhodecode/templates/admin/users/user_add.html:12
+msgid "add new user"
+msgstr "新しいユーザーを追加"
+
+#: rhodecode/templates/admin/users/user_add.html:50
+msgid "Password confirmation"
+msgstr "パスワード再入力"
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
+#: rhodecode/templates/admin/users_groups/users_group_add.html:41
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
+msgid "Active"
+msgstr "アクティブ"
+
+#: rhodecode/templates/admin/users/user_edit.html:5
+msgid "Edit user"
+msgstr "ユーザー編集"
+
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
+msgid "Change your avatar at"
+msgstr "アイコンを変えられます : "
+
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
+msgid "Using"
+msgstr "メールアドレス:"
+
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
+msgid "API key"
+msgstr "APIキー"
+
+#: rhodecode/templates/admin/users/user_edit.html:59
+msgid "LDAP DN"
+msgstr "LDAP DN"
+
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
+msgid "New password"
+msgstr "新しいパスワード"
+
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr "新しいパスワード 再入力"
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+#, fuzzy
+msgid "Inherit default permissions"
+msgstr "デフォルトの権限"
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
+msgid "Create repositories"
+msgstr "リポジトリを作成する"
+
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+#, fuzzy
+msgid "Fork repositories"
+msgstr "リポジトリ"
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+#, fuzzy
+msgid "Nothing here yet"
+msgstr "通知はまだありません"
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+msgid "Permission"
+msgstr "権限"
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+#, fuzzy
+msgid "Edit Permission"
+msgstr "リポジトリの権限"
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+msgid "Email addresses"
+msgstr "メールアドレス"
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, python-format
+msgid "Confirm to delete this email: %s"
+msgstr "このメールを削除してよろしいですか: %s"
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+msgid "New email address"
+msgstr "新しいメールアドレス"
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+msgid "Add"
+msgstr "追加"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
+msgid "My account"
+msgstr "アカウント"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:9
+msgid "My Account"
+msgstr "アカウント"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+msgid "My permissions"
+msgstr "権限"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+msgid "My repos"
+msgstr "リポジトリ"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+#, fuzzy
+msgid "My pull requests"
+msgstr "すべてのプルリクエスト"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+#, fuzzy
+msgid "Add repo"
+msgstr "新規追加"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, fuzzy, python-format
+msgid "Pull request #%s opened on %s"
+msgstr "プルリクエスト #%s"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+#, fuzzy
+msgid "Confirm to delete this pull request"
+msgstr "このリポジトリを削除しますか?"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40
+#: rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
+msgstr "リビジョン"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr "非公開"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr "このリポジトリを削除しますか? : %s"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
+msgid "No repositories yet"
+msgstr "まだリポジトリがありません"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
+msgid "create one now"
+msgstr "今すぐ作成する"
+
+#: rhodecode/templates/admin/users/users.html:5
+msgid "Users administration"
+msgstr "ユーザー管理"
+
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr "ユーザー"
+
+#: rhodecode/templates/admin/users/users.html:23
+msgid "ADD NEW USER"
+msgstr "新しいユーザーを追加"
+
+#: rhodecode/templates/admin/users/users.html:77
+msgid "username"
+msgstr "ユーザー名"
+
+#: rhodecode/templates/admin/users/users.html:80
+#, fuzzy
+msgid "firstname"
+msgstr "名前"
+
+#: rhodecode/templates/admin/users/users.html:81
+msgid "lastname"
+msgstr "名字"
+
+#: rhodecode/templates/admin/users/users.html:82
+msgid "last login"
+msgstr "最終ログイン日"
+
+#: rhodecode/templates/admin/users/users.html:84
+#: rhodecode/templates/admin/users_groups/users_groups.html:34
+msgid "active"
+msgstr "アクティブ"
+
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
+msgid "ldap"
+msgstr "LDAP"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:5
+msgid "Add users group"
+msgstr "ユーザーグループを追加"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:10
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+msgid "Users groups"
+msgstr "ユーザーグループ"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:12
+msgid "add new users group"
+msgstr "新しいユーザーグループを追加"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:5
+msgid "Edit users group"
+msgstr "ユーザーグループを編集"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:11
+msgid "UsersGroups"
+msgstr "ユーザーグループ"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:50
+msgid "Members"
+msgstr "メンバー"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:58
+msgid "Choosen group members"
+msgstr "グループメンバーを選ぶ"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:61
+msgid "Remove all elements"
+msgstr "全ての要素を削除"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:75
+msgid "Available members"
+msgstr "有効なメンバー"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:79
+msgid "Add all elements"
+msgstr "全ての要素を追加"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+msgid "Group members"
+msgstr "グループメンバー"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:5
+msgid "Users groups administration"
+msgstr "ユーザーグループ管理"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:23
+msgid "ADD NEW USER GROUP"
+msgstr "新しいユーザーグループを追加"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:32
+msgid "group name"
+msgstr "グループ名"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr "メンバー"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr "バグレポート"
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr "ログイン"
+
+#: rhodecode/templates/base/base.html:100
+msgid "Forgot password ?"
+msgstr "パスワードを忘れた場合はこちら"
+
+#: rhodecode/templates/base/base.html:107
+msgid "Log In"
+msgstr "ログイン"
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr "受信箱"
+
+#: rhodecode/templates/base/base.html:122
+#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302
+#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8
+#: rhodecode/templates/tags/tags.html:11
+msgid "Home"
+msgstr "ホーム"
+
+#: rhodecode/templates/base/base.html:123
+#: rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311
+#: rhodecode/templates/base/base.html:313
+#: rhodecode/templates/journal/journal.html:4
+#: rhodecode/templates/journal/journal.html:21
+#: rhodecode/templates/journal/public_journal.html:4
+msgid "Journal"
+msgstr "ジャーナル"
+
+#: rhodecode/templates/base/base.html:125
+msgid "Log Out"
+msgstr "ログアウト"
+
+#: rhodecode/templates/base/base.html:144
+msgid "Switch repository"
+msgstr "リポジトリの切り替え"
+
+#: rhodecode/templates/base/base.html:146
+msgid "Products"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:152
+#: rhodecode/templates/base/base.html:182
+msgid "loading..."
+msgstr "読み込み中..."
+
+#: rhodecode/templates/base/base.html:158
+#: rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
+msgstr "要約"
+
+#: rhodecode/templates/base/base.html:166
+#: rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
+msgstr "履歴"
+
+#: rhodecode/templates/base/base.html:175
+#: rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
+msgstr "ブランチの切り替え"
+
+#: rhodecode/templates/base/base.html:186
+#: rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
+msgstr "ファイル"
+
+#: rhodecode/templates/base/base.html:195
+#: rhodecode/templates/base/base.html:199
+msgid "Options"
+msgstr "オプション"
+
+#: rhodecode/templates/base/base.html:204
+#: rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
+msgid "settings"
+msgstr "設定"
+
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
+msgstr "フォーク"
+
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
+msgstr "新しいプルリクエストを作成"
+
+#: rhodecode/templates/base/base.html:213
+msgid "search"
+msgstr "検索"
+
+#: rhodecode/templates/base/base.html:222
+msgid "repositories groups"
+msgstr "リポジトリグループ"
+
+#: rhodecode/templates/base/base.html:224
+msgid "users groups"
+msgstr "ユーザーグループ"
+
+#: rhodecode/templates/base/base.html:225
+msgid "permissions"
+msgstr "権限"
+
+#: rhodecode/templates/base/base.html:238
+#: rhodecode/templates/base/base.html:240
+msgid "Followers"
+msgstr "フォロワー"
+
+#: rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248
+msgid "Forks"
+msgstr "フォーク"
+
+#: rhodecode/templates/base/base.html:327
+#: rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331
+#: rhodecode/templates/search/search.html:52
+msgid "Search"
+msgstr "検索"
+
+#: rhodecode/templates/base/root.html:42
+msgid "add another comment"
+msgstr "別のコメントを追加"
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
+msgid "Stop following this repository"
+msgstr "このリポジトリのフォローをやめる"
+
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
+msgid "Start following this repository"
+msgstr "このリポジトリのフォローする"
+
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr "グループ"
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr "検索結果は省略されています"
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr "マッチするファイルはありません"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr "%s ブックマーク"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39
+#: rhodecode/templates/tags/tags_data.html:8
+msgid "Author"
+msgstr "作成者"
+
+#: rhodecode/templates/branches/branches.html:5
+#, python-format
+msgid "%s Branches"
+msgstr "%s ブランチ"
+
+#: rhodecode/templates/branches/branches.html:29
+msgid "Compare branches"
+msgstr "ブランチの比較"
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+msgid "Compare"
+msgstr "比較"
+
+#: rhodecode/templates/branches/branches_data.html:6
+msgid "name"
+msgstr "名前"
+
+#: rhodecode/templates/branches/branches_data.html:7
+msgid "date"
+msgstr "日付"
+
+#: rhodecode/templates/branches/branches_data.html:8
+#: rhodecode/templates/shortlog/shortlog_data.html:8
+msgid "author"
+msgstr "作成者"
+
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
+msgstr "リビジョン"
+
+#: rhodecode/templates/branches/branches_data.html:10
+msgid "compare"
+msgstr "比較"
+
+#: rhodecode/templates/changelog/changelog.html:6
+#, python-format
+msgid "%s Changelog"
+msgstr "%s チェンジログ"
+
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
+msgstr "%s とフォークを比較"
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+msgid "Compare fork"
+msgstr "フォークを比較"
+
+#: rhodecode/templates/changelog/changelog.html:46
+msgid "Show"
+msgstr "表示"
+
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
+msgstr "もっと表示"
+
+#: rhodecode/templates/changelog/changelog.html:76
+msgid "Affected number of files, click to show more details"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+msgid "Changeset status"
+msgstr "リビジョンステータス"
+
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
+msgid "Parent"
+msgstr "親リビジョン"
+
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
+msgid "No parents"
+msgstr "親リビジョンはありません"
+
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr "マージ"
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
+#: rhodecode/templates/files/files.html:29
+#: rhodecode/templates/files/files_add.html:33
+#: rhodecode/templates/files/files_edit.html:33
+#: rhodecode/templates/shortlog/shortlog_data.html:9
+msgid "branch"
+msgstr "ブランチ"
+
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr "ブックマーク"
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
+msgid "tag"
+msgstr "タグ"
+
+#: rhodecode/templates/changelog/changelog.html:164
+msgid "Show selected changes __S -> __E"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:255
+msgid "There are no changes yet"
+msgstr "まだ変更がありません"
+
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
+msgid "removed"
+msgstr "削除"
+
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
+msgid "changed"
+msgstr "変更"
+
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
+msgid "added"
+msgstr "追加"
+
+#: rhodecode/templates/changelog/changelog_details.html:8
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
+#, python-format
+msgid "affected %s files"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:6
+#, python-format
+msgid "%s Changeset"
+msgstr "%s チェンジセット"
+
+#: rhodecode/templates/changeset/changeset.html:14
+msgid "Changeset"
+msgstr "チェンジセット"
+
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
+msgid "raw diff"
+msgstr "差分を表示"
+
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
+msgid "download diff"
+msgstr "差分をダウンロード"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d コメント"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] "(%d インライン)"
+
+#: rhodecode/templates/changeset/changeset.html:103
+#, python-format
+msgid "%s files affected with %s insertions and %s deletions:"
+msgstr "%s ファイルに影響。 %s 個の追加と %s 個の削除:"
+
+#: rhodecode/templates/changeset/changeset.html:119
+msgid "Changeset was too big and was cut off..."
+msgstr "チェンジセットが大きすぎるため、省略しました"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr "サブミット中..."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr "{1} 行目にコメント"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr "コメントには %s 構文 ( %s サポートつき ) が利用出来ます"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr "テキスト内で @username を使うと、この RhodeCode のユーザーに通知を送信します"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+msgid "Comment"
+msgstr "コメント"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
+msgstr "隠す"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "You need to be logged in to comment."
+msgstr "コメントするにはログインが必要です"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr "今すぐログインする"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr "コメントを残す"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "change status"
+msgstr "ステータスを変更する"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, python-format
+msgid "%s Changesets"
+msgstr "%s チェンジセット"
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
+msgid "Compare View"
+msgstr "比較ビュー"
+
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
+msgid "Files affected"
+msgstr ""
+
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
+msgstr "差分"
+
+#: rhodecode/templates/changeset/diff_block.html:27
+msgid "show inline comments"
+msgstr "インラインコメントを表示"
+
+#: rhodecode/templates/compare/compare_cs.html:5
+msgid "No changesets"
+msgstr "チェンジセットはありません"
+
+#: rhodecode/templates/compare/compare_diff.html:37
+msgid "Outgoing changesets"
+msgstr "送信可能なチェンジセット"
+
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
+msgstr "フォーク"
+
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
+msgstr "Mercurialリポジトリ"
+
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
+msgstr "Gitリポジトリ"
+
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
+msgstr "公開リポジトリ"
+
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
+msgstr "フォーク元: "
+
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
+msgstr "まだチェンジセットがありません"
+
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, python-format
+msgid "Confirm to delete this user: %s"
+msgstr "このユーザーを本当に削除してよろしいですか?: %s"
+
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
+msgstr "RhodeCodeからの通知があります"
+
+#: rhodecode/templates/errors/error_document.html:46
+#, python-format
+msgid "You will be redirected to %s in %s seconds"
+msgstr ""
+
+#: rhodecode/templates/files/file_diff.html:4
+#, python-format
+msgid "%s File diff"
+msgstr "%s ファイル差分"
+
+#: rhodecode/templates/files/file_diff.html:12
+msgid "File diff"
+msgstr "ファイル差分"
+
+#: rhodecode/templates/files/files.html:4
+#: rhodecode/templates/files/files.html:72
+#, fuzzy, python-format
+msgid "%s files"
+msgstr "%s ファイル"
+
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr "ファイル"
+
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, python-format
+msgid "%s Edit file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:19
+msgid "add file"
+msgstr "ファイルを追加"
+
+#: rhodecode/templates/files/files_add.html:40
+msgid "Add new file"
+msgstr "新しいファイルを追加"
+
+#: rhodecode/templates/files/files_add.html:45
+msgid "File Name"
+msgstr "ファイル名"
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+msgid "or"
+msgstr "または"
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+msgid "Upload file"
+msgstr "アップロード"
+
+#: rhodecode/templates/files/files_add.html:58
+msgid "Create new file"
+msgstr "新しいファイルを作成"
+
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr "場所"
+
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr "ディレクトリの区切りには / を使います"
+
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr "コミットメッセージ"
+
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
+msgstr "変更をコミット"
+
+#: rhodecode/templates/files/files_browser.html:13
+msgid "view"
+msgstr "表示"
+
+#: rhodecode/templates/files/files_browser.html:14
+msgid "previous revision"
+msgstr "前のリビジョン"
+
+#: rhodecode/templates/files/files_browser.html:16
+msgid "next revision"
+msgstr "次のリビジョン"
+
+#: rhodecode/templates/files/files_browser.html:23
+msgid "follow current branch"
+msgstr "このブランチで追跡"
+
+#: rhodecode/templates/files/files_browser.html:27
+msgid "search file list"
+msgstr "ファイル一覧を検索"
+
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+msgid "add new file"
+msgstr "新しいファイルを追加"
+
+#: rhodecode/templates/files/files_browser.html:35
+msgid "Loading file list..."
+msgstr "ファイル一覧を読み込み中..."
+
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
+msgstr "サイズ"
+
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
+msgstr "Mimetype"
+
+#: rhodecode/templates/files/files_browser.html:50
+msgid "Last Revision"
+msgstr "最後のリビジョン"
+
+#: rhodecode/templates/files/files_browser.html:51
+msgid "Last modified"
+msgstr "最終更新日"
+
+#: rhodecode/templates/files/files_browser.html:52
+msgid "Last commiter"
+msgstr "最後の作成者"
+
+#: rhodecode/templates/files/files_edit.html:19
+msgid "edit file"
+msgstr "ファイルを編集"
+
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr "アノテーションを表示"
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
+msgstr "元のファイルを表示"
+
+#: rhodecode/templates/files/files_edit.html:51
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
+msgstr "元のファイルをダウンロード"
+
+#: rhodecode/templates/files/files_edit.html:54
+msgid "source"
+msgstr "ソース"
+
+#: rhodecode/templates/files/files_edit.html:59
+msgid "Editing file"
+msgstr "ファイルを編集"
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr "変更履歴"
+
+#: rhodecode/templates/files/files_source.html:9
+msgid "diff to revision"
+msgstr "このリビジョンの差分を見る"
+
+#: rhodecode/templates/files/files_source.html:10
+msgid "show at revision"
+msgstr "このリビジョンを見る"
+
+#: rhodecode/templates/files/files_source.html:14
+#, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] "%s 作成者"
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
+msgstr "ソースを表示"
+
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr "バイナリファイル (%s)"
+
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
+msgstr "表示するには大きすぎるファイルです"
+
+#: rhodecode/templates/files/files_source.html:124
+msgid "Selection link"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:5
+msgid "annotation"
+msgstr "アノテーション"
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr "戻る"
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr "そのパスにはファイルはありません"
+
+#: rhodecode/templates/followers/followers.html:5
+#, python-format
+msgid "%s Followers"
+msgstr "%s フォロワー"
+
+#: rhodecode/templates/followers/followers.html:13
+msgid "followers"
+msgstr "フォロワー"
+
+#: rhodecode/templates/followers/followers_data.html:12
+msgid "Started following -"
+msgstr "フォロー開始日 -"
+
+#: rhodecode/templates/forks/fork.html:5
+#, python-format
+msgid "%s Fork"
+msgstr "%s フォーク"
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr "フォーク名"
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr "非公開"
+
+#: rhodecode/templates/forks/fork.html:77
+msgid "Copy permissions"
+msgstr "権限のコピー"
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr "フォーク元リポジトリから権限をコピーします"
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr "クローン後にupdateする"
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr "クローンした後にソースをチェックアウトします"
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr "このリポジトリをフォーク"
+
+#: rhodecode/templates/forks/forks.html:5
+#, python-format
+msgid "%s Forks"
+msgstr "%s フォーク"
+
+#: rhodecode/templates/forks/forks.html:13
+msgid "forks"
+msgstr "フォーク"
+
+#: rhodecode/templates/forks/forks_data.html:17
+msgid "forked"
+msgstr "フォークしました"
+
+#: rhodecode/templates/forks/forks_data.html:38
+msgid "There are no forks yet"
+msgstr "まだフォークがありません"
+
+#: rhodecode/templates/journal/journal.html:13
+msgid "ATOM journal feed"
+msgstr "ATOM ジャーナルフィード"
+
+#: rhodecode/templates/journal/journal.html:14
+msgid "RSS journal feed"
+msgstr "RSS ジャーナルフィード"
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr "更新"
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+msgid "RSS feed"
+msgstr "RSSフィード"
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
+msgstr "ATOMフィード"
+
+#: rhodecode/templates/journal/journal.html:41
+msgid "Watched"
+msgstr "ウォッチ"
+
+#: rhodecode/templates/journal/journal.html:46
+msgid "ADD"
+msgstr "追加"
+
+#: rhodecode/templates/journal/journal.html:114
+msgid "following user"
+msgstr "フォローしているユーザー"
+
+#: rhodecode/templates/journal/journal.html:114
+msgid "user"
+msgstr "ユーザー"
+
+#: rhodecode/templates/journal/journal.html:147
+msgid "You are not following any users or repositories"
+msgstr "まだどのユーザーもリポジトリもフォローしていません"
+
+#: rhodecode/templates/journal/journal_data.html:47
+msgid "No entries yet"
+msgstr "まだエントリがありません"
+
+#: rhodecode/templates/journal/public_journal.html:13
+msgid "ATOM public journal feed"
+msgstr "ATOM 公開ジャーナルフィード"
+
+#: rhodecode/templates/journal/public_journal.html:14
+msgid "RSS public journal feed"
+msgstr "RSS 公開ジャーナルフィード"
+
+#: rhodecode/templates/journal/public_journal.html:21
+msgid "Public Journal"
+msgstr "公開ジャーナル"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr "新しいプルリクエスト"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr "概要の更新"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+msgid "Detailed compare view"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr "プルリクエストレビュアー"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+#, fuzzy
+msgid "owner"
+msgstr "所有者"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+#, fuzzy
+msgid "Add reviewer to this pull request."
+msgstr "新しいプルリクエスト"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+msgid "Create new pull request"
+msgstr "新しいプルリクエストを作成"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+msgid "Title"
+msgstr "タイトル"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+msgid "description"
+msgstr "説明"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr "プルリクエストを送る"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+#, fuzzy
+msgid "Status"
+msgstr "ステータスを変更する"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr "プルリクエストステータス"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+#, fuzzy
+msgid "Still not reviewed by"
+msgstr "未レビュー"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, fuzzy, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] "レビュー中"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+msgid "Created on"
+msgstr "作成日"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+msgid "Compare view"
+msgstr "比較ビュー"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+msgid "Incoming changesets"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+#, fuzzy
+msgid "all pull requests"
+msgstr "すべてのプルリクエスト"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr "すべてのプルリクエスト"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr "\"%s\" を %s リポジトリから検索"
+
+#: rhodecode/templates/search/search.html:8
+#, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr "\"%s\" を全てのリポジトリから検索"
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, python-format
+msgid "Search in repository: %s"
+msgstr "%s リポジトリから検索"
+
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+msgid "Search in all repositories"
+msgstr "全てのリポジトリから検索"
+
+#: rhodecode/templates/search/search.html:48
+msgid "Search term"
+msgstr "検索キーワード"
+
+#: rhodecode/templates/search/search.html:60
+msgid "Search in"
+msgstr "検索対象"
+
+#: rhodecode/templates/search/search.html:63
+msgid "File contents"
+msgstr "ファイル内容"
+
+#: rhodecode/templates/search/search.html:64
+#, fuzzy
+msgid "Commit messages"
+msgstr "コミットメッセージ"
+
+#: rhodecode/templates/search/search.html:65
+msgid "File names"
+msgstr "ファイル名"
+
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
+#: rhodecode/templates/search/search_path.html:15
+msgid "Permission denied"
+msgstr "権限がありません"
+
+#: rhodecode/templates/settings/repo_settings.html:5
+#, python-format
+msgid "%s Settings"
+msgstr "%s 設定"
+
+#: rhodecode/templates/shortlog/shortlog.html:5
+#, python-format
+msgid "%s Shortlog"
+msgstr "%s 短いログ"
+
+#: rhodecode/templates/shortlog/shortlog.html:14
+msgid "shortlog"
+msgstr "ログ"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:7
+msgid "age"
+msgstr "経過時間"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+msgid "No commit message"
+msgstr "コミットメッセージが有りません"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr "RhodeCode経由で直接ファイルを追加またはアップロード"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr "新しいリポジトリをプッシュ"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+msgid "Existing repository?"
+msgstr "存在するリポジトリをプッシュ"
+
+#: rhodecode/templates/summary/summary.html:4
+#, python-format
+msgid "%s Summary"
+msgstr "%s 要約"
+
+#: rhodecode/templates/summary/summary.html:12
+msgid "summary"
+msgstr "要約"
+
+#: rhodecode/templates/summary/summary.html:20
+#, python-format
+msgid "repo %s ATOM feed"
+msgstr "リポジトリ %s ATOM フィード"
+
+#: rhodecode/templates/summary/summary.html:21
+#, python-format
+msgid "repo %s RSS feed"
+msgstr "リポジトリ %s RSS フィード"
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+msgid "ATOM"
+msgstr "ATOM"
+
+#: rhodecode/templates/summary/summary.html:82
+#, python-format
+msgid "Non changable ID %s"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr "公開"
+
+#: rhodecode/templates/summary/summary.html:95
+msgid "remote clone"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
+msgstr "コンタクト"
+
+#: rhodecode/templates/summary/summary.html:130
+msgid "Clone url"
+msgstr "クローンURL"
+
+#: rhodecode/templates/summary/summary.html:133
+msgid "Show by Name"
+msgstr "名前で表示"
+
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
+msgstr "IDで表示"
+
+#: rhodecode/templates/summary/summary.html:142
+msgid "Trending files"
+msgstr "トレンドファイル"
+
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
+msgstr "有効にする"
+
+#: rhodecode/templates/summary/summary.html:158
+msgid "Download"
+msgstr "ダウンロード"
+
+#: rhodecode/templates/summary/summary.html:162
+msgid "There are no downloads yet"
+msgstr "まだダウンロードがありません"
+
+#: rhodecode/templates/summary/summary.html:164
+msgid "Downloads are disabled for this repository"
+msgstr "このリポジトリのダウンロードは無効化されています"
+
+#: rhodecode/templates/summary/summary.html:170
+msgid "Download as zip"
+msgstr "ZIPとしてダウンロード"
+
+#: rhodecode/templates/summary/summary.html:173
+msgid "Check this to download archive with subrepos"
+msgstr "チェックするとダウンロードアーカイブにサブリポジトリが含まれます"
+
+#: rhodecode/templates/summary/summary.html:173
+msgid "with subrepos"
+msgstr "サブリポジトリを含む"
+
+#: rhodecode/templates/summary/summary.html:186
+msgid "Commit activity by day / author"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
+msgstr "ログ"
+
+#: rhodecode/templates/summary/summary.html:220
+msgid "Quick start"
+msgstr "クイックスタート"
+
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr "%s を %sとしてダウンロード"
+
+#: rhodecode/templates/summary/summary.html:650
+msgid "commits"
+msgstr "コミット"
+
+#: rhodecode/templates/summary/summary.html:651
+msgid "files added"
+msgstr "追加されたファイル"
+
+#: rhodecode/templates/summary/summary.html:652
+msgid "files changed"
+msgstr "変更されたファイル"
+
+#: rhodecode/templates/summary/summary.html:653
+msgid "files removed"
+msgstr "削除されたファイル"
+
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr "コミット"
+
+#: rhodecode/templates/summary/summary.html:657
+msgid "file added"
+msgstr "追加されたファイル"
+
+#: rhodecode/templates/summary/summary.html:658
+msgid "file changed"
+msgstr "変更されたファイル"
+
+#: rhodecode/templates/summary/summary.html:659
+msgid "file removed"
+msgstr "削除されたファイル"
+
+#: rhodecode/templates/tags/tags.html:5
+#, python-format
+msgid "%s Tags"
+msgstr "%s タグ"
+
diff --git a/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.mo b/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.mo
index c72e89a8..a28cc684 100644
--- a/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.mo
+++ b/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.mo
Binary files differ
diff --git a/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po b/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po
index 9ac40eab..b85248dd 100644
--- a/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po
+++ b/rhodecode/i18n/pt_BR/LC_MESSAGES/rhodecode.po
@@ -1,14 +1,14 @@
# Portuguese (Brazil) translations for RhodeCode.
-# Copyright (C) 2011 Augusto Herrmann
+# Copyright (C) 2011-2012 Augusto Herrmann
# This file is distributed under the same license as the RhodeCode project.
-# Augusto Herrmann <augusto.herrmann@gmail.com>, 2011.
+# Augusto Herrmann <augusto.herrmann@gmail.com>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: RhodeCode 1.2.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2011-09-14 15:50-0300\n"
-"PO-Revision-Date: 2011-09-14 15:53-0300\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\n"
+"PO-Revision-Date: 2012-05-22 16:47-0300\n"
"Last-Translator: Augusto Herrmann <augusto.herrmann@gmail.com>\n"
"Language-Team: pt_BR <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
@@ -17,21 +17,38 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-#: rhodecode/controllers/changeset.py:108
-#: rhodecode/controllers/changeset.py:149
-#: rhodecode/controllers/changeset.py:216
-#: rhodecode/controllers/changeset.py:229
+#: rhodecode/controllers/changelog.py:94
+msgid "All Branches"
+msgstr "Todos os Ramos"
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr "mostrar espaços em branco"
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr "ignorar espaços em branco"
+
+#: rhodecode/controllers/changeset.py:157
+#, python-format
+msgid "%s line context"
+msgstr "contexto de %s linhas"
+
+#: rhodecode/controllers/changeset.py:333
+#: rhodecode/controllers/changeset.py:348 rhodecode/lib/diffs.py:70
msgid "binary file"
msgstr "arquivo binário"
-#: rhodecode/controllers/changeset.py:123
-#: rhodecode/controllers/changeset.py:168
-msgid "Changeset is to big and was cut off, see raw changeset instead"
-msgstr "Conjunto de mudanças é grande demais e foi cortado, em vez disso veja o conjunto de mudanças bruto"
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is "
+"not allowed"
+msgstr ""
-#: rhodecode/controllers/changeset.py:159
-msgid "Diff is to big and was cut off, see raw diff instead"
-msgstr "Diff é grande demais e foi cortado, em vez disso veja diff bruto"
+#: rhodecode/controllers/compare.py:69
+#, fuzzy
+msgid "There are no changesets yet"
+msgstr "Ainda não há alteações"
#: rhodecode/controllers/error.py:69
msgid "Home page"
@@ -39,7 +56,9 @@ msgstr "Página inicial"
#: rhodecode/controllers/error.py:98
msgid "The request could not be understood by the server due to malformed syntax."
-msgstr "A requisição não pôde ser compreendida pelo servidor devido à sintaxe mal formada"
+msgstr ""
+"A requisição não pôde ser compreendida pelo servidor devido à sintaxe mal"
+" formada"
#: rhodecode/controllers/error.py:101
msgid "Unauthorized access to resource"
@@ -54,250 +73,325 @@ msgid "The resource could not be found"
msgstr "O recurso não pôde ser encontrado"
#: rhodecode/controllers/error.py:107
-msgid "The server encountered an unexpected condition which prevented it from fulfilling the request."
-msgstr "O servidor encontrou uma condição inesperada que o impediu de satisfazer a requisição"
+msgid ""
+"The server encountered an unexpected condition which prevented it from "
+"fulfilling the request."
+msgstr ""
+"O servidor encontrou uma condição inesperada que o impediu de satisfazer "
+"a requisição"
-#: rhodecode/controllers/feed.py:48
+#: rhodecode/controllers/feed.py:49
#, python-format
msgid "Changes on %s repository"
msgstr "Alterações no repositório %s"
-#: rhodecode/controllers/feed.py:49
+#: rhodecode/controllers/feed.py:50
#, python-format
msgid "%s %s feed"
msgstr "%s - feed %s"
-#: rhodecode/controllers/files.py:72
-msgid "There are no files yet"
-msgstr "Ainda não há arquivos"
+#: rhodecode/controllers/feed.py:75
+#, fuzzy
+msgid "commited on"
+msgstr "commit"
-#: rhodecode/controllers/files.py:262
+#: rhodecode/controllers/files.py:84
+#, fuzzy
+msgid "click here to add new file"
+msgstr "adicionar novo arquivo"
+
+#: rhodecode/controllers/files.py:85
+#, python-format
+msgid "There are no files yet %s"
+msgstr "Ainda não há arquivos %s"
+
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:266
#, python-format
msgid "Edited %s via RhodeCode"
msgstr "Editado %s via RhodeCode"
-#: rhodecode/controllers/files.py:267
-#: rhodecode/templates/files/file_diff.html:40
+#: rhodecode/controllers/files.py:271
msgid "No changes"
msgstr "Sem alterações"
-#: rhodecode/controllers/files.py:278
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
#, python-format
msgid "Successfully committed to %s"
msgstr "Commit realizado com sucesso para %s"
-#: rhodecode/controllers/files.py:283
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
msgid "Error occurred during commit"
msgstr "Ocorreu um erro ao realizar commit"
-#: rhodecode/controllers/files.py:308
+#: rhodecode/controllers/files.py:318
+#, python-format
+msgid "Added %s via RhodeCode"
+msgstr "Adicionado %s via RhodeCode"
+
+#: rhodecode/controllers/files.py:332
+msgid "No content"
+msgstr "Nenhum conteúdo"
+
+#: rhodecode/controllers/files.py:336
+msgid "No filename"
+msgstr "Nenhum nomes de arquivo"
+
+#: rhodecode/controllers/files.py:378
msgid "downloads disabled"
msgstr "downloads desabilitados"
-#: rhodecode/controllers/files.py:313
+#: rhodecode/controllers/files.py:389
#, python-format
msgid "Unknown revision %s"
msgstr "Revisão desconhecida %s"
-#: rhodecode/controllers/files.py:315
+#: rhodecode/controllers/files.py:391
msgid "Empty repository"
msgstr "Repositório vazio"
-#: rhodecode/controllers/files.py:317
+#: rhodecode/controllers/files.py:393
msgid "Unknown archive type"
msgstr "Arquivo de tipo desconhecido"
-#: rhodecode/controllers/files.py:385
-#: rhodecode/controllers/files.py:398
-msgid "Binary file"
-msgstr "Arquivo binário"
-
-#: rhodecode/controllers/files.py:417
-#: rhodecode/templates/changeset/changeset_range.html:4
-#: rhodecode/templates/changeset/changeset_range.html:12
-#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
msgid "Changesets"
msgstr "Conjuntos de mudanças"
-#: rhodecode/controllers/files.py:418
-#: rhodecode/controllers/summary.py:175
-#: rhodecode/templates/branches/branches.html:5
-#: rhodecode/templates/summary/summary.html:690
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
msgid "Branches"
msgstr "Ramos"
-#: rhodecode/controllers/files.py:419
-#: rhodecode/controllers/summary.py:176
-#: rhodecode/templates/summary/summary.html:679
-#: rhodecode/templates/tags/tags.html:5
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
msgid "Tags"
msgstr "Etiquetas"
-#: rhodecode/controllers/journal.py:50
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
#, python-format
-msgid "%s public journal %s feed"
-msgstr "diário público de %s - feed %s"
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"repositório %s não está mapeado ao bd. Talvez ele tenha sido criado ou "
+"renomeado a partir do sistema de arquivos. Por favor execute a aplicação "
+"outra vez para varrer novamente por repositórios"
-#: rhodecode/controllers/journal.py:178
-#: rhodecode/controllers/journal.py:212
-#: rhodecode/templates/admin/repos/repo_edit.html:171
-#: rhodecode/templates/base/base.html:50
-msgid "Public journal"
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the file system please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"repositório %s não está mapeado ao bd. Talvez ele tenha sido criado ou "
+"renomeado a partir do sistema de arquivos. Por favor execute a aplicação "
+"outra vez para varrer novamente por repositórios"
+
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr "bifurcado repositório %s como %s"
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr "Ocorreu um erro ao bifurcar o repositório %s"
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+#, fuzzy
+msgid "public journal"
msgstr "Diário público"
-#: rhodecode/controllers/login.py:111
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr "diário"
+
+#: rhodecode/controllers/login.py:143
msgid "You have successfully registered into rhodecode"
msgstr "Você se registrou com sucesso no rhodecode"
-#: rhodecode/controllers/login.py:133
+#: rhodecode/controllers/login.py:164
msgid "Your password reset link was sent"
msgstr "Seu link de reinicialização de senha foi enviado"
-#: rhodecode/controllers/login.py:155
-msgid "Your password reset was successful, new password has been sent to your email"
-msgstr "Sua reinicialização de senha foi bem sucedida, sua senha foi enviada ao seu e-mail"
+#: rhodecode/controllers/login.py:184
+msgid ""
+"Your password reset was successful, new password has been sent to your "
+"email"
+msgstr ""
+"Sua reinicialização de senha foi bem sucedida, sua senha foi enviada ao "
+"seu e-mail"
+
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+msgid "Bookmarks"
+msgstr "Marcadores"
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+#, fuzzy
+msgid "error during creation of pull request"
+msgstr "ocorreu um erro ao criar o usuário %s"
+
+#: rhodecode/controllers/pullrequests.py:181
+#, fuzzy
+msgid "Successfully opened new pull request"
+msgstr "usuário excluído com sucesso"
+
+#: rhodecode/controllers/pullrequests.py:184
+#, fuzzy
+msgid "Error occurred during sending pull request"
+msgstr "ocorreu um erro ao criar o repositório %s"
+
+#: rhodecode/controllers/pullrequests.py:217
+#, fuzzy
+msgid "Successfully deleted pull request"
+msgstr "usuário excluído com sucesso"
-#: rhodecode/controllers/search.py:109
+#: rhodecode/controllers/search.py:131
msgid "Invalid search query. Try quoting it."
msgstr "Consulta de busca inválida. Tente usar aspas."
-#: rhodecode/controllers/search.py:114
+#: rhodecode/controllers/search.py:136
msgid "There is no index to search in. Please run whoosh indexer"
msgstr "Não há índice onde pesquisa. Por favor execute o indexador whoosh"
-#: rhodecode/controllers/search.py:118
+#: rhodecode/controllers/search.py:140
msgid "An error occurred during this search operation"
msgstr "Ocorreu um erro durante essa operação de busca"
-#: rhodecode/controllers/settings.py:61
-#: rhodecode/controllers/settings.py:171
-#, python-format
-msgid "%s repository is not mapped to db perhaps it was created or renamed from the file system please run the application again in order to rescan repositories"
-msgstr "repositório %s não está mapeado ao bd. Talvez ele tenha sido criado ou renomeado a partir do sistema de arquivos. Por favor execute a aplicação outra vez para varrer novamente por repositórios"
-
-#: rhodecode/controllers/settings.py:109
-#: rhodecode/controllers/admin/repos.py:239
+#: rhodecode/controllers/settings.py:107
+#: rhodecode/controllers/admin/repos.py:266
#, python-format
msgid "Repository %s updated successfully"
msgstr "Repositório %s atualizado com sucesso"
-#: rhodecode/controllers/settings.py:126
-#: rhodecode/controllers/admin/repos.py:257
+#: rhodecode/controllers/settings.py:125
+#: rhodecode/controllers/admin/repos.py:284
#, python-format
msgid "error occurred during update of repository %s"
msgstr "ocorreu um erro ao atualizar o repositório %s"
-#: rhodecode/controllers/settings.py:144
-#: rhodecode/controllers/admin/repos.py:275
+#: rhodecode/controllers/settings.py:143
+#: rhodecode/controllers/admin/repos.py:302
#, python-format
-msgid "%s repository is not mapped to db perhaps it was moved or renamed from the filesystem please run the application again in order to rescan repositories"
-msgstr "repositório %s não está mapeado ao bd. Talvez ele tenha sido movido ou renomeado a partir do sistema de arquivos. Por favor execute a aplicação outra vez para varrer novamente por repositórios"
+msgid ""
+"%s repository is not mapped to db perhaps it was moved or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+"repositório %s não está mapeado ao bd. Talvez ele tenha sido movido ou "
+"renomeado a partir do sistema de arquivos. Por favor execute a aplicação "
+"outra vez para varrer novamente por repositórios"
-#: rhodecode/controllers/settings.py:156
-#: rhodecode/controllers/admin/repos.py:287
+#: rhodecode/controllers/settings.py:155
+#: rhodecode/controllers/admin/repos.py:314
#, python-format
msgid "deleted repository %s"
msgstr "excluído o repositório %s"
#: rhodecode/controllers/settings.py:159
-#: rhodecode/controllers/admin/repos.py:297
-#: rhodecode/controllers/admin/repos.py:303
+#: rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
#, python-format
msgid "An error occurred during deletion of %s"
msgstr "Ocorreu um erro durante a exclusão de %s"
-#: rhodecode/controllers/settings.py:193
-#, python-format
-msgid "forked %s repository as %s"
-msgstr "bifurcado repositório %s como %s"
-
-#: rhodecode/controllers/settings.py:211
-#, python-format
-msgid "An error occurred during repository forking %s"
-msgstr "Ocorreu um erro ao bifurcar o repositório %s"
-
-#: rhodecode/controllers/summary.py:123
+#: rhodecode/controllers/summary.py:138
msgid "No data loaded yet"
msgstr "Ainda não há dados carregados"
-#: rhodecode/controllers/summary.py:126
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
msgid "Statistics are disabled for this repository"
msgstr "As estatísticas estão desabillitadas para este repositório"
-#: rhodecode/controllers/admin/ldap_settings.py:49
+#: rhodecode/controllers/admin/ldap_settings.py:50
msgid "BASE"
msgstr "BASE"
-#: rhodecode/controllers/admin/ldap_settings.py:50
+#: rhodecode/controllers/admin/ldap_settings.py:51
msgid "ONELEVEL"
msgstr "UMNÍVEL"
-#: rhodecode/controllers/admin/ldap_settings.py:51
+#: rhodecode/controllers/admin/ldap_settings.py:52
msgid "SUBTREE"
msgstr "SUBÁRVORE"
-#: rhodecode/controllers/admin/ldap_settings.py:55
+#: rhodecode/controllers/admin/ldap_settings.py:56
msgid "NEVER"
msgstr "NUNCA"
-#: rhodecode/controllers/admin/ldap_settings.py:56
+#: rhodecode/controllers/admin/ldap_settings.py:57
msgid "ALLOW"
msgstr "PERMITIR"
-#: rhodecode/controllers/admin/ldap_settings.py:57
+#: rhodecode/controllers/admin/ldap_settings.py:58
msgid "TRY"
msgstr "TENTAR"
-#: rhodecode/controllers/admin/ldap_settings.py:58
+#: rhodecode/controllers/admin/ldap_settings.py:59
msgid "DEMAND"
msgstr "EXIGIR"
-#: rhodecode/controllers/admin/ldap_settings.py:59
+#: rhodecode/controllers/admin/ldap_settings.py:60
msgid "HARD"
msgstr "DIFÍCIL"
-#: rhodecode/controllers/admin/ldap_settings.py:63
+#: rhodecode/controllers/admin/ldap_settings.py:64
msgid "No encryption"
msgstr "Sem criptografia"
-#: rhodecode/controllers/admin/ldap_settings.py:64
+#: rhodecode/controllers/admin/ldap_settings.py:65
msgid "LDAPS connection"
msgstr "Conexão LDAPS"
-#: rhodecode/controllers/admin/ldap_settings.py:65
+#: rhodecode/controllers/admin/ldap_settings.py:66
msgid "START_TLS on LDAP connection"
msgstr "START_TLS na conexão LDAP"
-#: rhodecode/controllers/admin/ldap_settings.py:115
+#: rhodecode/controllers/admin/ldap_settings.py:126
msgid "Ldap settings updated successfully"
msgstr "Configurações de LDAP atualizadas com sucesso"
-#: rhodecode/controllers/admin/ldap_settings.py:120
+#: rhodecode/controllers/admin/ldap_settings.py:130
msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
msgstr "Não foi possível ativar LDAP. A biblioteca \"python-ldap\" está faltando."
-#: rhodecode/controllers/admin/ldap_settings.py:134
+#: rhodecode/controllers/admin/ldap_settings.py:147
msgid "error occurred during update of ldap settings"
msgstr "ocorreu um erro ao atualizar as configurações de LDAP"
-#: rhodecode/controllers/admin/permissions.py:56
+#: rhodecode/controllers/admin/permissions.py:59
msgid "None"
msgstr "Nenhum"
-#: rhodecode/controllers/admin/permissions.py:57
+#: rhodecode/controllers/admin/permissions.py:60
msgid "Read"
msgstr "Ler"
-#: rhodecode/controllers/admin/permissions.py:58
+#: rhodecode/controllers/admin/permissions.py:61
msgid "Write"
msgstr "Gravar"
-#: rhodecode/controllers/admin/permissions.py:59
+#: rhodecode/controllers/admin/permissions.py:62
#: rhodecode/templates/admin/ldap/ldap.html:9
#: rhodecode/templates/admin/permissions/permissions.html:9
#: rhodecode/templates/admin/repos/repo_add.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:9
-#: rhodecode/templates/admin/repos/repos.html:10
+#: rhodecode/templates/admin/repos/repos.html:9
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
@@ -305,547 +399,929 @@ msgstr "Gravar"
#: rhodecode/templates/admin/settings/settings.html:9
#: rhodecode/templates/admin/users/user_add.html:8
#: rhodecode/templates/admin/users/user_edit.html:9
-#: rhodecode/templates/admin/users/user_edit.html:110
+#: rhodecode/templates/admin/users/user_edit.html:122
#: rhodecode/templates/admin/users/users.html:9
#: rhodecode/templates/admin/users_groups/users_group_add.html:8
#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
#: rhodecode/templates/admin/users_groups/users_groups.html:9
-#: rhodecode/templates/base/base.html:279
-#: rhodecode/templates/base/base.html:366
-#: rhodecode/templates/base/base.html:368
-#: rhodecode/templates/base/base.html:370
+#: rhodecode/templates/base/base.html:197
+#: rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339
+#: rhodecode/templates/base/base.html:341
msgid "Admin"
msgstr "Administrador"
-#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/controllers/admin/permissions.py:65
msgid "disabled"
msgstr "desabilitado"
-#: rhodecode/controllers/admin/permissions.py:64
+#: rhodecode/controllers/admin/permissions.py:67
msgid "allowed with manual account activation"
msgstr "permitido com ativação manual de conta"
-#: rhodecode/controllers/admin/permissions.py:66
+#: rhodecode/controllers/admin/permissions.py:69
msgid "allowed with automatic account activation"
msgstr "permitido com ativação automática de conta"
-#: rhodecode/controllers/admin/permissions.py:68
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
msgid "Disabled"
msgstr "Desabilitado"
-#: rhodecode/controllers/admin/permissions.py:69
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
msgid "Enabled"
msgstr "Habilitado"
-#: rhodecode/controllers/admin/permissions.py:102
+#: rhodecode/controllers/admin/permissions.py:116
msgid "Default permissions updated successfully"
msgstr "Permissões padrões atualizadas com sucesso"
-#: rhodecode/controllers/admin/permissions.py:119
+#: rhodecode/controllers/admin/permissions.py:130
msgid "error occurred during update of permissions"
msgstr "ocorreu um erro ao atualizar as permissões"
-#: rhodecode/controllers/admin/repos.py:96
-#, python-format
-msgid "%s repository is not mapped to db perhaps it was created or renamed from the filesystem please run the application again in order to rescan repositories"
-msgstr "repositório %s não está mapeado ao bd. Talvez ele tenha sido criado ou renomeado a partir do sistema de arquivos. Por favor execute a aplicação outra vez para varrer novamente por repositórios"
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
+msgstr "--REMOVER BIFURCAÇÂO--"
-#: rhodecode/controllers/admin/repos.py:172
+#: rhodecode/controllers/admin/repos.py:192
#, python-format
msgid "created repository %s from %s"
msgstr "repositório %s criado a partir de %s"
-#: rhodecode/controllers/admin/repos.py:176
+#: rhodecode/controllers/admin/repos.py:196
#, python-format
msgid "created repository %s"
msgstr "repositório %s criado"
-#: rhodecode/controllers/admin/repos.py:205
+#: rhodecode/controllers/admin/repos.py:227
#, python-format
msgid "error occurred during creation of repository %s"
msgstr "ocorreu um erro ao criar o repositório %s"
-#: rhodecode/controllers/admin/repos.py:292
+#: rhodecode/controllers/admin/repos.py:319
#, python-format
msgid "Cannot delete %s it still contains attached forks"
msgstr "Nao é possível excluir %s pois ele ainda contém bifurcações vinculadas"
-#: rhodecode/controllers/admin/repos.py:320
+#: rhodecode/controllers/admin/repos.py:348
msgid "An error occurred during deletion of repository user"
msgstr "Ocorreu um erro ao excluir usuário de repositório"
-#: rhodecode/controllers/admin/repos.py:335
+#: rhodecode/controllers/admin/repos.py:367
msgid "An error occurred during deletion of repository users groups"
msgstr "Ocorreu um erro ao excluir grupo de usuário de repositório"
-#: rhodecode/controllers/admin/repos.py:352
+#: rhodecode/controllers/admin/repos.py:385
msgid "An error occurred during deletion of repository stats"
msgstr "Ocorreu um erro ao excluir estatísticas de repositório"
-#: rhodecode/controllers/admin/repos.py:367
+#: rhodecode/controllers/admin/repos.py:402
msgid "An error occurred during cache invalidation"
msgstr "Ocorreu um erro ao invalidar o cache"
-#: rhodecode/controllers/admin/repos.py:387
+#: rhodecode/controllers/admin/repos.py:422
+#, fuzzy
+msgid "An error occurred during unlocking"
+msgstr "Ocorreu um erro durante essa operação"
+
+#: rhodecode/controllers/admin/repos.py:442
msgid "Updated repository visibility in public journal"
msgstr "Atualizada a visibilidade do repositório no diário público"
-#: rhodecode/controllers/admin/repos.py:390
+#: rhodecode/controllers/admin/repos.py:446
msgid "An error occurred during setting this repository in public journal"
msgstr "Ocorreu um erro ao ajustar esse repositório no diário público"
-#: rhodecode/controllers/admin/repos.py:395
-#: rhodecode/model/forms.py:53
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
msgid "Token mismatch"
msgstr "Descompasso de Token"
-#: rhodecode/controllers/admin/repos.py:408
+#: rhodecode/controllers/admin/repos.py:464
msgid "Pulled from remote location"
msgstr "Realizado pull de localização remota"
-#: rhodecode/controllers/admin/repos.py:410
+#: rhodecode/controllers/admin/repos.py:466
msgid "An error occurred during pull from remote location"
msgstr "Ocorreu um erro ao realizar pull de localização remota"
-#: rhodecode/controllers/admin/repos_groups.py:83
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr "Nada"
+
+#: rhodecode/controllers/admin/repos.py:484
+#, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr "Marcado repositório %s como bifurcação de %s"
+
+#: rhodecode/controllers/admin/repos.py:488
+msgid "An error occurred during this operation"
+msgstr "Ocorreu um erro durante essa operação"
+
+#: rhodecode/controllers/admin/repos_groups.py:116
#, python-format
msgid "created repos group %s"
msgstr "criado grupo de repositórios %s"
-#: rhodecode/controllers/admin/repos_groups.py:96
+#: rhodecode/controllers/admin/repos_groups.py:129
#, python-format
msgid "error occurred during creation of repos group %s"
msgstr "ccorreu um erro ao criar grupo de repositório %s"
-#: rhodecode/controllers/admin/repos_groups.py:130
+#: rhodecode/controllers/admin/repos_groups.py:163
#, python-format
msgid "updated repos group %s"
msgstr "atualizado grupo de repositórios %s"
-#: rhodecode/controllers/admin/repos_groups.py:143
+#: rhodecode/controllers/admin/repos_groups.py:176
#, python-format
msgid "error occurred during update of repos group %s"
msgstr "ocorreu um erro ao atualizar grupo de repositórios %s"
-#: rhodecode/controllers/admin/repos_groups.py:164
+#: rhodecode/controllers/admin/repos_groups.py:194
#, python-format
msgid "This group contains %s repositores and cannot be deleted"
msgstr "Esse grupo contém %s repositórios e não pode ser excluído"
-#: rhodecode/controllers/admin/repos_groups.py:171
+#: rhodecode/controllers/admin/repos_groups.py:202
#, python-format
msgid "removed repos group %s"
msgstr "removido grupo de repositórios %s"
-#: rhodecode/controllers/admin/repos_groups.py:175
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr "Nao é possível excluir este grupo pois ele ainda contém subgrupos"
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
#, python-format
msgid "error occurred during deletion of repos group %s"
msgstr "ccorreu um erro ao excluir grupo de repositórios %s"
-#: rhodecode/controllers/admin/settings.py:109
+#: rhodecode/controllers/admin/repos_groups.py:238
+msgid "An error occurred during deletion of group user"
+msgstr "Ocorreu um erro ao excluir o usuário de grupo"
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr "Ocorreu um erro ao excluir o grupo do grupo de usuários"
+
+#: rhodecode/controllers/admin/settings.py:121
#, python-format
msgid "Repositories successfully rescanned added: %s,removed: %s"
msgstr "Repositórios varridos com sucesso adicionados: %s, removidos: %s"
-#: rhodecode/controllers/admin/settings.py:118
+#: rhodecode/controllers/admin/settings.py:129
msgid "Whoosh reindex task scheduled"
msgstr "Tarefa de reindexação do whoosh agendada"
-#: rhodecode/controllers/admin/settings.py:143
+#: rhodecode/controllers/admin/settings.py:160
msgid "Updated application settings"
msgstr "Configurações da aplicação atualizadas"
-#: rhodecode/controllers/admin/settings.py:148
-#: rhodecode/controllers/admin/settings.py:215
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
msgid "error occurred during updating application settings"
msgstr "ocorreu um erro ao atualizar as configurações da aplicação"
-#: rhodecode/controllers/admin/settings.py:210
-msgid "Updated mercurial settings"
+#: rhodecode/controllers/admin/settings.py:200
+#, fuzzy
+msgid "Updated visualisation settings"
+msgstr "Configurações da aplicação atualizadas"
+
+#: rhodecode/controllers/admin/settings.py:205
+#, fuzzy
+msgid "error occurred during updating visualisation settings"
+msgstr "ocorreu um erro ao atualizar as configurações da aplicação"
+
+#: rhodecode/controllers/admin/settings.py:271
+#, fuzzy
+msgid "Updated VCS settings"
msgstr "Atualizadas as configurações do mercurial"
-#: rhodecode/controllers/admin/settings.py:236
+#: rhodecode/controllers/admin/settings.py:285
msgid "Added new hook"
msgstr "Adicionado novo gancho"
-#: rhodecode/controllers/admin/settings.py:247
+#: rhodecode/controllers/admin/settings.py:297
msgid "Updated hooks"
msgstr "Atualizados os ganchos"
-#: rhodecode/controllers/admin/settings.py:251
+#: rhodecode/controllers/admin/settings.py:301
msgid "error occurred during hook creation"
msgstr "ocorreu um erro ao criar gancho"
-#: rhodecode/controllers/admin/settings.py:310
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr "Tarefa de e-mail criada"
+
+#: rhodecode/controllers/admin/settings.py:375
msgid "You can't edit this user since it's crucial for entire application"
msgstr "Você não pode editar esse usuário pois ele é crucial para toda a aplicação"
-#: rhodecode/controllers/admin/settings.py:339
+#: rhodecode/controllers/admin/settings.py:406
msgid "Your account was updated successfully"
msgstr "Sua conta foi atualizada com sucesso"
-#: rhodecode/controllers/admin/settings.py:359
-#: rhodecode/controllers/admin/users.py:130
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
#, python-format
msgid "error occurred during update of user %s"
msgstr "ocorreu um erro ao atualizar o usuário %s"
-#: rhodecode/controllers/admin/users.py:78
+#: rhodecode/controllers/admin/users.py:130
#, python-format
msgid "created user %s"
msgstr "usuário %s criado"
-#: rhodecode/controllers/admin/users.py:90
+#: rhodecode/controllers/admin/users.py:142
#, python-format
msgid "error occurred during creation of user %s"
msgstr "ocorreu um erro ao criar o usuário %s"
-#: rhodecode/controllers/admin/users.py:116
+#: rhodecode/controllers/admin/users.py:171
msgid "User updated successfully"
msgstr "Usuário atualizado com sucesso"
-#: rhodecode/controllers/admin/users.py:146
+#: rhodecode/controllers/admin/users.py:207
msgid "successfully deleted user"
msgstr "usuário excluído com sucesso"
-#: rhodecode/controllers/admin/users.py:150
+#: rhodecode/controllers/admin/users.py:212
msgid "An error occurred during deletion of user"
msgstr "Ocorreu um erro ao excluir o usuário"
-#: rhodecode/controllers/admin/users.py:166
+#: rhodecode/controllers/admin/users.py:226
msgid "You can't edit this user"
msgstr "Você não pode editar esse usuário"
-#: rhodecode/controllers/admin/users.py:195
-#: rhodecode/controllers/admin/users_groups.py:202
+#: rhodecode/controllers/admin/users.py:266
msgid "Granted 'repository create' permission to user"
msgstr "Concedida permissão de 'criar repositório' ao usuário"
-#: rhodecode/controllers/admin/users.py:204
-#: rhodecode/controllers/admin/users_groups.py:211
+#: rhodecode/controllers/admin/users.py:271
msgid "Revoked 'repository create' permission to user"
msgstr "Revogada permissão de 'criar repositório' ao usuário"
-#: rhodecode/controllers/admin/users_groups.py:74
+#: rhodecode/controllers/admin/users.py:277
+#, fuzzy
+msgid "Granted 'repository fork' permission to user"
+msgstr "Concedida permissão de 'criar repositório' ao usuário"
+
+#: rhodecode/controllers/admin/users.py:282
+#, fuzzy
+msgid "Revoked 'repository fork' permission to user"
+msgstr "Revogada permissão de 'criar repositório' ao usuário"
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+#, fuzzy
+msgid "An error occurred during permissions saving"
+msgstr "Ocorreu um erro durante essa operação"
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:309
+#, fuzzy
+msgid "An error occurred during email saving"
+msgstr "Ocorreu um erro durante essa operação"
+
+#: rhodecode/controllers/admin/users.py:319
+#, fuzzy
+msgid "Removed email from user"
+msgstr "removido grupo de repositórios %s"
+
+#: rhodecode/controllers/admin/users_groups.py:84
#, python-format
msgid "created users group %s"
msgstr "criado grupo de usuários %s"
-#: rhodecode/controllers/admin/users_groups.py:86
+#: rhodecode/controllers/admin/users_groups.py:95
#, python-format
msgid "error occurred during creation of users group %s"
msgstr "ocorreu um erro ao criar o grupo de usuários %s"
-#: rhodecode/controllers/admin/users_groups.py:119
+#: rhodecode/controllers/admin/users_groups.py:135
#, python-format
msgid "updated users group %s"
msgstr "grupo de usuários %s atualizado"
-#: rhodecode/controllers/admin/users_groups.py:138
+#: rhodecode/controllers/admin/users_groups.py:157
#, python-format
msgid "error occurred during update of users group %s"
msgstr "ocorreu um erro ao atualizar o grupo de usuários %s"
-#: rhodecode/controllers/admin/users_groups.py:154
+#: rhodecode/controllers/admin/users_groups.py:174
msgid "successfully deleted users group"
msgstr "grupo de usuários excluído com sucesso"
-#: rhodecode/controllers/admin/users_groups.py:158
+#: rhodecode/controllers/admin/users_groups.py:179
msgid "An error occurred during deletion of users group"
msgstr "Ocorreu um erro ao excluir o grupo de usuários"
-#: rhodecode/lib/__init__.py:279
-msgid "year"
-msgstr "ano"
-
-#: rhodecode/lib/__init__.py:280
-msgid "month"
-msgstr "mês"
-
-#: rhodecode/lib/__init__.py:281
-msgid "day"
-msgstr "dia"
-
-#: rhodecode/lib/__init__.py:282
-msgid "hour"
-msgstr "hora"
-
-#: rhodecode/lib/__init__.py:283
-msgid "minute"
-msgstr "minuto"
+#: rhodecode/controllers/admin/users_groups.py:233
+#, fuzzy
+msgid "Granted 'repository create' permission to users group"
+msgstr "Concedida permissão de 'criar repositório' ao usuário"
-#: rhodecode/lib/__init__.py:284
-msgid "second"
-msgstr "segundo"
+#: rhodecode/controllers/admin/users_groups.py:238
+#, fuzzy
+msgid "Revoked 'repository create' permission to users group"
+msgstr "Revogada permissão de 'criar repositório' ao usuário"
-#: rhodecode/lib/__init__.py:293
-msgid "ago"
-msgstr "atrás"
+#: rhodecode/controllers/admin/users_groups.py:244
+#, fuzzy
+msgid "Granted 'repository fork' permission to users group"
+msgstr "Concedida permissão de 'criar repositório' ao usuário"
-#: rhodecode/lib/__init__.py:296
-msgid "just now"
-msgstr "agora há pouco"
+#: rhodecode/controllers/admin/users_groups.py:249
+#, fuzzy
+msgid "Revoked 'repository fork' permission to users group"
+msgstr "Revogada permissão de 'criar repositório' ao usuário"
-#: rhodecode/lib/auth.py:377
+#: rhodecode/lib/auth.py:499
msgid "You need to be a registered user to perform this action"
msgstr "Você precisa ser um usuário registrado para realizar essa ação"
-#: rhodecode/lib/auth.py:421
+#: rhodecode/lib/auth.py:540
msgid "You need to be a signed in to view this page"
msgstr "Você precisa estar logado para ver essa página"
-#: rhodecode/lib/helpers.py:307
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
+msgstr ""
+"Conjunto de mudanças é grande demais e foi cortado, use o menu de "
+"diferenças para ver as diferenças"
+
+#: rhodecode/lib/diffs.py:96
+msgid "No changes detected"
+msgstr "Nenhuma alteração detectada"
+
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:484
msgid "True"
msgstr "Verdadeiro"
-#: rhodecode/lib/helpers.py:311
+#: rhodecode/lib/helpers.py:488
msgid "False"
msgstr "Falso"
-#: rhodecode/lib/helpers.py:352
+#: rhodecode/lib/helpers.py:532
+msgid "Changeset not found"
+msgstr "Conjunto de alterações não encontrado"
+
+#: rhodecode/lib/helpers.py:555
#, python-format
msgid "Show all combined changesets %s->%s"
msgstr "Ver todos os conjuntos de mudanças combinados %s->%s"
-#: rhodecode/lib/helpers.py:356
+#: rhodecode/lib/helpers.py:561
msgid "compare view"
msgstr "comparar exibir"
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:581
msgid "and"
msgstr "e"
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:582
#, python-format
msgid "%s more"
msgstr "%s mais"
-#: rhodecode/lib/helpers.py:367
-#: rhodecode/templates/changelog/changelog.html:14
-#: rhodecode/templates/changelog/changelog.html:39
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
msgid "revisions"
msgstr "revisões"
-#: rhodecode/lib/helpers.py:385
+#: rhodecode/lib/helpers.py:606
msgid "fork name "
msgstr "nome da bifurcação"
-#: rhodecode/lib/helpers.py:388
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:626
msgid "[deleted] repository"
msgstr "repositório [excluído]"
-#: rhodecode/lib/helpers.py:389
-#: rhodecode/lib/helpers.py:393
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
msgid "[created] repository"
msgstr "repositório [criado]"
-#: rhodecode/lib/helpers.py:390
-#: rhodecode/lib/helpers.py:394
+#: rhodecode/lib/helpers.py:630
+msgid "[created] repository as fork"
+msgstr "repositório [criado] como uma bifurcação"
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
msgid "[forked] repository"
msgstr "repositório [bifurcado]"
-#: rhodecode/lib/helpers.py:391
-#: rhodecode/lib/helpers.py:395
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
msgid "[updated] repository"
msgstr "repositório [atualizado]"
-#: rhodecode/lib/helpers.py:392
+#: rhodecode/lib/helpers.py:636
msgid "[delete] repository"
msgstr "[excluir] repositório"
-#: rhodecode/lib/helpers.py:396
+#: rhodecode/lib/helpers.py:644
+#, fuzzy
+msgid "[created] user"
+msgstr "usuário %s criado"
+
+#: rhodecode/lib/helpers.py:646
+#, fuzzy
+msgid "[updated] user"
+msgstr "grupo de usuários %s atualizado"
+
+#: rhodecode/lib/helpers.py:648
+#, fuzzy
+msgid "[created] users group"
+msgstr "criado grupo de usuários %s"
+
+#: rhodecode/lib/helpers.py:650
+#, fuzzy
+msgid "[updated] users group"
+msgstr "grupo de usuários %s atualizado"
+
+#: rhodecode/lib/helpers.py:652
+#, fuzzy
+msgid "[commented] on revision in repository"
+msgstr "repositório [criado]"
+
+#: rhodecode/lib/helpers.py:654
+#, fuzzy
+msgid "[commented] on pull request for"
+msgstr "repositório [criado]"
+
+#: rhodecode/lib/helpers.py:656
+#, fuzzy
+msgid "[closed] pull request for"
+msgstr "repositório [criado]"
+
+#: rhodecode/lib/helpers.py:658
msgid "[pushed] into"
msgstr "[realizado push] para"
-#: rhodecode/lib/helpers.py:397
-msgid "[committed via RhodeCode] into"
+#: rhodecode/lib/helpers.py:660
+#, fuzzy
+msgid "[committed via RhodeCode] into repository"
msgstr "[realizado commit via RhodeCode] para"
-#: rhodecode/lib/helpers.py:398
-msgid "[pulled from remote] into"
+#: rhodecode/lib/helpers.py:662
+#, fuzzy
+msgid "[pulled from remote] into repository"
msgstr "[realizado pull remoto] para"
-#: rhodecode/lib/helpers.py:399
+#: rhodecode/lib/helpers.py:664
msgid "[pulled] from"
msgstr "[realizado pull] a partir de"
-#: rhodecode/lib/helpers.py:400
+#: rhodecode/lib/helpers.py:666
msgid "[started following] repository"
msgstr "[passou a seguir] o repositório"
-#: rhodecode/lib/helpers.py:401
+#: rhodecode/lib/helpers.py:668
msgid "[stopped following] repository"
msgstr "[parou de seguir] o repositório"
-#: rhodecode/lib/helpers.py:577
+#: rhodecode/lib/helpers.py:840
#, python-format
msgid " and %s more"
msgstr " e mais %s"
-#: rhodecode/lib/helpers.py:581
+#: rhodecode/lib/helpers.py:844
msgid "No Files"
msgstr "Nenhum Arquivo"
-#: rhodecode/model/forms.py:66
-msgid "Invalid username"
-msgstr "Nome de usuário inválido"
+#: rhodecode/lib/utils2.py:335
+#, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] "%d ano"
+msgstr[1] "%d anos"
+
+#: rhodecode/lib/utils2.py:336
+#, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] "%d mês"
+msgstr[1] "%d meses"
+
+#: rhodecode/lib/utils2.py:337
+#, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] "%d dia"
+msgstr[1] "%d dias"
+
+#: rhodecode/lib/utils2.py:338
+#, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] "%d hora"
+msgstr[1] "%d horas"
+
+#: rhodecode/lib/utils2.py:339
+#, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] "%d minuto"
+msgstr[1] "%d minutos"
+
+#: rhodecode/lib/utils2.py:340
+#, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] "%d segundo"
+msgstr[1] "%d segundos"
+
+#: rhodecode/lib/utils2.py:355
+#, python-format
+msgid "%s ago"
+msgstr "%s atrás"
+
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
+msgstr "%s e %s atrás"
+
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
+msgstr "agora há pouco"
+
+#: rhodecode/lib/celerylib/tasks.py:269
+msgid "password reset link"
+msgstr "link de reinicialização de senha"
+
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
+msgstr "na linha %s"
+
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
+msgstr "[Menção]"
+
+#: rhodecode/model/db.py:1140
+#, fuzzy
+msgid "Repository no access"
+msgstr "repositórios"
+
+#: rhodecode/model/db.py:1141
+#, fuzzy
+msgid "Repository read access"
+msgstr "Esse repositório já existe"
+
+#: rhodecode/model/db.py:1142
+#, fuzzy
+msgid "Repository write access"
+msgstr "repositórios"
+
+#: rhodecode/model/db.py:1143
+#, fuzzy
+msgid "Repository admin access"
+msgstr "repositórios"
+
+#: rhodecode/model/db.py:1145
+#, fuzzy
+msgid "Repositories Group no access"
+msgstr "grupos de repositórios"
+
+#: rhodecode/model/db.py:1146
+#, fuzzy
+msgid "Repositories Group read access"
+msgstr "grupos de repositórios"
+
+#: rhodecode/model/db.py:1147
+#, fuzzy
+msgid "Repositories Group write access"
+msgstr "grupos de repositórios"
+
+#: rhodecode/model/db.py:1148
+#, fuzzy
+msgid "Repositories Group admin access"
+msgstr "grupos de repositórios"
+
+#: rhodecode/model/db.py:1150
+#, fuzzy
+msgid "RhodeCode Administrator"
+msgstr "Administração de usuários"
+
+#: rhodecode/model/db.py:1151
+#, fuzzy
+msgid "Repository creation disabled"
+msgstr "Criação de repositório"
+
+#: rhodecode/model/db.py:1152
+#, fuzzy
+msgid "Repository creation enabled"
+msgstr "Criação de repositório"
+
+#: rhodecode/model/db.py:1153
+#, fuzzy
+msgid "Repository forking disabled"
+msgstr "Criação de repositório"
+
+#: rhodecode/model/db.py:1154
+#, fuzzy
+msgid "Repository forking enabled"
+msgstr "Criação de repositório"
+
+#: rhodecode/model/db.py:1155
+#, fuzzy
+msgid "Register disabled"
+msgstr "desabilitado"
+
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
+msgstr ""
+
+#: rhodecode/model/db.py:1580
+#, fuzzy
+msgid "Approved"
+msgstr "removidos"
+
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
+msgstr ""
+
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
+msgstr ""
+
+#: rhodecode/model/forms.py:43
+msgid "Please enter a login"
+msgstr "Por favor entre um login"
+
+#: rhodecode/model/forms.py:44
+#, python-format
+msgid "Enter a value %(min)i characters long or more"
+msgstr "Entre um valor com %(min)i caracteres ou mais"
+
+#: rhodecode/model/forms.py:52
+msgid "Please enter a password"
+msgstr "Por favor entre com uma senha"
+
+#: rhodecode/model/forms.py:53
+#, python-format
+msgid "Enter %(min)i characters or more"
+msgstr "Entre com %(min)i caracteres ou mais"
-#: rhodecode/model/forms.py:75
-msgid "This username already exists"
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr "comentado no commit"
+
+#: rhodecode/model/notification.py:221
+msgid "sent message"
+msgstr "mensagem enviada"
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr "mencionou você"
+
+#: rhodecode/model/notification.py:223
+msgid "registered in RhodeCode"
+msgstr "registrado no RhodeCode"
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
+msgstr ""
+
+#: rhodecode/model/notification.py:225
+#, fuzzy
+msgid "commented on pull request"
+msgstr "comentado no commit"
+
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+#, fuzzy
+msgid "latest tip"
+msgstr "último login"
+
+#: rhodecode/model/user.py:230
+msgid "new user registration"
+msgstr "registro de novo usuário"
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
+msgid "You can't Edit this user since it's crucial for entire application"
+msgstr ""
+"Você não pode Editar esse usuário, pois ele é crucial para toda a "
+"aplicação"
+
+#: rhodecode/model/user.py:323
+msgid "You can't remove this user since it's crucial for entire application"
+msgstr ""
+"Você não pode remover esse usuário, pois ele é crucial para toda a "
+"aplicação"
+
+#: rhodecode/model/user.py:329
+#, python-format
+msgid ""
+"user \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories. %s"
+msgstr ""
+"usuário \"%s\" ainda é dono de %s repositórios e não pode ser removido. "
+"Troque os donos ou remova esses repositórios. %s"
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, fuzzy, python-format
+msgid "Username \"%(username)s\" already exists"
msgstr "Esse nome de usuário já existe"
-#: rhodecode/model/forms.py:79
-msgid "Username may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
-msgstr "Nome de usuário pode conter somente caracteres alfanuméricos, sublinha, pontos e hífens e deve iniciar com caractere alfanumérico"
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr ""
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with alphanumeric character"
+msgstr ""
+"Nome de usuário pode conter somente caracteres alfanuméricos, sublinha, "
+"pontos e hífens e deve iniciar com caractere alfanumérico"
-#: rhodecode/model/forms.py:94
-msgid "Invalid group name"
-msgstr "Nome de grupo inválido"
+#: rhodecode/model/validators.py:114
+#, fuzzy, python-format
+msgid "Username %(username)s is not valid"
+msgstr "Esse nome de usuário ou nome de grupo de usuários não é válido"
+
+#: rhodecode/model/validators.py:133
+#, fuzzy
+msgid "Invalid users group name"
+msgstr "nome de usuário inválido"
-#: rhodecode/model/forms.py:104
-msgid "This users group already exists"
+#: rhodecode/model/validators.py:134
+#, fuzzy, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
msgstr "Esse grupo de usuários já existe"
-#: rhodecode/model/forms.py:110
-msgid "Group name may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
-msgstr "Nome de grupo pode conter somente caracteres alfanuméricos, sublinha, pontos e hífens e deve iniciar com caractere alfanumérico"
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr ""
+"Nome de grupo de repositório pode conter somente caracteres "
+"alfanuméricos, sublinha, pontos e hífens e deve iniciar com caractere "
+"alfanumérico"
-#: rhodecode/model/forms.py:132
+#: rhodecode/model/validators.py:174
msgid "Cannot assign this group as parent"
msgstr "Não é possível associar esse grupo como progenitor"
-#: rhodecode/model/forms.py:148
-msgid "This group already exists"
-msgstr "Esse grupo já existe"
+#: rhodecode/model/validators.py:175
+#, fuzzy, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr "Esse nome de usuário já existe"
+
+#: rhodecode/model/validators.py:177
+#, fuzzy, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr "Já existe um repositório com esse nome"
-#: rhodecode/model/forms.py:164
-#: rhodecode/model/forms.py:172
-#: rhodecode/model/forms.py:180
-msgid "Invalid characters in password"
+#: rhodecode/model/validators.py:235
+#, fuzzy
+msgid "Invalid characters (non-ascii) in password"
msgstr "Caracteres inválidos na senha"
-#: rhodecode/model/forms.py:191
+#: rhodecode/model/validators.py:250
msgid "Passwords do not match"
msgstr "Senhas não conferem"
-#: rhodecode/model/forms.py:196
+#: rhodecode/model/validators.py:267
msgid "invalid password"
msgstr "senha inválida"
-#: rhodecode/model/forms.py:197
+#: rhodecode/model/validators.py:268
msgid "invalid user name"
msgstr "nome de usuário inválido"
-#: rhodecode/model/forms.py:198
+#: rhodecode/model/validators.py:269
msgid "Your account is disabled"
msgstr "Sua conta está desabilitada"
-#: rhodecode/model/forms.py:233
-msgid "This username is not valid"
-msgstr "Esse nome de usuário não é válido"
-
-#: rhodecode/model/forms.py:245
-msgid "This repository name is disallowed"
+#: rhodecode/model/validators.py:313
+#, fuzzy, python-format
+msgid "Repository name %(repo)s is disallowed"
msgstr "Esse nome de repositório não é permitido"
-#: rhodecode/model/forms.py:266
-#, python-format
-msgid "This repository already exists in group \"%s\""
-msgstr "Esse repositório já existe no grupo \"%s\""
+#: rhodecode/model/validators.py:315
+#, fuzzy, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr "Já existe um repositório com esse nome"
-#: rhodecode/model/forms.py:274
-msgid "This repository already exists"
-msgstr "Esse repositório já existe"
+#: rhodecode/model/validators.py:316
+#, fuzzy, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr "Esse repositório já existe em um grupo \"%s\""
+
+#: rhodecode/model/validators.py:318
+#, fuzzy, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
+msgstr "Já existe um repositório com esse nome"
-#: rhodecode/model/forms.py:312
-#: rhodecode/model/forms.py:319
+#: rhodecode/model/validators.py:431
msgid "invalid clone url"
msgstr "URL de clonagem inválida"
-#: rhodecode/model/forms.py:322
-msgid "Invalid clone url, provide a valid clone http\\s url"
+#: rhodecode/model/validators.py:432
+#, fuzzy
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
msgstr "URL de clonagem inválida, forneça uma URL válida de clonagem http\\s"
-#: rhodecode/model/forms.py:334
-msgid "Fork have to be the same type as original"
+#: rhodecode/model/validators.py:457
+#, fuzzy
+msgid "Fork have to be the same type as parent"
msgstr "Bifurcação precisa ser do mesmo tipo que o original"
-#: rhodecode/model/forms.py:341
+#: rhodecode/model/validators.py:478
msgid "This username or users group name is not valid"
msgstr "Esse nome de usuário ou nome de grupo de usuários não é válido"
-#: rhodecode/model/forms.py:403
+#: rhodecode/model/validators.py:562
msgid "This is not a valid path"
msgstr "Esse não é um caminho válido"
-#: rhodecode/model/forms.py:416
+#: rhodecode/model/validators.py:577
msgid "This e-mail address is already taken"
msgstr "Esse endereço de e-mail já está tomado"
-#: rhodecode/model/forms.py:427
-msgid "This e-mail address doesn't exist."
+#: rhodecode/model/validators.py:597
+#, fuzzy, python-format
+msgid "e-mail \"%(email)s\" does not exist."
msgstr "Esse endereço de e-mail não existe."
-#: rhodecode/model/forms.py:447
-msgid "The LDAP Login attribute of the CN must be specified - this is the name of the attribute that is equivalent to 'username'"
-msgstr "O atributo de login LDAP do CN deve ser especificado - isto é o nome do atributo que é equivalente ao 'nome de usuário'"
-
-#: rhodecode/model/forms.py:466
-msgid "Please enter a login"
-msgstr "Por favor entre um login"
-
-#: rhodecode/model/forms.py:467
-#, python-format
-msgid "Enter a value %(min)i characters long or more"
-msgstr "Entre um valor com %(min)i caracteres ou mais"
-
-#: rhodecode/model/forms.py:475
-msgid "Please enter a password"
-msgstr "Por favor entre com uma senha"
-
-#: rhodecode/model/forms.py:476
-#, python-format
-msgid "Enter %(min)i characters or more"
-msgstr "Entre com %(min)i caracteres ou mais"
-
-#: rhodecode/model/user.py:145
-msgid "[RhodeCode] New User registration"
-msgstr "[RhodeCode] Registro de Novo Usuário"
-
-#: rhodecode/model/user.py:157
-#: rhodecode/model/user.py:179
-msgid "You can't Edit this user since it's crucial for entire application"
-msgstr "Você não pode Editar esse usuário, pois ele é crucial para toda a aplicação"
-
-#: rhodecode/model/user.py:201
-msgid "You can't remove this user since it's crucial for entire application"
-msgstr "Você não pode remover esse usuário, pois ele é crucial para toda a aplicação"
+#: rhodecode/model/validators.py:634
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
+msgstr ""
+"O atributo de login LDAP do CN deve ser especificado - isto é o nome do "
+"atributo que é equivalente ao 'nome de usuário'"
-#: rhodecode/model/user.py:204
+#: rhodecode/model/validators.py:653
#, python-format
-msgid "This user still owns %s repositories and cannot be removed. Switch owners or remove those repositories"
-msgstr "Esse usuário ainda é dono de %s repositórios e não pode ser removido. Troque os donos ou remova esses repositórios"
+msgid "Revisions %(revs)s are already part of pull request or have set status"
+msgstr ""
-#: rhodecode/templates/index.html:4
+#: rhodecode/templates/index.html:3
msgid "Dashboard"
msgstr "Painel de Controle"
-#: rhodecode/templates/index_base.html:22
-#: rhodecode/templates/admin/users/user_edit_my_account.html:102
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
msgid "quick filter..."
msgstr "filtro rápido..."
-#: rhodecode/templates/index_base.html:23
-#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
msgid "repositories"
msgstr "repositórios"
-#: rhodecode/templates/index_base.html:29
-#: rhodecode/templates/admin/repos/repos.html:22
-msgid "ADD NEW REPOSITORY"
-msgstr "ADICIONAR NOVO REPOSITÓRIO"
+#: rhodecode/templates/index_base.html:13
+#: rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
+msgstr "ADICIONAR REPOSITÓRIO"
-#: rhodecode/templates/index_base.html:41
+#: rhodecode/templates/index_base.html:29
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
@@ -854,159 +1330,158 @@ msgstr "ADICIONAR NOVO REPOSITÓRIO"
msgid "Group name"
msgstr "Nome do grupo"
-#: rhodecode/templates/index_base.html:42
-#: rhodecode/templates/index_base.html:73
-#: rhodecode/templates/admin/repos/repo_add_base.html:44
-#: rhodecode/templates/admin/repos/repo_edit.html:64
-#: rhodecode/templates/admin/repos/repos.html:31
+#: rhodecode/templates/index_base.html:30
+#: rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142
+#: rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
-#: rhodecode/templates/settings/repo_fork.html:40
-#: rhodecode/templates/settings/repo_settings.html:40
-#: rhodecode/templates/summary/summary.html:92
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
msgid "Description"
msgstr "Descrição"
-#: rhodecode/templates/index_base.html:53
+#: rhodecode/templates/index_base.html:40
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
msgid "Repositories group"
msgstr "Grupo de repositórios"
-#: rhodecode/templates/index_base.html:72
+#: rhodecode/templates/index_base.html:70
+#: rhodecode/templates/index_base.html:166
#: rhodecode/templates/admin/repos/repo_add_base.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:32
-#: rhodecode/templates/admin/repos/repos.html:30
-#: rhodecode/templates/admin/users/user_edit_my_account.html:117
-#: rhodecode/templates/files/files_browser.html:157
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
#: rhodecode/templates/settings/repo_settings.html:31
-#: rhodecode/templates/summary/summary.html:31
-#: rhodecode/templates/summary/summary.html:107
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36
+#: rhodecode/templates/tags/tags_data.html:6
msgid "Name"
msgstr "Nome"
-#: rhodecode/templates/index_base.html:74
-#: rhodecode/templates/admin/repos/repos.html:32
-#: rhodecode/templates/summary/summary.html:114
+#: rhodecode/templates/index_base.html:72
msgid "Last change"
msgstr "Última alteração"
-#: rhodecode/templates/index_base.html:75
-#: rhodecode/templates/admin/repos/repos.html:33
+#: rhodecode/templates/index_base.html:73
+#: rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
msgid "Tip"
msgstr "Ponta"
-#: rhodecode/templates/index_base.html:76
-#: rhodecode/templates/admin/repos/repo_edit.html:97
+#: rhodecode/templates/index_base.html:74
+#: rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
msgid "Owner"
msgstr "Dono"
-#: rhodecode/templates/index_base.html:77
-#: rhodecode/templates/journal/public_journal.html:20
-#: rhodecode/templates/summary/summary.html:180
-#: rhodecode/templates/summary/summary.html:183
+#: rhodecode/templates/index_base.html:75
+#: rhodecode/templates/summary/summary.html:48
+#: rhodecode/templates/summary/summary.html:51
msgid "RSS"
msgstr "RSS"
-#: rhodecode/templates/index_base.html:78
-#: rhodecode/templates/journal/public_journal.html:23
-#: rhodecode/templates/summary/summary.html:181
-#: rhodecode/templates/summary/summary.html:184
+#: rhodecode/templates/index_base.html:76
msgid "Atom"
msgstr "Atom"
-#: rhodecode/templates/index_base.html:87
-#: rhodecode/templates/index_base.html:89
-#: rhodecode/templates/index_base.html:91
-#: rhodecode/templates/base/base.html:209
-#: rhodecode/templates/base/base.html:211
-#: rhodecode/templates/base/base.html:213
-#: rhodecode/templates/summary/summary.html:4
-msgid "Summary"
-msgstr "Sumário"
-
-#: rhodecode/templates/index_base.html:95
-#: rhodecode/templates/index_base.html:97
-#: rhodecode/templates/index_base.html:99
-#: rhodecode/templates/base/base.html:225
-#: rhodecode/templates/base/base.html:227
-#: rhodecode/templates/base/base.html:229
-#: rhodecode/templates/changelog/changelog.html:6
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "Changelog"
-msgstr "Registro de alterações"
-
-#: rhodecode/templates/index_base.html:103
-#: rhodecode/templates/index_base.html:105
-#: rhodecode/templates/index_base.html:107
-#: rhodecode/templates/base/base.html:268
-#: rhodecode/templates/base/base.html:270
-#: rhodecode/templates/base/base.html:272
-#: rhodecode/templates/files/files.html:4
-msgid "Files"
-msgstr "Arquivos"
-
-#: rhodecode/templates/index_base.html:116
-#: rhodecode/templates/admin/repos/repos.html:42
-#: rhodecode/templates/admin/users/user_edit_my_account.html:127
-#: rhodecode/templates/summary/summary.html:48
-msgid "Mercurial repository"
-msgstr "Repositório Mercurial"
-
-#: rhodecode/templates/index_base.html:118
-#: rhodecode/templates/admin/repos/repos.html:44
-#: rhodecode/templates/admin/users/user_edit_my_account.html:129
-#: rhodecode/templates/summary/summary.html:51
-msgid "Git repository"
-msgstr "Repositório Git"
-
-#: rhodecode/templates/index_base.html:123
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
-#: rhodecode/templates/journal/journal.html:53
-#: rhodecode/templates/summary/summary.html:56
-msgid "private repository"
-msgstr "repositório privado"
-
-#: rhodecode/templates/index_base.html:125
-#: rhodecode/templates/journal/journal.html:55
-#: rhodecode/templates/summary/summary.html:58
-msgid "public repository"
-msgstr "repositório público"
-
-#: rhodecode/templates/index_base.html:133
-#: rhodecode/templates/base/base.html:291
-#: rhodecode/templates/settings/repo_fork.html:13
-msgid "fork"
-msgstr "bifurcação"
-
-#: rhodecode/templates/index_base.html:134
-#: rhodecode/templates/admin/repos/repos.html:60
-#: rhodecode/templates/admin/users/user_edit_my_account.html:143
-#: rhodecode/templates/summary/summary.html:69
-#: rhodecode/templates/summary/summary.html:71
-msgid "Fork of"
-msgstr "Bifurcação de"
-
-#: rhodecode/templates/index_base.html:155
-#: rhodecode/templates/admin/repos/repos.html:73
-msgid "No changesets yet"
-msgstr "Ainda não há conjuntos de mudanças"
-
-#: rhodecode/templates/index_base.html:161
-#: rhodecode/templates/index_base.html:163
+#: rhodecode/templates/index_base.html:110
+#: rhodecode/templates/index_base.html:112
#, python-format
msgid "Subscribe to %s rss feed"
msgstr "Assinar o feed rss de %s"
-#: rhodecode/templates/index_base.html:168
-#: rhodecode/templates/index_base.html:170
+#: rhodecode/templates/index_base.html:117
+#: rhodecode/templates/index_base.html:119
#, python-format
msgid "Subscribe to %s atom feed"
msgstr "Assinar o feed atom de %s"
-#: rhodecode/templates/login.html:5
-#: rhodecode/templates/login.html:54
-#: rhodecode/templates/base/base.html:38
+#: rhodecode/templates/index_base.html:140
+msgid "Group Name"
+msgstr "Nome do Grupo"
+
+#: rhodecode/templates/index_base.html:158
+#: rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
+msgstr "Clique para ordenar em ordem crescente"
+
+#: rhodecode/templates/index_base.html:159
+#: rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
+msgstr "Clique para ordenar em ordem descrescente"
+
+#: rhodecode/templates/index_base.html:169
+msgid "Last Change"
+msgstr "Última Alteração"
+
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
+msgstr "Nenhum registro encontrado."
+
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
+msgstr "Erro de dados."
+
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+msgid "Loading..."
+msgstr "Carregando..."
+
+#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
msgid "Sign In"
msgstr "Entrar"
@@ -1014,31 +1489,32 @@ msgstr "Entrar"
msgid "Sign In to"
msgstr "Entrar em"
-#: rhodecode/templates/login.html:31
-#: rhodecode/templates/register.html:20
+#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
#: rhodecode/templates/admin/admin_log.html:5
#: rhodecode/templates/admin/users/user_add.html:32
-#: rhodecode/templates/admin/users/user_edit.html:47
-#: rhodecode/templates/admin/users/user_edit_my_account.html:45
-#: rhodecode/templates/base/base.html:15
-#: rhodecode/templates/summary/summary.html:106
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
msgid "Username"
msgstr "Nome de usuário"
-#: rhodecode/templates/login.html:40
-#: rhodecode/templates/register.html:29
+#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
#: rhodecode/templates/admin/ldap/ldap.html:46
#: rhodecode/templates/admin/users/user_add.html:41
-#: rhodecode/templates/base/base.html:24
+#: rhodecode/templates/base/base.html:92
msgid "Password"
msgstr "Senha"
+#: rhodecode/templates/login.html:50
+msgid "Remember me"
+msgstr "Lembre-se de mim"
+
#: rhodecode/templates/login.html:60
msgid "Forgot your password ?"
msgstr "Esqueceu sua senha ?"
-#: rhodecode/templates/login.html:63
-#: rhodecode/templates/base/base.html:35
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
msgid "Don't have an account ?"
msgstr "Não possui uma conta ?"
@@ -1060,10 +1536,11 @@ msgstr "Reinicializar minha senha"
#: rhodecode/templates/password_reset.html:31
msgid "Password reset link will be send to matching email address"
-msgstr "Link de reinicialização de senha será enviado ao endereço de e-mail correspondente"
+msgstr ""
+"Link de reinicialização de senha será enviado ao endereço de e-mail "
+"correspondente"
-#: rhodecode/templates/register.html:5
-#: rhodecode/templates/register.html:74
+#: rhodecode/templates/register.html:5 rhodecode/templates/register.html:74
msgid "Sign Up"
msgstr "Inscrever-se"
@@ -1076,24 +1553,24 @@ msgid "Re-enter password"
msgstr "Repita a senha"
#: rhodecode/templates/register.html:47
-#: rhodecode/templates/admin/users/user_add.html:50
-#: rhodecode/templates/admin/users/user_edit.html:74
-#: rhodecode/templates/admin/users/user_edit_my_account.html:63
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
msgid "First Name"
msgstr "Primeiro Nome"
#: rhodecode/templates/register.html:56
-#: rhodecode/templates/admin/users/user_add.html:59
-#: rhodecode/templates/admin/users/user_edit.html:83
-#: rhodecode/templates/admin/users/user_edit_my_account.html:72
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
msgid "Last Name"
msgstr "Último Nome"
#: rhodecode/templates/register.html:65
-#: rhodecode/templates/admin/users/user_add.html:68
-#: rhodecode/templates/admin/users/user_edit.html:92
-#: rhodecode/templates/admin/users/user_edit_my_account.html:81
-#: rhodecode/templates/summary/summary.html:108
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
msgid "Email"
msgstr "E-mail"
@@ -1105,20 +1582,59 @@ msgstr "Sua conta será ativada logo após o registro ser concluído"
msgid "Your account must wait for activation by administrator"
msgstr "Sua conta precisa esperar ativação por um administrador"
-#: rhodecode/templates/repo_switcher_list.html:14
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
msgid "Private repository"
msgstr "Repositório privado"
-#: rhodecode/templates/repo_switcher_list.html:19
+#: rhodecode/templates/repo_switcher_list.html:16
msgid "Public repository"
msgstr "Repositório público"
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr "ramos"
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr "Ainda não há ramos"
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr "etiquetas"
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr "Ainda não há etiquetas"
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr "marcadores"
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+msgid "There are no bookmarks yet"
+msgstr "Ainda não há marcadores"
+
#: rhodecode/templates/admin/admin.html:5
#: rhodecode/templates/admin/admin.html:9
msgid "Admin journal"
msgstr "Diário do administrador"
#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
msgid "Action"
msgstr "Ação"
@@ -1127,6 +1643,11 @@ msgid "Repository"
msgstr "Repositório"
#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37
+#: rhodecode/templates/tags/tags_data.html:7
msgid "Date"
msgstr "Data"
@@ -1134,7 +1655,7 @@ msgstr "Data"
msgid "From IP"
msgstr "A partir do IP"
-#: rhodecode/templates/admin/admin_log.html:52
+#: rhodecode/templates/admin/admin_log.html:53
msgid "No actions yet"
msgstr "Ainda não há ações"
@@ -1211,23 +1732,64 @@ msgid "E-mail Attribute"
msgstr "Atributo de E-mail"
#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
#: rhodecode/templates/admin/settings/hooks.html:73
-#: rhodecode/templates/admin/users/user_edit.html:117
-#: rhodecode/templates/admin/users/user_edit.html:142
-#: rhodecode/templates/admin/users/user_edit_my_account.html:89
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:263
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
msgid "Save"
msgstr "Salvar"
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr "Minhas Notificações"
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+#, fuzzy
+msgid "Comments"
+msgstr "commits"
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254
+#: rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr "Marcar tudo como lido"
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+msgid "No notifications here yet"
+msgstr "Ainda não há notificações aqui"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+msgid "Show notification"
+msgstr "Mostrar notificação"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+msgid "Notifications"
+msgstr "Notificações"
+
#: rhodecode/templates/admin/permissions/permissions.html:5
msgid "Permissions administration"
msgstr "Administração de permissões"
#: rhodecode/templates/admin/permissions/permissions.html:11
-#: rhodecode/templates/admin/repos/repo_edit.html:109
-#: rhodecode/templates/admin/users/user_edit.html:127
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:248
-#: rhodecode/templates/settings/repo_settings.html:58
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
msgid "Permissions"
msgstr "Permissões"
@@ -1244,8 +1806,14 @@ msgid "Repository permission"
msgstr "Permissão de repositório"
#: rhodecode/templates/admin/permissions/permissions.html:49
-msgid "All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost"
-msgstr "Todas as permissões padrão em cada repositório serão reinicializadas para as permissões escolhidas. Note que todas as permissões padrão customizadas nos repositórios serão perdidas"
+msgid ""
+"All default permissions on each repository will be reset to choosen "
+"permission, note that all custom default permission on repositories will "
+"be lost"
+msgstr ""
+"Todas as permissões padrão em cada repositório serão reinicializadas para"
+" as permissões escolhidas. Note que todas as permissões padrão "
+"customizadas nos repositórios serão perdidas"
#: rhodecode/templates/admin/permissions/permissions.html:50
msgid "overwrite existing settings"
@@ -1260,6 +1828,12 @@ msgid "Repository creation"
msgstr "Criação de repositório"
#: rhodecode/templates/admin/permissions/permissions.html:71
+#, fuzzy
+msgid "Repository forking"
+msgstr "Criação de repositório"
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
msgid "set"
msgstr "ajustar"
@@ -1270,7 +1844,6 @@ msgstr "Adicionar repositório"
#: rhodecode/templates/admin/repos/repo_add.html:11
#: rhodecode/templates/admin/repos/repo_edit.html:11
-#: rhodecode/templates/admin/repos/repos.html:10
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
msgid "Repositories"
msgstr "Repositórios"
@@ -1280,30 +1853,76 @@ msgid "add new"
msgstr "adicionar novo"
#: rhodecode/templates/admin/repos/repo_add_base.html:20
-#: rhodecode/templates/summary/summary.html:80
-#: rhodecode/templates/summary/summary.html:82
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
msgid "Clone from"
msgstr "Clonar de"
-#: rhodecode/templates/admin/repos/repo_add_base.html:28
-#: rhodecode/templates/admin/repos/repo_edit.html:48
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr "URL opcional http[s] da qual o repositório deve ser clonado."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
msgid "Repository group"
msgstr "Grupo de repositórios"
-#: rhodecode/templates/admin/repos/repo_add_base.html:36
-#: rhodecode/templates/admin/repos/repo_edit.html:56
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+#, fuzzy
+msgid "Optionaly select a group to put this repository into."
+msgstr "Opcionalmente selecione um grupo no qual colocar esse repositório."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
msgid "Type"
msgstr "Tipo"
-#: rhodecode/templates/admin/repos/repo_add_base.html:52
-#: rhodecode/templates/admin/repos/repo_edit.html:73
-#: rhodecode/templates/settings/repo_fork.html:48
-#: rhodecode/templates/settings/repo_settings.html:49
-msgid "Private"
-msgstr "Privado"
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+msgid "Type of repository to create."
+msgstr "Tipo de repositório a criar."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+#, fuzzy
+msgid "Landing revision"
+msgstr "próxima revisão"
-#: rhodecode/templates/admin/repos/repo_add_base.html:59
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr ""
+"Seja sucinto e objetivo. Use um arquivo README para descrições mais "
+"longas."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr ""
+"Repositórios privados são visíveis somente por pessoas explicitamente "
+"adicionadas como colaboradores."
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
msgid "add"
msgstr "adicionar"
@@ -1317,183 +1936,283 @@ msgstr "Editar repositório"
#: rhodecode/templates/admin/repos/repo_edit.html:13
#: rhodecode/templates/admin/users/user_edit.html:13
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
-#: rhodecode/templates/files/files_annotate.html:49
-#: rhodecode/templates/files/files_source.html:20
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
msgid "edit"
msgstr "editar"
#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
msgid "Clone uri"
msgstr "URI de clonagem"
-#: rhodecode/templates/admin/repos/repo_edit.html:81
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr "Opcionalmente selecione um grupo no qual colocar esse repositório."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
msgid "Enable statistics"
msgstr "Habilitar estatísticas"
-#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr "Habilitar janela de estatísticas na página de sumário."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
msgid "Enable downloads"
msgstr "Habilitar downloads"
-#: rhodecode/templates/admin/repos/repo_edit.html:127
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr "Habilitar menu de descarregar na página de sumário."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+#, fuzzy
+msgid "Enable locking"
+msgstr "habilitar"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+msgid "Change owner of this repository."
+msgstr "Mudar o dono desse repositório."
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr "Limpar"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
msgid "Administration"
msgstr "Administração"
-#: rhodecode/templates/admin/repos/repo_edit.html:130
+#: rhodecode/templates/admin/repos/repo_edit.html:155
msgid "Statistics"
msgstr "Estatísticas"
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Reset current statistics"
msgstr "Reinicializar estatísticas atuais"
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Confirm to remove current statistics"
msgstr "Confirma remover atuais estatísticas"
-#: rhodecode/templates/admin/repos/repo_edit.html:137
+#: rhodecode/templates/admin/repos/repo_edit.html:162
msgid "Fetched to rev"
msgstr "Trazida à rev"
-#: rhodecode/templates/admin/repos/repo_edit.html:138
-msgid "Percentage of stats gathered"
-msgstr "Porcentagem das estatísticas totalizadas"
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
+msgstr "Estatísticas coletadas"
-#: rhodecode/templates/admin/repos/repo_edit.html:147
+#: rhodecode/templates/admin/repos/repo_edit.html:171
msgid "Remote"
msgstr "Remoto"
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Pull changes from remote location"
msgstr "Realizar pull de alterações a partir de localização remota"
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Confirm to pull changes from remote side"
msgstr "Confirma realizar pull de alterações a partir de lado remoto"
-#: rhodecode/templates/admin/repos/repo_edit.html:162
+#: rhodecode/templates/admin/repos/repo_edit.html:186
msgid "Cache"
msgstr "Cache"
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Invalidate repository cache"
msgstr "Invalidar cache do repositório"
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Confirm to invalidate repository cache"
msgstr "Confirma invalidar cache do repositório"
-#: rhodecode/templates/admin/repos/repo_edit.html:177
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318
+#: rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr "Diário público"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
msgid "Remove from public journal"
msgstr "Remover do diário público"
-#: rhodecode/templates/admin/repos/repo_edit.html:179
+#: rhodecode/templates/admin/repos/repo_edit.html:203
msgid "Add to public journal"
msgstr "Adicionar ao diário público"
-#: rhodecode/templates/admin/repos/repo_edit.html:185
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in "
+"public journal"
+msgstr ""
+"Todas as ações feitas nesse repositório serão acessíveis a todos no "
+"diário público"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+#, fuzzy
+msgid "Locking"
+msgstr "destravar"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+#, fuzzy
+msgid "Confirm to unlock repository"
+msgstr "Confirma excluir este repositório"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+#, fuzzy
+msgid "Confirm to lock repository"
+msgstr "Confirma excluir este repositório"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+#, fuzzy
+msgid "Repository is not locked"
+msgstr "repositórios"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+#, fuzzy
+msgid "Set as fork of"
+msgstr "Marcar como bifurcação"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+#, fuzzy
+msgid "Manually set this repository as a fork of another from the list"
+msgstr "Marcar manualmente este repositório como sendo uma bifurcação de outro"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
msgid "Delete"
msgstr "Excluir"
-#: rhodecode/templates/admin/repos/repo_edit.html:189
+#: rhodecode/templates/admin/repos/repo_edit.html:255
msgid "Remove this repository"
msgstr "Remover deste repositório"
-#: rhodecode/templates/admin/repos/repo_edit.html:189
-#: rhodecode/templates/admin/repos/repos.html:79
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
msgid "Confirm to delete this repository"
msgstr "Confirma excluir este repositório"
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be "
+"unaccesible for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem "
+"please do it manually"
+msgstr ""
+"Este repositório será renomeado de uma maneira especial, de forma a ser "
+"inacessível ao RhodeCode e sistemas de controle de versão.\n"
+" Se você precisa exclui-lo completamente do "
+"sistema de arquivos, por favor faça-o manualmente"
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
msgid "none"
msgstr "nenhum"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
msgid "read"
msgstr "ler"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
msgid "write"
msgstr "escrever"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
-#: rhodecode/templates/admin/users/users.html:38
-#: rhodecode/templates/base/base.html:296
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
msgid "admin"
msgstr "administrador"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
msgid "member"
msgstr "membro"
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr "repositório privado"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+#, fuzzy
+msgid "default"
+msgstr "excluir"
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:53
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
msgid "revoke"
msgstr "revogar"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:75
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
msgid "Add another member"
msgstr "Adicionar outro membro"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:89
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
msgid "Failed to remove user"
msgstr "Falha ao reomver usuário"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:104
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
msgid "Failed to remove users group"
msgstr "Falha ao remover grupo de usuários"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:205
-msgid "Group"
-msgstr "Grupo"
-
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:206
-#: rhodecode/templates/admin/users_groups/users_groups.html:33
-msgid "members"
-msgstr "membros"
-
#: rhodecode/templates/admin/repos/repos.html:5
msgid "Repositories administration"
msgstr "Administração de repositórios"
-#: rhodecode/templates/admin/repos/repos.html:34
-#: rhodecode/templates/summary/summary.html:100
-msgid "Contact"
-msgstr "Contato"
-
-#: rhodecode/templates/admin/repos/repos.html:35
-#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
-#: rhodecode/templates/admin/users/user_edit_my_account.html:119
-#: rhodecode/templates/admin/users/users.html:40
-#: rhodecode/templates/admin/users_groups/users_groups.html:35
-msgid "action"
-msgstr "ação"
-
-#: rhodecode/templates/admin/repos/repos.html:51
-#: rhodecode/templates/admin/users/user_edit_my_account.html:134
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
-msgid "private"
-msgstr "privado"
-
-#: rhodecode/templates/admin/repos/repos.html:53
-#: rhodecode/templates/admin/repos/repos.html:59
-#: rhodecode/templates/admin/users/user_edit_my_account.html:136
-#: rhodecode/templates/admin/users/user_edit_my_account.html:142
-#: rhodecode/templates/summary/summary.html:68
-msgid "public"
-msgstr "público"
-
-#: rhodecode/templates/admin/repos/repos.html:79
-#: rhodecode/templates/admin/users/users.html:55
-msgid "delete"
-msgstr "excluir"
-
#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
msgid "Groups"
msgstr "Grupos"
-#: rhodecode/templates/admin/repos_groups/repos_groups.html:13
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
msgid "with"
msgstr "com"
@@ -1516,10 +2235,10 @@ msgid "Group parent"
msgstr "Progenitor do grupo"
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
-#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
-#: rhodecode/templates/admin/users/user_add.html:85
+#: rhodecode/templates/admin/users/user_add.html:94
#: rhodecode/templates/admin/users_groups/users_group_add.html:49
#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
msgid "save"
msgstr "salvar"
@@ -1531,6 +2250,12 @@ msgstr "Editar grupo de repositórios"
msgid "edit repos group"
msgstr "editar grupo de repositórios"
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
msgid "Repositories groups administration"
msgstr "Administração de grupos de repositórios"
@@ -1540,12 +2265,27 @@ msgid "ADD NEW GROUP"
msgstr "ADICIONAR NOVO GRUPO"
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
-msgid "Number of repositories"
-msgstr "Número de repositórios"
+msgid "Number of toplevel repositories"
+msgstr "Número de repositórios de nível superior"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
+msgstr "ação"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr "excluir"
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
-msgid "Confirm to delete this group"
-msgstr "Confirme para excluir este grupo"
+#, python-format
+msgid "Confirm to delete this group: %s"
+msgstr "Confirme para excluir esse grupo: %s"
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
msgid "There are no repositories groups yet"
@@ -1558,7 +2298,6 @@ msgstr "Administração de configurações"
#: rhodecode/templates/admin/settings/hooks.html:9
#: rhodecode/templates/admin/settings/settings.html:9
-#: rhodecode/templates/settings/repo_settings.html:5
#: rhodecode/templates/settings/repo_settings.html:13
msgid "Settings"
msgstr "Configurações"
@@ -1588,119 +2327,208 @@ msgid "rescan option"
msgstr "opção de varredura"
#: rhodecode/templates/admin/settings/settings.html:38
-msgid "In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it."
-msgstr "Caso um repositório tenha sido excluído do sistema de arquivos e haja restos no banco de dados, marque esta opção para varrer dados obsoletos no banco e removê-los."
+msgid ""
+"In case a repository was deleted from filesystem and there are leftovers "
+"in the database check this option to scan obsolete data in database and "
+"remove it."
+msgstr ""
+"Caso um repositório tenha sido excluído do sistema de arquivos e haja "
+"restos no banco de dados, marque esta opção para varrer dados obsoletos "
+"no banco e removê-los."
#: rhodecode/templates/admin/settings/settings.html:39
msgid "destroy old data"
msgstr "destruir dados antigos"
-#: rhodecode/templates/admin/settings/settings.html:45
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete "
+"if `destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
msgid "Rescan repositories"
msgstr "Varrer repositórios"
-#: rhodecode/templates/admin/settings/settings.html:51
+#: rhodecode/templates/admin/settings/settings.html:52
msgid "Whoosh indexing"
msgstr "Indexação do Whoosh"
-#: rhodecode/templates/admin/settings/settings.html:59
+#: rhodecode/templates/admin/settings/settings.html:60
msgid "index build option"
msgstr "opção de construção de índice"
-#: rhodecode/templates/admin/settings/settings.html:64
+#: rhodecode/templates/admin/settings/settings.html:65
msgid "build from scratch"
msgstr "construir do início"
-#: rhodecode/templates/admin/settings/settings.html:70
+#: rhodecode/templates/admin/settings/settings.html:71
msgid "Reindex"
msgstr "Reindexar"
-#: rhodecode/templates/admin/settings/settings.html:76
+#: rhodecode/templates/admin/settings/settings.html:77
msgid "Global application settings"
msgstr "Configurações globais da aplicação"
-#: rhodecode/templates/admin/settings/settings.html:85
+#: rhodecode/templates/admin/settings/settings.html:86
msgid "Application name"
msgstr "Nome da aplicação"
-#: rhodecode/templates/admin/settings/settings.html:94
+#: rhodecode/templates/admin/settings/settings.html:95
msgid "Realm text"
msgstr "Texto de esfera"
-#: rhodecode/templates/admin/settings/settings.html:103
+#: rhodecode/templates/admin/settings/settings.html:104
msgid "GA code"
msgstr "Código GA"
-#: rhodecode/templates/admin/settings/settings.html:111
-#: rhodecode/templates/admin/settings/settings.html:177
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
msgid "Save settings"
msgstr "Salvar configurações"
-#: rhodecode/templates/admin/settings/settings.html:112
-#: rhodecode/templates/admin/settings/settings.html:178
-#: rhodecode/templates/admin/users/user_edit.html:118
-#: rhodecode/templates/admin/users/user_edit.html:143
-#: rhodecode/templates/admin/users/user_edit_my_account.html:90
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:264
-#: rhodecode/templates/files/files_edit.html:50
-msgid "Reset"
-msgstr "Limpar"
+#: rhodecode/templates/admin/settings/settings.html:119
+#, fuzzy
+msgid "Visualisation settings"
+msgstr "Configurações globais da aplicação"
+
+#: rhodecode/templates/admin/settings/settings.html:128
+#, fuzzy
+msgid "Icons"
+msgstr "Opções"
+
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+#, fuzzy
+msgid "Show private repo icon on repositories"
+msgstr "repositório privado"
-#: rhodecode/templates/admin/settings/settings.html:118
-msgid "Mercurial settings"
-msgstr "Configurações do Mercurial"
+#: rhodecode/templates/admin/settings/settings.html:144
+#, fuzzy
+msgid "Meta-Tagging"
+msgstr "configurações"
-#: rhodecode/templates/admin/settings/settings.html:127
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:176
+#, fuzzy
+msgid "VCS settings"
+msgstr "configurações"
+
+#: rhodecode/templates/admin/settings/settings.html:185
msgid "Web"
msgstr "Web"
-#: rhodecode/templates/admin/settings/settings.html:132
-msgid "require ssl for pushing"
+#: rhodecode/templates/admin/settings/settings.html:190
+#, fuzzy
+msgid "require ssl for vcs operations"
msgstr "exigir ssl para realizar push"
-#: rhodecode/templates/admin/settings/settings.html:139
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
+"will return HTTP Error 406: Not Acceptable"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:198
msgid "Hooks"
msgstr "Ganchos"
-#: rhodecode/templates/admin/settings/settings.html:142
-msgid "advanced setup"
-msgstr "confirguações avançadas"
-
-#: rhodecode/templates/admin/settings/settings.html:147
+#: rhodecode/templates/admin/settings/settings.html:203
msgid "Update repository after push (hg update)"
msgstr "Atualizar repositório após realizar push (hg update)"
-#: rhodecode/templates/admin/settings/settings.html:151
+#: rhodecode/templates/admin/settings/settings.html:207
msgid "Show repository size after push"
msgstr "Mostrar tamanho do repositório após o push"
-#: rhodecode/templates/admin/settings/settings.html:155
+#: rhodecode/templates/admin/settings/settings.html:211
msgid "Log user push commands"
msgstr "Armazenar registro de comandos de push dos usuários"
-#: rhodecode/templates/admin/settings/settings.html:159
+#: rhodecode/templates/admin/settings/settings.html:215
msgid "Log user pull commands"
msgstr "Armazenar registro de comandos de pull dos usuários"
-#: rhodecode/templates/admin/settings/settings.html:166
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr "confirguações avançadas"
+
+#: rhodecode/templates/admin/settings/settings.html:224
+#, fuzzy
+msgid "Mercurial Extensions"
+msgstr "Repositório Mercurial"
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
msgid "Repositories location"
msgstr "Localização dos repositórios"
-#: rhodecode/templates/admin/settings/settings.html:171
-msgid "This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock."
-msgstr "Essa é uma configuração crucial da aplicação. Se você realmente tem certeza de que quer mudar isto, você precisa reiniciar a aplicação para que essa configuração tenha efeito. Clique este rótulo para destravar."
+#: rhodecode/templates/admin/settings/settings.html:250
+msgid ""
+"This a crucial application setting. If you are really sure you need to "
+"change this, you must restart application in order to make this setting "
+"take effect. Click this label to unlock."
+msgstr ""
+"Essa é uma configuração crucial da aplicação. Se você realmente tem "
+"certeza de que quer mudar isto, você precisa reiniciar a aplicação para "
+"que essa configuração tenha efeito. Clique este rótulo para destravar."
-#: rhodecode/templates/admin/settings/settings.html:172
+#: rhodecode/templates/admin/settings/settings.html:251
msgid "unlock"
msgstr "destravar"
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a "
+"restart, and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr "Testar E-mail"
+
+#: rhodecode/templates/admin/settings/settings.html:280
+msgid "Email to"
+msgstr "E-mail para"
+
+#: rhodecode/templates/admin/settings/settings.html:288
+msgid "Send"
+msgstr "Enviar"
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr "Informações de Sistema e Pacotes"
+
+#: rhodecode/templates/admin/settings/settings.html:297
+msgid "show"
+msgstr "mostrar"
+
#: rhodecode/templates/admin/users/user_add.html:5
msgid "Add user"
msgstr "Adicionar usuário"
#: rhodecode/templates/admin/users/user_add.html:10
#: rhodecode/templates/admin/users/user_edit.html:11
-#: rhodecode/templates/admin/users/users.html:9
msgid "Users"
msgstr "Usuários"
@@ -1708,8 +2536,12 @@ msgstr "Usuários"
msgid "add new user"
msgstr "adicionar novo usuário"
-#: rhodecode/templates/admin/users/user_add.html:77
-#: rhodecode/templates/admin/users/user_edit.html:101
+#: rhodecode/templates/admin/users/user_add.html:50
+msgid "Password confirmation"
+msgstr "Confirmação de senha"
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
#: rhodecode/templates/admin/users_groups/users_group_add.html:41
#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
msgid "Active"
@@ -1719,36 +2551,100 @@ msgstr "Ativo"
msgid "Edit user"
msgstr "Editar usuário"
-#: rhodecode/templates/admin/users/user_edit.html:33
-#: rhodecode/templates/admin/users/user_edit_my_account.html:32
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
msgid "Change your avatar at"
msgstr "Altere o seu avatar em"
-#: rhodecode/templates/admin/users/user_edit.html:34
-#: rhodecode/templates/admin/users/user_edit_my_account.html:33
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
msgid "Using"
msgstr "Usando"
-#: rhodecode/templates/admin/users/user_edit.html:40
-#: rhodecode/templates/admin/users/user_edit_my_account.html:39
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
msgid "API key"
msgstr "Chave de API"
-#: rhodecode/templates/admin/users/user_edit.html:56
+#: rhodecode/templates/admin/users/user_edit.html:59
msgid "LDAP DN"
msgstr "DN LDAP"
-#: rhodecode/templates/admin/users/user_edit.html:65
-#: rhodecode/templates/admin/users/user_edit_my_account.html:54
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
msgid "New password"
msgstr "Nova senha"
-#: rhodecode/templates/admin/users/user_edit.html:135
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:256
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr "Confirmação de nova senha"
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+#, fuzzy
+msgid "Inherit default permissions"
+msgstr "Permissões padrão"
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
msgid "Create repositories"
msgstr "Criar repositórios"
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+#, fuzzy
+msgid "Fork repositories"
+msgstr "repositórios"
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+#, fuzzy
+msgid "Nothing here yet"
+msgstr "Ainda não há notificações aqui"
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+msgid "Permission"
+msgstr "Permissão"
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+#, fuzzy
+msgid "Edit Permission"
+msgstr "Permissão de repositório"
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+#, fuzzy
+msgid "Email addresses"
+msgstr "Endereço de e-mail"
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, fuzzy, python-format
+msgid "Confirm to delete this email: %s"
+msgstr "Confirma excluir este usuário: %s"
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+#, fuzzy
+msgid "New email address"
+msgstr "Endereço de e-mail"
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+#, fuzzy
+msgid "Add"
+msgstr "adicionar"
+
#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
msgid "My account"
msgstr "Minha conta"
@@ -1756,26 +2652,77 @@ msgstr "Minha conta"
msgid "My Account"
msgstr "Minha Conta"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:101
-msgid "My repositories"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+msgid "My permissions"
+msgstr "Minhas permissões"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+msgid "My repos"
msgstr "Meus repositórios"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:107
-msgid "ADD REPOSITORY"
-msgstr "ADICIONAR REPOSITÓRIO"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+#, fuzzy
+msgid "My pull requests"
+msgstr "comentado no commit"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:118
-#: rhodecode/templates/branches/branches_data.html:7
-#: rhodecode/templates/shortlog/shortlog_data.html:8
-#: rhodecode/templates/tags/tags_data.html:7
-msgid "revision"
-msgstr "revisão"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+#, fuzzy
+msgid "Add repo"
+msgstr "adicionar novo"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, python-format
+msgid "Pull request #%s opened on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+#, fuzzy
+msgid "Confirm to delete this pull request"
+msgstr "Confirma excluir este repositório"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40
+#: rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
+msgstr "Revisão"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr "privado"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr "Confirma excluir esse repositório: %s"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
msgid "No repositories yet"
msgstr "Ainda não há repositórios"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
msgid "create one now"
msgstr "criar um agora"
@@ -1783,42 +2730,42 @@ msgstr "criar um agora"
msgid "Users administration"
msgstr "Administração de usuários"
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr "usuários"
+
#: rhodecode/templates/admin/users/users.html:23
msgid "ADD NEW USER"
msgstr "ADICIONAR NOVO USUÁRIO"
-#: rhodecode/templates/admin/users/users.html:33
+#: rhodecode/templates/admin/users/users.html:77
msgid "username"
msgstr "nome de usuário"
-#: rhodecode/templates/admin/users/users.html:34
-#: rhodecode/templates/branches/branches_data.html:5
-#: rhodecode/templates/tags/tags_data.html:5
-msgid "name"
-msgstr "nome"
+#: rhodecode/templates/admin/users/users.html:80
+#, fuzzy
+msgid "firstname"
+msgstr "Primeiro Nome"
-#: rhodecode/templates/admin/users/users.html:35
+#: rhodecode/templates/admin/users/users.html:81
msgid "lastname"
msgstr "sobrenome"
-#: rhodecode/templates/admin/users/users.html:36
+#: rhodecode/templates/admin/users/users.html:82
msgid "last login"
msgstr "último login"
-#: rhodecode/templates/admin/users/users.html:37
+#: rhodecode/templates/admin/users/users.html:84
#: rhodecode/templates/admin/users_groups/users_groups.html:34
msgid "active"
msgstr "ativo"
-#: rhodecode/templates/admin/users/users.html:39
-#: rhodecode/templates/base/base.html:305
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
msgid "ldap"
msgstr "ldap"
-#: rhodecode/templates/admin/users/users.html:56
-msgid "Confirm to delete this user"
-msgstr "Conforma excluir este usuário"
-
#: rhodecode/templates/admin/users_groups/users_group_add.html:5
msgid "Add users group"
msgstr "Adicionar grupo de usuários"
@@ -1860,6 +2807,10 @@ msgstr "Membros disponíveis"
msgid "Add all elements"
msgstr "Adicionar todos os elementos"
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+msgid "Group members"
+msgstr "Membros do grupo"
+
#: rhodecode/templates/admin/users_groups/users_groups.html:5
msgid "Users groups administration"
msgstr "Administração de grupos de usuários"
@@ -1872,399 +2823,627 @@ msgstr "ADICIONAR NOVO GRUPO DE USUÁRIOS"
msgid "group name"
msgstr "nome do grupo"
-#: rhodecode/templates/base/base.html:32
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr "membros"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr "Confirme para excluir este grupo de usuários: %s"
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr "Encaminhe um bug"
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr "Entrar com sua conta"
+
+#: rhodecode/templates/base/base.html:100
msgid "Forgot password ?"
msgstr "Esqueceu a senha ?"
-#: rhodecode/templates/base/base.html:57
-#: rhodecode/templates/base/base.html:338
-#: rhodecode/templates/base/base.html:340
-#: rhodecode/templates/base/base.html:342
+#: rhodecode/templates/base/base.html:107
+msgid "Log In"
+msgstr "Entrar"
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr "Caixa de Entrada"
+
+#: rhodecode/templates/base/base.html:122
+#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302
+#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8
+#: rhodecode/templates/tags/tags.html:11
msgid "Home"
msgstr "Início"
-#: rhodecode/templates/base/base.html:61
-#: rhodecode/templates/base/base.html:347
-#: rhodecode/templates/base/base.html:349
-#: rhodecode/templates/base/base.html:351
+#: rhodecode/templates/base/base.html:123
+#: rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311
+#: rhodecode/templates/base/base.html:313
#: rhodecode/templates/journal/journal.html:4
-#: rhodecode/templates/journal/journal.html:17
+#: rhodecode/templates/journal/journal.html:21
#: rhodecode/templates/journal/public_journal.html:4
msgid "Journal"
msgstr "Diário"
-#: rhodecode/templates/base/base.html:66
-msgid "Login"
-msgstr "Entrar"
-
-#: rhodecode/templates/base/base.html:68
+#: rhodecode/templates/base/base.html:125
msgid "Log Out"
msgstr "Sair"
-#: rhodecode/templates/base/base.html:107
-msgid "Submit a bug"
-msgstr "Encaminhe um bug"
-
-#: rhodecode/templates/base/base.html:141
+#: rhodecode/templates/base/base.html:144
msgid "Switch repository"
msgstr "Trocar repositório"
-#: rhodecode/templates/base/base.html:143
+#: rhodecode/templates/base/base.html:146
msgid "Products"
msgstr "Produtos"
-#: rhodecode/templates/base/base.html:149
+#: rhodecode/templates/base/base.html:152
+#: rhodecode/templates/base/base.html:182
msgid "loading..."
msgstr "carregando..."
-#: rhodecode/templates/base/base.html:234
-#: rhodecode/templates/base/base.html:236
-#: rhodecode/templates/base/base.html:238
-msgid "Switch to"
-msgstr "Trocar para"
-
-#: rhodecode/templates/base/base.html:242
-#: rhodecode/templates/branches/branches.html:13
-msgid "branches"
-msgstr "ramos"
+#: rhodecode/templates/base/base.html:158
+#: rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
+msgstr "Sumário"
-#: rhodecode/templates/base/base.html:249
-#: rhodecode/templates/branches/branches_data.html:52
-msgid "There are no branches yet"
-msgstr "Ainda não há ramos"
+#: rhodecode/templates/base/base.html:166
+#: rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
+msgstr "Registro de alterações"
-#: rhodecode/templates/base/base.html:254
-#: rhodecode/templates/shortlog/shortlog_data.html:10
-#: rhodecode/templates/tags/tags.html:14
-msgid "tags"
-msgstr "etiquetas"
+#: rhodecode/templates/base/base.html:175
+#: rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
+msgstr "Trocar para"
-#: rhodecode/templates/base/base.html:261
-#: rhodecode/templates/tags/tags_data.html:32
-msgid "There are no tags yet"
-msgstr "Ainda não há etiquetas"
+#: rhodecode/templates/base/base.html:186
+#: rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
+msgstr "Arquivos"
-#: rhodecode/templates/base/base.html:277
-#: rhodecode/templates/base/base.html:281
-#: rhodecode/templates/files/files_annotate.html:40
-#: rhodecode/templates/files/files_source.html:11
+#: rhodecode/templates/base/base.html:195
+#: rhodecode/templates/base/base.html:199
msgid "Options"
msgstr "Opções"
-#: rhodecode/templates/base/base.html:286
-#: rhodecode/templates/base/base.html:288
-#: rhodecode/templates/base/base.html:306
+#: rhodecode/templates/base/base.html:204
+#: rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
msgid "settings"
msgstr "configurações"
-#: rhodecode/templates/base/base.html:292
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
+msgstr "bifurcação"
+
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:213
msgid "search"
msgstr "pesquisar"
-#: rhodecode/templates/base/base.html:299
-msgid "journal"
-msgstr "diário"
-
-#: rhodecode/templates/base/base.html:301
+#: rhodecode/templates/base/base.html:222
msgid "repositories groups"
msgstr "grupos de repositórios"
-#: rhodecode/templates/base/base.html:302
-msgid "users"
-msgstr "usuários"
-
-#: rhodecode/templates/base/base.html:303
+#: rhodecode/templates/base/base.html:224
msgid "users groups"
msgstr "grupos de usuários"
-#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/base/base.html:225
msgid "permissions"
msgstr "permissões"
-#: rhodecode/templates/base/base.html:317
-#: rhodecode/templates/base/base.html:319
-#: rhodecode/templates/followers/followers.html:5
+#: rhodecode/templates/base/base.html:238
+#: rhodecode/templates/base/base.html:240
msgid "Followers"
msgstr "Seguidores"
-#: rhodecode/templates/base/base.html:325
-#: rhodecode/templates/base/base.html:327
-#: rhodecode/templates/forks/forks.html:5
+#: rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248
msgid "Forks"
msgstr "Bifurcações"
-#: rhodecode/templates/base/base.html:356
-#: rhodecode/templates/base/base.html:358
-#: rhodecode/templates/base/base.html:360
-#: rhodecode/templates/search/search.html:4
-#: rhodecode/templates/search/search.html:24
-#: rhodecode/templates/search/search.html:46
+#: rhodecode/templates/base/base.html:327
+#: rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331
+#: rhodecode/templates/search/search.html:52
msgid "Search"
msgstr "Pesquisar"
-#: rhodecode/templates/base/root.html:57
-#: rhodecode/templates/journal/journal.html:48
-#: rhodecode/templates/summary/summary.html:36
+#: rhodecode/templates/base/root.html:42
+msgid "add another comment"
+msgstr "adicionar outro comentário"
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
msgid "Stop following this repository"
msgstr "Parar de seguir este repositório"
-#: rhodecode/templates/base/root.html:66
-#: rhodecode/templates/summary/summary.html:40
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
msgid "Start following this repository"
msgstr "Passar a seguir este repositório"
-#: rhodecode/templates/branches/branches_data.html:4
-#: rhodecode/templates/tags/tags_data.html:4
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr "Grupo"
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr "pesquisa truncada"
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr "nenhum arquivo corresponde"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, fuzzy, python-format
+msgid "%s Bookmarks"
+msgstr "marcadores"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39
+#: rhodecode/templates/tags/tags_data.html:8
+msgid "Author"
+msgstr "Autor"
+
+#: rhodecode/templates/branches/branches.html:5
+#, fuzzy, python-format
+msgid "%s Branches"
+msgstr "ramos"
+
+#: rhodecode/templates/branches/branches.html:29
+#, fuzzy
+msgid "Compare branches"
+msgstr "ramos"
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+#, fuzzy
+msgid "Compare"
+msgstr "comparar exibir"
+
+#: rhodecode/templates/branches/branches_data.html:6
+msgid "name"
+msgstr "nome"
+
+#: rhodecode/templates/branches/branches_data.html:7
msgid "date"
msgstr "data"
-#: rhodecode/templates/branches/branches_data.html:6
-#: rhodecode/templates/shortlog/shortlog_data.html:7
-#: rhodecode/templates/tags/tags_data.html:6
+#: rhodecode/templates/branches/branches_data.html:8
+#: rhodecode/templates/shortlog/shortlog_data.html:8
msgid "author"
msgstr "autor"
-#: rhodecode/templates/branches/branches_data.html:8
-#: rhodecode/templates/shortlog/shortlog_data.html:11
-#: rhodecode/templates/tags/tags_data.html:8
-msgid "links"
-msgstr "inks"
-
-#: rhodecode/templates/branches/branches_data.html:23
-#: rhodecode/templates/branches/branches_data.html:43
-#: rhodecode/templates/shortlog/shortlog_data.html:39
-#: rhodecode/templates/tags/tags_data.html:24
-msgid "changeset"
-msgstr "conjunto de mudanças"
-
-#: rhodecode/templates/branches/branches_data.html:25
-#: rhodecode/templates/branches/branches_data.html:45
-#: rhodecode/templates/files/files.html:12
-#: rhodecode/templates/shortlog/shortlog_data.html:41
-#: rhodecode/templates/summary/summary.html:233
-#: rhodecode/templates/tags/tags_data.html:26
-msgid "files"
-msgstr "arquivos"
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
+msgstr "revisão"
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "showing "
-msgstr "mostrando "
+#: rhodecode/templates/branches/branches_data.html:10
+#, fuzzy
+msgid "compare"
+msgstr "comparar exibir"
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "out of"
-msgstr "de"
+#: rhodecode/templates/changelog/changelog.html:6
+#, fuzzy, python-format
+msgid "%s Changelog"
+msgstr "Registro de alterações"
+
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] "mostrando %d de %d revisão"
+msgstr[1] "mostrando %d de %d revisões"
#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+#, fuzzy
+msgid "Compare fork"
+msgstr "comparar exibir"
+
+#: rhodecode/templates/changelog/changelog.html:46
msgid "Show"
msgstr "Mostrar"
-#: rhodecode/templates/changelog/changelog.html:50
-#: rhodecode/templates/changeset/changeset.html:42
-#: rhodecode/templates/summary/summary.html:609
-msgid "commit"
-msgstr "commit"
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
+msgstr "mostrar mais"
-#: rhodecode/templates/changelog/changelog.html:63
+#: rhodecode/templates/changelog/changelog.html:76
msgid "Affected number of files, click to show more details"
msgstr "Número de arquivos afetados, clique para mostrar mais detalhes"
-#: rhodecode/templates/changelog/changelog.html:67
-#: rhodecode/templates/changeset/changeset.html:66
-msgid "merge"
-msgstr "mesclar"
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+#, fuzzy
+msgid "Changeset status"
+msgstr "Conjuntos de mudanças"
-#: rhodecode/templates/changelog/changelog.html:72
-#: rhodecode/templates/changeset/changeset.html:72
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
msgid "Parent"
msgstr "Progenitor"
-#: rhodecode/templates/changelog/changelog.html:77
-#: rhodecode/templates/changeset/changeset.html:77
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
msgid "No parents"
msgstr "Sem progenitores"
-#: rhodecode/templates/changelog/changelog.html:82
-#: rhodecode/templates/changeset/changeset.html:80
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr "mesclar"
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
#: rhodecode/templates/files/files.html:29
-#: rhodecode/templates/files/files_annotate.html:25
+#: rhodecode/templates/files/files_add.html:33
#: rhodecode/templates/files/files_edit.html:33
#: rhodecode/templates/shortlog/shortlog_data.html:9
msgid "branch"
msgstr "ramo"
-#: rhodecode/templates/changelog/changelog.html:86
-#: rhodecode/templates/changeset/changeset.html:83
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr "marcador"
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
msgid "tag"
msgstr "etiqueta"
-#: rhodecode/templates/changelog/changelog.html:122
+#: rhodecode/templates/changelog/changelog.html:164
msgid "Show selected changes __S -> __E"
msgstr "Mostrar alterações selecionadas __S -> __E"
-#: rhodecode/templates/changelog/changelog.html:172
-#: rhodecode/templates/shortlog/shortlog_data.html:61
+#: rhodecode/templates/changelog/changelog.html:255
msgid "There are no changes yet"
msgstr "Ainda não há alteações"
-#: rhodecode/templates/changelog/changelog_details.html:2
-#: rhodecode/templates/changeset/changeset.html:55
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
msgid "removed"
msgstr "removidos"
-#: rhodecode/templates/changelog/changelog_details.html:3
-#: rhodecode/templates/changeset/changeset.html:56
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
msgid "changed"
msgstr "alterados"
-#: rhodecode/templates/changelog/changelog_details.html:4
-#: rhodecode/templates/changeset/changeset.html:57
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
msgid "added"
msgstr "adicionados"
-#: rhodecode/templates/changelog/changelog_details.html:6
-#: rhodecode/templates/changelog/changelog_details.html:7
#: rhodecode/templates/changelog/changelog_details.html:8
-#: rhodecode/templates/changeset/changeset.html:59
-#: rhodecode/templates/changeset/changeset.html:60
-#: rhodecode/templates/changeset/changeset.html:61
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
#, python-format
msgid "affected %s files"
msgstr "%s arquivos afetados"
#: rhodecode/templates/changeset/changeset.html:6
+#, fuzzy, python-format
+msgid "%s Changeset"
+msgstr "Conjunto de Mudanças"
+
#: rhodecode/templates/changeset/changeset.html:14
-#: rhodecode/templates/changeset/changeset.html:31
msgid "Changeset"
msgstr "Conjunto de Mudanças"
-#: rhodecode/templates/changeset/changeset.html:32
-#: rhodecode/templates/changeset/changeset.html:121
-#: rhodecode/templates/changeset/changeset_range.html:78
-#: rhodecode/templates/files/file_diff.html:32
-#: rhodecode/templates/files/file_diff.html:42
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
msgid "raw diff"
msgstr "diff bruto"
-#: rhodecode/templates/changeset/changeset.html:34
-#: rhodecode/templates/changeset/changeset.html:123
-#: rhodecode/templates/changeset/changeset_range.html:80
-#: rhodecode/templates/files/file_diff.html:34
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
msgid "download diff"
msgstr "descarregar diff"
-#: rhodecode/templates/changeset/changeset.html:90
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d comentário"
+msgstr[1] "%d comentários"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
#, python-format
-msgid "%s files affected with %s additions and %s deletions."
-msgstr "%s arquivos afetados com %s adições e %s exclusões"
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] "(%d em linha)"
+msgstr[1] "(%d em linha)"
-#: rhodecode/templates/changeset/changeset.html:101
+#: rhodecode/templates/changeset/changeset.html:103
+#, python-format
+msgid "%s files affected with %s insertions and %s deletions:"
+msgstr "%s arquivos afetados com %s inserções e %s exclusões"
+
+#: rhodecode/templates/changeset/changeset.html:119
msgid "Changeset was too big and was cut off..."
msgstr "Conjunto de mudanças era grande demais e foi cortado..."
-#: rhodecode/templates/changeset/changeset.html:119
-#: rhodecode/templates/changeset/changeset_range.html:76
-#: rhodecode/templates/files/file_diff.html:30
-msgid "diff"
-msgstr "diff"
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr "Enviando..."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr "Comentando a linha {1}."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr "Comentários interpretados usando a sintaxe %s com suporte a %s."
-#: rhodecode/templates/changeset/changeset.html:132
-#: rhodecode/templates/changeset/changeset_range.html:89
-msgid "No changes in this file"
-msgstr "Nenhuma alteração nesse arquivo"
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr ""
+"Use @nomedeusuário dentro desse texto para enviar notificação a este "
+"usuário do RhodeCode"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+msgid "Comment"
+msgstr "Comentário"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
+msgstr "Ocultar"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "You need to be logged in to comment."
+msgstr "Você precisa estar logado para comentar."
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr "Entrar agora"
-#: rhodecode/templates/changeset/changeset_range.html:30
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr "Deixar um comentário"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+#, fuzzy
+msgid "change status"
+msgstr "Conjuntos de mudanças"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, fuzzy, python-format
+msgid "%s Changesets"
+msgstr "Conjuntos de mudanças"
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
msgid "Compare View"
msgstr "Exibir Comparação"
-#: rhodecode/templates/changeset/changeset_range.html:52
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
msgid "Files affected"
msgstr "Arquivos afetados"
-#: rhodecode/templates/errors/error_document.html:44
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
+msgstr "diff"
+
+#: rhodecode/templates/changeset/diff_block.html:27
+msgid "show inline comments"
+msgstr "mostrar comentários em linha"
+
+#: rhodecode/templates/compare/compare_cs.html:5
+#, fuzzy
+msgid "No changesets"
+msgstr "Nenhum conjunto de alterações ainda."
+
+#: rhodecode/templates/compare/compare_diff.html:37
+#, fuzzy
+msgid "Outgoing changesets"
+msgstr "Nenhum conjunto de alterações ainda."
+
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
+msgstr "Bifurcação"
+
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
+msgstr "Repositório Mercurial"
+
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
+msgstr "Repositório Git"
+
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
+msgstr "repositório público"
+
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
+msgstr "Bifurcação de"
+
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
+msgstr "Nenhum conjunto de alterações ainda."
+
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, python-format
+msgid "Confirm to delete this user: %s"
+msgstr "Confirma excluir este usuário: %s"
+
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
+msgstr "Esta é uma notificação do RhodeCode."
+
+#: rhodecode/templates/errors/error_document.html:46
#, python-format
msgid "You will be redirected to %s in %s seconds"
msgstr "Você será redirecionado para %s em %s segundos"
#: rhodecode/templates/files/file_diff.html:4
+#, fuzzy, python-format
+msgid "%s File diff"
+msgstr "Diff do arquivo"
+
#: rhodecode/templates/files/file_diff.html:12
msgid "File diff"
msgstr "Diff do arquivo"
-#: rhodecode/templates/files/file_diff.html:42
-msgid "Diff is to big to display"
-msgstr "Diff é grande demais para exibir"
-
-#: rhodecode/templates/files/files.html:37
-#: rhodecode/templates/files/files_annotate.html:31
-#: rhodecode/templates/files/files_edit.html:39
-msgid "Location"
-msgstr "Local"
-
-#: rhodecode/templates/files/files.html:46
-msgid "Go back"
-msgstr "Voltar"
+#: rhodecode/templates/files/files.html:4
+#: rhodecode/templates/files/files.html:72
+#, fuzzy, python-format
+msgid "%s files"
+msgstr "arquivos"
-#: rhodecode/templates/files/files.html:47
-msgid "No files at given path"
-msgstr "Nenhum arquivo no caminho especificado"
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr "arquivos"
-#: rhodecode/templates/files/files_annotate.html:4
-msgid "File annotate"
-msgstr "Anotar arquivo"
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, fuzzy, python-format
+msgid "%s Edit file"
+msgstr "editar arquivo"
-#: rhodecode/templates/files/files_annotate.html:12
-msgid "annotate"
-msgstr "anotar"
+#: rhodecode/templates/files/files_add.html:19
+msgid "add file"
+msgstr "adicionar arquivo"
-#: rhodecode/templates/files/files_annotate.html:33
-#: rhodecode/templates/files/files_browser.html:160
-#: rhodecode/templates/files/files_source.html:2
-msgid "Revision"
-msgstr "Revisão"
+#: rhodecode/templates/files/files_add.html:40
+msgid "Add new file"
+msgstr "Adicionar novo arquivo"
-#: rhodecode/templates/files/files_annotate.html:36
-#: rhodecode/templates/files/files_browser.html:158
-#: rhodecode/templates/files/files_source.html:7
-msgid "Size"
-msgstr "Tamanho"
+#: rhodecode/templates/files/files_add.html:45
+msgid "File Name"
+msgstr "Nome de Arquivo"
-#: rhodecode/templates/files/files_annotate.html:38
-#: rhodecode/templates/files/files_browser.html:159
-#: rhodecode/templates/files/files_source.html:9
-msgid "Mimetype"
-msgstr "Mimetype"
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+msgid "or"
+msgstr "ou"
-#: rhodecode/templates/files/files_annotate.html:41
-msgid "show source"
-msgstr "mostrar fonte"
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+msgid "Upload file"
+msgstr "Enviar arquivo"
-#: rhodecode/templates/files/files_annotate.html:43
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:14
-#: rhodecode/templates/files/files_source.html:51
-msgid "show as raw"
-msgstr "mostrar como bruto"
+#: rhodecode/templates/files/files_add.html:58
+msgid "Create new file"
+msgstr "Criar novo arquivo"
-#: rhodecode/templates/files/files_annotate.html:45
-#: rhodecode/templates/files/files_source.html:16
-msgid "download as raw"
-msgstr "descarregar como bruto"
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr "Local"
-#: rhodecode/templates/files/files_annotate.html:54
-#: rhodecode/templates/files/files_source.html:25
-msgid "History"
-msgstr "Histórico"
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr "use / para separar diretórios"
-#: rhodecode/templates/files/files_annotate.html:73
-#: rhodecode/templates/files/files_source.html:46
-#, python-format
-msgid "Binary file (%s)"
-msgstr "Arquivo binário (%s)"
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr "mensagem de commit"
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:51
-msgid "File is too big to display"
-msgstr "Arquivo é grande demais para exibir"
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
+msgstr "Realizar commit das alterações"
#: rhodecode/templates/files/files_browser.html:13
msgid "view"
@@ -2286,59 +3465,165 @@ msgstr "seguir ramo atual"
msgid "search file list"
msgstr "pesquisar lista de arquivos"
-#: rhodecode/templates/files/files_browser.html:32
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+msgid "add new file"
+msgstr "adicionar novo arquivo"
+
+#: rhodecode/templates/files/files_browser.html:35
msgid "Loading file list..."
msgstr "Carregando lista de arquivos..."
-#: rhodecode/templates/files/files_browser.html:111
-msgid "search truncated"
-msgstr "pesquisa truncada"
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
+msgstr "Tamanho"
-#: rhodecode/templates/files/files_browser.html:122
-msgid "no matching files"
-msgstr "nenhum arquivo corresponde"
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
+msgstr "Mimetype"
-#: rhodecode/templates/files/files_browser.html:161
+#: rhodecode/templates/files/files_browser.html:50
+msgid "Last Revision"
+msgstr "Última revisão"
+
+#: rhodecode/templates/files/files_browser.html:51
msgid "Last modified"
msgstr "Última alteração"
-#: rhodecode/templates/files/files_browser.html:162
+#: rhodecode/templates/files/files_browser.html:52
msgid "Last commiter"
msgstr "Último commiter"
-#: rhodecode/templates/files/files_edit.html:4
-msgid "Edit file"
-msgstr "Editar arquivo"
-
#: rhodecode/templates/files/files_edit.html:19
msgid "edit file"
msgstr "editar arquivo"
-#: rhodecode/templates/files/files_edit.html:45
-#: rhodecode/templates/shortlog/shortlog_data.html:5
-msgid "commit message"
-msgstr "mensagem de commit"
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr "mostrar anotação"
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
+msgstr "mostrar como bruto"
#: rhodecode/templates/files/files_edit.html:51
-msgid "Commit changes"
-msgstr "Realizar commit das alterações"
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
+msgstr "descarregar como bruto"
-#: rhodecode/templates/files/files_source.html:12
-msgid "show annotation"
-msgstr "mostrar anotação"
+#: rhodecode/templates/files/files_edit.html:54
+msgid "source"
+msgstr "fonte"
+
+#: rhodecode/templates/files/files_edit.html:59
+msgid "Editing file"
+msgstr "Editando arquivo"
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr "Histórico"
+
+#: rhodecode/templates/files/files_source.html:9
+#, fuzzy
+msgid "diff to revision"
+msgstr "próxima revisão"
+
+#: rhodecode/templates/files/files_source.html:10
+#, fuzzy
+msgid "show at revision"
+msgstr "próxima revisão"
+
+#: rhodecode/templates/files/files_source.html:14
+#, fuzzy, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] "autor"
+msgstr[1] "autors"
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
+msgstr "mostrar fonte"
+
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr "Arquivo binário (%s)"
-#: rhodecode/templates/files/files_source.html:153
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
+msgstr "Arquivo é grande demais para exibir"
+
+#: rhodecode/templates/files/files_source.html:124
msgid "Selection link"
msgstr "Link da seleção"
+#: rhodecode/templates/files/files_ypjax.html:5
+msgid "annotation"
+msgstr "anotação"
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr "Voltar"
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr "Nenhum arquivo no caminho especificado"
+
+#: rhodecode/templates/followers/followers.html:5
+#, fuzzy, python-format
+msgid "%s Followers"
+msgstr "seguidores"
+
#: rhodecode/templates/followers/followers.html:13
msgid "followers"
msgstr "seguidores"
#: rhodecode/templates/followers/followers_data.html:12
-msgid "Started following"
+#, fuzzy
+msgid "Started following -"
msgstr "Passou a seguir"
+#: rhodecode/templates/forks/fork.html:5
+#, fuzzy, python-format
+msgid "%s Fork"
+msgstr "bifurcação"
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr "Nome da bifurcação"
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr "Privado"
+
+#: rhodecode/templates/forks/fork.html:77
+msgid "Copy permissions"
+msgstr "Copiar permissões"
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr "Atualizar após clonar"
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr "bifurcar este repositório"
+
+#: rhodecode/templates/forks/forks.html:5
+#, fuzzy, python-format
+msgid "%s Forks"
+msgstr "bifurcações"
+
#: rhodecode/templates/forks/forks.html:13
msgid "forks"
msgstr "bifurcações"
@@ -2347,184 +3632,413 @@ msgstr "bifurcações"
msgid "forked"
msgstr "bifurcado"
-#: rhodecode/templates/forks/forks_data.html:34
+#: rhodecode/templates/forks/forks_data.html:38
msgid "There are no forks yet"
msgstr "Ainda não há bifurcações"
-#: rhodecode/templates/journal/journal.html:34
-msgid "Following"
-msgstr "Seguindo"
+#: rhodecode/templates/journal/journal.html:13
+#, fuzzy
+msgid "ATOM journal feed"
+msgstr "diário público de %s - feed %s"
+
+#: rhodecode/templates/journal/journal.html:14
+#, fuzzy
+msgid "RSS journal feed"
+msgstr "diário público de %s - feed %s"
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr "Atualizar"
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+#, fuzzy
+msgid "RSS feed"
+msgstr "%s - feed %s"
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
+msgstr ""
#: rhodecode/templates/journal/journal.html:41
+msgid "Watched"
+msgstr "Seguindo"
+
+#: rhodecode/templates/journal/journal.html:46
+msgid "ADD"
+msgstr "ADICIONAR"
+
+#: rhodecode/templates/journal/journal.html:114
msgid "following user"
msgstr "seguindo usuário"
-#: rhodecode/templates/journal/journal.html:41
+#: rhodecode/templates/journal/journal.html:114
msgid "user"
msgstr "usuário"
-#: rhodecode/templates/journal/journal.html:65
+#: rhodecode/templates/journal/journal.html:147
msgid "You are not following any users or repositories"
msgstr "Você não está seguindo quaisquer usuários ou repositórios"
-#: rhodecode/templates/journal/journal_data.html:46
+#: rhodecode/templates/journal/journal_data.html:47
msgid "No entries yet"
msgstr "Ainda não há entradas"
-#: rhodecode/templates/journal/public_journal.html:17
+#: rhodecode/templates/journal/public_journal.html:13
+#, fuzzy
+msgid "ATOM public journal feed"
+msgstr "diário público de %s - feed %s"
+
+#: rhodecode/templates/journal/public_journal.html:14
+#, fuzzy
+msgid "RSS public journal feed"
+msgstr "diário público de %s - feed %s"
+
+#: rhodecode/templates/journal/public_journal.html:21
msgid "Public Journal"
msgstr "Diário Público"
-#: rhodecode/templates/search/search.html:7
-#: rhodecode/templates/search/search.html:26
-msgid "in repository: "
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+#, fuzzy
+msgid "Detailed compare view"
+msgstr "comparar exibir"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+#, fuzzy
+msgid "owner"
+msgstr "Dono"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+msgid "Add reviewer to this pull request."
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+#, fuzzy
+msgid "Create new pull request"
+msgstr "Criar novo arquivo"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+#, fuzzy
+msgid "Title"
+msgstr "escrever"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+#, fuzzy
+msgid "description"
+msgstr "Descrição"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+#, fuzzy
+msgid "Status"
+msgstr "Conjuntos de mudanças"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+msgid "Still not reviewed by"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+#, fuzzy
+msgid "Created on"
+msgstr "criar um agora"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+#, fuzzy
+msgid "Compare view"
+msgstr "comparar exibir"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+#, fuzzy
+msgid "Incoming changesets"
+msgstr "Nenhum conjunto de alterações ainda."
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+#, fuzzy
+msgid "all pull requests"
+msgstr "Criar novo arquivo"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, fuzzy, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr "no repositório"
+
+#: rhodecode/templates/search/search.html:8
+#, fuzzy, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr "em todos os repositórios"
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, fuzzy, python-format
+msgid "Search in repository: %s"
msgstr "no repositório"
-#: rhodecode/templates/search/search.html:9
-#: rhodecode/templates/search/search.html:28
-msgid "in all repositories"
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+#, fuzzy
+msgid "Search in all repositories"
msgstr "em todos os repositórios"
-#: rhodecode/templates/search/search.html:42
+#: rhodecode/templates/search/search.html:48
msgid "Search term"
msgstr "Termo de pesquisa"
-#: rhodecode/templates/search/search.html:54
+#: rhodecode/templates/search/search.html:60
msgid "Search in"
msgstr "Pesquisando em"
-#: rhodecode/templates/search/search.html:57
+#: rhodecode/templates/search/search.html:63
msgid "File contents"
msgstr "Conteúdo dos arquivos"
-#: rhodecode/templates/search/search.html:59
+#: rhodecode/templates/search/search.html:64
+#, fuzzy
+msgid "Commit messages"
+msgstr "mensagem de commit"
+
+#: rhodecode/templates/search/search.html:65
msgid "File names"
msgstr "Nomes dos arquivos"
-#: rhodecode/templates/search/search_content.html:20
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
#: rhodecode/templates/search/search_path.html:15
msgid "Permission denied"
msgstr "Permissão negada"
-#: rhodecode/templates/settings/repo_fork.html:5
-msgid "Fork"
-msgstr "Bifurcação"
-
-#: rhodecode/templates/settings/repo_fork.html:31
-msgid "Fork name"
-msgstr "Nome da bifurcação"
-
-#: rhodecode/templates/settings/repo_fork.html:55
-msgid "fork this repository"
-msgstr "bifurcar este repositório"
+#: rhodecode/templates/settings/repo_settings.html:5
+#, fuzzy, python-format
+msgid "%s Settings"
+msgstr "configurações"
#: rhodecode/templates/shortlog/shortlog.html:5
-#: rhodecode/templates/summary/summary.html:666
-msgid "Shortlog"
-msgstr "Log resumido"
+#, fuzzy, python-format
+msgid "%s Shortlog"
+msgstr "log resumido"
#: rhodecode/templates/shortlog/shortlog.html:14
msgid "shortlog"
msgstr "log resumido"
-#: rhodecode/templates/shortlog/shortlog_data.html:6
+#: rhodecode/templates/shortlog/shortlog_data.html:7
msgid "age"
msgstr "idade"
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+msgid "No commit message"
+msgstr "Nenhuma mensagem de commit"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr "Adicionar ou enviar arquivos diretamente pelo RhodeCode"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr "Fazer push de novo repositório"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+msgid "Existing repository?"
+msgstr "Repositório existente?"
+
+#: rhodecode/templates/summary/summary.html:4
+#, fuzzy, python-format
+msgid "%s Summary"
+msgstr "sumário"
+
#: rhodecode/templates/summary/summary.html:12
msgid "summary"
msgstr "sumário"
-#: rhodecode/templates/summary/summary.html:79
+#: rhodecode/templates/summary/summary.html:20
+#, fuzzy, python-format
+msgid "repo %s ATOM feed"
+msgstr "Assinar o feed atom de %s"
+
+#: rhodecode/templates/summary/summary.html:21
+#, fuzzy, python-format
+msgid "repo %s RSS feed"
+msgstr "Assinar o feed rss de %s"
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+msgid "ATOM"
+msgstr "ATOM"
+
+#: rhodecode/templates/summary/summary.html:82
+#, python-format
+msgid "Non changable ID %s"
+msgstr "ID não alterável %s"
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr "público"
+
+#: rhodecode/templates/summary/summary.html:95
msgid "remote clone"
msgstr "clone remoto"
-#: rhodecode/templates/summary/summary.html:121
-msgid "by"
-msgstr "por"
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
+msgstr "Contato"
-#: rhodecode/templates/summary/summary.html:128
+#: rhodecode/templates/summary/summary.html:130
msgid "Clone url"
msgstr "URL de clonagem"
-#: rhodecode/templates/summary/summary.html:137
-msgid "Trending source files"
-msgstr "Tendências nos arquivos fonte"
+#: rhodecode/templates/summary/summary.html:133
+msgid "Show by Name"
+msgstr "Mostrar por Nome"
+
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
+msgstr "Mostrar por ID"
+
+#: rhodecode/templates/summary/summary.html:142
+msgid "Trending files"
+msgstr "Tendências em arquivos"
+
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
+msgstr "habilitar"
-#: rhodecode/templates/summary/summary.html:146
+#: rhodecode/templates/summary/summary.html:158
msgid "Download"
msgstr "Download"
-#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:162
msgid "There are no downloads yet"
msgstr "Ainda não há downloads"
-#: rhodecode/templates/summary/summary.html:152
+#: rhodecode/templates/summary/summary.html:164
msgid "Downloads are disabled for this repository"
msgstr "Downloads estão desabilitados para este repositório"
-#: rhodecode/templates/summary/summary.html:154
-#: rhodecode/templates/summary/summary.html:320
-msgid "enable"
-msgstr "habilitar"
-
-#: rhodecode/templates/summary/summary.html:162
-#: rhodecode/templates/summary/summary.html:297
-#, python-format
-msgid "Download %s as %s"
-msgstr "Descarregar %s como %s"
+#: rhodecode/templates/summary/summary.html:170
+#, fuzzy
+msgid "Download as zip"
+msgstr "descarregar como bruto"
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:173
msgid "Check this to download archive with subrepos"
msgstr "Marque isto para descarregar arquivo com subrepositórios"
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:173
msgid "with subrepos"
msgstr "com subrepositórios"
-#: rhodecode/templates/summary/summary.html:176
-msgid "Feeds"
-msgstr "Feeds"
-
-#: rhodecode/templates/summary/summary.html:257
-#: rhodecode/templates/summary/summary.html:684
-#: rhodecode/templates/summary/summary.html:695
-msgid "show more"
-msgstr "mostrar mais"
-
-#: rhodecode/templates/summary/summary.html:312
+#: rhodecode/templates/summary/summary.html:186
msgid "Commit activity by day / author"
msgstr "Atividade de commit por dia / autor"
-#: rhodecode/templates/summary/summary.html:324
-msgid "Loaded in"
-msgstr "Carregado em"
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
+msgstr "Estatísticas coletadas:"
+
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
+msgstr "Log resumido"
+
+#: rhodecode/templates/summary/summary.html:220
+msgid "Quick start"
+msgstr "Início rápido"
-#: rhodecode/templates/summary/summary.html:603
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr "Descarregar %s como %s"
+
+#: rhodecode/templates/summary/summary.html:650
msgid "commits"
msgstr "commits"
-#: rhodecode/templates/summary/summary.html:604
+#: rhodecode/templates/summary/summary.html:651
msgid "files added"
msgstr "arquivos adicionados"
-#: rhodecode/templates/summary/summary.html:605
+#: rhodecode/templates/summary/summary.html:652
msgid "files changed"
msgstr "arquivos alterados"
-#: rhodecode/templates/summary/summary.html:606
+#: rhodecode/templates/summary/summary.html:653
msgid "files removed"
msgstr "arquivos removidos"
-#: rhodecode/templates/summary/summary.html:610
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr "commit"
+
+#: rhodecode/templates/summary/summary.html:657
msgid "file added"
msgstr "arquivo adicionado"
-#: rhodecode/templates/summary/summary.html:611
+#: rhodecode/templates/summary/summary.html:658
msgid "file changed"
msgstr "arquivo alterado"
-#: rhodecode/templates/summary/summary.html:612
+#: rhodecode/templates/summary/summary.html:659
msgid "file removed"
msgstr "arquivo removido"
+#: rhodecode/templates/tags/tags.html:5
+#, fuzzy, python-format
+msgid "%s Tags"
+msgstr "%s atrás"
+
diff --git a/rhodecode/i18n/rhodecode.pot b/rhodecode/i18n/rhodecode.pot
index 3b696d28..53651f5a 100644
--- a/rhodecode/i18n/rhodecode.pot
+++ b/rhodecode/i18n/rhodecode.pot
@@ -1,14 +1,14 @@
# Translations template for RhodeCode.
-# Copyright (C) 2011 ORGANIZATION
+# Copyright (C) 2012 ORGANIZATION
# This file is distributed under the same license as the RhodeCode project.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: RhodeCode 1.2.0\n"
+"Project-Id-Version: RhodeCode 1.4.0b\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2011-09-14 15:50-0300\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\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"
@@ -17,17 +17,36 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-#: rhodecode/controllers/changeset.py:108 rhodecode/controllers/changeset.py:149
-#: rhodecode/controllers/changeset.py:216 rhodecode/controllers/changeset.py:229
+#: rhodecode/controllers/changelog.py:94
+msgid "All Branches"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:157
+#, python-format
+msgid "%s line context"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:333 rhodecode/controllers/changeset.py:348
+#: rhodecode/lib/diffs.py:70
msgid "binary file"
msgstr ""
-#: rhodecode/controllers/changeset.py:123 rhodecode/controllers/changeset.py:168
-msgid "Changeset is to big and was cut off, see raw changeset instead"
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is not "
+"allowed"
msgstr ""
-#: rhodecode/controllers/changeset.py:159
-msgid "Diff is to big and was cut off, see raw diff instead"
+#: rhodecode/controllers/compare.py:69
+msgid "There are no changesets yet"
msgstr ""
#: rhodecode/controllers/error.py:69
@@ -56,240 +75,290 @@ msgid ""
"fulfilling the request."
msgstr ""
-#: rhodecode/controllers/feed.py:48
+#: rhodecode/controllers/feed.py:49
#, python-format
msgid "Changes on %s repository"
msgstr ""
-#: rhodecode/controllers/feed.py:49
+#: rhodecode/controllers/feed.py:50
#, python-format
msgid "%s %s feed"
msgstr ""
-#: rhodecode/controllers/files.py:72
-msgid "There are no files yet"
+#: rhodecode/controllers/feed.py:75
+msgid "commited on"
+msgstr ""
+
+#: rhodecode/controllers/files.py:84
+msgid "click here to add new file"
+msgstr ""
+
+#: rhodecode/controllers/files.py:85
+#, python-format
+msgid "There are no files yet %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
msgstr ""
-#: rhodecode/controllers/files.py:262
+#: rhodecode/controllers/files.py:266
#, python-format
msgid "Edited %s via RhodeCode"
msgstr ""
-#: rhodecode/controllers/files.py:267 rhodecode/templates/files/file_diff.html:40
+#: rhodecode/controllers/files.py:271
msgid "No changes"
msgstr ""
-#: rhodecode/controllers/files.py:278
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
#, python-format
msgid "Successfully committed to %s"
msgstr ""
-#: rhodecode/controllers/files.py:283
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
msgid "Error occurred during commit"
msgstr ""
-#: rhodecode/controllers/files.py:308
+#: rhodecode/controllers/files.py:318
+#, python-format
+msgid "Added %s via RhodeCode"
+msgstr ""
+
+#: rhodecode/controllers/files.py:332
+msgid "No content"
+msgstr ""
+
+#: rhodecode/controllers/files.py:336
+msgid "No filename"
+msgstr ""
+
+#: rhodecode/controllers/files.py:378
msgid "downloads disabled"
msgstr ""
-#: rhodecode/controllers/files.py:313
+#: rhodecode/controllers/files.py:389
#, python-format
msgid "Unknown revision %s"
msgstr ""
-#: rhodecode/controllers/files.py:315
+#: rhodecode/controllers/files.py:391
msgid "Empty repository"
msgstr ""
-#: rhodecode/controllers/files.py:317
+#: rhodecode/controllers/files.py:393
msgid "Unknown archive type"
msgstr ""
-#: rhodecode/controllers/files.py:385 rhodecode/controllers/files.py:398
-msgid "Binary file"
-msgstr ""
-
-#: rhodecode/controllers/files.py:417
-#: rhodecode/templates/changeset/changeset_range.html:4
-#: rhodecode/templates/changeset/changeset_range.html:12
-#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
msgid "Changesets"
msgstr ""
-#: rhodecode/controllers/files.py:418 rhodecode/controllers/summary.py:175
-#: rhodecode/templates/branches/branches.html:5
-#: rhodecode/templates/summary/summary.html:690
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
msgid "Branches"
msgstr ""
-#: rhodecode/controllers/files.py:419 rhodecode/controllers/summary.py:176
-#: rhodecode/templates/summary/summary.html:679
-#: rhodecode/templates/tags/tags.html:5
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
msgid "Tags"
msgstr ""
-#: rhodecode/controllers/journal.py:50
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
#, python-format
-msgid "%s public journal %s feed"
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from the "
+"filesystem please run the application again in order to rescan repositories"
msgstr ""
-#: rhodecode/controllers/journal.py:178 rhodecode/controllers/journal.py:212
-#: rhodecode/templates/admin/repos/repo_edit.html:171
-#: rhodecode/templates/base/base.html:50
-msgid "Public journal"
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from the "
+"file system please run the application again in order to rescan repositories"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr ""
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+msgid "public journal"
msgstr ""
-#: rhodecode/controllers/login.py:111
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr ""
+
+#: rhodecode/controllers/login.py:143
msgid "You have successfully registered into rhodecode"
msgstr ""
-#: rhodecode/controllers/login.py:133
+#: rhodecode/controllers/login.py:164
msgid "Your password reset link was sent"
msgstr ""
-#: rhodecode/controllers/login.py:155
+#: rhodecode/controllers/login.py:184
msgid "Your password reset was successful, new password has been sent to your email"
msgstr ""
-#: rhodecode/controllers/search.py:109
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+msgid "Bookmarks"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+msgid "error during creation of pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:181
+msgid "Successfully opened new pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:184
+msgid "Error occurred during sending pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:217
+msgid "Successfully deleted pull request"
+msgstr ""
+
+#: rhodecode/controllers/search.py:131
msgid "Invalid search query. Try quoting it."
msgstr ""
-#: rhodecode/controllers/search.py:114
+#: rhodecode/controllers/search.py:136
msgid "There is no index to search in. Please run whoosh indexer"
msgstr ""
-#: rhodecode/controllers/search.py:118
+#: rhodecode/controllers/search.py:140
msgid "An error occurred during this search operation"
msgstr ""
-#: rhodecode/controllers/settings.py:61 rhodecode/controllers/settings.py:171
-#, python-format
-msgid ""
-"%s repository is not mapped to db perhaps it was created or renamed from the "
-"file system please run the application again in order to rescan repositories"
-msgstr ""
-
-#: rhodecode/controllers/settings.py:109 rhodecode/controllers/admin/repos.py:239
+#: rhodecode/controllers/settings.py:107 rhodecode/controllers/admin/repos.py:266
#, python-format
msgid "Repository %s updated successfully"
msgstr ""
-#: rhodecode/controllers/settings.py:126 rhodecode/controllers/admin/repos.py:257
+#: rhodecode/controllers/settings.py:125 rhodecode/controllers/admin/repos.py:284
#, python-format
msgid "error occurred during update of repository %s"
msgstr ""
-#: rhodecode/controllers/settings.py:144 rhodecode/controllers/admin/repos.py:275
+#: rhodecode/controllers/settings.py:143 rhodecode/controllers/admin/repos.py:302
#, python-format
msgid ""
"%s repository is not mapped to db perhaps it was moved or renamed from the "
"filesystem please run the application again in order to rescan repositories"
msgstr ""
-#: rhodecode/controllers/settings.py:156 rhodecode/controllers/admin/repos.py:287
+#: rhodecode/controllers/settings.py:155 rhodecode/controllers/admin/repos.py:314
#, python-format
msgid "deleted repository %s"
msgstr ""
-#: rhodecode/controllers/settings.py:159 rhodecode/controllers/admin/repos.py:297
-#: rhodecode/controllers/admin/repos.py:303
+#: rhodecode/controllers/settings.py:159 rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
#, python-format
msgid "An error occurred during deletion of %s"
msgstr ""
-#: rhodecode/controllers/settings.py:193
-#, python-format
-msgid "forked %s repository as %s"
-msgstr ""
-
-#: rhodecode/controllers/settings.py:211
-#, python-format
-msgid "An error occurred during repository forking %s"
-msgstr ""
-
-#: rhodecode/controllers/summary.py:123
+#: rhodecode/controllers/summary.py:138
msgid "No data loaded yet"
msgstr ""
-#: rhodecode/controllers/summary.py:126
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
msgid "Statistics are disabled for this repository"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:49
+#: rhodecode/controllers/admin/ldap_settings.py:50
msgid "BASE"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:50
+#: rhodecode/controllers/admin/ldap_settings.py:51
msgid "ONELEVEL"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:51
+#: rhodecode/controllers/admin/ldap_settings.py:52
msgid "SUBTREE"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:55
+#: rhodecode/controllers/admin/ldap_settings.py:56
msgid "NEVER"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:56
+#: rhodecode/controllers/admin/ldap_settings.py:57
msgid "ALLOW"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:57
+#: rhodecode/controllers/admin/ldap_settings.py:58
msgid "TRY"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:58
+#: rhodecode/controllers/admin/ldap_settings.py:59
msgid "DEMAND"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:59
+#: rhodecode/controllers/admin/ldap_settings.py:60
msgid "HARD"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:63
+#: rhodecode/controllers/admin/ldap_settings.py:64
msgid "No encryption"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:64
+#: rhodecode/controllers/admin/ldap_settings.py:65
msgid "LDAPS connection"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:65
+#: rhodecode/controllers/admin/ldap_settings.py:66
msgid "START_TLS on LDAP connection"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:115
+#: rhodecode/controllers/admin/ldap_settings.py:126
msgid "Ldap settings updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:120
+#: rhodecode/controllers/admin/ldap_settings.py:130
msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:134
+#: rhodecode/controllers/admin/ldap_settings.py:147
msgid "error occurred during update of ldap settings"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:56
+#: rhodecode/controllers/admin/permissions.py:59
msgid "None"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:57
+#: rhodecode/controllers/admin/permissions.py:60
msgid "Read"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:58
+#: rhodecode/controllers/admin/permissions.py:61
msgid "Write"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:59
+#: rhodecode/controllers/admin/permissions.py:62
#: rhodecode/templates/admin/ldap/ldap.html:9
#: rhodecode/templates/admin/permissions/permissions.html:9
#: rhodecode/templates/admin/repos/repo_add.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:9
-#: rhodecode/templates/admin/repos/repos.html:10
+#: rhodecode/templates/admin/repos/repos.html:9
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
@@ -297,546 +366,868 @@ msgstr ""
#: rhodecode/templates/admin/settings/settings.html:9
#: rhodecode/templates/admin/users/user_add.html:8
#: rhodecode/templates/admin/users/user_edit.html:9
-#: rhodecode/templates/admin/users/user_edit.html:110
+#: rhodecode/templates/admin/users/user_edit.html:122
#: rhodecode/templates/admin/users/users.html:9
#: rhodecode/templates/admin/users_groups/users_group_add.html:8
#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
#: rhodecode/templates/admin/users_groups/users_groups.html:9
-#: rhodecode/templates/base/base.html:279 rhodecode/templates/base/base.html:366
-#: rhodecode/templates/base/base.html:368 rhodecode/templates/base/base.html:370
+#: rhodecode/templates/base/base.html:197 rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339 rhodecode/templates/base/base.html:341
msgid "Admin"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/controllers/admin/permissions.py:65
msgid "disabled"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:64
+#: rhodecode/controllers/admin/permissions.py:67
msgid "allowed with manual account activation"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:66
+#: rhodecode/controllers/admin/permissions.py:69
msgid "allowed with automatic account activation"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:68
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
msgid "Disabled"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:69
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
msgid "Enabled"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:102
+#: rhodecode/controllers/admin/permissions.py:116
msgid "Default permissions updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:119
+#: rhodecode/controllers/admin/permissions.py:130
msgid "error occurred during update of permissions"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:96
-#, python-format
-msgid ""
-"%s repository is not mapped to db perhaps it was created or renamed from the "
-"filesystem please run the application again in order to rescan repositories"
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:172
+#: rhodecode/controllers/admin/repos.py:192
#, python-format
msgid "created repository %s from %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:176
+#: rhodecode/controllers/admin/repos.py:196
#, python-format
msgid "created repository %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:205
+#: rhodecode/controllers/admin/repos.py:227
#, python-format
msgid "error occurred during creation of repository %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:292
+#: rhodecode/controllers/admin/repos.py:319
#, python-format
msgid "Cannot delete %s it still contains attached forks"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:320
+#: rhodecode/controllers/admin/repos.py:348
msgid "An error occurred during deletion of repository user"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:335
+#: rhodecode/controllers/admin/repos.py:367
msgid "An error occurred during deletion of repository users groups"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:352
+#: rhodecode/controllers/admin/repos.py:385
msgid "An error occurred during deletion of repository stats"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:367
+#: rhodecode/controllers/admin/repos.py:402
msgid "An error occurred during cache invalidation"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:387
+#: rhodecode/controllers/admin/repos.py:422
+msgid "An error occurred during unlocking"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:442
msgid "Updated repository visibility in public journal"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:390
+#: rhodecode/controllers/admin/repos.py:446
msgid "An error occurred during setting this repository in public journal"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:395 rhodecode/model/forms.py:53
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
msgid "Token mismatch"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:408
+#: rhodecode/controllers/admin/repos.py:464
msgid "Pulled from remote location"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:410
+#: rhodecode/controllers/admin/repos.py:466
msgid "An error occurred during pull from remote location"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:83
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:484
+#, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:488
+msgid "An error occurred during this operation"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:116
#, python-format
msgid "created repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:96
+#: rhodecode/controllers/admin/repos_groups.py:129
#, python-format
msgid "error occurred during creation of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:130
+#: rhodecode/controllers/admin/repos_groups.py:163
#, python-format
msgid "updated repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:143
+#: rhodecode/controllers/admin/repos_groups.py:176
#, python-format
msgid "error occurred during update of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:164
+#: rhodecode/controllers/admin/repos_groups.py:194
#, python-format
msgid "This group contains %s repositores and cannot be deleted"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:171
+#: rhodecode/controllers/admin/repos_groups.py:202
#, python-format
msgid "removed repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:175
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
#, python-format
msgid "error occurred during deletion of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:109
+#: rhodecode/controllers/admin/repos_groups.py:238
+msgid "An error occurred during deletion of group user"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:121
#, python-format
msgid "Repositories successfully rescanned added: %s,removed: %s"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:118
+#: rhodecode/controllers/admin/settings.py:129
msgid "Whoosh reindex task scheduled"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:143
+#: rhodecode/controllers/admin/settings.py:160
msgid "Updated application settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:148
-#: rhodecode/controllers/admin/settings.py:215
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
msgid "error occurred during updating application settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:210
-msgid "Updated mercurial settings"
+#: rhodecode/controllers/admin/settings.py:200
+msgid "Updated visualisation settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:205
+msgid "error occurred during updating visualisation settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:271
+msgid "Updated VCS settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:236
+#: rhodecode/controllers/admin/settings.py:285
msgid "Added new hook"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:247
+#: rhodecode/controllers/admin/settings.py:297
msgid "Updated hooks"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:251
+#: rhodecode/controllers/admin/settings.py:301
msgid "error occurred during hook creation"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:310
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:375
msgid "You can't edit this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:339
+#: rhodecode/controllers/admin/settings.py:406
msgid "Your account was updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:359
-#: rhodecode/controllers/admin/users.py:130
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
#, python-format
msgid "error occurred during update of user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:78
+#: rhodecode/controllers/admin/users.py:130
#, python-format
msgid "created user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:90
+#: rhodecode/controllers/admin/users.py:142
#, python-format
msgid "error occurred during creation of user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:116
+#: rhodecode/controllers/admin/users.py:171
msgid "User updated successfully"
msgstr ""
-#: rhodecode/controllers/admin/users.py:146
+#: rhodecode/controllers/admin/users.py:207
msgid "successfully deleted user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:150
+#: rhodecode/controllers/admin/users.py:212
msgid "An error occurred during deletion of user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:166
+#: rhodecode/controllers/admin/users.py:226
msgid "You can't edit this user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:195
-#: rhodecode/controllers/admin/users_groups.py:202
+#: rhodecode/controllers/admin/users.py:266
msgid "Granted 'repository create' permission to user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:204
-#: rhodecode/controllers/admin/users_groups.py:211
+#: rhodecode/controllers/admin/users.py:271
msgid "Revoked 'repository create' permission to user"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:74
+#: rhodecode/controllers/admin/users.py:277
+msgid "Granted 'repository fork' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:282
+msgid "Revoked 'repository fork' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+msgid "An error occurred during permissions saving"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:309
+msgid "An error occurred during email saving"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:319
+msgid "Removed email from user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:84
#, python-format
msgid "created users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:86
+#: rhodecode/controllers/admin/users_groups.py:95
#, python-format
msgid "error occurred during creation of users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:119
+#: rhodecode/controllers/admin/users_groups.py:135
#, python-format
msgid "updated users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:138
+#: rhodecode/controllers/admin/users_groups.py:157
#, python-format
msgid "error occurred during update of users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:154
+#: rhodecode/controllers/admin/users_groups.py:174
msgid "successfully deleted users group"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:158
+#: rhodecode/controllers/admin/users_groups.py:179
msgid "An error occurred during deletion of users group"
msgstr ""
-#: rhodecode/lib/__init__.py:279
-msgid "year"
-msgstr ""
-
-#: rhodecode/lib/__init__.py:280
-msgid "month"
+#: rhodecode/controllers/admin/users_groups.py:233
+msgid "Granted 'repository create' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:281
-msgid "day"
+#: rhodecode/controllers/admin/users_groups.py:238
+msgid "Revoked 'repository create' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:282
-msgid "hour"
+#: rhodecode/controllers/admin/users_groups.py:244
+msgid "Granted 'repository fork' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:283
-msgid "minute"
+#: rhodecode/controllers/admin/users_groups.py:249
+msgid "Revoked 'repository fork' permission to users group"
msgstr ""
-#: rhodecode/lib/__init__.py:284
-msgid "second"
+#: rhodecode/lib/auth.py:499
+msgid "You need to be a registered user to perform this action"
msgstr ""
-#: rhodecode/lib/__init__.py:293
-msgid "ago"
+#: rhodecode/lib/auth.py:540
+msgid "You need to be a signed in to view this page"
msgstr ""
-#: rhodecode/lib/__init__.py:296
-msgid "just now"
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
msgstr ""
-#: rhodecode/lib/auth.py:377
-msgid "You need to be a registered user to perform this action"
+#: rhodecode/lib/diffs.py:96
+msgid "No changes detected"
msgstr ""
-#: rhodecode/lib/auth.py:421
-msgid "You need to be a signed in to view this page"
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
msgstr ""
-#: rhodecode/lib/helpers.py:307
+#: rhodecode/lib/helpers.py:484
msgid "True"
msgstr ""
-#: rhodecode/lib/helpers.py:311
+#: rhodecode/lib/helpers.py:488
msgid "False"
msgstr ""
-#: rhodecode/lib/helpers.py:352
+#: rhodecode/lib/helpers.py:532
+msgid "Changeset not found"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:555
#, python-format
msgid "Show all combined changesets %s->%s"
msgstr ""
-#: rhodecode/lib/helpers.py:356
+#: rhodecode/lib/helpers.py:561
msgid "compare view"
msgstr ""
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:581
msgid "and"
msgstr ""
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:582
#, python-format
msgid "%s more"
msgstr ""
-#: rhodecode/lib/helpers.py:367 rhodecode/templates/changelog/changelog.html:14
-#: rhodecode/templates/changelog/changelog.html:39
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
msgid "revisions"
msgstr ""
-#: rhodecode/lib/helpers.py:385
+#: rhodecode/lib/helpers.py:606
msgid "fork name "
msgstr ""
-#: rhodecode/lib/helpers.py:388
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:626
msgid "[deleted] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:389 rhodecode/lib/helpers.py:393
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
msgid "[created] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:390 rhodecode/lib/helpers.py:394
+#: rhodecode/lib/helpers.py:630
+msgid "[created] repository as fork"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
msgid "[forked] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:391 rhodecode/lib/helpers.py:395
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
msgid "[updated] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:392
+#: rhodecode/lib/helpers.py:636
msgid "[delete] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:396
+#: rhodecode/lib/helpers.py:644
+msgid "[created] user"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:646
+msgid "[updated] user"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:648
+msgid "[created] users group"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:650
+msgid "[updated] users group"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:652
+msgid "[commented] on revision in repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:654
+msgid "[commented] on pull request for"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:656
+msgid "[closed] pull request for"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:658
msgid "[pushed] into"
msgstr ""
-#: rhodecode/lib/helpers.py:397
-msgid "[committed via RhodeCode] into"
+#: rhodecode/lib/helpers.py:660
+msgid "[committed via RhodeCode] into repository"
msgstr ""
-#: rhodecode/lib/helpers.py:398
-msgid "[pulled from remote] into"
+#: rhodecode/lib/helpers.py:662
+msgid "[pulled from remote] into repository"
msgstr ""
-#: rhodecode/lib/helpers.py:399
+#: rhodecode/lib/helpers.py:664
msgid "[pulled] from"
msgstr ""
-#: rhodecode/lib/helpers.py:400
+#: rhodecode/lib/helpers.py:666
msgid "[started following] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:401
+#: rhodecode/lib/helpers.py:668
msgid "[stopped following] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:577
+#: rhodecode/lib/helpers.py:840
#, python-format
msgid " and %s more"
msgstr ""
-#: rhodecode/lib/helpers.py:581
+#: rhodecode/lib/helpers.py:844
msgid "No Files"
msgstr ""
-#: rhodecode/model/forms.py:66
-msgid "Invalid username"
+#: rhodecode/lib/utils2.py:335
+#, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:336
+#, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:337
+#, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:338
+#, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:339
+#, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:340
+#, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/lib/utils2.py:355
+#, python-format
+msgid "%s ago"
msgstr ""
-#: rhodecode/model/forms.py:75
-msgid "This username already exists"
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
msgstr ""
-#: rhodecode/model/forms.py:79
-msgid ""
-"Username may only contain alphanumeric characters underscores, periods or "
-"dashes and must begin with alphanumeric character"
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
msgstr ""
-#: rhodecode/model/forms.py:94
-msgid "Invalid group name"
+#: rhodecode/lib/celerylib/tasks.py:269
+msgid "password reset link"
msgstr ""
-#: rhodecode/model/forms.py:104
-msgid "This users group already exists"
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
msgstr ""
-#: rhodecode/model/forms.py:110
-msgid ""
-"Group name may only contain alphanumeric characters underscores, periods or "
-"dashes and must begin with alphanumeric character"
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
msgstr ""
-#: rhodecode/model/forms.py:132
-msgid "Cannot assign this group as parent"
+#: rhodecode/model/db.py:1140
+msgid "Repository no access"
msgstr ""
-#: rhodecode/model/forms.py:148
-msgid "This group already exists"
+#: rhodecode/model/db.py:1141
+msgid "Repository read access"
msgstr ""
-#: rhodecode/model/forms.py:164 rhodecode/model/forms.py:172
-#: rhodecode/model/forms.py:180
-msgid "Invalid characters in password"
+#: rhodecode/model/db.py:1142
+msgid "Repository write access"
msgstr ""
-#: rhodecode/model/forms.py:191
-msgid "Passwords do not match"
+#: rhodecode/model/db.py:1143
+msgid "Repository admin access"
msgstr ""
-#: rhodecode/model/forms.py:196
-msgid "invalid password"
+#: rhodecode/model/db.py:1145
+msgid "Repositories Group no access"
msgstr ""
-#: rhodecode/model/forms.py:197
-msgid "invalid user name"
+#: rhodecode/model/db.py:1146
+msgid "Repositories Group read access"
msgstr ""
-#: rhodecode/model/forms.py:198
-msgid "Your account is disabled"
+#: rhodecode/model/db.py:1147
+msgid "Repositories Group write access"
msgstr ""
-#: rhodecode/model/forms.py:233
-msgid "This username is not valid"
+#: rhodecode/model/db.py:1148
+msgid "Repositories Group admin access"
msgstr ""
-#: rhodecode/model/forms.py:245
-msgid "This repository name is disallowed"
+#: rhodecode/model/db.py:1150
+msgid "RhodeCode Administrator"
msgstr ""
-#: rhodecode/model/forms.py:266
-#, python-format
-msgid "This repository already exists in group \"%s\""
+#: rhodecode/model/db.py:1151
+msgid "Repository creation disabled"
msgstr ""
-#: rhodecode/model/forms.py:274
-msgid "This repository already exists"
+#: rhodecode/model/db.py:1152
+msgid "Repository creation enabled"
msgstr ""
-#: rhodecode/model/forms.py:312 rhodecode/model/forms.py:319
-msgid "invalid clone url"
+#: rhodecode/model/db.py:1153
+msgid "Repository forking disabled"
msgstr ""
-#: rhodecode/model/forms.py:322
-msgid "Invalid clone url, provide a valid clone http\\s url"
+#: rhodecode/model/db.py:1154
+msgid "Repository forking enabled"
msgstr ""
-#: rhodecode/model/forms.py:334
-msgid "Fork have to be the same type as original"
+#: rhodecode/model/db.py:1155
+msgid "Register disabled"
msgstr ""
-#: rhodecode/model/forms.py:341
-msgid "This username or users group name is not valid"
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
msgstr ""
-#: rhodecode/model/forms.py:403
-msgid "This is not a valid path"
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
msgstr ""
-#: rhodecode/model/forms.py:416
-msgid "This e-mail address is already taken"
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
msgstr ""
-#: rhodecode/model/forms.py:427
-msgid "This e-mail address doesn't exist."
+#: rhodecode/model/db.py:1580
+msgid "Approved"
msgstr ""
-#: rhodecode/model/forms.py:447
-msgid ""
-"The LDAP Login attribute of the CN must be specified - this is the name of "
-"the attribute that is equivalent to 'username'"
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
+msgstr ""
+
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
msgstr ""
-#: rhodecode/model/forms.py:466
+#: rhodecode/model/forms.py:43
msgid "Please enter a login"
msgstr ""
-#: rhodecode/model/forms.py:467
+#: rhodecode/model/forms.py:44
#, python-format
msgid "Enter a value %(min)i characters long or more"
msgstr ""
-#: rhodecode/model/forms.py:475
+#: rhodecode/model/forms.py:52
msgid "Please enter a password"
msgstr ""
-#: rhodecode/model/forms.py:476
+#: rhodecode/model/forms.py:53
#, python-format
msgid "Enter %(min)i characters or more"
msgstr ""
-#: rhodecode/model/user.py:145
-msgid "[RhodeCode] New User registration"
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr ""
+
+#: rhodecode/model/notification.py:221
+msgid "sent message"
+msgstr ""
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr ""
+
+#: rhodecode/model/notification.py:223
+msgid "registered in RhodeCode"
+msgstr ""
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
msgstr ""
-#: rhodecode/model/user.py:157 rhodecode/model/user.py:179
+#: rhodecode/model/notification.py:225
+msgid "commented on pull request"
+msgstr ""
+
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+msgid "latest tip"
+msgstr ""
+
+#: rhodecode/model/user.py:230
+msgid "new user registration"
+msgstr ""
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
msgid "You can't Edit this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/model/user.py:201
+#: rhodecode/model/user.py:323
msgid "You can't remove this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/model/user.py:204
+#: rhodecode/model/user.py:329
#, python-format
msgid ""
-"This user still owns %s repositories and cannot be removed. Switch owners or "
-"remove those repositories"
+"user \"%s\" still owns %s repositories and cannot be removed. Switch owners "
+"or remove those repositories. %s"
+msgstr ""
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, python-format
+msgid "Username \"%(username)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr ""
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or "
+"dashes and must begin with alphanumeric character"
+msgstr ""
+
+#: rhodecode/model/validators.py:114
+#, python-format
+msgid "Username %(username)s is not valid"
+msgstr ""
+
+#: rhodecode/model/validators.py:133
+msgid "Invalid users group name"
+msgstr ""
+
+#: rhodecode/model/validators.py:134
+#, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr ""
+
+#: rhodecode/model/validators.py:174
+msgid "Cannot assign this group as parent"
+msgstr ""
+
+#: rhodecode/model/validators.py:175
+#, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:177
+#, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:235
+msgid "Invalid characters (non-ascii) in password"
+msgstr ""
+
+#: rhodecode/model/validators.py:250
+msgid "Passwords do not match"
+msgstr ""
+
+#: rhodecode/model/validators.py:267
+msgid "invalid password"
msgstr ""
-#: rhodecode/templates/index.html:4
+#: rhodecode/model/validators.py:268
+msgid "invalid user name"
+msgstr ""
+
+#: rhodecode/model/validators.py:269
+msgid "Your account is disabled"
+msgstr ""
+
+#: rhodecode/model/validators.py:313
+#, python-format
+msgid "Repository name %(repo)s is disallowed"
+msgstr ""
+
+#: rhodecode/model/validators.py:315
+#, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:316
+#, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr ""
+
+#: rhodecode/model/validators.py:318
+#, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
+msgstr ""
+
+#: rhodecode/model/validators.py:431
+msgid "invalid clone url"
+msgstr ""
+
+#: rhodecode/model/validators.py:432
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
+msgstr ""
+
+#: rhodecode/model/validators.py:457
+msgid "Fork have to be the same type as parent"
+msgstr ""
+
+#: rhodecode/model/validators.py:478
+msgid "This username or users group name is not valid"
+msgstr ""
+
+#: rhodecode/model/validators.py:562
+msgid "This is not a valid path"
+msgstr ""
+
+#: rhodecode/model/validators.py:577
+msgid "This e-mail address is already taken"
+msgstr ""
+
+#: rhodecode/model/validators.py:597
+#, python-format
+msgid "e-mail \"%(email)s\" does not exist."
+msgstr ""
+
+#: rhodecode/model/validators.py:634
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name of "
+"the attribute that is equivalent to \"username\""
+msgstr ""
+
+#: rhodecode/model/validators.py:653
+#, python-format
+msgid "Revisions %(revs)s are already part of pull request or have set status"
+msgstr ""
+
+#: rhodecode/templates/index.html:3
msgid "Dashboard"
msgstr ""
-#: rhodecode/templates/index_base.html:22
-#: rhodecode/templates/admin/users/user_edit_my_account.html:102
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
msgid "quick filter..."
msgstr ""
-#: rhodecode/templates/index_base.html:23 rhodecode/templates/base/base.html:300
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
msgid "repositories"
msgstr ""
-#: rhodecode/templates/index_base.html:29
-#: rhodecode/templates/admin/repos/repos.html:22
-msgid "ADD NEW REPOSITORY"
+#: rhodecode/templates/index_base.html:13 rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
msgstr ""
-#: rhodecode/templates/index_base.html:41
+#: rhodecode/templates/index_base.html:29
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
@@ -845,145 +1236,148 @@ msgstr ""
msgid "Group name"
msgstr ""
-#: rhodecode/templates/index_base.html:42 rhodecode/templates/index_base.html:73
-#: rhodecode/templates/admin/repos/repo_add_base.html:44
-#: rhodecode/templates/admin/repos/repo_edit.html:64
-#: rhodecode/templates/admin/repos/repos.html:31
+#: rhodecode/templates/index_base.html:30 rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142 rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
-#: rhodecode/templates/settings/repo_fork.html:40
-#: rhodecode/templates/settings/repo_settings.html:40
-#: rhodecode/templates/summary/summary.html:92
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
msgid "Description"
msgstr ""
-#: rhodecode/templates/index_base.html:53
+#: rhodecode/templates/index_base.html:40
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
msgid "Repositories group"
msgstr ""
-#: rhodecode/templates/index_base.html:72
+#: rhodecode/templates/index_base.html:70 rhodecode/templates/index_base.html:166
#: rhodecode/templates/admin/repos/repo_add_base.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:32
-#: rhodecode/templates/admin/repos/repos.html:30
-#: rhodecode/templates/admin/users/user_edit_my_account.html:117
-#: rhodecode/templates/files/files_browser.html:157
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
#: rhodecode/templates/settings/repo_settings.html:31
-#: rhodecode/templates/summary/summary.html:31
-#: rhodecode/templates/summary/summary.html:107
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36 rhodecode/templates/tags/tags_data.html:6
msgid "Name"
msgstr ""
-#: rhodecode/templates/index_base.html:74
-#: rhodecode/templates/admin/repos/repos.html:32
-#: rhodecode/templates/summary/summary.html:114
+#: rhodecode/templates/index_base.html:72
msgid "Last change"
msgstr ""
-#: rhodecode/templates/index_base.html:75
-#: rhodecode/templates/admin/repos/repos.html:33
+#: rhodecode/templates/index_base.html:73 rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
msgid "Tip"
msgstr ""
-#: rhodecode/templates/index_base.html:76
-#: rhodecode/templates/admin/repos/repo_edit.html:97
+#: rhodecode/templates/index_base.html:74 rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
msgid "Owner"
msgstr ""
-#: rhodecode/templates/index_base.html:77
-#: rhodecode/templates/journal/public_journal.html:20
-#: rhodecode/templates/summary/summary.html:180
-#: rhodecode/templates/summary/summary.html:183
+#: rhodecode/templates/index_base.html:75
+#: rhodecode/templates/summary/summary.html:48
+#: rhodecode/templates/summary/summary.html:51
msgid "RSS"
msgstr ""
-#: rhodecode/templates/index_base.html:78
-#: rhodecode/templates/journal/public_journal.html:23
-#: rhodecode/templates/summary/summary.html:181
-#: rhodecode/templates/summary/summary.html:184
+#: rhodecode/templates/index_base.html:76
msgid "Atom"
msgstr ""
-#: rhodecode/templates/index_base.html:87 rhodecode/templates/index_base.html:89
-#: rhodecode/templates/index_base.html:91 rhodecode/templates/base/base.html:209
-#: rhodecode/templates/base/base.html:211 rhodecode/templates/base/base.html:213
-#: rhodecode/templates/summary/summary.html:4
-msgid "Summary"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:95 rhodecode/templates/index_base.html:97
-#: rhodecode/templates/index_base.html:99 rhodecode/templates/base/base.html:225
-#: rhodecode/templates/base/base.html:227 rhodecode/templates/base/base.html:229
-#: rhodecode/templates/changelog/changelog.html:6
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "Changelog"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:103 rhodecode/templates/index_base.html:105
-#: rhodecode/templates/index_base.html:107 rhodecode/templates/base/base.html:268
-#: rhodecode/templates/base/base.html:270 rhodecode/templates/base/base.html:272
-#: rhodecode/templates/files/files.html:4
-msgid "Files"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:116
-#: rhodecode/templates/admin/repos/repos.html:42
-#: rhodecode/templates/admin/users/user_edit_my_account.html:127
-#: rhodecode/templates/summary/summary.html:48
-msgid "Mercurial repository"
+#: rhodecode/templates/index_base.html:110 rhodecode/templates/index_base.html:112
+#, python-format
+msgid "Subscribe to %s rss feed"
msgstr ""
-#: rhodecode/templates/index_base.html:118
-#: rhodecode/templates/admin/repos/repos.html:44
-#: rhodecode/templates/admin/users/user_edit_my_account.html:129
-#: rhodecode/templates/summary/summary.html:51
-msgid "Git repository"
+#: rhodecode/templates/index_base.html:117 rhodecode/templates/index_base.html:119
+#, python-format
+msgid "Subscribe to %s atom feed"
msgstr ""
-#: rhodecode/templates/index_base.html:123
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
-#: rhodecode/templates/journal/journal.html:53
-#: rhodecode/templates/summary/summary.html:56
-msgid "private repository"
+#: rhodecode/templates/index_base.html:140
+msgid "Group Name"
msgstr ""
-#: rhodecode/templates/index_base.html:125
-#: rhodecode/templates/journal/journal.html:55
-#: rhodecode/templates/summary/summary.html:58
-msgid "public repository"
+#: rhodecode/templates/index_base.html:158 rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
msgstr ""
-#: rhodecode/templates/index_base.html:133 rhodecode/templates/base/base.html:291
-#: rhodecode/templates/settings/repo_fork.html:13
-msgid "fork"
+#: rhodecode/templates/index_base.html:159 rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
msgstr ""
-#: rhodecode/templates/index_base.html:134
-#: rhodecode/templates/admin/repos/repos.html:60
-#: rhodecode/templates/admin/users/user_edit_my_account.html:143
-#: rhodecode/templates/summary/summary.html:69
-#: rhodecode/templates/summary/summary.html:71
-msgid "Fork of"
+#: rhodecode/templates/index_base.html:169
+msgid "Last Change"
msgstr ""
-#: rhodecode/templates/index_base.html:155
-#: rhodecode/templates/admin/repos/repos.html:73
-msgid "No changesets yet"
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
msgstr ""
-#: rhodecode/templates/index_base.html:161 rhodecode/templates/index_base.html:163
-#, python-format
-msgid "Subscribe to %s rss feed"
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
msgstr ""
-#: rhodecode/templates/index_base.html:168 rhodecode/templates/index_base.html:170
-#, python-format
-msgid "Subscribe to %s atom feed"
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+msgid "Loading..."
msgstr ""
#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
-#: rhodecode/templates/base/base.html:38
msgid "Sign In"
msgstr ""
@@ -994,25 +1388,29 @@ msgstr ""
#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
#: rhodecode/templates/admin/admin_log.html:5
#: rhodecode/templates/admin/users/user_add.html:32
-#: rhodecode/templates/admin/users/user_edit.html:47
-#: rhodecode/templates/admin/users/user_edit_my_account.html:45
-#: rhodecode/templates/base/base.html:15
-#: rhodecode/templates/summary/summary.html:106
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
msgid "Username"
msgstr ""
#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
#: rhodecode/templates/admin/ldap/ldap.html:46
#: rhodecode/templates/admin/users/user_add.html:41
-#: rhodecode/templates/base/base.html:24
+#: rhodecode/templates/base/base.html:92
msgid "Password"
msgstr ""
+#: rhodecode/templates/login.html:50
+msgid "Remember me"
+msgstr ""
+
#: rhodecode/templates/login.html:60
msgid "Forgot your password ?"
msgstr ""
-#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:35
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
msgid "Don't have an account ?"
msgstr ""
@@ -1049,24 +1447,24 @@ msgid "Re-enter password"
msgstr ""
#: rhodecode/templates/register.html:47
-#: rhodecode/templates/admin/users/user_add.html:50
-#: rhodecode/templates/admin/users/user_edit.html:74
-#: rhodecode/templates/admin/users/user_edit_my_account.html:63
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
msgid "First Name"
msgstr ""
#: rhodecode/templates/register.html:56
-#: rhodecode/templates/admin/users/user_add.html:59
-#: rhodecode/templates/admin/users/user_edit.html:83
-#: rhodecode/templates/admin/users/user_edit_my_account.html:72
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
msgid "Last Name"
msgstr ""
#: rhodecode/templates/register.html:65
-#: rhodecode/templates/admin/users/user_add.html:68
-#: rhodecode/templates/admin/users/user_edit.html:92
-#: rhodecode/templates/admin/users/user_edit_my_account.html:81
-#: rhodecode/templates/summary/summary.html:108
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
msgid "Email"
msgstr ""
@@ -1078,19 +1476,58 @@ msgstr ""
msgid "Your account must wait for activation by administrator"
msgstr ""
-#: rhodecode/templates/repo_switcher_list.html:14
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
msgid "Private repository"
msgstr ""
-#: rhodecode/templates/repo_switcher_list.html:19
+#: rhodecode/templates/repo_switcher_list.html:16
msgid "Public repository"
msgstr ""
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+msgid "There are no bookmarks yet"
+msgstr ""
+
#: rhodecode/templates/admin/admin.html:5 rhodecode/templates/admin/admin.html:9
msgid "Admin journal"
msgstr ""
#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
msgid "Action"
msgstr ""
@@ -1099,6 +1536,10 @@ msgid "Repository"
msgstr ""
#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37 rhodecode/templates/tags/tags_data.html:7
msgid "Date"
msgstr ""
@@ -1106,7 +1547,7 @@ msgstr ""
msgid "From IP"
msgstr ""
-#: rhodecode/templates/admin/admin_log.html:52
+#: rhodecode/templates/admin/admin_log.html:53
msgid "No actions yet"
msgstr ""
@@ -1183,23 +1624,62 @@ msgid "E-mail Attribute"
msgstr ""
#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
#: rhodecode/templates/admin/settings/hooks.html:73
-#: rhodecode/templates/admin/users/user_edit.html:117
-#: rhodecode/templates/admin/users/user_edit.html:142
-#: rhodecode/templates/admin/users/user_edit_my_account.html:89
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:263
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
msgid "Save"
msgstr ""
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+msgid "Comments"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254 rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+msgid "No notifications here yet"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+msgid "Show notification"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+msgid "Notifications"
+msgstr ""
+
#: rhodecode/templates/admin/permissions/permissions.html:5
msgid "Permissions administration"
msgstr ""
#: rhodecode/templates/admin/permissions/permissions.html:11
-#: rhodecode/templates/admin/repos/repo_edit.html:109
-#: rhodecode/templates/admin/users/user_edit.html:127
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:248
-#: rhodecode/templates/settings/repo_settings.html:58
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
msgid "Permissions"
msgstr ""
@@ -1235,6 +1715,11 @@ msgid "Repository creation"
msgstr ""
#: rhodecode/templates/admin/permissions/permissions.html:71
+msgid "Repository forking"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
msgid "set"
msgstr ""
@@ -1245,7 +1730,6 @@ msgstr ""
#: rhodecode/templates/admin/repos/repo_add.html:11
#: rhodecode/templates/admin/repos/repo_edit.html:11
-#: rhodecode/templates/admin/repos/repos.html:10
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
msgid "Repositories"
msgstr ""
@@ -1255,30 +1739,70 @@ msgid "add new"
msgstr ""
#: rhodecode/templates/admin/repos/repo_add_base.html:20
-#: rhodecode/templates/summary/summary.html:80
-#: rhodecode/templates/summary/summary.html:82
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
msgid "Clone from"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:28
-#: rhodecode/templates/admin/repos/repo_edit.html:48
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
msgid "Repository group"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:36
-#: rhodecode/templates/admin/repos/repo_edit.html:56
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+msgid "Optionaly select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
msgid "Type"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:52
-#: rhodecode/templates/admin/repos/repo_edit.html:73
-#: rhodecode/templates/settings/repo_fork.html:48
-#: rhodecode/templates/settings/repo_settings.html:49
-msgid "Private"
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+msgid "Type of repository to create."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+msgid "Landing revision"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:59
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
msgid "add"
msgstr ""
@@ -1292,183 +1816,268 @@ msgstr ""
#: rhodecode/templates/admin/repos/repo_edit.html:13
#: rhodecode/templates/admin/users/user_edit.html:13
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
-#: rhodecode/templates/files/files_annotate.html:49
-#: rhodecode/templates/files/files_source.html:20
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
msgid "edit"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
msgid "Clone uri"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:81
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
msgid "Enable statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
msgid "Enable downloads"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:127
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+msgid "Enable locking"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+msgid "Change owner of this repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
msgid "Administration"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:130
+#: rhodecode/templates/admin/repos/repo_edit.html:155
msgid "Statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Reset current statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Confirm to remove current statistics"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:137
+#: rhodecode/templates/admin/repos/repo_edit.html:162
msgid "Fetched to rev"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:138
-msgid "Percentage of stats gathered"
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:147
+#: rhodecode/templates/admin/repos/repo_edit.html:171
msgid "Remote"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Pull changes from remote location"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Confirm to pull changes from remote side"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:162
+#: rhodecode/templates/admin/repos/repo_edit.html:186
msgid "Cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Invalidate repository cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Confirm to invalidate repository cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:177
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318 rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
msgid "Remove from public journal"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:179
+#: rhodecode/templates/admin/repos/repo_edit.html:203
msgid "Add to public journal"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:185
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in public "
+"journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+msgid "Locking"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Confirm to unlock repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "Confirm to lock repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+msgid "Repository is not locked"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+msgid "Set as fork of"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+msgid "Manually set this repository as a fork of another from the list"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
msgid "Delete"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:189
+#: rhodecode/templates/admin/repos/repo_edit.html:255
msgid "Remove this repository"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:189
-#: rhodecode/templates/admin/repos/repos.html:79
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
msgid "Confirm to delete this repository"
msgstr ""
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be unaccesible "
+"for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem please "
+"do it manually"
+msgstr ""
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
msgid "none"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
msgid "read"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
msgid "write"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
-#: rhodecode/templates/admin/users/users.html:38
-#: rhodecode/templates/base/base.html:296
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
msgid "admin"
msgstr ""
#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
msgid "member"
msgstr ""
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+msgid "default"
+msgstr ""
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:53
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
msgid "revoke"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:75
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
msgid "Add another member"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:89
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
msgid "Failed to remove user"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:104
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
msgid "Failed to remove users group"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:205
-msgid "Group"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:206
-#: rhodecode/templates/admin/users_groups/users_groups.html:33
-msgid "members"
-msgstr ""
-
#: rhodecode/templates/admin/repos/repos.html:5
msgid "Repositories administration"
msgstr ""
-#: rhodecode/templates/admin/repos/repos.html:34
-#: rhodecode/templates/summary/summary.html:100
-msgid "Contact"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:35
-#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
-#: rhodecode/templates/admin/users/user_edit_my_account.html:119
-#: rhodecode/templates/admin/users/users.html:40
-#: rhodecode/templates/admin/users_groups/users_groups.html:35
-msgid "action"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:51
-#: rhodecode/templates/admin/users/user_edit_my_account.html:134
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
-msgid "private"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:53
-#: rhodecode/templates/admin/repos/repos.html:59
-#: rhodecode/templates/admin/users/user_edit_my_account.html:136
-#: rhodecode/templates/admin/users/user_edit_my_account.html:142
-#: rhodecode/templates/summary/summary.html:68
-msgid "public"
-msgstr ""
-
-#: rhodecode/templates/admin/repos/repos.html:79
-#: rhodecode/templates/admin/users/users.html:55
-msgid "delete"
-msgstr ""
-
#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
msgid "Groups"
msgstr ""
-#: rhodecode/templates/admin/repos_groups/repos_groups.html:13
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
msgid "with"
msgstr ""
@@ -1491,10 +2100,10 @@ msgid "Group parent"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
-#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
-#: rhodecode/templates/admin/users/user_add.html:85
+#: rhodecode/templates/admin/users/user_add.html:94
#: rhodecode/templates/admin/users_groups/users_group_add.html:49
#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
msgid "save"
msgstr ""
@@ -1506,6 +2115,12 @@ msgstr ""
msgid "edit repos group"
msgstr ""
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other "
+"groups and repositories inside"
+msgstr ""
+
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
msgid "Repositories groups administration"
msgstr ""
@@ -1515,11 +2130,26 @@ msgid "ADD NEW GROUP"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
-msgid "Number of repositories"
+msgid "Number of toplevel repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
-msgid "Confirm to delete this group"
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#, python-format
+msgid "Confirm to delete this group: %s"
msgstr ""
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
@@ -1533,7 +2163,6 @@ msgstr ""
#: rhodecode/templates/admin/settings/hooks.html:9
#: rhodecode/templates/admin/settings/settings.html:9
-#: rhodecode/templates/settings/repo_settings.html:5
#: rhodecode/templates/settings/repo_settings.html:13
msgid "Settings"
msgstr ""
@@ -1573,115 +2202,185 @@ msgstr ""
msgid "destroy old data"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:45
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete if "
+"`destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
msgid "Rescan repositories"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:51
+#: rhodecode/templates/admin/settings/settings.html:52
msgid "Whoosh indexing"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:59
+#: rhodecode/templates/admin/settings/settings.html:60
msgid "index build option"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:64
+#: rhodecode/templates/admin/settings/settings.html:65
msgid "build from scratch"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:70
+#: rhodecode/templates/admin/settings/settings.html:71
msgid "Reindex"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:76
+#: rhodecode/templates/admin/settings/settings.html:77
msgid "Global application settings"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:85
+#: rhodecode/templates/admin/settings/settings.html:86
msgid "Application name"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:94
+#: rhodecode/templates/admin/settings/settings.html:95
msgid "Realm text"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:103
+#: rhodecode/templates/admin/settings/settings.html:104
msgid "GA code"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:111
-#: rhodecode/templates/admin/settings/settings.html:177
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
msgid "Save settings"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:112
-#: rhodecode/templates/admin/settings/settings.html:178
-#: rhodecode/templates/admin/users/user_edit.html:118
-#: rhodecode/templates/admin/users/user_edit.html:143
-#: rhodecode/templates/admin/users/user_edit_my_account.html:90
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:264
-#: rhodecode/templates/files/files_edit.html:50
-msgid "Reset"
+#: rhodecode/templates/admin/settings/settings.html:119
+msgid "Visualisation settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:128
+msgid "Icons"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+msgid "Show private repo icon on repositories"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:118
-msgid "Mercurial settings"
+#: rhodecode/templates/admin/settings/settings.html:144
+msgid "Meta-Tagging"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:127
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:176
+msgid "VCS settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:185
msgid "Web"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:132
-msgid "require ssl for pushing"
+#: rhodecode/templates/admin/settings/settings.html:190
+msgid "require ssl for vcs operations"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:139
-msgid "Hooks"
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it will "
+"return HTTP Error 406: Not Acceptable"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:142
-msgid "advanced setup"
+#: rhodecode/templates/admin/settings/settings.html:198
+msgid "Hooks"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:147
+#: rhodecode/templates/admin/settings/settings.html:203
msgid "Update repository after push (hg update)"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:151
+#: rhodecode/templates/admin/settings/settings.html:207
msgid "Show repository size after push"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:155
+#: rhodecode/templates/admin/settings/settings.html:211
msgid "Log user push commands"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:159
+#: rhodecode/templates/admin/settings/settings.html:215
msgid "Log user pull commands"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:166
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:224
+msgid "Mercurial Extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
msgid "Repositories location"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:171
+#: rhodecode/templates/admin/settings/settings.html:250
msgid ""
"This a crucial application setting. If you are really sure you need to change"
" this, you must restart application in order to make this setting take "
"effect. Click this label to unlock."
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:172
+#: rhodecode/templates/admin/settings/settings.html:251
msgid "unlock"
msgstr ""
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a restart, "
+"and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:280
+msgid "Email to"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:288
+msgid "Send"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:297
+msgid "show"
+msgstr ""
+
#: rhodecode/templates/admin/users/user_add.html:5
msgid "Add user"
msgstr ""
#: rhodecode/templates/admin/users/user_add.html:10
#: rhodecode/templates/admin/users/user_edit.html:11
-#: rhodecode/templates/admin/users/users.html:9
msgid "Users"
msgstr ""
@@ -1689,8 +2388,12 @@ msgstr ""
msgid "add new user"
msgstr ""
-#: rhodecode/templates/admin/users/user_add.html:77
-#: rhodecode/templates/admin/users/user_edit.html:101
+#: rhodecode/templates/admin/users/user_add.html:50
+msgid "Password confirmation"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
#: rhodecode/templates/admin/users_groups/users_group_add.html:41
#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
msgid "Active"
@@ -1700,36 +2403,93 @@ msgstr ""
msgid "Edit user"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:33
-#: rhodecode/templates/admin/users/user_edit_my_account.html:32
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
msgid "Change your avatar at"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:34
-#: rhodecode/templates/admin/users/user_edit_my_account.html:33
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
msgid "Using"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:40
-#: rhodecode/templates/admin/users/user_edit_my_account.html:39
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
msgid "API key"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:56
+#: rhodecode/templates/admin/users/user_edit.html:59
msgid "LDAP DN"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:65
-#: rhodecode/templates/admin/users/user_edit_my_account.html:54
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
msgid "New password"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:135
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:256
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+msgid "Inherit default permissions"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
msgid "Create repositories"
msgstr ""
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+msgid "Fork repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+msgid "Nothing here yet"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+msgid "Permission"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+msgid "Edit Permission"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+msgid "Email addresses"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, python-format
+msgid "Confirm to delete this email: %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+msgid "New email address"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+msgid "Add"
+msgstr ""
+
#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
msgid "My account"
msgstr ""
@@ -1737,26 +2497,73 @@ msgstr ""
msgid "My Account"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:101
-msgid "My repositories"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+msgid "My permissions"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:107
-msgid "ADD REPOSITORY"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+msgid "My repos"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:118
-#: rhodecode/templates/branches/branches_data.html:7
-#: rhodecode/templates/shortlog/shortlog_data.html:8
-#: rhodecode/templates/tags/tags_data.html:7
-msgid "revision"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+msgid "My pull requests"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+msgid "Add repo"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, python-format
+msgid "Pull request #%s opened on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+msgid "Confirm to delete this pull request"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40 rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
msgid "No repositories yet"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
msgid "create one now"
msgstr ""
@@ -1764,42 +2571,41 @@ msgstr ""
msgid "Users administration"
msgstr ""
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr ""
+
#: rhodecode/templates/admin/users/users.html:23
msgid "ADD NEW USER"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:33
+#: rhodecode/templates/admin/users/users.html:77
msgid "username"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:34
-#: rhodecode/templates/branches/branches_data.html:5
-#: rhodecode/templates/tags/tags_data.html:5
-msgid "name"
+#: rhodecode/templates/admin/users/users.html:80
+msgid "firstname"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:35
+#: rhodecode/templates/admin/users/users.html:81
msgid "lastname"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:36
+#: rhodecode/templates/admin/users/users.html:82
msgid "last login"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:37
+#: rhodecode/templates/admin/users/users.html:84
#: rhodecode/templates/admin/users_groups/users_groups.html:34
msgid "active"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:39
-#: rhodecode/templates/base/base.html:305
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
msgid "ldap"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:56
-msgid "Confirm to delete this user"
-msgstr ""
-
#: rhodecode/templates/admin/users_groups/users_group_add.html:5
msgid "Add users group"
msgstr ""
@@ -1841,6 +2647,10 @@ msgstr ""
msgid "Add all elements"
msgstr ""
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+msgid "Group members"
+msgstr ""
+
#: rhodecode/templates/admin/users_groups/users_groups.html:5
msgid "Users groups administration"
msgstr ""
@@ -1853,387 +2663,598 @@ msgstr ""
msgid "group name"
msgstr ""
-#: rhodecode/templates/base/base.html:32
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:100
msgid "Forgot password ?"
msgstr ""
-#: rhodecode/templates/base/base.html:57 rhodecode/templates/base/base.html:338
-#: rhodecode/templates/base/base.html:340 rhodecode/templates/base/base.html:342
+#: rhodecode/templates/base/base.html:107
+msgid "Log In"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:122 rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302 rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8 rhodecode/templates/tags/tags.html:11
msgid "Home"
msgstr ""
-#: rhodecode/templates/base/base.html:61 rhodecode/templates/base/base.html:347
-#: rhodecode/templates/base/base.html:349 rhodecode/templates/base/base.html:351
+#: rhodecode/templates/base/base.html:123 rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311 rhodecode/templates/base/base.html:313
#: rhodecode/templates/journal/journal.html:4
-#: rhodecode/templates/journal/journal.html:17
+#: rhodecode/templates/journal/journal.html:21
#: rhodecode/templates/journal/public_journal.html:4
msgid "Journal"
msgstr ""
-#: rhodecode/templates/base/base.html:66
-msgid "Login"
-msgstr ""
-
-#: rhodecode/templates/base/base.html:68
+#: rhodecode/templates/base/base.html:125
msgid "Log Out"
msgstr ""
-#: rhodecode/templates/base/base.html:107
-msgid "Submit a bug"
-msgstr ""
-
-#: rhodecode/templates/base/base.html:141
+#: rhodecode/templates/base/base.html:144
msgid "Switch repository"
msgstr ""
-#: rhodecode/templates/base/base.html:143
+#: rhodecode/templates/base/base.html:146
msgid "Products"
msgstr ""
-#: rhodecode/templates/base/base.html:149
+#: rhodecode/templates/base/base.html:152 rhodecode/templates/base/base.html:182
msgid "loading..."
msgstr ""
-#: rhodecode/templates/base/base.html:234 rhodecode/templates/base/base.html:236
-#: rhodecode/templates/base/base.html:238
-msgid "Switch to"
-msgstr ""
-
-#: rhodecode/templates/base/base.html:242
-#: rhodecode/templates/branches/branches.html:13
-msgid "branches"
+#: rhodecode/templates/base/base.html:158 rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
msgstr ""
-#: rhodecode/templates/base/base.html:249
-#: rhodecode/templates/branches/branches_data.html:52
-msgid "There are no branches yet"
+#: rhodecode/templates/base/base.html:166 rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
msgstr ""
-#: rhodecode/templates/base/base.html:254
-#: rhodecode/templates/shortlog/shortlog_data.html:10
-#: rhodecode/templates/tags/tags.html:14
-msgid "tags"
+#: rhodecode/templates/base/base.html:175 rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
msgstr ""
-#: rhodecode/templates/base/base.html:261
-#: rhodecode/templates/tags/tags_data.html:32
-msgid "There are no tags yet"
+#: rhodecode/templates/base/base.html:186 rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
msgstr ""
-#: rhodecode/templates/base/base.html:277 rhodecode/templates/base/base.html:281
-#: rhodecode/templates/files/files_annotate.html:40
-#: rhodecode/templates/files/files_source.html:11
+#: rhodecode/templates/base/base.html:195 rhodecode/templates/base/base.html:199
msgid "Options"
msgstr ""
-#: rhodecode/templates/base/base.html:286 rhodecode/templates/base/base.html:288
-#: rhodecode/templates/base/base.html:306
+#: rhodecode/templates/base/base.html:204 rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
msgid "settings"
msgstr ""
-#: rhodecode/templates/base/base.html:292
-msgid "search"
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
msgstr ""
-#: rhodecode/templates/base/base.html:299
-msgid "journal"
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
msgstr ""
-#: rhodecode/templates/base/base.html:301
-msgid "repositories groups"
+#: rhodecode/templates/base/base.html:213
+msgid "search"
msgstr ""
-#: rhodecode/templates/base/base.html:302
-msgid "users"
+#: rhodecode/templates/base/base.html:222
+msgid "repositories groups"
msgstr ""
-#: rhodecode/templates/base/base.html:303
+#: rhodecode/templates/base/base.html:224
msgid "users groups"
msgstr ""
-#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/base/base.html:225
msgid "permissions"
msgstr ""
-#: rhodecode/templates/base/base.html:317 rhodecode/templates/base/base.html:319
-#: rhodecode/templates/followers/followers.html:5
+#: rhodecode/templates/base/base.html:238 rhodecode/templates/base/base.html:240
msgid "Followers"
msgstr ""
-#: rhodecode/templates/base/base.html:325 rhodecode/templates/base/base.html:327
-#: rhodecode/templates/forks/forks.html:5
+#: rhodecode/templates/base/base.html:246 rhodecode/templates/base/base.html:248
msgid "Forks"
msgstr ""
-#: rhodecode/templates/base/base.html:356 rhodecode/templates/base/base.html:358
-#: rhodecode/templates/base/base.html:360 rhodecode/templates/search/search.html:4
-#: rhodecode/templates/search/search.html:24
-#: rhodecode/templates/search/search.html:46
+#: rhodecode/templates/base/base.html:327 rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331 rhodecode/templates/search/search.html:52
msgid "Search"
msgstr ""
-#: rhodecode/templates/base/root.html:57
-#: rhodecode/templates/journal/journal.html:48
-#: rhodecode/templates/summary/summary.html:36
+#: rhodecode/templates/base/root.html:42
+msgid "add another comment"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
msgid "Stop following this repository"
msgstr ""
-#: rhodecode/templates/base/root.html:66
-#: rhodecode/templates/summary/summary.html:40
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
msgid "Start following this repository"
msgstr ""
-#: rhodecode/templates/branches/branches_data.html:4
-#: rhodecode/templates/tags/tags_data.html:4
-msgid "date"
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr ""
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr ""
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39 rhodecode/templates/tags/tags_data.html:8
+msgid "Author"
+msgstr ""
+
+#: rhodecode/templates/branches/branches.html:5
+#, python-format
+msgid "%s Branches"
+msgstr ""
+
+#: rhodecode/templates/branches/branches.html:29
+msgid "Compare branches"
+msgstr ""
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+msgid "Compare"
msgstr ""
#: rhodecode/templates/branches/branches_data.html:6
-#: rhodecode/templates/shortlog/shortlog_data.html:7
-#: rhodecode/templates/tags/tags_data.html:6
-msgid "author"
+msgid "name"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:7
+msgid "date"
msgstr ""
#: rhodecode/templates/branches/branches_data.html:8
-#: rhodecode/templates/shortlog/shortlog_data.html:11
-#: rhodecode/templates/tags/tags_data.html:8
-msgid "links"
+#: rhodecode/templates/shortlog/shortlog_data.html:8
+msgid "author"
msgstr ""
-#: rhodecode/templates/branches/branches_data.html:23
-#: rhodecode/templates/branches/branches_data.html:43
-#: rhodecode/templates/shortlog/shortlog_data.html:39
-#: rhodecode/templates/tags/tags_data.html:24
-msgid "changeset"
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
msgstr ""
-#: rhodecode/templates/branches/branches_data.html:25
-#: rhodecode/templates/branches/branches_data.html:45
-#: rhodecode/templates/files/files.html:12
-#: rhodecode/templates/shortlog/shortlog_data.html:41
-#: rhodecode/templates/summary/summary.html:233
-#: rhodecode/templates/tags/tags_data.html:26
-msgid "files"
+#: rhodecode/templates/branches/branches_data.html:10
+msgid "compare"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "showing "
+#: rhodecode/templates/changelog/changelog.html:6
+#, python-format
+msgid "%s Changelog"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "out of"
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
msgstr ""
#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+msgid "Compare fork"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:46
msgid "Show"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:50
-#: rhodecode/templates/changeset/changeset.html:42
-#: rhodecode/templates/summary/summary.html:609
-msgid "commit"
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:63
+#: rhodecode/templates/changelog/changelog.html:76
msgid "Affected number of files, click to show more details"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:67
-#: rhodecode/templates/changeset/changeset.html:66
-msgid "merge"
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+msgid "Changeset status"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:72
-#: rhodecode/templates/changeset/changeset.html:72
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
msgid "Parent"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:77
-#: rhodecode/templates/changeset/changeset.html:77
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
msgid "No parents"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:82
-#: rhodecode/templates/changeset/changeset.html:80
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
#: rhodecode/templates/files/files.html:29
-#: rhodecode/templates/files/files_annotate.html:25
+#: rhodecode/templates/files/files_add.html:33
#: rhodecode/templates/files/files_edit.html:33
#: rhodecode/templates/shortlog/shortlog_data.html:9
msgid "branch"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:86
-#: rhodecode/templates/changeset/changeset.html:83
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
msgid "tag"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:122
+#: rhodecode/templates/changelog/changelog.html:164
msgid "Show selected changes __S -> __E"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:172
-#: rhodecode/templates/shortlog/shortlog_data.html:61
+#: rhodecode/templates/changelog/changelog.html:255
msgid "There are no changes yet"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:2
-#: rhodecode/templates/changeset/changeset.html:55
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
msgid "removed"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:3
-#: rhodecode/templates/changeset/changeset.html:56
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
msgid "changed"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:4
-#: rhodecode/templates/changeset/changeset.html:57
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
msgid "added"
msgstr ""
-#: rhodecode/templates/changelog/changelog_details.html:6
-#: rhodecode/templates/changelog/changelog_details.html:7
#: rhodecode/templates/changelog/changelog_details.html:8
-#: rhodecode/templates/changeset/changeset.html:59
-#: rhodecode/templates/changeset/changeset.html:60
-#: rhodecode/templates/changeset/changeset.html:61
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
#, python-format
msgid "affected %s files"
msgstr ""
#: rhodecode/templates/changeset/changeset.html:6
+#, python-format
+msgid "%s Changeset"
+msgstr ""
+
#: rhodecode/templates/changeset/changeset.html:14
-#: rhodecode/templates/changeset/changeset.html:31
msgid "Changeset"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:32
-#: rhodecode/templates/changeset/changeset.html:121
-#: rhodecode/templates/changeset/changeset_range.html:78
-#: rhodecode/templates/files/file_diff.html:32
-#: rhodecode/templates/files/file_diff.html:42
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
msgid "raw diff"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:34
-#: rhodecode/templates/changeset/changeset.html:123
-#: rhodecode/templates/changeset/changeset_range.html:80
-#: rhodecode/templates/files/file_diff.html:34
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
msgid "download diff"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:90
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
#, python-format
-msgid "%s files affected with %s additions and %s deletions."
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/changeset/changeset.html:103
+#, python-format
+msgid "%s files affected with %s insertions and %s deletions:"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:101
+#: rhodecode/templates/changeset/changeset.html:119
msgid "Changeset was too big and was cut off..."
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:119
-#: rhodecode/templates/changeset/changeset_range.html:76
-#: rhodecode/templates/files/file_diff.html:30
-msgid "diff"
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+msgid "Comment"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "You need to be logged in to comment."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "change status"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:132
-#: rhodecode/templates/changeset/changeset_range.html:89
-msgid "No changes in this file"
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
msgstr ""
-#: rhodecode/templates/changeset/changeset_range.html:30
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, python-format
+msgid "%s Changesets"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
msgid "Compare View"
msgstr ""
-#: rhodecode/templates/changeset/changeset_range.html:52
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
msgid "Files affected"
msgstr ""
-#: rhodecode/templates/errors/error_document.html:44
-#, python-format
-msgid "You will be redirected to %s in %s seconds"
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
msgstr ""
-#: rhodecode/templates/files/file_diff.html:4
-#: rhodecode/templates/files/file_diff.html:12
-msgid "File diff"
+#: rhodecode/templates/changeset/diff_block.html:27
+msgid "show inline comments"
msgstr ""
-#: rhodecode/templates/files/file_diff.html:42
-msgid "Diff is to big to display"
+#: rhodecode/templates/compare/compare_cs.html:5
+msgid "No changesets"
msgstr ""
-#: rhodecode/templates/files/files.html:37
-#: rhodecode/templates/files/files_annotate.html:31
-#: rhodecode/templates/files/files_edit.html:39
-msgid "Location"
+#: rhodecode/templates/compare/compare_diff.html:37
+msgid "Outgoing changesets"
msgstr ""
-#: rhodecode/templates/files/files.html:46
-msgid "Go back"
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
msgstr ""
-#: rhodecode/templates/files/files.html:47
-msgid "No files at given path"
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:4
-msgid "File annotate"
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:12
-msgid "annotate"
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:33
-#: rhodecode/templates/files/files_browser.html:160
-#: rhodecode/templates/files/files_source.html:2
-msgid "Revision"
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:36
-#: rhodecode/templates/files/files_browser.html:158
-#: rhodecode/templates/files/files_source.html:7
-msgid "Size"
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:38
-#: rhodecode/templates/files/files_browser.html:159
-#: rhodecode/templates/files/files_source.html:9
-msgid "Mimetype"
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, python-format
+msgid "Confirm to delete this user: %s"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:41
-msgid "show source"
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:43
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:14
-#: rhodecode/templates/files/files_source.html:51
-msgid "show as raw"
+#: rhodecode/templates/errors/error_document.html:46
+#, python-format
+msgid "You will be redirected to %s in %s seconds"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:45
-#: rhodecode/templates/files/files_source.html:16
-msgid "download as raw"
+#: rhodecode/templates/files/file_diff.html:4
+#, python-format
+msgid "%s File diff"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:54
-#: rhodecode/templates/files/files_source.html:25
-msgid "History"
+#: rhodecode/templates/files/file_diff.html:12
+msgid "File diff"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:73
-#: rhodecode/templates/files/files_source.html:46
+#: rhodecode/templates/files/files.html:4 rhodecode/templates/files/files.html:72
#, python-format
-msgid "Binary file (%s)"
+msgid "%s files"
msgstr ""
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:51
-msgid "File is too big to display"
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, python-format
+msgid "%s Edit file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:19
+msgid "add file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:40
+msgid "Add new file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:45
+msgid "File Name"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+msgid "or"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+msgid "Upload file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:58
+msgid "Create new file"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
msgstr ""
#: rhodecode/templates/files/files_browser.html:13
@@ -2256,57 +3277,160 @@ msgstr ""
msgid "search file list"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:32
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+msgid "add new file"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:35
msgid "Loading file list..."
msgstr ""
-#: rhodecode/templates/files/files_browser.html:111
-msgid "search truncated"
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:122
-msgid "no matching files"
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:161
-msgid "Last modified"
+#: rhodecode/templates/files/files_browser.html:50
+msgid "Last Revision"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:162
-msgid "Last commiter"
+#: rhodecode/templates/files/files_browser.html:51
+msgid "Last modified"
msgstr ""
-#: rhodecode/templates/files/files_edit.html:4
-msgid "Edit file"
+#: rhodecode/templates/files/files_browser.html:52
+msgid "Last commiter"
msgstr ""
#: rhodecode/templates/files/files_edit.html:19
msgid "edit file"
msgstr ""
-#: rhodecode/templates/files/files_edit.html:45
-#: rhodecode/templates/shortlog/shortlog_data.html:5
-msgid "commit message"
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr ""
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
msgstr ""
#: rhodecode/templates/files/files_edit.html:51
-msgid "Commit changes"
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
msgstr ""
-#: rhodecode/templates/files/files_source.html:12
-msgid "show annotation"
+#: rhodecode/templates/files/files_edit.html:54
+msgid "source"
+msgstr ""
+
+#: rhodecode/templates/files/files_edit.html:59
+msgid "Editing file"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:9
+msgid "diff to revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:10
+msgid "show at revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:14
+#, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
msgstr ""
-#: rhodecode/templates/files/files_source.html:153
+#: rhodecode/templates/files/files_source.html:124
msgid "Selection link"
msgstr ""
+#: rhodecode/templates/files/files_ypjax.html:5
+msgid "annotation"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr ""
+
+#: rhodecode/templates/followers/followers.html:5
+#, python-format
+msgid "%s Followers"
+msgstr ""
+
#: rhodecode/templates/followers/followers.html:13
msgid "followers"
msgstr ""
#: rhodecode/templates/followers/followers_data.html:12
-msgid "Started following"
+msgid "Started following -"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:5
+#, python-format
+msgid "%s Fork"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:77
+msgid "Copy permissions"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr ""
+
+#: rhodecode/templates/forks/forks.html:5
+#, python-format
+msgid "%s Forks"
msgstr ""
#: rhodecode/templates/forks/forks.html:13
@@ -2317,184 +3441,395 @@ msgstr ""
msgid "forked"
msgstr ""
-#: rhodecode/templates/forks/forks_data.html:34
+#: rhodecode/templates/forks/forks_data.html:38
msgid "There are no forks yet"
msgstr ""
-#: rhodecode/templates/journal/journal.html:34
-msgid "Following"
+#: rhodecode/templates/journal/journal.html:13
+msgid "ATOM journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:14
+msgid "RSS journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+msgid "RSS feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
msgstr ""
#: rhodecode/templates/journal/journal.html:41
+msgid "Watched"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:46
+msgid "ADD"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:114
msgid "following user"
msgstr ""
-#: rhodecode/templates/journal/journal.html:41
+#: rhodecode/templates/journal/journal.html:114
msgid "user"
msgstr ""
-#: rhodecode/templates/journal/journal.html:65
+#: rhodecode/templates/journal/journal.html:147
msgid "You are not following any users or repositories"
msgstr ""
-#: rhodecode/templates/journal/journal_data.html:46
+#: rhodecode/templates/journal/journal_data.html:47
msgid "No entries yet"
msgstr ""
-#: rhodecode/templates/journal/public_journal.html:17
+#: rhodecode/templates/journal/public_journal.html:13
+msgid "ATOM public journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/public_journal.html:14
+msgid "RSS public journal feed"
+msgstr ""
+
+#: rhodecode/templates/journal/public_journal.html:21
msgid "Public Journal"
msgstr ""
-#: rhodecode/templates/search/search.html:7
-#: rhodecode/templates/search/search.html:26
-msgid "in repository: "
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+msgid "Detailed compare view"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+msgid "owner"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+msgid "Add reviewer to this pull request."
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+msgid "Create new pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+msgid "Title"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+msgid "description"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+msgid "Status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+msgid "Still not reviewed by"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+msgstr[1] ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+msgid "Created on"
msgstr ""
-#: rhodecode/templates/search/search.html:9
-#: rhodecode/templates/search/search.html:28
-msgid "in all repositories"
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+msgid "Compare view"
msgstr ""
-#: rhodecode/templates/search/search.html:42
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+msgid "Incoming changesets"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+msgid "all pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:8
+#, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, python-format
+msgid "Search in repository: %s"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+msgid "Search in all repositories"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:48
msgid "Search term"
msgstr ""
-#: rhodecode/templates/search/search.html:54
+#: rhodecode/templates/search/search.html:60
msgid "Search in"
msgstr ""
-#: rhodecode/templates/search/search.html:57
+#: rhodecode/templates/search/search.html:63
msgid "File contents"
msgstr ""
-#: rhodecode/templates/search/search.html:59
+#: rhodecode/templates/search/search.html:64
+msgid "Commit messages"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:65
msgid "File names"
msgstr ""
-#: rhodecode/templates/search/search_content.html:20
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
#: rhodecode/templates/search/search_path.html:15
msgid "Permission denied"
msgstr ""
-#: rhodecode/templates/settings/repo_fork.html:5
-msgid "Fork"
-msgstr ""
-
-#: rhodecode/templates/settings/repo_fork.html:31
-msgid "Fork name"
-msgstr ""
-
-#: rhodecode/templates/settings/repo_fork.html:55
-msgid "fork this repository"
+#: rhodecode/templates/settings/repo_settings.html:5
+#, python-format
+msgid "%s Settings"
msgstr ""
#: rhodecode/templates/shortlog/shortlog.html:5
-#: rhodecode/templates/summary/summary.html:666
-msgid "Shortlog"
+#, python-format
+msgid "%s Shortlog"
msgstr ""
#: rhodecode/templates/shortlog/shortlog.html:14
msgid "shortlog"
msgstr ""
-#: rhodecode/templates/shortlog/shortlog_data.html:6
+#: rhodecode/templates/shortlog/shortlog_data.html:7
msgid "age"
msgstr ""
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+msgid "No commit message"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+msgid "Existing repository?"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:4
+#, python-format
+msgid "%s Summary"
+msgstr ""
+
#: rhodecode/templates/summary/summary.html:12
msgid "summary"
msgstr ""
-#: rhodecode/templates/summary/summary.html:79
+#: rhodecode/templates/summary/summary.html:20
+#, python-format
+msgid "repo %s ATOM feed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:21
+#, python-format
+msgid "repo %s RSS feed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+msgid "ATOM"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:82
+#, python-format
+msgid "Non changable ID %s"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:95
msgid "remote clone"
msgstr ""
-#: rhodecode/templates/summary/summary.html:121
-msgid "by"
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
msgstr ""
-#: rhodecode/templates/summary/summary.html:128
+#: rhodecode/templates/summary/summary.html:130
msgid "Clone url"
msgstr ""
-#: rhodecode/templates/summary/summary.html:137
-msgid "Trending source files"
+#: rhodecode/templates/summary/summary.html:133
+msgid "Show by Name"
msgstr ""
-#: rhodecode/templates/summary/summary.html:146
-msgid "Download"
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
msgstr ""
-#: rhodecode/templates/summary/summary.html:150
-msgid "There are no downloads yet"
+#: rhodecode/templates/summary/summary.html:142
+msgid "Trending files"
msgstr ""
-#: rhodecode/templates/summary/summary.html:152
-msgid "Downloads are disabled for this repository"
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
msgstr ""
-#: rhodecode/templates/summary/summary.html:154
-#: rhodecode/templates/summary/summary.html:320
-msgid "enable"
+#: rhodecode/templates/summary/summary.html:158
+msgid "Download"
msgstr ""
#: rhodecode/templates/summary/summary.html:162
-#: rhodecode/templates/summary/summary.html:297
-#, python-format
-msgid "Download %s as %s"
+msgid "There are no downloads yet"
msgstr ""
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:164
+msgid "Downloads are disabled for this repository"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:170
+msgid "Download as zip"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:173
msgid "Check this to download archive with subrepos"
msgstr ""
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:173
msgid "with subrepos"
msgstr ""
-#: rhodecode/templates/summary/summary.html:176
-msgid "Feeds"
+#: rhodecode/templates/summary/summary.html:186
+msgid "Commit activity by day / author"
msgstr ""
-#: rhodecode/templates/summary/summary.html:257
-#: rhodecode/templates/summary/summary.html:684
-#: rhodecode/templates/summary/summary.html:695
-msgid "show more"
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
msgstr ""
-#: rhodecode/templates/summary/summary.html:312
-msgid "Commit activity by day / author"
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
msgstr ""
-#: rhodecode/templates/summary/summary.html:324
-msgid "Loaded in"
+#: rhodecode/templates/summary/summary.html:220
+msgid "Quick start"
msgstr ""
-#: rhodecode/templates/summary/summary.html:603
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:650
msgid "commits"
msgstr ""
-#: rhodecode/templates/summary/summary.html:604
+#: rhodecode/templates/summary/summary.html:651
msgid "files added"
msgstr ""
-#: rhodecode/templates/summary/summary.html:605
+#: rhodecode/templates/summary/summary.html:652
msgid "files changed"
msgstr ""
-#: rhodecode/templates/summary/summary.html:606
+#: rhodecode/templates/summary/summary.html:653
msgid "files removed"
msgstr ""
-#: rhodecode/templates/summary/summary.html:610
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:657
msgid "file added"
msgstr ""
-#: rhodecode/templates/summary/summary.html:611
+#: rhodecode/templates/summary/summary.html:658
msgid "file changed"
msgstr ""
-#: rhodecode/templates/summary/summary.html:612
+#: rhodecode/templates/summary/summary.html:659
msgid "file removed"
msgstr ""
+#: rhodecode/templates/tags/tags.html:5
+#, python-format
+msgid "%s Tags"
+msgstr ""
+
diff --git a/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.mo b/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.mo
new file mode 100644
index 00000000..341b4127
--- /dev/null
+++ b/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.mo
Binary files differ
diff --git a/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po b/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po
new file mode 100644
index 00000000..b569b73e
--- /dev/null
+++ b/rhodecode/i18n/zh_CN/LC_MESSAGES/rhodecode.po
@@ -0,0 +1,4022 @@
+# Chinese (China) translations for RhodeCode.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the RhodeCode project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+# mikespook <mikespook@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: RhodeCode 1.2.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\n"
+"PO-Revision-Date: 2012-04-05 17:37+0800\n"
+"Last-Translator: mikespook <mikespook@gmail.com>\n"
+"Language-Team: mikespook\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: rhodecode/controllers/changelog.py:94
+#, fuzzy
+msgid "All Branches"
+msgstr "分支"
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:157
+#, fuzzy, python-format
+msgid "%s line context"
+msgstr "文件内容"
+
+#: rhodecode/controllers/changeset.py:333
+#: rhodecode/controllers/changeset.py:348 rhodecode/lib/diffs.py:70
+msgid "binary file"
+msgstr "二进制文件"
+
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is "
+"not allowed"
+msgstr ""
+
+#: rhodecode/controllers/compare.py:69
+#, fuzzy
+msgid "There are no changesets yet"
+msgstr "没有任何变更"
+
+#: rhodecode/controllers/error.py:69
+msgid "Home page"
+msgstr "主页"
+
+#: rhodecode/controllers/error.py:98
+msgid "The request could not be understood by the server due to malformed syntax."
+msgstr "由于错误的语法,服务器无法对请求进行响应。"
+
+#: rhodecode/controllers/error.py:101
+msgid "Unauthorized access to resource"
+msgstr "未授权的资源访问"
+
+#: rhodecode/controllers/error.py:103
+msgid "You don't have permission to view this page"
+msgstr "无权访问该页面"
+
+#: rhodecode/controllers/error.py:105
+msgid "The resource could not be found"
+msgstr "资源未找到"
+
+#: rhodecode/controllers/error.py:107
+msgid ""
+"The server encountered an unexpected condition which prevented it from "
+"fulfilling the request."
+msgstr "服务进入非预期的混乱状态,这会阻止它对请求进行响应。"
+
+#: rhodecode/controllers/feed.py:49
+#, python-format
+msgid "Changes on %s repository"
+msgstr "%s 库的修改"
+
+#: rhodecode/controllers/feed.py:50
+#, python-format
+msgid "%s %s feed"
+msgstr "%s %s 订阅"
+
+#: rhodecode/controllers/feed.py:75
+#, fuzzy
+msgid "commited on"
+msgstr "提交"
+
+#: rhodecode/controllers/files.py:84
+#, fuzzy
+msgid "click here to add new file"
+msgstr "添加新用户"
+
+#: rhodecode/controllers/files.py:85
+#, fuzzy, python-format
+msgid "There are no files yet %s"
+msgstr "尚无文件"
+
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:266
+#, python-format
+msgid "Edited %s via RhodeCode"
+msgstr "通过 RhodeCode 修改了 %s"
+
+#: rhodecode/controllers/files.py:271
+msgid "No changes"
+msgstr "无变更"
+
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
+#, python-format
+msgid "Successfully committed to %s"
+msgstr "成功提交到 %s"
+
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
+msgid "Error occurred during commit"
+msgstr "提交时发生错误"
+
+#: rhodecode/controllers/files.py:318
+#, fuzzy, python-format
+msgid "Added %s via RhodeCode"
+msgstr "通过 RhodeCode 修改了 %s"
+
+#: rhodecode/controllers/files.py:332
+#, fuzzy
+msgid "No content"
+msgstr "文件内容"
+
+#: rhodecode/controllers/files.py:336
+#, fuzzy
+msgid "No filename"
+msgstr "文件名"
+
+#: rhodecode/controllers/files.py:378
+msgid "downloads disabled"
+msgstr "禁止下载"
+
+#: rhodecode/controllers/files.py:389
+#, python-format
+msgid "Unknown revision %s"
+msgstr "未知版本 %s"
+
+#: rhodecode/controllers/files.py:391
+msgid "Empty repository"
+msgstr "空版本库"
+
+#: rhodecode/controllers/files.py:393
+msgid "Unknown archive type"
+msgstr "未知包类型"
+
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
+msgid "Changesets"
+msgstr "变更集"
+
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
+msgid "Branches"
+msgstr "分支"
+
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
+msgid "Tags"
+msgstr "标签"
+
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the file system please run the application again in order to rescan "
+"repositories"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr "版本库 %s 被分支到 %s"
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr ""
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+#, fuzzy
+msgid "public journal"
+msgstr "公共日志"
+
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr "日志"
+
+#: rhodecode/controllers/login.py:143
+msgid "You have successfully registered into rhodecode"
+msgstr "成功注册到 rhodecode"
+
+#: rhodecode/controllers/login.py:164
+msgid "Your password reset link was sent"
+msgstr "密码重置链接已经发送"
+
+#: rhodecode/controllers/login.py:184
+msgid ""
+"Your password reset was successful, new password has been sent to your "
+"email"
+msgstr "密码已经成功重置,新密码已经发送到你的邮箱"
+
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+msgid "Bookmarks"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+#, fuzzy
+msgid "error during creation of pull request"
+msgstr "提交时发生错误"
+
+#: rhodecode/controllers/pullrequests.py:181
+#, fuzzy
+msgid "Successfully opened new pull request"
+msgstr "用户删除成功"
+
+#: rhodecode/controllers/pullrequests.py:184
+#, fuzzy
+msgid "Error occurred during sending pull request"
+msgstr "提交时发生错误"
+
+#: rhodecode/controllers/pullrequests.py:217
+#, fuzzy
+msgid "Successfully deleted pull request"
+msgstr "用户删除成功"
+
+#: rhodecode/controllers/search.py:131
+msgid "Invalid search query. Try quoting it."
+msgstr "错误的搜索。请尝试用引号包含它。"
+
+#: rhodecode/controllers/search.py:136
+msgid "There is no index to search in. Please run whoosh indexer"
+msgstr "没有索引用于搜索。请运行 whoosh 索引器"
+
+#: rhodecode/controllers/search.py:140
+msgid "An error occurred during this search operation"
+msgstr "在搜索操作中发生异常"
+
+#: rhodecode/controllers/settings.py:107
+#: rhodecode/controllers/admin/repos.py:266
+#, python-format
+msgid "Repository %s updated successfully"
+msgstr "版本库 %s 成功更新"
+
+#: rhodecode/controllers/settings.py:125
+#: rhodecode/controllers/admin/repos.py:284
+#, python-format
+msgid "error occurred during update of repository %s"
+msgstr ""
+
+#: rhodecode/controllers/settings.py:143
+#: rhodecode/controllers/admin/repos.py:302
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was moved or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+
+#: rhodecode/controllers/settings.py:155
+#: rhodecode/controllers/admin/repos.py:314
+#, python-format
+msgid "deleted repository %s"
+msgstr "已经删除版本库 %s"
+
+#: rhodecode/controllers/settings.py:159
+#: rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
+#, python-format
+msgid "An error occurred during deletion of %s"
+msgstr ""
+
+#: rhodecode/controllers/summary.py:138
+msgid "No data loaded yet"
+msgstr ""
+
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
+msgid "Statistics are disabled for this repository"
+msgstr "该版本库统计功能已经禁用"
+
+#: rhodecode/controllers/admin/ldap_settings.py:50
+msgid "BASE"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:51
+msgid "ONELEVEL"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:52
+msgid "SUBTREE"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:56
+msgid "NEVER"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:57
+msgid "ALLOW"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:58
+msgid "TRY"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:59
+msgid "DEMAND"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:60
+msgid "HARD"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:64
+msgid "No encryption"
+msgstr "未加密"
+
+#: rhodecode/controllers/admin/ldap_settings.py:65
+msgid "LDAPS connection"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:66
+msgid "START_TLS on LDAP connection"
+msgstr ""
+
+#: rhodecode/controllers/admin/ldap_settings.py:126
+msgid "Ldap settings updated successfully"
+msgstr "LDAP 设置已经成功更新"
+
+#: rhodecode/controllers/admin/ldap_settings.py:130
+msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
+msgstr "无法启用 LDAP。库“python-ldap”缺失。"
+
+#: rhodecode/controllers/admin/ldap_settings.py:147
+msgid "error occurred during update of ldap settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/permissions.py:59
+msgid "None"
+msgstr "无"
+
+#: rhodecode/controllers/admin/permissions.py:60
+msgid "Read"
+msgstr "读"
+
+#: rhodecode/controllers/admin/permissions.py:61
+msgid "Write"
+msgstr "写"
+
+#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/templates/admin/ldap/ldap.html:9
+#: rhodecode/templates/admin/permissions/permissions.html:9
+#: rhodecode/templates/admin/repos/repo_add.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:9
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
+#: rhodecode/templates/admin/settings/hooks.html:9
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/admin/users/user_add.html:8
+#: rhodecode/templates/admin/users/user_edit.html:9
+#: rhodecode/templates/admin/users/user_edit.html:122
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/admin/users_groups/users_group_add.html:8
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+#: rhodecode/templates/base/base.html:197
+#: rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339
+#: rhodecode/templates/base/base.html:341
+msgid "Admin"
+msgstr "管理"
+
+#: rhodecode/controllers/admin/permissions.py:65
+msgid "disabled"
+msgstr "禁用"
+
+#: rhodecode/controllers/admin/permissions.py:67
+msgid "allowed with manual account activation"
+msgstr "允许手工启用帐号"
+
+#: rhodecode/controllers/admin/permissions.py:69
+msgid "allowed with automatic account activation"
+msgstr "允许自动启用帐号"
+
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
+msgid "Disabled"
+msgstr "停用"
+
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
+msgid "Enabled"
+msgstr "启用"
+
+#: rhodecode/controllers/admin/permissions.py:116
+msgid "Default permissions updated successfully"
+msgstr "成功更新默认权限"
+
+#: rhodecode/controllers/admin/permissions.py:130
+msgid "error occurred during update of permissions"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:192
+#, python-format
+msgid "created repository %s from %s"
+msgstr "新版本库 %s 基于 %s 建立。"
+
+#: rhodecode/controllers/admin/repos.py:196
+#, python-format
+msgid "created repository %s"
+msgstr "建立版本库 %s"
+
+#: rhodecode/controllers/admin/repos.py:227
+#, python-format
+msgid "error occurred during creation of repository %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:319
+#, python-format
+msgid "Cannot delete %s it still contains attached forks"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:348
+msgid "An error occurred during deletion of repository user"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:367
+msgid "An error occurred during deletion of repository users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:385
+msgid "An error occurred during deletion of repository stats"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:402
+msgid "An error occurred during cache invalidation"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:422
+#, fuzzy
+msgid "An error occurred during unlocking"
+msgstr "在搜索操作中发生异常"
+
+#: rhodecode/controllers/admin/repos.py:442
+msgid "Updated repository visibility in public journal"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:446
+msgid "An error occurred during setting this repository in public journal"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
+msgid "Token mismatch"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:464
+msgid "Pulled from remote location"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:466
+msgid "An error occurred during pull from remote location"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:484
+#, fuzzy, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr "新版本库 %s 基于 %s 建立。"
+
+#: rhodecode/controllers/admin/repos.py:488
+#, fuzzy
+msgid "An error occurred during this operation"
+msgstr "在搜索操作中发生异常"
+
+#: rhodecode/controllers/admin/repos_groups.py:116
+#, python-format
+msgid "created repos group %s"
+msgstr "建立版本库组 %s"
+
+#: rhodecode/controllers/admin/repos_groups.py:129
+#, python-format
+msgid "error occurred during creation of repos group %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:163
+#, python-format
+msgid "updated repos group %s"
+msgstr "更新版本库组 %s"
+
+#: rhodecode/controllers/admin/repos_groups.py:176
+#, python-format
+msgid "error occurred during update of repos group %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:194
+#, python-format
+msgid "This group contains %s repositores and cannot be deleted"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:202
+#, python-format
+msgid "removed repos group %s"
+msgstr "移除版本库组 %s"
+
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
+#, python-format
+msgid "error occurred during deletion of repos group %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:238
+#, fuzzy
+msgid "An error occurred during deletion of group user"
+msgstr "在搜索操作中发生异常"
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:121
+#, python-format
+msgid "Repositories successfully rescanned added: %s,removed: %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:129
+msgid "Whoosh reindex task scheduled"
+msgstr "Whoosh 重新索引任务调度"
+
+#: rhodecode/controllers/admin/settings.py:160
+msgid "Updated application settings"
+msgstr "更新应用设置"
+
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
+msgid "error occurred during updating application settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:200
+#, fuzzy
+msgid "Updated visualisation settings"
+msgstr "更新应用设置"
+
+#: rhodecode/controllers/admin/settings.py:205
+#, fuzzy
+msgid "error occurred during updating visualisation settings"
+msgstr "提交时发生错误"
+
+#: rhodecode/controllers/admin/settings.py:271
+#, fuzzy
+msgid "Updated VCS settings"
+msgstr "更新 mercurial 设置"
+
+#: rhodecode/controllers/admin/settings.py:285
+msgid "Added new hook"
+msgstr "新增钩子"
+
+#: rhodecode/controllers/admin/settings.py:297
+msgid "Updated hooks"
+msgstr "更新钩子"
+
+#: rhodecode/controllers/admin/settings.py:301
+msgid "error occurred during hook creation"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:375
+msgid "You can't edit this user since it's crucial for entire application"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:406
+msgid "Your account was updated successfully"
+msgstr "你的帐号已经更新完成"
+
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
+#, python-format
+msgid "error occurred during update of user %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:130
+#, python-format
+msgid "created user %s"
+msgstr "创建用户 %s"
+
+#: rhodecode/controllers/admin/users.py:142
+#, python-format
+msgid "error occurred during creation of user %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:171
+msgid "User updated successfully"
+msgstr "用户更新成功"
+
+#: rhodecode/controllers/admin/users.py:207
+msgid "successfully deleted user"
+msgstr "用户删除成功"
+
+#: rhodecode/controllers/admin/users.py:212
+msgid "An error occurred during deletion of user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:226
+msgid "You can't edit this user"
+msgstr "无法编辑该用户"
+
+#: rhodecode/controllers/admin/users.py:266
+msgid "Granted 'repository create' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:271
+msgid "Revoked 'repository create' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:277
+#, fuzzy
+msgid "Granted 'repository fork' permission to user"
+msgstr "版本库权限"
+
+#: rhodecode/controllers/admin/users.py:282
+#, fuzzy
+msgid "Revoked 'repository fork' permission to user"
+msgstr "版本库权限"
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+#, fuzzy
+msgid "An error occurred during permissions saving"
+msgstr "在搜索操作中发生异常"
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:309
+#, fuzzy
+msgid "An error occurred during email saving"
+msgstr "在搜索操作中发生异常"
+
+#: rhodecode/controllers/admin/users.py:319
+#, fuzzy
+msgid "Removed email from user"
+msgstr "移除版本库组 %s"
+
+#: rhodecode/controllers/admin/users_groups.py:84
+#, python-format
+msgid "created users group %s"
+msgstr "建立用户组 %s"
+
+#: rhodecode/controllers/admin/users_groups.py:95
+#, python-format
+msgid "error occurred during creation of users group %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:135
+#, python-format
+msgid "updated users group %s"
+msgstr "更新用户组 %s"
+
+#: rhodecode/controllers/admin/users_groups.py:157
+#, python-format
+msgid "error occurred during update of users group %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:174
+msgid "successfully deleted users group"
+msgstr "删除用户组成功"
+
+#: rhodecode/controllers/admin/users_groups.py:179
+msgid "An error occurred during deletion of users group"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:233
+msgid "Granted 'repository create' permission to users group"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:238
+msgid "Revoked 'repository create' permission to users group"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:244
+msgid "Granted 'repository fork' permission to users group"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:249
+msgid "Revoked 'repository fork' permission to users group"
+msgstr ""
+
+#: rhodecode/lib/auth.py:499
+msgid "You need to be a registered user to perform this action"
+msgstr "必须是注册用户才能进行此操作"
+
+#: rhodecode/lib/auth.py:540
+msgid "You need to be a signed in to view this page"
+msgstr "必须登录才能访问该页面"
+
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
+msgstr "变更集因过大而被截断,可查看原始变更集作为替代"
+
+#: rhodecode/lib/diffs.py:96
+#, fuzzy
+msgid "No changes detected"
+msgstr "尚无修订"
+
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:484
+msgid "True"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:488
+msgid "False"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:532
+#, fuzzy
+msgid "Changeset not found"
+msgstr "修改"
+
+#: rhodecode/lib/helpers.py:555
+#, python-format
+msgid "Show all combined changesets %s->%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:561
+msgid "compare view"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:581
+msgid "and"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:582
+#, python-format
+msgid "%s more"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
+msgid "revisions"
+msgstr "修订"
+
+#: rhodecode/lib/helpers.py:606
+msgid "fork name "
+msgstr "分支名称"
+
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:626
+msgid "[deleted] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
+msgid "[created] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:630
+#, fuzzy
+msgid "[created] repository as fork"
+msgstr "建立版本库 %s"
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
+msgid "[forked] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
+msgid "[updated] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:636
+msgid "[delete] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:644
+#, fuzzy
+msgid "[created] user"
+msgstr "创建用户 %s"
+
+#: rhodecode/lib/helpers.py:646
+#, fuzzy
+msgid "[updated] user"
+msgstr "更新用户组 %s"
+
+#: rhodecode/lib/helpers.py:648
+#, fuzzy
+msgid "[created] users group"
+msgstr "建立用户组 %s"
+
+#: rhodecode/lib/helpers.py:650
+#, fuzzy
+msgid "[updated] users group"
+msgstr "更新用户组 %s"
+
+#: rhodecode/lib/helpers.py:652
+msgid "[commented] on revision in repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:654
+#, fuzzy
+msgid "[commented] on pull request for"
+msgstr "创建用户 %s"
+
+#: rhodecode/lib/helpers.py:656
+msgid "[closed] pull request for"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:658
+msgid "[pushed] into"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:660
+msgid "[committed via RhodeCode] into repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:662
+msgid "[pulled from remote] into repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:664
+msgid "[pulled] from"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:666
+msgid "[started following] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:668
+msgid "[stopped following] repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:840
+#, python-format
+msgid " and %s more"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:844
+msgid "No Files"
+msgstr "没有文件"
+
+#: rhodecode/lib/utils2.py:335
+#, fuzzy, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] "年"
+
+#: rhodecode/lib/utils2.py:336
+#, fuzzy, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] "月"
+
+#: rhodecode/lib/utils2.py:337
+#, fuzzy, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] "日"
+
+#: rhodecode/lib/utils2.py:338
+#, fuzzy, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] "时"
+
+#: rhodecode/lib/utils2.py:339
+#, fuzzy, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] "分"
+
+#: rhodecode/lib/utils2.py:340
+#, fuzzy, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] "秒"
+
+#: rhodecode/lib/utils2.py:355
+#, fuzzy, python-format
+msgid "%s ago"
+msgstr "之前"
+
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
+msgstr ""
+
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
+msgstr "现在"
+
+#: rhodecode/lib/celerylib/tasks.py:269
+#, fuzzy
+msgid "password reset link"
+msgstr "密码重置链接已经发送"
+
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
+msgstr ""
+
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
+msgstr ""
+
+#: rhodecode/model/db.py:1140
+#, fuzzy
+msgid "Repository no access"
+msgstr "个版本库"
+
+#: rhodecode/model/db.py:1141
+#, fuzzy
+msgid "Repository read access"
+msgstr "这个版本库已经存在"
+
+#: rhodecode/model/db.py:1142
+#, fuzzy
+msgid "Repository write access"
+msgstr "个版本库"
+
+#: rhodecode/model/db.py:1143
+#, fuzzy
+msgid "Repository admin access"
+msgstr "个版本库"
+
+#: rhodecode/model/db.py:1145
+#, fuzzy
+msgid "Repositories Group no access"
+msgstr "版本库组"
+
+#: rhodecode/model/db.py:1146
+#, fuzzy
+msgid "Repositories Group read access"
+msgstr "版本库组"
+
+#: rhodecode/model/db.py:1147
+#, fuzzy
+msgid "Repositories Group write access"
+msgstr "版本库组"
+
+#: rhodecode/model/db.py:1148
+#, fuzzy
+msgid "Repositories Group admin access"
+msgstr "版本库组"
+
+#: rhodecode/model/db.py:1150
+#, fuzzy
+msgid "RhodeCode Administrator"
+msgstr "用户管理员"
+
+#: rhodecode/model/db.py:1151
+#, fuzzy
+msgid "Repository creation disabled"
+msgstr "建立版本库"
+
+#: rhodecode/model/db.py:1152
+#, fuzzy
+msgid "Repository creation enabled"
+msgstr "建立版本库"
+
+#: rhodecode/model/db.py:1153
+#, fuzzy
+msgid "Repository forking disabled"
+msgstr "建立版本库"
+
+#: rhodecode/model/db.py:1154
+#, fuzzy
+msgid "Repository forking enabled"
+msgstr "建立版本库"
+
+#: rhodecode/model/db.py:1155
+#, fuzzy
+msgid "Register disabled"
+msgstr "禁用"
+
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
+msgstr ""
+
+#: rhodecode/model/db.py:1580
+#, fuzzy
+msgid "Approved"
+msgstr "移除"
+
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
+msgstr ""
+
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
+msgstr ""
+
+#: rhodecode/model/forms.py:43
+msgid "Please enter a login"
+msgstr "请登录"
+
+#: rhodecode/model/forms.py:44
+#, python-format
+msgid "Enter a value %(min)i characters long or more"
+msgstr ""
+
+#: rhodecode/model/forms.py:52
+msgid "Please enter a password"
+msgstr "请输入密码"
+
+#: rhodecode/model/forms.py:53
+#, python-format
+msgid "Enter %(min)i characters or more"
+msgstr ""
+
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr ""
+
+#: rhodecode/model/notification.py:221
+#, fuzzy
+msgid "sent message"
+msgstr "提交信息"
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr ""
+
+#: rhodecode/model/notification.py:223
+#, fuzzy
+msgid "registered in RhodeCode"
+msgstr "成功注册到 rhodecode"
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
+msgstr ""
+
+#: rhodecode/model/notification.py:225
+msgid "commented on pull request"
+msgstr ""
+
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+#, fuzzy
+msgid "latest tip"
+msgstr "最后登录"
+
+#: rhodecode/model/user.py:230
+#, fuzzy
+msgid "new user registration"
+msgstr "[RhodeCode] 新用户注册"
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
+msgid "You can't Edit this user since it's crucial for entire application"
+msgstr "由于是系统帐号,无法编辑该用户"
+
+#: rhodecode/model/user.py:323
+msgid "You can't remove this user since it's crucial for entire application"
+msgstr "由于是系统帐号,无法删除该用户"
+
+#: rhodecode/model/user.py:329
+#, fuzzy, python-format
+msgid ""
+"user \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories. %s"
+msgstr "由于该用户拥有版本库 %s 因而无法删除,请变更版本库所有者或删除版本库"
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, fuzzy, python-format
+msgid "Username \"%(username)s\" already exists"
+msgstr "该用户名已经存在"
+
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr ""
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with alphanumeric character"
+msgstr "只能使用字母、数字、下划线、小数点或减号作为用户名,且必须由数字或字母开头"
+
+#: rhodecode/model/validators.py:114
+#, fuzzy, python-format
+msgid "Username %(username)s is not valid"
+msgstr "用户或用户组名称无效"
+
+#: rhodecode/model/validators.py:133
+#, fuzzy
+msgid "Invalid users group name"
+msgstr "无效用户名"
+
+#: rhodecode/model/validators.py:134
+#, fuzzy, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
+msgstr "该用户组名称已经存在"
+
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr "只能使用字母、数字、下划线、小数点或减号作为用户组名,且必须由数字或字母开头"
+
+#: rhodecode/model/validators.py:174
+msgid "Cannot assign this group as parent"
+msgstr ""
+
+#: rhodecode/model/validators.py:175
+#, fuzzy, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr "该用户名已经存在"
+
+#: rhodecode/model/validators.py:177
+#, fuzzy, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr "这个版本库已经存在"
+
+#: rhodecode/model/validators.py:235
+#, fuzzy
+msgid "Invalid characters (non-ascii) in password"
+msgstr "密码含有无效字符"
+
+#: rhodecode/model/validators.py:250
+msgid "Passwords do not match"
+msgstr "密码不符"
+
+#: rhodecode/model/validators.py:267
+msgid "invalid password"
+msgstr "无效密码"
+
+#: rhodecode/model/validators.py:268
+msgid "invalid user name"
+msgstr "无效用户名"
+
+#: rhodecode/model/validators.py:269
+msgid "Your account is disabled"
+msgstr "该帐号已被禁用"
+
+#: rhodecode/model/validators.py:313
+#, fuzzy, python-format
+msgid "Repository name %(repo)s is disallowed"
+msgstr "该版本库名称被禁用"
+
+#: rhodecode/model/validators.py:315
+#, fuzzy, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr "这个版本库已经存在"
+
+#: rhodecode/model/validators.py:316
+#, fuzzy, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr "组中已经存在该版本库"
+
+#: rhodecode/model/validators.py:318
+#, fuzzy, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
+msgstr "这个版本库已经存在"
+
+#: rhodecode/model/validators.py:431
+msgid "invalid clone url"
+msgstr "无效的 clone 地址"
+
+#: rhodecode/model/validators.py:432
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
+msgstr ""
+
+#: rhodecode/model/validators.py:457
+#, fuzzy
+msgid "Fork have to be the same type as parent"
+msgstr "分支必须使用相同的版本库类型"
+
+#: rhodecode/model/validators.py:478
+msgid "This username or users group name is not valid"
+msgstr "用户或用户组名称无效"
+
+#: rhodecode/model/validators.py:562
+msgid "This is not a valid path"
+msgstr "不是一个合法的路径"
+
+#: rhodecode/model/validators.py:577
+msgid "This e-mail address is already taken"
+msgstr "该邮件地址已被使用"
+
+#: rhodecode/model/validators.py:597
+#, fuzzy, python-format
+msgid "e-mail \"%(email)s\" does not exist."
+msgstr "该邮件地址不存在"
+
+#: rhodecode/model/validators.py:634
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
+msgstr ""
+
+#: rhodecode/model/validators.py:653
+#, python-format
+msgid "Revisions %(revs)s are already part of pull request or have set status"
+msgstr ""
+
+#: rhodecode/templates/index.html:3
+msgid "Dashboard"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
+msgid "quick filter..."
+msgstr "快速过滤..."
+
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
+msgid "repositories"
+msgstr "个版本库"
+
+#: rhodecode/templates/index_base.html:13
+#: rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
+msgstr "新增版本库"
+
+#: rhodecode/templates/index_base.html:29
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
+#: rhodecode/templates/admin/users_groups/users_group_add.html:32
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:33
+msgid "Group name"
+msgstr "组名"
+
+#: rhodecode/templates/index_base.html:30
+#: rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142
+#: rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
+msgid "Description"
+msgstr "描述"
+
+#: rhodecode/templates/index_base.html:40
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
+msgid "Repositories group"
+msgstr "版本库组"
+
+#: rhodecode/templates/index_base.html:70
+#: rhodecode/templates/index_base.html:166
+#: rhodecode/templates/admin/repos/repo_add_base.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:32
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
+#: rhodecode/templates/settings/repo_settings.html:31
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36
+#: rhodecode/templates/tags/tags_data.html:6
+msgid "Name"
+msgstr "名称"
+
+#: rhodecode/templates/index_base.html:72
+msgid "Last change"
+msgstr "最后修改"
+
+#: rhodecode/templates/index_base.html:73
+#: rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
+msgid "Tip"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:74
+#: rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
+msgid "Owner"
+msgstr "所有者"
+
+#: rhodecode/templates/index_base.html:75
+#: rhodecode/templates/summary/summary.html:48
+#: rhodecode/templates/summary/summary.html:51
+msgid "RSS"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:76
+msgid "Atom"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:110
+#: rhodecode/templates/index_base.html:112
+#, python-format
+msgid "Subscribe to %s rss feed"
+msgstr "订阅 rss %s"
+
+#: rhodecode/templates/index_base.html:117
+#: rhodecode/templates/index_base.html:119
+#, python-format
+msgid "Subscribe to %s atom feed"
+msgstr "订阅 atom %s"
+
+#: rhodecode/templates/index_base.html:140
+#, fuzzy
+msgid "Group Name"
+msgstr "组名"
+
+#: rhodecode/templates/index_base.html:158
+#: rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:159
+#: rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:169
+#, fuzzy
+msgid "Last Change"
+msgstr "最后修改"
+
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
+msgstr ""
+
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
+msgstr ""
+
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+#, fuzzy
+msgid "Loading..."
+msgstr "加载文件列表..."
+
+#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
+msgid "Sign In"
+msgstr "登录"
+
+#: rhodecode/templates/login.html:21
+msgid "Sign In to"
+msgstr "登录到"
+
+#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
+#: rhodecode/templates/admin/admin_log.html:5
+#: rhodecode/templates/admin/users/user_add.html:32
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
+msgid "Username"
+msgstr "帐号"
+
+#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
+#: rhodecode/templates/admin/ldap/ldap.html:46
+#: rhodecode/templates/admin/users/user_add.html:41
+#: rhodecode/templates/base/base.html:92
+msgid "Password"
+msgstr "密码"
+
+#: rhodecode/templates/login.html:50
+#, fuzzy
+msgid "Remember me"
+msgstr "成员"
+
+#: rhodecode/templates/login.html:60
+msgid "Forgot your password ?"
+msgstr "忘记了密码?"
+
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
+msgid "Don't have an account ?"
+msgstr "还没有帐号?"
+
+#: rhodecode/templates/password_reset.html:5
+msgid "Reset your password"
+msgstr "重置密码"
+
+#: rhodecode/templates/password_reset.html:11
+msgid "Reset your password to"
+msgstr "重置密码"
+
+#: rhodecode/templates/password_reset.html:21
+msgid "Email address"
+msgstr "邮件地址"
+
+#: rhodecode/templates/password_reset.html:30
+msgid "Reset my password"
+msgstr "重置密码"
+
+#: rhodecode/templates/password_reset.html:31
+msgid "Password reset link will be send to matching email address"
+msgstr "密码重置地址已经发送到邮件"
+
+#: rhodecode/templates/register.html:5 rhodecode/templates/register.html:74
+msgid "Sign Up"
+msgstr "注册"
+
+#: rhodecode/templates/register.html:11
+msgid "Sign Up to"
+msgstr "注册"
+
+#: rhodecode/templates/register.html:38
+msgid "Re-enter password"
+msgstr "确认密码"
+
+#: rhodecode/templates/register.html:47
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
+msgid "First Name"
+msgstr "名"
+
+#: rhodecode/templates/register.html:56
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
+msgid "Last Name"
+msgstr "姓"
+
+#: rhodecode/templates/register.html:65
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
+msgid "Email"
+msgstr "电子邮件"
+
+#: rhodecode/templates/register.html:76
+msgid "Your account will be activated right after registration"
+msgstr "注册后,帐号将启用"
+
+#: rhodecode/templates/register.html:78
+msgid "Your account must wait for activation by administrator"
+msgstr "管理员审核后,你注册的帐号将被启用"
+
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
+msgid "Private repository"
+msgstr "私有版本库"
+
+#: rhodecode/templates/repo_switcher_list.html:16
+msgid "Public repository"
+msgstr "公共版本库"
+
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr "分支"
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr "没有任何分支"
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr "标签"
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr "没有任何标签"
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+#, fuzzy
+msgid "There are no bookmarks yet"
+msgstr "尚未有任何分支"
+
+#: rhodecode/templates/admin/admin.html:5
+#: rhodecode/templates/admin/admin.html:9
+msgid "Admin journal"
+msgstr "管理员日志"
+
+#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
+msgid "Action"
+msgstr "操作"
+
+#: rhodecode/templates/admin/admin_log.html:7
+msgid "Repository"
+msgstr "版本库"
+
+#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37
+#: rhodecode/templates/tags/tags_data.html:7
+msgid "Date"
+msgstr "日期"
+
+#: rhodecode/templates/admin/admin_log.html:9
+msgid "From IP"
+msgstr "来源 IP"
+
+#: rhodecode/templates/admin/admin_log.html:53
+msgid "No actions yet"
+msgstr "尚无操作"
+
+#: rhodecode/templates/admin/ldap/ldap.html:5
+msgid "LDAP administration"
+msgstr "LDAP 管理员"
+
+#: rhodecode/templates/admin/ldap/ldap.html:11
+msgid "Ldap"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:28
+msgid "Connection settings"
+msgstr "连接设置"
+
+#: rhodecode/templates/admin/ldap/ldap.html:30
+msgid "Enable LDAP"
+msgstr "启用 LDAP"
+
+#: rhodecode/templates/admin/ldap/ldap.html:34
+msgid "Host"
+msgstr "主机"
+
+#: rhodecode/templates/admin/ldap/ldap.html:38
+msgid "Port"
+msgstr "端口"
+
+#: rhodecode/templates/admin/ldap/ldap.html:42
+msgid "Account"
+msgstr "帐号"
+
+#: rhodecode/templates/admin/ldap/ldap.html:50
+msgid "Connection security"
+msgstr "连接安全"
+
+#: rhodecode/templates/admin/ldap/ldap.html:54
+msgid "Certificate Checks"
+msgstr "凭证确认"
+
+#: rhodecode/templates/admin/ldap/ldap.html:57
+msgid "Search settings"
+msgstr "搜索设置"
+
+#: rhodecode/templates/admin/ldap/ldap.html:59
+msgid "Base DN"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:63
+msgid "LDAP Filter"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:67
+msgid "LDAP Search Scope"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:70
+msgid "Attribute mappings"
+msgstr "属性映射"
+
+#: rhodecode/templates/admin/ldap/ldap.html:72
+msgid "Login Attribute"
+msgstr "登录属性"
+
+#: rhodecode/templates/admin/ldap/ldap.html:76
+msgid "First Name Attribute"
+msgstr "名"
+
+#: rhodecode/templates/admin/ldap/ldap.html:80
+msgid "Last Name Attribute"
+msgstr "姓"
+
+#: rhodecode/templates/admin/ldap/ldap.html:84
+msgid "E-mail Attribute"
+msgstr "电子邮件属性"
+
+#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
+#: rhodecode/templates/admin/settings/hooks.html:73
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
+msgid "Save"
+msgstr "保存"
+
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+#, fuzzy
+msgid "Comments"
+msgstr "提交"
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254
+#: rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+#, fuzzy
+msgid "No notifications here yet"
+msgstr "尚无操作"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+#, fuzzy
+msgid "Show notification"
+msgstr "显示注释"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+#, fuzzy
+msgid "Notifications"
+msgstr "位置"
+
+#: rhodecode/templates/admin/permissions/permissions.html:5
+msgid "Permissions administration"
+msgstr "权限管理"
+
+#: rhodecode/templates/admin/permissions/permissions.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
+msgid "Permissions"
+msgstr "权限"
+
+#: rhodecode/templates/admin/permissions/permissions.html:24
+msgid "Default permissions"
+msgstr "默认权限"
+
+#: rhodecode/templates/admin/permissions/permissions.html:31
+msgid "Anonymous access"
+msgstr "匿名访问"
+
+#: rhodecode/templates/admin/permissions/permissions.html:41
+msgid "Repository permission"
+msgstr "版本库权限"
+
+#: rhodecode/templates/admin/permissions/permissions.html:49
+msgid ""
+"All default permissions on each repository will be reset to choosen "
+"permission, note that all custom default permission on repositories will "
+"be lost"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:50
+msgid "overwrite existing settings"
+msgstr "覆盖已有设置"
+
+#: rhodecode/templates/admin/permissions/permissions.html:55
+msgid "Registration"
+msgstr "注册"
+
+#: rhodecode/templates/admin/permissions/permissions.html:63
+msgid "Repository creation"
+msgstr "建立版本库"
+
+#: rhodecode/templates/admin/permissions/permissions.html:71
+#, fuzzy
+msgid "Repository forking"
+msgstr "建立版本库"
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
+msgid "set"
+msgstr "设置"
+
+#: rhodecode/templates/admin/repos/repo_add.html:5
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:5
+msgid "Add repository"
+msgstr "添加版本库"
+
+#: rhodecode/templates/admin/repos/repo_add.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:11
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
+msgid "Repositories"
+msgstr "版本库"
+
+#: rhodecode/templates/admin/repos/repo_add.html:13
+msgid "add new"
+msgstr "新增"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:20
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
+msgid "Clone from"
+msgstr "clone 自"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
+msgid "Repository group"
+msgstr "版本库组"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+msgid "Optionaly select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
+msgid "Type"
+msgstr "类型"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+#, fuzzy
+msgid "Type of repository to create."
+msgstr "建立版本库"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+#, fuzzy
+msgid "Landing revision"
+msgstr "下一个修订"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
+msgid "add"
+msgstr "新增"
+
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:9
+msgid "add new repository"
+msgstr "新增版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:5
+msgid "Edit repository"
+msgstr "编辑版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
+msgid "edit"
+msgstr "编辑"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
+msgid "Clone uri"
+msgstr "clone 地址"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
+msgid "Enable statistics"
+msgstr "启用统计"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
+msgid "Enable downloads"
+msgstr "启用下载"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+#, fuzzy
+msgid "Enable locking"
+msgstr "启用"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+#, fuzzy
+msgid "Change owner of this repository."
+msgstr "%s 库的修改"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr "重置"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
+msgid "Administration"
+msgstr "管理"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:155
+msgid "Statistics"
+msgstr "统计"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:159
+msgid "Reset current statistics"
+msgstr "重置统计"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:159
+msgid "Confirm to remove current statistics"
+msgstr "确认移除当前统计"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:162
+msgid "Fetched to rev"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:171
+msgid "Remote"
+msgstr "远程"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:175
+msgid "Pull changes from remote location"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:175
+msgid "Confirm to pull changes from remote side"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:186
+msgid "Cache"
+msgstr "缓存"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:190
+msgid "Invalidate repository cache"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:190
+msgid "Confirm to invalidate repository cache"
+msgstr "确认清除版本库缓存"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318
+#: rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr "公共日志"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
+msgid "Remove from public journal"
+msgstr "从公共日志删除"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:203
+msgid "Add to public journal"
+msgstr "添加到公共日志"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in "
+"public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+#, fuzzy
+msgid "Locking"
+msgstr "解锁"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+#, fuzzy
+msgid "Confirm to unlock repository"
+msgstr "确认删除版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+#, fuzzy
+msgid "Confirm to lock repository"
+msgstr "确认删除版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+#, fuzzy
+msgid "Repository is not locked"
+msgstr "个版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+msgid "Set as fork of"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+msgid "Manually set this repository as a fork of another from the list"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
+msgid "Delete"
+msgstr "删除"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+msgid "Remove this repository"
+msgstr "删除版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
+msgid "Confirm to delete this repository"
+msgstr "确认删除版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be "
+"unaccesible for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem "
+"please do it manually"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
+msgid "none"
+msgstr "无"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
+msgid "read"
+msgstr "读"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
+msgid "write"
+msgstr "写"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
+msgid "admin"
+msgstr "管理员"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
+msgid "member"
+msgstr "成员"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr "私有版本库"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+#, fuzzy
+msgid "default"
+msgstr "删除"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
+msgid "revoke"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
+msgid "Add another member"
+msgstr "添加成员"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
+msgid "Failed to remove user"
+msgstr "删除用户失败"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
+msgid "Failed to remove users group"
+msgstr "删除用户组失败"
+
+#: rhodecode/templates/admin/repos/repos.html:5
+msgid "Repositories administration"
+msgstr "版本库管理员"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
+msgid "Groups"
+msgstr "组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
+msgid "with"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:5
+msgid "Add repos group"
+msgstr "添加版本库组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:10
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:10
+msgid "Repos groups"
+msgstr "版本库组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:12
+msgid "add new repos group"
+msgstr "添加新版本库组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:50
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:50
+msgid "Group parent"
+msgstr "上级组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
+#: rhodecode/templates/admin/users/user_add.html:94
+#: rhodecode/templates/admin/users_groups/users_group_add.html:49
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
+msgid "save"
+msgstr "保存"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:5
+msgid "Edit repos group"
+msgstr "编辑版本库组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:12
+msgid "edit repos group"
+msgstr "编辑版本库组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
+msgid "Repositories groups administration"
+msgstr "版本库管理员"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:22
+msgid "ADD NEW GROUP"
+msgstr "添加组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
+#, fuzzy
+msgid "Number of toplevel repositories"
+msgstr "版本库数量"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
+msgstr "操作"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr "删除"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#, fuzzy, python-format
+msgid "Confirm to delete this group: %s"
+msgstr "确认删除该组"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
+msgid "There are no repositories groups yet"
+msgstr "没有版本库组"
+
+#: rhodecode/templates/admin/settings/hooks.html:5
+#: rhodecode/templates/admin/settings/settings.html:5
+msgid "Settings administration"
+msgstr "设置管理员"
+
+#: rhodecode/templates/admin/settings/hooks.html:9
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/settings/repo_settings.html:13
+msgid "Settings"
+msgstr "设置"
+
+#: rhodecode/templates/admin/settings/hooks.html:24
+msgid "Built in hooks - read only"
+msgstr "内建钩子 - 只读"
+
+#: rhodecode/templates/admin/settings/hooks.html:40
+msgid "Custom hooks"
+msgstr "自定义钩子"
+
+#: rhodecode/templates/admin/settings/hooks.html:56
+msgid "remove"
+msgstr "删除"
+
+#: rhodecode/templates/admin/settings/hooks.html:88
+msgid "Failed to remove hook"
+msgstr "移除钩子失败"
+
+#: rhodecode/templates/admin/settings/settings.html:24
+msgid "Remap and rescan repositories"
+msgstr "重新扫描并映射版本库"
+
+#: rhodecode/templates/admin/settings/settings.html:32
+msgid "rescan option"
+msgstr "重新扫描选项"
+
+#: rhodecode/templates/admin/settings/settings.html:38
+msgid ""
+"In case a repository was deleted from filesystem and there are leftovers "
+"in the database check this option to scan obsolete data in database and "
+"remove it."
+msgstr "如果版本库已经从文件系统删除,但数据库仍然有遗留信息,请勾选该项进行清理"
+
+#: rhodecode/templates/admin/settings/settings.html:39
+msgid "destroy old data"
+msgstr "清理旧数据"
+
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete "
+"if `destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
+msgid "Rescan repositories"
+msgstr "重新扫描版本库"
+
+#: rhodecode/templates/admin/settings/settings.html:52
+msgid "Whoosh indexing"
+msgstr "Whoosh 索引"
+
+#: rhodecode/templates/admin/settings/settings.html:60
+msgid "index build option"
+msgstr "构建索引选项"
+
+#: rhodecode/templates/admin/settings/settings.html:65
+msgid "build from scratch"
+msgstr "重新建立"
+
+#: rhodecode/templates/admin/settings/settings.html:71
+msgid "Reindex"
+msgstr "重新索引"
+
+#: rhodecode/templates/admin/settings/settings.html:77
+msgid "Global application settings"
+msgstr "全局设置"
+
+#: rhodecode/templates/admin/settings/settings.html:86
+msgid "Application name"
+msgstr "应用名称"
+
+#: rhodecode/templates/admin/settings/settings.html:95
+msgid "Realm text"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:104
+msgid "GA code"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
+msgid "Save settings"
+msgstr "保存设置"
+
+#: rhodecode/templates/admin/settings/settings.html:119
+#, fuzzy
+msgid "Visualisation settings"
+msgstr "全局设置"
+
+#: rhodecode/templates/admin/settings/settings.html:128
+#, fuzzy
+msgid "Icons"
+msgstr "选项"
+
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+#, fuzzy
+msgid "Show private repo icon on repositories"
+msgstr "私有版本库"
+
+#: rhodecode/templates/admin/settings/settings.html:144
+#, fuzzy
+msgid "Meta-Tagging"
+msgstr "设置"
+
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:176
+#, fuzzy
+msgid "VCS settings"
+msgstr "设置"
+
+#: rhodecode/templates/admin/settings/settings.html:185
+msgid "Web"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:190
+#, fuzzy
+msgid "require ssl for vcs operations"
+msgstr "使用 SSL 推送"
+
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
+"will return HTTP Error 406: Not Acceptable"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:198
+msgid "Hooks"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:203
+msgid "Update repository after push (hg update)"
+msgstr "推送后更新版本库(hg update)"
+
+#: rhodecode/templates/admin/settings/settings.html:207
+msgid "Show repository size after push"
+msgstr "推送后显示版本库大小"
+
+#: rhodecode/templates/admin/settings/settings.html:211
+msgid "Log user push commands"
+msgstr "记录用户推送命令"
+
+#: rhodecode/templates/admin/settings/settings.html:215
+msgid "Log user pull commands"
+msgstr "记录用户拉取命令"
+
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr "高级设置"
+
+#: rhodecode/templates/admin/settings/settings.html:224
+#, fuzzy
+msgid "Mercurial Extensions"
+msgstr "Mercurial 版本库"
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
+msgid "Repositories location"
+msgstr "版本库路径"
+
+#: rhodecode/templates/admin/settings/settings.html:250
+msgid ""
+"This a crucial application setting. If you are really sure you need to "
+"change this, you must restart application in order to make this setting "
+"take effect. Click this label to unlock."
+msgstr "这是一个关键设置。如果确认修改该项设置,请重启服务以便设置生效。"
+
+#: rhodecode/templates/admin/settings/settings.html:251
+msgid "unlock"
+msgstr "解锁"
+
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a "
+"restart, and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:280
+#, fuzzy
+msgid "Email to"
+msgstr "电子邮件"
+
+#: rhodecode/templates/admin/settings/settings.html:288
+#, fuzzy
+msgid "Send"
+msgstr "秒"
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:297
+#, fuzzy
+msgid "show"
+msgstr "显示"
+
+#: rhodecode/templates/admin/users/user_add.html:5
+msgid "Add user"
+msgstr "添加用户"
+
+#: rhodecode/templates/admin/users/user_add.html:10
+#: rhodecode/templates/admin/users/user_edit.html:11
+msgid "Users"
+msgstr "用户"
+
+#: rhodecode/templates/admin/users/user_add.html:12
+msgid "add new user"
+msgstr "添加新用户"
+
+#: rhodecode/templates/admin/users/user_add.html:50
+#, fuzzy
+msgid "Password confirmation"
+msgstr "密码不符"
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
+#: rhodecode/templates/admin/users_groups/users_group_add.html:41
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
+msgid "Active"
+msgstr "启用"
+
+#: rhodecode/templates/admin/users/user_edit.html:5
+msgid "Edit user"
+msgstr "编辑用户"
+
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
+msgid "Change your avatar at"
+msgstr "修改你的头像"
+
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
+msgid "Using"
+msgstr "使用中"
+
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
+msgid "API key"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:59
+msgid "LDAP DN"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
+msgid "New password"
+msgstr "新密码"
+
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+#, fuzzy
+msgid "Inherit default permissions"
+msgstr "默认权限"
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
+msgid "Create repositories"
+msgstr "创建版本库"
+
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+#, fuzzy
+msgid "Fork repositories"
+msgstr "个版本库"
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+#, fuzzy
+msgid "Nothing here yet"
+msgstr "尚无操作"
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+#, fuzzy
+msgid "Permission"
+msgstr "权限"
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+#, fuzzy
+msgid "Edit Permission"
+msgstr "版本库权限"
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+#, fuzzy
+msgid "Email addresses"
+msgstr "邮件地址"
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, fuzzy, python-format
+msgid "Confirm to delete this email: %s"
+msgstr "确认删除该用户"
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+#, fuzzy
+msgid "New email address"
+msgstr "邮件地址"
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+#, fuzzy
+msgid "Add"
+msgstr "新增"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
+msgid "My account"
+msgstr "我的账户"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:9
+msgid "My Account"
+msgstr "我的账户"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+#, fuzzy
+msgid "My permissions"
+msgstr "权限"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+#, fuzzy
+msgid "My repos"
+msgstr "空版本库"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+#, fuzzy
+msgid "My pull requests"
+msgstr "创建用户 %s"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+#, fuzzy
+msgid "Add repo"
+msgstr "新增"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, python-format
+msgid "Pull request #%s opened on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+#, fuzzy
+msgid "Confirm to delete this pull request"
+msgstr "确认删除版本库"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40
+#: rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
+msgstr "修订"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr "私有"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, fuzzy, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr "确认删除版本库"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
+msgid "No repositories yet"
+msgstr "没有任何版本库"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
+msgid "create one now"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:5
+msgid "Users administration"
+msgstr "用户管理员"
+
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr "用户"
+
+#: rhodecode/templates/admin/users/users.html:23
+msgid "ADD NEW USER"
+msgstr "添加用户"
+
+#: rhodecode/templates/admin/users/users.html:77
+msgid "username"
+msgstr "用户名"
+
+#: rhodecode/templates/admin/users/users.html:80
+#, fuzzy
+msgid "firstname"
+msgstr "名"
+
+#: rhodecode/templates/admin/users/users.html:81
+msgid "lastname"
+msgstr "姓"
+
+#: rhodecode/templates/admin/users/users.html:82
+msgid "last login"
+msgstr "最后登录"
+
+#: rhodecode/templates/admin/users/users.html:84
+#: rhodecode/templates/admin/users_groups/users_groups.html:34
+msgid "active"
+msgstr "启用"
+
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
+msgid "ldap"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:5
+msgid "Add users group"
+msgstr "添加用户组"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:10
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+msgid "Users groups"
+msgstr "用户组"
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:12
+msgid "add new users group"
+msgstr "添加新用户组"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:5
+msgid "Edit users group"
+msgstr "编辑用户组"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:11
+msgid "UsersGroups"
+msgstr "用户组"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:50
+msgid "Members"
+msgstr "成员"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:58
+msgid "Choosen group members"
+msgstr "选择组成员"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:61
+msgid "Remove all elements"
+msgstr "移除全部项目"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:75
+msgid "Available members"
+msgstr "启用成员"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:79
+msgid "Add all elements"
+msgstr "添加全部项目"
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+#, fuzzy
+msgid "Group members"
+msgstr "选择组成员"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:5
+msgid "Users groups administration"
+msgstr "用户组管理"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:23
+msgid "ADD NEW USER GROUP"
+msgstr "添加新用户组"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:32
+msgid "group name"
+msgstr "组名"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr "成员"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, fuzzy, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr "确认删除该组"
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr "提交 bug"
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:100
+msgid "Forgot password ?"
+msgstr "忘记密码?"
+
+#: rhodecode/templates/base/base.html:107
+#, fuzzy
+msgid "Log In"
+msgstr "登录"
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:122
+#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302
+#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8
+#: rhodecode/templates/tags/tags.html:11
+msgid "Home"
+msgstr "首页"
+
+#: rhodecode/templates/base/base.html:123
+#: rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311
+#: rhodecode/templates/base/base.html:313
+#: rhodecode/templates/journal/journal.html:4
+#: rhodecode/templates/journal/journal.html:21
+#: rhodecode/templates/journal/public_journal.html:4
+msgid "Journal"
+msgstr "日志"
+
+#: rhodecode/templates/base/base.html:125
+msgid "Log Out"
+msgstr "退出"
+
+#: rhodecode/templates/base/base.html:144
+msgid "Switch repository"
+msgstr "切换版本库"
+
+#: rhodecode/templates/base/base.html:146
+msgid "Products"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:152
+#: rhodecode/templates/base/base.html:182
+msgid "loading..."
+msgstr ""
+
+#: rhodecode/templates/base/base.html:158
+#: rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
+msgstr "概况"
+
+#: rhodecode/templates/base/base.html:166
+#: rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
+msgstr "修改记录"
+
+#: rhodecode/templates/base/base.html:175
+#: rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
+msgstr "切换到"
+
+#: rhodecode/templates/base/base.html:186
+#: rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
+msgstr "档案"
+
+#: rhodecode/templates/base/base.html:195
+#: rhodecode/templates/base/base.html:199
+msgid "Options"
+msgstr "选项"
+
+#: rhodecode/templates/base/base.html:204
+#: rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
+msgid "settings"
+msgstr "设置"
+
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:213
+msgid "search"
+msgstr "搜索"
+
+#: rhodecode/templates/base/base.html:222
+msgid "repositories groups"
+msgstr "版本库组"
+
+#: rhodecode/templates/base/base.html:224
+msgid "users groups"
+msgstr "用户组"
+
+#: rhodecode/templates/base/base.html:225
+msgid "permissions"
+msgstr "权限"
+
+#: rhodecode/templates/base/base.html:238
+#: rhodecode/templates/base/base.html:240
+msgid "Followers"
+msgstr "跟随者"
+
+#: rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248
+msgid "Forks"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:327
+#: rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331
+#: rhodecode/templates/search/search.html:52
+msgid "Search"
+msgstr "搜索"
+
+#: rhodecode/templates/base/root.html:42
+#, fuzzy
+msgid "add another comment"
+msgstr "添加成员"
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
+msgid "Stop following this repository"
+msgstr "停止跟随该版本库"
+
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
+msgid "Start following this repository"
+msgstr "开始跟随该版本库"
+
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr "组"
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr "没有符合的文件"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr ""
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39
+#: rhodecode/templates/tags/tags_data.html:8
+#, fuzzy
+msgid "Author"
+msgstr "作者"
+
+#: rhodecode/templates/branches/branches.html:5
+#, fuzzy, python-format
+msgid "%s Branches"
+msgstr "分支"
+
+#: rhodecode/templates/branches/branches.html:29
+#, fuzzy
+msgid "Compare branches"
+msgstr "分支"
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+#, fuzzy
+msgid "Compare"
+msgstr "比较显示"
+
+#: rhodecode/templates/branches/branches_data.html:6
+msgid "name"
+msgstr "名称"
+
+#: rhodecode/templates/branches/branches_data.html:7
+msgid "date"
+msgstr "日期"
+
+#: rhodecode/templates/branches/branches_data.html:8
+#: rhodecode/templates/shortlog/shortlog_data.html:8
+msgid "author"
+msgstr "作者"
+
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
+msgstr "修订"
+
+#: rhodecode/templates/branches/branches_data.html:10
+#, fuzzy
+msgid "compare"
+msgstr "比较显示"
+
+#: rhodecode/templates/changelog/changelog.html:6
+#, fuzzy, python-format
+msgid "%s Changelog"
+msgstr "修改记录"
+
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+msgid "Compare fork"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:46
+msgid "Show"
+msgstr "显示"
+
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:76
+msgid "Affected number of files, click to show more details"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+#, fuzzy
+msgid "Changeset status"
+msgstr "变更集"
+
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
+msgid "Parent"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
+msgid "No parents"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr "合并"
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
+#: rhodecode/templates/files/files.html:29
+#: rhodecode/templates/files/files_add.html:33
+#: rhodecode/templates/files/files_edit.html:33
+#: rhodecode/templates/shortlog/shortlog_data.html:9
+msgid "branch"
+msgstr "分支"
+
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
+msgid "tag"
+msgstr "标签"
+
+#: rhodecode/templates/changelog/changelog.html:164
+msgid "Show selected changes __S -> __E"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:255
+msgid "There are no changes yet"
+msgstr "没有任何变更"
+
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
+msgid "removed"
+msgstr "移除"
+
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
+msgid "changed"
+msgstr "修改"
+
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
+msgid "added"
+msgstr "添加"
+
+#: rhodecode/templates/changelog/changelog_details.html:8
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
+#, python-format
+msgid "affected %s files"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:6
+#, fuzzy, python-format
+msgid "%s Changeset"
+msgstr "无变更"
+
+#: rhodecode/templates/changeset/changeset.html:14
+msgid "Changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
+msgid "raw diff"
+msgstr "原始 diff"
+
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
+msgid "download diff"
+msgstr "下载 diff"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, fuzzy, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "提交"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, python-format
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] ""
+
+#: rhodecode/templates/changeset/changeset.html:103
+#, python-format
+msgid "%s files affected with %s insertions and %s deletions:"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:119
+msgid "Changeset was too big and was cut off..."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+#, fuzzy
+msgid "Comment"
+msgstr "提交"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+#, fuzzy
+msgid "You need to be logged in to comment."
+msgstr "必须登录才能访问该页面"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+#, fuzzy
+msgid "change status"
+msgstr "变更集"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, fuzzy, python-format
+msgid "%s Changesets"
+msgstr "变更集"
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
+msgid "Compare View"
+msgstr "比较显示"
+
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
+msgid "Files affected"
+msgstr ""
+
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
+msgstr ""
+
+#: rhodecode/templates/changeset/diff_block.html:27
+#, fuzzy
+msgid "show inline comments"
+msgstr "文件内容"
+
+#: rhodecode/templates/compare/compare_cs.html:5
+#, fuzzy
+msgid "No changesets"
+msgstr "尚无修订"
+
+#: rhodecode/templates/compare/compare_diff.html:37
+#, fuzzy
+msgid "Outgoing changesets"
+msgstr "尚无修订"
+
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
+msgstr "分支"
+
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
+msgstr "Mercurial 版本库"
+
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
+msgstr "Git 版本库"
+
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
+msgstr "公共版本库"
+
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
+msgstr ""
+
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
+msgstr "尚无修订"
+
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, fuzzy, python-format
+msgid "Confirm to delete this user: %s"
+msgstr "确认删除该用户"
+
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
+msgstr ""
+
+#: rhodecode/templates/errors/error_document.html:46
+#, python-format
+msgid "You will be redirected to %s in %s seconds"
+msgstr ""
+
+#: rhodecode/templates/files/file_diff.html:4
+#, fuzzy, python-format
+msgid "%s File diff"
+msgstr "文件 diff"
+
+#: rhodecode/templates/files/file_diff.html:12
+msgid "File diff"
+msgstr "文件 diff"
+
+#: rhodecode/templates/files/files.html:4
+#: rhodecode/templates/files/files.html:72
+#, fuzzy, python-format
+msgid "%s files"
+msgstr "文件"
+
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr "文件"
+
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, fuzzy, python-format
+msgid "%s Edit file"
+msgstr "编辑文件"
+
+#: rhodecode/templates/files/files_add.html:19
+#, fuzzy
+msgid "add file"
+msgstr "编辑文件"
+
+#: rhodecode/templates/files/files_add.html:40
+#, fuzzy
+msgid "Add new file"
+msgstr "添加新用户"
+
+#: rhodecode/templates/files/files_add.html:45
+#, fuzzy
+msgid "File Name"
+msgstr "文件名"
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+#, fuzzy
+msgid "or"
+msgstr "时"
+
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+#, fuzzy
+msgid "Upload file"
+msgstr "编辑文件"
+
+#: rhodecode/templates/files/files_add.html:58
+#, fuzzy
+msgid "Create new file"
+msgstr "创建用户 %s"
+
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr "位置"
+
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr ""
+
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr "提交信息"
+
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
+msgstr "提交修改"
+
+#: rhodecode/templates/files/files_browser.html:13
+msgid "view"
+msgstr "显示"
+
+#: rhodecode/templates/files/files_browser.html:14
+msgid "previous revision"
+msgstr "上一个修订"
+
+#: rhodecode/templates/files/files_browser.html:16
+msgid "next revision"
+msgstr "下一个修订"
+
+#: rhodecode/templates/files/files_browser.html:23
+msgid "follow current branch"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:27
+msgid "search file list"
+msgstr "搜索文件列表"
+
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+#, fuzzy
+msgid "add new file"
+msgstr "添加新用户"
+
+#: rhodecode/templates/files/files_browser.html:35
+msgid "Loading file list..."
+msgstr "加载文件列表..."
+
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
+msgstr "大小"
+
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:50
+#, fuzzy
+msgid "Last Revision"
+msgstr "下一个修订"
+
+#: rhodecode/templates/files/files_browser.html:51
+msgid "Last modified"
+msgstr "最后修改"
+
+#: rhodecode/templates/files/files_browser.html:52
+msgid "Last commiter"
+msgstr "最后提交"
+
+#: rhodecode/templates/files/files_edit.html:19
+msgid "edit file"
+msgstr "编辑文件"
+
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr "显示注释"
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
+msgstr "显示原始文件"
+
+#: rhodecode/templates/files/files_edit.html:51
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
+msgstr "下载原始文件"
+
+#: rhodecode/templates/files/files_edit.html:54
+#, fuzzy
+msgid "source"
+msgstr "显示代码"
+
+#: rhodecode/templates/files/files_edit.html:59
+#, fuzzy
+msgid "Editing file"
+msgstr "编辑文件"
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr "历史"
+
+#: rhodecode/templates/files/files_source.html:9
+#, fuzzy
+msgid "diff to revision"
+msgstr "下一个修订"
+
+#: rhodecode/templates/files/files_source.html:10
+#, fuzzy
+msgid "show at revision"
+msgstr "下一个修订"
+
+#: rhodecode/templates/files/files_source.html:14
+#, fuzzy, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] "作者"
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
+msgstr "显示代码"
+
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr "二进制文件(%s)"
+
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
+msgstr "文件过大,不能显示"
+
+#: rhodecode/templates/files/files_source.html:124
+msgid "Selection link"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:5
+#, fuzzy
+msgid "annotation"
+msgstr "显示注释"
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr ""
+
+#: rhodecode/templates/followers/followers.html:5
+#, fuzzy, python-format
+msgid "%s Followers"
+msgstr "跟随者"
+
+#: rhodecode/templates/followers/followers.html:13
+msgid "followers"
+msgstr "跟随者"
+
+#: rhodecode/templates/followers/followers_data.html:12
+#, fuzzy
+msgid "Started following -"
+msgstr "开始跟随"
+
+#: rhodecode/templates/forks/fork.html:5
+#, fuzzy, python-format
+msgid "%s Fork"
+msgstr "分支"
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr "分支名"
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr "私有"
+
+#: rhodecode/templates/forks/fork.html:77
+#, fuzzy
+msgid "Copy permissions"
+msgstr "权限"
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr "对该版本库建立分支"
+
+#: rhodecode/templates/forks/forks.html:5
+#, fuzzy, python-format
+msgid "%s Forks"
+msgstr "分支"
+
+#: rhodecode/templates/forks/forks.html:13
+msgid "forks"
+msgstr "分支"
+
+#: rhodecode/templates/forks/forks_data.html:17
+msgid "forked"
+msgstr "已有分支"
+
+#: rhodecode/templates/forks/forks_data.html:38
+msgid "There are no forks yet"
+msgstr "尚未有任何分支"
+
+#: rhodecode/templates/journal/journal.html:13
+#, fuzzy
+msgid "ATOM journal feed"
+msgstr "公共日志 %s %s 订阅"
+
+#: rhodecode/templates/journal/journal.html:14
+#, fuzzy
+msgid "RSS journal feed"
+msgstr "公共日志 %s %s 订阅"
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+#, fuzzy
+msgid "RSS feed"
+msgstr "%s %s 订阅"
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:41
+#, fuzzy
+msgid "Watched"
+msgstr "缓存"
+
+#: rhodecode/templates/journal/journal.html:46
+#, fuzzy
+msgid "ADD"
+msgstr "新增"
+
+#: rhodecode/templates/journal/journal.html:114
+msgid "following user"
+msgstr "跟随中用户"
+
+#: rhodecode/templates/journal/journal.html:114
+msgid "user"
+msgstr "用户"
+
+#: rhodecode/templates/journal/journal.html:147
+msgid "You are not following any users or repositories"
+msgstr "尚未跟随任何用户或版本库"
+
+#: rhodecode/templates/journal/journal_data.html:47
+msgid "No entries yet"
+msgstr ""
+
+#: rhodecode/templates/journal/public_journal.html:13
+#, fuzzy
+msgid "ATOM public journal feed"
+msgstr "公共日志 %s %s 订阅"
+
+#: rhodecode/templates/journal/public_journal.html:14
+#, fuzzy
+msgid "RSS public journal feed"
+msgstr "公共日志 %s %s 订阅"
+
+#: rhodecode/templates/journal/public_journal.html:21
+msgid "Public Journal"
+msgstr "公共日志"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+#, fuzzy
+msgid "Detailed compare view"
+msgstr "比较显示"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+#, fuzzy
+msgid "owner"
+msgstr "所有者"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+msgid "Add reviewer to this pull request."
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+#, fuzzy
+msgid "Create new pull request"
+msgstr "创建用户 %s"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+#, fuzzy
+msgid "Title"
+msgstr "写"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+#, fuzzy
+msgid "description"
+msgstr "描述"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+#, fuzzy
+msgid "Status"
+msgstr "变更集"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+msgid "Still not reviewed by"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+#, fuzzy
+msgid "Created on"
+msgstr "创建用户 %s"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+#, fuzzy
+msgid "Compare view"
+msgstr "比较显示"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+#, fuzzy
+msgid "Incoming changesets"
+msgstr "尚无修订"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+#, fuzzy
+msgid "all pull requests"
+msgstr "创建用户 %s"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, fuzzy, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr "在版本库:"
+
+#: rhodecode/templates/search/search.html:8
+#, fuzzy, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr "在所有的版本库"
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, fuzzy, python-format
+msgid "Search in repository: %s"
+msgstr "在版本库:"
+
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+#, fuzzy
+msgid "Search in all repositories"
+msgstr "在所有的版本库"
+
+#: rhodecode/templates/search/search.html:48
+msgid "Search term"
+msgstr "搜索短语"
+
+#: rhodecode/templates/search/search.html:60
+msgid "Search in"
+msgstr "搜索范围"
+
+#: rhodecode/templates/search/search.html:63
+msgid "File contents"
+msgstr "文件内容"
+
+#: rhodecode/templates/search/search.html:64
+#, fuzzy
+msgid "Commit messages"
+msgstr "提交信息"
+
+#: rhodecode/templates/search/search.html:65
+msgid "File names"
+msgstr "文件名"
+
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
+#: rhodecode/templates/search/search_path.html:15
+msgid "Permission denied"
+msgstr "权限不足"
+
+#: rhodecode/templates/settings/repo_settings.html:5
+#, fuzzy, python-format
+msgid "%s Settings"
+msgstr "设置"
+
+#: rhodecode/templates/shortlog/shortlog.html:5
+#, fuzzy, python-format
+msgid "%s Shortlog"
+msgstr "简短日志"
+
+#: rhodecode/templates/shortlog/shortlog.html:14
+msgid "shortlog"
+msgstr "简短日志"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:7
+msgid "age"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+#, fuzzy
+msgid "No commit message"
+msgstr "提交信息"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+#, fuzzy
+msgid "Existing repository?"
+msgstr "Git 版本库"
+
+#: rhodecode/templates/summary/summary.html:4
+#, fuzzy, python-format
+msgid "%s Summary"
+msgstr "概要"
+
+#: rhodecode/templates/summary/summary.html:12
+msgid "summary"
+msgstr "概要"
+
+#: rhodecode/templates/summary/summary.html:20
+#, fuzzy, python-format
+msgid "repo %s ATOM feed"
+msgstr "订阅 atom %s"
+
+#: rhodecode/templates/summary/summary.html:21
+#, fuzzy, python-format
+msgid "repo %s RSS feed"
+msgstr "订阅 rss %s"
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+#, fuzzy
+msgid "ATOM"
+msgstr "作者"
+
+#: rhodecode/templates/summary/summary.html:82
+#, fuzzy, python-format
+msgid "Non changable ID %s"
+msgstr "无变更"
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr "公共"
+
+#: rhodecode/templates/summary/summary.html:95
+msgid "remote clone"
+msgstr "远程 clone"
+
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
+msgstr "联系方式"
+
+#: rhodecode/templates/summary/summary.html:130
+msgid "Clone url"
+msgstr "clone 地址"
+
+#: rhodecode/templates/summary/summary.html:133
+msgid "Show by Name"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:142
+#, fuzzy
+msgid "Trending files"
+msgstr "编辑文件"
+
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
+msgstr "启用"
+
+#: rhodecode/templates/summary/summary.html:158
+msgid "Download"
+msgstr "下载"
+
+#: rhodecode/templates/summary/summary.html:162
+msgid "There are no downloads yet"
+msgstr "尚无任何下载"
+
+#: rhodecode/templates/summary/summary.html:164
+msgid "Downloads are disabled for this repository"
+msgstr "这个版本库的下载已经禁用"
+
+#: rhodecode/templates/summary/summary.html:170
+#, fuzzy
+msgid "Download as zip"
+msgstr "下载原始文件"
+
+#: rhodecode/templates/summary/summary.html:173
+msgid "Check this to download archive with subrepos"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:173
+msgid "with subrepos"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:186
+msgid "Commit activity by day / author"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
+msgstr "简短日志"
+
+#: rhodecode/templates/summary/summary.html:220
+#, fuzzy
+msgid "Quick start"
+msgstr "快速过滤..."
+
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr "下载 %s 作为 %s"
+
+#: rhodecode/templates/summary/summary.html:650
+msgid "commits"
+msgstr "提交"
+
+#: rhodecode/templates/summary/summary.html:651
+msgid "files added"
+msgstr "文件已添加"
+
+#: rhodecode/templates/summary/summary.html:652
+msgid "files changed"
+msgstr "文件已更改"
+
+#: rhodecode/templates/summary/summary.html:653
+msgid "files removed"
+msgstr "文件已删除"
+
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr "提交"
+
+#: rhodecode/templates/summary/summary.html:657
+msgid "file added"
+msgstr "文件已添加"
+
+#: rhodecode/templates/summary/summary.html:658
+msgid "file changed"
+msgstr "文件已更改"
+
+#: rhodecode/templates/summary/summary.html:659
+msgid "file removed"
+msgstr "文件已删除"
+
+#: rhodecode/templates/tags/tags.html:5
+#, fuzzy, python-format
+msgid "%s Tags"
+msgstr "之前"
+
diff --git a/rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po b/rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po
index d4f007fc..68c31fb3 100644
--- a/rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po
+++ b/rhodecode/i18n/zh_TW/LC_MESSAGES/rhodecode.po
@@ -1,4 +1,4 @@
-# Translations template for RhodeCode.
+# Chinese (Taiwan) translations for RhodeCode.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the RhodeCode project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
@@ -7,33 +7,49 @@ msgid ""
msgstr ""
"Project-Id-Version: RhodeCode 1.2.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2011-09-14 15:50-0300\n"
+"POT-Creation-Date: 2012-09-02 20:30+0200\n"
"PO-Revision-Date: 2012-05-09 22:23+0800\n"
"Last-Translator: Nansen <nansenat16@gmail.com>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language-Team: zh_TW <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
-"X-Poedit-Language: Chinese\n"
-"X-Poedit-Country: TAIWAN\n"
-"X-Poedit-SourceCharset: utf-8\n"
-
-#: rhodecode/controllers/changeset.py:108
-#: rhodecode/controllers/changeset.py:149
-#: rhodecode/controllers/changeset.py:216
-#: rhodecode/controllers/changeset.py:229
+
+#: rhodecode/controllers/changelog.py:94
+#, fuzzy
+msgid "All Branches"
+msgstr "分支"
+
+#: rhodecode/controllers/changeset.py:83
+msgid "show white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:90 rhodecode/controllers/changeset.py:97
+msgid "ignore white space"
+msgstr ""
+
+#: rhodecode/controllers/changeset.py:157
+#, fuzzy, python-format
+msgid "%s line context"
+msgstr "文件內容"
+
+#: rhodecode/controllers/changeset.py:333
+#: rhodecode/controllers/changeset.py:348 rhodecode/lib/diffs.py:70
msgid "binary file"
msgstr "二進位檔"
-#: rhodecode/controllers/changeset.py:123
-#: rhodecode/controllers/changeset.py:168
-msgid "Changeset is to big and was cut off, see raw changeset instead"
+#: rhodecode/controllers/changeset.py:408
+msgid ""
+"Changing status on a changeset associated witha closed pull request is "
+"not allowed"
msgstr ""
-#: rhodecode/controllers/changeset.py:159
-msgid "Diff is to big and was cut off, see raw diff instead"
-msgstr ""
+#: rhodecode/controllers/compare.py:69
+#, fuzzy
+msgid "There are no changesets yet"
+msgstr "尚未有任何變更"
#: rhodecode/controllers/error.py:69
msgid "Home page"
@@ -56,250 +72,313 @@ msgid "The resource could not be found"
msgstr "找不到這個資源"
#: rhodecode/controllers/error.py:107
-msgid "The server encountered an unexpected condition which prevented it from fulfilling the request."
+msgid ""
+"The server encountered an unexpected condition which prevented it from "
+"fulfilling the request."
msgstr ""
-#: rhodecode/controllers/feed.py:48
+#: rhodecode/controllers/feed.py:49
#, python-format
msgid "Changes on %s repository"
msgstr "修改於版本庫 %s"
-#: rhodecode/controllers/feed.py:49
+#: rhodecode/controllers/feed.py:50
#, python-format
msgid "%s %s feed"
msgstr ""
-#: rhodecode/controllers/files.py:72
-msgid "There are no files yet"
+#: rhodecode/controllers/feed.py:75
+#, fuzzy
+msgid "commited on"
+msgstr "遞交"
+
+#: rhodecode/controllers/files.py:84
+#, fuzzy
+msgid "click here to add new file"
+msgstr "新增使用者"
+
+#: rhodecode/controllers/files.py:85
+#, fuzzy, python-format
+msgid "There are no files yet %s"
msgstr "尚未有任何檔案"
-#: rhodecode/controllers/files.py:262
+#: rhodecode/controllers/files.py:239 rhodecode/controllers/files.py:299
+#, python-format
+msgid "This repository is has been locked by %s on %s"
+msgstr ""
+
+#: rhodecode/controllers/files.py:266
#, python-format
msgid "Edited %s via RhodeCode"
msgstr "使用 RhodeCode 編輯 %s"
-#: rhodecode/controllers/files.py:267
-#: rhodecode/templates/files/file_diff.html:40
+#: rhodecode/controllers/files.py:271
msgid "No changes"
msgstr "沒有修改"
-#: rhodecode/controllers/files.py:278
+#: rhodecode/controllers/files.py:282 rhodecode/controllers/files.py:346
#, python-format
msgid "Successfully committed to %s"
msgstr "成功遞交至 %s"
-#: rhodecode/controllers/files.py:283
+#: rhodecode/controllers/files.py:287 rhodecode/controllers/files.py:352
msgid "Error occurred during commit"
msgstr ""
-#: rhodecode/controllers/files.py:308
+#: rhodecode/controllers/files.py:318
+#, fuzzy, python-format
+msgid "Added %s via RhodeCode"
+msgstr "使用 RhodeCode 編輯 %s"
+
+#: rhodecode/controllers/files.py:332
+#, fuzzy
+msgid "No content"
+msgstr "文件內容"
+
+#: rhodecode/controllers/files.py:336
+#, fuzzy
+msgid "No filename"
+msgstr "檔案名稱"
+
+#: rhodecode/controllers/files.py:378
msgid "downloads disabled"
msgstr "下載已關閉"
-#: rhodecode/controllers/files.py:313
+#: rhodecode/controllers/files.py:389
#, python-format
msgid "Unknown revision %s"
msgstr "未知修訂 %s"
-#: rhodecode/controllers/files.py:315
+#: rhodecode/controllers/files.py:391
msgid "Empty repository"
msgstr "空的版本庫"
-#: rhodecode/controllers/files.py:317
+#: rhodecode/controllers/files.py:393
msgid "Unknown archive type"
msgstr "未知的存檔類型"
-#: rhodecode/controllers/files.py:385
-#: rhodecode/controllers/files.py:398
-msgid "Binary file"
-msgstr "二進位檔"
-
-#: rhodecode/controllers/files.py:417
-#: rhodecode/templates/changeset/changeset_range.html:4
-#: rhodecode/templates/changeset/changeset_range.html:12
-#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/controllers/files.py:494
+#: rhodecode/templates/changeset/changeset_range.html:13
+#: rhodecode/templates/changeset/changeset_range.html:31
msgid "Changesets"
msgstr "變更"
-#: rhodecode/controllers/files.py:418
-#: rhodecode/controllers/summary.py:175
-#: rhodecode/templates/branches/branches.html:5
-#: rhodecode/templates/summary/summary.html:690
+#: rhodecode/controllers/files.py:495 rhodecode/controllers/pullrequests.py:72
+#: rhodecode/controllers/summary.py:232 rhodecode/model/scm.py:543
msgid "Branches"
msgstr "分支"
-#: rhodecode/controllers/files.py:419
-#: rhodecode/controllers/summary.py:176
-#: rhodecode/templates/summary/summary.html:679
-#: rhodecode/templates/tags/tags.html:5
+#: rhodecode/controllers/files.py:496 rhodecode/controllers/pullrequests.py:76
+#: rhodecode/controllers/summary.py:233 rhodecode/model/scm.py:554
msgid "Tags"
msgstr "標籤"
-#: rhodecode/controllers/journal.py:50
+#: rhodecode/controllers/forks.py:73 rhodecode/controllers/admin/repos.py:90
#, python-format
-msgid "%s public journal %s feed"
-msgstr "%s 公開日誌 %s feed"
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
-#: rhodecode/controllers/journal.py:178
-#: rhodecode/controllers/journal.py:212
-#: rhodecode/templates/admin/repos/repo_edit.html:171
-#: rhodecode/templates/base/base.html:50
-msgid "Public journal"
+#: rhodecode/controllers/forks.py:133 rhodecode/controllers/settings.py:72
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the file system please run the application again in order to rescan "
+"repositories"
+msgstr ""
+
+#: rhodecode/controllers/forks.py:167
+#, python-format
+msgid "forked %s repository as %s"
+msgstr "forked %s 版本庫為 %s"
+
+#: rhodecode/controllers/forks.py:181
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr ""
+
+#: rhodecode/controllers/journal.py:202 rhodecode/controllers/journal.py:239
+#, fuzzy
+msgid "public journal"
msgstr "公開日誌"
-#: rhodecode/controllers/login.py:111
+#: rhodecode/controllers/journal.py:206 rhodecode/controllers/journal.py:243
+#: rhodecode/templates/base/base.html:220
+msgid "journal"
+msgstr "日誌"
+
+#: rhodecode/controllers/login.py:143
msgid "You have successfully registered into rhodecode"
msgstr "您已經成功註冊rhodecode"
-#: rhodecode/controllers/login.py:133
+#: rhodecode/controllers/login.py:164
msgid "Your password reset link was sent"
msgstr "您的密碼重設連結已寄出"
-#: rhodecode/controllers/login.py:155
-msgid "Your password reset was successful, new password has been sent to your email"
+#: rhodecode/controllers/login.py:184
+msgid ""
+"Your password reset was successful, new password has been sent to your "
+"email"
msgstr "您的密碼重設動作已完成,新的密碼已寄至您的信箱"
-#: rhodecode/controllers/search.py:109
+#: rhodecode/controllers/pullrequests.py:74 rhodecode/model/scm.py:549
+msgid "Bookmarks"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:158
+msgid "Pull request requires a title with min. 3 chars"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:160
+#, fuzzy
+msgid "error during creation of pull request"
+msgstr "建立使用者 %s"
+
+#: rhodecode/controllers/pullrequests.py:181
+#, fuzzy
+msgid "Successfully opened new pull request"
+msgstr "成功刪除使用者"
+
+#: rhodecode/controllers/pullrequests.py:184
+msgid "Error occurred during sending pull request"
+msgstr ""
+
+#: rhodecode/controllers/pullrequests.py:217
+#, fuzzy
+msgid "Successfully deleted pull request"
+msgstr "成功刪除使用者"
+
+#: rhodecode/controllers/search.py:131
msgid "Invalid search query. Try quoting it."
msgstr "無效的查詢。請使用跳脫字元"
-#: rhodecode/controllers/search.py:114
+#: rhodecode/controllers/search.py:136
msgid "There is no index to search in. Please run whoosh indexer"
msgstr "沒有任何索引可以搜尋。請執行 whoosh 建立索引"
-#: rhodecode/controllers/search.py:118
+#: rhodecode/controllers/search.py:140
msgid "An error occurred during this search operation"
msgstr ""
-#: rhodecode/controllers/settings.py:61
-#: rhodecode/controllers/settings.py:171
-#, python-format
-msgid "%s repository is not mapped to db perhaps it was created or renamed from the file system please run the application again in order to rescan repositories"
-msgstr ""
-
-#: rhodecode/controllers/settings.py:109
-#: rhodecode/controllers/admin/repos.py:239
+#: rhodecode/controllers/settings.py:107
+#: rhodecode/controllers/admin/repos.py:266
#, python-format
msgid "Repository %s updated successfully"
msgstr "版本庫 %s 更新完成"
-#: rhodecode/controllers/settings.py:126
-#: rhodecode/controllers/admin/repos.py:257
+#: rhodecode/controllers/settings.py:125
+#: rhodecode/controllers/admin/repos.py:284
#, python-format
msgid "error occurred during update of repository %s"
msgstr ""
-#: rhodecode/controllers/settings.py:144
-#: rhodecode/controllers/admin/repos.py:275
+#: rhodecode/controllers/settings.py:143
+#: rhodecode/controllers/admin/repos.py:302
#, python-format
-msgid "%s repository is not mapped to db perhaps it was moved or renamed from the filesystem please run the application again in order to rescan repositories"
+msgid ""
+"%s repository is not mapped to db perhaps it was moved or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
msgstr ""
-#: rhodecode/controllers/settings.py:156
-#: rhodecode/controllers/admin/repos.py:287
+#: rhodecode/controllers/settings.py:155
+#: rhodecode/controllers/admin/repos.py:314
#, python-format
msgid "deleted repository %s"
msgstr "刪除版本庫 %s"
#: rhodecode/controllers/settings.py:159
-#: rhodecode/controllers/admin/repos.py:297
-#: rhodecode/controllers/admin/repos.py:303
+#: rhodecode/controllers/admin/repos.py:324
+#: rhodecode/controllers/admin/repos.py:330
#, python-format
msgid "An error occurred during deletion of %s"
msgstr ""
-#: rhodecode/controllers/settings.py:193
-#, python-format
-msgid "forked %s repository as %s"
-msgstr "forked %s 版本庫為 %s"
-
-#: rhodecode/controllers/settings.py:211
-#, python-format
-msgid "An error occurred during repository forking %s"
-msgstr ""
-
-#: rhodecode/controllers/summary.py:123
+#: rhodecode/controllers/summary.py:138
msgid "No data loaded yet"
msgstr ""
-#: rhodecode/controllers/summary.py:126
+#: rhodecode/controllers/summary.py:142
+#: rhodecode/templates/summary/summary.html:148
msgid "Statistics are disabled for this repository"
msgstr "這個版本庫的統計功能已停用"
-#: rhodecode/controllers/admin/ldap_settings.py:49
+#: rhodecode/controllers/admin/ldap_settings.py:50
msgid "BASE"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:50
+#: rhodecode/controllers/admin/ldap_settings.py:51
msgid "ONELEVEL"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:51
+#: rhodecode/controllers/admin/ldap_settings.py:52
msgid "SUBTREE"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:55
+#: rhodecode/controllers/admin/ldap_settings.py:56
msgid "NEVER"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:56
+#: rhodecode/controllers/admin/ldap_settings.py:57
msgid "ALLOW"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:57
+#: rhodecode/controllers/admin/ldap_settings.py:58
msgid "TRY"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:58
+#: rhodecode/controllers/admin/ldap_settings.py:59
msgid "DEMAND"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:59
+#: rhodecode/controllers/admin/ldap_settings.py:60
msgid "HARD"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:63
+#: rhodecode/controllers/admin/ldap_settings.py:64
msgid "No encryption"
msgstr "無加密"
-#: rhodecode/controllers/admin/ldap_settings.py:64
+#: rhodecode/controllers/admin/ldap_settings.py:65
msgid "LDAPS connection"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:65
+#: rhodecode/controllers/admin/ldap_settings.py:66
msgid "START_TLS on LDAP connection"
msgstr ""
-#: rhodecode/controllers/admin/ldap_settings.py:115
+#: rhodecode/controllers/admin/ldap_settings.py:126
msgid "Ldap settings updated successfully"
msgstr "LDAP設定更新完成"
-#: rhodecode/controllers/admin/ldap_settings.py:120
+#: rhodecode/controllers/admin/ldap_settings.py:130
msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
msgstr "無法啟用LDAP。找不到python-ldap函式庫"
-#: rhodecode/controllers/admin/ldap_settings.py:134
+#: rhodecode/controllers/admin/ldap_settings.py:147
msgid "error occurred during update of ldap settings"
msgstr ""
-#: rhodecode/controllers/admin/permissions.py:56
+#: rhodecode/controllers/admin/permissions.py:59
msgid "None"
msgstr "無"
-#: rhodecode/controllers/admin/permissions.py:57
+#: rhodecode/controllers/admin/permissions.py:60
msgid "Read"
msgstr "讀"
-#: rhodecode/controllers/admin/permissions.py:58
+#: rhodecode/controllers/admin/permissions.py:61
msgid "Write"
msgstr "寫"
-#: rhodecode/controllers/admin/permissions.py:59
+#: rhodecode/controllers/admin/permissions.py:62
#: rhodecode/templates/admin/ldap/ldap.html:9
#: rhodecode/templates/admin/permissions/permissions.html:9
#: rhodecode/templates/admin/repos/repo_add.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:9
-#: rhodecode/templates/admin/repos/repos.html:10
+#: rhodecode/templates/admin/repos/repos.html:9
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:8
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
@@ -307,547 +386,900 @@ msgstr "寫"
#: rhodecode/templates/admin/settings/settings.html:9
#: rhodecode/templates/admin/users/user_add.html:8
#: rhodecode/templates/admin/users/user_edit.html:9
-#: rhodecode/templates/admin/users/user_edit.html:110
+#: rhodecode/templates/admin/users/user_edit.html:122
#: rhodecode/templates/admin/users/users.html:9
#: rhodecode/templates/admin/users_groups/users_group_add.html:8
#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
#: rhodecode/templates/admin/users_groups/users_groups.html:9
-#: rhodecode/templates/base/base.html:279
-#: rhodecode/templates/base/base.html:366
-#: rhodecode/templates/base/base.html:368
-#: rhodecode/templates/base/base.html:370
+#: rhodecode/templates/base/base.html:197
+#: rhodecode/templates/base/base.html:337
+#: rhodecode/templates/base/base.html:339
+#: rhodecode/templates/base/base.html:341
msgid "Admin"
msgstr "管理"
-#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/controllers/admin/permissions.py:65
msgid "disabled"
msgstr "停用"
-#: rhodecode/controllers/admin/permissions.py:64
+#: rhodecode/controllers/admin/permissions.py:67
msgid "allowed with manual account activation"
msgstr "允許手動啟用帳號"
-#: rhodecode/controllers/admin/permissions.py:66
+#: rhodecode/controllers/admin/permissions.py:69
msgid "allowed with automatic account activation"
msgstr "允許自動啟用帳號"
-#: rhodecode/controllers/admin/permissions.py:68
+#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:74
msgid "Disabled"
msgstr "停用"
-#: rhodecode/controllers/admin/permissions.py:69
+#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:75
msgid "Enabled"
msgstr "啟用"
-#: rhodecode/controllers/admin/permissions.py:102
+#: rhodecode/controllers/admin/permissions.py:116
msgid "Default permissions updated successfully"
msgstr "預設權限更新完成"
-#: rhodecode/controllers/admin/permissions.py:119
+#: rhodecode/controllers/admin/permissions.py:130
msgid "error occurred during update of permissions"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:96
-#, python-format
-msgid "%s repository is not mapped to db perhaps it was created or renamed from the filesystem please run the application again in order to rescan repositories"
+#: rhodecode/controllers/admin/repos.py:123
+msgid "--REMOVE FORK--"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:172
+#: rhodecode/controllers/admin/repos.py:192
#, python-format
msgid "created repository %s from %s"
msgstr "建立版本庫 %s 到 %s"
-#: rhodecode/controllers/admin/repos.py:176
+#: rhodecode/controllers/admin/repos.py:196
#, python-format
msgid "created repository %s"
msgstr "建立版本庫 %s"
-#: rhodecode/controllers/admin/repos.py:205
+#: rhodecode/controllers/admin/repos.py:227
#, python-format
msgid "error occurred during creation of repository %s"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:292
+#: rhodecode/controllers/admin/repos.py:319
#, python-format
msgid "Cannot delete %s it still contains attached forks"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:320
+#: rhodecode/controllers/admin/repos.py:348
msgid "An error occurred during deletion of repository user"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:335
+#: rhodecode/controllers/admin/repos.py:367
msgid "An error occurred during deletion of repository users groups"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:352
+#: rhodecode/controllers/admin/repos.py:385
msgid "An error occurred during deletion of repository stats"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:367
+#: rhodecode/controllers/admin/repos.py:402
msgid "An error occurred during cache invalidation"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:387
+#: rhodecode/controllers/admin/repos.py:422
+msgid "An error occurred during unlocking"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:442
msgid "Updated repository visibility in public journal"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:390
+#: rhodecode/controllers/admin/repos.py:446
msgid "An error occurred during setting this repository in public journal"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:395
-#: rhodecode/model/forms.py:53
+#: rhodecode/controllers/admin/repos.py:451 rhodecode/model/validators.py:299
msgid "Token mismatch"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:408
+#: rhodecode/controllers/admin/repos.py:464
msgid "Pulled from remote location"
msgstr ""
-#: rhodecode/controllers/admin/repos.py:410
+#: rhodecode/controllers/admin/repos.py:466
msgid "An error occurred during pull from remote location"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:83
+#: rhodecode/controllers/admin/repos.py:482
+msgid "Nothing"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:484
+#, fuzzy, python-format
+msgid "Marked repo %s as fork of %s"
+msgstr "建立版本庫 %s 到 %s"
+
+#: rhodecode/controllers/admin/repos.py:488
+msgid "An error occurred during this operation"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:116
#, python-format
msgid "created repos group %s"
msgstr "建立版本庫群組 %s"
-#: rhodecode/controllers/admin/repos_groups.py:96
+#: rhodecode/controllers/admin/repos_groups.py:129
#, python-format
msgid "error occurred during creation of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:130
+#: rhodecode/controllers/admin/repos_groups.py:163
#, python-format
msgid "updated repos group %s"
msgstr "更新版本庫群組 %s"
-#: rhodecode/controllers/admin/repos_groups.py:143
+#: rhodecode/controllers/admin/repos_groups.py:176
#, python-format
msgid "error occurred during update of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:164
+#: rhodecode/controllers/admin/repos_groups.py:194
#, python-format
msgid "This group contains %s repositores and cannot be deleted"
msgstr ""
-#: rhodecode/controllers/admin/repos_groups.py:171
+#: rhodecode/controllers/admin/repos_groups.py:202
#, python-format
msgid "removed repos group %s"
msgstr "移除版本庫群組 %s"
-#: rhodecode/controllers/admin/repos_groups.py:175
+#: rhodecode/controllers/admin/repos_groups.py:208
+msgid "Cannot delete this group it still contains subgroups"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:213
+#: rhodecode/controllers/admin/repos_groups.py:218
#, python-format
msgid "error occurred during deletion of repos group %s"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:109
+#: rhodecode/controllers/admin/repos_groups.py:238
+msgid "An error occurred during deletion of group user"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos_groups.py:258
+msgid "An error occurred during deletion of group users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:121
#, python-format
msgid "Repositories successfully rescanned added: %s,removed: %s"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:118
+#: rhodecode/controllers/admin/settings.py:129
msgid "Whoosh reindex task scheduled"
msgstr "Whoosh 重新索引工作排程"
-#: rhodecode/controllers/admin/settings.py:143
+#: rhodecode/controllers/admin/settings.py:160
msgid "Updated application settings"
msgstr "更新應用設定"
-#: rhodecode/controllers/admin/settings.py:148
-#: rhodecode/controllers/admin/settings.py:215
+#: rhodecode/controllers/admin/settings.py:164
+#: rhodecode/controllers/admin/settings.py:275
msgid "error occurred during updating application settings"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:210
-msgid "Updated mercurial settings"
+#: rhodecode/controllers/admin/settings.py:200
+#, fuzzy
+msgid "Updated visualisation settings"
+msgstr "更新應用設定"
+
+#: rhodecode/controllers/admin/settings.py:205
+msgid "error occurred during updating visualisation settings"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:271
+#, fuzzy
+msgid "Updated VCS settings"
msgstr "更新 mercurial 設定"
-#: rhodecode/controllers/admin/settings.py:236
+#: rhodecode/controllers/admin/settings.py:285
msgid "Added new hook"
msgstr "新增hook"
-#: rhodecode/controllers/admin/settings.py:247
+#: rhodecode/controllers/admin/settings.py:297
msgid "Updated hooks"
msgstr "更新hook"
-#: rhodecode/controllers/admin/settings.py:251
+#: rhodecode/controllers/admin/settings.py:301
msgid "error occurred during hook creation"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:310
+#: rhodecode/controllers/admin/settings.py:320
+msgid "Email task created"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:375
msgid "You can't edit this user since it's crucial for entire application"
msgstr ""
-#: rhodecode/controllers/admin/settings.py:339
+#: rhodecode/controllers/admin/settings.py:406
msgid "Your account was updated successfully"
msgstr "您的帳號已更新完成"
-#: rhodecode/controllers/admin/settings.py:359
-#: rhodecode/controllers/admin/users.py:130
+#: rhodecode/controllers/admin/settings.py:421
+#: rhodecode/controllers/admin/users.py:191
#, python-format
msgid "error occurred during update of user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:78
+#: rhodecode/controllers/admin/users.py:130
#, python-format
msgid "created user %s"
msgstr "建立使用者 %s"
-#: rhodecode/controllers/admin/users.py:90
+#: rhodecode/controllers/admin/users.py:142
#, python-format
msgid "error occurred during creation of user %s"
msgstr ""
-#: rhodecode/controllers/admin/users.py:116
+#: rhodecode/controllers/admin/users.py:171
msgid "User updated successfully"
msgstr "使用者更新完成"
-#: rhodecode/controllers/admin/users.py:146
+#: rhodecode/controllers/admin/users.py:207
msgid "successfully deleted user"
msgstr "成功刪除使用者"
-#: rhodecode/controllers/admin/users.py:150
+#: rhodecode/controllers/admin/users.py:212
msgid "An error occurred during deletion of user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:166
+#: rhodecode/controllers/admin/users.py:226
msgid "You can't edit this user"
msgstr "您無法編輯這位使用者"
-#: rhodecode/controllers/admin/users.py:195
-#: rhodecode/controllers/admin/users_groups.py:202
+#: rhodecode/controllers/admin/users.py:266
msgid "Granted 'repository create' permission to user"
msgstr ""
-#: rhodecode/controllers/admin/users.py:204
-#: rhodecode/controllers/admin/users_groups.py:211
+#: rhodecode/controllers/admin/users.py:271
msgid "Revoked 'repository create' permission to user"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:74
+#: rhodecode/controllers/admin/users.py:277
+#, fuzzy
+msgid "Granted 'repository fork' permission to user"
+msgstr "版本庫權限"
+
+#: rhodecode/controllers/admin/users.py:282
+#, fuzzy
+msgid "Revoked 'repository fork' permission to user"
+msgstr "版本庫權限"
+
+#: rhodecode/controllers/admin/users.py:288
+#: rhodecode/controllers/admin/users_groups.py:255
+msgid "An error occurred during permissions saving"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:303
+#, python-format
+msgid "Added email %s to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:309
+msgid "An error occurred during email saving"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:319
+#, fuzzy
+msgid "Removed email from user"
+msgstr "移除版本庫群組 %s"
+
+#: rhodecode/controllers/admin/users_groups.py:84
#, python-format
msgid "created users group %s"
msgstr "建立使用者群組 %s"
-#: rhodecode/controllers/admin/users_groups.py:86
+#: rhodecode/controllers/admin/users_groups.py:95
#, python-format
msgid "error occurred during creation of users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:119
+#: rhodecode/controllers/admin/users_groups.py:135
#, python-format
msgid "updated users group %s"
msgstr "更新使用者群組 %s"
-#: rhodecode/controllers/admin/users_groups.py:138
+#: rhodecode/controllers/admin/users_groups.py:157
#, python-format
msgid "error occurred during update of users group %s"
msgstr ""
-#: rhodecode/controllers/admin/users_groups.py:154
+#: rhodecode/controllers/admin/users_groups.py:174
msgid "successfully deleted users group"
msgstr "成功移除使用者群組"
-#: rhodecode/controllers/admin/users_groups.py:158
+#: rhodecode/controllers/admin/users_groups.py:179
msgid "An error occurred during deletion of users group"
msgstr ""
-#: rhodecode/lib/__init__.py:279
-msgid "year"
-msgstr "年"
-
-#: rhodecode/lib/__init__.py:280
-msgid "month"
-msgstr "月"
-
-#: rhodecode/lib/__init__.py:281
-msgid "day"
-msgstr "日"
-
-#: rhodecode/lib/__init__.py:282
-msgid "hour"
-msgstr "時"
-
-#: rhodecode/lib/__init__.py:283
-msgid "minute"
-msgstr "分"
+#: rhodecode/controllers/admin/users_groups.py:233
+msgid "Granted 'repository create' permission to users group"
+msgstr ""
-#: rhodecode/lib/__init__.py:284
-msgid "second"
-msgstr "秒"
+#: rhodecode/controllers/admin/users_groups.py:238
+msgid "Revoked 'repository create' permission to users group"
+msgstr ""
-#: rhodecode/lib/__init__.py:293
-msgid "ago"
-msgstr "之前"
+#: rhodecode/controllers/admin/users_groups.py:244
+msgid "Granted 'repository fork' permission to users group"
+msgstr ""
-#: rhodecode/lib/__init__.py:296
-msgid "just now"
-msgstr "現在"
+#: rhodecode/controllers/admin/users_groups.py:249
+msgid "Revoked 'repository fork' permission to users group"
+msgstr ""
-#: rhodecode/lib/auth.py:377
+#: rhodecode/lib/auth.py:499
msgid "You need to be a registered user to perform this action"
msgstr "您必須是註冊使用者才能執行這個動作"
-#: rhodecode/lib/auth.py:421
+#: rhodecode/lib/auth.py:540
msgid "You need to be a signed in to view this page"
msgstr "您必須登入後才能瀏覽這個頁面"
-#: rhodecode/lib/helpers.py:307
+#: rhodecode/lib/diffs.py:86
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
+msgstr ""
+
+#: rhodecode/lib/diffs.py:96
+msgid "No changes detected"
+msgstr "尚未有任何變更"
+
+#: rhodecode/lib/helpers.py:372
+#, python-format
+msgid "%a, %d %b %Y %H:%M:%S"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:484
msgid "True"
msgstr "真"
-#: rhodecode/lib/helpers.py:311
+#: rhodecode/lib/helpers.py:488
msgid "False"
msgstr "假"
-#: rhodecode/lib/helpers.py:352
+#: rhodecode/lib/helpers.py:532
+#, fuzzy
+msgid "Changeset not found"
+msgstr "修改"
+
+#: rhodecode/lib/helpers.py:555
#, python-format
msgid "Show all combined changesets %s->%s"
msgstr ""
-#: rhodecode/lib/helpers.py:356
+#: rhodecode/lib/helpers.py:561
msgid "compare view"
msgstr ""
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:581
msgid "and"
msgstr "和"
-#: rhodecode/lib/helpers.py:365
+#: rhodecode/lib/helpers.py:582
#, python-format
msgid "%s more"
msgstr ""
-#: rhodecode/lib/helpers.py:367
-#: rhodecode/templates/changelog/changelog.html:14
-#: rhodecode/templates/changelog/changelog.html:39
+#: rhodecode/lib/helpers.py:583 rhodecode/templates/changelog/changelog.html:48
msgid "revisions"
msgstr "修訂"
-#: rhodecode/lib/helpers.py:385
+#: rhodecode/lib/helpers.py:606
msgid "fork name "
msgstr "fork 名稱"
-#: rhodecode/lib/helpers.py:388
+#: rhodecode/lib/helpers.py:620
+#: rhodecode/templates/pullrequests/pullrequest_show.html:4
+#: rhodecode/templates/pullrequests/pullrequest_show.html:12
+#, python-format
+msgid "Pull request #%s"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:626
msgid "[deleted] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:389
-#: rhodecode/lib/helpers.py:393
+#: rhodecode/lib/helpers.py:628 rhodecode/lib/helpers.py:638
msgid "[created] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:390
-#: rhodecode/lib/helpers.py:394
+#: rhodecode/lib/helpers.py:630
+#, fuzzy
+msgid "[created] repository as fork"
+msgstr "建立版本庫 %s"
+
+#: rhodecode/lib/helpers.py:632 rhodecode/lib/helpers.py:640
msgid "[forked] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:391
-#: rhodecode/lib/helpers.py:395
+#: rhodecode/lib/helpers.py:634 rhodecode/lib/helpers.py:642
msgid "[updated] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:392
+#: rhodecode/lib/helpers.py:636
msgid "[delete] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:396
+#: rhodecode/lib/helpers.py:644
+#, fuzzy
+msgid "[created] user"
+msgstr "建立使用者 %s"
+
+#: rhodecode/lib/helpers.py:646
+#, fuzzy
+msgid "[updated] user"
+msgstr "更新使用者群組 %s"
+
+#: rhodecode/lib/helpers.py:648
+#, fuzzy
+msgid "[created] users group"
+msgstr "建立使用者群組 %s"
+
+#: rhodecode/lib/helpers.py:650
+#, fuzzy
+msgid "[updated] users group"
+msgstr "更新使用者群組 %s"
+
+#: rhodecode/lib/helpers.py:652
+msgid "[commented] on revision in repository"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:654
+#, fuzzy
+msgid "[commented] on pull request for"
+msgstr "建立使用者 %s"
+
+#: rhodecode/lib/helpers.py:656
+msgid "[closed] pull request for"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:658
msgid "[pushed] into"
msgstr ""
-#: rhodecode/lib/helpers.py:397
-msgid "[committed via RhodeCode] into"
+#: rhodecode/lib/helpers.py:660
+msgid "[committed via RhodeCode] into repository"
msgstr ""
-#: rhodecode/lib/helpers.py:398
-msgid "[pulled from remote] into"
+#: rhodecode/lib/helpers.py:662
+msgid "[pulled from remote] into repository"
msgstr ""
-#: rhodecode/lib/helpers.py:399
+#: rhodecode/lib/helpers.py:664
msgid "[pulled] from"
msgstr ""
-#: rhodecode/lib/helpers.py:400
+#: rhodecode/lib/helpers.py:666
msgid "[started following] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:401
+#: rhodecode/lib/helpers.py:668
msgid "[stopped following] repository"
msgstr ""
-#: rhodecode/lib/helpers.py:577
+#: rhodecode/lib/helpers.py:840
#, python-format
msgid " and %s more"
msgstr ""
-#: rhodecode/lib/helpers.py:581
+#: rhodecode/lib/helpers.py:844
msgid "No Files"
msgstr "沒有檔案"
-#: rhodecode/model/forms.py:66
-msgid "Invalid username"
-msgstr "無效的使用者名稱"
+#: rhodecode/lib/utils2.py:335
+#, fuzzy, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] "年"
+
+#: rhodecode/lib/utils2.py:336
+#, fuzzy, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] "月"
+
+#: rhodecode/lib/utils2.py:337
+#, fuzzy, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] "日"
+
+#: rhodecode/lib/utils2.py:338
+#, fuzzy, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] "時"
+
+#: rhodecode/lib/utils2.py:339
+#, fuzzy, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] "分"
+
+#: rhodecode/lib/utils2.py:340
+#, fuzzy, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] "秒"
+
+#: rhodecode/lib/utils2.py:355
+#, fuzzy, python-format
+msgid "%s ago"
+msgstr "之前"
+
+#: rhodecode/lib/utils2.py:357
+#, python-format
+msgid "%s and %s ago"
+msgstr ""
+
+#: rhodecode/lib/utils2.py:360
+msgid "just now"
+msgstr "現在"
+
+#: rhodecode/lib/celerylib/tasks.py:269
+#, fuzzy
+msgid "password reset link"
+msgstr "您的密碼重設連結已寄出"
+
+#: rhodecode/model/comment.py:110
+#, python-format
+msgid "on line %s"
+msgstr ""
+
+#: rhodecode/model/comment.py:157
+msgid "[Mention]"
+msgstr ""
+
+#: rhodecode/model/db.py:1140
+#, fuzzy
+msgid "Repository no access"
+msgstr "個版本庫"
+
+#: rhodecode/model/db.py:1141
+#, fuzzy
+msgid "Repository read access"
+msgstr "這個版本庫已經存在"
+
+#: rhodecode/model/db.py:1142
+#, fuzzy
+msgid "Repository write access"
+msgstr "個版本庫"
+
+#: rhodecode/model/db.py:1143
+#, fuzzy
+msgid "Repository admin access"
+msgstr "個版本庫"
+
+#: rhodecode/model/db.py:1145
+#, fuzzy
+msgid "Repositories Group no access"
+msgstr "版本庫群組"
+
+#: rhodecode/model/db.py:1146
+#, fuzzy
+msgid "Repositories Group read access"
+msgstr "版本庫群組"
+
+#: rhodecode/model/db.py:1147
+#, fuzzy
+msgid "Repositories Group write access"
+msgstr "版本庫群組"
+
+#: rhodecode/model/db.py:1148
+#, fuzzy
+msgid "Repositories Group admin access"
+msgstr "版本庫群組"
+
+#: rhodecode/model/db.py:1150
+#, fuzzy
+msgid "RhodeCode Administrator"
+msgstr "使用者管理員"
+
+#: rhodecode/model/db.py:1151
+#, fuzzy
+msgid "Repository creation disabled"
+msgstr "版本庫建立"
+
+#: rhodecode/model/db.py:1152
+#, fuzzy
+msgid "Repository creation enabled"
+msgstr "版本庫建立"
+
+#: rhodecode/model/db.py:1153
+#, fuzzy
+msgid "Repository forking disabled"
+msgstr "版本庫建立"
+
+#: rhodecode/model/db.py:1154
+#, fuzzy
+msgid "Repository forking enabled"
+msgstr "版本庫建立"
-#: rhodecode/model/forms.py:75
-msgid "This username already exists"
+#: rhodecode/model/db.py:1155
+#, fuzzy
+msgid "Register disabled"
+msgstr "停用"
+
+#: rhodecode/model/db.py:1156
+msgid "Register new user with RhodeCode with manual activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1159
+msgid "Register new user with RhodeCode with auto activation"
+msgstr ""
+
+#: rhodecode/model/db.py:1579
+msgid "Not Reviewed"
+msgstr ""
+
+#: rhodecode/model/db.py:1580
+#, fuzzy
+msgid "Approved"
+msgstr "移除"
+
+#: rhodecode/model/db.py:1581
+msgid "Rejected"
+msgstr ""
+
+#: rhodecode/model/db.py:1582
+msgid "Under Review"
+msgstr ""
+
+#: rhodecode/model/forms.py:43
+msgid "Please enter a login"
+msgstr "請登入"
+
+#: rhodecode/model/forms.py:44
+#, python-format
+msgid "Enter a value %(min)i characters long or more"
+msgstr ""
+
+#: rhodecode/model/forms.py:52
+msgid "Please enter a password"
+msgstr "請輸入密碼"
+
+#: rhodecode/model/forms.py:53
+#, python-format
+msgid "Enter %(min)i characters or more"
+msgstr ""
+
+#: rhodecode/model/notification.py:220
+msgid "commented on commit"
+msgstr ""
+
+#: rhodecode/model/notification.py:221
+#, fuzzy
+msgid "sent message"
+msgstr "遞交資訊"
+
+#: rhodecode/model/notification.py:222
+msgid "mentioned you"
+msgstr ""
+
+#: rhodecode/model/notification.py:223
+#, fuzzy
+msgid "registered in RhodeCode"
+msgstr "您已經成功註冊rhodecode"
+
+#: rhodecode/model/notification.py:224
+msgid "opened new pull request"
+msgstr ""
+
+#: rhodecode/model/notification.py:225
+msgid "commented on pull request"
+msgstr ""
+
+#: rhodecode/model/pull_request.py:84
+#, python-format
+msgid "%(user)s wants you to review pull request #%(pr_id)s"
+msgstr ""
+
+#: rhodecode/model/scm.py:535
+#, fuzzy
+msgid "latest tip"
+msgstr "最後登入"
+
+#: rhodecode/model/user.py:230
+#, fuzzy
+msgid "new user registration"
+msgstr "[RhodeCode] 新使用者註冊"
+
+#: rhodecode/model/user.py:255 rhodecode/model/user.py:277
+#: rhodecode/model/user.py:299
+msgid "You can't Edit this user since it's crucial for entire application"
+msgstr "您無法編輯這個使用者,因為他是系統帳號"
+
+#: rhodecode/model/user.py:323
+msgid "You can't remove this user since it's crucial for entire application"
+msgstr "您無法移除這個使用者,因為他是系統帳號"
+
+#: rhodecode/model/user.py:329
+#, fuzzy, python-format
+msgid ""
+"user \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories. %s"
+msgstr "這個使用者擁有 %s 個版本庫所以無法移除,請先變更版本庫擁有者或者刪除版本庫"
+
+#: rhodecode/model/validators.py:35 rhodecode/model/validators.py:36
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: rhodecode/model/validators.py:82
+#, fuzzy, python-format
+msgid "Username \"%(username)s\" already exists"
msgstr "使用者名稱已存在"
-#: rhodecode/model/forms.py:79
-msgid "Username may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
+#: rhodecode/model/validators.py:84
+#, python-format
+msgid "Username \"%(username)s\" is forbidden"
+msgstr ""
+
+#: rhodecode/model/validators.py:86
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with alphanumeric character"
msgstr "使用者名稱只能使用字母數字、底線、小數點或破折號,且必須使用數字或字母開頭"
-#: rhodecode/model/forms.py:94
-msgid "Invalid group name"
-msgstr "無效的群組名稱"
+#: rhodecode/model/validators.py:114
+#, fuzzy, python-format
+msgid "Username %(username)s is not valid"
+msgstr "使用者名稱或群組名稱無效"
+
+#: rhodecode/model/validators.py:133
+#, fuzzy
+msgid "Invalid users group name"
+msgstr "無效的使用者名稱"
-#: rhodecode/model/forms.py:104
-msgid "This users group already exists"
+#: rhodecode/model/validators.py:134
+#, fuzzy, python-format
+msgid "Users group \"%(usersgroup)s\" already exists"
msgstr "這個使用者群組已存在"
-#: rhodecode/model/forms.py:110
-msgid "Group name may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
+#: rhodecode/model/validators.py:136
+msgid ""
+"users group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
msgstr "群組名稱只能使用字母數字、底線、小數點或破折號,且必須使用數字或字母開頭"
-#: rhodecode/model/forms.py:132
+#: rhodecode/model/validators.py:174
msgid "Cannot assign this group as parent"
msgstr ""
-#: rhodecode/model/forms.py:148
-msgid "This group already exists"
-msgstr "這個群組已存在"
+#: rhodecode/model/validators.py:175
+#, fuzzy, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr "使用者名稱已存在"
+
+#: rhodecode/model/validators.py:177
+#, fuzzy, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr "這個版本庫已經存在"
-#: rhodecode/model/forms.py:164
-#: rhodecode/model/forms.py:172
-#: rhodecode/model/forms.py:180
-msgid "Invalid characters in password"
+#: rhodecode/model/validators.py:235
+#, fuzzy
+msgid "Invalid characters (non-ascii) in password"
msgstr "無效的字元在密碼中"
-#: rhodecode/model/forms.py:191
+#: rhodecode/model/validators.py:250
msgid "Passwords do not match"
msgstr "密碼不相符"
-#: rhodecode/model/forms.py:196
+#: rhodecode/model/validators.py:267
msgid "invalid password"
msgstr "無效的密碼"
-#: rhodecode/model/forms.py:197
+#: rhodecode/model/validators.py:268
msgid "invalid user name"
msgstr "無效的使用者名稱"
-#: rhodecode/model/forms.py:198
+#: rhodecode/model/validators.py:269
msgid "Your account is disabled"
msgstr "您的帳號已被停用"
-#: rhodecode/model/forms.py:233
-msgid "This username is not valid"
-msgstr "無效的使用者名稱"
-
-#: rhodecode/model/forms.py:245
-msgid "This repository name is disallowed"
+#: rhodecode/model/validators.py:313
+#, fuzzy, python-format
+msgid "Repository name %(repo)s is disallowed"
msgstr "不允許的版本庫名稱"
-#: rhodecode/model/forms.py:266
-#, python-format
-msgid "This repository already exists in group \"%s\""
+#: rhodecode/model/validators.py:315
+#, fuzzy, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr "這個版本庫已經存在"
+
+#: rhodecode/model/validators.py:316
+#, fuzzy, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
msgstr "這個版本庫已存在於群組 \"%s\""
-#: rhodecode/model/forms.py:274
-msgid "This repository already exists"
+#: rhodecode/model/validators.py:318
+#, fuzzy, python-format
+msgid "Repositories group with name \"%(repo)s\" already exists"
msgstr "這個版本庫已經存在"
-#: rhodecode/model/forms.py:312
-#: rhodecode/model/forms.py:319
+#: rhodecode/model/validators.py:431
msgid "invalid clone url"
msgstr "無效的複製URL"
-#: rhodecode/model/forms.py:322
-msgid "Invalid clone url, provide a valid clone http\\s url"
+#: rhodecode/model/validators.py:432
+msgid "Invalid clone url, provide a valid clone http(s)/svn+http(s) url"
msgstr ""
-#: rhodecode/model/forms.py:334
-msgid "Fork have to be the same type as original"
+#: rhodecode/model/validators.py:457
+#, fuzzy
+msgid "Fork have to be the same type as parent"
msgstr "Fork 必須使用相同的版本庫類型"
-#: rhodecode/model/forms.py:341
+#: rhodecode/model/validators.py:478
msgid "This username or users group name is not valid"
msgstr "使用者名稱或群組名稱無效"
-#: rhodecode/model/forms.py:403
+#: rhodecode/model/validators.py:562
msgid "This is not a valid path"
msgstr "不是一個有效的路徑"
-#: rhodecode/model/forms.py:416
+#: rhodecode/model/validators.py:577
msgid "This e-mail address is already taken"
msgstr "這個郵件位址已經使用了"
-#: rhodecode/model/forms.py:427
-msgid "This e-mail address doesn't exist."
+#: rhodecode/model/validators.py:597
+#, fuzzy, python-format
+msgid "e-mail \"%(email)s\" does not exist."
msgstr "這個郵件位址不存在"
-#: rhodecode/model/forms.py:447
-msgid "The LDAP Login attribute of the CN must be specified - this is the name of the attribute that is equivalent to 'username'"
+#: rhodecode/model/validators.py:634
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
msgstr ""
-#: rhodecode/model/forms.py:466
-msgid "Please enter a login"
-msgstr "請登入"
-
-#: rhodecode/model/forms.py:467
+#: rhodecode/model/validators.py:653
#, python-format
-msgid "Enter a value %(min)i characters long or more"
+msgid "Revisions %(revs)s are already part of pull request or have set status"
msgstr ""
-#: rhodecode/model/forms.py:475
-msgid "Please enter a password"
-msgstr "請輸入密碼"
-
-#: rhodecode/model/forms.py:476
-#, python-format
-msgid "Enter %(min)i characters or more"
-msgstr ""
-
-#: rhodecode/model/user.py:145
-msgid "[RhodeCode] New User registration"
-msgstr "[RhodeCode] 新使用者註冊"
-
-#: rhodecode/model/user.py:157
-#: rhodecode/model/user.py:179
-msgid "You can't Edit this user since it's crucial for entire application"
-msgstr "您無法編輯這個使用者,因為他是系統帳號"
-
-#: rhodecode/model/user.py:201
-msgid "You can't remove this user since it's crucial for entire application"
-msgstr "您無法移除這個使用者,因為他是系統帳號"
-
-#: rhodecode/model/user.py:204
-#, python-format
-msgid "This user still owns %s repositories and cannot be removed. Switch owners or remove those repositories"
-msgstr "這個使用者擁有 %s 個版本庫所以無法移除,請先變更版本庫擁有者或者刪除版本庫"
-
-#: rhodecode/templates/index.html:4
+#: rhodecode/templates/index.html:3
msgid "Dashboard"
msgstr "儀表板"
-#: rhodecode/templates/index_base.html:22
-#: rhodecode/templates/admin/users/user_edit_my_account.html:102
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/repo_switcher_list.html:4
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/admin/users/user_edit_my_account.html:31
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/bookmarks/bookmarks.html:10
+#: rhodecode/templates/branches/branches.html:9
+#: rhodecode/templates/journal/journal.html:40
+#: rhodecode/templates/tags/tags.html:10
msgid "quick filter..."
msgstr "快速過濾..."
-#: rhodecode/templates/index_base.html:23
-#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/index_base.html:6
+#: rhodecode/templates/admin/repos/repos.html:9
+#: rhodecode/templates/base/base.html:221
msgid "repositories"
msgstr "個版本庫"
-#: rhodecode/templates/index_base.html:29
-#: rhodecode/templates/admin/repos/repos.html:22
-msgid "ADD NEW REPOSITORY"
+#: rhodecode/templates/index_base.html:13
+#: rhodecode/templates/index_base.html:15
+#: rhodecode/templates/admin/repos/repos.html:21
+msgid "ADD REPOSITORY"
msgstr "新增版本庫"
-#: rhodecode/templates/index_base.html:41
+#: rhodecode/templates/index_base.html:29
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:32
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:33
@@ -856,159 +1288,161 @@ msgstr "新增版本庫"
msgid "Group name"
msgstr "群組名稱"
-#: rhodecode/templates/index_base.html:42
-#: rhodecode/templates/index_base.html:73
-#: rhodecode/templates/admin/repos/repo_add_base.html:44
-#: rhodecode/templates/admin/repos/repo_edit.html:64
-#: rhodecode/templates/admin/repos/repos.html:31
+#: rhodecode/templates/index_base.html:30
+#: rhodecode/templates/index_base.html:71
+#: rhodecode/templates/index_base.html:142
+#: rhodecode/templates/index_base.html:168
+#: rhodecode/templates/admin/repos/repo_add_base.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:75
+#: rhodecode/templates/admin/repos/repos.html:72
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:41
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:34
-#: rhodecode/templates/settings/repo_fork.html:40
-#: rhodecode/templates/settings/repo_settings.html:40
-#: rhodecode/templates/summary/summary.html:92
+#: rhodecode/templates/forks/fork.html:59
+#: rhodecode/templates/settings/repo_settings.html:66
+#: rhodecode/templates/summary/summary.html:105
msgid "Description"
msgstr "描述"
-#: rhodecode/templates/index_base.html:53
+#: rhodecode/templates/index_base.html:40
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:46
msgid "Repositories group"
msgstr "版本庫群組"
-#: rhodecode/templates/index_base.html:72
+#: rhodecode/templates/index_base.html:70
+#: rhodecode/templates/index_base.html:166
#: rhodecode/templates/admin/repos/repo_add_base.html:9
#: rhodecode/templates/admin/repos/repo_edit.html:32
-#: rhodecode/templates/admin/repos/repos.html:30
-#: rhodecode/templates/admin/users/user_edit_my_account.html:117
-#: rhodecode/templates/files/files_browser.html:157
+#: rhodecode/templates/admin/repos/repos.html:70
+#: rhodecode/templates/admin/users/user_edit.html:192
+#: rhodecode/templates/admin/users/user_edit_my_account.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:6
+#: rhodecode/templates/bookmarks/bookmarks.html:36
+#: rhodecode/templates/bookmarks/bookmarks_data.html:6
+#: rhodecode/templates/branches/branches.html:51
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/journal/journal.html:59
+#: rhodecode/templates/journal/journal.html:107
+#: rhodecode/templates/journal/journal.html:186
#: rhodecode/templates/settings/repo_settings.html:31
-#: rhodecode/templates/summary/summary.html:31
-#: rhodecode/templates/summary/summary.html:107
+#: rhodecode/templates/summary/summary.html:43
+#: rhodecode/templates/summary/summary.html:123
+#: rhodecode/templates/tags/tags.html:36
+#: rhodecode/templates/tags/tags_data.html:6
msgid "Name"
msgstr "名稱"
-#: rhodecode/templates/index_base.html:74
-#: rhodecode/templates/admin/repos/repos.html:32
-#: rhodecode/templates/summary/summary.html:114
+#: rhodecode/templates/index_base.html:72
msgid "Last change"
msgstr "最後修改"
-#: rhodecode/templates/index_base.html:75
-#: rhodecode/templates/admin/repos/repos.html:33
+#: rhodecode/templates/index_base.html:73
+#: rhodecode/templates/index_base.html:171
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/journal/journal.html:188
msgid "Tip"
msgstr ""
-#: rhodecode/templates/index_base.html:76
-#: rhodecode/templates/admin/repos/repo_edit.html:97
+#: rhodecode/templates/index_base.html:74
+#: rhodecode/templates/index_base.html:173
+#: rhodecode/templates/admin/repos/repo_edit.html:121
+#: rhodecode/templates/admin/repos/repos.html:73
msgid "Owner"
msgstr "擁有者"
-#: rhodecode/templates/index_base.html:77
-#: rhodecode/templates/journal/public_journal.html:20
-#: rhodecode/templates/summary/summary.html:180
-#: rhodecode/templates/summary/summary.html:183
-msgid "RSS"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:78
-#: rhodecode/templates/journal/public_journal.html:23
-#: rhodecode/templates/summary/summary.html:181
-#: rhodecode/templates/summary/summary.html:184
-msgid "Atom"
-msgstr ""
-
-#: rhodecode/templates/index_base.html:87
-#: rhodecode/templates/index_base.html:89
-#: rhodecode/templates/index_base.html:91
-#: rhodecode/templates/base/base.html:209
-#: rhodecode/templates/base/base.html:211
-#: rhodecode/templates/base/base.html:213
-#: rhodecode/templates/summary/summary.html:4
-msgid "Summary"
-msgstr "概況"
-
-#: rhodecode/templates/index_base.html:95
-#: rhodecode/templates/index_base.html:97
-#: rhodecode/templates/index_base.html:99
-#: rhodecode/templates/base/base.html:225
-#: rhodecode/templates/base/base.html:227
-#: rhodecode/templates/base/base.html:229
-#: rhodecode/templates/changelog/changelog.html:6
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "Changelog"
-msgstr "修改紀錄"
-
-#: rhodecode/templates/index_base.html:103
-#: rhodecode/templates/index_base.html:105
-#: rhodecode/templates/index_base.html:107
-#: rhodecode/templates/base/base.html:268
-#: rhodecode/templates/base/base.html:270
-#: rhodecode/templates/base/base.html:272
-#: rhodecode/templates/files/files.html:4
-msgid "Files"
-msgstr "檔案"
-
-#: rhodecode/templates/index_base.html:116
-#: rhodecode/templates/admin/repos/repos.html:42
-#: rhodecode/templates/admin/users/user_edit_my_account.html:127
+#: rhodecode/templates/index_base.html:75
#: rhodecode/templates/summary/summary.html:48
-msgid "Mercurial repository"
-msgstr "Mercurial 版本庫"
-
-#: rhodecode/templates/index_base.html:118
-#: rhodecode/templates/admin/repos/repos.html:44
-#: rhodecode/templates/admin/users/user_edit_my_account.html:129
#: rhodecode/templates/summary/summary.html:51
-msgid "Git repository"
-msgstr "Git 版本庫"
-
-#: rhodecode/templates/index_base.html:123
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
-#: rhodecode/templates/journal/journal.html:53
-#: rhodecode/templates/summary/summary.html:56
-msgid "private repository"
-msgstr "私有版本庫"
-
-#: rhodecode/templates/index_base.html:125
-#: rhodecode/templates/journal/journal.html:55
-#: rhodecode/templates/summary/summary.html:58
-msgid "public repository"
-msgstr "公開版本庫"
-
-#: rhodecode/templates/index_base.html:133
-#: rhodecode/templates/base/base.html:291
-#: rhodecode/templates/settings/repo_fork.html:13
-msgid "fork"
+msgid "RSS"
msgstr ""
-#: rhodecode/templates/index_base.html:134
-#: rhodecode/templates/admin/repos/repos.html:60
-#: rhodecode/templates/admin/users/user_edit_my_account.html:143
-#: rhodecode/templates/summary/summary.html:69
-#: rhodecode/templates/summary/summary.html:71
-msgid "Fork of"
+#: rhodecode/templates/index_base.html:76
+msgid "Atom"
msgstr ""
-#: rhodecode/templates/index_base.html:155
-#: rhodecode/templates/admin/repos/repos.html:73
-msgid "No changesets yet"
-msgstr "尚未有任何變更"
-
-#: rhodecode/templates/index_base.html:161
-#: rhodecode/templates/index_base.html:163
+#: rhodecode/templates/index_base.html:110
+#: rhodecode/templates/index_base.html:112
#, python-format
msgid "Subscribe to %s rss feed"
msgstr "訂閱 %s rss"
-#: rhodecode/templates/index_base.html:168
-#: rhodecode/templates/index_base.html:170
+#: rhodecode/templates/index_base.html:117
+#: rhodecode/templates/index_base.html:119
#, python-format
msgid "Subscribe to %s atom feed"
msgstr "訂閱 %s atom"
-#: rhodecode/templates/login.html:5
-#: rhodecode/templates/login.html:54
-#: rhodecode/templates/base/base.html:38
+#: rhodecode/templates/index_base.html:140
+#, fuzzy
+msgid "Group Name"
+msgstr "群組名稱"
+
+#: rhodecode/templates/index_base.html:158
+#: rhodecode/templates/index_base.html:198
+#: rhodecode/templates/admin/repos/repos.html:94
+#: rhodecode/templates/admin/users/user_edit_my_account.html:179
+#: rhodecode/templates/admin/users/users.html:107
+#: rhodecode/templates/bookmarks/bookmarks.html:60
+#: rhodecode/templates/branches/branches.html:77
+#: rhodecode/templates/journal/journal.html:211
+#: rhodecode/templates/tags/tags.html:60
+msgid "Click to sort ascending"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:159
+#: rhodecode/templates/index_base.html:199
+#: rhodecode/templates/admin/repos/repos.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account.html:180
+#: rhodecode/templates/admin/users/users.html:108
+#: rhodecode/templates/bookmarks/bookmarks.html:61
+#: rhodecode/templates/branches/branches.html:78
+#: rhodecode/templates/journal/journal.html:212
+#: rhodecode/templates/tags/tags.html:61
+msgid "Click to sort descending"
+msgstr ""
+
+#: rhodecode/templates/index_base.html:169
+#, fuzzy
+msgid "Last Change"
+msgstr "最後修改"
+
+#: rhodecode/templates/index_base.html:200
+#: rhodecode/templates/admin/repos/repos.html:96
+#: rhodecode/templates/admin/users/user_edit_my_account.html:181
+#: rhodecode/templates/admin/users/users.html:109
+#: rhodecode/templates/bookmarks/bookmarks.html:62
+#: rhodecode/templates/branches/branches.html:79
+#: rhodecode/templates/journal/journal.html:213
+#: rhodecode/templates/tags/tags.html:62
+msgid "No records found."
+msgstr ""
+
+#: rhodecode/templates/index_base.html:201
+#: rhodecode/templates/admin/repos/repos.html:97
+#: rhodecode/templates/admin/users/user_edit_my_account.html:182
+#: rhodecode/templates/admin/users/users.html:110
+#: rhodecode/templates/bookmarks/bookmarks.html:63
+#: rhodecode/templates/branches/branches.html:80
+#: rhodecode/templates/journal/journal.html:214
+#: rhodecode/templates/tags/tags.html:63
+msgid "Data error."
+msgstr ""
+
+#: rhodecode/templates/index_base.html:202
+#: rhodecode/templates/admin/repos/repos.html:98
+#: rhodecode/templates/admin/users/user_edit_my_account.html:183
+#: rhodecode/templates/admin/users/users.html:111
+#: rhodecode/templates/bookmarks/bookmarks.html:64
+#: rhodecode/templates/branches/branches.html:81
+#: rhodecode/templates/journal/journal.html:215
+#: rhodecode/templates/tags/tags.html:64
+#, fuzzy
+msgid "Loading..."
+msgstr "載入中..."
+
+#: rhodecode/templates/login.html:5 rhodecode/templates/login.html:54
msgid "Sign In"
msgstr "登入"
@@ -1016,31 +1450,33 @@ msgstr "登入"
msgid "Sign In to"
msgstr "登入"
-#: rhodecode/templates/login.html:31
-#: rhodecode/templates/register.html:20
+#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
#: rhodecode/templates/admin/admin_log.html:5
#: rhodecode/templates/admin/users/user_add.html:32
-#: rhodecode/templates/admin/users/user_edit.html:47
-#: rhodecode/templates/admin/users/user_edit_my_account.html:45
-#: rhodecode/templates/base/base.html:15
-#: rhodecode/templates/summary/summary.html:106
+#: rhodecode/templates/admin/users/user_edit.html:50
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:26
+#: rhodecode/templates/base/base.html:83
+#: rhodecode/templates/summary/summary.html:122
msgid "Username"
msgstr "帳號"
-#: rhodecode/templates/login.html:40
-#: rhodecode/templates/register.html:29
+#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
#: rhodecode/templates/admin/ldap/ldap.html:46
#: rhodecode/templates/admin/users/user_add.html:41
-#: rhodecode/templates/base/base.html:24
+#: rhodecode/templates/base/base.html:92
msgid "Password"
msgstr "密碼"
+#: rhodecode/templates/login.html:50
+#, fuzzy
+msgid "Remember me"
+msgstr "成員"
+
#: rhodecode/templates/login.html:60
msgid "Forgot your password ?"
msgstr "忘記您的密碼?"
-#: rhodecode/templates/login.html:63
-#: rhodecode/templates/base/base.html:35
+#: rhodecode/templates/login.html:63 rhodecode/templates/base/base.html:103
msgid "Don't have an account ?"
msgstr "沒有帳號?"
@@ -1064,8 +1500,7 @@ msgstr "重設我的密碼"
msgid "Password reset link will be send to matching email address"
msgstr "密碼重設連結已郵寄至您的信箱"
-#: rhodecode/templates/register.html:5
-#: rhodecode/templates/register.html:74
+#: rhodecode/templates/register.html:5 rhodecode/templates/register.html:74
msgid "Sign Up"
msgstr "登入"
@@ -1078,24 +1513,24 @@ msgid "Re-enter password"
msgstr "確認密碼"
#: rhodecode/templates/register.html:47
-#: rhodecode/templates/admin/users/user_add.html:50
-#: rhodecode/templates/admin/users/user_edit.html:74
-#: rhodecode/templates/admin/users/user_edit_my_account.html:63
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:86
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:53
msgid "First Name"
msgstr "名"
#: rhodecode/templates/register.html:56
-#: rhodecode/templates/admin/users/user_add.html:59
-#: rhodecode/templates/admin/users/user_edit.html:83
-#: rhodecode/templates/admin/users/user_edit_my_account.html:72
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:95
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:62
msgid "Last Name"
msgstr "姓"
#: rhodecode/templates/register.html:65
-#: rhodecode/templates/admin/users/user_add.html:68
-#: rhodecode/templates/admin/users/user_edit.html:92
-#: rhodecode/templates/admin/users/user_edit_my_account.html:81
-#: rhodecode/templates/summary/summary.html:108
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:104
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:71
+#: rhodecode/templates/summary/summary.html:124
msgid "Email"
msgstr "電子郵件"
@@ -1107,20 +1542,60 @@ msgstr "您的帳號註冊後將會啟用"
msgid "Your account must wait for activation by administrator"
msgstr "您的帳號註冊後將等待管理員啟用"
-#: rhodecode/templates/repo_switcher_list.html:14
+#: rhodecode/templates/repo_switcher_list.html:11
+#: rhodecode/templates/admin/repos/repo_add_base.html:65
+#: rhodecode/templates/admin/repos/repo_edit.html:85
+#: rhodecode/templates/settings/repo_settings.html:76
msgid "Private repository"
msgstr "私有的版本庫"
-#: rhodecode/templates/repo_switcher_list.html:19
+#: rhodecode/templates/repo_switcher_list.html:16
msgid "Public repository"
msgstr "公開的版本庫"
+#: rhodecode/templates/switch_to_list.html:3
+#: rhodecode/templates/branches/branches.html:14
+msgid "branches"
+msgstr "分支"
+
+#: rhodecode/templates/switch_to_list.html:10
+#: rhodecode/templates/branches/branches_data.html:57
+msgid "There are no branches yet"
+msgstr "沒有任何分支"
+
+#: rhodecode/templates/switch_to_list.html:15
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:15
+msgid "tags"
+msgstr "標籤"
+
+#: rhodecode/templates/switch_to_list.html:22
+#: rhodecode/templates/tags/tags_data.html:33
+msgid "There are no tags yet"
+msgstr "沒有任何標籤"
+
+#: rhodecode/templates/switch_to_list.html:28
+#: rhodecode/templates/bookmarks/bookmarks.html:15
+msgid "bookmarks"
+msgstr ""
+
+#: rhodecode/templates/switch_to_list.html:35
+#: rhodecode/templates/bookmarks/bookmarks_data.html:32
+#, fuzzy
+msgid "There are no bookmarks yet"
+msgstr "尚未有任何 fork"
+
#: rhodecode/templates/admin/admin.html:5
#: rhodecode/templates/admin/admin.html:9
msgid "Admin journal"
msgstr "管理員日誌"
#: rhodecode/templates/admin/admin_log.html:6
+#: rhodecode/templates/admin/repos/repos.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:8
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:9
+#: rhodecode/templates/journal/journal.html:61
+#: rhodecode/templates/journal/journal.html:62
msgid "Action"
msgstr "動作"
@@ -1129,6 +1604,11 @@ msgid "Repository"
msgstr "版本庫"
#: rhodecode/templates/admin/admin_log.html:8
+#: rhodecode/templates/bookmarks/bookmarks.html:37
+#: rhodecode/templates/bookmarks/bookmarks_data.html:7
+#: rhodecode/templates/branches/branches.html:52
+#: rhodecode/templates/tags/tags.html:37
+#: rhodecode/templates/tags/tags_data.html:7
msgid "Date"
msgstr "時間"
@@ -1136,7 +1616,7 @@ msgstr "時間"
msgid "From IP"
msgstr "來源IP"
-#: rhodecode/templates/admin/admin_log.html:52
+#: rhodecode/templates/admin/admin_log.html:53
msgid "No actions yet"
msgstr ""
@@ -1213,23 +1693,66 @@ msgid "E-mail Attribute"
msgstr "電子郵件屬性"
#: rhodecode/templates/admin/ldap/ldap.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:141
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:74
#: rhodecode/templates/admin/settings/hooks.html:73
-#: rhodecode/templates/admin/users/user_edit.html:117
-#: rhodecode/templates/admin/users/user_edit.html:142
-#: rhodecode/templates/admin/users/user_edit_my_account.html:89
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:263
+#: rhodecode/templates/admin/users/user_edit.html:129
+#: rhodecode/templates/admin/users/user_edit.html:174
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:79
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:135
+#: rhodecode/templates/settings/repo_settings.html:93
msgid "Save"
msgstr "儲存"
+#: rhodecode/templates/admin/notifications/notifications.html:5
+#: rhodecode/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:29
+msgid "All"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:30
+#, fuzzy
+msgid "Comments"
+msgstr "遞交"
+
+#: rhodecode/templates/admin/notifications/notifications.html:31
+#: rhodecode/templates/base/base.html:254
+#: rhodecode/templates/base/base.html:256
+msgid "Pull requests"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications.html:35
+msgid "Mark all read"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/notifications_data.html:39
+msgid "No notifications here yet"
+msgstr ""
+
+#: rhodecode/templates/admin/notifications/show_notification.html:5
+#: rhodecode/templates/admin/notifications/show_notification.html:11
+#, fuzzy
+msgid "Show notification"
+msgstr "險是註釋"
+
+#: rhodecode/templates/admin/notifications/show_notification.html:9
+#, fuzzy
+msgid "Notifications"
+msgstr "位置"
+
#: rhodecode/templates/admin/permissions/permissions.html:5
msgid "Permissions administration"
msgstr "權限管理員"
#: rhodecode/templates/admin/permissions/permissions.html:11
-#: rhodecode/templates/admin/repos/repo_edit.html:109
-#: rhodecode/templates/admin/users/user_edit.html:127
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:248
-#: rhodecode/templates/settings/repo_settings.html:58
+#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
+#: rhodecode/templates/admin/users/user_edit.html:139
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:100
+#: rhodecode/templates/settings/repo_settings.html:86
msgid "Permissions"
msgstr "權限"
@@ -1246,7 +1769,10 @@ msgid "Repository permission"
msgstr "版本庫權限"
#: rhodecode/templates/admin/permissions/permissions.html:49
-msgid "All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost"
+msgid ""
+"All default permissions on each repository will be reset to choosen "
+"permission, note that all custom default permission on repositories will "
+"be lost"
msgstr ""
#: rhodecode/templates/admin/permissions/permissions.html:50
@@ -1262,6 +1788,12 @@ msgid "Repository creation"
msgstr "版本庫建立"
#: rhodecode/templates/admin/permissions/permissions.html:71
+#, fuzzy
+msgid "Repository forking"
+msgstr "版本庫建立"
+
+#: rhodecode/templates/admin/permissions/permissions.html:78
+#: rhodecode/templates/admin/repos/repo_edit.html:241
msgid "set"
msgstr "設定"
@@ -1272,7 +1804,6 @@ msgstr "新增版本庫"
#: rhodecode/templates/admin/repos/repo_add.html:11
#: rhodecode/templates/admin/repos/repo_edit.html:11
-#: rhodecode/templates/admin/repos/repos.html:10
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:10
msgid "Repositories"
msgstr "版本庫"
@@ -1282,30 +1813,72 @@ msgid "add new"
msgstr "新增"
#: rhodecode/templates/admin/repos/repo_add_base.html:20
-#: rhodecode/templates/summary/summary.html:80
-#: rhodecode/templates/summary/summary.html:82
+#: rhodecode/templates/summary/summary.html:95
+#: rhodecode/templates/summary/summary.html:96
msgid "Clone from"
msgstr "複製由"
-#: rhodecode/templates/admin/repos/repo_add_base.html:28
-#: rhodecode/templates/admin/repos/repo_edit.html:48
+#: rhodecode/templates/admin/repos/repo_add_base.html:24
+#: rhodecode/templates/admin/repos/repo_edit.html:44
+#: rhodecode/templates/settings/repo_settings.html:43
+msgid "Optional http[s] url from which repository should be cloned."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:49
#: rhodecode/templates/admin/repos_groups/repos_groups.html:4
+#: rhodecode/templates/forks/fork.html:50
+#: rhodecode/templates/settings/repo_settings.html:48
msgid "Repository group"
msgstr "版本庫群組"
-#: rhodecode/templates/admin/repos/repo_add_base.html:36
-#: rhodecode/templates/admin/repos/repo_edit.html:56
+#: rhodecode/templates/admin/repos/repo_add_base.html:33
+#: rhodecode/templates/forks/fork.html:54
+msgid "Optionaly select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:38
+#: rhodecode/templates/admin/repos/repo_edit.html:58
msgid "Type"
msgstr "類型"
-#: rhodecode/templates/admin/repos/repo_add_base.html:52
-#: rhodecode/templates/admin/repos/repo_edit.html:73
-#: rhodecode/templates/settings/repo_fork.html:48
-#: rhodecode/templates/settings/repo_settings.html:49
-msgid "Private"
-msgstr "私有"
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+#, fuzzy
+msgid "Type of repository to create."
+msgstr "版本庫建立"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:47
+#: rhodecode/templates/admin/repos/repo_edit.html:66
+#: rhodecode/templates/forks/fork.html:41
+#: rhodecode/templates/settings/repo_settings.html:57
+#, fuzzy
+msgid "Landing revision"
+msgstr "下一個修訂"
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:51
+#: rhodecode/templates/admin/repos/repo_edit.html:70
+#: rhodecode/templates/forks/fork.html:45
+#: rhodecode/templates/settings/repo_settings.html:61
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:60
+#: rhodecode/templates/admin/repos/repo_edit.html:79
+#: rhodecode/templates/forks/fork.html:63
+#: rhodecode/templates/settings/repo_settings.html:70
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr ""
-#: rhodecode/templates/admin/repos/repo_add_base.html:59
+#: rhodecode/templates/admin/repos/repo_add_base.html:69
+#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/forks/fork.html:72
+#: rhodecode/templates/settings/repo_settings.html:80
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:73
msgid "add"
msgstr "新增"
@@ -1319,183 +1892,276 @@ msgstr "編輯版本庫"
#: rhodecode/templates/admin/repos/repo_edit.html:13
#: rhodecode/templates/admin/users/user_edit.html:13
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
+#: rhodecode/templates/admin/users/user_edit.html:224
+#: rhodecode/templates/admin/users/user_edit.html:226
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
-#: rhodecode/templates/files/files_annotate.html:49
-#: rhodecode/templates/files/files_source.html:20
+#: rhodecode/templates/files/files_source.html:44
+#: rhodecode/templates/journal/journal.html:81
msgid "edit"
msgstr "編輯"
#: rhodecode/templates/admin/repos/repo_edit.html:40
+#: rhodecode/templates/settings/repo_settings.html:39
msgid "Clone uri"
msgstr "複製URL"
-#: rhodecode/templates/admin/repos/repo_edit.html:81
+#: rhodecode/templates/admin/repos/repo_edit.html:53
+#: rhodecode/templates/settings/repo_settings.html:52
+msgid "Optional select a group to put this repository into."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:94
msgid "Enable statistics"
msgstr "啟用統計"
-#: rhodecode/templates/admin/repos/repo_edit.html:89
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+msgid "Enable statistics window on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:103
msgid "Enable downloads"
msgstr "啟用下載"
-#: rhodecode/templates/admin/repos/repo_edit.html:127
+#: rhodecode/templates/admin/repos/repo_edit.html:107
+msgid "Enable download menu on summary page."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:112
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:66
+#, fuzzy
+msgid "Enable locking"
+msgstr "啟用"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:116
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:126
+#, fuzzy
+msgid "Change owner of this repository."
+msgstr "修改於版本庫 %s"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:142
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:75
+#: rhodecode/templates/admin/settings/settings.html:113
+#: rhodecode/templates/admin/settings/settings.html:168
+#: rhodecode/templates/admin/settings/settings.html:258
+#: rhodecode/templates/admin/users/user_edit.html:130
+#: rhodecode/templates/admin/users/user_edit.html:175
+#: rhodecode/templates/admin/users/user_edit.html:278
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:80
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:136
+#: rhodecode/templates/files/files_add.html:82
+#: rhodecode/templates/files/files_edit.html:68
+#: rhodecode/templates/pullrequests/pullrequest.html:124
+#: rhodecode/templates/settings/repo_settings.html:94
+msgid "Reset"
+msgstr "重設"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:152
msgid "Administration"
msgstr "管理者"
-#: rhodecode/templates/admin/repos/repo_edit.html:130
+#: rhodecode/templates/admin/repos/repo_edit.html:155
msgid "Statistics"
msgstr "統計"
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Reset current statistics"
msgstr "重設目前的統計"
-#: rhodecode/templates/admin/repos/repo_edit.html:134
+#: rhodecode/templates/admin/repos/repo_edit.html:159
msgid "Confirm to remove current statistics"
msgstr "確認移除目前的統計"
-#: rhodecode/templates/admin/repos/repo_edit.html:137
+#: rhodecode/templates/admin/repos/repo_edit.html:162
msgid "Fetched to rev"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:138
-msgid "Percentage of stats gathered"
+#: rhodecode/templates/admin/repos/repo_edit.html:163
+msgid "Stats gathered"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:147
+#: rhodecode/templates/admin/repos/repo_edit.html:171
msgid "Remote"
msgstr "遠端"
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Pull changes from remote location"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:151
+#: rhodecode/templates/admin/repos/repo_edit.html:175
msgid "Confirm to pull changes from remote side"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:162
+#: rhodecode/templates/admin/repos/repo_edit.html:186
msgid "Cache"
msgstr "快取"
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Invalidate repository cache"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit.html:166
+#: rhodecode/templates/admin/repos/repo_edit.html:190
msgid "Confirm to invalidate repository cache"
msgstr "確認廢止版本庫快取"
-#: rhodecode/templates/admin/repos/repo_edit.html:177
+#: rhodecode/templates/admin/repos/repo_edit.html:195
+#: rhodecode/templates/base/base.html:318
+#: rhodecode/templates/base/base.html:320
+#: rhodecode/templates/base/base.html:322
+msgid "Public journal"
+msgstr "公開日誌"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:201
msgid "Remove from public journal"
msgstr "從公開日誌移除"
-#: rhodecode/templates/admin/repos/repo_edit.html:179
+#: rhodecode/templates/admin/repos/repo_edit.html:203
msgid "Add to public journal"
msgstr "新增至公開日誌"
-#: rhodecode/templates/admin/repos/repo_edit.html:185
+#: rhodecode/templates/admin/repos/repo_edit.html:208
+msgid ""
+"All actions made on this repository will be accessible to everyone in "
+"public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:215
+#, fuzzy
+msgid "Locking"
+msgstr "解鎖"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+msgid "Unlock locked repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:220
+#, fuzzy
+msgid "Confirm to unlock repository"
+msgstr "確認移除這個版本庫"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+msgid "lock repo"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:223
+#, fuzzy
+msgid "Confirm to lock repository"
+msgstr "確認移除這個版本庫"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:224
+#, fuzzy
+msgid "Repository is not locked"
+msgstr "個版本庫"
+
+#: rhodecode/templates/admin/repos/repo_edit.html:229
+msgid "Force locking on repository. Works only when anonymous access is disabled"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:236
+msgid "Set as fork of"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:245
+msgid "Manually set this repository as a fork of another from the list"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:251
+#: rhodecode/templates/changeset/changeset_file_comment.html:26
msgid "Delete"
msgstr "移除"
-#: rhodecode/templates/admin/repos/repo_edit.html:189
+#: rhodecode/templates/admin/repos/repo_edit.html:255
msgid "Remove this repository"
msgstr "移除版本庫"
-#: rhodecode/templates/admin/repos/repo_edit.html:189
-#: rhodecode/templates/admin/repos/repos.html:79
+#: rhodecode/templates/admin/repos/repo_edit.html:255
+#: rhodecode/templates/journal/journal.html:84
msgid "Confirm to delete this repository"
msgstr "確認移除這個版本庫"
+#: rhodecode/templates/admin/repos/repo_edit.html:259
+msgid ""
+"This repository will be renamed in a special way in order to be "
+"unaccesible for RhodeCode and VCS systems.\n"
+" If you need fully delete it from filesystem "
+"please do it manually"
+msgstr ""
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:3
msgid "none"
msgstr "無"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:4
msgid "read"
msgstr "讀"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:5
msgid "write"
msgstr "寫"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
-#: rhodecode/templates/admin/users/users.html:38
-#: rhodecode/templates/base/base.html:296
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:85
+#: rhodecode/templates/base/base.html:217
msgid "admin"
msgstr "管理員"
#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:7
msgid "member"
msgstr "成員"
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:16
+#: rhodecode/templates/data_table/_dt_elements.html:67
+#: rhodecode/templates/journal/journal.html:132
+#: rhodecode/templates/summary/summary.html:76
+msgid "private repository"
+msgstr "私有版本庫"
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:19
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:28
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:18
+#, fuzzy
+msgid "default"
+msgstr "刪除"
+
#: rhodecode/templates/admin/repos/repo_edit_perms.html:33
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:53
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:58
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:23
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:42
msgid "revoke"
msgstr ""
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:75
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:83
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:67
msgid "Add another member"
msgstr "新增另ㄧ位成員"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:89
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:97
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:81
msgid "Failed to remove user"
msgstr "移除使用者失敗"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:104
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:112
+#: rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html:96
msgid "Failed to remove users group"
msgstr "移除使用者群組失敗"
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:205
-msgid "Group"
-msgstr "群組"
-
-#: rhodecode/templates/admin/repos/repo_edit_perms.html:206
-#: rhodecode/templates/admin/users_groups/users_groups.html:33
-msgid "members"
-msgstr "成員"
-
#: rhodecode/templates/admin/repos/repos.html:5
msgid "Repositories administration"
msgstr "版本庫管理員"
-#: rhodecode/templates/admin/repos/repos.html:34
-#: rhodecode/templates/summary/summary.html:100
-msgid "Contact"
-msgstr "聯絡方式"
-
-#: rhodecode/templates/admin/repos/repos.html:35
-#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
-#: rhodecode/templates/admin/users/user_edit_my_account.html:119
-#: rhodecode/templates/admin/users/users.html:40
-#: rhodecode/templates/admin/users_groups/users_groups.html:35
-msgid "action"
-msgstr "動作"
-
-#: rhodecode/templates/admin/repos/repos.html:51
-#: rhodecode/templates/admin/users/user_edit_my_account.html:134
-#: rhodecode/templates/admin/users/user_edit_my_account.html:148
-msgid "private"
-msgstr "私有"
-
-#: rhodecode/templates/admin/repos/repos.html:53
-#: rhodecode/templates/admin/repos/repos.html:59
-#: rhodecode/templates/admin/users/user_edit_my_account.html:136
-#: rhodecode/templates/admin/users/user_edit_my_account.html:142
-#: rhodecode/templates/summary/summary.html:68
-msgid "public"
-msgstr "公開"
-
-#: rhodecode/templates/admin/repos/repos.html:79
-#: rhodecode/templates/admin/users/users.html:55
-msgid "delete"
-msgstr "刪除"
-
#: rhodecode/templates/admin/repos_groups/repos_groups.html:8
msgid "Groups"
msgstr "群組"
-#: rhodecode/templates/admin/repos_groups/repos_groups.html:13
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:12
msgid "with"
msgstr ""
@@ -1518,10 +2184,10 @@ msgid "Group parent"
msgstr "父群組"
#: rhodecode/templates/admin/repos_groups/repos_groups_add.html:58
-#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:58
-#: rhodecode/templates/admin/users/user_add.html:85
+#: rhodecode/templates/admin/users/user_add.html:94
#: rhodecode/templates/admin/users_groups/users_group_add.html:49
#: rhodecode/templates/admin/users_groups/users_group_edit.html:90
+#: rhodecode/templates/pullrequests/pullrequest_show.html:113
msgid "save"
msgstr "儲存"
@@ -1533,6 +2199,12 @@ msgstr "編輯版本庫群組"
msgid "edit repos group"
msgstr "編輯版本庫群組"
+#: rhodecode/templates/admin/repos_groups/repos_groups_edit.html:70
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:5
msgid "Repositories groups administration"
msgstr "版本庫群組管理員"
@@ -1542,11 +2214,27 @@ msgid "ADD NEW GROUP"
msgstr "新增群組"
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:35
-msgid "Number of repositories"
+#, fuzzy
+msgid "Number of toplevel repositories"
msgstr "版本庫數量"
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:36
+#: rhodecode/templates/admin/users/users.html:87
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
+msgstr "動作"
+
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
-msgid "Confirm to delete this group"
+#: rhodecode/templates/admin/users/user_edit.html:255
+#: rhodecode/templates/admin/users_groups/users_groups.html:44
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#: rhodecode/templates/data_table/_dt_elements.html:103
+msgid "delete"
+msgstr "刪除"
+
+#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:54
+#, fuzzy, python-format
+msgid "Confirm to delete this group: %s"
msgstr "確認刪除這個群組"
#: rhodecode/templates/admin/repos_groups/repos_groups_show.html:62
@@ -1560,7 +2248,6 @@ msgstr "設定管理員"
#: rhodecode/templates/admin/settings/hooks.html:9
#: rhodecode/templates/admin/settings/settings.html:9
-#: rhodecode/templates/settings/repo_settings.html:5
#: rhodecode/templates/settings/repo_settings.html:13
msgid "Settings"
msgstr "設定"
@@ -1590,119 +2277,205 @@ msgid "rescan option"
msgstr "重新掃描選項"
#: rhodecode/templates/admin/settings/settings.html:38
-msgid "In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it."
+msgid ""
+"In case a repository was deleted from filesystem and there are leftovers "
+"in the database check this option to scan obsolete data in database and "
+"remove it."
msgstr "如果版本庫已從檔案系統中刪除,但是資料還留在資料庫,請勾選這個項目清理資料庫中舊的資料"
#: rhodecode/templates/admin/settings/settings.html:39
msgid "destroy old data"
msgstr "移除舊資料"
-#: rhodecode/templates/admin/settings/settings.html:45
+#: rhodecode/templates/admin/settings/settings.html:41
+msgid ""
+"Rescan repositories location for new repositories. Also deletes obsolete "
+"if `destroy` flag is checked "
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:46
msgid "Rescan repositories"
msgstr "重新掃描版本庫"
-#: rhodecode/templates/admin/settings/settings.html:51
+#: rhodecode/templates/admin/settings/settings.html:52
msgid "Whoosh indexing"
msgstr "Whoosh 索引"
-#: rhodecode/templates/admin/settings/settings.html:59
+#: rhodecode/templates/admin/settings/settings.html:60
msgid "index build option"
msgstr "索引選項"
-#: rhodecode/templates/admin/settings/settings.html:64
+#: rhodecode/templates/admin/settings/settings.html:65
msgid "build from scratch"
msgstr "重頭建立索引"
-#: rhodecode/templates/admin/settings/settings.html:70
+#: rhodecode/templates/admin/settings/settings.html:71
msgid "Reindex"
msgstr "重新索引"
-#: rhodecode/templates/admin/settings/settings.html:76
+#: rhodecode/templates/admin/settings/settings.html:77
msgid "Global application settings"
msgstr "全域設定"
-#: rhodecode/templates/admin/settings/settings.html:85
+#: rhodecode/templates/admin/settings/settings.html:86
msgid "Application name"
msgstr "應用名稱"
-#: rhodecode/templates/admin/settings/settings.html:94
+#: rhodecode/templates/admin/settings/settings.html:95
msgid "Realm text"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:103
+#: rhodecode/templates/admin/settings/settings.html:104
msgid "GA code"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:111
-#: rhodecode/templates/admin/settings/settings.html:177
+#: rhodecode/templates/admin/settings/settings.html:112
+#: rhodecode/templates/admin/settings/settings.html:167
+#: rhodecode/templates/admin/settings/settings.html:257
msgid "Save settings"
msgstr "儲存設定"
-#: rhodecode/templates/admin/settings/settings.html:112
-#: rhodecode/templates/admin/settings/settings.html:178
-#: rhodecode/templates/admin/users/user_edit.html:118
-#: rhodecode/templates/admin/users/user_edit.html:143
-#: rhodecode/templates/admin/users/user_edit_my_account.html:90
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:264
-#: rhodecode/templates/files/files_edit.html:50
-msgid "Reset"
-msgstr "重設"
+#: rhodecode/templates/admin/settings/settings.html:119
+#, fuzzy
+msgid "Visualisation settings"
+msgstr "全域設定"
-#: rhodecode/templates/admin/settings/settings.html:118
-msgid "Mercurial settings"
-msgstr "Mercurial 設定"
+#: rhodecode/templates/admin/settings/settings.html:128
+#, fuzzy
+msgid "Icons"
+msgstr "選項"
-#: rhodecode/templates/admin/settings/settings.html:127
+#: rhodecode/templates/admin/settings/settings.html:133
+msgid "Show public repo icon on repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:137
+#, fuzzy
+msgid "Show private repo icon on repositories"
+msgstr "私有版本庫"
+
+#: rhodecode/templates/admin/settings/settings.html:144
+#, fuzzy
+msgid "Meta-Tagging"
+msgstr "設定"
+
+#: rhodecode/templates/admin/settings/settings.html:149
+msgid "Stylify recognised metatags:"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:176
+#, fuzzy
+msgid "VCS settings"
+msgstr "設定"
+
+#: rhodecode/templates/admin/settings/settings.html:185
msgid "Web"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:132
-msgid "require ssl for pushing"
+#: rhodecode/templates/admin/settings/settings.html:190
+#, fuzzy
+msgid "require ssl for vcs operations"
msgstr "推送時要求使用SSL"
-#: rhodecode/templates/admin/settings/settings.html:139
-msgid "Hooks"
+#: rhodecode/templates/admin/settings/settings.html:192
+msgid ""
+"RhodeCode will require SSL for pushing or pulling. If SSL is missing it "
+"will return HTTP Error 406: Not Acceptable"
msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:142
-msgid "advanced setup"
-msgstr "進階設定"
+#: rhodecode/templates/admin/settings/settings.html:198
+msgid "Hooks"
+msgstr ""
-#: rhodecode/templates/admin/settings/settings.html:147
+#: rhodecode/templates/admin/settings/settings.html:203
msgid "Update repository after push (hg update)"
msgstr "push後更新版本庫 (hg update)"
-#: rhodecode/templates/admin/settings/settings.html:151
+#: rhodecode/templates/admin/settings/settings.html:207
msgid "Show repository size after push"
msgstr "push 後顯示版本庫大小"
-#: rhodecode/templates/admin/settings/settings.html:155
+#: rhodecode/templates/admin/settings/settings.html:211
msgid "Log user push commands"
msgstr "紀錄使用者推送命令"
-#: rhodecode/templates/admin/settings/settings.html:159
+#: rhodecode/templates/admin/settings/settings.html:215
msgid "Log user pull commands"
msgstr "紀錄使用者抓取命令"
-#: rhodecode/templates/admin/settings/settings.html:166
+#: rhodecode/templates/admin/settings/settings.html:219
+msgid "advanced setup"
+msgstr "進階設定"
+
+#: rhodecode/templates/admin/settings/settings.html:224
+#, fuzzy
+msgid "Mercurial Extensions"
+msgstr "Mercurial 版本庫"
+
+#: rhodecode/templates/admin/settings/settings.html:229
+msgid "largefiles extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:233
+msgid "hgsubversion extensions"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:235
+msgid ""
+"Requires hgsubversion library installed. Allows clonning from svn remote "
+"locations"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:245
msgid "Repositories location"
msgstr "版本庫路徑"
-#: rhodecode/templates/admin/settings/settings.html:171
-msgid "This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock."
+#: rhodecode/templates/admin/settings/settings.html:250
+msgid ""
+"This a crucial application setting. If you are really sure you need to "
+"change this, you must restart application in order to make this setting "
+"take effect. Click this label to unlock."
msgstr "這是一個關鍵的設定,如果您確定要修改這個設定,請重新啟動應用程式以套用設定"
-#: rhodecode/templates/admin/settings/settings.html:172
+#: rhodecode/templates/admin/settings/settings.html:251
msgid "unlock"
msgstr "解鎖"
+#: rhodecode/templates/admin/settings/settings.html:252
+msgid ""
+"Location where repositories are stored. After changing this value a "
+"restart, and rescan is required"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:272
+msgid "Test Email"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:280
+#, fuzzy
+msgid "Email to"
+msgstr "電子郵件"
+
+#: rhodecode/templates/admin/settings/settings.html:288
+#, fuzzy
+msgid "Send"
+msgstr "秒"
+
+#: rhodecode/templates/admin/settings/settings.html:294
+msgid "System Info and Packages"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:297
+#, fuzzy
+msgid "show"
+msgstr "顯示"
+
#: rhodecode/templates/admin/users/user_add.html:5
msgid "Add user"
msgstr "新增使用者"
#: rhodecode/templates/admin/users/user_add.html:10
#: rhodecode/templates/admin/users/user_edit.html:11
-#: rhodecode/templates/admin/users/users.html:9
msgid "Users"
msgstr "使用者"
@@ -1710,8 +2483,13 @@ msgstr "使用者"
msgid "add new user"
msgstr "新增使用者"
-#: rhodecode/templates/admin/users/user_add.html:77
-#: rhodecode/templates/admin/users/user_edit.html:101
+#: rhodecode/templates/admin/users/user_add.html:50
+#, fuzzy
+msgid "Password confirmation"
+msgstr "密碼不相符"
+
+#: rhodecode/templates/admin/users/user_add.html:86
+#: rhodecode/templates/admin/users/user_edit.html:113
#: rhodecode/templates/admin/users_groups/users_group_add.html:41
#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
msgid "Active"
@@ -1721,36 +2499,101 @@ msgstr "啟用"
msgid "Edit user"
msgstr "編輯使用者"
-#: rhodecode/templates/admin/users/user_edit.html:33
-#: rhodecode/templates/admin/users/user_edit_my_account.html:32
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:10
msgid "Change your avatar at"
msgstr "修改您的頭像於"
-#: rhodecode/templates/admin/users/user_edit.html:34
-#: rhodecode/templates/admin/users/user_edit_my_account.html:33
+#: rhodecode/templates/admin/users/user_edit.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:11
msgid "Using"
msgstr "使用中"
-#: rhodecode/templates/admin/users/user_edit.html:40
-#: rhodecode/templates/admin/users/user_edit_my_account.html:39
+#: rhodecode/templates/admin/users/user_edit.html:43
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:20
msgid "API key"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:56
+#: rhodecode/templates/admin/users/user_edit.html:59
msgid "LDAP DN"
msgstr ""
-#: rhodecode/templates/admin/users/user_edit.html:65
-#: rhodecode/templates/admin/users/user_edit_my_account.html:54
+#: rhodecode/templates/admin/users/user_edit.html:68
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:35
msgid "New password"
msgstr "新密碼"
-#: rhodecode/templates/admin/users/user_edit.html:135
-#: rhodecode/templates/admin/users_groups/users_group_edit.html:256
+#: rhodecode/templates/admin/users/user_edit.html:77
+#: rhodecode/templates/admin/users/user_edit_my_account_form.html:44
+msgid "New password confirmation"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:147
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:108
+#, fuzzy
+msgid "Inherit default permissions"
+msgstr "預設權限"
+
+#: rhodecode/templates/admin/users/user_edit.html:152
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:113
+#, python-format
+msgid ""
+"Select to inherit permissions from %s settings. With this selected below "
+"options does not have any action"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:158
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:119
msgid "Create repositories"
msgstr "建立版本庫"
+#: rhodecode/templates/admin/users/user_edit.html:166
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:127
+#, fuzzy
+msgid "Fork repositories"
+msgstr "個版本庫"
+
+#: rhodecode/templates/admin/users/user_edit.html:186
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:22
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:39
+#, fuzzy
+msgid "Nothing here yet"
+msgstr "尚未有任何變更"
+
+#: rhodecode/templates/admin/users/user_edit.html:193
+#: rhodecode/templates/admin/users/user_edit_my_account.html:60
+#: rhodecode/templates/admin/users/user_edit_my_account.html:194
+#, fuzzy
+msgid "Permission"
+msgstr "權限"
+
+#: rhodecode/templates/admin/users/user_edit.html:194
+#, fuzzy
+msgid "Edit Permission"
+msgstr "版本庫權限"
+
+#: rhodecode/templates/admin/users/user_edit.html:243
+#, fuzzy
+msgid "Email addresses"
+msgstr "郵件位址"
+
+#: rhodecode/templates/admin/users/user_edit.html:256
+#, fuzzy, python-format
+msgid "Confirm to delete this email: %s"
+msgstr "確認刪除這個使用者"
+
+#: rhodecode/templates/admin/users/user_edit.html:270
+#, fuzzy
+msgid "New email address"
+msgstr "郵件位址"
+
+#: rhodecode/templates/admin/users/user_edit.html:277
+#, fuzzy
+msgid "Add"
+msgstr "新增"
+
#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+#: rhodecode/templates/base/base.html:124
msgid "My account"
msgstr "我的帳號"
@@ -1758,26 +2601,79 @@ msgstr "我的帳號"
msgid "My Account"
msgstr "我的帳號"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:101
-msgid "My repositories"
-msgstr "我的版本庫"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:35
+#, fuzzy
+msgid "My permissions"
+msgstr "權限"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:107
-msgid "ADD REPOSITORY"
-msgstr "新增版本庫"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:38
+#: rhodecode/templates/journal/journal.html:41
+#, fuzzy
+msgid "My repos"
+msgstr "空的版本庫"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:118
-#: rhodecode/templates/branches/branches_data.html:7
-#: rhodecode/templates/shortlog/shortlog_data.html:8
-#: rhodecode/templates/tags/tags_data.html:7
-msgid "revision"
+#: rhodecode/templates/admin/users/user_edit_my_account.html:41
+#, fuzzy
+msgid "My pull requests"
+msgstr "建立使用者 %s"
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+#, fuzzy
+msgid "Add repo"
+msgstr "新增"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:2
+msgid "Opened by me"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:10
+#, python-format
+msgid "Pull request #%s opened on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:15
+#, fuzzy
+msgid "Confirm to delete this pull request"
+msgstr "確認移除這個版本庫"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:26
+msgid "I participate in"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html:33
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:30
+#, python-format
+msgid "Pull request #%s opened by %s on %s"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:7
+#: rhodecode/templates/bookmarks/bookmarks.html:40
+#: rhodecode/templates/bookmarks/bookmarks_data.html:9
+#: rhodecode/templates/branches/branches.html:55
+#: rhodecode/templates/journal/journal.html:60
+#: rhodecode/templates/tags/tags.html:40
+#: rhodecode/templates/tags/tags_data.html:9
+msgid "Revision"
msgstr "修訂"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:28
+#: rhodecode/templates/journal/journal.html:81
+msgid "private"
+msgstr "私有"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:7
+#, fuzzy, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr "確認移除這個版本庫"
+
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:38
+#: rhodecode/templates/journal/journal.html:94
msgid "No repositories yet"
msgstr "沒有任何版本庫"
-#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+#: rhodecode/templates/admin/users/user_edit_my_account_repos.html:40
+#: rhodecode/templates/journal/journal.html:96
msgid "create one now"
msgstr ""
@@ -1785,42 +2681,42 @@ msgstr ""
msgid "Users administration"
msgstr "使用者管理員"
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/base/base.html:223
+msgid "users"
+msgstr "使用者"
+
#: rhodecode/templates/admin/users/users.html:23
msgid "ADD NEW USER"
msgstr "新增使用者"
-#: rhodecode/templates/admin/users/users.html:33
+#: rhodecode/templates/admin/users/users.html:77
msgid "username"
msgstr "使用者名稱"
-#: rhodecode/templates/admin/users/users.html:34
-#: rhodecode/templates/branches/branches_data.html:5
-#: rhodecode/templates/tags/tags_data.html:5
-msgid "name"
-msgstr "名字"
+#: rhodecode/templates/admin/users/users.html:80
+#, fuzzy
+msgid "firstname"
+msgstr "名"
-#: rhodecode/templates/admin/users/users.html:35
+#: rhodecode/templates/admin/users/users.html:81
msgid "lastname"
msgstr "姓"
-#: rhodecode/templates/admin/users/users.html:36
+#: rhodecode/templates/admin/users/users.html:82
msgid "last login"
msgstr "最後登入"
-#: rhodecode/templates/admin/users/users.html:37
+#: rhodecode/templates/admin/users/users.html:84
#: rhodecode/templates/admin/users_groups/users_groups.html:34
msgid "active"
msgstr "啟用"
-#: rhodecode/templates/admin/users/users.html:39
-#: rhodecode/templates/base/base.html:305
+#: rhodecode/templates/admin/users/users.html:86
+#: rhodecode/templates/base/base.html:226
msgid "ldap"
msgstr ""
-#: rhodecode/templates/admin/users/users.html:56
-msgid "Confirm to delete this user"
-msgstr "確認刪除這個使用者"
-
#: rhodecode/templates/admin/users_groups/users_group_add.html:5
msgid "Add users group"
msgstr "新增使用者群組"
@@ -1862,6 +2758,11 @@ msgstr "啟用的成員"
msgid "Add all elements"
msgstr "新增索有元素"
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:146
+#, fuzzy
+msgid "Group members"
+msgstr "選擇群組成員"
+
#: rhodecode/templates/admin/users_groups/users_groups.html:5
msgid "Users groups administration"
msgstr "使用者群組管理員"
@@ -1874,399 +2775,633 @@ msgstr "建立新的使用者群組"
msgid "group name"
msgstr "群組名稱"
-#: rhodecode/templates/base/base.html:32
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+#: rhodecode/templates/base/root.html:46
+msgid "members"
+msgstr "成員"
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:45
+#, fuzzy, python-format
+msgid "Confirm to delete this users group: %s"
+msgstr "確認刪除這個群組"
+
+#: rhodecode/templates/base/base.html:41
+msgid "Submit a bug"
+msgstr "回報錯誤"
+
+#: rhodecode/templates/base/base.html:77
+msgid "Login to your account"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:100
msgid "Forgot password ?"
msgstr "忘記密碼?"
-#: rhodecode/templates/base/base.html:57
-#: rhodecode/templates/base/base.html:338
-#: rhodecode/templates/base/base.html:340
-#: rhodecode/templates/base/base.html:342
+#: rhodecode/templates/base/base.html:107
+#, fuzzy
+msgid "Log In"
+msgstr "登入"
+
+#: rhodecode/templates/base/base.html:118
+msgid "Inbox"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:122
+#: rhodecode/templates/base/base.html:300
+#: rhodecode/templates/base/base.html:302
+#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/bookmarks/bookmarks.html:11
+#: rhodecode/templates/branches/branches.html:10
+#: rhodecode/templates/changelog/changelog.html:10
+#: rhodecode/templates/changeset/changeset.html:10
+#: rhodecode/templates/changeset/changeset_range.html:9
+#: rhodecode/templates/compare/compare_diff.html:9
+#: rhodecode/templates/files/file_diff.html:8
+#: rhodecode/templates/files/files.html:8
+#: rhodecode/templates/files/files_add.html:15
+#: rhodecode/templates/files/files_edit.html:15
+#: rhodecode/templates/followers/followers.html:9
+#: rhodecode/templates/forks/fork.html:9 rhodecode/templates/forks/forks.html:9
+#: rhodecode/templates/pullrequests/pullrequest.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show.html:8
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:8
+#: rhodecode/templates/settings/repo_settings.html:9
+#: rhodecode/templates/shortlog/shortlog.html:10
+#: rhodecode/templates/summary/summary.html:8
+#: rhodecode/templates/tags/tags.html:11
msgid "Home"
msgstr "首頁"
-#: rhodecode/templates/base/base.html:61
-#: rhodecode/templates/base/base.html:347
-#: rhodecode/templates/base/base.html:349
-#: rhodecode/templates/base/base.html:351
+#: rhodecode/templates/base/base.html:123
+#: rhodecode/templates/base/base.html:309
+#: rhodecode/templates/base/base.html:311
+#: rhodecode/templates/base/base.html:313
#: rhodecode/templates/journal/journal.html:4
-#: rhodecode/templates/journal/journal.html:17
+#: rhodecode/templates/journal/journal.html:21
#: rhodecode/templates/journal/public_journal.html:4
msgid "Journal"
msgstr "日誌"
-#: rhodecode/templates/base/base.html:66
-msgid "Login"
-msgstr "登入"
-
-#: rhodecode/templates/base/base.html:68
+#: rhodecode/templates/base/base.html:125
msgid "Log Out"
msgstr "登出"
-#: rhodecode/templates/base/base.html:107
-msgid "Submit a bug"
-msgstr "回報錯誤"
-
-#: rhodecode/templates/base/base.html:141
+#: rhodecode/templates/base/base.html:144
msgid "Switch repository"
msgstr "切換版本庫"
-#: rhodecode/templates/base/base.html:143
+#: rhodecode/templates/base/base.html:146
msgid "Products"
msgstr ""
-#: rhodecode/templates/base/base.html:149
+#: rhodecode/templates/base/base.html:152
+#: rhodecode/templates/base/base.html:182
msgid "loading..."
msgstr "載入中..."
-#: rhodecode/templates/base/base.html:234
-#: rhodecode/templates/base/base.html:236
-#: rhodecode/templates/base/base.html:238
-msgid "Switch to"
-msgstr "切換至"
-
-#: rhodecode/templates/base/base.html:242
-#: rhodecode/templates/branches/branches.html:13
-msgid "branches"
-msgstr "分支"
+#: rhodecode/templates/base/base.html:158
+#: rhodecode/templates/base/base.html:160
+#: rhodecode/templates/base/base.html:162
+#: rhodecode/templates/data_table/_dt_elements.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:17
+#: rhodecode/templates/data_table/_dt_elements.html:19
+msgid "Summary"
+msgstr "概況"
-#: rhodecode/templates/base/base.html:249
-#: rhodecode/templates/branches/branches_data.html:52
-msgid "There are no branches yet"
-msgstr "沒有任何分支"
+#: rhodecode/templates/base/base.html:166
+#: rhodecode/templates/base/base.html:168
+#: rhodecode/templates/base/base.html:170
+#: rhodecode/templates/changelog/changelog.html:15
+#: rhodecode/templates/data_table/_dt_elements.html:23
+#: rhodecode/templates/data_table/_dt_elements.html:25
+#: rhodecode/templates/data_table/_dt_elements.html:27
+msgid "Changelog"
+msgstr "修改紀錄"
-#: rhodecode/templates/base/base.html:254
-#: rhodecode/templates/shortlog/shortlog_data.html:10
-#: rhodecode/templates/tags/tags.html:14
-msgid "tags"
-msgstr "標籤"
+#: rhodecode/templates/base/base.html:175
+#: rhodecode/templates/base/base.html:177
+#: rhodecode/templates/base/base.html:179
+msgid "Switch to"
+msgstr "切換至"
-#: rhodecode/templates/base/base.html:261
-#: rhodecode/templates/tags/tags_data.html:32
-msgid "There are no tags yet"
-msgstr "沒有任何標籤"
+#: rhodecode/templates/base/base.html:186
+#: rhodecode/templates/base/base.html:188
+#: rhodecode/templates/base/base.html:190
+#: rhodecode/templates/data_table/_dt_elements.html:31
+#: rhodecode/templates/data_table/_dt_elements.html:33
+#: rhodecode/templates/data_table/_dt_elements.html:35
+msgid "Files"
+msgstr "檔案"
-#: rhodecode/templates/base/base.html:277
-#: rhodecode/templates/base/base.html:281
-#: rhodecode/templates/files/files_annotate.html:40
-#: rhodecode/templates/files/files_source.html:11
+#: rhodecode/templates/base/base.html:195
+#: rhodecode/templates/base/base.html:199
msgid "Options"
msgstr "選項"
-#: rhodecode/templates/base/base.html:286
-#: rhodecode/templates/base/base.html:288
-#: rhodecode/templates/base/base.html:306
+#: rhodecode/templates/base/base.html:204
+#: rhodecode/templates/base/base.html:206
+#: rhodecode/templates/base/base.html:227
msgid "settings"
msgstr "設定"
-#: rhodecode/templates/base/base.html:292
+#: rhodecode/templates/base/base.html:209
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/forks/fork.html:13
+msgid "fork"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:211
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "Open new pull request"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:213
msgid "search"
msgstr "搜尋"
-#: rhodecode/templates/base/base.html:299
-msgid "journal"
-msgstr "日誌"
-
-#: rhodecode/templates/base/base.html:301
+#: rhodecode/templates/base/base.html:222
msgid "repositories groups"
msgstr "版本庫群組"
-#: rhodecode/templates/base/base.html:302
-msgid "users"
-msgstr "使用者"
-
-#: rhodecode/templates/base/base.html:303
+#: rhodecode/templates/base/base.html:224
msgid "users groups"
msgstr "使用者群組"
-#: rhodecode/templates/base/base.html:304
+#: rhodecode/templates/base/base.html:225
msgid "permissions"
msgstr "權限"
-#: rhodecode/templates/base/base.html:317
-#: rhodecode/templates/base/base.html:319
-#: rhodecode/templates/followers/followers.html:5
+#: rhodecode/templates/base/base.html:238
+#: rhodecode/templates/base/base.html:240
msgid "Followers"
msgstr "追蹤者"
-#: rhodecode/templates/base/base.html:325
-#: rhodecode/templates/base/base.html:327
-#: rhodecode/templates/forks/forks.html:5
+#: rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248
msgid "Forks"
msgstr ""
-#: rhodecode/templates/base/base.html:356
-#: rhodecode/templates/base/base.html:358
-#: rhodecode/templates/base/base.html:360
-#: rhodecode/templates/search/search.html:4
-#: rhodecode/templates/search/search.html:24
-#: rhodecode/templates/search/search.html:46
+#: rhodecode/templates/base/base.html:327
+#: rhodecode/templates/base/base.html:329
+#: rhodecode/templates/base/base.html:331
+#: rhodecode/templates/search/search.html:52
msgid "Search"
msgstr "搜尋"
-#: rhodecode/templates/base/root.html:57
-#: rhodecode/templates/journal/journal.html:48
-#: rhodecode/templates/summary/summary.html:36
+#: rhodecode/templates/base/root.html:42
+#, fuzzy
+msgid "add another comment"
+msgstr "新增另ㄧ位成員"
+
+#: rhodecode/templates/base/root.html:43
+#: rhodecode/templates/journal/journal.html:120
+#: rhodecode/templates/summary/summary.html:57
msgid "Stop following this repository"
msgstr "停止追蹤這個版本庫"
-#: rhodecode/templates/base/root.html:66
-#: rhodecode/templates/summary/summary.html:40
+#: rhodecode/templates/base/root.html:44
+#: rhodecode/templates/summary/summary.html:61
msgid "Start following this repository"
msgstr "開始追蹤這個版本庫"
-#: rhodecode/templates/branches/branches_data.html:4
-#: rhodecode/templates/tags/tags_data.html:4
+#: rhodecode/templates/base/root.html:45
+msgid "Group"
+msgstr "群組"
+
+#: rhodecode/templates/base/root.html:47
+msgid "search truncated"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:48
+msgid "no matching files"
+msgstr "無符合的檔案"
+
+#: rhodecode/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr ""
+
+#: rhodecode/templates/bookmarks/bookmarks.html:39
+#: rhodecode/templates/bookmarks/bookmarks_data.html:8
+#: rhodecode/templates/branches/branches.html:54
+#: rhodecode/templates/tags/tags.html:39
+#: rhodecode/templates/tags/tags_data.html:8
+#, fuzzy
+msgid "Author"
+msgstr "作者"
+
+#: rhodecode/templates/branches/branches.html:5
+#, fuzzy, python-format
+msgid "%s Branches"
+msgstr "分支"
+
+#: rhodecode/templates/branches/branches.html:29
+#, fuzzy
+msgid "Compare branches"
+msgstr "分支"
+
+#: rhodecode/templates/branches/branches.html:57
+#: rhodecode/templates/compare/compare_diff.html:5
+#: rhodecode/templates/compare/compare_diff.html:13
+#, fuzzy
+msgid "Compare"
+msgstr "比較顯示"
+
+#: rhodecode/templates/branches/branches_data.html:6
+msgid "name"
+msgstr "名字"
+
+#: rhodecode/templates/branches/branches_data.html:7
msgid "date"
msgstr "日期"
-#: rhodecode/templates/branches/branches_data.html:6
-#: rhodecode/templates/shortlog/shortlog_data.html:7
-#: rhodecode/templates/tags/tags_data.html:6
+#: rhodecode/templates/branches/branches_data.html:8
+#: rhodecode/templates/shortlog/shortlog_data.html:8
msgid "author"
msgstr "作者"
-#: rhodecode/templates/branches/branches_data.html:8
-#: rhodecode/templates/shortlog/shortlog_data.html:11
-#: rhodecode/templates/tags/tags_data.html:8
-msgid "links"
-msgstr "連結"
-
-#: rhodecode/templates/branches/branches_data.html:23
-#: rhodecode/templates/branches/branches_data.html:43
-#: rhodecode/templates/shortlog/shortlog_data.html:39
-#: rhodecode/templates/tags/tags_data.html:24
-msgid "changeset"
-msgstr "修改"
+#: rhodecode/templates/branches/branches_data.html:9
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "revision"
+msgstr "修訂"
-#: rhodecode/templates/branches/branches_data.html:25
-#: rhodecode/templates/branches/branches_data.html:45
-#: rhodecode/templates/files/files.html:12
-#: rhodecode/templates/shortlog/shortlog_data.html:41
-#: rhodecode/templates/summary/summary.html:233
-#: rhodecode/templates/tags/tags_data.html:26
-msgid "files"
-msgstr "檔案"
+#: rhodecode/templates/branches/branches_data.html:10
+#, fuzzy
+msgid "compare"
+msgstr "比較顯示"
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "showing "
-msgstr ""
+#: rhodecode/templates/changelog/changelog.html:6
+#, fuzzy, python-format
+msgid "%s Changelog"
+msgstr "修改紀錄"
+
+#: rhodecode/templates/changelog/changelog.html:15
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] ""
-#: rhodecode/templates/changelog/changelog.html:14
-msgid "out of"
+#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:19
+#, python-format
+msgid "compare fork with %s"
msgstr ""
#: rhodecode/templates/changelog/changelog.html:37
+#: rhodecode/templates/forks/forks_data.html:21
+msgid "Compare fork"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:46
msgid "Show"
msgstr "顯示"
-#: rhodecode/templates/changelog/changelog.html:50
-#: rhodecode/templates/changeset/changeset.html:42
-#: rhodecode/templates/summary/summary.html:609
-msgid "commit"
-msgstr "遞交"
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/summary/summary.html:364
+msgid "show more"
+msgstr "顯示更多"
-#: rhodecode/templates/changelog/changelog.html:63
+#: rhodecode/templates/changelog/changelog.html:76
msgid "Affected number of files, click to show more details"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:67
-#: rhodecode/templates/changeset/changeset.html:66
-msgid "merge"
-msgstr "合併"
+#: rhodecode/templates/changelog/changelog.html:89
+#: rhodecode/templates/changeset/changeset.html:38
+#: rhodecode/templates/changeset/changeset_file_comment.html:20
+#: rhodecode/templates/changeset/changeset_range.html:46
+#, fuzzy
+msgid "Changeset status"
+msgstr "變更"
-#: rhodecode/templates/changelog/changelog.html:72
-#: rhodecode/templates/changeset/changeset.html:72
+#: rhodecode/templates/changelog/changelog.html:92
+msgid "Click to open associated pull request"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:102
+#: rhodecode/templates/changeset/changeset.html:78
msgid "Parent"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:77
-#: rhodecode/templates/changeset/changeset.html:77
+#: rhodecode/templates/changelog/changelog.html:108
+#: rhodecode/templates/changeset/changeset.html:84
msgid "No parents"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:82
-#: rhodecode/templates/changeset/changeset.html:80
+#: rhodecode/templates/changelog/changelog.html:113
+#: rhodecode/templates/changeset/changeset.html:88
+msgid "merge"
+msgstr "合併"
+
+#: rhodecode/templates/changelog/changelog.html:116
+#: rhodecode/templates/changeset/changeset.html:91
#: rhodecode/templates/files/files.html:29
-#: rhodecode/templates/files/files_annotate.html:25
+#: rhodecode/templates/files/files_add.html:33
#: rhodecode/templates/files/files_edit.html:33
#: rhodecode/templates/shortlog/shortlog_data.html:9
msgid "branch"
msgstr "分支"
-#: rhodecode/templates/changelog/changelog.html:86
-#: rhodecode/templates/changeset/changeset.html:83
+#: rhodecode/templates/changelog/changelog.html:122
+msgid "bookmark"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:128
+#: rhodecode/templates/changeset/changeset.html:96
msgid "tag"
msgstr "標籤"
-#: rhodecode/templates/changelog/changelog.html:122
+#: rhodecode/templates/changelog/changelog.html:164
msgid "Show selected changes __S -> __E"
msgstr ""
-#: rhodecode/templates/changelog/changelog.html:172
-#: rhodecode/templates/shortlog/shortlog_data.html:61
+#: rhodecode/templates/changelog/changelog.html:255
msgid "There are no changes yet"
msgstr "尚未有任何變更"
-#: rhodecode/templates/changelog/changelog_details.html:2
-#: rhodecode/templates/changeset/changeset.html:55
+#: rhodecode/templates/changelog/changelog_details.html:4
+#: rhodecode/templates/changeset/changeset.html:66
msgid "removed"
msgstr "移除"
-#: rhodecode/templates/changelog/changelog_details.html:3
-#: rhodecode/templates/changeset/changeset.html:56
+#: rhodecode/templates/changelog/changelog_details.html:5
+#: rhodecode/templates/changeset/changeset.html:67
msgid "changed"
msgstr "修改"
-#: rhodecode/templates/changelog/changelog_details.html:4
-#: rhodecode/templates/changeset/changeset.html:57
+#: rhodecode/templates/changelog/changelog_details.html:6
+#: rhodecode/templates/changeset/changeset.html:68
msgid "added"
msgstr "新增"
-#: rhodecode/templates/changelog/changelog_details.html:6
-#: rhodecode/templates/changelog/changelog_details.html:7
#: rhodecode/templates/changelog/changelog_details.html:8
-#: rhodecode/templates/changeset/changeset.html:59
-#: rhodecode/templates/changeset/changeset.html:60
-#: rhodecode/templates/changeset/changeset.html:61
+#: rhodecode/templates/changelog/changelog_details.html:9
+#: rhodecode/templates/changelog/changelog_details.html:10
+#: rhodecode/templates/changeset/changeset.html:70
+#: rhodecode/templates/changeset/changeset.html:71
+#: rhodecode/templates/changeset/changeset.html:72
#, python-format
msgid "affected %s files"
msgstr ""
#: rhodecode/templates/changeset/changeset.html:6
+#, fuzzy, python-format
+msgid "%s Changeset"
+msgstr "沒有修改"
+
#: rhodecode/templates/changeset/changeset.html:14
-#: rhodecode/templates/changeset/changeset.html:31
msgid "Changeset"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:32
-#: rhodecode/templates/changeset/changeset.html:121
-#: rhodecode/templates/changeset/changeset_range.html:78
-#: rhodecode/templates/files/file_diff.html:32
-#: rhodecode/templates/files/file_diff.html:42
+#: rhodecode/templates/changeset/changeset.html:43
+#: rhodecode/templates/changeset/diff_block.html:20
msgid "raw diff"
msgstr "原始差異"
-#: rhodecode/templates/changeset/changeset.html:34
-#: rhodecode/templates/changeset/changeset.html:123
-#: rhodecode/templates/changeset/changeset_range.html:80
-#: rhodecode/templates/files/file_diff.html:34
+#: rhodecode/templates/changeset/changeset.html:44
+#: rhodecode/templates/changeset/diff_block.html:21
msgid "download diff"
msgstr "下載差異"
-#: rhodecode/templates/changeset/changeset.html:90
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
+#, fuzzy, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "遞交"
+
+#: rhodecode/templates/changeset/changeset.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:82
#, python-format
-msgid "%s files affected with %s additions and %s deletions."
+msgid "(%d inline)"
+msgid_plural "(%d inline)"
+msgstr[0] ""
+
+#: rhodecode/templates/changeset/changeset.html:103
+#, python-format
+msgid "%s files affected with %s insertions and %s deletions:"
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:101
+#: rhodecode/templates/changeset/changeset.html:119
msgid "Changeset was too big and was cut off..."
msgstr ""
-#: rhodecode/templates/changeset/changeset.html:119
-#: rhodecode/templates/changeset/changeset_range.html:76
-#: rhodecode/templates/files/file_diff.html:30
-msgid "diff"
-msgstr "差異"
+#: rhodecode/templates/changeset/changeset_file_comment.html:42
+msgid "Submitting..."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:45
+msgid "Commenting on line {1}."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:46
+#: rhodecode/templates/changeset/changeset_file_comment.html:121
+#, python-format
+msgid "Comments parsed using %s syntax with %s support."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:48
+#: rhodecode/templates/changeset/changeset_file_comment.html:123
+msgid "Use @username inside this text to send notification to this RhodeCode user"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:59
+#: rhodecode/templates/changeset/changeset_file_comment.html:138
+#, fuzzy
+msgid "Comment"
+msgstr "遞交"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:60
+#: rhodecode/templates/changeset/changeset_file_comment.html:71
+msgid "Hide"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+#, fuzzy
+msgid "You need to be logged in to comment."
+msgstr "您必須登入後才能瀏覽這個頁面"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:67
+msgid "Login now"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:118
+msgid "Leave a comment"
+msgstr ""
-#: rhodecode/templates/changeset/changeset.html:132
-#: rhodecode/templates/changeset/changeset_range.html:89
-msgid "No changes in this file"
-msgstr "這個檔案沒有任何變更"
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+msgid "Check this to change current status of code-review for this changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:124
+#, fuzzy
+msgid "change status"
+msgstr "變更"
+
+#: rhodecode/templates/changeset/changeset_file_comment.html:140
+msgid "Comment and close"
+msgstr ""
-#: rhodecode/templates/changeset/changeset_range.html:30
+#: rhodecode/templates/changeset/changeset_range.html:5
+#, fuzzy, python-format
+msgid "%s Changesets"
+msgstr "變更"
+
+#: rhodecode/templates/changeset/changeset_range.html:29
+#: rhodecode/templates/compare/compare_diff.html:29
msgid "Compare View"
msgstr "比較顯示"
-#: rhodecode/templates/changeset/changeset_range.html:52
+#: rhodecode/templates/changeset/changeset_range.html:54
+#: rhodecode/templates/compare/compare_diff.html:41
+#: rhodecode/templates/pullrequests/pullrequest_show.html:69
msgid "Files affected"
msgstr ""
-#: rhodecode/templates/errors/error_document.html:44
+#: rhodecode/templates/changeset/diff_block.html:19
+msgid "diff"
+msgstr "差異"
+
+#: rhodecode/templates/changeset/diff_block.html:27
+#, fuzzy
+msgid "show inline comments"
+msgstr "文件內容"
+
+#: rhodecode/templates/compare/compare_cs.html:5
+#, fuzzy
+msgid "No changesets"
+msgstr "尚未有任何變更"
+
+#: rhodecode/templates/compare/compare_diff.html:37
+#, fuzzy
+msgid "Outgoing changesets"
+msgstr "尚未有任何變更"
+
+#: rhodecode/templates/data_table/_dt_elements.html:39
+#: rhodecode/templates/data_table/_dt_elements.html:41
+#: rhodecode/templates/data_table/_dt_elements.html:43
+msgid "Fork"
+msgstr "分支"
+
+#: rhodecode/templates/data_table/_dt_elements.html:60
+#: rhodecode/templates/journal/journal.html:126
+#: rhodecode/templates/summary/summary.html:68
+msgid "Mercurial repository"
+msgstr "Mercurial 版本庫"
+
+#: rhodecode/templates/data_table/_dt_elements.html:62
+#: rhodecode/templates/journal/journal.html:128
+#: rhodecode/templates/summary/summary.html:71
+msgid "Git repository"
+msgstr "Git 版本庫"
+
+#: rhodecode/templates/data_table/_dt_elements.html:69
+#: rhodecode/templates/journal/journal.html:134
+#: rhodecode/templates/summary/summary.html:78
+msgid "public repository"
+msgstr "公開版本庫"
+
+#: rhodecode/templates/data_table/_dt_elements.html:80
+#: rhodecode/templates/summary/summary.html:87
+#: rhodecode/templates/summary/summary.html:88
+msgid "Fork of"
+msgstr ""
+
+#: rhodecode/templates/data_table/_dt_elements.html:92
+msgid "No changesets yet"
+msgstr "尚未有任何變更"
+
+#: rhodecode/templates/data_table/_dt_elements.html:104
+#, fuzzy, python-format
+msgid "Confirm to delete this user: %s"
+msgstr "確認刪除這個使用者"
+
+#: rhodecode/templates/email_templates/main.html:8
+msgid "This is an notification from RhodeCode."
+msgstr ""
+
+#: rhodecode/templates/errors/error_document.html:46
#, python-format
msgid "You will be redirected to %s in %s seconds"
msgstr ""
#: rhodecode/templates/files/file_diff.html:4
+#, fuzzy, python-format
+msgid "%s File diff"
+msgstr "檔案差異"
+
#: rhodecode/templates/files/file_diff.html:12
msgid "File diff"
msgstr "檔案差異"
-#: rhodecode/templates/files/file_diff.html:42
-msgid "Diff is to big to display"
-msgstr ""
-
-#: rhodecode/templates/files/files.html:37
-#: rhodecode/templates/files/files_annotate.html:31
-#: rhodecode/templates/files/files_edit.html:39
-msgid "Location"
-msgstr "位置"
-
-#: rhodecode/templates/files/files.html:46
-msgid "Go back"
-msgstr ""
+#: rhodecode/templates/files/files.html:4
+#: rhodecode/templates/files/files.html:72
+#, fuzzy, python-format
+msgid "%s files"
+msgstr "檔案"
-#: rhodecode/templates/files/files.html:47
-msgid "No files at given path"
-msgstr ""
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/summary/summary.html:340
+msgid "files"
+msgstr "檔案"
-#: rhodecode/templates/files/files_annotate.html:4
-msgid "File annotate"
-msgstr "檔案註釋"
+#: rhodecode/templates/files/files_add.html:4
+#: rhodecode/templates/files/files_edit.html:4
+#, fuzzy, python-format
+msgid "%s Edit file"
+msgstr "編輯檔案"
-#: rhodecode/templates/files/files_annotate.html:12
-msgid "annotate"
-msgstr "註釋"
+#: rhodecode/templates/files/files_add.html:19
+#, fuzzy
+msgid "add file"
+msgstr "編輯檔案"
-#: rhodecode/templates/files/files_annotate.html:33
-#: rhodecode/templates/files/files_browser.html:160
-#: rhodecode/templates/files/files_source.html:2
-msgid "Revision"
-msgstr "修訂"
+#: rhodecode/templates/files/files_add.html:40
+#, fuzzy
+msgid "Add new file"
+msgstr "新增使用者"
-#: rhodecode/templates/files/files_annotate.html:36
-#: rhodecode/templates/files/files_browser.html:158
-#: rhodecode/templates/files/files_source.html:7
-msgid "Size"
-msgstr "大小"
+#: rhodecode/templates/files/files_add.html:45
+#, fuzzy
+msgid "File Name"
+msgstr "檔案名稱"
-#: rhodecode/templates/files/files_annotate.html:38
-#: rhodecode/templates/files/files_browser.html:159
-#: rhodecode/templates/files/files_source.html:9
-msgid "Mimetype"
-msgstr ""
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:58
+#, fuzzy
+msgid "or"
+msgstr "時"
-#: rhodecode/templates/files/files_annotate.html:41
-msgid "show source"
-msgstr "顯示原始碼"
+#: rhodecode/templates/files/files_add.html:49
+#: rhodecode/templates/files/files_add.html:54
+#, fuzzy
+msgid "Upload file"
+msgstr "編輯檔案"
-#: rhodecode/templates/files/files_annotate.html:43
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:14
-#: rhodecode/templates/files/files_source.html:51
-msgid "show as raw"
-msgstr "顯示原始文件"
+#: rhodecode/templates/files/files_add.html:58
+#, fuzzy
+msgid "Create new file"
+msgstr "建立使用者 %s"
-#: rhodecode/templates/files/files_annotate.html:45
-#: rhodecode/templates/files/files_source.html:16
-msgid "download as raw"
-msgstr "下載原始文件"
+#: rhodecode/templates/files/files_add.html:63
+#: rhodecode/templates/files/files_edit.html:39
+#: rhodecode/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr "位置"
-#: rhodecode/templates/files/files_annotate.html:54
-#: rhodecode/templates/files/files_source.html:25
-msgid "History"
-msgstr "歷史"
+#: rhodecode/templates/files/files_add.html:67
+msgid "use / to separate directories"
+msgstr ""
-#: rhodecode/templates/files/files_annotate.html:73
-#: rhodecode/templates/files/files_source.html:46
-#, python-format
-msgid "Binary file (%s)"
-msgstr "二進位檔 (%s)"
+#: rhodecode/templates/files/files_add.html:77
+#: rhodecode/templates/files/files_edit.html:63
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "commit message"
+msgstr "遞交資訊"
-#: rhodecode/templates/files/files_annotate.html:78
-#: rhodecode/templates/files/files_source.html:51
-msgid "File is too big to display"
-msgstr "顯示的檔案太大"
+#: rhodecode/templates/files/files_add.html:81
+#: rhodecode/templates/files/files_edit.html:67
+msgid "Commit changes"
+msgstr "遞交修改"
#: rhodecode/templates/files/files_browser.html:13
msgid "view"
@@ -2288,59 +3423,170 @@ msgstr ""
msgid "search file list"
msgstr "搜尋檔案列表"
-#: rhodecode/templates/files/files_browser.html:32
+#: rhodecode/templates/files/files_browser.html:31
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+#, fuzzy
+msgid "add new file"
+msgstr "新增使用者"
+
+#: rhodecode/templates/files/files_browser.html:35
msgid "Loading file list..."
msgstr "載入檔案列表..."
-#: rhodecode/templates/files/files_browser.html:111
-msgid "search truncated"
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Size"
+msgstr "大小"
+
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Mimetype"
msgstr ""
-#: rhodecode/templates/files/files_browser.html:122
-msgid "no matching files"
-msgstr "無符合的檔案"
+#: rhodecode/templates/files/files_browser.html:50
+#, fuzzy
+msgid "Last Revision"
+msgstr "下一個修訂"
-#: rhodecode/templates/files/files_browser.html:161
+#: rhodecode/templates/files/files_browser.html:51
msgid "Last modified"
msgstr "最後修改"
-#: rhodecode/templates/files/files_browser.html:162
+#: rhodecode/templates/files/files_browser.html:52
msgid "Last commiter"
msgstr "最後的遞交者"
-#: rhodecode/templates/files/files_edit.html:4
-msgid "Edit file"
-msgstr "編輯檔案"
-
#: rhodecode/templates/files/files_edit.html:19
msgid "edit file"
msgstr "編輯檔案"
-#: rhodecode/templates/files/files_edit.html:45
-#: rhodecode/templates/shortlog/shortlog_data.html:5
-msgid "commit message"
-msgstr "遞交資訊"
+#: rhodecode/templates/files/files_edit.html:49
+#: rhodecode/templates/files/files_source.html:38
+msgid "show annotation"
+msgstr "險是註釋"
+
+#: rhodecode/templates/files/files_edit.html:50
+#: rhodecode/templates/files/files_source.html:40
+#: rhodecode/templates/files/files_source.html:68
+msgid "show as raw"
+msgstr "顯示原始文件"
#: rhodecode/templates/files/files_edit.html:51
-msgid "Commit changes"
-msgstr "遞交修改"
+#: rhodecode/templates/files/files_source.html:41
+msgid "download as raw"
+msgstr "下載原始文件"
-#: rhodecode/templates/files/files_source.html:12
-msgid "show annotation"
-msgstr "險是註釋"
+#: rhodecode/templates/files/files_edit.html:54
+#, fuzzy
+msgid "source"
+msgstr "顯示原始碼"
+
+#: rhodecode/templates/files/files_edit.html:59
+#, fuzzy
+msgid "Editing file"
+msgstr "編輯檔案"
+
+#: rhodecode/templates/files/files_source.html:2
+msgid "History"
+msgstr "歷史"
+
+#: rhodecode/templates/files/files_source.html:9
+#, fuzzy
+msgid "diff to revision"
+msgstr "下一個修訂"
+
+#: rhodecode/templates/files/files_source.html:10
+#, fuzzy
+msgid "show at revision"
+msgstr "下一個修訂"
+
+#: rhodecode/templates/files/files_source.html:14
+#, fuzzy, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] "作者"
+
+#: rhodecode/templates/files/files_source.html:36
+msgid "show source"
+msgstr "顯示原始碼"
+
+#: rhodecode/templates/files/files_source.html:59
+#, python-format
+msgid "Binary file (%s)"
+msgstr "二進位檔 (%s)"
+
+#: rhodecode/templates/files/files_source.html:68
+msgid "File is too big to display"
+msgstr "顯示的檔案太大"
-#: rhodecode/templates/files/files_source.html:153
+#: rhodecode/templates/files/files_source.html:124
msgid "Selection link"
msgstr ""
+#: rhodecode/templates/files/files_ypjax.html:5
+#, fuzzy
+msgid "annotation"
+msgstr "險是註釋"
+
+#: rhodecode/templates/files/files_ypjax.html:15
+msgid "Go back"
+msgstr ""
+
+#: rhodecode/templates/files/files_ypjax.html:16
+msgid "No files at given path"
+msgstr ""
+
+#: rhodecode/templates/followers/followers.html:5
+#, fuzzy, python-format
+msgid "%s Followers"
+msgstr "追蹤者"
+
#: rhodecode/templates/followers/followers.html:13
msgid "followers"
msgstr "追蹤者"
#: rhodecode/templates/followers/followers_data.html:12
-msgid "Started following"
+#, fuzzy
+msgid "Started following -"
msgstr "開始追蹤"
+#: rhodecode/templates/forks/fork.html:5
+#, fuzzy, python-format
+msgid "%s Fork"
+msgstr "分支"
+
+#: rhodecode/templates/forks/fork.html:31
+msgid "Fork name"
+msgstr "分支名稱"
+
+#: rhodecode/templates/forks/fork.html:68
+msgid "Private"
+msgstr "私有"
+
+#: rhodecode/templates/forks/fork.html:77
+#, fuzzy
+msgid "Copy permissions"
+msgstr "權限"
+
+#: rhodecode/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:86
+msgid "Update after clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:90
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: rhodecode/templates/forks/fork.html:94
+msgid "fork this repository"
+msgstr "fork 這個版本庫"
+
+#: rhodecode/templates/forks/forks.html:5
+#, fuzzy, python-format
+msgid "%s Forks"
+msgstr "分支"
+
#: rhodecode/templates/forks/forks.html:13
msgid "forks"
msgstr "分支"
@@ -2349,184 +3595,419 @@ msgstr "分支"
msgid "forked"
msgstr "已建立分支"
-#: rhodecode/templates/forks/forks_data.html:34
+#: rhodecode/templates/forks/forks_data.html:38
msgid "There are no forks yet"
msgstr "尚未有任何 fork"
-#: rhodecode/templates/journal/journal.html:34
-msgid "Following"
-msgstr "已追蹤"
+#: rhodecode/templates/journal/journal.html:13
+#, fuzzy
+msgid "ATOM journal feed"
+msgstr "%s 公開日誌 %s feed"
+
+#: rhodecode/templates/journal/journal.html:14
+#, fuzzy
+msgid "RSS journal feed"
+msgstr "%s 公開日誌 %s feed"
+
+#: rhodecode/templates/journal/journal.html:24
+#: rhodecode/templates/pullrequests/pullrequest.html:27
+msgid "Refresh"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:27
+#: rhodecode/templates/journal/public_journal.html:24
+msgid "RSS feed"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:30
+#: rhodecode/templates/journal/public_journal.html:27
+msgid "ATOM feed"
+msgstr ""
#: rhodecode/templates/journal/journal.html:41
+#, fuzzy
+msgid "Watched"
+msgstr "快取"
+
+#: rhodecode/templates/journal/journal.html:46
+#, fuzzy
+msgid "ADD"
+msgstr "新增"
+
+#: rhodecode/templates/journal/journal.html:114
msgid "following user"
msgstr "追蹤使用者"
-#: rhodecode/templates/journal/journal.html:41
+#: rhodecode/templates/journal/journal.html:114
msgid "user"
msgstr "使用者"
-#: rhodecode/templates/journal/journal.html:65
+#: rhodecode/templates/journal/journal.html:147
msgid "You are not following any users or repositories"
msgstr "您尚未追蹤任何使用者或版本庫"
-#: rhodecode/templates/journal/journal_data.html:46
+#: rhodecode/templates/journal/journal_data.html:47
msgid "No entries yet"
msgstr ""
-#: rhodecode/templates/journal/public_journal.html:17
+#: rhodecode/templates/journal/public_journal.html:13
+#, fuzzy
+msgid "ATOM public journal feed"
+msgstr "%s 公開日誌 %s feed"
+
+#: rhodecode/templates/journal/public_journal.html:14
+#, fuzzy
+msgid "RSS public journal feed"
+msgstr "%s 公開日誌 %s feed"
+
+#: rhodecode/templates/journal/public_journal.html:21
msgid "Public Journal"
msgstr "開放日誌"
-#: rhodecode/templates/search/search.html:7
-#: rhodecode/templates/search/search.html:26
-msgid "in repository: "
+#: rhodecode/templates/pullrequests/pullrequest.html:4
+#: rhodecode/templates/pullrequests/pullrequest.html:12
+msgid "New pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:28
+msgid "refresh overview"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:66
+#, fuzzy
+msgid "Detailed compare view"
+msgstr "比較顯示"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:70
+#: rhodecode/templates/pullrequests/pullrequest_show.html:82
+msgid "Pull request reviewers"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:79
+#: rhodecode/templates/pullrequests/pullrequest_show.html:94
+#, fuzzy
+msgid "owner"
+msgstr "擁有者"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:91
+#: rhodecode/templates/pullrequests/pullrequest_show.html:109
+msgid "Add reviewer to this pull request."
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest.html:97
+#, fuzzy
+msgid "Create new pull request"
+msgstr "建立使用者 %s"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:106
+#: rhodecode/templates/pullrequests/pullrequest_show.html:25
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:33
+#, fuzzy
+msgid "Title"
+msgstr "寫"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:115
+#, fuzzy
+msgid "description"
+msgstr "描述"
+
+#: rhodecode/templates/pullrequests/pullrequest.html:123
+msgid "Send pull request"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:23
+#, python-format
+msgid "Closed %s"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:31
+#, fuzzy
+msgid "Status"
+msgstr "變更"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:36
+msgid "Pull request status"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:44
+msgid "Still not reviewed by"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:47
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:54
+#, fuzzy
+msgid "Created on"
+msgstr "建立使用者 %s"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:61
+#, fuzzy
+msgid "Compare view"
+msgstr "比較顯示"
+
+#: rhodecode/templates/pullrequests/pullrequest_show.html:65
+#, fuzzy
+msgid "Incoming changesets"
+msgstr "尚未有任何變更"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:4
+#, fuzzy
+msgid "all pull requests"
+msgstr "建立使用者 %s"
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:12
+msgid "All pull requests"
+msgstr ""
+
+#: rhodecode/templates/pullrequests/pullrequest_show_all.html:27
+msgid "Closed"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:6
+#, fuzzy, python-format
+msgid "Search \"%s\" in repository: %s"
+msgstr "於版本庫:"
+
+#: rhodecode/templates/search/search.html:8
+#, fuzzy, python-format
+msgid "Search \"%s\" in all repositories"
+msgstr "於所有的版本庫"
+
+#: rhodecode/templates/search/search.html:12
+#: rhodecode/templates/search/search.html:32
+#, fuzzy, python-format
+msgid "Search in repository: %s"
msgstr "於版本庫:"
-#: rhodecode/templates/search/search.html:9
-#: rhodecode/templates/search/search.html:28
-msgid "in all repositories"
+#: rhodecode/templates/search/search.html:14
+#: rhodecode/templates/search/search.html:34
+#, fuzzy
+msgid "Search in all repositories"
msgstr "於所有的版本庫"
-#: rhodecode/templates/search/search.html:42
+#: rhodecode/templates/search/search.html:48
msgid "Search term"
msgstr "搜尋關鍵字"
-#: rhodecode/templates/search/search.html:54
+#: rhodecode/templates/search/search.html:60
msgid "Search in"
msgstr "搜尋範圍"
-#: rhodecode/templates/search/search.html:57
+#: rhodecode/templates/search/search.html:63
msgid "File contents"
msgstr "文件內容"
-#: rhodecode/templates/search/search.html:59
+#: rhodecode/templates/search/search.html:64
+#, fuzzy
+msgid "Commit messages"
+msgstr "遞交資訊"
+
+#: rhodecode/templates/search/search.html:65
msgid "File names"
msgstr "檔案名稱"
-#: rhodecode/templates/search/search_content.html:20
+#: rhodecode/templates/search/search_commit.html:35
+#: rhodecode/templates/search/search_content.html:21
#: rhodecode/templates/search/search_path.html:15
msgid "Permission denied"
msgstr "權限不足"
-#: rhodecode/templates/settings/repo_fork.html:5
-msgid "Fork"
-msgstr "分支"
-
-#: rhodecode/templates/settings/repo_fork.html:31
-msgid "Fork name"
-msgstr "分支名稱"
-
-#: rhodecode/templates/settings/repo_fork.html:55
-msgid "fork this repository"
-msgstr "fork 這個版本庫"
+#: rhodecode/templates/settings/repo_settings.html:5
+#, fuzzy, python-format
+msgid "%s Settings"
+msgstr "設定"
#: rhodecode/templates/shortlog/shortlog.html:5
-#: rhodecode/templates/summary/summary.html:666
-msgid "Shortlog"
+#, fuzzy, python-format
+msgid "%s Shortlog"
msgstr "簡短紀錄"
#: rhodecode/templates/shortlog/shortlog.html:14
msgid "shortlog"
msgstr "簡短紀錄"
-#: rhodecode/templates/shortlog/shortlog_data.html:6
+#: rhodecode/templates/shortlog/shortlog_data.html:7
msgid "age"
msgstr ""
+#: rhodecode/templates/shortlog/shortlog_data.html:18
+#, fuzzy
+msgid "No commit message"
+msgstr "遞交資訊"
+
+#: rhodecode/templates/shortlog/shortlog_data.html:62
+msgid "Add or upload files directly via RhodeCode"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:71
+msgid "Push new repo"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:79
+#, fuzzy
+msgid "Existing repository?"
+msgstr "Git 版本庫"
+
+#: rhodecode/templates/summary/summary.html:4
+#, fuzzy, python-format
+msgid "%s Summary"
+msgstr "概況"
+
#: rhodecode/templates/summary/summary.html:12
msgid "summary"
msgstr "概況"
-#: rhodecode/templates/summary/summary.html:79
+#: rhodecode/templates/summary/summary.html:20
+#, fuzzy, python-format
+msgid "repo %s ATOM feed"
+msgstr "訂閱 %s atom"
+
+#: rhodecode/templates/summary/summary.html:21
+#, fuzzy, python-format
+msgid "repo %s RSS feed"
+msgstr "訂閱 %s rss"
+
+#: rhodecode/templates/summary/summary.html:49
+#: rhodecode/templates/summary/summary.html:52
+#, fuzzy
+msgid "ATOM"
+msgstr "作者"
+
+#: rhodecode/templates/summary/summary.html:82
+#, fuzzy, python-format
+msgid "Non changable ID %s"
+msgstr "沒有修改"
+
+#: rhodecode/templates/summary/summary.html:87
+msgid "public"
+msgstr "公開"
+
+#: rhodecode/templates/summary/summary.html:95
msgid "remote clone"
msgstr "遠端複製"
-#: rhodecode/templates/summary/summary.html:121
-msgid "by"
-msgstr ""
+#: rhodecode/templates/summary/summary.html:116
+msgid "Contact"
+msgstr "聯絡方式"
-#: rhodecode/templates/summary/summary.html:128
+#: rhodecode/templates/summary/summary.html:130
msgid "Clone url"
msgstr "複製連結"
-#: rhodecode/templates/summary/summary.html:137
-msgid "Trending source files"
+#: rhodecode/templates/summary/summary.html:133
+#, fuzzy
+msgid "Show by Name"
+msgstr "顯示更多"
+
+#: rhodecode/templates/summary/summary.html:134
+msgid "Show by ID"
msgstr ""
-#: rhodecode/templates/summary/summary.html:146
+#: rhodecode/templates/summary/summary.html:142
+#, fuzzy
+msgid "Trending files"
+msgstr "編輯檔案"
+
+#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:166
+#: rhodecode/templates/summary/summary.html:194
+msgid "enable"
+msgstr "啟用"
+
+#: rhodecode/templates/summary/summary.html:158
msgid "Download"
msgstr "下載"
-#: rhodecode/templates/summary/summary.html:150
+#: rhodecode/templates/summary/summary.html:162
msgid "There are no downloads yet"
msgstr "沒有任何下載"
-#: rhodecode/templates/summary/summary.html:152
+#: rhodecode/templates/summary/summary.html:164
msgid "Downloads are disabled for this repository"
msgstr "這個版本庫的下載已停用"
-#: rhodecode/templates/summary/summary.html:154
-#: rhodecode/templates/summary/summary.html:320
-msgid "enable"
-msgstr "啟用"
-
-#: rhodecode/templates/summary/summary.html:162
-#: rhodecode/templates/summary/summary.html:297
-#, python-format
-msgid "Download %s as %s"
-msgstr "下載 %s 為 %s"
+#: rhodecode/templates/summary/summary.html:170
+#, fuzzy
+msgid "Download as zip"
+msgstr "下載原始文件"
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:173
msgid "Check this to download archive with subrepos"
msgstr ""
-#: rhodecode/templates/summary/summary.html:168
+#: rhodecode/templates/summary/summary.html:173
msgid "with subrepos"
msgstr ""
-#: rhodecode/templates/summary/summary.html:176
-msgid "Feeds"
+#: rhodecode/templates/summary/summary.html:186
+msgid "Commit activity by day / author"
msgstr ""
-#: rhodecode/templates/summary/summary.html:257
-#: rhodecode/templates/summary/summary.html:684
-#: rhodecode/templates/summary/summary.html:695
-msgid "show more"
-msgstr "顯示更多"
+#: rhodecode/templates/summary/summary.html:197
+msgid "Stats gathered: "
+msgstr ""
-#: rhodecode/templates/summary/summary.html:312
-msgid "Commit activity by day / author"
+#: rhodecode/templates/summary/summary.html:218
+msgid "Shortlog"
+msgstr "簡短紀錄"
+
+#: rhodecode/templates/summary/summary.html:220
+#, fuzzy
+msgid "Quick start"
+msgstr "快速過濾..."
+
+#: rhodecode/templates/summary/summary.html:233
+#, python-format
+msgid "Readme file at revision '%s'"
msgstr ""
-#: rhodecode/templates/summary/summary.html:324
-msgid "Loaded in"
+#: rhodecode/templates/summary/summary.html:236
+msgid "Permalink to this readme"
msgstr ""
-#: rhodecode/templates/summary/summary.html:603
+#: rhodecode/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr "下載 %s 為 %s"
+
+#: rhodecode/templates/summary/summary.html:650
msgid "commits"
msgstr "遞交"
-#: rhodecode/templates/summary/summary.html:604
+#: rhodecode/templates/summary/summary.html:651
msgid "files added"
msgstr "多個檔案新增"
-#: rhodecode/templates/summary/summary.html:605
+#: rhodecode/templates/summary/summary.html:652
msgid "files changed"
msgstr "多個檔案修改"
-#: rhodecode/templates/summary/summary.html:606
+#: rhodecode/templates/summary/summary.html:653
msgid "files removed"
msgstr "移除多個檔案"
-#: rhodecode/templates/summary/summary.html:610
+#: rhodecode/templates/summary/summary.html:656
+msgid "commit"
+msgstr "遞交"
+
+#: rhodecode/templates/summary/summary.html:657
msgid "file added"
msgstr "檔案新增"
-#: rhodecode/templates/summary/summary.html:611
+#: rhodecode/templates/summary/summary.html:658
msgid "file changed"
msgstr "檔案修改"
-#: rhodecode/templates/summary/summary.html:612
+#: rhodecode/templates/summary/summary.html:659
msgid "file removed"
msgstr "移除檔案"
+#: rhodecode/templates/tags/tags.html:5
+#, fuzzy, python-format
+msgid "%s Tags"
+msgstr "之前"
+
diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py
index 49416f78..d234e3e2 100644
--- a/rhodecode/lib/auth.py
+++ b/rhodecode/lib/auth.py
@@ -35,14 +35,9 @@ from pylons import config, url, request
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
-from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
+from rhodecode import __platform__, is_windows, is_unix
from rhodecode.model.meta import Session
-if __platform__ in PLATFORM_WIN:
- from hashlib import sha256
-if __platform__ in PLATFORM_OTHERS:
- import bcrypt
-
from rhodecode.lib.utils2 import str2bool, safe_unicode
from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
@@ -97,9 +92,11 @@ class RhodeCodeCrypto(object):
:param password: password to hash
"""
- if __platform__ in PLATFORM_WIN:
+ if is_windows:
+ from hashlib import sha256
return sha256(str_).hexdigest()
- elif __platform__ in PLATFORM_OTHERS:
+ elif is_unix:
+ import bcrypt
return bcrypt.hashpw(str_, bcrypt.gensalt(10))
else:
raise Exception('Unknown or unsupported platform %s' \
@@ -115,9 +112,11 @@ class RhodeCodeCrypto(object):
:param hashed: password in hashed form
"""
- if __platform__ in PLATFORM_WIN:
+ if is_windows:
+ from hashlib import sha256
return sha256(password).hexdigest() == hashed
- elif __platform__ in PLATFORM_OTHERS:
+ elif is_unix:
+ import bcrypt
return bcrypt.hashpw(password, hashed) == hashed
else:
raise Exception('Unknown or unsupported platform %s' \
@@ -236,7 +235,7 @@ def authenticate(username, password):
user_attrs):
log.info('created new ldap user %s' % username)
- Session.commit()
+ Session().commit()
return True
except (LdapUsernameError, LdapPasswordError,):
pass
@@ -263,7 +262,7 @@ def login_container_auth(username):
return None
user.update_lastlogin()
- Session.commit()
+ Session().commit()
log.debug('User %s is now logged in by container authentication',
user.username)
@@ -325,6 +324,7 @@ class AuthUser(object):
self.email = ''
self.is_authenticated = False
self.admin = False
+ self.inherit_default_permissions = False
self.permissions = {}
self._api_key = api_key
self.propagate_data()
@@ -351,6 +351,7 @@ class AuthUser(object):
log.debug('Auth User lookup by USER NAME %s' % self.username)
dbuser = login_container_auth(self.username)
if dbuser is not None:
+ log.debug('filling all attributes to object')
for k, v in dbuser.get_dict().items():
setattr(self, k, v)
self.set_authenticated()
@@ -460,8 +461,9 @@ class LoginRequired(object):
loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
if user.is_authenticated or api_access_ok:
- log.info('user %s is authenticated and granted access to %s' % (
- user.username, loc)
+ reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
+ log.info('user %s is authenticated and granted access to %s '
+ 'using %s' % (user.username, loc, reason)
)
return func(*fargs, **fkwargs)
else:
@@ -768,7 +770,7 @@ class HasReposGroupPermissionAny(PermsFunction):
class HasReposGroupPermissionAll(PermsFunction):
def __call__(self, group_name=None, check_Location=''):
self.group_name = group_name
- return super(HasReposGroupPermissionAny, self).__call__(check_Location)
+ return super(HasReposGroupPermissionAll, self).__call__(check_Location)
def check_permissions(self):
try:
@@ -805,7 +807,7 @@ class HasPermissionAnyMiddleware(object):
return self.check_permissions()
def check_permissions(self):
- log.debug('checking mercurial protocol '
+ log.debug('checking VCS protocol '
'permissions %s for user:%s repository:%s', self.user_perms,
self.username, self.repo_name)
if self.required_perms.intersection(self.user_perms):
diff --git a/rhodecode/lib/auth_ldap.py b/rhodecode/lib/auth_ldap.py
index 6ad8c3da..e5d0cb6a 100644
--- a/rhodecode/lib/auth_ldap.py
+++ b/rhodecode/lib/auth_ldap.py
@@ -27,6 +27,7 @@ import logging
from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
LdapPasswordError
+from rhodecode.lib.utils2 import safe_str
log = logging.getLogger(__name__)
@@ -60,15 +61,15 @@ class AuthLdap(object):
self.LDAP_SERVER_PORT = port
# USE FOR READ ONLY BIND TO LDAP SERVER
- self.LDAP_BIND_DN = bind_dn
- self.LDAP_BIND_PASS = bind_pass
+ self.LDAP_BIND_DN = safe_str(bind_dn)
+ self.LDAP_BIND_PASS = safe_str(bind_pass)
self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
self.LDAP_SERVER_ADDRESS,
self.LDAP_SERVER_PORT)
- self.BASE_DN = base_dn
- self.LDAP_FILTER = ldap_filter
+ self.BASE_DN = safe_str(base_dn)
+ self.LDAP_FILTER = safe_str(ldap_filter)
self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
self.attr_login = attr_login
diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py
index 16f3a9a2..99df848a 100644
--- a/rhodecode/lib/base.py
+++ b/rhodecode/lib/base.py
@@ -8,6 +8,7 @@ import traceback
from paste.auth.basic import AuthBasicAuthenticator
from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
+from webob.exc import HTTPClientError
from paste.httpheaders import WWW_AUTHENTICATE
from pylons import config, tmpl_context as c, request, session, url
@@ -17,19 +18,47 @@ from pylons.templating import render_mako as render
from rhodecode import __version__, BACKENDS
-from rhodecode.lib.utils2 import str2bool, safe_unicode
+from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
+ safe_str
from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
HasPermissionAnyMiddleware, CookieStoreWrapper
from rhodecode.lib.utils import get_repo_slug, invalidate_cache
from rhodecode.model import meta
-from rhodecode.model.db import Repository
+from rhodecode.model.db import Repository, RhodeCodeUi, User
from rhodecode.model.notification import NotificationModel
from rhodecode.model.scm import ScmModel
+from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
+def _get_ip_addr(environ):
+ proxy_key = 'HTTP_X_REAL_IP'
+ proxy_key2 = 'HTTP_X_FORWARDED_FOR'
+ def_key = 'REMOTE_ADDR'
+
+ ip = environ.get(proxy_key2)
+ if ip:
+ return ip
+
+ ip = environ.get(proxy_key)
+
+ if ip:
+ return ip
+
+ ip = environ.get(def_key, '0.0.0.0')
+ return ip
+
+
+def _get_access_path(environ):
+ path = environ.get('PATH_INFO')
+ org_req = environ.get('pylons.original_request')
+ if org_req:
+ path = org_req.environ.get('PATH_INFO')
+ return path
+
+
class BasicAuth(AuthBasicAuthenticator):
def __init__(self, realm, authfunc, auth_http_code=None):
@@ -117,15 +146,65 @@ class BaseVCSController(object):
return True
def _get_ip_addr(self, environ):
- proxy_key = 'HTTP_X_REAL_IP'
- proxy_key2 = 'HTTP_X_FORWARDED_FOR'
- def_key = 'REMOTE_ADDR'
+ return _get_ip_addr(environ)
+
+ def _check_ssl(self, environ, start_response):
+ """
+ Checks the SSL check flag and returns False if SSL is not present
+ and required True otherwise
+ """
+ org_proto = environ['wsgi._org_proto']
+ #check if we have SSL required ! if not it's a bad request !
+ require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
+ if require_ssl and org_proto == 'http':
+ log.debug('proto is %s and SSL is required BAD REQUEST !'
+ % org_proto)
+ return False
+ return True
- return environ.get(proxy_key2,
- environ.get(proxy_key,
- environ.get(def_key, '0.0.0.0')
- )
- )
+ def _check_locking_state(self, environ, action, repo, user_id):
+ """
+ Checks locking on this repository, if locking is enabled and lock is
+ present returns a tuple of make_lock, locked, locked_by.
+ make_lock can have 3 states None (do nothing) True, make lock
+ False release lock, This value is later propagated to hooks, which
+ do the locking. Think about this as signals passed to hooks what to do.
+
+ """
+ locked = False # defines that locked error should be thrown to user
+ make_lock = None
+ repo = Repository.get_by_repo_name(repo)
+ user = User.get(user_id)
+
+ # this is kind of hacky, but due to how mercurial handles client-server
+ # server see all operation on changeset; bookmarks, phases and
+ # obsolescence marker in different transaction, we don't want to check
+ # locking on those
+ obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
+ locked_by = repo.locked
+ if repo and repo.enable_locking and not obsolete_call:
+ if action == 'push':
+ #check if it's already locked !, if it is compare users
+ user_id, _date = repo.locked
+ if user.user_id == user_id:
+ log.debug('Got push from user %s, now unlocking' % (user))
+ # unlock if we have push from user who locked
+ make_lock = False
+ else:
+ # we're not the same user who locked, ban with 423 !
+ locked = True
+ if action == 'pull':
+ if repo.locked[0] and repo.locked[1]:
+ locked = True
+ else:
+ log.debug('Setting lock on repo %s by %s' % (repo, user))
+ make_lock = True
+
+ else:
+ log.debug('Repository %s do not have locking enabled' % (repo))
+ log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
+ % (make_lock, locked, locked_by))
+ return make_lock, locked, locked_by
def __call__(self, environ, start_response):
start = time.time()
@@ -145,6 +224,12 @@ class BaseController(WSGIController):
c.rhodecode_name = config.get('rhodecode_title')
c.use_gravatar = str2bool(config.get('use_gravatar'))
c.ga_code = config.get('rhodecode_ga_code')
+ # Visual options
+ c.visual = AttributeDict({})
+ c.visual.show_public_icon = str2bool(config.get('rhodecode_show_public_icon'))
+ c.visual.show_private_icon = str2bool(config.get('rhodecode_show_private_icon'))
+ c.visual.stylify_metatags = str2bool(config.get('rhodecode_stylify_metatags'))
+
c.repo_name = get_repo_slug(request)
c.backends = BACKENDS.keys()
c.unread_notifications = NotificationModel()\
@@ -153,6 +238,7 @@ class BaseController(WSGIController):
self.sa = meta.Session
self.scm_model = ScmModel(self.sa)
+ self.ip_addr = ''
def __call__(self, environ, start_response):
"""Invoke the Controller"""
@@ -161,6 +247,7 @@ class BaseController(WSGIController):
# available in environ['pylons.routes_dict']
start = time.time()
try:
+ self.ip_addr = _get_ip_addr(environ)
# make sure that we update permissions each time we call controller
api_key = request.GET.get('api_key')
cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
@@ -174,13 +261,14 @@ class BaseController(WSGIController):
self.rhodecode_user.set_authenticated(
cookie_store.get('is_authenticated')
)
- log.info('User: %s accessed %s' % (
- auth_user, safe_unicode(environ.get('PATH_INFO')))
+ log.info('IP: %s User: %s accessed %s' % (
+ self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
)
return WSGIController.__call__(self, environ, start_response)
finally:
- log.info('Request to %s time: %.3fs' % (
- safe_unicode(environ.get('PATH_INFO')), time.time() - start)
+ log.info('IP: %s Request to %s time: %.3fs' % (
+ _get_ip_addr(environ),
+ safe_unicode(_get_access_path(environ)), time.time() - start)
)
meta.Session.remove()
@@ -200,7 +288,7 @@ class BaseRepoController(BaseController):
super(BaseRepoController, self).__before__()
if c.repo_name:
- c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
+ dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
if c.rhodecode_repo is None:
@@ -209,5 +297,7 @@ class BaseRepoController(BaseController):
redirect(url('home'))
- c.repository_followers = self.scm_model.get_followers(c.repo_name)
- c.repository_forks = self.scm_model.get_forks(c.repo_name)
+ # some globals counter for menu
+ c.repository_followers = self.scm_model.get_followers(dbr)
+ c.repository_forks = self.scm_model.get_forks(dbr)
+ c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
diff --git a/rhodecode/lib/celerylib/__init__.py b/rhodecode/lib/celerylib/__init__.py
index 7f2f4074..7cbec266 100644
--- a/rhodecode/lib/celerylib/__init__.py
+++ b/rhodecode/lib/celerylib/__init__.py
@@ -112,7 +112,7 @@ def get_session():
if CELERY_ON:
engine = engine_from_config(config, 'sqlalchemy.db1.')
init_model(engine)
- sa = meta.Session
+ sa = meta.Session()
return sa
diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py
index 5d25ffe7..bba86196 100644
--- a/rhodecode/lib/celerylib/tasks.py
+++ b/rhodecode/lib/celerylib/tasks.py
@@ -75,7 +75,7 @@ def get_logger(cls):
@dbsession
def whoosh_index(repo_location, full_index):
from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
- log = whoosh_index.get_logger(whoosh_index)
+ log = get_logger(whoosh_index)
DBS = get_session()
index_location = config['index_dir']
@@ -367,32 +367,46 @@ def create_repo_fork(form_data, cur_user):
:param cur_user:
"""
from rhodecode.model.repo import RepoModel
+ from rhodecode.model.user import UserModel
log = get_logger(create_repo_fork)
DBS = get_session()
base_path = Repository.base_path()
+ cur_user = UserModel(DBS)._get_user(cur_user)
- fork_repo = RepoModel(DBS).create(form_data, cur_user,
- just_db=True, fork=True)
-
- alias = form_data['repo_type']
- org_repo_name = form_data['org_path']
fork_name = form_data['repo_name_full']
+ repo_type = form_data['repo_type']
+ description = form_data['description']
+ owner = cur_user
+ private = form_data['private']
+ clone_uri = form_data.get('clone_uri')
+ repos_group = form_data['repo_group']
+ landing_rev = form_data['landing_rev']
+ copy_fork_permissions = form_data.get('copy_permissions')
+ fork_of = RepoModel(DBS)._get_repo(form_data.get('fork_parent_id'))
+
+ fork_repo = RepoModel(DBS).create_repo(
+ fork_name, repo_type, description, owner, private, clone_uri,
+ repos_group, landing_rev, just_db=True, fork_of=fork_of,
+ copy_fork_permissions=copy_fork_permissions
+ )
+
update_after_clone = form_data['update_after_clone']
- source_repo_path = os.path.join(base_path, org_repo_name)
+
+ source_repo_path = os.path.join(base_path, fork_of.repo_name)
destination_fork_path = os.path.join(base_path, fork_name)
log.info('creating fork of %s as %s', source_repo_path,
destination_fork_path)
- backend = get_backend(alias)
+ backend = get_backend(repo_type)
backend(safe_str(destination_fork_path), create=True,
src_url=safe_str(source_repo_path),
update_after_clone=update_after_clone)
log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
- org_repo_name, '', DBS)
+ fork_of.repo_name, '', DBS)
action_logger(cur_user, 'user_created_fork:%s' % fork_name,
fork_name, '', DBS)
diff --git a/rhodecode/lib/cleanup.py b/rhodecode/lib/cleanup.py
new file mode 100644
index 00000000..f9985c05
--- /dev/null
+++ b/rhodecode/lib/cleanup.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+"""
+ package.rhodecode.lib.cleanup
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :created_on: Jul 14, 2012
+ :author: marcink
+ :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+from __future__ import with_statement
+
+import os
+import sys
+import re
+import shutil
+import logging
+import datetime
+
+from os.path import dirname as dn, join as jn
+from rhodecode.model import init_model
+from rhodecode.lib.utils2 import engine_from_config
+from rhodecode.model.db import RhodeCodeUi
+
+
+#to get the rhodecode import
+sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
+
+from rhodecode.lib.utils import BasePasterCommand, Command, ask_ok,\
+ REMOVED_REPO_PAT, add_cache
+
+log = logging.getLogger(__name__)
+
+
+class CleanupCommand(BasePasterCommand):
+
+ max_args = 1
+ min_args = 1
+
+ usage = "CONFIG_FILE"
+ summary = "Cleanup deleted repos"
+ group_name = "RhodeCode"
+ takes_config_file = -1
+ parser = Command.standard_parser(verbose=True)
+
+ def _parse_older_than(self, val):
+ regex = re.compile(r'((?P<days>\d+?)d)?((?P<hours>\d+?)h)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')
+ parts = regex.match(val)
+ if not parts:
+ return
+ parts = parts.groupdict()
+ time_params = {}
+ for (name, param) in parts.iteritems():
+ if param:
+ time_params[name] = int(param)
+ return datetime.timedelta(**time_params)
+
+ def _extract_date(self, name):
+ """
+ Extract the date part from rm__<date> pattern of removed repos,
+ and convert it to datetime object
+
+ :param name:
+ """
+ date_part = name[4:19] # 4:19 since we don't parse milisecods
+ return datetime.datetime.strptime(date_part, '%Y%m%d_%H%M%S')
+
+ def command(self):
+ logging.config.fileConfig(self.path_to_ini_file)
+ from pylons import config
+
+ #get to remove repos !!
+ add_cache(config)
+ engine = engine_from_config(config, 'sqlalchemy.db1.')
+ init_model(engine)
+
+ repos_location = RhodeCodeUi.get_repos_location()
+ to_remove = []
+ for loc in os.listdir(repos_location):
+ if REMOVED_REPO_PAT.match(loc):
+ to_remove.append([loc, self._extract_date(loc)])
+
+ #filter older than (if present)!
+ now = datetime.datetime.now()
+ older_than = self.options.older_than
+ if older_than:
+ to_remove_filtered = []
+ older_than_date = self._parse_older_than(older_than)
+ for name, date_ in to_remove:
+ repo_age = now - date_
+ if repo_age > older_than_date:
+ to_remove_filtered.append([name, date_])
+
+ to_remove = to_remove_filtered
+ print >> sys.stdout, 'removing [%s] deleted repos older than %s[%s]' \
+ % (len(to_remove), older_than, older_than_date)
+ else:
+ print >> sys.stdout, 'removing all [%s] deleted repos' \
+ % len(to_remove)
+ if self.options.dont_ask or not to_remove:
+ # don't ask just remove !
+ remove = True
+ else:
+ remove = ask_ok('are you sure to remove listed repos \n%s [y/n]?'
+ % ', \n'.join(['%s removed on %s' % (x[0], x[1])
+ for x in to_remove]))
+
+ if remove:
+ for name, date_ in to_remove:
+ print >> sys.stdout, 'removing repository %s' % name
+ shutil.rmtree(os.path.join(repos_location, name))
+ else:
+ print 'nothing done exiting...'
+ sys.exit(0)
+
+ def update_parser(self):
+ self.parser.add_option('--older-than',
+ action='store',
+ dest='older_than',
+ help=(
+ "only remove repos that have been removed "
+ "at least given time ago "
+ "ex. --older-than=30d deletes repositores "
+ "removed more than 30days ago. Possible options "
+ "d[ays]/h[ours]/m[inutes]/s[seconds]. OPTIONAL"),
+ )
+ self.parser.add_option('--dont-ask',
+ action='store_true',
+ dest='dont_ask',
+ help=("Don't ask to remove repos"))
diff --git a/rhodecode/lib/compat.py b/rhodecode/lib/compat.py
index 36dbe9b4..bdefbd63 100644
--- a/rhodecode/lib/compat.py
+++ b/rhodecode/lib/compat.py
@@ -25,7 +25,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-from rhodecode import __platform__, PLATFORM_WIN
+from rhodecode import __platform__, PLATFORM_WIN, __py_version__
#==============================================================================
# json
@@ -394,3 +394,192 @@ except ImportError:
result = [x + [y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
+
+
+#==============================================================================
+# BytesIO
+#==============================================================================
+
+try:
+ from io import BytesIO
+except ImportError:
+ from cStringIO import StringIO as BytesIO
+
+
+#==============================================================================
+# deque
+#==============================================================================
+
+if __py_version__ >= (2, 6):
+ from collections import deque
+else:
+ #need to implement our own deque with maxlen
+ class deque(object):
+
+ def __init__(self, iterable=(), maxlen=-1):
+ if not hasattr(self, 'data'):
+ self.left = self.right = 0
+ self.data = {}
+ self.maxlen = maxlen
+ self.extend(iterable)
+
+ def append(self, x):
+ self.data[self.right] = x
+ self.right += 1
+ if self.maxlen != -1 and len(self) > self.maxlen:
+ self.popleft()
+
+ def appendleft(self, x):
+ self.left -= 1
+ self.data[self.left] = x
+ if self.maxlen != -1 and len(self) > self.maxlen:
+ self.pop()
+
+ def pop(self):
+ if self.left == self.right:
+ raise IndexError('cannot pop from empty deque')
+ self.right -= 1
+ elem = self.data[self.right]
+ del self.data[self.right]
+ return elem
+
+ def popleft(self):
+ if self.left == self.right:
+ raise IndexError('cannot pop from empty deque')
+ elem = self.data[self.left]
+ del self.data[self.left]
+ self.left += 1
+ return elem
+
+ def clear(self):
+ self.data.clear()
+ self.left = self.right = 0
+
+ def extend(self, iterable):
+ for elem in iterable:
+ self.append(elem)
+
+ def extendleft(self, iterable):
+ for elem in iterable:
+ self.appendleft(elem)
+
+ def rotate(self, n=1):
+ if self:
+ n %= len(self)
+ for i in xrange(n):
+ self.appendleft(self.pop())
+
+ def __getitem__(self, i):
+ if i < 0:
+ i += len(self)
+ try:
+ return self.data[i + self.left]
+ except KeyError:
+ raise IndexError
+
+ def __setitem__(self, i, value):
+ if i < 0:
+ i += len(self)
+ try:
+ self.data[i + self.left] = value
+ except KeyError:
+ raise IndexError
+
+ def __delitem__(self, i):
+ size = len(self)
+ if not (-size <= i < size):
+ raise IndexError
+ data = self.data
+ if i < 0:
+ i += size
+ for j in xrange(self.left + i, self.right - 1):
+ data[j] = data[j + 1]
+ self.pop()
+
+ def __len__(self):
+ return self.right - self.left
+
+ def __cmp__(self, other):
+ if type(self) != type(other):
+ return cmp(type(self), type(other))
+ return cmp(list(self), list(other))
+
+ def __repr__(self, _track=[]):
+ if id(self) in _track:
+ return '...'
+ _track.append(id(self))
+ r = 'deque(%r, maxlen=%s)' % (list(self), self.maxlen)
+ _track.remove(id(self))
+ return r
+
+ def __getstate__(self):
+ return (tuple(self),)
+
+ def __setstate__(self, s):
+ self.__init__(s[0])
+
+ def __hash__(self):
+ raise TypeError
+
+ def __copy__(self):
+ return self.__class__(self)
+
+ def __deepcopy__(self, memo={}):
+ from copy import deepcopy
+ result = self.__class__()
+ memo[id(self)] = result
+ result.__init__(deepcopy(tuple(self), memo))
+ return result
+
+
+#==============================================================================
+# threading.Event
+#==============================================================================
+
+if __py_version__ >= (2, 6):
+ from threading import Event
+else:
+ from threading import _Verbose, Condition, Lock
+
+ def Event(*args, **kwargs):
+ return _Event(*args, **kwargs)
+
+ class _Event(_Verbose):
+
+ # After Tim Peters' event class (without is_posted())
+
+ def __init__(self, verbose=None):
+ _Verbose.__init__(self, verbose)
+ self.__cond = Condition(Lock())
+ self.__flag = False
+
+ def isSet(self):
+ return self.__flag
+
+ is_set = isSet
+
+ def set(self):
+ self.__cond.acquire()
+ try:
+ self.__flag = True
+ self.__cond.notify_all()
+ finally:
+ self.__cond.release()
+
+ def clear(self):
+ self.__cond.acquire()
+ try:
+ self.__flag = False
+ finally:
+ self.__cond.release()
+
+ def wait(self, timeout=None):
+ self.__cond.acquire()
+ try:
+ if not self.__flag:
+ self.__cond.wait(timeout)
+ finally:
+ self.__cond.release()
+
+
+
diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py
index 7f1672b2..2fa8f4c9 100644
--- a/rhodecode/lib/db_manage.py
+++ b/rhodecode/lib/db_manage.py
@@ -31,17 +31,20 @@ import logging
from os.path import dirname as dn, join as jn
from rhodecode import __dbversion__
-from rhodecode.model import meta
from rhodecode.model.user import UserModel
from rhodecode.lib.utils import ask_ok
from rhodecode.model import init_model
from rhodecode.model.db import User, Permission, RhodeCodeUi, \
- RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
+ RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup, \
UserRepoGroupToPerm
from sqlalchemy.engine import create_engine
+from sqlalchemy.schema import MetaData
from rhodecode.model.repos_group import ReposGroupModel
+#from rhodecode.model import meta
+from rhodecode.model.meta import Session, Base
+
log = logging.getLogger(__name__)
@@ -59,25 +62,25 @@ class DbManage(object):
def init_db(self):
engine = create_engine(self.dburi, echo=self.log_sql)
init_model(engine)
- self.sa = meta.Session
+ self.sa = Session()
- def create_tables(self, override=False):
+ def create_tables(self, override=False, defaults={}):
"""
Create a auth database
"""
-
+ quiet = defaults.get('quiet')
log.info("Any existing database is going to be destroyed")
- if self.tests:
+ if self.tests or quiet:
destroy = True
else:
destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
if not destroy:
sys.exit()
if destroy:
- meta.Base.metadata.drop_all()
+ Base.metadata.drop_all()
checkfirst = not override
- meta.Base.metadata.create_all(checkfirst=checkfirst)
+ Base.metadata.create_all(checkfirst=checkfirst)
log.info('Created tables for %s' % self.dbname)
def set_db_version(self):
@@ -178,6 +181,44 @@ class DbManage(object):
def step_5(self):
pass
+ def step_6(self):
+ print ('re-checking permissions')
+ self.klass.create_permissions()
+
+ print ('installing new hooks')
+ hooks4 = RhodeCodeUi()
+ hooks4.ui_section = 'hooks'
+ hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
+ hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
+ Session().add(hooks4)
+
+ hooks6 = RhodeCodeUi()
+ hooks6.ui_section = 'hooks'
+ hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
+ hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
+ Session().add(hooks6)
+
+ print ('installing hgsubversion option')
+ # enable hgsubversion disabled by default
+ hgsubversion = RhodeCodeUi()
+ hgsubversion.ui_section = 'extensions'
+ hgsubversion.ui_key = 'hgsubversion'
+ hgsubversion.ui_value = ''
+ hgsubversion.ui_active = False
+ Session().add(hgsubversion)
+
+ print ('installing hg git option')
+ # enable hggit disabled by default
+ hggit = RhodeCodeUi()
+ hggit.ui_section = 'extensions'
+ hggit.ui_key = 'hggit'
+ hggit.ui_value = ''
+ hggit.ui_active = False
+ Session().add(hggit)
+
+ print ('re-check default permissions')
+ self.klass.populate_default_permissions()
+
upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
# CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
@@ -274,9 +315,9 @@ class DbManage(object):
self.create_user(username, password, email, True)
else:
log.info('creating admin and regular test users')
- from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
- TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
- TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
+ from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
+ TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
+ TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
@@ -305,44 +346,64 @@ class DbManage(object):
hooks1.ui_key = hooks1_key
hooks1.ui_value = 'hg update >&2'
hooks1.ui_active = False
+ self.sa.add(hooks1)
hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
hooks2_ = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
-
hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
hooks2.ui_section = 'hooks'
hooks2.ui_key = hooks2_key
hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
+ self.sa.add(hooks2)
hooks3 = RhodeCodeUi()
hooks3.ui_section = 'hooks'
hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
+ self.sa.add(hooks3)
hooks4 = RhodeCodeUi()
hooks4.ui_section = 'hooks'
- hooks4.ui_key = RhodeCodeUi.HOOK_PULL
- hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
+ hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
+ hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
+ self.sa.add(hooks4)
+
+ hooks5 = RhodeCodeUi()
+ hooks5.ui_section = 'hooks'
+ hooks5.ui_key = RhodeCodeUi.HOOK_PULL
+ hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
+ self.sa.add(hooks5)
- # For mercurial 1.7 set backward comapatibility with format
- dotencode_disable = RhodeCodeUi()
- dotencode_disable.ui_section = 'format'
- dotencode_disable.ui_key = 'dotencode'
- dotencode_disable.ui_value = 'false'
+ hooks6 = RhodeCodeUi()
+ hooks6.ui_section = 'hooks'
+ hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
+ hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
+ self.sa.add(hooks6)
# enable largefiles
largefiles = RhodeCodeUi()
largefiles.ui_section = 'extensions'
largefiles.ui_key = 'largefiles'
largefiles.ui_value = ''
-
- self.sa.add(hooks1)
- self.sa.add(hooks2)
- self.sa.add(hooks3)
- self.sa.add(hooks4)
self.sa.add(largefiles)
+ # enable hgsubversion disabled by default
+ hgsubversion = RhodeCodeUi()
+ hgsubversion.ui_section = 'extensions'
+ hgsubversion.ui_key = 'hgsubversion'
+ hgsubversion.ui_value = ''
+ hgsubversion.ui_active = False
+ self.sa.add(hgsubversion)
+
+ # enable hggit disabled by default
+ hggit = RhodeCodeUi()
+ hggit.ui_section = 'extensions'
+ hggit.ui_key = 'hggit'
+ hggit.ui_value = ''
+ hggit.ui_active = False
+ self.sa.add(hggit)
+
def create_ldap_options(self, skip_existing=False):
"""Creates ldap settings"""
@@ -443,18 +504,30 @@ class DbManage(object):
paths.ui_key = '/'
paths.ui_value = path
- hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
- hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
- hgsettings3 = RhodeCodeSetting('ga_code', '')
+ phases = RhodeCodeUi()
+ phases.ui_section = 'phases'
+ phases.ui_key = 'publish'
+ phases.ui_value = False
+
+ sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
+ sett2 = RhodeCodeSetting('title', 'RhodeCode')
+ sett3 = RhodeCodeSetting('ga_code', '')
+
+ sett4 = RhodeCodeSetting('show_public_icon', True)
+ sett5 = RhodeCodeSetting('show_private_icon', True)
+ sett6 = RhodeCodeSetting('stylify_metatags', False)
self.sa.add(web1)
self.sa.add(web2)
self.sa.add(web3)
self.sa.add(web4)
self.sa.add(paths)
- self.sa.add(hgsettings1)
- self.sa.add(hgsettings2)
- self.sa.add(hgsettings3)
+ self.sa.add(sett1)
+ self.sa.add(sett2)
+ self.sa.add(sett3)
+ self.sa.add(sett4)
+ self.sa.add(sett5)
+ self.sa.add(sett6)
self.create_ldap_options()
@@ -463,7 +536,7 @@ class DbManage(object):
def create_user(self, username, password, email='', admin=False):
log.info('creating user %s' % username)
UserModel().create_or_update(username, password, email,
- name='RhodeCode', lastname='Admin',
+ firstname='RhodeCode', lastname='Admin',
active=True, admin=admin)
def create_default_user(self):
@@ -472,64 +545,39 @@ class DbManage(object):
UserModel().create_or_update(username='default',
password=str(uuid.uuid1())[:8],
email='anonymous@rhodecode.org',
- name='Anonymous', lastname='User')
+ firstname='Anonymous', lastname='User')
def create_permissions(self):
# module.(access|create|change|delete)_[name]
# module.(none|read|write|admin)
- perms = [
- ('repository.none', 'Repository no access'),
- ('repository.read', 'Repository read access'),
- ('repository.write', 'Repository write access'),
- ('repository.admin', 'Repository admin access'),
-
- ('group.none', 'Repositories Group no access'),
- ('group.read', 'Repositories Group read access'),
- ('group.write', 'Repositories Group write access'),
- ('group.admin', 'Repositories Group admin access'),
-
- ('hg.admin', 'Hg Administrator'),
- ('hg.create.repository', 'Repository create'),
- ('hg.create.none', 'Repository creation disabled'),
- ('hg.register.none', 'Register disabled'),
- ('hg.register.manual_activate', 'Register new user with RhodeCode '
- 'without manual activation'),
-
- ('hg.register.auto_activate', 'Register new user with RhodeCode '
- 'without auto activation'),
- ]
-
- for p in perms:
+
+ for p in Permission.PERMS:
if not Permission.get_by_key(p[0]):
new_perm = Permission()
new_perm.permission_name = p[0]
- new_perm.permission_longname = p[1]
+ new_perm.permission_longname = p[0]
self.sa.add(new_perm)
def populate_default_permissions(self):
log.info('creating default user permissions')
- default_user = self.sa.query(User)\
- .filter(User.username == 'default').scalar()
-
- reg_perm = UserToPerm()
- reg_perm.user = default_user
- reg_perm.permission = self.sa.query(Permission)\
- .filter(Permission.permission_name == 'hg.register.manual_activate')\
- .scalar()
-
- create_repo_perm = UserToPerm()
- create_repo_perm.user = default_user
- create_repo_perm.permission = self.sa.query(Permission)\
- .filter(Permission.permission_name == 'hg.create.repository')\
- .scalar()
-
- default_repo_perm = UserToPerm()
- default_repo_perm.user = default_user
- default_repo_perm.permission = self.sa.query(Permission)\
- .filter(Permission.permission_name == 'repository.read')\
- .scalar()
-
- self.sa.add(reg_perm)
- self.sa.add(create_repo_perm)
- self.sa.add(default_repo_perm)
+ default_user = User.get_by_username('default')
+
+ for def_perm in ['hg.register.manual_activate', 'hg.create.repository',
+ 'hg.fork.repository', 'repository.read']:
+
+ perm = self.sa.query(Permission)\
+ .filter(Permission.permission_name == def_perm)\
+ .scalar()
+ if not perm:
+ raise Exception(
+ 'CRITICAL: permission %s not found inside database !!'
+ % def_perm
+ )
+ if not UserToPerm.query()\
+ .filter(UserToPerm.permission == perm)\
+ .filter(UserToPerm.user == default_user).scalar():
+ reg_perm = UserToPerm()
+ reg_perm.user = default_user
+ reg_perm.permission = perm
+ self.sa.add(reg_perm)
diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py b/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py
index d5ad27a0..fdd2b97a 100644
--- a/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py
+++ b/rhodecode/lib/dbmigrate/migrate/changeset/ansisql.py
@@ -94,6 +94,7 @@ class ANSIColumnGenerator(AlterTableVisitor, SchemaGenerator):
table = self.start_alter_table(column)
self.append("ADD ")
+
self.append(self.get_column_specification(column))
for cons in column.constraints:
diff --git a/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py b/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py
index a4538479..97830e97 100644
--- a/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py
+++ b/rhodecode/lib/dbmigrate/migrate/changeset/databases/sqlite.py
@@ -49,6 +49,7 @@ class SQLiteHelper(SQLiteCommon):
else:
column = delta
table = self._to_table(column.table)
+
self.recreate_table(table,column,delta)
class SQLiteColumnGenerator(SQLiteSchemaGenerator,
diff --git a/rhodecode/lib/dbmigrate/schema/db_1_2_0.py b/rhodecode/lib/dbmigrate/schema/db_1_2_0.py
index 5eb5cdbd..823ab2f6 100755
--- a/rhodecode/lib/dbmigrate/schema/db_1_2_0.py
+++ b/rhodecode/lib/dbmigrate/schema/db_1_2_0.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""
- rhodecode.model.db
- ~~~~~~~~~~~~~~~~~~
+ rhodecode.model.db_1_2_0
+ ~~~~~~~~~~~~~~~~~~~~~~~~
- Database Models for RhodeCode
+ Database Models for RhodeCode <=1.2.X
:created_on: Apr 08, 2010
:author: marcink
diff --git a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py
index 24ee1523..e4e6728b 100644
--- a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py
+++ b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""
- rhodecode.model.db
- ~~~~~~~~~~~~~~~~~~
+ rhodecode.model.db_1_3_0
+ ~~~~~~~~~~~~~~~~~~~~~~~~
- Database Models for RhodeCode
+ Database Models for RhodeCode <=1.3.X
:created_on: Apr 08, 2010
:author: marcink
@@ -23,6 +23,1298 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#TODO: when branch 1.3 is finished replacem with db.py content
+import os
+import logging
+import datetime
+import traceback
+from collections import defaultdict
-from rhodecode.model.db import *
+from sqlalchemy import *
+from sqlalchemy.ext.hybrid import hybrid_property
+from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
+from beaker.cache import cache_region, region_invalidate
+
+from rhodecode.lib.vcs import get_backend
+from rhodecode.lib.vcs.utils.helpers import get_scm
+from rhodecode.lib.vcs.exceptions import VCSError
+from rhodecode.lib.vcs.utils.lazy import LazyProperty
+
+from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
+ safe_unicode
+from rhodecode.lib.compat import json
+from rhodecode.lib.caching_query import FromCache
+
+from rhodecode.model.meta import Base, Session
+import hashlib
+
+
+log = logging.getLogger(__name__)
+
+#==============================================================================
+# BASE CLASSES
+#==============================================================================
+
+_hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
+
+
+class ModelSerializer(json.JSONEncoder):
+ """
+ Simple Serializer for JSON,
+
+ usage::
+
+ to make object customized for serialization implement a __json__
+ method that will return a dict for serialization into json
+
+ example::
+
+ class Task(object):
+
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+
+ def __json__(self):
+ return dict(name=self.name,
+ value=self.value)
+
+ """
+
+ def default(self, obj):
+
+ if hasattr(obj, '__json__'):
+ return obj.__json__()
+ else:
+ return json.JSONEncoder.default(self, obj)
+
+
+class BaseModel(object):
+ """
+ Base Model for all classess
+ """
+
+ @classmethod
+ def _get_keys(cls):
+ """return column names for this model """
+ return class_mapper(cls).c.keys()
+
+ def get_dict(self):
+ """
+ return dict with keys and values corresponding
+ to this model data """
+
+ d = {}
+ for k in self._get_keys():
+ d[k] = getattr(self, k)
+
+ # also use __json__() if present to get additional fields
+ for k, val in getattr(self, '__json__', lambda: {})().iteritems():
+ d[k] = val
+ return d
+
+ def get_appstruct(self):
+ """return list with keys and values tupples corresponding
+ to this model data """
+
+ l = []
+ for k in self._get_keys():
+ l.append((k, getattr(self, k),))
+ return l
+
+ def populate_obj(self, populate_dict):
+ """populate model with data from given populate_dict"""
+
+ for k in self._get_keys():
+ if k in populate_dict:
+ setattr(self, k, populate_dict[k])
+
+ @classmethod
+ def query(cls):
+ return Session.query(cls)
+
+ @classmethod
+ def get(cls, id_):
+ if id_:
+ return cls.query().get(id_)
+
+ @classmethod
+ def getAll(cls):
+ return cls.query().all()
+
+ @classmethod
+ def delete(cls, id_):
+ obj = cls.query().get(id_)
+ Session.delete(obj)
+
+ def __repr__(self):
+ if hasattr(self, '__unicode__'):
+ # python repr needs to return str
+ return safe_str(self.__unicode__())
+ return '<DB:%s>' % (self.__class__.__name__)
+
+class RhodeCodeSetting(Base, BaseModel):
+ __tablename__ = 'rhodecode_settings'
+ __table_args__ = (
+ UniqueConstraint('app_settings_name'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+
+ def __init__(self, k='', v=''):
+ self.app_settings_name = k
+ self.app_settings_value = v
+
+ @validates('_app_settings_value')
+ def validate_settings_value(self, key, val):
+ assert type(val) == unicode
+ return val
+
+ @hybrid_property
+ def app_settings_value(self):
+ v = self._app_settings_value
+ if self.app_settings_name == 'ldap_active':
+ v = str2bool(v)
+ return v
+
+ @app_settings_value.setter
+ def app_settings_value(self, val):
+ """
+ Setter that will always make sure we use unicode in app_settings_value
+
+ :param val:
+ """
+ self._app_settings_value = safe_unicode(val)
+
+ def __unicode__(self):
+ return u"<%s('%s:%s')>" % (
+ self.__class__.__name__,
+ self.app_settings_name, self.app_settings_value
+ )
+
+ @classmethod
+ def get_by_name(cls, ldap_key):
+ return cls.query()\
+ .filter(cls.app_settings_name == ldap_key).scalar()
+
+ @classmethod
+ def get_app_settings(cls, cache=False):
+
+ ret = cls.query()
+
+ if cache:
+ ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
+
+ if not ret:
+ raise Exception('Could not get application settings !')
+ settings = {}
+ for each in ret:
+ settings['rhodecode_' + each.app_settings_name] = \
+ each.app_settings_value
+
+ return settings
+
+ @classmethod
+ def get_ldap_settings(cls, cache=False):
+ ret = cls.query()\
+ .filter(cls.app_settings_name.startswith('ldap_')).all()
+ fd = {}
+ for row in ret:
+ fd.update({row.app_settings_name:row.app_settings_value})
+
+ return fd
+
+
+class RhodeCodeUi(Base, BaseModel):
+ __tablename__ = 'rhodecode_ui'
+ __table_args__ = (
+ UniqueConstraint('ui_key'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+
+ HOOK_UPDATE = 'changegroup.update'
+ HOOK_REPO_SIZE = 'changegroup.repo_size'
+ HOOK_PUSH = 'pretxnchangegroup.push_logger'
+ HOOK_PULL = 'preoutgoing.pull_logger'
+
+ ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
+
+ @classmethod
+ def get_by_key(cls, key):
+ return cls.query().filter(cls.ui_key == key)
+
+ @classmethod
+ def get_builtin_hooks(cls):
+ q = cls.query()
+ q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
+ cls.HOOK_REPO_SIZE,
+ cls.HOOK_PUSH, cls.HOOK_PULL]))
+ return q.all()
+
+ @classmethod
+ def get_custom_hooks(cls):
+ q = cls.query()
+ q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
+ cls.HOOK_REPO_SIZE,
+ cls.HOOK_PUSH, cls.HOOK_PULL]))
+ q = q.filter(cls.ui_section == 'hooks')
+ return q.all()
+
+ @classmethod
+ def create_or_update_hook(cls, key, val):
+ new_ui = cls.get_by_key(key).scalar() or cls()
+ new_ui.ui_section = 'hooks'
+ new_ui.ui_active = True
+ new_ui.ui_key = key
+ new_ui.ui_value = val
+
+ Session.add(new_ui)
+
+
+class User(Base, BaseModel):
+ __tablename__ = 'users'
+ __table_args__ = (
+ UniqueConstraint('username'), UniqueConstraint('email'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ active = Column("active", Boolean(), nullable=True, unique=None, default=None)
+ admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
+ name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
+ ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+
+ user_log = relationship('UserLog', cascade='all')
+ user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
+
+ repositories = relationship('Repository')
+ user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
+ repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
+ repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
+
+ group_member = relationship('UsersGroupMember', cascade='all')
+
+ notifications = relationship('UserNotification', cascade='all')
+ # notifications assigned to this user
+ user_created_notifications = relationship('Notification', cascade='all')
+ # comments created by this user
+ user_comments = relationship('ChangesetComment', cascade='all')
+
+ @hybrid_property
+ def email(self):
+ return self._email
+
+ @email.setter
+ def email(self, val):
+ self._email = val.lower() if val else None
+
+ @property
+ def full_name(self):
+ return '%s %s' % (self.name, self.lastname)
+
+ @property
+ def full_name_or_username(self):
+ return ('%s %s' % (self.name, self.lastname)
+ if (self.name and self.lastname) else self.username)
+
+ @property
+ def full_contact(self):
+ return '%s %s <%s>' % (self.name, self.lastname, self.email)
+
+ @property
+ def short_contact(self):
+ return '%s %s' % (self.name, self.lastname)
+
+ @property
+ def is_admin(self):
+ return self.admin
+
+ def __unicode__(self):
+ return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
+ self.user_id, self.username)
+
+ @classmethod
+ def get_by_username(cls, username, case_insensitive=False, cache=False):
+ if case_insensitive:
+ q = cls.query().filter(cls.username.ilike(username))
+ else:
+ q = cls.query().filter(cls.username == username)
+
+ if cache:
+ q = q.options(FromCache(
+ "sql_cache_short",
+ "get_user_%s" % _hash_key(username)
+ )
+ )
+ return q.scalar()
+
+ @classmethod
+ def get_by_api_key(cls, api_key, cache=False):
+ q = cls.query().filter(cls.api_key == api_key)
+
+ if cache:
+ q = q.options(FromCache("sql_cache_short",
+ "get_api_key_%s" % api_key))
+ return q.scalar()
+
+ @classmethod
+ def get_by_email(cls, email, case_insensitive=False, cache=False):
+ if case_insensitive:
+ q = cls.query().filter(cls.email.ilike(email))
+ else:
+ q = cls.query().filter(cls.email == email)
+
+ if cache:
+ q = q.options(FromCache("sql_cache_short",
+ "get_api_key_%s" % email))
+ return q.scalar()
+
+ def update_lastlogin(self):
+ """Update user lastlogin"""
+ self.last_login = datetime.datetime.now()
+ Session.add(self)
+ log.debug('updated user %s lastlogin' % self.username)
+
+ def __json__(self):
+ return dict(
+ user_id=self.user_id,
+ first_name=self.name,
+ last_name=self.lastname,
+ email=self.email,
+ full_name=self.full_name,
+ full_name_or_username=self.full_name_or_username,
+ short_contact=self.short_contact,
+ full_contact=self.full_contact
+ )
+
+
+class UserLog(Base, BaseModel):
+ __tablename__ = 'user_logs'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+ user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+ repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
+ repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
+
+ @property
+ def action_as_day(self):
+ return datetime.date(*self.action_date.timetuple()[:3])
+
+ user = relationship('User')
+ repository = relationship('Repository', cascade='')
+
+
+class UsersGroup(Base, BaseModel):
+ __tablename__ = 'users_groups'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+ users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
+
+ members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
+ users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
+ users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
+
+ def __unicode__(self):
+ return u'<userGroup(%s)>' % (self.users_group_name)
+
+ @classmethod
+ def get_by_group_name(cls, group_name, cache=False,
+ case_insensitive=False):
+ if case_insensitive:
+ q = cls.query().filter(cls.users_group_name.ilike(group_name))
+ else:
+ q = cls.query().filter(cls.users_group_name == group_name)
+ if cache:
+ q = q.options(FromCache(
+ "sql_cache_short",
+ "get_user_%s" % _hash_key(group_name)
+ )
+ )
+ return q.scalar()
+
+ @classmethod
+ def get(cls, users_group_id, cache=False):
+ users_group = cls.query()
+ if cache:
+ users_group = users_group.options(FromCache("sql_cache_short",
+ "get_users_group_%s" % users_group_id))
+ return users_group.get(users_group_id)
+
+
+class UsersGroupMember(Base, BaseModel):
+ __tablename__ = 'users_groups_members'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+
+ user = relationship('User', lazy='joined')
+ users_group = relationship('UsersGroup')
+
+ def __init__(self, gr_id='', u_id=''):
+ self.users_group_id = gr_id
+ self.user_id = u_id
+
+
+class Repository(Base, BaseModel):
+ __tablename__ = 'repositories'
+ __table_args__ = (
+ UniqueConstraint('repo_name'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+ clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
+ repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
+ private = Column("private", Boolean(), nullable=True, unique=None, default=None)
+ enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
+ enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
+ description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
+
+ fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
+ group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
+
+ user = relationship('User')
+ fork = relationship('Repository', remote_side=repo_id)
+ group = relationship('RepoGroup')
+ repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
+ users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
+ stats = relationship('Statistics', cascade='all', uselist=False)
+
+ followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
+
+ logs = relationship('UserLog')
+
+ def __unicode__(self):
+ return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
+ self.repo_name)
+
+ @classmethod
+ def url_sep(cls):
+ return '/'
+
+ @classmethod
+ def get_by_repo_name(cls, repo_name):
+ q = Session.query(cls).filter(cls.repo_name == repo_name)
+ q = q.options(joinedload(Repository.fork))\
+ .options(joinedload(Repository.user))\
+ .options(joinedload(Repository.group))
+ return q.scalar()
+
+ @classmethod
+ def get_repo_forks(cls, repo_id):
+ return cls.query().filter(Repository.fork_id == repo_id)
+
+ @classmethod
+ def base_path(cls):
+ """
+ Returns base path when all repos are stored
+
+ :param cls:
+ """
+ q = Session.query(RhodeCodeUi)\
+ .filter(RhodeCodeUi.ui_key == cls.url_sep())
+ q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
+ return q.one().ui_value
+
+ @property
+ def just_name(self):
+ return self.repo_name.split(Repository.url_sep())[-1]
+
+ @property
+ def groups_with_parents(self):
+ groups = []
+ if self.group is None:
+ return groups
+
+ cur_gr = self.group
+ groups.insert(0, cur_gr)
+ while 1:
+ gr = getattr(cur_gr, 'parent_group', None)
+ cur_gr = cur_gr.parent_group
+ if gr is None:
+ break
+ groups.insert(0, gr)
+
+ return groups
+
+ @property
+ def groups_and_repo(self):
+ return self.groups_with_parents, self.just_name
+
+ @LazyProperty
+ def repo_path(self):
+ """
+ Returns base full path for that repository means where it actually
+ exists on a filesystem
+ """
+ q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
+ Repository.url_sep())
+ q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
+ return q.one().ui_value
+
+ @property
+ def repo_full_path(self):
+ p = [self.repo_path]
+ # we need to split the name by / since this is how we store the
+ # names in the database, but that eventually needs to be converted
+ # into a valid system path
+ p += self.repo_name.split(Repository.url_sep())
+ return os.path.join(*p)
+
+ def get_new_name(self, repo_name):
+ """
+ returns new full repository name based on assigned group and new new
+
+ :param group_name:
+ """
+ path_prefix = self.group.full_path_splitted if self.group else []
+ return Repository.url_sep().join(path_prefix + [repo_name])
+
+ @property
+ def _ui(self):
+ """
+ Creates an db based ui object for this repository
+ """
+ from mercurial import ui
+ from mercurial import config
+ baseui = ui.ui()
+
+ #clean the baseui object
+ baseui._ocfg = config.config()
+ baseui._ucfg = config.config()
+ baseui._tcfg = config.config()
+
+ ret = RhodeCodeUi.query()\
+ .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
+
+ hg_ui = ret
+ for ui_ in hg_ui:
+ if ui_.ui_active:
+ log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
+ ui_.ui_key, ui_.ui_value)
+ baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
+
+ return baseui
+
+ @classmethod
+ def is_valid(cls, repo_name):
+ """
+ returns True if given repo name is a valid filesystem repository
+
+ :param cls:
+ :param repo_name:
+ """
+ from rhodecode.lib.utils import is_valid_repo
+
+ return is_valid_repo(repo_name, cls.base_path())
+
+ #==========================================================================
+ # SCM PROPERTIES
+ #==========================================================================
+
+ def get_changeset(self, rev):
+ return get_changeset_safe(self.scm_instance, rev)
+
+ @property
+ def tip(self):
+ return self.get_changeset('tip')
+
+ @property
+ def author(self):
+ return self.tip.author
+
+ @property
+ def last_change(self):
+ return self.scm_instance.last_change
+
+ def comments(self, revisions=None):
+ """
+ Returns comments for this repository grouped by revisions
+
+ :param revisions: filter query by revisions only
+ """
+ cmts = ChangesetComment.query()\
+ .filter(ChangesetComment.repo == self)
+ if revisions:
+ cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
+ grouped = defaultdict(list)
+ for cmt in cmts.all():
+ grouped[cmt.revision].append(cmt)
+ return grouped
+
+ #==========================================================================
+ # SCM CACHE INSTANCE
+ #==========================================================================
+
+ @property
+ def invalidate(self):
+ return CacheInvalidation.invalidate(self.repo_name)
+
+ def set_invalidate(self):
+ """
+ set a cache for invalidation for this instance
+ """
+ CacheInvalidation.set_invalidate(self.repo_name)
+
+ @LazyProperty
+ def scm_instance(self):
+ return self.__get_instance()
+
+ @property
+ def scm_instance_cached(self):
+ @cache_region('long_term')
+ def _c(repo_name):
+ return self.__get_instance()
+ rn = self.repo_name
+ log.debug('Getting cached instance of repo')
+ inv = self.invalidate
+ if inv is not None:
+ region_invalidate(_c, None, rn)
+ # update our cache
+ CacheInvalidation.set_valid(inv.cache_key)
+ return _c(rn)
+
+ def __get_instance(self):
+ repo_full_path = self.repo_full_path
+ try:
+ alias = get_scm(repo_full_path)[0]
+ log.debug('Creating instance of %s repository' % alias)
+ backend = get_backend(alias)
+ except VCSError:
+ log.error(traceback.format_exc())
+ log.error('Perhaps this repository is in db and not in '
+ 'filesystem run rescan repositories with '
+ '"destroy old data " option from admin panel')
+ return
+
+ if alias == 'hg':
+
+ repo = backend(safe_str(repo_full_path), create=False,
+ baseui=self._ui)
+ # skip hidden web repository
+ if repo._get_hidden():
+ return
+ else:
+ repo = backend(repo_full_path, create=False)
+
+ return repo
+
+
+class RepoGroup(Base, BaseModel):
+ __tablename__ = 'groups'
+ __table_args__ = (
+ UniqueConstraint('group_name', 'group_parent_id'),
+ CheckConstraint('group_id != group_parent_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+ __mapper_args__ = {'order_by': 'group_name'}
+
+ group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+ group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
+ group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+
+ repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
+ users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
+
+ parent_group = relationship('RepoGroup', remote_side=group_id)
+
+ def __init__(self, group_name='', parent_group=None):
+ self.group_name = group_name
+ self.parent_group = parent_group
+
+ def __unicode__(self):
+ return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
+ self.group_name)
+
+ @classmethod
+ def groups_choices(cls):
+ from webhelpers.html import literal as _literal
+ repo_groups = [('', '')]
+ sep = ' &raquo; '
+ _name = lambda k: _literal(sep.join(k))
+
+ repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
+ for x in cls.query().all()])
+
+ repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
+ return repo_groups
+
+ @classmethod
+ def url_sep(cls):
+ return '/'
+
+ @classmethod
+ def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
+ if case_insensitive:
+ gr = cls.query()\
+ .filter(cls.group_name.ilike(group_name))
+ else:
+ gr = cls.query()\
+ .filter(cls.group_name == group_name)
+ if cache:
+ gr = gr.options(FromCache(
+ "sql_cache_short",
+ "get_group_%s" % _hash_key(group_name)
+ )
+ )
+ return gr.scalar()
+
+ @property
+ def parents(self):
+ parents_recursion_limit = 5
+ groups = []
+ if self.parent_group is None:
+ return groups
+ cur_gr = self.parent_group
+ groups.insert(0, cur_gr)
+ cnt = 0
+ while 1:
+ cnt += 1
+ gr = getattr(cur_gr, 'parent_group', None)
+ cur_gr = cur_gr.parent_group
+ if gr is None:
+ break
+ if cnt == parents_recursion_limit:
+ # this will prevent accidental infinit loops
+ log.error('group nested more than %s' %
+ parents_recursion_limit)
+ break
+
+ groups.insert(0, gr)
+ return groups
+
+ @property
+ def children(self):
+ return RepoGroup.query().filter(RepoGroup.parent_group == self)
+
+ @property
+ def name(self):
+ return self.group_name.split(RepoGroup.url_sep())[-1]
+
+ @property
+ def full_path(self):
+ return self.group_name
+
+ @property
+ def full_path_splitted(self):
+ return self.group_name.split(RepoGroup.url_sep())
+
+ @property
+ def repositories(self):
+ return Repository.query()\
+ .filter(Repository.group == self)\
+ .order_by(Repository.repo_name)
+
+ @property
+ def repositories_recursive_count(self):
+ cnt = self.repositories.count()
+
+ def children_count(group):
+ cnt = 0
+ for child in group.children:
+ cnt += child.repositories.count()
+ cnt += children_count(child)
+ return cnt
+
+ return cnt + children_count(self)
+
+ def get_new_name(self, group_name):
+ """
+ returns new full group name based on parent and new name
+
+ :param group_name:
+ """
+ path_prefix = (self.parent_group.full_path_splitted if
+ self.parent_group else [])
+ return RepoGroup.url_sep().join(path_prefix + [group_name])
+
+
+class Permission(Base, BaseModel):
+ __tablename__ = 'permissions'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+ permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+
+ def __unicode__(self):
+ return u"<%s('%s:%s')>" % (
+ self.__class__.__name__, self.permission_id, self.permission_name
+ )
+
+ @classmethod
+ def get_by_key(cls, key):
+ return cls.query().filter(cls.permission_name == key).scalar()
+
+ @classmethod
+ def get_default_perms(cls, default_user_id):
+ q = Session.query(UserRepoToPerm, Repository, cls)\
+ .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
+ .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+ .filter(UserRepoToPerm.user_id == default_user_id)
+
+ return q.all()
+
+ @classmethod
+ def get_default_group_perms(cls, default_user_id):
+ q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
+ .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
+ .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+ .filter(UserRepoGroupToPerm.user_id == default_user_id)
+
+ return q.all()
+
+
+class UserRepoToPerm(Base, BaseModel):
+ __tablename__ = 'repo_to_perm'
+ __table_args__ = (
+ UniqueConstraint('user_id', 'repository_id', 'permission_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+ permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+ repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
+
+ user = relationship('User')
+ repository = relationship('Repository')
+ permission = relationship('Permission')
+
+ @classmethod
+ def create(cls, user, repository, permission):
+ n = cls()
+ n.user = user
+ n.repository = repository
+ n.permission = permission
+ Session.add(n)
+ return n
+
+ def __unicode__(self):
+ return u'<user:%s => %s >' % (self.user, self.repository)
+
+
+class UserToPerm(Base, BaseModel):
+ __tablename__ = 'user_to_perm'
+ __table_args__ = (
+ UniqueConstraint('user_id', 'permission_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+ permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+ user = relationship('User')
+ permission = relationship('Permission', lazy='joined')
+
+
+class UsersGroupRepoToPerm(Base, BaseModel):
+ __tablename__ = 'users_group_repo_to_perm'
+ __table_args__ = (
+ UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+ permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+ repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
+
+ users_group = relationship('UsersGroup')
+ permission = relationship('Permission')
+ repository = relationship('Repository')
+
+ @classmethod
+ def create(cls, users_group, repository, permission):
+ n = cls()
+ n.users_group = users_group
+ n.repository = repository
+ n.permission = permission
+ Session.add(n)
+ return n
+
+ def __unicode__(self):
+ return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
+
+
+class UsersGroupToPerm(Base, BaseModel):
+ __tablename__ = 'users_group_to_perm'
+ __table_args__ = (
+ UniqueConstraint('users_group_id', 'permission_id',),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+ permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+ users_group = relationship('UsersGroup')
+ permission = relationship('Permission')
+
+
+class UserRepoGroupToPerm(Base, BaseModel):
+ __tablename__ = 'user_repo_group_to_perm'
+ __table_args__ = (
+ UniqueConstraint('user_id', 'group_id', 'permission_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+
+ group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+ group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
+ permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+ user = relationship('User')
+ group = relationship('RepoGroup')
+ permission = relationship('Permission')
+
+
+class UsersGroupRepoGroupToPerm(Base, BaseModel):
+ __tablename__ = 'users_group_repo_group_to_perm'
+ __table_args__ = (
+ UniqueConstraint('users_group_id', 'group_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+
+ users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+ group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
+ permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+ users_group = relationship('UsersGroup')
+ permission = relationship('Permission')
+ group = relationship('RepoGroup')
+
+
+class Statistics(Base, BaseModel):
+ __tablename__ = 'statistics'
+ __table_args__ = (
+ UniqueConstraint('repository_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
+ stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
+ commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
+ commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
+ languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
+
+ repository = relationship('Repository', single_parent=True)
+
+
+class UserFollowing(Base, BaseModel):
+ __tablename__ = 'user_followings'
+ __table_args__ = (
+ UniqueConstraint('user_id', 'follows_repository_id'),
+ UniqueConstraint('user_id', 'follows_user_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+
+ user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+ follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
+ follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
+ follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
+
+ user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
+
+ follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
+ follows_repository = relationship('Repository', order_by='Repository.repo_name')
+
+ @classmethod
+ def get_repo_followers(cls, repo_id):
+ return cls.query().filter(cls.follows_repo_id == repo_id)
+
+
+class CacheInvalidation(Base, BaseModel):
+ __tablename__ = 'cache_invalidation'
+ __table_args__ = (
+ UniqueConstraint('cache_key'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+ cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
+
+ def __init__(self, cache_key, cache_args=''):
+ self.cache_key = cache_key
+ self.cache_args = cache_args
+ self.cache_active = False
+
+ def __unicode__(self):
+ return u"<%s('%s:%s')>" % (self.__class__.__name__,
+ self.cache_id, self.cache_key)
+ @classmethod
+ def clear_cache(cls):
+ cls.query().delete()
+
+ @classmethod
+ def _get_key(cls, key):
+ """
+ Wrapper for generating a key, together with a prefix
+
+ :param key:
+ """
+ import rhodecode
+ prefix = ''
+ iid = rhodecode.CONFIG.get('instance_id')
+ if iid:
+ prefix = iid
+ return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
+
+ @classmethod
+ def get_by_key(cls, key):
+ return cls.query().filter(cls.cache_key == key).scalar()
+
+ @classmethod
+ def _get_or_create_key(cls, key, prefix, org_key):
+ inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
+ if not inv_obj:
+ try:
+ inv_obj = CacheInvalidation(key, org_key)
+ Session.add(inv_obj)
+ Session.commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ Session.rollback()
+ return inv_obj
+
+ @classmethod
+ def invalidate(cls, key):
+ """
+ Returns Invalidation object if this given key should be invalidated
+ None otherwise. `cache_active = False` means that this cache
+ state is not valid and needs to be invalidated
+
+ :param key:
+ """
+
+ key, _prefix, _org_key = cls._get_key(key)
+ inv = cls._get_or_create_key(key, _prefix, _org_key)
+
+ if inv and inv.cache_active is False:
+ return inv
+
+ @classmethod
+ def set_invalidate(cls, key):
+ """
+ Mark this Cache key for invalidation
+
+ :param key:
+ """
+
+ key, _prefix, _org_key = cls._get_key(key)
+ inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
+ log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
+ _org_key))
+ try:
+ for inv_obj in inv_objs:
+ if inv_obj:
+ inv_obj.cache_active = False
+
+ Session.add(inv_obj)
+ Session.commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ Session.rollback()
+
+ @classmethod
+ def set_valid(cls, key):
+ """
+ Mark this cache key as active and currently cached
+
+ :param key:
+ """
+ inv_obj = cls.get_by_key(key)
+ inv_obj.cache_active = True
+ Session.add(inv_obj)
+ Session.commit()
+
+
+class ChangesetComment(Base, BaseModel):
+ __tablename__ = 'changeset_comments'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+ comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
+ repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+ revision = Column('revision', String(40), nullable=False)
+ line_no = Column('line_no', Unicode(10), nullable=True)
+ f_path = Column('f_path', Unicode(1000), nullable=True)
+ user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
+ text = Column('text', Unicode(25000), nullable=False)
+ modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
+
+ author = relationship('User', lazy='joined')
+ repo = relationship('Repository')
+
+ @classmethod
+ def get_users(cls, revision):
+ """
+ Returns user associated with this changesetComment. ie those
+ who actually commented
+
+ :param cls:
+ :param revision:
+ """
+ return Session.query(User)\
+ .filter(cls.revision == revision)\
+ .join(ChangesetComment.author).all()
+
+
+class Notification(Base, BaseModel):
+ __tablename__ = 'notifications'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ TYPE_CHANGESET_COMMENT = u'cs_comment'
+ TYPE_MESSAGE = u'message'
+ TYPE_MENTION = u'mention'
+ TYPE_REGISTRATION = u'registration'
+
+ notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
+ subject = Column('subject', Unicode(512), nullable=True)
+ body = Column('body', Unicode(50000), nullable=True)
+ created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
+ created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+ type_ = Column('type', Unicode(256))
+
+ created_by_user = relationship('User')
+ notifications_to_users = relationship('UserNotification', lazy='joined',
+ cascade="all, delete, delete-orphan")
+
+ @property
+ def recipients(self):
+ return [x.user for x in UserNotification.query()\
+ .filter(UserNotification.notification == self).all()]
+
+ @classmethod
+ def create(cls, created_by, subject, body, recipients, type_=None):
+ if type_ is None:
+ type_ = Notification.TYPE_MESSAGE
+
+ notification = cls()
+ notification.created_by_user = created_by
+ notification.subject = subject
+ notification.body = body
+ notification.type_ = type_
+ notification.created_on = datetime.datetime.now()
+
+ for u in recipients:
+ assoc = UserNotification()
+ assoc.notification = notification
+ u.notifications.append(assoc)
+ Session.add(notification)
+ return notification
+
+ @property
+ def description(self):
+ from rhodecode.model.notification import NotificationModel
+ return NotificationModel().make_description(self)
+
+
+class UserNotification(Base, BaseModel):
+ __tablename__ = 'user_to_notification'
+ __table_args__ = (
+ UniqueConstraint('user_id', 'notification_id'),
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
+ notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
+ read = Column('read', Boolean, default=False)
+ sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
+
+ user = relationship('User', lazy="joined")
+ notification = relationship('Notification', lazy="joined",
+ order_by=lambda: Notification.created_on.desc(),)
+
+ def mark_as_read(self):
+ self.read = True
+ Session.add(self)
+
+
+class DbMigrateVersion(Base, BaseModel):
+ __tablename__ = 'db_migrate_version'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine':'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+ repository_id = Column('repository_id', String(250), primary_key=True)
+ repository_path = Column('repository_path', Text)
+ version = Column('version', Integer)
+
+## this is migration from 1_4_0, but now it's here to overcome a problem of
+## attaching a FK to this from 1_3_0 !
+
+
+class PullRequest(Base, BaseModel):
+ __tablename__ = 'pull_requests'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ STATUS_NEW = u'new'
+ STATUS_OPEN = u'open'
+ STATUS_CLOSED = u'closed'
+
+ pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
+ title = Column('title', Unicode(256), nullable=True)
+ description = Column('description', UnicodeText(10240), nullable=True)
+ status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
+ created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+ updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
+ _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
+ org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+ org_ref = Column('org_ref', Unicode(256), nullable=False)
+ other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+ other_ref = Column('other_ref', Unicode(256), nullable=False) \ No newline at end of file
diff --git a/rhodecode/lib/dbmigrate/schema/db_1_4_0.py b/rhodecode/lib/dbmigrate/schema/db_1_4_0.py
new file mode 100644
index 00000000..d90e8578
--- /dev/null
+++ b/rhodecode/lib/dbmigrate/schema/db_1_4_0.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.model.db_1_4_0
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Database Models for RhodeCode <=1.4.X
+
+ :created_on: Apr 08, 2010
+ :author: marcink
+ :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+#TODO: replace that will db.py content after 1.5 Release
+
+from rhodecode.model.db import *
diff --git a/rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py b/rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py
new file mode 100644
index 00000000..1ffbe4d9
--- /dev/null
+++ b/rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py
@@ -0,0 +1,186 @@
+import logging
+import datetime
+
+from sqlalchemy import *
+from sqlalchemy.exc import DatabaseError
+from sqlalchemy.orm import relation, backref, class_mapper
+from sqlalchemy.orm.session import Session
+from sqlalchemy.ext.declarative import declarative_base
+
+from rhodecode.lib.dbmigrate.migrate import *
+from rhodecode.lib.dbmigrate.migrate.changeset import *
+
+from rhodecode.model.meta import Base
+from rhodecode.model import meta
+
+log = logging.getLogger(__name__)
+
+
+def upgrade(migrate_engine):
+ """
+ Upgrade operations go here.
+ Don't create your own engine; bind migrate_engine to your metadata
+ """
+
+ #==========================================================================
+ # USEREMAILMAP
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_4_0 import UserEmailMap
+ tbl = UserEmailMap.__table__
+ tbl.create()
+ #==========================================================================
+ # PULL REQUEST
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequest
+ tbl = PullRequest.__table__
+ tbl.create()
+
+ #==========================================================================
+ # PULL REQUEST REVIEWERS
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequestReviewers
+ tbl = PullRequestReviewers.__table__
+ tbl.create()
+
+ #==========================================================================
+ # CHANGESET STATUS
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetStatus
+ tbl = ChangesetStatus.__table__
+ tbl.create()
+
+ ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
+ Base = declarative_base()
+ Base.metadata.clear()
+ Base.metadata = MetaData()
+ Base.metadata.bind = migrate_engine
+ meta.Base = Base
+
+ #==========================================================================
+ # USERS TABLE
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import User
+ tbl = User.__table__
+
+ # change column name -> firstname
+ col = User.__table__.columns.name
+ col.alter(index=Index('u_username_idx', 'username'))
+ col.alter(index=Index('u_email_idx', 'email'))
+ col.alter(name="firstname", table=tbl)
+
+ # add inherit_default_permission column
+ inherit_default_permissions = Column("inherit_default_permissions",
+ Boolean(), nullable=True, unique=None,
+ default=True)
+ inherit_default_permissions.create(table=tbl)
+ inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
+
+ #==========================================================================
+ # USERS GROUP TABLE
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import UsersGroup
+ tbl = UsersGroup.__table__
+ # add inherit_default_permission column
+ gr_inherit_default_permissions = Column(
+ "users_group_inherit_default_permissions",
+ Boolean(), nullable=True, unique=None,
+ default=True)
+ gr_inherit_default_permissions.create(table=tbl)
+ gr_inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
+
+ #==========================================================================
+ # REPOSITORIES
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Repository
+ tbl = Repository.__table__
+
+ # add enable locking column
+ enable_locking = Column("enable_locking", Boolean(), nullable=True,
+ unique=None, default=False)
+ enable_locking.create(table=tbl)
+ enable_locking.alter(nullable=False, default=False, table=tbl)
+
+ # add locked column
+ _locked = Column("locked", String(255), nullable=True, unique=False,
+ default=None)
+ _locked.create(table=tbl)
+
+ #add langing revision column
+ landing_rev = Column("landing_revision", String(255), nullable=True,
+ unique=False, default='tip')
+ landing_rev.create(table=tbl)
+ landing_rev.alter(nullable=False, default='tip', table=tbl)
+
+ #==========================================================================
+ # GROUPS
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import RepoGroup
+ tbl = RepoGroup.__table__
+
+ # add enable locking column
+ enable_locking = Column("enable_locking", Boolean(), nullable=True,
+ unique=None, default=False)
+ enable_locking.create(table=tbl)
+ enable_locking.alter(nullable=False, default=False)
+
+ #==========================================================================
+ # CACHE INVALIDATION
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import CacheInvalidation
+ tbl = CacheInvalidation.__table__
+
+ # add INDEX for cache keys
+ col = CacheInvalidation.__table__.columns.cache_key
+ col.alter(index=Index('key_idx', 'cache_key'))
+
+ #==========================================================================
+ # NOTIFICATION
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Notification
+ tbl = Notification.__table__
+
+ # add index for notification type
+ col = Notification.__table__.columns.type
+ col.alter(index=Index('notification_type_idx', 'type'),)
+
+ #==========================================================================
+ # CHANGESET_COMMENTS
+ #==========================================================================
+ from rhodecode.lib.dbmigrate.schema.db_1_3_0 import ChangesetComment
+
+ tbl = ChangesetComment.__table__
+ col = ChangesetComment.__table__.columns.revision
+
+ # add index for revisions
+ col.alter(index=Index('cc_revision_idx', 'revision'),)
+
+ # add hl_lines column
+ hl_lines = Column('hl_lines', Unicode(512), nullable=True)
+ hl_lines.create(table=tbl)
+
+ # add created_on column
+ created_on = Column('created_on', DateTime(timezone=False), nullable=True,
+ default=datetime.datetime.now)
+ created_on.create(table=tbl)
+ created_on.alter(nullable=False, default=datetime.datetime.now)
+
+ modified_at = Column('modified_at', DateTime(timezone=False), nullable=False,
+ default=datetime.datetime.now)
+ modified_at.alter(type=DateTime(timezone=False), table=tbl)
+
+ # add FK to pull_request
+ pull_request_id = Column("pull_request_id", Integer(),
+ ForeignKey('pull_requests.pull_request_id'),
+ nullable=True)
+ pull_request_id.create(table=tbl)
+ ## RESET COMPLETLY THE metadata for sqlalchemy back after using 1_3_0
+ Base = declarative_base()
+ Base.metadata.clear()
+ Base.metadata = MetaData()
+ Base.metadata.bind = migrate_engine
+ meta.Base = Base
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py
index 1f69ad28..cdb7e563 100644
--- a/rhodecode/lib/diffs.py
+++ b/rhodecode/lib/diffs.py
@@ -28,14 +28,22 @@
import re
import difflib
import markupsafe
+
from itertools import tee, imap
+from mercurial import patch
+from mercurial.mdiff import diffopts
+from mercurial.bundlerepo import bundlerepository
+
from pylons.i18n.translation import _
+from rhodecode.lib.compat import BytesIO
+from rhodecode.lib.vcs.utils.hgcompat import localrepo
from rhodecode.lib.vcs.exceptions import VCSError
from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
from rhodecode.lib.helpers import escape
-from rhodecode.lib.utils import EmptyChangeset
+from rhodecode.lib.utils import make_ui
def wrap_to_table(str_):
@@ -75,7 +83,7 @@ def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
stats = diff_processor.stat()
size = len(diff or '')
else:
- diff = wrap_to_table(_('Changeset was to big and was cut off, use '
+ diff = wrap_to_table(_('Changeset was too big and was cut off, use '
'diff menu to display this diff'))
stats = (0, 0)
size = 0
@@ -127,8 +135,9 @@ class DiffProcessor(object):
can be used to render it in a HTML template.
"""
_chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
+ _newline_marker = '\\ No newline at end of file\n'
- def __init__(self, diff, differ='diff', format='udiff'):
+ def __init__(self, diff, differ='diff', format='gitdiff'):
"""
:param diff: a text in diff format or generator
:param format: format of diff passed, `udiff` or `gitdiff`
@@ -171,7 +180,7 @@ class DiffProcessor(object):
def _extract_rev(self, line1, line2):
"""
- Extract the filename and revision hint from a line.
+ Extract the operation (A/M/D), filename and revision hint from a line.
"""
try:
@@ -189,11 +198,15 @@ class DiffProcessor(object):
filename = (old_filename
if old_filename != '/dev/null' else new_filename)
- return filename, new_rev, old_rev
+ operation = 'D' if new_filename == '/dev/null' else None
+ if not operation:
+ operation = 'M' if old_filename != '/dev/null' else 'A'
+
+ return operation, filename, new_rev, old_rev
except (ValueError, IndexError):
pass
- return None, None, None
+ return None, None, None, None
def _parse_gitdiff(self, diffiterator):
def line_decoder(l):
@@ -278,7 +291,7 @@ class DiffProcessor(object):
do(line)
do(next_)
- def _parse_udiff(self):
+ def _parse_udiff(self, inline_diff=True):
"""
Parse the diff an return data for the template.
"""
@@ -286,8 +299,6 @@ class DiffProcessor(object):
files = []
try:
line = lineiter.next()
- # skip first context
- skipfirst = True
while 1:
# continue until we found the old file
if not line.startswith('--- '):
@@ -295,13 +306,16 @@ class DiffProcessor(object):
continue
chunks = []
- filename, old_rev, new_rev = \
+ stats = [0, 0]
+ operation, filename, old_rev, new_rev = \
self._extract_rev(line, lineiter.next())
files.append({
'filename': filename,
'old_revision': old_rev,
'new_revision': new_rev,
- 'chunks': chunks
+ 'chunks': chunks,
+ 'operation': operation,
+ 'stats': stats,
})
line = lineiter.next()
@@ -317,27 +331,33 @@ class DiffProcessor(object):
[int(x or 1) for x in match.groups()[:-1]]
old_line -= 1
new_line -= 1
- context = len(match.groups()) == 5
+ gr = match.groups()
+ context = len(gr) == 5
old_end += old_line
new_end += new_line
if context:
- if not skipfirst:
+ # skip context only if it's first line
+ if int(gr[0]) > 1:
lines.append({
'old_lineno': '...',
'new_lineno': '...',
'action': 'context',
'line': line,
})
- else:
- skipfirst = False
line = lineiter.next()
+
while old_line < old_end or new_line < new_end:
if line:
- command, line = line[0], line[1:]
+ command = line[0]
+ if command in ['+', '-', ' ']:
+ #only modify the line if it's actually a diff
+ # thing
+ line = line[1:]
else:
command = ' '
+
affects_old = affects_new = False
# ignore those if we don't expect them
@@ -346,51 +366,67 @@ class DiffProcessor(object):
elif command == '+':
affects_new = True
action = 'add'
+ stats[0] += 1
elif command == '-':
affects_old = True
action = 'del'
+ stats[1] += 1
else:
affects_old = affects_new = True
action = 'unmod'
- old_line += affects_old
- new_line += affects_new
- lines.append({
- 'old_lineno': affects_old and old_line or '',
- 'new_lineno': affects_new and new_line or '',
- 'action': action,
- 'line': line
- })
+ if line != self._newline_marker:
+ old_line += affects_old
+ new_line += affects_new
+ lines.append({
+ 'old_lineno': affects_old and old_line or '',
+ 'new_lineno': affects_new and new_line or '',
+ 'action': action,
+ 'line': line
+ })
+
line = lineiter.next()
+ if line == self._newline_marker:
+ # we need to append to lines, since this is not
+ # counted in the line specs of diff
+ lines.append({
+ 'old_lineno': '...',
+ 'new_lineno': '...',
+ 'action': 'context',
+ 'line': line
+ })
except StopIteration:
pass
+ sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation'])
+ if inline_diff is False:
+ return sorted(files, key=sorter)
+
# highlight inline changes
- for _ in files:
- for chunk in chunks:
+ for diff_data in files:
+ for chunk in diff_data['chunks']:
lineiter = iter(chunk)
- #first = True
try:
while 1:
line = lineiter.next()
- if line['action'] != 'unmod':
+ if line['action'] not in ['unmod', 'context']:
nextline = lineiter.next()
- if nextline['action'] == 'unmod' or \
+ if nextline['action'] in ['unmod', 'context'] or \
nextline['action'] == line['action']:
continue
self.differ(line, nextline)
except StopIteration:
pass
- return files
+ return sorted(files, key=sorter)
- def prepare(self):
+ def prepare(self, inline_diff=True):
"""
Prepare the passed udiff for HTML rendering. It'l return a list
of dicts
"""
- return self._parse_udiff()
+ return self._parse_udiff(inline_diff=inline_diff)
def _safe_id(self, idstring):
"""Make a string safe for including in an id attribute.
@@ -424,9 +460,9 @@ class DiffProcessor(object):
def as_html(self, table_class='code-difftable', line_class='line',
new_lineno_class='lineno old', old_lineno_class='lineno new',
- code_class='code', enable_comments=False):
+ code_class='code', enable_comments=False, diff_lines=None):
"""
- Return udiff as html table with customized css classes
+ Return given diff as html table with customized css classes
"""
def _link_to_if(condition, label, url):
"""
@@ -440,7 +476,8 @@ class DiffProcessor(object):
}
else:
return label
- diff_lines = self.prepare()
+ if diff_lines is None:
+ diff_lines = self.prepare()
_html_empty = True
_html = []
_html.append('''<table class="%(table_class)s">\n''' % {
@@ -522,3 +559,78 @@ class DiffProcessor(object):
Returns tuple of added, and removed lines for this instance
"""
return self.adds, self.removes
+
+
+class InMemoryBundleRepo(bundlerepository):
+ def __init__(self, ui, path, bundlestream):
+ self._tempparent = None
+ localrepo.localrepository.__init__(self, ui, path)
+ self.ui.setconfig('phases', 'publish', False)
+
+ self.bundle = bundlestream
+
+ # dict with the mapping 'filename' -> position in the bundle
+ self.bundlefilespos = {}
+
+
+def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None):
+ """
+ General differ between branches, bookmarks or separate but releated
+ repositories
+
+ :param org_repo:
+ :type org_repo:
+ :param org_ref:
+ :type org_ref:
+ :param other_repo:
+ :type other_repo:
+ :param other_ref:
+ :type other_ref:
+ """
+
+ bundlerepo = None
+ ignore_whitespace = False
+ context = 3
+ org_repo = org_repo.scm_instance._repo
+ other_repo = other_repo.scm_instance._repo
+ opts = diffopts(git=True, ignorews=ignore_whitespace, context=context)
+ org_ref = org_ref[1]
+ other_ref = other_ref[1]
+
+ if org_repo != other_repo:
+
+ common, incoming, rheads = discovery_data
+ other_repo_peer = localrepo.locallegacypeer(other_repo.local())
+ # create a bundle (uncompressed if other repo is not local)
+ if other_repo_peer.capable('getbundle') and incoming:
+ # disable repo hooks here since it's just bundle !
+ # patch and reset hooks section of UI config to not run any
+ # hooks on fetching archives with subrepos
+ for k, _ in other_repo.ui.configitems('hooks'):
+ other_repo.ui.setconfig('hooks', k, None)
+
+ unbundle = other_repo.getbundle('incoming', common=common,
+ heads=rheads)
+
+ buf = BytesIO()
+ while True:
+ chunk = unbundle._stream.read(1024 * 4)
+ if not chunk:
+ break
+ buf.write(chunk)
+
+ buf.seek(0)
+ # replace chunked _stream with data that can do tell() and seek()
+ unbundle._stream = buf
+
+ ui = make_ui('db')
+ bundlerepo = InMemoryBundleRepo(ui, path=org_repo.root,
+ bundlestream=unbundle)
+
+ return ''.join(patch.diff(bundlerepo or org_repo,
+ node1=org_repo[org_ref].node(),
+ node2=other_repo[other_ref].node(),
+ opts=opts))
+ else:
+ return ''.join(patch.diff(org_repo, node1=org_ref, node2=other_ref,
+ opts=opts))
diff --git a/rhodecode/lib/exceptions.py b/rhodecode/lib/exceptions.py
index 3339c14c..19349fda 100644
--- a/rhodecode/lib/exceptions.py
+++ b/rhodecode/lib/exceptions.py
@@ -23,6 +23,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from webob.exc import HTTPClientError
+
class LdapUsernameError(Exception):
pass
@@ -50,3 +52,20 @@ class UserOwnsReposException(Exception):
class UsersGroupsAssignedException(Exception):
pass
+
+
+class StatusChangeOnClosedPullRequestError(Exception):
+ pass
+
+
+class HTTPLockedRC(HTTPClientError):
+ """
+ Special Exception For locked Repos in RhodeCode
+ """
+ code = 423
+ title = explanation = 'Repository Locked'
+
+ def __init__(self, reponame, username, *args, **kwargs):
+ self.title = self.explanation = ('Repository `%s` locked by '
+ 'user `%s`' % (reponame, username))
+ super(HTTPLockedRC, self).__init__(*args, **kwargs)
diff --git a/rhodecode/lib/ext_json.py b/rhodecode/lib/ext_json.py
index 098bffd7..63773588 100644
--- a/rhodecode/lib/ext_json.py
+++ b/rhodecode/lib/ext_json.py
@@ -60,7 +60,7 @@ def _obj_dump(obj):
# Import simplejson
try:
# import simplejson initially
- import simplejson as _sj
+ import simplejson
def extended_encode(obj):
try:
@@ -70,37 +70,42 @@ try:
raise TypeError("%r is not JSON serializable" % (obj,))
# we handle decimals our own it makes unified behavior of json vs
# simplejson
- _sj.dumps = functools.partial(_sj.dumps, default=extended_encode,
- use_decimal=False)
- _sj.dump = functools.partial(_sj.dump, default=extended_encode,
- use_decimal=False)
- simplejson = _sj
-
+ simplejson.dumps = functools.partial(simplejson.dumps,
+ default=extended_encode,
+ use_decimal=False)
+ simplejson.dump = functools.partial(simplejson.dump,
+ default=extended_encode,
+ use_decimal=False)
except ImportError:
# no simplejson set it to None
- _sj = None
+ simplejson = None
try:
# simplejson not found try out regular json module
- import json as _json
+ import json
# extended JSON encoder for json
- class ExtendedEncoder(_json.JSONEncoder):
+ class ExtendedEncoder(json.JSONEncoder):
def default(self, obj):
try:
return _obj_dump(obj)
except NotImplementedError:
pass
- return _json.JSONEncoder.default(self, obj)
+ return json.JSONEncoder.default(self, obj)
# monkey-patch JSON encoder to use extended version
- _json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder)
- _json.dump = functools.partial(_json.dump, cls=ExtendedEncoder)
- stdlib = _json
+ json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder)
+ json.dump = functools.partial(json.dump, cls=ExtendedEncoder)
+
except ImportError:
- _json = None
+ json = None
+
+stdlib = json
# set all available json modules
-simplejson = _sj
-stdjson = _json
-json = _sj if _sj else _json
+if simplejson:
+ json = simplejson
+elif json:
+ json = json
+else:
+ raise ImportError('Could not find any json modules')
diff --git a/rhodecode/lib/graphmod.py b/rhodecode/lib/graphmod.py
new file mode 100644
index 00000000..e5f86e05
--- /dev/null
+++ b/rhodecode/lib/graphmod.py
@@ -0,0 +1,127 @@
+"""
+Modified mercurial DAG graph functions that re-uses VCS structure
+
+It allows to have a shared codebase for DAG generation for hg and git repos
+"""
+
+nullrev = -1
+
+
+def grandparent(parentrev_func, lowestrev, roots, head):
+ """
+ Return all ancestors of head in roots which revision is
+ greater or equal to lowestrev.
+ """
+ pending = set([head])
+ seen = set()
+ kept = set()
+ llowestrev = max(nullrev, lowestrev)
+ while pending:
+ r = pending.pop()
+ if r >= llowestrev and r not in seen:
+ if r in roots:
+ kept.add(r)
+ else:
+ pending.update([p for p in parentrev_func(r)])
+ seen.add(r)
+ return sorted(kept)
+
+
+def _dagwalker(repo, revs, alias):
+ if not revs:
+ return
+
+ if alias == 'hg':
+ cl = repo._repo.changelog.parentrevs
+ repo = repo
+ elif alias == 'git':
+ def cl(rev):
+ return [x.revision for x in repo[rev].parents()]
+ repo = repo
+
+ lowestrev = min(revs)
+ gpcache = {}
+
+ knownrevs = set(revs)
+ for rev in revs:
+ ctx = repo[rev]
+ parents = sorted(set([p.revision for p in ctx.parents
+ if p.revision in knownrevs]))
+ mpars = [p.revision for p in ctx.parents if
+ p.revision != nullrev and p.revision not in parents]
+
+ for mpar in mpars:
+ gp = gpcache.get(mpar)
+ if gp is None:
+ gp = gpcache[mpar] = grandparent(cl, lowestrev, revs, mpar)
+ if not gp:
+ parents.append(mpar)
+ else:
+ parents.extend(g for g in gp if g not in parents)
+
+ yield (ctx.revision, 'C', ctx, parents)
+
+
+def _colored(dag):
+ """annotates a DAG with colored edge information
+
+ For each DAG node this function emits tuples::
+
+ (id, type, data, (col, color), [(col, nextcol, color)])
+
+ with the following new elements:
+
+ - Tuple (col, color) with column and color index for the current node
+ - A list of tuples indicating the edges between the current node and its
+ parents.
+ """
+ seen = []
+ colors = {}
+ newcolor = 1
+
+ getconf = lambda rev: {}
+
+ for (cur, type, data, parents) in dag:
+
+ # Compute seen and next
+ if cur not in seen:
+ seen.append(cur) # new head
+ colors[cur] = newcolor
+ newcolor += 1
+
+ col = seen.index(cur)
+ color = colors.pop(cur)
+ next = seen[:]
+
+ # Add parents to next
+ addparents = [p for p in parents if p not in next]
+ next[col:col + 1] = addparents
+
+ # Set colors for the parents
+ for i, p in enumerate(addparents):
+ if not i:
+ colors[p] = color
+ else:
+ colors[p] = newcolor
+ newcolor += 1
+
+ # Add edges to the graph
+ edges = []
+ for ecol, eid in enumerate(seen):
+ if eid in next:
+ bconf = getconf(eid)
+ edges.append((
+ ecol, next.index(eid), colors[eid],
+ bconf.get('width', -1),
+ bconf.get('color', '')))
+ elif eid == cur:
+ for p in parents:
+ bconf = getconf(p)
+ edges.append((
+ ecol, next.index(p), color,
+ bconf.get('width', -1),
+ bconf.get('color', '')))
+
+ # Yield and move on
+ yield (cur, type, data, (col, color), edges)
+ seen = next
diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py
index 3051b8e1..f0db7238 100644
--- a/rhodecode/lib/helpers.py
+++ b/rhodecode/lib/helpers.py
@@ -9,6 +9,7 @@ import StringIO
import urllib
import math
import logging
+import re
from datetime import datetime
from pygments.formatters.html import HtmlFormatter
@@ -40,12 +41,31 @@ from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
from rhodecode.lib.annotate import annotate_highlight
from rhodecode.lib.utils import repo_name_slug
from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
- get_changeset_safe
+ get_changeset_safe, datetime_to_time, time_to_datetime
from rhodecode.lib.markup_renderer import MarkupRenderer
+from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
+from rhodecode.lib.vcs.backends.base import BaseChangeset
+from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
+from rhodecode.model.changeset_status import ChangesetStatusModel
+from rhodecode.model.db import URL_SEP, Permission
log = logging.getLogger(__name__)
+html_escape_table = {
+ "&": "&amp;",
+ '"': "&quot;",
+ "'": "&apos;",
+ ">": "&gt;",
+ "<": "&lt;",
+}
+
+
+def html_escape(text):
+ """Produce entities within text."""
+ return "".join(html_escape_table.get(c,c) for c in text)
+
+
def shorter(text, size=20):
postfix = '...'
if len(text) > size:
@@ -105,7 +125,7 @@ class _GetError(object):
def __call__(self, field_name, form_errors):
tmpl = """<span class="error_msg">%s</span>"""
- if form_errors and form_errors.has_key(field_name):
+ if form_errors and field_name in form_errors:
return literal(tmpl % form_errors.get(field_name))
get_error = _GetError()
@@ -114,12 +134,15 @@ get_error = _GetError()
class _ToolTip(object):
def __call__(self, tooltip_title, trim_at=50):
- """Special function just to wrap our text into nice formatted
+ """
+ Special function just to wrap our text into nice formatted
autowrapped text
:param tooltip_title:
"""
- return escape(tooltip_title)
+ tooltip_title = escape(tooltip_title)
+ tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
+ return tooltip_title
tooltip = _ToolTip()
@@ -130,7 +153,8 @@ class _FilesBreadCrumbs(object):
paths = safe_unicode(paths)
url_l = [link_to(repo_name, url('files_home',
repo_name=repo_name,
- revision=rev, f_path=''))]
+ revision=rev, f_path=''),
+ class_='ypjax-link')]
paths_l = paths.split('/')
for cnt, p in enumerate(paths_l):
if p != '':
@@ -139,7 +163,8 @@ class _FilesBreadCrumbs(object):
repo_name=repo_name,
revision=rev,
f_path='/'.join(paths_l[:cnt + 1])
- )
+ ),
+ class_='ypjax-link'
)
)
@@ -333,7 +358,7 @@ flash = _Flash()
#==============================================================================
from rhodecode.lib.vcs.utils import author_name, author_email
from rhodecode.lib.utils2 import credentials_filter, age as _age
-from rhodecode.model.db import User
+from rhodecode.model.db import User, ChangesetStatus
age = lambda x: _age(x)
capitalize = lambda x: x.capitalize()
@@ -342,6 +367,14 @@ short_id = lambda x: x[:12]
hide_credentials = lambda x: ''.join(credentials_filter(x))
+def fmt_date(date):
+ if date:
+ _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
+ return date.strftime(_fmt).decode('utf8')
+
+ return ""
+
+
def is_git(repository):
if hasattr(repository, 'alias'):
_type = repository.alias
@@ -363,8 +396,14 @@ def is_hg(repository):
def email_or_none(author):
+ # extract email from the commit string
_email = email(author)
if _email != '':
+ # check it against RhodeCode database, and use the MAIN email for this
+ # user
+ user = User.get_by_email(_email, case_insensitive=True, cache=True)
+ if user is not None:
+ return user.email
return _email
# See if it contains a username we can get an email from
@@ -377,9 +416,9 @@ def email_or_none(author):
return None
-def person(author):
+def person(author, show_attr="username_and_name"):
# attr to return from fetched user
- person_getter = lambda usr: usr.username
+ person_getter = lambda usr: getattr(usr, show_attr)
# Valid email in the attribute passed, see if they're in the system
_email = email(author)
@@ -400,6 +439,39 @@ def person(author):
return _author
+def person_by_id(id_, show_attr="username_and_name"):
+ # attr to return from fetched user
+ person_getter = lambda usr: getattr(usr, show_attr)
+
+ #maybe it's an ID ?
+ if str(id_).isdigit() or isinstance(id_, int):
+ id_ = int(id_)
+ user = User.get(id_)
+ if user is not None:
+ return person_getter(user)
+ return id_
+
+
+def desc_stylize(value):
+ """
+ converts tags from value into html equivalent
+
+ :param value:
+ """
+ value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
+ '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
+ value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
+ '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
+ value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
+ '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
+ value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
+ '<div class="metatag" tag="lang">\\2</div>', value)
+ value = re.sub(r'\[([a-z]+)\]',
+ '<div class="metatag" tag="\\1">\\1</div>', value)
+
+ return value
+
+
def bool2icon(value):
"""Returns True/False values represented as small html image of true/false
icons
@@ -447,22 +519,30 @@ def action_parser(user_log, feed=False):
repo = user_log.repository.scm_instance
- message = lambda rev: rev.message
- lnk = lambda rev, repo_name: (
- link_to('r%s:%s' % (rev.revision, rev.short_id),
- url('changeset_home', repo_name=repo_name,
- revision=rev.raw_id),
- title=tooltip(message(rev)), class_='tooltip')
- )
+ def lnk(rev, repo_name):
+
+ if isinstance(rev, BaseChangeset):
+ lbl = 'r%s:%s' % (rev.revision, rev.short_id)
+ _url = url('changeset_home', repo_name=repo_name,
+ revision=rev.raw_id)
+ title = tooltip(rev.message)
+ else:
+ lbl = '%s' % rev
+ _url = '#'
+ title = _('Changeset not found')
+
+ return link_to(lbl, _url, title=title, class_='tooltip',)
revs = []
if len(filter(lambda v: v != '', revs_ids)) > 0:
- # get only max revs_top_limit of changeset for performance/ui reasons
- revs = [
- x for x in repo.get_changesets(revs_ids[0],
- revs_ids[:revs_top_limit][-1])
- ]
-
+ for rev in revs_ids[:revs_top_limit]:
+ try:
+ rev = repo.get_changeset(rev)
+ revs.append(rev)
+ except ChangesetDoesNotExistError:
+ log.error('cannot find revision %s in this repo' % rev)
+ revs.append(rev)
+ continue
cs_links = []
cs_links.append(" " + ', '.join(
[lnk(rev, repo_name) for rev in revs[:revs_limit]]
@@ -526,22 +606,68 @@ def action_parser(user_log, feed=False):
return _('fork name ') + str(link_to(action_params, url('summary_home',
repo_name=repo_name,)))
- action_map = {'user_deleted_repo': (_('[deleted] repository'), None),
- 'user_created_repo': (_('[created] repository'), None),
- 'user_created_fork': (_('[created] repository as fork'), None),
- 'user_forked_repo': (_('[forked] repository'), get_fork_name),
- 'user_updated_repo': (_('[updated] repository'), None),
- 'admin_deleted_repo': (_('[delete] repository'), None),
- 'admin_created_repo': (_('[created] repository'), None),
- 'admin_forked_repo': (_('[forked] repository'), None),
- 'admin_updated_repo': (_('[updated] repository'), None),
- 'push': (_('[pushed] into'), get_cs_links),
- 'push_local': (_('[committed via RhodeCode] into'), get_cs_links),
- 'push_remote': (_('[pulled from remote] into'), get_cs_links),
- 'pull': (_('[pulled] from'), None),
- 'started_following_repo': (_('[started following] repository'), None),
- 'stopped_following_repo': (_('[stopped following] repository'), None),
- }
+ def get_user_name():
+ user_name = action_params
+ return user_name
+
+ def get_users_group():
+ group_name = action_params
+ return group_name
+
+ def get_pull_request():
+ pull_request_id = action_params
+ repo_name = user_log.repository.repo_name
+ return link_to(_('Pull request #%s') % pull_request_id,
+ url('pullrequest_show', repo_name=repo_name,
+ pull_request_id=pull_request_id))
+
+ # action : translated str, callback(extractor), icon
+ action_map = {
+ 'user_deleted_repo': (_('[deleted] repository'),
+ None, 'database_delete.png'),
+ 'user_created_repo': (_('[created] repository'),
+ None, 'database_add.png'),
+ 'user_created_fork': (_('[created] repository as fork'),
+ None, 'arrow_divide.png'),
+ 'user_forked_repo': (_('[forked] repository'),
+ get_fork_name, 'arrow_divide.png'),
+ 'user_updated_repo': (_('[updated] repository'),
+ None, 'database_edit.png'),
+ 'admin_deleted_repo': (_('[delete] repository'),
+ None, 'database_delete.png'),
+ 'admin_created_repo': (_('[created] repository'),
+ None, 'database_add.png'),
+ 'admin_forked_repo': (_('[forked] repository'),
+ None, 'arrow_divide.png'),
+ 'admin_updated_repo': (_('[updated] repository'),
+ None, 'database_edit.png'),
+ 'admin_created_user': (_('[created] user'),
+ get_user_name, 'user_add.png'),
+ 'admin_updated_user': (_('[updated] user'),
+ get_user_name, 'user_edit.png'),
+ 'admin_created_users_group': (_('[created] users group'),
+ get_users_group, 'group_add.png'),
+ 'admin_updated_users_group': (_('[updated] users group'),
+ get_users_group, 'group_edit.png'),
+ 'user_commented_revision': (_('[commented] on revision in repository'),
+ get_cs_links, 'comment_add.png'),
+ 'user_commented_pull_request': (_('[commented] on pull request for'),
+ get_pull_request, 'comment_add.png'),
+ 'user_closed_pull_request': (_('[closed] pull request for'),
+ get_pull_request, 'tick.png'),
+ 'push': (_('[pushed] into'),
+ get_cs_links, 'script_add.png'),
+ 'push_local': (_('[committed via RhodeCode] into repository'),
+ get_cs_links, 'script_edit.png'),
+ 'push_remote': (_('[pulled from remote] into repository'),
+ get_cs_links, 'connect.png'),
+ 'pull': (_('[pulled] from'),
+ None, 'down_16.png'),
+ 'started_following_repo': (_('[started following] repository'),
+ None, 'heart_add.png'),
+ 'stopped_following_repo': (_('[stopped following] repository'),
+ None, 'heart_delete.png'),
+ }
action_str = action_map.get(action, action)
if feed:
@@ -556,36 +682,21 @@ def action_parser(user_log, feed=False):
if callable(action_str[1]):
action_params_func = action_str[1]
- return [literal(action), action_params_func]
+ def action_parser_icon():
+ action = user_log.action
+ action_params = None
+ x = action.split(':')
+ if len(x) > 1:
+ action, action_params = x
-def action_parser_icon(user_log):
- action = user_log.action
- action_params = None
- x = action.split(':')
+ tmpl = """<img src="%s%s" alt="%s"/>"""
+ ico = action_map.get(action, ['', '', ''])[2]
+ return literal(tmpl % ((url('/images/icons/')), ico, action))
- if len(x) > 1:
- action, action_params = x
+ # returned callbacks we need to call to get
+ return [lambda: literal(action), action_params_func, action_parser_icon]
- tmpl = """<img src="%s%s" alt="%s"/>"""
- map = {'user_deleted_repo':'database_delete.png',
- 'user_created_repo':'database_add.png',
- 'user_created_fork':'arrow_divide.png',
- 'user_forked_repo':'arrow_divide.png',
- 'user_updated_repo':'database_edit.png',
- 'admin_deleted_repo':'database_delete.png',
- 'admin_created_repo':'database_add.png',
- 'admin_forked_repo':'arrow_divide.png',
- 'admin_updated_repo':'database_edit.png',
- 'push':'script_add.png',
- 'push_local':'script_edit.png',
- 'push_remote':'connect.png',
- 'pull':'down_16.png',
- 'started_following_repo':'heart_add.png',
- 'stopped_following_repo':'heart_delete.png',
- }
- return literal(tmpl % ((url('/images/icons/')),
- map.get(action, action), action))
#==============================================================================
@@ -600,6 +711,14 @@ HasRepoPermissionAny, HasRepoPermissionAll
#==============================================================================
def gravatar_url(email_address, size=30):
+ if(str2bool(config['app_conf'].get('use_gravatar')) and
+ config['app_conf'].get('alternative_gravatar_url')):
+ tmpl = config['app_conf'].get('alternative_gravatar_url', '')
+ tmpl = tmpl.replace('{email}', email_address)\
+ .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest())\
+ .replace('{size}', str(size))
+ return tmpl
+
if (not str2bool(config['app_conf'].get('use_gravatar')) or
not email_address or email_address == 'anonymous@rhodecode.org'):
f = lambda a, l: min(l, key=lambda x: abs(x - a))
@@ -875,7 +994,6 @@ def urlify_commit(text_, repository=None, link_=None):
return ''.join(links)
-
# urlify changesets - extrac revisions and make link out of them
text_ = urlify_changesets(escaper(text_), repository)
@@ -902,7 +1020,8 @@ def urlify_commit(text_, repository=None, link_=None):
url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
if repository:
url = url.replace('{repo}', repository)
-
+ repo_name = repository.split(URL_SEP)[-1]
+ url = url.replace('{repo_name}', repo_name)
return tmpl % {
'pref': pref,
'cls': 'issue-tracker-link',
@@ -939,3 +1058,15 @@ def rst_w_mentions(source):
"""
return literal('<div class="rst-block">%s</div>' %
MarkupRenderer.rst_with_mentions(source))
+
+
+def changeset_status(repo, revision):
+ return ChangesetStatusModel().get_status(repo, revision)
+
+
+def changeset_status_lbl(changeset_status):
+ return dict(ChangesetStatus.STATUSES).get(changeset_status)
+
+
+def get_permission_name(key):
+ return dict(Permission.PERMS).get(key)
diff --git a/rhodecode/lib/hooks.py b/rhodecode/lib/hooks.py
index 93c5f584..4eaefd7e 100644
--- a/rhodecode/lib/hooks.py
+++ b/rhodecode/lib/hooks.py
@@ -24,13 +24,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
+import binascii
+from inspect import isfunction
from mercurial.scmutil import revrange
from mercurial.node import nullrev
-from rhodecode import EXTENSIONS
+
from rhodecode.lib import helpers as h
from rhodecode.lib.utils import action_logger
-from inspect import isfunction
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
+from rhodecode.lib.compat import json
+from rhodecode.model.db import Repository, User
+from rhodecode.lib.utils2 import safe_str
+from rhodecode.lib.exceptions import HTTPLockedRC
def _get_scm_size(alias, root_path):
@@ -81,6 +87,60 @@ def repo_size(ui, repo, hooktype=None, **kwargs):
sys.stdout.write(msg)
+def pre_push(ui, repo, **kwargs):
+ # pre push function, currently used to ban pushing when
+ # repository is locked
+ try:
+ rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
+ except:
+ rc_extras = {}
+ extras = dict(repo.ui.configitems('rhodecode_extras'))
+
+ if 'username' in extras:
+ username = extras['username']
+ repository = extras['repository']
+ scm = extras['scm']
+ locked_by = extras['locked_by']
+ elif 'username' in rc_extras:
+ username = rc_extras['username']
+ repository = rc_extras['repository']
+ scm = rc_extras['scm']
+ locked_by = rc_extras['locked_by']
+ else:
+ raise Exception('Missing data in repo.ui and os.environ')
+
+ usr = User.get_by_username(username)
+ if locked_by[0] and usr.user_id != int(locked_by[0]):
+ locked_by = User.get(locked_by[0]).username
+ raise HTTPLockedRC(repository, locked_by)
+
+
+def pre_pull(ui, repo, **kwargs):
+ # pre push function, currently used to ban pushing when
+ # repository is locked
+ try:
+ rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
+ except:
+ rc_extras = {}
+ extras = dict(repo.ui.configitems('rhodecode_extras'))
+ if 'username' in extras:
+ username = extras['username']
+ repository = extras['repository']
+ scm = extras['scm']
+ locked_by = extras['locked_by']
+ elif 'username' in rc_extras:
+ username = rc_extras['username']
+ repository = rc_extras['repository']
+ scm = rc_extras['scm']
+ locked_by = rc_extras['locked_by']
+ else:
+ raise Exception('Missing data in repo.ui and os.environ')
+
+ if locked_by[0]:
+ locked_by = User.get(locked_by[0]).username
+ raise HTTPLockedRC(repository, locked_by)
+
+
def log_pull_action(ui, repo, **kwargs):
"""
Logs user last pull action
@@ -88,21 +148,40 @@ def log_pull_action(ui, repo, **kwargs):
:param ui:
:param repo:
"""
-
+ try:
+ rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
+ except:
+ rc_extras = {}
extras = dict(repo.ui.configitems('rhodecode_extras'))
- username = extras['username']
- repository = extras['repository']
- scm = extras['scm']
+ if 'username' in extras:
+ username = extras['username']
+ repository = extras['repository']
+ scm = extras['scm']
+ make_lock = extras['make_lock']
+ elif 'username' in rc_extras:
+ username = rc_extras['username']
+ repository = rc_extras['repository']
+ scm = rc_extras['scm']
+ make_lock = rc_extras['make_lock']
+ else:
+ raise Exception('Missing data in repo.ui and os.environ')
+ user = User.get_by_username(username)
action = 'pull'
-
- action_logger(username, action, repository, extras['ip'], commit=True)
+ action_logger(user, action, repository, extras['ip'], commit=True)
# extension hook call
+ from rhodecode import EXTENSIONS
callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
if isfunction(callback):
kw = {}
kw.update(extras)
callback(**kw)
+
+ if make_lock is True:
+ Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
+ #msg = 'Made lock on repo `%s`' % repository
+ #sys.stdout.write(msg)
+
return 0
@@ -114,11 +193,26 @@ def log_push_action(ui, repo, **kwargs):
:param repo: repo object containing the `ui` object
"""
+ try:
+ rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
+ except:
+ rc_extras = {}
+
extras = dict(repo.ui.configitems('rhodecode_extras'))
- username = extras['username']
- repository = extras['repository']
- action = extras['action'] + ':%s'
- scm = extras['scm']
+ if 'username' in extras:
+ username = extras['username']
+ repository = extras['repository']
+ scm = extras['scm']
+ make_lock = extras['make_lock']
+ elif 'username' in rc_extras:
+ username = rc_extras['username']
+ repository = rc_extras['repository']
+ scm = rc_extras['scm']
+ make_lock = rc_extras['make_lock']
+ else:
+ raise Exception('Missing data in repo.ui and os.environ')
+
+ action = 'push' + ':%s'
if scm == 'hg':
node = kwargs['node']
@@ -134,21 +228,30 @@ def log_push_action(ui, repo, **kwargs):
return (len(repo) - 1, 0)
stop, start = get_revs(repo, [node + ':'])
-
- revs = (str(repo[r]) for r in xrange(start, stop + 1))
+ h = binascii.hexlify
+ revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
elif scm == 'git':
- revs = []
+ revs = kwargs.get('_git_revs', [])
+ if '_git_revs' in kwargs:
+ kwargs.pop('_git_revs')
action = action % ','.join(revs)
action_logger(username, action, repository, extras['ip'], commit=True)
# extension hook call
+ from rhodecode import EXTENSIONS
callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
if isfunction(callback):
kw = {'pushed_revs': revs}
kw.update(extras)
callback(**kw)
+
+ if make_lock is False:
+ Repository.unlock(Repository.get_by_repo_name(repository))
+ msg = 'Released lock on repo `%s`\n' % repository
+ sys.stdout.write(msg)
+
return 0
@@ -178,7 +281,7 @@ def log_create_repository(repository_dict, created_by, **kwargs):
'repo_name'
"""
-
+ from rhodecode import EXTENSIONS
callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
if isfunction(callback):
kw = {}
@@ -188,3 +291,88 @@ def log_create_repository(repository_dict, created_by, **kwargs):
return callback(**kw)
return 0
+
+handle_git_pre_receive = (lambda repo_path, revs, env:
+ handle_git_receive(repo_path, revs, env, hook_type='pre'))
+handle_git_post_receive = (lambda repo_path, revs, env:
+ handle_git_receive(repo_path, revs, env, hook_type='post'))
+
+
+def handle_git_receive(repo_path, revs, env, hook_type='post'):
+ """
+ A really hacky method that is runned by git post-receive hook and logs
+ an push action together with pushed revisions. It's executed by subprocess
+ thus needs all info to be able to create a on the fly pylons enviroment,
+ connect to database and run the logging code. Hacky as sh*t but works.
+
+ :param repo_path:
+ :type repo_path:
+ :param revs:
+ :type revs:
+ :param env:
+ :type env:
+ """
+ from paste.deploy import appconfig
+ from sqlalchemy import engine_from_config
+ from rhodecode.config.environment import load_environment
+ from rhodecode.model import init_model
+ from rhodecode.model.db import RhodeCodeUi
+ from rhodecode.lib.utils import make_ui
+
+ path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
+ conf = appconfig('config:%s' % ini_name, relative_to=path)
+ load_environment(conf.global_conf, conf.local_conf)
+
+ engine = engine_from_config(conf, 'sqlalchemy.db1.')
+ init_model(engine)
+
+ baseui = make_ui('db')
+ # fix if it's not a bare repo
+ if repo_path.endswith('.git'):
+ repo_path = repo_path[:-4]
+ repo = Repository.get_by_full_path(repo_path)
+ _hooks = dict(baseui.configitems('hooks')) or {}
+
+ extras = json.loads(env['RHODECODE_EXTRAS'])
+ for k, v in extras.items():
+ baseui.setconfig('rhodecode_extras', k, v)
+ repo = repo.scm_instance
+ repo.ui = baseui
+
+ if hook_type == 'pre':
+ pre_push(baseui, repo)
+
+ # if push hook is enabled via web interface
+ elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
+
+ rev_data = []
+ for l in revs:
+ old_rev, new_rev, ref = l.split(' ')
+ _ref_data = ref.split('/')
+ if _ref_data[1] in ['tags', 'heads']:
+ rev_data.append({'old_rev': old_rev,
+ 'new_rev': new_rev,
+ 'ref': ref,
+ 'type': _ref_data[1],
+ 'name': _ref_data[2].strip()})
+
+ git_revs = []
+ for push_ref in rev_data:
+ _type = push_ref['type']
+ if _type == 'heads':
+ if push_ref['old_rev'] == EmptyChangeset().raw_id:
+ cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
+ heads = repo.run_git_command(cmd)[0]
+ heads = heads.replace(push_ref['ref'], '')
+ heads = ' '.join(map(lambda c: c.strip('\n').strip(),
+ heads.splitlines()))
+ cmd = (('log %(new_rev)s' % push_ref) +
+ ' --reverse --pretty=format:"%H" --not ' + heads)
+ else:
+ cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
+ ' --reverse --pretty=format:"%H"')
+ git_revs += repo.run_git_command(cmd)[0].splitlines()
+ elif _type == 'tags':
+ git_revs += [push_ref['name']]
+
+ log_push_action(baseui, repo, _git_revs=git_revs)
diff --git a/rhodecode/lib/indexers/__init__.py b/rhodecode/lib/indexers/__init__.py
index fa11f90b..473fe8e1 100644
--- a/rhodecode/lib/indexers/__init__.py
+++ b/rhodecode/lib/indexers/__init__.py
@@ -35,12 +35,12 @@ from string import strip
from shutil import rmtree
from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
-from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
+from whoosh.fields import TEXT, ID, STORED, NUMERIC, BOOLEAN, Schema, FieldType
from whoosh.index import create_in, open_dir
from whoosh.formats import Characters
from whoosh.highlight import highlight, HtmlFormatter, ContextFragmenter
-from webhelpers.html.builder import escape
+from webhelpers.html.builder import escape, literal
from sqlalchemy import engine_from_config
from rhodecode.model import init_model
@@ -51,12 +51,14 @@ from rhodecode.lib.utils2 import LazyProperty
from rhodecode.lib.utils import BasePasterCommand, Command, add_cache,\
load_rcextensions
+log = logging.getLogger(__name__)
+
# CUSTOM ANALYZER wordsplit + lowercase filter
ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
-
#INDEX SCHEMA DEFINITION
SCHEMA = Schema(
+ fileid=ID(unique=True),
owner=TEXT(),
repository=TEXT(stored=True),
path=TEXT(stored=True),
@@ -70,6 +72,23 @@ IDX_NAME = 'HG_INDEX'
FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
FRAGMENTER = ContextFragmenter(200)
+CHGSETS_SCHEMA = Schema(
+ raw_id=ID(unique=True, stored=True),
+ date=NUMERIC(stored=True),
+ last=BOOLEAN(),
+ owner=TEXT(),
+ repository=ID(unique=True, stored=True),
+ author=TEXT(stored=True),
+ message=FieldType(format=Characters(), analyzer=ANALYZER,
+ scorable=True, stored=True),
+ parents=TEXT(),
+ added=TEXT(),
+ removed=TEXT(),
+ changed=TEXT(),
+)
+
+CHGSET_IDX_NAME = 'CHGSET_INDEX'
+
class MakeIndex(BasePasterCommand):
@@ -93,6 +112,8 @@ class MakeIndex(BasePasterCommand):
if self.options.repo_location else RepoModel().repos_path
repo_list = map(strip, self.options.repo_list.split(',')) \
if self.options.repo_list else None
+ repo_update_list = map(strip, self.options.repo_update_list.split(',')) \
+ if self.options.repo_update_list else None
load_rcextensions(config['here'])
#======================================================================
# WHOOSH DAEMON
@@ -103,7 +124,8 @@ class MakeIndex(BasePasterCommand):
l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
WhooshIndexingDaemon(index_location=index_location,
repo_location=repo_location,
- repo_list=repo_list,)\
+ repo_list=repo_list,
+ repo_update_list=repo_update_list)\
.run(full_index=self.options.full_index)
l.release()
except LockHeld:
@@ -119,7 +141,14 @@ class MakeIndex(BasePasterCommand):
action='store',
dest='repo_list',
help="Specifies a comma separated list of repositores "
- "to build index on OPTIONAL",
+ "to build index on. If not given all repositories "
+ "are scanned for indexing. OPTIONAL",
+ )
+ self.parser.add_option('--update-only',
+ action='store',
+ dest='repo_update_list',
+ help="Specifies a comma separated list of repositores "
+ "to re-build index on. OPTIONAL",
)
self.parser.add_option('-f',
action='store_true',
@@ -129,13 +158,15 @@ class MakeIndex(BasePasterCommand):
default=False)
-class ResultWrapper(object):
- def __init__(self, search_type, searcher, matcher, highlight_items):
+class WhooshResultWrapper(object):
+ def __init__(self, search_type, searcher, matcher, highlight_items,
+ repo_location):
self.search_type = search_type
self.searcher = searcher
self.matcher = matcher
self.highlight_items = highlight_items
self.fragment_size = 200
+ self.repo_location = repo_location
@LazyProperty
def doc_ids(self):
@@ -178,13 +209,25 @@ class ResultWrapper(object):
def get_full_content(self, docid):
res = self.searcher.stored_fields(docid[0])
- f_path = res['path'][res['path'].find(res['repository']) \
- + len(res['repository']):].lstrip('/')
-
- content_short = self.get_short_content(res, docid[1])
- res.update({'content_short': content_short,
- 'content_short_hl': self.highlight(content_short),
- 'f_path': f_path})
+ log.debug('result: %s' % res)
+ if self.search_type == 'content':
+ full_repo_path = jn(self.repo_location, res['repository'])
+ f_path = res['path'].split(full_repo_path)[-1]
+ f_path = f_path.lstrip(os.sep)
+ content_short = self.get_short_content(res, docid[1])
+ res.update({'content_short': content_short,
+ 'content_short_hl': self.highlight(content_short),
+ 'f_path': f_path
+ })
+ elif self.search_type == 'path':
+ full_repo_path = jn(self.repo_location, res['repository'])
+ f_path = res['path'].split(full_repo_path)[-1]
+ f_path = f_path.lstrip(os.sep)
+ res.update({'f_path': f_path})
+ elif self.search_type == 'message':
+ res.update({'message_hl': self.highlight(res['message'])})
+
+ log.debug('result: %s' % res)
return res
@@ -202,22 +245,23 @@ class ResultWrapper(object):
:param size:
"""
memory = [(0, 0)]
- for span in self.matcher.spans():
- start = span.startchar or 0
- end = span.endchar or 0
- start_offseted = max(0, start - self.fragment_size)
- end_offseted = end + self.fragment_size
-
- if start_offseted < memory[-1][1]:
- start_offseted = memory[-1][1]
- memory.append((start_offseted, end_offseted,))
- yield (start_offseted, end_offseted,)
+ if self.matcher.supports('positions'):
+ for span in self.matcher.spans():
+ start = span.startchar or 0
+ end = span.endchar or 0
+ start_offseted = max(0, start - self.fragment_size)
+ end_offseted = end + self.fragment_size
+
+ if start_offseted < memory[-1][1]:
+ start_offseted = memory[-1][1]
+ memory.append((start_offseted, end_offseted,))
+ yield (start_offseted, end_offseted,)
def highlight(self, content, top=5):
- if self.search_type != 'content':
+ if self.search_type not in ['content', 'message']:
return ''
hl = highlight(
- text=escape(content),
+ text=content,
terms=self.highlight_items,
analyzer=ANALYZER,
fragmenter=FRAGMENTER,
diff --git a/rhodecode/lib/indexers/daemon.py b/rhodecode/lib/indexers/daemon.py
index 9b5aa806..51122e9c 100644
--- a/rhodecode/lib/indexers/daemon.py
+++ b/rhodecode/lib/indexers/daemon.py
@@ -22,6 +22,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import with_statement
import os
import sys
@@ -41,23 +42,27 @@ sys.path.append(project_path)
from rhodecode.config.conf import INDEX_EXTENSIONS
from rhodecode.model.scm import ScmModel
from rhodecode.lib.utils2 import safe_unicode
-from rhodecode.lib.indexers import SCHEMA, IDX_NAME
+from rhodecode.lib.indexers import SCHEMA, IDX_NAME, CHGSETS_SCHEMA, \
+ CHGSET_IDX_NAME
from rhodecode.lib.vcs.exceptions import ChangesetError, RepositoryError, \
NodeDoesNotExistError
-from whoosh.index import create_in, open_dir
+from whoosh.index import create_in, open_dir, exists_in
+from whoosh.query import *
+from whoosh.qparser import QueryParser
log = logging.getLogger('whoosh_indexer')
class WhooshIndexingDaemon(object):
"""
- Daemon for atomic jobs
+ Daemon for atomic indexing jobs
"""
def __init__(self, indexname=IDX_NAME, index_location=None,
- repo_location=None, sa=None, repo_list=None):
+ repo_location=None, sa=None, repo_list=None,
+ repo_update_list=None):
self.indexname = indexname
self.index_location = index_location
@@ -70,20 +75,37 @@ class WhooshIndexingDaemon(object):
self.repo_paths = ScmModel(sa).repo_scan(self.repo_location)
+ #filter repo list
if repo_list:
- filtered_repo_paths = {}
+ self.filtered_repo_paths = {}
for repo_name, repo in self.repo_paths.items():
if repo_name in repo_list:
- filtered_repo_paths[repo_name] = repo
+ self.filtered_repo_paths[repo_name] = repo
- self.repo_paths = filtered_repo_paths
+ self.repo_paths = self.filtered_repo_paths
- self.initial = False
+ #filter update repo list
+ self.filtered_repo_update_paths = {}
+ if repo_update_list:
+ self.filtered_repo_update_paths = {}
+ for repo_name, repo in self.repo_paths.items():
+ if repo_name in repo_update_list:
+ self.filtered_repo_update_paths[repo_name] = repo
+ self.repo_paths = self.filtered_repo_update_paths
+
+ self.initial = True
if not os.path.isdir(self.index_location):
os.makedirs(self.index_location)
log.info('Cannot run incremental index since it does not'
' yet exist running full build')
- self.initial = True
+ elif not exists_in(self.index_location, IDX_NAME):
+ log.info('Running full index build as the file content'
+ ' index does not exist')
+ elif not exists_in(self.index_location, CHGSET_IDX_NAME):
+ log.info('Running full index build as the changeset'
+ ' index does not exist')
+ else:
+ self.initial = False
def get_paths(self, repo):
"""
@@ -93,11 +115,11 @@ class WhooshIndexingDaemon(object):
index_paths_ = set()
try:
tip = repo.get_changeset('tip')
- for topnode, dirs, files in tip.walk('/'):
+ for _topnode, _dirs, files in tip.walk('/'):
for f in files:
index_paths_.add(jn(repo.path, f.path))
- except RepositoryError, e:
+ except RepositoryError:
log.debug(traceback.format_exc())
pass
return index_paths_
@@ -135,45 +157,136 @@ class WhooshIndexingDaemon(object):
u_content = u''
indexed += 1
+ p = safe_unicode(path)
writer.add_document(
+ fileid=p,
owner=unicode(repo.contact),
repository=safe_unicode(repo_name),
- path=safe_unicode(path),
+ path=p,
content=u_content,
modtime=self.get_node_mtime(node),
extension=node.extension
)
return indexed, indexed_w_content
- def build_index(self):
- if os.path.exists(self.index_location):
- log.debug('removing previous index')
- rmtree(self.index_location)
-
- if not os.path.exists(self.index_location):
- os.mkdir(self.index_location)
+ def index_changesets(self, writer, repo_name, repo, start_rev=None):
+ """
+ Add all changeset in the vcs repo starting at start_rev
+ to the index writer
+
+ :param writer: the whoosh index writer to add to
+ :param repo_name: name of the repository from whence the
+ changeset originates including the repository group
+ :param repo: the vcs repository instance to index changesets for,
+ the presumption is the repo has changesets to index
+ :param start_rev=None: the full sha id to start indexing from
+ if start_rev is None then index from the first changeset in
+ the repo
+ """
- idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
- writer = idx.writer()
- log.debug('BUILDIN INDEX FOR EXTENSIONS %s' % INDEX_EXTENSIONS)
- for repo_name, repo in self.repo_paths.items():
- log.debug('building index @ %s' % repo.path)
- i_cnt = iwc_cnt = 0
- for idx_path in self.get_paths(repo):
- i, iwc = self.add_doc(writer, idx_path, repo, repo_name)
- i_cnt += i
- iwc_cnt += iwc
- log.debug('added %s files %s with content for repo %s' % (
- i_cnt + iwc_cnt, iwc_cnt, repo.path)
+ if start_rev is None:
+ start_rev = repo[0].raw_id
+
+ log.debug('indexing changesets in %s starting at rev: %s' %
+ (repo_name, start_rev))
+
+ indexed = 0
+ for cs in repo.get_changesets(start=start_rev):
+ log.debug(' >> %s' % cs)
+ writer.add_document(
+ raw_id=unicode(cs.raw_id),
+ owner=unicode(repo.contact),
+ date=cs._timestamp,
+ repository=safe_unicode(repo_name),
+ author=cs.author,
+ message=cs.message,
+ last=cs.last,
+ added=u' '.join([safe_unicode(node.path) for node in cs.added]).lower(),
+ removed=u' '.join([safe_unicode(node.path) for node in cs.removed]).lower(),
+ changed=u' '.join([safe_unicode(node.path) for node in cs.changed]).lower(),
+ parents=u' '.join([cs.raw_id for cs in cs.parents]),
)
+ indexed += 1
- log.debug('>> COMMITING CHANGES <<')
- writer.commit(merge=True)
- log.debug('>>> FINISHED BUILDING INDEX <<<')
+ log.debug('indexed %d changesets for repo %s' % (indexed, repo_name))
+ return indexed
- def update_index(self):
- log.debug('STARTING INCREMENTAL INDEXING UPDATE FOR EXTENSIONS %s' %
- INDEX_EXTENSIONS)
+ def index_files(self, file_idx_writer, repo_name, repo):
+ """
+ Index files for given repo_name
+
+ :param file_idx_writer: the whoosh index writer to add to
+ :param repo_name: name of the repository we're indexing
+ :param repo: instance of vcs repo
+ """
+ i_cnt = iwc_cnt = 0
+ log.debug('building index for [%s]' % repo.path)
+ for idx_path in self.get_paths(repo):
+ i, iwc = self.add_doc(file_idx_writer, idx_path, repo, repo_name)
+ i_cnt += i
+ iwc_cnt += iwc
+
+ log.debug('added %s files %s with content for repo %s' %
+ (i_cnt + iwc_cnt, iwc_cnt, repo.path))
+ return i_cnt, iwc_cnt
+
+ def update_changeset_index(self):
+ idx = open_dir(self.index_location, indexname=CHGSET_IDX_NAME)
+
+ with idx.searcher() as searcher:
+ writer = idx.writer()
+ writer_is_dirty = False
+ try:
+ indexed_total = 0
+ for repo_name, repo in self.repo_paths.items():
+ # skip indexing if there aren't any revs in the repo
+ num_of_revs = len(repo)
+ if num_of_revs < 1:
+ continue
+
+ qp = QueryParser('repository', schema=CHGSETS_SCHEMA)
+ q = qp.parse(u"last:t AND %s" % repo_name)
+
+ results = searcher.search(q)
+
+ # default to scanning the entire repo
+ last_rev = 0
+ start_id = None
+
+ if len(results) > 0:
+ # assuming that there is only one result, if not this
+ # may require a full re-index.
+ start_id = results[0]['raw_id']
+ last_rev = repo.get_changeset(revision=start_id).revision
+
+ # there are new changesets to index or a new repo to index
+ if last_rev == 0 or num_of_revs > last_rev + 1:
+ # delete the docs in the index for the previous
+ # last changeset(s)
+ for hit in results:
+ q = qp.parse(u"last:t AND %s AND raw_id:%s" %
+ (repo_name, hit['raw_id']))
+ writer.delete_by_query(q)
+
+ # index from the previous last changeset + all new ones
+ indexed_total += self.index_changesets(writer,
+ repo_name, repo, start_id)
+ writer_is_dirty = True
+ log.debug('indexed %s changesets for repo %s' % (
+ indexed_total, repo_name)
+ )
+ finally:
+ if writer_is_dirty:
+ log.debug('>> COMMITING CHANGES TO CHANGESET INDEX<<')
+ writer.commit(merge=True)
+ log.debug('>> COMMITTED CHANGES TO CHANGESET INDEX<<')
+ else:
+ writer.cancel
+ log.debug('>> NOTHING TO COMMIT<<')
+
+ def update_file_index(self):
+ log.debug((u'STARTING INCREMENTAL INDEXING UPDATE FOR EXTENSIONS %s '
+ 'AND REPOS %s') % (INDEX_EXTENSIONS, self.repo_paths.keys()))
idx = open_dir(self.index_location, indexname=self.indexname)
# The set of all paths in the index
@@ -181,57 +294,120 @@ class WhooshIndexingDaemon(object):
# The set of all paths we need to re-index
to_index = set()
- reader = idx.reader()
writer = idx.writer()
+ writer_is_dirty = False
+ try:
+ with idx.reader() as reader:
+
+ # Loop over the stored fields in the index
+ for fields in reader.all_stored_fields():
+ indexed_path = fields['path']
+ indexed_repo_path = fields['repository']
+ indexed_paths.add(indexed_path)
+
+ if not indexed_repo_path in self.filtered_repo_update_paths:
+ continue
+
+ repo = self.repo_paths[indexed_repo_path]
+
+ try:
+ node = self.get_node(repo, indexed_path)
+ # Check if this file was changed since it was indexed
+ indexed_time = fields['modtime']
+ mtime = self.get_node_mtime(node)
+ if mtime > indexed_time:
+ # The file has changed, delete it and add it to
+ # the list of files to reindex
+ log.debug(
+ 'adding to reindex list %s mtime: %s vs %s' % (
+ indexed_path, mtime, indexed_time)
+ )
+ writer.delete_by_term('fileid', indexed_path)
+ writer_is_dirty = True
+
+ to_index.add(indexed_path)
+ except (ChangesetError, NodeDoesNotExistError):
+ # This file was deleted since it was indexed
+ log.debug('removing from index %s' % indexed_path)
+ writer.delete_by_term('path', indexed_path)
+ writer_is_dirty = True
+
+ # Loop over the files in the filesystem
+ # Assume we have a function that gathers the filenames of the
+ # documents to be indexed
+ ri_cnt_total = 0 # indexed
+ riwc_cnt_total = 0 # indexed with content
+ for repo_name, repo in self.repo_paths.items():
+ # skip indexing if there aren't any revisions
+ if len(repo) < 1:
+ continue
+ ri_cnt = 0 # indexed
+ riwc_cnt = 0 # indexed with content
+ for path in self.get_paths(repo):
+ path = safe_unicode(path)
+ if path in to_index or path not in indexed_paths:
+
+ # This is either a file that's changed, or a new file
+ # that wasn't indexed before. So index it!
+ i, iwc = self.add_doc(writer, path, repo, repo_name)
+ writer_is_dirty = True
+ log.debug('re indexing %s' % path)
+ ri_cnt += i
+ ri_cnt_total += 1
+ riwc_cnt += iwc
+ riwc_cnt_total += iwc
+ log.debug('added %s files %s with content for repo %s' % (
+ ri_cnt + riwc_cnt, riwc_cnt, repo.path)
+ )
+ log.debug('indexed %s files in total and %s with content' % (
+ ri_cnt_total, riwc_cnt_total)
+ )
+ finally:
+ if writer_is_dirty:
+ log.debug('>> COMMITING CHANGES <<')
+ writer.commit(merge=True)
+ log.debug('>>> FINISHED REBUILDING INDEX <<<')
+ else:
+ log.debug('>> NOTHING TO COMMIT<<')
+ writer.cancel()
+
+ def build_indexes(self):
+ if os.path.exists(self.index_location):
+ log.debug('removing previous index')
+ rmtree(self.index_location)
- # Loop over the stored fields in the index
- for fields in reader.all_stored_fields():
- indexed_path = fields['path']
- indexed_paths.add(indexed_path)
+ if not os.path.exists(self.index_location):
+ os.mkdir(self.index_location)
- repo = self.repo_paths[fields['repository']]
+ chgset_idx = create_in(self.index_location, CHGSETS_SCHEMA,
+ indexname=CHGSET_IDX_NAME)
+ chgset_idx_writer = chgset_idx.writer()
- try:
- node = self.get_node(repo, indexed_path)
- except (ChangesetError, NodeDoesNotExistError):
- # This file was deleted since it was indexed
- log.debug('removing from index %s' % indexed_path)
- writer.delete_by_term('path', indexed_path)
+ file_idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
+ file_idx_writer = file_idx.writer()
+ log.debug('BUILDING INDEX FOR EXTENSIONS %s '
+ 'AND REPOS %s' % (INDEX_EXTENSIONS, self.repo_paths.keys()))
- else:
- # Check if this file was changed since it was indexed
- indexed_time = fields['modtime']
- mtime = self.get_node_mtime(node)
- if mtime > indexed_time:
- # The file has changed, delete it and add it to the list of
- # files to reindex
- log.debug('adding to reindex list %s' % indexed_path)
- writer.delete_by_term('path', indexed_path)
- to_index.add(indexed_path)
-
- # Loop over the files in the filesystem
- # Assume we have a function that gathers the filenames of the
- # documents to be indexed
- ri_cnt = riwc_cnt = 0
for repo_name, repo in self.repo_paths.items():
- for path in self.get_paths(repo):
- if path in to_index or path not in indexed_paths:
- # This is either a file that's changed, or a new file
- # that wasn't indexed before. So index it!
- i, iwc = self.add_doc(writer, path, repo, repo_name)
- log.debug('re indexing %s' % path)
- ri_cnt += i
- riwc_cnt += iwc
- log.debug('added %s files %s with content for repo %s' % (
- ri_cnt + riwc_cnt, riwc_cnt, repo.path)
- )
+ # skip indexing if there aren't any revisions
+ if len(repo) < 1:
+ continue
+
+ self.index_files(file_idx_writer, repo_name, repo)
+ self.index_changesets(chgset_idx_writer, repo_name, repo)
+
log.debug('>> COMMITING CHANGES <<')
- writer.commit(merge=True)
- log.debug('>>> FINISHED REBUILDING INDEX <<<')
+ file_idx_writer.commit(merge=True)
+ chgset_idx_writer.commit(merge=True)
+ log.debug('>>> FINISHED BUILDING INDEX <<<')
+
+ def update_indexes(self):
+ self.update_file_index()
+ self.update_changeset_index()
def run(self, full_index=False):
"""Run daemon"""
if full_index or self.initial:
- self.build_index()
+ self.build_indexes()
else:
- self.update_index()
+ self.update_indexes()
diff --git a/rhodecode/lib/markup_renderer.py b/rhodecode/lib/markup_renderer.py
index e015ca54..1a4371ba 100644
--- a/rhodecode/lib/markup_renderer.py
+++ b/rhodecode/lib/markup_renderer.py
@@ -26,6 +26,7 @@
import re
import logging
+import traceback
from rhodecode.lib.utils2 import safe_unicode, MENTIONS_REGEX
@@ -93,7 +94,7 @@ class MarkupRenderer(object):
return '<br />' + source.replace("\n", '<br />')
@classmethod
- def markdown(cls, source):
+ def markdown(cls, source, safe=True):
source = safe_unicode(source)
try:
import markdown as __markdown
@@ -101,9 +102,15 @@ class MarkupRenderer(object):
except ImportError:
log.warning('Install markdown to use this function')
return cls.plain(source)
+ except Exception:
+ log.error(traceback.format_exc())
+ if safe:
+ return source
+ else:
+ raise
@classmethod
- def rst(cls, source):
+ def rst(cls, source, safe=True):
source = safe_unicode(source)
try:
from docutils.core import publish_parts
@@ -125,6 +132,12 @@ class MarkupRenderer(object):
except ImportError:
log.warning('Install docutils to use this function')
return cls.plain(source)
+ except Exception:
+ log.error(traceback.format_exc())
+ if safe:
+ return source
+ else:
+ raise
@classmethod
def rst_with_mentions(cls, source):
diff --git a/rhodecode/lib/middleware/https_fixup.py b/rhodecode/lib/middleware/https_fixup.py
index 5e1bab03..968a4b81 100644
--- a/rhodecode/lib/middleware/https_fixup.py
+++ b/rhodecode/lib/middleware/https_fixup.py
@@ -42,21 +42,20 @@ class HttpsFixup(object):
middleware you should set this header inside your
proxy ie. nginx, apache etc.
"""
+ # DETECT PROTOCOL !
+ if 'HTTP_X_URL_SCHEME' in environ:
+ proto = environ.get('HTTP_X_URL_SCHEME')
+ elif 'HTTP_X_FORWARDED_SCHEME' in environ:
+ proto = environ.get('HTTP_X_FORWARDED_SCHEME')
+ elif 'HTTP_X_FORWARDED_PROTO' in environ:
+ proto = environ.get('HTTP_X_FORWARDED_PROTO')
+ else:
+ proto = 'http'
+ org_proto = proto
+ # if we have force, just override
if str2bool(self.config.get('force_https')):
proto = 'https'
- else:
- if 'HTTP_X_URL_SCHEME' in environ:
- proto = environ.get('HTTP_X_URL_SCHEME')
- elif 'HTTP_X_FORWARDED_SCHEME' in environ:
- proto = environ.get('HTTP_X_FORWARDED_SCHEME')
- elif 'HTTP_X_FORWARDED_PROTO' in environ:
- proto = environ.get('HTTP_X_FORWARDED_PROTO')
- else:
- proto = 'http'
- if proto == 'https':
- environ['wsgi.url_scheme'] = proto
- else:
- environ['wsgi.url_scheme'] = 'http'
- return None
+ environ['wsgi.url_scheme'] = proto
+ environ['wsgi._org_proto'] = org_proto
diff --git a/rhodecode/lib/middleware/pygrack.py b/rhodecode/lib/middleware/pygrack.py
new file mode 100644
index 00000000..73402654
--- /dev/null
+++ b/rhodecode/lib/middleware/pygrack.py
@@ -0,0 +1,200 @@
+import os
+import socket
+import logging
+import subprocess
+
+from webob import Request, Response, exc
+
+from rhodecode.lib import subprocessio
+
+log = logging.getLogger(__name__)
+
+
+class FileWrapper(object):
+
+ def __init__(self, fd, content_length):
+ self.fd = fd
+ self.content_length = content_length
+ self.remain = content_length
+
+ def read(self, size):
+ if size <= self.remain:
+ try:
+ data = self.fd.read(size)
+ except socket.error:
+ raise IOError(self)
+ self.remain -= size
+ elif self.remain:
+ data = self.fd.read(self.remain)
+ self.remain = 0
+ else:
+ data = None
+ return data
+
+ def __repr__(self):
+ return '<FileWrapper %s len: %s, read: %s>' % (
+ self.fd, self.content_length, self.content_length - self.remain
+ )
+
+
+class GitRepository(object):
+ git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
+ commands = ['git-upload-pack', 'git-receive-pack']
+
+ def __init__(self, repo_name, content_path, extras):
+ files = set([f.lower() for f in os.listdir(content_path)])
+ if not (self.git_folder_signature.intersection(files)
+ == self.git_folder_signature):
+ raise OSError('%s missing git signature' % content_path)
+ self.content_path = content_path
+ self.valid_accepts = ['application/x-%s-result' %
+ c for c in self.commands]
+ self.repo_name = repo_name
+ self.extras = extras
+
+ def _get_fixedpath(self, path):
+ """
+ Small fix for repo_path
+
+ :param path:
+ :type path:
+ """
+ return path.split(self.repo_name, 1)[-1].strip('/')
+
+ def inforefs(self, request, environ):
+ """
+ WSGI Response producer for HTTP GET Git Smart
+ HTTP /info/refs request.
+ """
+
+ git_command = request.GET.get('service')
+ if git_command not in self.commands:
+ log.debug('command %s not allowed' % git_command)
+ return exc.HTTPMethodNotAllowed()
+
+ # note to self:
+ # please, resist the urge to add '\n' to git capture and increment
+ # line count by 1.
+ # The code in Git client not only does NOT need '\n', but actually
+ # blows up if you sprinkle "flush" (0000) as "0001\n".
+ # It reads binary, per number of bytes specified.
+ # if you do add '\n' as part of data, count it.
+ server_advert = '# service=%s' % git_command
+ packet_len = str(hex(len(server_advert) + 4)[2:].rjust(4, '0')).lower()
+ try:
+ out = subprocessio.SubprocessIOChunker(
+ r'git %s --stateless-rpc --advertise-refs "%s"' % (
+ git_command[4:], self.content_path),
+ starting_values=[
+ packet_len + server_advert + '0000'
+ ]
+ )
+ except EnvironmentError, e:
+ log.exception(e)
+ raise exc.HTTPExpectationFailed()
+ resp = Response()
+ resp.content_type = 'application/x-%s-advertisement' % str(git_command)
+ resp.charset = None
+ resp.app_iter = out
+ return resp
+
+ def backend(self, request, environ):
+ """
+ WSGI Response producer for HTTP POST Git Smart HTTP requests.
+ Reads commands and data from HTTP POST's body.
+ returns an iterator obj with contents of git command's
+ response to stdout
+ """
+ git_command = self._get_fixedpath(request.path_info)
+ if git_command not in self.commands:
+ log.debug('command %s not allowed' % git_command)
+ return exc.HTTPMethodNotAllowed()
+
+ if 'CONTENT_LENGTH' in environ:
+ inputstream = FileWrapper(environ['wsgi.input'],
+ request.content_length)
+ else:
+ inputstream = environ['wsgi.input']
+
+ try:
+ gitenv = os.environ
+ from rhodecode import CONFIG
+ from rhodecode.lib.compat import json
+ gitenv['RHODECODE_EXTRAS'] = json.dumps(self.extras)
+ # forget all configs
+ gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
+ # we need current .ini file used to later initialize rhodecode
+ # env and connect to db
+ gitenv['RHODECODE_CONFIG_FILE'] = CONFIG['__file__']
+ opts = dict(
+ env=gitenv,
+ cwd=os.getcwd()
+ )
+ out = subprocessio.SubprocessIOChunker(
+ r'git %s --stateless-rpc "%s"' % (git_command[4:],
+ self.content_path),
+ inputstream=inputstream,
+ **opts
+ )
+ except EnvironmentError, e:
+ log.exception(e)
+ raise exc.HTTPExpectationFailed()
+
+ if git_command in [u'git-receive-pack']:
+ # updating refs manually after each push.
+ # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
+ subprocess.call(u'git --git-dir "%s" '
+ 'update-server-info' % self.content_path,
+ shell=True)
+
+ resp = Response()
+ resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
+ resp.charset = None
+ resp.app_iter = out
+ return resp
+
+ def __call__(self, environ, start_response):
+ request = Request(environ)
+ _path = self._get_fixedpath(request.path_info)
+ if _path.startswith('info/refs'):
+ app = self.inforefs
+ elif [a for a in self.valid_accepts if a in request.accept]:
+ app = self.backend
+ try:
+ resp = app(request, environ)
+ except exc.HTTPException, e:
+ resp = e
+ log.exception(e)
+ except Exception, e:
+ log.exception(e)
+ resp = exc.HTTPInternalServerError()
+ return resp(environ, start_response)
+
+
+class GitDirectory(object):
+
+ def __init__(self, repo_root, repo_name, extras):
+ repo_location = os.path.join(repo_root, repo_name)
+ if not os.path.isdir(repo_location):
+ raise OSError(repo_location)
+
+ self.content_path = repo_location
+ self.repo_name = repo_name
+ self.repo_location = repo_location
+ self.extras = extras
+
+ def __call__(self, environ, start_response):
+ content_path = self.content_path
+ try:
+ app = GitRepository(self.repo_name, content_path, self.extras)
+ except (AssertionError, OSError):
+ content_path = os.path.join(content_path, '.git')
+ if os.path.isdir(content_path):
+ app = GitRepository(self.repo_name, content_path, self.extras)
+ else:
+ return exc.HTTPNotFound()(environ, start_response)
+ return app(environ, start_response)
+
+
+def make_wsgi_app(repo_name, repo_root, extras):
+ return GitDirectory(repo_root, repo_name, extras)
diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py
index 65b75fdb..5ac4378f 100644
--- a/rhodecode/lib/middleware/simplegit.py
+++ b/rhodecode/lib/middleware/simplegit.py
@@ -30,6 +30,9 @@ import logging
import traceback
from dulwich import server as dulserver
+from dulwich.web import LimitedInputFilter, GunzipFilter
+from rhodecode.lib.exceptions import HTTPLockedRC
+from rhodecode.lib.hooks import pre_pull
class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
@@ -62,22 +65,26 @@ class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
dulserver.DEFAULT_HANDLERS = {
+ #git-ls-remote, git-clone, git-fetch and git-pull
'git-upload-pack': SimpleGitUploadPackHandler,
+ #git-push
'git-receive-pack': dulserver.ReceivePackHandler,
}
-from dulwich.repo import Repo
-from dulwich.web import make_wsgi_chain
+# not used for now until dulwich get's fixed
+#from dulwich.repo import Repo
+#from dulwich.web import make_wsgi_chain
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
+from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
+ HTTPBadRequest, HTTPNotAcceptable
from rhodecode.lib.utils2 import safe_str
from rhodecode.lib.base import BaseVCSController
from rhodecode.lib.auth import get_container_username
from rhodecode.lib.utils import is_valid_repo, make_ui
-from rhodecode.model.db import User
-
-from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
+from rhodecode.lib.compat import json
+from rhodecode.model.db import User, RhodeCodeUi
log = logging.getLogger(__name__)
@@ -97,9 +104,10 @@ def is_git(environ):
class SimpleGit(BaseVCSController):
def _handle_request(self, environ, start_response):
-
if not is_git(environ):
return self.application(environ, start_response)
+ if not self._check_ssl(environ, start_response):
+ return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
ipaddr = self._get_ip_addr(environ)
username = None
@@ -117,7 +125,7 @@ class SimpleGit(BaseVCSController):
return HTTPInternalServerError()(environ, start_response)
# quick check if that dir exists...
- if is_valid_repo(repo_name, self.basepath) is False:
+ if is_valid_repo(repo_name, self.basepath, 'git') is False:
return HTTPNotFound()(environ, start_response)
#======================================================================
@@ -164,27 +172,30 @@ class SimpleGit(BaseVCSController):
#==============================================================
# CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
#==============================================================
- if action in ['pull', 'push']:
- try:
- user = self.__get_user(username)
- if user is None or not user.active:
- return HTTPForbidden()(environ, start_response)
- username = user.username
- except:
- log.error(traceback.format_exc())
- return HTTPInternalServerError()(environ,
- start_response)
-
- #check permissions for this repository
- perm = self._check_permission(action, user, repo_name)
- if perm is not True:
+ try:
+ user = self.__get_user(username)
+ if user is None or not user.active:
return HTTPForbidden()(environ, start_response)
+ username = user.username
+ except:
+ log.error(traceback.format_exc())
+ return HTTPInternalServerError()(environ, start_response)
+
+ #check permissions for this repository
+ perm = self._check_permission(action, user, repo_name)
+ if perm is not True:
+ return HTTPForbidden()(environ, start_response)
+
+ # extras are injected into UI object and later available
+ # in hooks executed by rhodecode
extras = {
'ip': ipaddr,
'username': username,
'action': action,
'repository': repo_name,
'scm': 'git',
+ 'make_lock': None,
+ 'locked_by': [None, None]
}
#===================================================================
@@ -193,10 +204,24 @@ class SimpleGit(BaseVCSController):
repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
log.debug('Repository path is %s' % repo_path)
+ # CHECK LOCKING only if it's not ANONYMOUS USER
+ if username != User.DEFAULT_USER:
+ log.debug('Checking locking on repository')
+ (make_lock,
+ locked,
+ locked_by) = self._check_locking_state(
+ environ=environ, action=action,
+ repo=repo_name, user_id=user.user_id
+ )
+ # store the make_lock for later evaluation in hooks
+ extras.update({'make_lock': make_lock,
+ 'locked_by': locked_by})
+ # set the environ variables for this request
+ os.environ['RC_SCM_DATA'] = json.dumps(extras)
+ log.debug('HOOKS extras is %s' % extras)
baseui = make_ui('db')
self.__inject_extras(repo_path, baseui, extras)
-
try:
# invalidate cache on push
if action == 'push':
@@ -204,24 +229,31 @@ class SimpleGit(BaseVCSController):
self._handle_githooks(repo_name, action, baseui, environ)
log.info('%s action on GIT repo "%s"' % (action, repo_name))
- app = self.__make_app(repo_name, repo_path)
+ app = self.__make_app(repo_name, repo_path, extras)
return app(environ, start_response)
+ except HTTPLockedRC, e:
+ log.debug('Repositry LOCKED ret code 423!')
+ return e(environ, start_response)
except Exception:
log.error(traceback.format_exc())
return HTTPInternalServerError()(environ, start_response)
- def __make_app(self, repo_name, repo_path):
+ def __make_app(self, repo_name, repo_path, extras):
"""
Make an wsgi application using dulserver
:param repo_name: name of the repository
:param repo_path: full path to the repository
"""
- _d = {'/' + repo_name: Repo(repo_path)}
- backend = dulserver.DictBackend(_d)
- gitserve = make_wsgi_chain(backend)
- return gitserve
+ from rhodecode.lib.middleware.pygrack import make_wsgi_app
+ app = make_wsgi_app(
+ repo_root=safe_str(self.basepath),
+ repo_name=repo_name,
+ extras=extras,
+ )
+ app = GunzipFilter(LimitedInputFilter(app))
+ return app
def __get_repository(self, environ):
"""
@@ -265,8 +297,12 @@ class SimpleGit(BaseVCSController):
return op
def _handle_githooks(self, repo_name, action, baseui, environ):
- from rhodecode.lib.hooks import log_pull_action, log_push_action
+ """
+ Handles pull action, push is handled by post-receive hook
+ """
+ from rhodecode.lib.hooks import log_pull_action
service = environ['QUERY_STRING'].split('=')
+
if len(service) < 2:
return
@@ -275,12 +311,11 @@ class SimpleGit(BaseVCSController):
_repo = _repo.scm_instance
_repo._repo.ui = baseui
- push_hook = 'pretxnchangegroup.push_logger'
- pull_hook = 'preoutgoing.pull_logger'
_hooks = dict(baseui.configitems('hooks')) or {}
- if action == 'push' and _hooks.get(push_hook):
- log_push_action(ui=baseui, repo=_repo._repo)
- elif action == 'pull' and _hooks.get(pull_hook):
+ if action == 'pull':
+ # stupid git, emulate pre-pull hook !
+ pre_pull(ui=baseui, repo=_repo._repo)
+ if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
log_pull_action(ui=baseui, repo=_repo._repo)
def __inject_extras(self, repo_path, baseui, extras={}):
diff --git a/rhodecode/lib/middleware/simplehg.py b/rhodecode/lib/middleware/simplehg.py
index 86afe05b..1a6f32ff 100644
--- a/rhodecode/lib/middleware/simplehg.py
+++ b/rhodecode/lib/middleware/simplehg.py
@@ -33,14 +33,17 @@ from mercurial.error import RepoError
from mercurial.hgweb import hgweb_mod
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
+from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
+ HTTPBadRequest, HTTPNotAcceptable
from rhodecode.lib.utils2 import safe_str
from rhodecode.lib.base import BaseVCSController
from rhodecode.lib.auth import get_container_username
from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
+from rhodecode.lib.compat import json
from rhodecode.model.db import User
+from rhodecode.lib.exceptions import HTTPLockedRC
-from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
log = logging.getLogger(__name__)
@@ -68,9 +71,11 @@ class SimpleHg(BaseVCSController):
def _handle_request(self, environ, start_response):
if not is_mercurial(environ):
return self.application(environ, start_response)
+ if not self._check_ssl(environ, start_response):
+ return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
ipaddr = self._get_ip_addr(environ)
-
+ username = None
# skip passing error to error controller
environ['pylons.status_code_redirect'] = True
@@ -84,7 +89,7 @@ class SimpleHg(BaseVCSController):
return HTTPInternalServerError()(environ, start_response)
# quick check if that dir exists...
- if is_valid_repo(repo_name, self.basepath) is False:
+ if is_valid_repo(repo_name, self.basepath, 'hg') is False:
return HTTPNotFound()(environ, start_response)
#======================================================================
@@ -131,21 +136,19 @@ class SimpleHg(BaseVCSController):
#==============================================================
# CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
#==============================================================
- if action in ['pull', 'push']:
- try:
- user = self.__get_user(username)
- if user is None or not user.active:
- return HTTPForbidden()(environ, start_response)
- username = user.username
- except:
- log.error(traceback.format_exc())
- return HTTPInternalServerError()(environ,
- start_response)
-
- #check permissions for this repository
- perm = self._check_permission(action, user, repo_name)
- if perm is not True:
+ try:
+ user = self.__get_user(username)
+ if user is None or not user.active:
return HTTPForbidden()(environ, start_response)
+ username = user.username
+ except:
+ log.error(traceback.format_exc())
+ return HTTPInternalServerError()(environ, start_response)
+
+ #check permissions for this repository
+ perm = self._check_permission(action, user, repo_name)
+ if perm is not True:
+ return HTTPForbidden()(environ, start_response)
# extras are injected into mercurial UI object and later available
# in hg hooks executed by rhodecode
@@ -155,14 +158,31 @@ class SimpleHg(BaseVCSController):
'action': action,
'repository': repo_name,
'scm': 'hg',
+ 'make_lock': None,
+ 'locked_by': [None, None]
}
-
#======================================================================
# MERCURIAL REQUEST HANDLING
#======================================================================
repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
log.debug('Repository path is %s' % repo_path)
+ # CHECK LOCKING only if it's not ANONYMOUS USER
+ if username != User.DEFAULT_USER:
+ log.debug('Checking locking on repository')
+ (make_lock,
+ locked,
+ locked_by) = self._check_locking_state(
+ environ=environ, action=action,
+ repo=repo_name, user_id=user.user_id
+ )
+ # store the make_lock for later evaluation in hooks
+ extras.update({'make_lock': make_lock,
+ 'locked_by': locked_by})
+
+ # set the environ variables for this request
+ os.environ['RC_SCM_DATA'] = json.dumps(extras)
+ log.debug('HOOKS extras is %s' % extras)
baseui = make_ui('db')
self.__inject_extras(repo_path, baseui, extras)
@@ -176,6 +196,9 @@ class SimpleHg(BaseVCSController):
except RepoError, e:
if str(e).find('not found') != -1:
return HTTPNotFound()(environ, start_response)
+ except HTTPLockedRC, e:
+ log.debug('Repositry LOCKED ret code 423!')
+ return e(environ, start_response)
except Exception:
log.error(traceback.format_exc())
return HTTPInternalServerError()(environ, start_response)
@@ -225,8 +248,11 @@ class SimpleHg(BaseVCSController):
cmd = qry.split('=')[-1]
if cmd in mapping:
return mapping[cmd]
- else:
- return 'pull'
+
+ return 'pull'
+
+ raise Exception('Unable to detect pull/push action !!'
+ 'Are you using non standard command or client ?')
def __inject_extras(self, repo_path, baseui, extras={}):
"""
diff --git a/rhodecode/lib/pidlock.py b/rhodecode/lib/pidlock.py
index d540a62d..c2b65965 100644
--- a/rhodecode/lib/pidlock.py
+++ b/rhodecode/lib/pidlock.py
@@ -8,6 +8,7 @@ from multiprocessing.util import Finalize
from rhodecode.lib.compat import kill
+
class LockHeld(Exception):
pass
@@ -123,6 +124,10 @@ class DaemonLock(object):
"""
if self.debug:
print 'creating a file %s and pid: %s' % (pidfile, lockname)
+
+ dir_, file_ = os.path.split(pidfile)
+ if not os.path.isdir(dir_):
+ os.makedirs(dir_)
pidfile = open(self.pidfile, "wb")
pidfile.write(lockname)
pidfile.close
diff --git a/rhodecode/lib/profiler.py b/rhodecode/lib/profiler.py
index e3e0b075..f2e54f06 100644
--- a/rhodecode/lib/profiler.py
+++ b/rhodecode/lib/profiler.py
@@ -1,5 +1,7 @@
from __future__ import with_statement
+import gc
+import objgraph
import cProfile
import pstats
import cgi
@@ -26,7 +28,7 @@ class ProfilingMiddleware(object):
profiler.snapshot_stats()
stats = pstats.Stats(profiler)
- stats.sort_stats('cumulative')
+ stats.sort_stats('calls') #cummulative
# Redirect output
out = StringIO()
@@ -44,6 +46,11 @@ class ProfilingMiddleware(object):
'border-top: 4px dashed red; padding: 1em;">')
resp += cgi.escape(out.getvalue(), True)
+ ct = objgraph.show_most_common_types()
+ print ct
+
+ resp += ct if ct else '---'
+
output = StringIO()
pprint.pprint(environ, output, depth=3)
diff --git a/rhodecode/lib/rcmail/message.py b/rhodecode/lib/rcmail/message.py
index 4dbfbe56..0684f7cb 100644
--- a/rhodecode/lib/rcmail/message.py
+++ b/rhodecode/lib/rcmail/message.py
@@ -3,6 +3,7 @@ from rhodecode.lib.rcmail.response import MailResponse
from rhodecode.lib.rcmail.exceptions import BadHeaders
from rhodecode.lib.rcmail.exceptions import InvalidMessage
+
class Attachment(object):
"""
Encapsulates file attachment information.
@@ -134,13 +135,13 @@ class Message(object):
"""
if not self.recipients:
- raise InvalidMessage, "No recipients have been added"
+ raise InvalidMessage("No recipients have been added")
if not self.body and not self.html:
- raise InvalidMessage, "No body has been set"
+ raise InvalidMessage("No body has been set")
if not self.sender:
- raise InvalidMessage, "No sender address has been set"
+ raise InvalidMessage("No sender address has been set")
if self.is_bad_headers():
raise BadHeaders
diff --git a/rhodecode/lib/rcmail/response.py b/rhodecode/lib/rcmail/response.py
index a7cfa51f..f7baf7f8 100644
--- a/rhodecode/lib/rcmail/response.py
+++ b/rhodecode/lib/rcmail/response.py
@@ -364,6 +364,7 @@ def to_message(mail, separator="; "):
return out
+
class MIMEPart(MIMEBase):
"""
A reimplementation of nearly everything in email.mime to be more useful
@@ -387,7 +388,8 @@ class MIMEPart(MIMEBase):
self.set_payload(encoded, charset=charset)
def extract_payload(self, mail):
- if mail.body == None: return # only None, '' is still ok
+ if mail.body == None:
+ return # only None, '' is still ok
ctype, ctype_params = mail.content_encoding['Content-Type']
cdisp, cdisp_params = mail.content_encoding['Content-Disposition']
@@ -415,7 +417,8 @@ class MIMEPart(MIMEBase):
def header_to_mime_encoding(value, not_email=False, separator=", "):
- if not value: return ""
+ if not value:
+ return ""
encoder = Charset(DEFAULT_ENCODING)
if type(value) == list:
@@ -424,6 +427,7 @@ def header_to_mime_encoding(value, not_email=False, separator=", "):
else:
return properly_encode_header(value, encoder, not_email)
+
def properly_encode_header(value, encoder, not_email):
"""
The only thing special (weird) about this function is that it tries
diff --git a/rhodecode/lib/rcmail/smtp_mailer.py b/rhodecode/lib/rcmail/smtp_mailer.py
index 55b00492..82c87328 100644
--- a/rhodecode/lib/rcmail/smtp_mailer.py
+++ b/rhodecode/lib/rcmail/smtp_mailer.py
@@ -21,11 +21,13 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+import time
import logging
import smtplib
from socket import sslerror
+from email.utils import formatdate
from rhodecode.lib.rcmail.message import Message
+from rhodecode.lib.rcmail.utils import DNS_NAME
class SmtpMailer(object):
@@ -59,14 +61,19 @@ class SmtpMailer(object):
if isinstance(recipients, basestring):
recipients = [recipients]
+ headers = {
+ 'Date': formatdate(time.time())
+ }
msg = Message(subject, recipients, body, html, self.mail_from,
- recipients_separator=", ")
+ recipients_separator=", ", extra_headers=headers)
raw_msg = msg.to_message()
if self.ssl:
- smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
+ smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port,
+ local_hostname=DNS_NAME.get_fqdn())
else:
- smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
+ smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port,
+ local_hostname=DNS_NAME.get_fqdn())
if self.tls:
smtp_serv.ehlo()
@@ -91,4 +98,4 @@ class SmtpMailer(object):
smtp_serv.quit()
except sslerror:
# sslerror is raised in tls connections on closing sometimes
- pass
+ smtp_serv.close()
diff --git a/rhodecode/lib/rcmail/utils.py b/rhodecode/lib/rcmail/utils.py
new file mode 100644
index 00000000..322a3a1b
--- /dev/null
+++ b/rhodecode/lib/rcmail/utils.py
@@ -0,0 +1,19 @@
+"""
+Email message and email sending related helper functions.
+"""
+
+import socket
+
+
+# Cache the hostname, but do it lazily: socket.getfqdn() can take a couple of
+# seconds, which slows down the restart of the server.
+class CachedDnsName(object):
+ def __str__(self):
+ return self.get_fqdn()
+
+ def get_fqdn(self):
+ if not hasattr(self, '_fqdn'):
+ self._fqdn = socket.getfqdn()
+ return self._fqdn
+
+DNS_NAME = CachedDnsName()
diff --git a/rhodecode/lib/subprocessio.py b/rhodecode/lib/subprocessio.py
new file mode 100644
index 00000000..0a3c4b0a
--- /dev/null
+++ b/rhodecode/lib/subprocessio.py
@@ -0,0 +1,409 @@
+'''
+Module provides a class allowing to wrap communication over subprocess.Popen
+input, output, error streams into a meaningfull, non-blocking, concurrent
+stream processor exposing the output data as an iterator fitting to be a
+return value passed by a WSGI applicaiton to a WSGI server per PEP 3333.
+
+Copyright (c) 2011 Daniel Dotsenko <dotsa@hotmail.com>
+
+This file is part of git_http_backend.py Project.
+
+git_http_backend.py Project is free software: you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation, either version 2.1 of the License,
+or (at your option) any later version.
+
+git_http_backend.py Project 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with git_http_backend.py Project.
+If not, see <http://www.gnu.org/licenses/>.
+'''
+import os
+import subprocess
+import threading
+from rhodecode.lib.compat import deque, Event
+
+
+class StreamFeeder(threading.Thread):
+ """
+ Normal writing into pipe-like is blocking once the buffer is filled.
+ This thread allows a thread to seep data from a file-like into a pipe
+ without blocking the main thread.
+ We close inpipe once the end of the source stream is reached.
+ """
+ def __init__(self, source):
+ super(StreamFeeder, self).__init__()
+ self.daemon = True
+ filelike = False
+ self.bytes = bytes()
+ if type(source) in (type(''), bytes, bytearray): # string-like
+ self.bytes = bytes(source)
+ else: # can be either file pointer or file-like
+ if type(source) in (int, long): # file pointer it is
+ ## converting file descriptor (int) stdin into file-like
+ try:
+ source = os.fdopen(source, 'rb', 16384)
+ except Exception:
+ pass
+ # let's see if source is file-like by now
+ try:
+ filelike = source.read
+ except Exception:
+ pass
+ if not filelike and not self.bytes:
+ raise TypeError("StreamFeeder's source object must be a readable "
+ "file-like, a file descriptor, or a string-like.")
+ self.source = source
+ self.readiface, self.writeiface = os.pipe()
+
+ def run(self):
+ t = self.writeiface
+ if self.bytes:
+ os.write(t, self.bytes)
+ else:
+ s = self.source
+ b = s.read(4096)
+ while b:
+ os.write(t, b)
+ b = s.read(4096)
+ os.close(t)
+
+ @property
+ def output(self):
+ return self.readiface
+
+
+class InputStreamChunker(threading.Thread):
+ def __init__(self, source, target, buffer_size, chunk_size):
+
+ super(InputStreamChunker, self).__init__()
+
+ self.daemon = True # die die die.
+
+ self.source = source
+ self.target = target
+ self.chunk_count_max = int(buffer_size / chunk_size) + 1
+ self.chunk_size = chunk_size
+
+ self.data_added = Event()
+ self.data_added.clear()
+
+ self.keep_reading = Event()
+ self.keep_reading.set()
+
+ self.EOF = Event()
+ self.EOF.clear()
+
+ self.go = Event()
+ self.go.set()
+
+ def stop(self):
+ self.go.clear()
+ self.EOF.set()
+ try:
+ # this is not proper, but is done to force the reader thread let
+ # go of the input because, if successful, .close() will send EOF
+ # down the pipe.
+ self.source.close()
+ except:
+ pass
+
+ def run(self):
+ s = self.source
+ t = self.target
+ cs = self.chunk_size
+ ccm = self.chunk_count_max
+ kr = self.keep_reading
+ da = self.data_added
+ go = self.go
+ b = s.read(cs)
+ while b and go.is_set():
+ if len(t) > ccm:
+ kr.clear()
+ kr.wait(2)
+# # this only works on 2.7.x and up
+# if not kr.wait(10):
+# raise Exception("Timed out while waiting for input to be read.")
+ # instead we'll use this
+ if len(t) > ccm + 3:
+ raise IOError("Timed out while waiting for input from subprocess.")
+ t.append(b)
+ da.set()
+ b = s.read(cs)
+ self.EOF.set()
+ da.set() # for cases when done but there was no input.
+
+
+class BufferedGenerator():
+ '''
+ Class behaves as a non-blocking, buffered pipe reader.
+ Reads chunks of data (through a thread)
+ from a blocking pipe, and attaches these to an array (Deque) of chunks.
+ Reading is halted in the thread when max chunks is internally buffered.
+ The .next() may operate in blocking or non-blocking fashion by yielding
+ '' if no data is ready
+ to be sent or by not returning until there is some data to send
+ When we get EOF from underlying source pipe we raise the marker to raise
+ StopIteration after the last chunk of data is yielded.
+ '''
+
+ def __init__(self, source, buffer_size=65536, chunk_size=4096,
+ starting_values=[], bottomless=False):
+
+ if bottomless:
+ maxlen = int(buffer_size / chunk_size)
+ else:
+ maxlen = None
+
+ self.data = deque(starting_values, maxlen)
+
+ self.worker = InputStreamChunker(source, self.data, buffer_size,
+ chunk_size)
+ if starting_values:
+ self.worker.data_added.set()
+ self.worker.start()
+
+ ####################
+ # Generator's methods
+ ####################
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ while not len(self.data) and not self.worker.EOF.is_set():
+ self.worker.data_added.clear()
+ self.worker.data_added.wait(0.2)
+ if len(self.data):
+ self.worker.keep_reading.set()
+ return bytes(self.data.popleft())
+ elif self.worker.EOF.is_set():
+ raise StopIteration
+
+ def throw(self, type, value=None, traceback=None):
+ if not self.worker.EOF.is_set():
+ raise type(value)
+
+ def start(self):
+ self.worker.start()
+
+ def stop(self):
+ self.worker.stop()
+
+ def close(self):
+ try:
+ self.worker.stop()
+ self.throw(GeneratorExit)
+ except (GeneratorExit, StopIteration):
+ pass
+
+ def __del__(self):
+ self.close()
+
+ ####################
+ # Threaded reader's infrastructure.
+ ####################
+ @property
+ def input(self):
+ return self.worker.w
+
+ @property
+ def data_added_event(self):
+ return self.worker.data_added
+
+ @property
+ def data_added(self):
+ return self.worker.data_added.is_set()
+
+ @property
+ def reading_paused(self):
+ return not self.worker.keep_reading.is_set()
+
+ @property
+ def done_reading_event(self):
+ '''
+ Done_reding does not mean that the iterator's buffer is empty.
+ Iterator might have done reading from underlying source, but the read
+ chunks might still be available for serving through .next() method.
+
+ @return An Event class instance.
+ '''
+ return self.worker.EOF
+
+ @property
+ def done_reading(self):
+ '''
+ Done_reding does not mean that the iterator's buffer is empty.
+ Iterator might have done reading from underlying source, but the read
+ chunks might still be available for serving through .next() method.
+
+ @return An Bool value.
+ '''
+ return self.worker.EOF.is_set()
+
+ @property
+ def length(self):
+ '''
+ returns int.
+
+ This is the lenght of the que of chunks, not the length of
+ the combined contents in those chunks.
+
+ __len__() cannot be meaningfully implemented because this
+ reader is just flying throuh a bottomless pit content and
+ can only know the lenght of what it already saw.
+
+ If __len__() on WSGI server per PEP 3333 returns a value,
+ the responce's length will be set to that. In order not to
+ confuse WSGI PEP3333 servers, we will not implement __len__
+ at all.
+ '''
+ return len(self.data)
+
+ def prepend(self, x):
+ self.data.appendleft(x)
+
+ def append(self, x):
+ self.data.append(x)
+
+ def extend(self, o):
+ self.data.extend(o)
+
+ def __getitem__(self, i):
+ return self.data[i]
+
+
+class SubprocessIOChunker(object):
+ '''
+ Processor class wrapping handling of subprocess IO.
+
+ In a way, this is a "communicate()" replacement with a twist.
+
+ - We are multithreaded. Writing in and reading out, err are all sep threads.
+ - We support concurrent (in and out) stream processing.
+ - The output is not a stream. It's a queue of read string (bytes, not unicode)
+ chunks. The object behaves as an iterable. You can "for chunk in obj:" us.
+ - We are non-blocking in more respects than communicate()
+ (reading from subprocess out pauses when internal buffer is full, but
+ does not block the parent calling code. On the flip side, reading from
+ slow-yielding subprocess may block the iteration until data shows up. This
+ does not block the parallel inpipe reading occurring parallel thread.)
+
+ The purpose of the object is to allow us to wrap subprocess interactions into
+ and interable that can be passed to a WSGI server as the application's return
+ value. Because of stream-processing-ability, WSGI does not have to read ALL
+ of the subprocess's output and buffer it, before handing it to WSGI server for
+ HTTP response. Instead, the class initializer reads just a bit of the stream
+ to figure out if error ocurred or likely to occur and if not, just hands the
+ further iteration over subprocess output to the server for completion of HTTP
+ response.
+
+ The real or perceived subprocess error is trapped and raised as one of
+ EnvironmentError family of exceptions
+
+ Example usage:
+ # try:
+ # answer = SubprocessIOChunker(
+ # cmd,
+ # input,
+ # buffer_size = 65536,
+ # chunk_size = 4096
+ # )
+ # except (EnvironmentError) as e:
+ # print str(e)
+ # raise e
+ #
+ # return answer
+
+
+ '''
+ def __init__(self, cmd, inputstream=None, buffer_size=65536,
+ chunk_size=4096, starting_values=[], **kwargs):
+ '''
+ Initializes SubprocessIOChunker
+
+ :param cmd: A Subprocess.Popen style "cmd". Can be string or array of strings
+ :param inputstream: (Default: None) A file-like, string, or file pointer.
+ :param buffer_size: (Default: 65536) A size of total buffer per stream in bytes.
+ :param chunk_size: (Default: 4096) A max size of a chunk. Actual chunk may be smaller.
+ :param starting_values: (Default: []) An array of strings to put in front of output que.
+ '''
+
+ if inputstream:
+ input_streamer = StreamFeeder(inputstream)
+ input_streamer.start()
+ inputstream = input_streamer.output
+
+ if isinstance(cmd, (list, tuple)):
+ cmd = ' '.join(cmd)
+
+ _shell = kwargs.get('shell') or True
+ kwargs['shell'] = _shell
+ _p = subprocess.Popen(cmd,
+ bufsize=-1,
+ stdin=inputstream,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ **kwargs
+ )
+
+ bg_out = BufferedGenerator(_p.stdout, buffer_size, chunk_size, starting_values)
+ bg_err = BufferedGenerator(_p.stderr, 16000, 1, bottomless=True)
+
+ while not bg_out.done_reading and not bg_out.reading_paused and not bg_err.length:
+ # doing this until we reach either end of file, or end of buffer.
+ bg_out.data_added_event.wait(1)
+ bg_out.data_added_event.clear()
+
+ # at this point it's still ambiguous if we are done reading or just full buffer.
+ # Either way, if error (returned by ended process, or implied based on
+ # presence of stuff in stderr output) we error out.
+ # Else, we are happy.
+ _returncode = _p.poll()
+ if _returncode or (_returncode == None and bg_err.length):
+ try:
+ _p.terminate()
+ except:
+ pass
+ bg_out.stop()
+ bg_err.stop()
+ err = '%s' % ''.join(bg_err)
+ raise EnvironmentError("Subprocess exited due to an error:\n" + err)
+
+ self.process = _p
+ self.output = bg_out
+ self.error = bg_err
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if self.process.poll():
+ err = '%s' % ''.join(self.error)
+ raise EnvironmentError("Subprocess exited due to an error:\n" + err)
+ return self.output.next()
+
+ def throw(self, type, value=None, traceback=None):
+ if self.output.length or not self.output.done_reading:
+ raise type(value)
+
+ def close(self):
+ try:
+ self.process.terminate()
+ except:
+ pass
+ try:
+ self.output.close()
+ except:
+ pass
+ try:
+ self.error.close()
+ except:
+ pass
+
+ def __del__(self):
+ self.close()
diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py
index 064c7042..d6720ea3 100644
--- a/rhodecode/lib/utils.py
+++ b/rhodecode/lib/utils.py
@@ -51,8 +51,7 @@ from rhodecode.lib.caching_query import FromCache
from rhodecode.model import meta
from rhodecode.model.db import Repository, User, RhodeCodeUi, \
- UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm,\
- CacheInvalidation
+ UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
from rhodecode.model.meta import Session
from rhodecode.model.repos_group import ReposGroupModel
from rhodecode.lib.utils2 import safe_str, safe_unicode
@@ -129,7 +128,7 @@ def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
"""
if not sa:
- sa = meta.Session
+ sa = meta.Session()
try:
if hasattr(user, 'user_id'):
@@ -146,13 +145,14 @@ def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
repo_name = repo.lstrip('/')
repo_obj = Repository.get_by_repo_name(repo_name)
else:
- raise Exception('You have to provide repository to action logger')
+ repo_obj = None
+ repo_name = ''
user_log = UserLog()
user_log.user_id = user_obj.user_id
user_log.action = safe_unicode(action)
- user_log.repository_id = repo_obj.repo_id
+ user_log.repository = repo_obj
user_log.repository_name = repo_name
user_log.action_date = datetime.datetime.now()
@@ -203,19 +203,24 @@ def get_repos(path, recursive=False):
return _get_repos(path)
-def is_valid_repo(repo_name, base_path):
+def is_valid_repo(repo_name, base_path, scm=None):
"""
- Returns True if given path is a valid repository False otherwise
+ Returns True if given path is a valid repository False otherwise.
+ If scm param is given also compare if given scm is the same as expected
+ from scm parameter
:param repo_name:
:param base_path:
+ :param scm:
:return True: if given path is a valid repository
"""
full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
try:
- get_scm(full_path)
+ scm_ = get_scm(full_path)
+ if scm:
+ return scm_[0] == scm
return True
except VCSError:
return False
@@ -234,6 +239,15 @@ def is_valid_repos_group(repos_group_name, base_path):
if is_valid_repo(repos_group_name, base_path):
return False
+ try:
+ # we need to check bare git repos at higher level
+ # since we might match branches/hooks/info/objects or possible
+ # other things inside bare git repo
+ get_scm(os.path.dirname(full_path))
+ return False
+ except VCSError:
+ pass
+
# check if it's a valid path
if os.path.isdir(full_path):
return True
@@ -266,7 +280,7 @@ ui_sections = ['alias', 'auth',
'ui', 'web', ]
-def make_ui(read_from='file', path=None, checkpaths=True):
+def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
"""
A function that will read python rc files or database
and make an mercurial ui object from read options
@@ -296,7 +310,7 @@ def make_ui(read_from='file', path=None, checkpaths=True):
baseui.setconfig(section, k, v)
elif read_from == 'db':
- sa = meta.Session
+ sa = meta.Session()
ret = sa.query(RhodeCodeUi)\
.options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
.all()
@@ -307,8 +321,12 @@ def make_ui(read_from='file', path=None, checkpaths=True):
log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
ui_.ui_key, ui_.ui_value)
baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
-
- meta.Session.remove()
+ if ui_.ui_key == 'push_ssl':
+ # force set push_ssl requirement to False, rhodecode
+ # handles that
+ baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
+ if clear_session:
+ meta.Session.remove()
return baseui
@@ -337,50 +355,6 @@ def invalidate_cache(cache_key, *args):
ScmModel().mark_for_invalidation(name)
-class EmptyChangeset(BaseChangeset):
- """
- An dummy empty changeset. It's possible to pass hash when creating
- an EmptyChangeset
- """
-
- def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
- alias=None):
- self._empty_cs = cs
- self.revision = -1
- self.message = ''
- self.author = ''
- self.date = ''
- self.repository = repo
- self.requested_revision = requested_revision
- self.alias = alias
-
- @LazyProperty
- def raw_id(self):
- """
- Returns raw string identifying this changeset, useful for web
- representation.
- """
-
- return self._empty_cs
-
- @LazyProperty
- def branch(self):
- return get_backend(self.alias).DEFAULT_BRANCH_NAME
-
- @LazyProperty
- def short_id(self):
- return self.raw_id[:12]
-
- def get_file_changeset(self, path):
- return self
-
- def get_file_content(self, path):
- return u''
-
- def get_file_size(self, path):
- return 0
-
-
def map_groups(path):
"""
Given a full path to a repository, create all nested groups that this
@@ -389,7 +363,7 @@ def map_groups(path):
:param paths: full path to repository
"""
- sa = meta.Session
+ sa = meta.Session()
groups = path.split(Repository.url_sep())
parent = None
group = None
@@ -418,7 +392,8 @@ def map_groups(path):
return group
-def repo2db_mapper(initial_repo_list, remove_obsolete=False):
+def repo2db_mapper(initial_repo_list, remove_obsolete=False,
+ install_git_hook=False):
"""
maps all repos given in initial_repo_list, non existing repositories
are created, if remove_obsolete is True it also check for db entries
@@ -426,9 +401,12 @@ def repo2db_mapper(initial_repo_list, remove_obsolete=False):
:param initial_repo_list: list of repositories found by scanning methods
:param remove_obsolete: check for obsolete entries in database
+ :param install_git_hook: if this is True, also check and install githook
+ for a repo if missing
"""
from rhodecode.model.repo import RepoModel
- sa = meta.Session
+ from rhodecode.model.scm import ScmModel
+ sa = meta.Session()
rm = RepoModel()
user = sa.query(User).filter(User.admin == True).first()
if user is None:
@@ -437,30 +415,45 @@ def repo2db_mapper(initial_repo_list, remove_obsolete=False):
for name, repo in initial_repo_list.items():
group = map_groups(name)
- if not rm.get_by_repo_name(name, cache=False):
- log.info('repository %s not found creating default' % name)
+ db_repo = rm.get_by_repo_name(name)
+ # found repo that is on filesystem not in RhodeCode database
+ if not db_repo:
+ log.info('repository %s not found creating now' % name)
added.append(name)
- form_data = {
- 'repo_name': name,
- 'repo_name_full': name,
- 'repo_type': repo.alias,
- 'description': repo.description \
- if repo.description != 'unknown' else '%s repository' % name,
- 'private': False,
- 'group_id': getattr(group, 'group_id', None)
- }
- rm.create(form_data, user, just_db=True)
+ desc = (repo.description
+ if repo.description != 'unknown'
+ else '%s repository' % name)
+ new_repo = rm.create_repo(
+ repo_name=name,
+ repo_type=repo.alias,
+ description=desc,
+ repos_group=getattr(group, 'group_id', None),
+ owner=user,
+ just_db=True
+ )
+ # we added that repo just now, and make sure it has githook
+ # installed
+ if new_repo.repo_type == 'git':
+ ScmModel().install_git_hook(new_repo.scm_instance)
+ elif install_git_hook:
+ if db_repo.repo_type == 'git':
+ ScmModel().install_git_hook(db_repo.scm_instance)
sa.commit()
removed = []
if remove_obsolete:
# remove from database those repositories that are not in the filesystem
for repo in sa.query(Repository).all():
if repo.repo_name not in initial_repo_list.keys():
- log.debug("Removing non existing repository found in db %s" %
+ log.debug("Removing non existing repository found in db `%s`" %
repo.repo_name)
- removed.append(repo.repo_name)
- sa.delete(repo)
- sa.commit()
+ try:
+ sa.delete(repo)
+ sa.commit()
+ removed.append(repo.repo_name)
+ except:
+ #don't hold further removals on error
+ log.error(traceback.format_exc())
+ sa.rollback()
# clear cache keys
log.debug("Clearing cache keys now...")
@@ -557,7 +550,7 @@ def create_test_env(repos_test_path, config):
install test repository into tmp dir
"""
from rhodecode.lib.db_manage import DbManage
- from rhodecode.tests import HG_REPO, TESTS_TMP_PATH
+ from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
# PART ONE create db
dbconf = config['sqlalchemy.db1.url']
@@ -576,7 +569,7 @@ def create_test_env(repos_test_path, config):
dbmanage.admin_prompt()
dbmanage.create_permissions()
dbmanage.populate_default_permissions()
- Session.commit()
+ Session().commit()
# PART TWO make test repo
log.debug('making test vcs repositories')
@@ -592,12 +585,21 @@ def create_test_env(repos_test_path, config):
log.debug('remove %s' % data_path)
shutil.rmtree(data_path)
- #CREATE DEFAULT HG REPOSITORY
+ #CREATE DEFAULT TEST REPOS
cur_dir = dn(dn(abspath(__file__)))
tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
tar.close()
+ cur_dir = dn(dn(abspath(__file__)))
+ tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
+ tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
+ tar.close()
+
+ #LOAD VCS test stuff
+ from rhodecode.tests.vcs import setup_package
+ setup_package()
+
#==============================================================================
# PASTER COMMANDS
diff --git a/rhodecode/lib/utils2.py b/rhodecode/lib/utils2.py
index 249597bb..6881bd32 100644
--- a/rhodecode/lib/utils2.py
+++ b/rhodecode/lib/utils2.py
@@ -24,6 +24,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
+import time
+import datetime
+from pylons.i18n.translation import _, ungettext
from rhodecode.lib.vcs.utils.lazy import LazyProperty
@@ -270,7 +273,6 @@ def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
context._query_start_time = time.time()
log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
-
def after_cursor_execute(conn, cursor, statement,
parameters, context, executemany):
total = time.time() - context._query_start_time
@@ -284,40 +286,76 @@ def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
return engine
-def age(curdate):
+def age(prevdate):
"""
turns a datetime into an age string.
- :param curdate: datetime object
+ :param prevdate: datetime object
:rtype: unicode
:returns: unicode words describing age
"""
- from datetime import datetime
- from webhelpers.date import time_ago_in_words
+ order = ['year', 'month', 'day', 'hour', 'minute', 'second']
+ deltas = {}
- _ = lambda s: s
+ # Get date parts deltas
+ now = datetime.datetime.now()
+ for part in order:
+ deltas[part] = getattr(now, part) - getattr(prevdate, part)
- if not curdate:
- return ''
+ # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
+ # not 1 hour, -59 minutes and -59 seconds)
+
+ for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
+ part = order[num]
+ carry_part = order[num - 1]
+
+ if deltas[part] < 0:
+ deltas[part] += length
+ deltas[carry_part] -= 1
+
+ # Same thing for days except that the increment depends on the (variable)
+ # number of days in the month
+ month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+ if deltas['day'] < 0:
+ if prevdate.month == 2 and (prevdate.year % 4 == 0 and
+ (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
+ deltas['day'] += 29
+ else:
+ deltas['day'] += month_lengths[prevdate.month - 1]
+
+ deltas['month'] -= 1
+
+ if deltas['month'] < 0:
+ deltas['month'] += 12
+ deltas['year'] -= 1
+
+ # Format the result
+ fmt_funcs = {
+ 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
+ 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
+ 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
+ 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
+ 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
+ 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
+ }
+
+ for i, part in enumerate(order):
+ value = deltas[part]
+ if value == 0:
+ continue
+
+ if i < 5:
+ sub_part = order[i + 1]
+ sub_value = deltas[sub_part]
+ else:
+ sub_value = 0
- agescales = [(_(u"year"), 3600 * 24 * 365),
- (_(u"month"), 3600 * 24 * 30),
- (_(u"day"), 3600 * 24),
- (_(u"hour"), 3600),
- (_(u"minute"), 60),
- (_(u"second"), 1), ]
-
- age = datetime.now() - curdate
- age_seconds = (age.days * agescales[2][1]) + age.seconds
- pos = 1
- for scale in agescales:
- if scale[1] <= age_seconds:
- if pos == 6:
- pos = 5
- return '%s %s' % (time_ago_in_words(curdate,
- agescales[pos][0]), _('ago'))
- pos += 1
+ if sub_value == 0:
+ return _(u'%s ago') % fmt_funcs[part](value)
+
+ return _(u'%s and %s ago') % (fmt_funcs[part](value),
+ fmt_funcs[sub_part](sub_value))
return _(u'just now')
@@ -379,6 +417,7 @@ def get_changeset_safe(repo, rev):
"""
from rhodecode.lib.vcs.backends.base import BaseRepository
from rhodecode.lib.vcs.exceptions import RepositoryError
+ from rhodecode.lib.vcs.backends.base import EmptyChangeset
if not isinstance(repo, BaseRepository):
raise Exception('You must pass an Repository '
'object as first argument got %s', type(repo))
@@ -386,11 +425,24 @@ def get_changeset_safe(repo, rev):
try:
cs = repo.get_changeset(rev)
except RepositoryError:
- from rhodecode.lib.utils import EmptyChangeset
cs = EmptyChangeset(requested_revision=rev)
return cs
+def datetime_to_time(dt):
+ if dt:
+ return time.mktime(dt.timetuple())
+
+
+def time_to_datetime(tm):
+ if tm:
+ if isinstance(tm, basestring):
+ try:
+ tm = float(tm)
+ except ValueError:
+ return
+ return datetime.datetime.fromtimestamp(tm)
+
MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
@@ -405,3 +457,10 @@ def extract_mentioned_users(s):
usrs.add(username)
return sorted(list(usrs), key=lambda k: k.lower())
+
+
+class AttributeDict(dict):
+ def __getattr__(self, attr):
+ return self.get(attr, None)
+ __setattr__ = dict.__setitem__
+ __delattr__ = dict.__delitem__
diff --git a/rhodecode/lib/vcs/__init__.py b/rhodecode/lib/vcs/__init__.py
index 869157e4..3d85daa3 100644
--- a/rhodecode/lib/vcs/__init__.py
+++ b/rhodecode/lib/vcs/__init__.py
@@ -10,7 +10,7 @@
:copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
"""
-VERSION = (0, 2, 3, 'dev')
+VERSION = (0, 3, 0, 'dev')
__version__ = '.'.join((str(each) for each in VERSION[:4]))
diff --git a/rhodecode/lib/vcs/backends/git/changeset.py b/rhodecode/lib/vcs/backends/git/changeset.py
index cda48417..224d94ff 100644
--- a/rhodecode/lib/vcs/backends/git/changeset.py
+++ b/rhodecode/lib/vcs/backends/git/changeset.py
@@ -9,7 +9,7 @@ from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
from rhodecode.lib.vcs.exceptions import VCSError
from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError
-from rhodecode.lib.vcs.backends.base import BaseChangeset
+from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \
RemovedFileNode, SubModuleNode
from rhodecode.lib.vcs.utils import safe_unicode
@@ -25,17 +25,24 @@ class GitChangeset(BaseChangeset):
def __init__(self, repository, revision):
self._stat_modes = {}
self.repository = repository
- self.raw_id = revision
- self.revision = repository.revisions.index(revision)
- self.short_id = self.raw_id[:12]
- self.id = self.raw_id
try:
- commit = self.repository._repo.get_object(self.raw_id)
+ commit = self.repository._repo.get_object(revision)
+ if isinstance(commit, objects.Tag):
+ revision = commit.object[1]
+ commit = self.repository._repo.get_object(commit.object[1])
except KeyError:
- raise RepositoryError("Cannot get object with id %s" % self.raw_id)
+ raise RepositoryError("Cannot get object with id %s" % revision)
+ self.raw_id = revision
+ self.id = self.raw_id
+ self.short_id = self.raw_id[:12]
self._commit = commit
+
self._tree_id = commit.tree
+ self._commiter_property = 'committer'
+ self._date_property = 'commit_time'
+ self._date_tz_property = 'commit_timezone'
+ self.revision = repository.revisions.index(revision)
self.message = safe_unicode(commit.message)
#self.branch = None
@@ -45,12 +52,16 @@ class GitChangeset(BaseChangeset):
@LazyProperty
def author(self):
- return safe_unicode(self._commit.committer)
+ return safe_unicode(getattr(self._commit, self._commiter_property))
@LazyProperty
def date(self):
- return date_fromtimestamp(self._commit.commit_time,
- self._commit.commit_timezone)
+ return date_fromtimestamp(getattr(self._commit, self._date_property),
+ getattr(self._commit, self._date_tz_property))
+
+ @LazyProperty
+ def _timestamp(self):
+ return getattr(self._commit, self._date_property)
@LazyProperty
def status(self):
@@ -83,7 +94,7 @@ class GitChangeset(BaseChangeset):
if not path in self._paths:
path = path.strip('/')
# set root tree
- tree = self.repository._repo[self._commit.tree]
+ tree = self.repository._repo[self._tree_id]
if path == '':
self._paths[''] = tree.id
return tree.id
@@ -132,8 +143,7 @@ class GitChangeset(BaseChangeset):
return self._paths[path]
def _get_kind(self, path):
- id = self._get_id_for_path(path)
- obj = self.repository._repo[id]
+ obj = self.repository._repo[self._get_id_for_path(path)]
if isinstance(obj, objects.Blob):
return NodeKind.FILE
elif isinstance(obj, objects.Tree):
@@ -148,7 +158,7 @@ class GitChangeset(BaseChangeset):
Returns list of parents changesets.
"""
return [self.repository.get_changeset(parent)
- for parent in self._commit.parents]
+ for parent in self._commit.parents]
def next(self, branch=None):
@@ -194,6 +204,13 @@ class GitChangeset(BaseChangeset):
return _prev(self, branch)
+ def diff(self, ignore_whitespace=True, context=3):
+ rev1 = self.parents[0] if self.parents else self.repository.EMPTY_CHANGESET
+ rev2 = self
+ return ''.join(self.repository.get_diff(rev1, rev2,
+ ignore_whitespace=ignore_whitespace,
+ context=context))
+
def get_file_mode(self, path):
"""
Returns stat mode of the file at the given ``path``.
@@ -254,10 +271,11 @@ class GitChangeset(BaseChangeset):
# --root ==> doesn't put '^' character for bounderies
# -r sha ==> blames for the given revision
so, se = self.repository.run_git_command(cmd)
+
annotate = []
for i, blame_line in enumerate(so.split('\n')[:-1]):
ln_no = i + 1
- id, line = re.split(r' \(.+?\) ', blame_line, 1)
+ id, line = re.split(r' ', blame_line, 1)
annotate.append((ln_no, self.repository.get_changeset(id), line))
return annotate
@@ -363,10 +381,10 @@ class GitChangeset(BaseChangeset):
raise NodeDoesNotExistError("Cannot find one of parents' "
"directories for a given path: %s" % path)
- als = self.repository.alias
_GL = lambda m: m and objects.S_ISGITLINK(m)
if _GL(self._stat_modes.get(path)):
- node = SubModuleNode(path, url=None, changeset=id_, alias=als)
+ node = SubModuleNode(path, url=None, changeset=id_,
+ alias=self.repository.alias)
else:
obj = self.repository._repo.get_object(id_)
@@ -392,39 +410,56 @@ class GitChangeset(BaseChangeset):
"""
Get's a fast accessible file changes for given changeset
"""
-
- return self.added + self.changed
+ a, m, d = self._changes_cache
+ return list(a.union(m).union(d))
@LazyProperty
def _diff_name_status(self):
output = []
for parent in self.parents:
- cmd = 'diff --name-status %s %s --encoding=utf8' % (parent.raw_id, self.raw_id)
+ cmd = 'diff --name-status %s %s --encoding=utf8' % (parent.raw_id,
+ self.raw_id)
so, se = self.repository.run_git_command(cmd)
output.append(so.strip())
return '\n'.join(output)
+ @LazyProperty
+ def _changes_cache(self):
+ added = set()
+ modified = set()
+ deleted = set()
+ _r = self.repository._repo
+
+ parents = self.parents
+ if not self.parents:
+ parents = [EmptyChangeset()]
+ for parent in parents:
+ if isinstance(parent, EmptyChangeset):
+ oid = None
+ else:
+ oid = _r[parent.raw_id].tree
+ changes = _r.object_store.tree_changes(oid, _r[self.raw_id].tree)
+ for (oldpath, newpath), (_, _), (_, _) in changes:
+ if newpath and oldpath:
+ modified.add(newpath)
+ elif newpath and not oldpath:
+ added.add(newpath)
+ elif not newpath and oldpath:
+ deleted.add(oldpath)
+ return added, modified, deleted
+
def _get_paths_for_status(self, status):
"""
Returns sorted list of paths for given ``status``.
:param status: one of: *added*, *modified* or *deleted*
"""
- paths = set()
- char = status[0].upper()
- for line in self._diff_name_status.splitlines():
- if not line:
- continue
-
- if line.startswith(char):
- splitted = line.split(char, 1)
- if not len(splitted) == 2:
- raise VCSError("Couldn't parse diff result:\n%s\n\n and "
- "particularly that line: %s" % (self._diff_name_status,
- line))
- _path = splitted[1].strip()
- paths.add(_path)
- return sorted(paths)
+ a, m, d = self._changes_cache
+ return sorted({
+ 'added': list(a),
+ 'modified': list(m),
+ 'deleted': list(d)}[status]
+ )
@LazyProperty
def added(self):
diff --git a/rhodecode/lib/vcs/backends/git/inmemory.py b/rhodecode/lib/vcs/backends/git/inmemory.py
index 3dd78461..28ed00be 100644
--- a/rhodecode/lib/vcs/backends/git/inmemory.py
+++ b/rhodecode/lib/vcs/backends/git/inmemory.py
@@ -83,7 +83,8 @@ class GitInMemoryChangeset(BaseInMemoryChangeset):
curtree = newtree
parent[reversed_dirnames[-1]] = DIRMOD, curtree.id
else:
- parent.add(node.mode, node_path, blob.id)
+ parent.add(name=node_path, mode=node.mode, hexsha=blob.id)
+
new_trees.append(parent)
# Update ancestors
for parent, tree, path in reversed([(a[1], b[1], b[0]) for a, b in
@@ -123,7 +124,7 @@ class GitInMemoryChangeset(BaseInMemoryChangeset):
commit.parents = [p._commit.id for p in self.parents if p]
commit.author = commit.committer = safe_str(author)
commit.encoding = ENCODING
- commit.message = safe_str(message) + ' '
+ commit.message = safe_str(message)
# Compute date
if date is None:
@@ -148,6 +149,8 @@ class GitInMemoryChangeset(BaseInMemoryChangeset):
# Update vcs repository object & recreate dulwich repo
self.repository.revisions.append(commit.id)
self.repository._repo = Repo(self.repository.path)
+ # invalidate parsed refs after commit
+ self.repository._parsed_refs = self.repository._get_parsed_refs()
tip = self.repository.get_changeset()
self.reset()
return tip
diff --git a/rhodecode/lib/vcs/backends/git/repository.py b/rhodecode/lib/vcs/backends/git/repository.py
index 1d647c54..a3a9a70c 100644
--- a/rhodecode/lib/vcs/backends/git/repository.py
+++ b/rhodecode/lib/vcs/backends/git/repository.py
@@ -13,6 +13,10 @@ import os
import re
import time
import posixpath
+import logging
+import traceback
+import urllib
+import urllib2
from dulwich.repo import Repo, NotGitRepository
#from dulwich.config import ConfigFile
from string import Template
@@ -33,6 +37,10 @@ from .workdir import GitWorkdir
from .changeset import GitChangeset
from .inmemory import GitInMemoryChangeset
from .config import ConfigFile
+from rhodecode.lib import subprocessio
+
+
+log = logging.getLogger(__name__)
class GitRepository(BaseRepository):
@@ -66,6 +74,7 @@ class GitRepository(BaseRepository):
'config'),
abspath(get_user_home(), '.gitconfig'),
]
+ self.bare = self._repo.bare
@LazyProperty
def revisions(self):
@@ -94,28 +103,32 @@ class GitRepository(BaseRepository):
cmd = [cmd]
_str_cmd = True
- cmd = ['GIT_CONFIG_NOGLOBAL=1', 'git'] + _copts + cmd
+ gitenv = os.environ
+ # need to clean fix GIT_DIR !
+ if 'GIT_DIR' in gitenv:
+ del gitenv['GIT_DIR']
+ gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
+
+ cmd = ['git'] + _copts + cmd
if _str_cmd:
cmd = ' '.join(cmd)
try:
opts = dict(
- shell=isinstance(cmd, basestring),
- stdout=PIPE,
- stderr=PIPE)
+ env=gitenv,
+ shell=False,
+ )
if os.path.isdir(self.path):
opts['cwd'] = self.path
- p = Popen(cmd, **opts)
- except OSError, err:
+ p = subprocessio.SubprocessIOChunker(cmd, **opts)
+ except (EnvironmentError, OSError), err:
+ log.error(traceback.format_exc())
raise RepositoryError("Couldn't run git command (%s).\n"
- "Original error was:%s" % (cmd, err))
- so, se = p.communicate()
- if not se.startswith("fatal: bad default revision 'HEAD'") and \
- p.returncode != 0:
- raise RepositoryError("Couldn't run git command (%s).\n"
- "stderr:\n%s" % (cmd, se))
- return so, se
+ "Original error was:%s" % (cmd, err))
+
+ return ''.join(p.output), ''.join(p.error)
- def _check_url(self, url):
+ @classmethod
+ def _check_url(cls, url):
"""
Functon will check given url and try to verify if it's a valid
link. Sometimes it may happened that mercurial will issue basic
@@ -124,9 +137,45 @@ class GitRepository(BaseRepository):
On failures it'll raise urllib2.HTTPError
"""
+ from mercurial.util import url as Url
+
+ # those authnadlers are patched for python 2.6.5 bug an
+ # infinit looping when given invalid resources
+ from mercurial.url import httpbasicauthhandler, httpdigestauthhandler
+
+ # check first if it's not an local url
+ if os.path.isdir(url) or url.startswith('file:'):
+ return True
+
+ if('+' in url[:url.find('://')]):
+ url = url[url.find('+') + 1:]
+
+ handlers = []
+ test_uri, authinfo = Url(url).authinfo()
+ if not test_uri.endswith('info/refs'):
+ test_uri = test_uri.rstrip('/') + '/info/refs'
+ if authinfo:
+ #create a password manager
+ passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
+ passmgr.add_password(*authinfo)
- #TODO: implement this
- pass
+ handlers.extend((httpbasicauthhandler(passmgr),
+ httpdigestauthhandler(passmgr)))
+
+ o = urllib2.build_opener(*handlers)
+ o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
+
+ q = {"service": 'git-upload-pack'}
+ qs = '?%s' % urllib.urlencode(q)
+ cu = "%s%s" % (test_uri, qs)
+ req = urllib2.Request(cu, None, {})
+
+ try:
+ resp = o.open(req)
+ return resp.code == 200
+ except Exception, e:
+ # means it cannot be cloned
+ raise urllib2.URLError("[%s] %s" % (url, e))
def _get_repo(self, create, src_url=None, update_after_clone=False,
bare=False):
@@ -137,7 +186,7 @@ class GitRepository(BaseRepository):
"given (clone operation creates repository)")
try:
if create and src_url:
- self._check_url(src_url)
+ GitRepository._check_url(src_url)
self.clone(src_url, update_after_clone, bare)
return Repo(self.path)
elif create:
@@ -152,15 +201,26 @@ class GitRepository(BaseRepository):
raise RepositoryError(err)
def _get_all_revisions(self):
- cmd = 'rev-list --all --date-order'
+ # we must check if this repo is not empty, since later command
+ # fails if it is. And it's cheaper to ask than throw the subprocess
+ # errors
+ try:
+ self._repo.head()
+ except KeyError:
+ return []
+ cmd = 'rev-list --all --reverse --date-order'
try:
so, se = self.run_git_command(cmd)
except RepositoryError:
# Can be raised for empty repositories
return []
- revisions = so.splitlines()
- revisions.reverse()
- return revisions
+ return so.splitlines()
+
+ def _get_all_revisions2(self):
+ #alternate implementation using dulwich
+ includes = [x[1][0] for x in self._parsed_refs.iteritems()
+ if x[1][1] != 'T']
+ return [c.commit.id for c in self._repo.get_walker(include=includes)]
def _get_revision(self, revision):
"""
@@ -186,7 +246,17 @@ class GitRepository(BaseRepository):
"for this repository %s" % (revision, self))
elif is_bstr(revision):
- if not pattern.match(revision) or revision not in self.revisions:
+ # get by branch/tag name
+ _ref_revision = self._parsed_refs.get(revision)
+ _tags_shas = self.tags.values()
+ if _ref_revision: # and _ref_revision[1] in ['H', 'RH', 'T']:
+ return _ref_revision[0]
+
+ # maybe it's a tag ? we don't have them in self.revisions
+ elif revision in _tags_shas:
+ return _tags_shas[_tags_shas.index(revision)]
+
+ elif not pattern.match(revision) or revision not in self.revisions:
raise ChangesetDoesNotExistError("Revision %r does not exist "
"for this repository %s" % (revision, self))
@@ -226,9 +296,10 @@ class GitRepository(BaseRepository):
try:
return time.mktime(self.get_changeset().date.timetuple())
except RepositoryError:
+ idx_loc = '' if self.bare else '.git'
# fallback to filesystem
- in_path = os.path.join(self.path, '.git', "index")
- he_path = os.path.join(self.path, '.git', "HEAD")
+ in_path = os.path.join(self.path, idx_loc, "index")
+ he_path = os.path.join(self.path, idx_loc, "HEAD")
if os.path.exists(in_path):
return os.stat(in_path).st_mtime
else:
@@ -236,8 +307,9 @@ class GitRepository(BaseRepository):
@LazyProperty
def description(self):
+ idx_loc = '' if self.bare else '.git'
undefined_description = u'unknown'
- description_path = os.path.join(self.path, '.git', 'description')
+ description_path = os.path.join(self.path, idx_loc, 'description')
if os.path.isfile(description_path):
return safe_unicode(open(description_path).read())
else:
@@ -252,38 +324,24 @@ class GitRepository(BaseRepository):
def branches(self):
if not self.revisions:
return {}
- refs = self._repo.refs.as_dict()
sortkey = lambda ctx: ctx[0]
- _branches = [('/'.join(ref.split('/')[2:]), head)
- for ref, head in refs.items()
- if ref.startswith('refs/heads/') and not ref.endswith('/HEAD')]
+ _branches = [(x[0], x[1][0])
+ for x in self._parsed_refs.iteritems() if x[1][1] == 'H']
return OrderedDict(sorted(_branches, key=sortkey, reverse=False))
- def _heads(self, reverse=False):
- refs = self._repo.get_refs()
- heads = {}
-
- for key, val in refs.items():
- for ref_key in ['refs/heads/', 'refs/remotes/origin/']:
- if key.startswith(ref_key):
- n = key[len(ref_key):]
- if n not in ['HEAD']:
- heads[n] = val
-
- return heads if reverse else dict((y,x) for x,y in heads.iteritems())
+ @LazyProperty
+ def tags(self):
+ return self._get_tags()
def _get_tags(self):
if not self.revisions:
return {}
+
sortkey = lambda ctx: ctx[0]
- _tags = [('/'.join(ref.split('/')[2:]), head) for ref, head in
- self._repo.get_refs().items() if ref.startswith('refs/tags/')]
+ _tags = [(x[0], x[1][0])
+ for x in self._parsed_refs.iteritems() if x[1][1] == 'T']
return OrderedDict(sorted(_tags, key=sortkey, reverse=True))
- @LazyProperty
- def tags(self):
- return self._get_tags()
-
def tag(self, name, user, revision=None, message=None, date=None,
**kwargs):
"""
@@ -304,6 +362,7 @@ class GitRepository(BaseRepository):
changeset.raw_id)
self._repo.refs["refs/tags/%s" % name] = changeset._commit.id
+ self._parsed_refs = self._get_parsed_refs()
self.tags = self._get_tags()
return changeset
@@ -323,10 +382,42 @@ class GitRepository(BaseRepository):
tagpath = posixpath.join(self._repo.refs.path, 'refs', 'tags', name)
try:
os.remove(tagpath)
+ self._parsed_refs = self._get_parsed_refs()
self.tags = self._get_tags()
except OSError, e:
raise RepositoryError(e.strerror)
+ @LazyProperty
+ def _parsed_refs(self):
+ return self._get_parsed_refs()
+
+ def _get_parsed_refs(self):
+ refs = self._repo.get_refs()
+ keys = [('refs/heads/', 'H'),
+ ('refs/remotes/origin/', 'RH'),
+ ('refs/tags/', 'T')]
+ _refs = {}
+ for ref, sha in refs.iteritems():
+ for k, type_ in keys:
+ if ref.startswith(k):
+ _key = ref[len(k):]
+ _refs[_key] = [sha, type_]
+ break
+ return _refs
+
+ def _heads(self, reverse=False):
+ refs = self._repo.get_refs()
+ heads = {}
+
+ for key, val in refs.items():
+ for ref_key in ['refs/heads/', 'refs/remotes/origin/']:
+ if key.startswith(ref_key):
+ n = key[len(ref_key):]
+ if n not in ['HEAD']:
+ heads[n] = val
+
+ return heads if reverse else dict((y, x) for x, y in heads.iteritems())
+
def get_changeset(self, revision=None):
"""
Returns ``GitChangeset`` object representing commit from git repository
@@ -429,6 +520,12 @@ class GitRepository(BaseRepository):
if ignore_whitespace:
flags.append('-w')
+ if hasattr(rev1, 'raw_id'):
+ rev1 = getattr(rev1, 'raw_id')
+
+ if hasattr(rev2, 'raw_id'):
+ rev2 = getattr(rev2, 'raw_id')
+
if rev1 == self.EMPTY_CHANGESET:
rev2 = self.get_changeset(rev2).raw_id
cmd = ' '.join(['show'] + flags + [rev2])
@@ -492,6 +589,17 @@ class GitRepository(BaseRepository):
# If error occurs run_git_command raises RepositoryError already
self.run_git_command(cmd)
+ def fetch(self, url):
+ """
+ Tries to pull changes from external location.
+ """
+ url = self._get_url(url)
+ cmd = ['fetch']
+ cmd.append(url)
+ cmd = ' '.join(cmd)
+ # If error occurs run_git_command raises RepositoryError already
+ self.run_git_command(cmd)
+
@LazyProperty
def workdir(self):
"""
diff --git a/rhodecode/lib/vcs/backends/hg/changeset.py b/rhodecode/lib/vcs/backends/hg/changeset.py
index 774985b9..790f8b01 100644
--- a/rhodecode/lib/vcs/backends/hg/changeset.py
+++ b/rhodecode/lib/vcs/backends/hg/changeset.py
@@ -12,8 +12,7 @@ from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \
from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp
from rhodecode.lib.vcs.utils.lazy import LazyProperty
from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
-
-from ...utils.hgcompat import archival, hex
+from rhodecode.lib.vcs.utils.hgcompat import archival, hex
class MercurialChangeset(BaseChangeset):
@@ -53,6 +52,10 @@ class MercurialChangeset(BaseChangeset):
return date_fromtimestamp(*self._ctx.date())
@LazyProperty
+ def _timestamp(self):
+ return self._ctx.date()[0]
+
+ @LazyProperty
def status(self):
"""
Returns modified, added, removed, deleted files for current changeset
@@ -136,6 +139,11 @@ class MercurialChangeset(BaseChangeset):
return _prev(self, branch)
+ def diff(self, ignore_whitespace=True, context=3):
+ return ''.join(self._ctx.diff(git=True,
+ ignore_whitespace=ignore_whitespace,
+ context=context))
+
def _fix_path(self, path):
"""
Paths are stored without trailing slash so we need to get rid off it if
diff --git a/rhodecode/lib/vcs/backends/hg/inmemory.py b/rhodecode/lib/vcs/backends/hg/inmemory.py
index bd9ece7d..ff834f0f 100644
--- a/rhodecode/lib/vcs/backends/hg/inmemory.py
+++ b/rhodecode/lib/vcs/backends/hg/inmemory.py
@@ -4,7 +4,7 @@ import errno
from rhodecode.lib.vcs.backends.base import BaseInMemoryChangeset
from rhodecode.lib.vcs.exceptions import RepositoryError
-from ...utils.hgcompat import memfilectx, memctx, hex, tolocal
+from rhodecode.lib.vcs.utils.hgcompat import memfilectx, memctx, hex, tolocal
class MercurialInMemoryChangeset(BaseInMemoryChangeset):
@@ -32,7 +32,8 @@ class MercurialInMemoryChangeset(BaseInMemoryChangeset):
from .repository import MercurialRepository
if not isinstance(message, unicode) or not isinstance(author, unicode):
raise RepositoryError('Given message and author needs to be '
- 'an <unicode> instance')
+ 'an <unicode> instance got %r & %r instead'
+ % (type(message), type(author)))
if branch is None:
branch = MercurialRepository.DEFAULT_BRANCH_NAME
diff --git a/rhodecode/lib/vcs/backends/hg/repository.py b/rhodecode/lib/vcs/backends/hg/repository.py
index c590c175..53cb9039 100644
--- a/rhodecode/lib/vcs/backends/hg/repository.py
+++ b/rhodecode/lib/vcs/backends/hg/repository.py
@@ -18,7 +18,7 @@ from rhodecode.lib.vcs.utils.lazy import LazyProperty
from rhodecode.lib.vcs.utils.ordered_dict import OrderedDict
from rhodecode.lib.vcs.utils.paths import abspath
-from ...utils.hgcompat import ui, nullid, match, patch, diffopts, clone, \
+from rhodecode.lib.vcs.utils.hgcompat import ui, nullid, match, patch, diffopts, clone, \
get_contact, pull, localrepository, RepoLookupError, Abort, RepoError, hex
@@ -231,6 +231,12 @@ class MercurialRepository(BaseRepository):
:param context: How many lines before/after changed lines should be
shown. Defaults to ``3``.
"""
+ if hasattr(rev1, 'raw_id'):
+ rev1 = getattr(rev1, 'raw_id')
+
+ if hasattr(rev2, 'raw_id'):
+ rev2 = getattr(rev2, 'raw_id')
+
# Check if given revisions are present at repository (may raise
# ChangesetDoesNotExistError)
if rev1 != self.EMPTY_CHANGESET:
@@ -243,7 +249,8 @@ class MercurialRepository(BaseRepository):
ignorews=ignore_whitespace,
context=context)))
- def _check_url(self, url):
+ @classmethod
+ def _check_url(cls, url):
"""
Function will check given url and try to verify if it's a valid
link. Sometimes it may happened that mercurial will issue basic
@@ -264,6 +271,9 @@ class MercurialRepository(BaseRepository):
if os.path.isdir(url) or url.startswith('file:'):
return True
+ if('+' in url[:url.find('://')]):
+ url = url[url.find('+') + 1:]
+
handlers = []
test_uri, authinfo = Url(url).authinfo()
@@ -290,7 +300,7 @@ class MercurialRepository(BaseRepository):
return resp.code == 200
except Exception, e:
# means it cannot be cloned
- raise urllib2.URLError(e)
+ raise urllib2.URLError("[%s] %s" % (url, e))
def _get_repo(self, create, src_url=None, update_after_clone=False):
"""
@@ -302,6 +312,7 @@ class MercurialRepository(BaseRepository):
location at given clone_point. Additionally it'll make update to
working copy accordingly to ``update_after_clone`` flag
"""
+
try:
if src_url:
url = str(self._get_url(src_url))
@@ -309,12 +320,13 @@ class MercurialRepository(BaseRepository):
if not update_after_clone:
opts.update({'noupdate': True})
try:
- self._check_url(url)
+ MercurialRepository._check_url(url)
clone(self.baseui, url, self.path, **opts)
# except urllib2.URLError:
# raise Abort("Got HTTP 404 error")
except Exception:
raise
+
# Don't try to create if we've already cloned repo
create = False
return localrepository(self.baseui, self.path, create=create)
diff --git a/rhodecode/lib/vcs/backends/hg/workdir.py b/rhodecode/lib/vcs/backends/hg/workdir.py
index 64218be4..822c92bd 100644
--- a/rhodecode/lib/vcs/backends/hg/workdir.py
+++ b/rhodecode/lib/vcs/backends/hg/workdir.py
@@ -1,7 +1,7 @@
from rhodecode.lib.vcs.backends.base import BaseWorkdir
from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError
-from ...utils.hgcompat import hg_merge
+from rhodecode.lib.vcs.utils.hgcompat import hg_merge
class MercurialWorkdir(BaseWorkdir):
diff --git a/rhodecode/lib/vcs/nodes.py b/rhodecode/lib/vcs/nodes.py
index 8fb66d01..a133af8c 100644
--- a/rhodecode/lib/vcs/nodes.py
+++ b/rhodecode/lib/vcs/nodes.py
@@ -16,7 +16,7 @@ import mimetypes
from pygments import lexers
from rhodecode.lib.vcs.utils.lazy import LazyProperty
-from rhodecode.lib.vcs.utils import safe_unicode, safe_str
+from rhodecode.lib.vcs.utils import safe_unicode
from rhodecode.lib.vcs.exceptions import NodeError
from rhodecode.lib.vcs.exceptions import RemovedFileNodeError
from rhodecode.lib.vcs.backends.base import EmptyChangeset
@@ -422,7 +422,7 @@ class FileNode(Node):
def __repr__(self):
return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
- self.changeset.short_id)
+ getattr(self.changeset, 'short_id', ''))
class RemovedFileNode(FileNode):
@@ -431,8 +431,10 @@ class RemovedFileNode(FileNode):
name, kind or state (or methods/attributes checking those two) would raise
RemovedFileNodeError.
"""
- ALLOWED_ATTRIBUTES = ['name', 'path', 'state', 'is_root', 'is_file',
- 'is_dir', 'kind', 'added', 'changed', 'not_changed', 'removed']
+ ALLOWED_ATTRIBUTES = [
+ 'name', 'path', 'state', 'is_root', 'is_file', 'is_dir', 'kind',
+ 'added', 'changed', 'not_changed', 'removed'
+ ]
def __init__(self, path):
"""
@@ -557,7 +559,7 @@ class DirNode(Node):
def __repr__(self):
return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
- self.changeset.short_id)
+ getattr(self.changeset, 'short_id', ''))
class RootNode(DirNode):
@@ -591,7 +593,7 @@ class SubModuleNode(Node):
def __repr__(self):
return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
- self.changeset.short_id)
+ getattr(self.changeset, 'short_id', ''))
def _extract_submodule_url(self):
if self.alias == 'git':
diff --git a/rhodecode/lib/vcs/utils/hgcompat.py b/rhodecode/lib/vcs/utils/hgcompat.py
index 51b5de32..547c8872 100644
--- a/rhodecode/lib/vcs/utils/hgcompat.py
+++ b/rhodecode/lib/vcs/utils/hgcompat.py
@@ -12,3 +12,6 @@ from mercurial.match import match
from mercurial.mdiff import diffopts
from mercurial.node import hex
from mercurial.encoding import tolocal
+from mercurial import discovery
+from mercurial import localrepo
+from mercurial import scmutil \ No newline at end of file
diff --git a/rhodecode/lib/vcs/utils/lazy.py b/rhodecode/lib/vcs/utils/lazy.py
index 55f45927..1a7df2d2 100644
--- a/rhodecode/lib/vcs/utils/lazy.py
+++ b/rhodecode/lib/vcs/utils/lazy.py
@@ -17,11 +17,12 @@ class LazyProperty(object):
def __init__(self, func):
self._func = func
+ self.__module__ = func.__module__
self.__name__ = func.__name__
self.__doc__ = func.__doc__
def __get__(self, obj, klass=None):
if obj is None:
- return None
+ return self
result = obj.__dict__[self.__name__] = self._func(obj)
return result
diff --git a/rhodecode/lib/vcs/utils/paths.py b/rhodecode/lib/vcs/utils/paths.py
index e30777dd..b84e5523 100644
--- a/rhodecode/lib/vcs/utils/paths.py
+++ b/rhodecode/lib/vcs/utils/paths.py
@@ -29,8 +29,9 @@ def get_dir_size(path):
pass
return size
+
def get_user_home():
"""
Returns home path of the user.
"""
- return os.getenv('HOME', os.getenv('USERPROFILE'))
+ return os.getenv('HOME', os.getenv('USERPROFILE')) or ''
diff --git a/rhodecode/model/__init__.py b/rhodecode/model/__init__.py
index 9452e8e9..cdf28f7f 100644
--- a/rhodecode/model/__init__.py
+++ b/rhodecode/model/__init__.py
@@ -42,8 +42,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
-
from rhodecode.model import meta
+from rhodecode.lib.utils2 import safe_str
log = logging.getLogger(__name__)
@@ -68,11 +68,13 @@ class BaseModel(object):
:param sa: If passed it reuses this session instead of creating a new one
"""
+ cls = None # override in child class
+
def __init__(self, sa=None):
if sa is not None:
self.sa = sa
else:
- self.sa = meta.Session
+ self.sa = meta.Session()
def _get_instance(self, cls, instance, callback=None):
"""
@@ -85,7 +87,7 @@ class BaseModel(object):
if isinstance(instance, cls):
return instance
- elif isinstance(instance, (int, long)) or str(instance).isdigit():
+ elif isinstance(instance, (int, long)) or safe_str(instance).isdigit():
return cls.get(instance)
else:
if instance:
@@ -96,3 +98,42 @@ class BaseModel(object):
)
else:
return callback(instance)
+
+ def _get_user(self, user):
+ """
+ Helper method to get user by ID, or username fallback
+
+ :param user:
+ :type user: UserID, username, or User instance
+ """
+ from rhodecode.model.db import User
+ return self._get_instance(User, user,
+ callback=User.get_by_username)
+
+ def _get_repo(self, repository):
+ """
+ Helper method to get repository by ID, or repository name
+
+ :param repository:
+ :type repository: RepoID, repository name or Repository Instance
+ """
+ from rhodecode.model.db import Repository
+ return self._get_instance(Repository, repository,
+ callback=Repository.get_by_repo_name)
+
+ def _get_perm(self, permission):
+ """
+ Helper method to get permission by ID, or permission name
+
+ :param permission:
+ :type permission: PermissionID, permission_name or Permission instance
+ """
+ from rhodecode.model.db import Permission
+ return self._get_instance(Permission, permission,
+ callback=Permission.get_by_key)
+
+ def get_all(self):
+ """
+ Returns all instances of what is defined in `cls` class variable
+ """
+ return self.cls.getAll()
diff --git a/rhodecode/model/changeset_status.py b/rhodecode/model/changeset_status.py
new file mode 100644
index 00000000..30f43475
--- /dev/null
+++ b/rhodecode/model/changeset_status.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.model.changeset_status
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ :created_on: Apr 30, 2012
+ :author: marcink
+ :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+import logging
+from collections import defaultdict
+
+from rhodecode.model import BaseModel
+from rhodecode.model.db import ChangesetStatus, PullRequest
+from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
+
+log = logging.getLogger(__name__)
+
+
+class ChangesetStatusModel(BaseModel):
+
+ cls = ChangesetStatus
+
+ def __get_changeset_status(self, changeset_status):
+ return self._get_instance(ChangesetStatus, changeset_status)
+
+ def __get_pull_request(self, pull_request):
+ return self._get_instance(PullRequest, pull_request)
+
+ def _get_status_query(self, repo, revision, pull_request,
+ with_revisions=False):
+ repo = self._get_repo(repo)
+
+ q = ChangesetStatus.query()\
+ .filter(ChangesetStatus.repo == repo)
+ if not with_revisions:
+ q = q.filter(ChangesetStatus.version == 0)
+
+ if revision:
+ q = q.filter(ChangesetStatus.revision == revision)
+ elif pull_request:
+ pull_request = self.__get_pull_request(pull_request)
+ q = q.filter(ChangesetStatus.pull_request == pull_request)
+ else:
+ raise Exception('Please specify revision or pull_request')
+ q.order_by(ChangesetStatus.version.asc())
+ return q
+
+ def calculate_status(self, statuses_by_reviewers):
+ """
+ leading one wins, if number of occurences are equal than weaker wins
+
+ :param statuses_by_reviewers:
+ """
+ status = None
+ votes = defaultdict(int)
+ reviewers_number = len(statuses_by_reviewers)
+ for user, statuses in statuses_by_reviewers:
+ if statuses:
+ ver, latest = statuses[0]
+ votes[latest.status] += 1
+ else:
+ votes[ChangesetStatus.DEFAULT] += 1
+
+ if votes.get(ChangesetStatus.STATUS_APPROVED) == reviewers_number:
+ return ChangesetStatus.STATUS_APPROVED
+ else:
+ return ChangesetStatus.STATUS_UNDER_REVIEW
+
+ def get_statuses(self, repo, revision=None, pull_request=None,
+ with_revisions=False):
+ q = self._get_status_query(repo, revision, pull_request,
+ with_revisions)
+ return q.all()
+
+ def get_status(self, repo, revision=None, pull_request=None):
+ """
+ Returns latest status of changeset for given revision or for given
+ pull request. Statuses are versioned inside a table itself and
+ version == 0 is always the current one
+
+ :param repo:
+ :type repo:
+ :param revision: 40char hash or None
+ :type revision: str
+ :param pull_request: pull_request reference
+ :type:
+ """
+ q = self._get_status_query(repo, revision, pull_request)
+
+ # need to use first here since there can be multiple statuses
+ # returned from pull_request
+ status = q.first()
+ status = status.status if status else status
+ st = status or ChangesetStatus.DEFAULT
+ return str(st)
+
+ def set_status(self, repo, status, user, comment, revision=None,
+ pull_request=None, dont_allow_on_closed_pull_request=False):
+ """
+ Creates new status for changeset or updates the old ones bumping their
+ version, leaving the current status at
+
+ :param repo:
+ :type repo:
+ :param revision:
+ :type revision:
+ :param status:
+ :type status:
+ :param user:
+ :type user:
+ :param comment:
+ :type comment:
+ :param dont_allow_on_closed_pull_request: don't allow a status change
+ if last status was for pull request and it's closed. We shouldn't
+ mess around this manually
+ """
+ repo = self._get_repo(repo)
+
+ q = ChangesetStatus.query()
+
+ if revision:
+ q = q.filter(ChangesetStatus.repo == repo)
+ q = q.filter(ChangesetStatus.revision == revision)
+ elif pull_request:
+ pull_request = self.__get_pull_request(pull_request)
+ q = q.filter(ChangesetStatus.repo == pull_request.org_repo)
+ q = q.filter(ChangesetStatus.pull_request == pull_request)
+ cur_statuses = q.all()
+
+ #if statuses exists and last is associated with a closed pull request
+ # we need to check if we can allow this status change
+ if (dont_allow_on_closed_pull_request and cur_statuses
+ and getattr(cur_statuses[0].pull_request, 'status', '')
+ == PullRequest.STATUS_CLOSED):
+ raise StatusChangeOnClosedPullRequestError(
+ 'Changing status on closed pull request is not allowed'
+ )
+
+ if cur_statuses:
+ for st in cur_statuses:
+ st.version += 1
+ self.sa.add(st)
+
+ def _create_status(user, repo, status, comment, revision, pull_request):
+ new_status = ChangesetStatus()
+ new_status.author = self._get_user(user)
+ new_status.repo = self._get_repo(repo)
+ new_status.status = status
+ new_status.comment = comment
+ new_status.revision = revision
+ new_status.pull_request = pull_request
+ return new_status
+
+ if revision:
+ new_status = _create_status(user=user, repo=repo, status=status,
+ comment=comment, revision=revision,
+ pull_request=None)
+ self.sa.add(new_status)
+ return new_status
+ elif pull_request:
+ #pull request can have more than one revision associated to it
+ #we need to create new version for each one
+ new_statuses = []
+ repo = pull_request.org_repo
+ for rev in pull_request.revisions:
+ new_status = _create_status(user=user, repo=repo,
+ status=status, comment=comment,
+ revision=rev,
+ pull_request=pull_request)
+ new_statuses.append(new_status)
+ self.sa.add(new_status)
+ return new_statuses
diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py
index 707345fb..a7520b3a 100644
--- a/rhodecode/model/comment.py
+++ b/rhodecode/model/comment.py
@@ -32,7 +32,8 @@ from sqlalchemy.util.compat import defaultdict
from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
from rhodecode.lib import helpers as h
from rhodecode.model import BaseModel
-from rhodecode.model.db import ChangesetComment, User, Repository, Notification
+from rhodecode.model.db import ChangesetComment, User, Repository, \
+ Notification, PullRequest
from rhodecode.model.notification import NotificationModel
log = logging.getLogger(__name__)
@@ -40,9 +41,14 @@ log = logging.getLogger(__name__)
class ChangesetCommentsModel(BaseModel):
+ cls = ChangesetComment
+
def __get_changeset_comment(self, changeset_comment):
return self._get_instance(ChangesetComment, changeset_comment)
+ def __get_pull_request(self, pull_request):
+ return self._get_instance(PullRequest, pull_request)
+
def _extract_mentions(self, s):
user_objects = []
for username in extract_mentioned_users(s):
@@ -51,41 +57,60 @@ class ChangesetCommentsModel(BaseModel):
user_objects.append(user_obj)
return user_objects
- def create(self, text, repo_id, user_id, revision, f_path=None,
- line_no=None):
+ def create(self, text, repo, user, revision=None, pull_request=None,
+ f_path=None, line_no=None, status_change=None):
"""
- Creates new comment for changeset
+ Creates new comment for changeset or pull request.
+ IF status_change is not none this comment is associated with a
+ status change of changeset or changesets associated with pull request
:param text:
- :param repo_id:
- :param user_id:
+ :param repo:
+ :param user:
:param revision:
+ :param pull_request:
:param f_path:
:param line_no:
+ :param status_change:
"""
-
- if text:
- repo = Repository.get(repo_id)
+ if not text:
+ return
+
+ repo = self._get_repo(repo)
+ user = self._get_user(user)
+ comment = ChangesetComment()
+ comment.repo = repo
+ comment.author = user
+ comment.text = text
+ comment.f_path = f_path
+ comment.line_no = line_no
+
+ if revision:
cs = repo.scm_instance.get_changeset(revision)
desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
author_email = cs.author_email
- comment = ChangesetComment()
- comment.repo = repo
- comment.user_id = user_id
comment.revision = revision
- comment.text = text
- comment.f_path = f_path
- comment.line_no = line_no
-
- self.sa.add(comment)
- self.sa.flush()
- # make notification
- line = ''
+ elif pull_request:
+ pull_request = self.__get_pull_request(pull_request)
+ comment.pull_request = pull_request
+ desc = pull_request.pull_request_id
+ else:
+ raise Exception('Please specify revision or pull_request_id')
+
+ self.sa.add(comment)
+ self.sa.flush()
+
+ # make notification
+ line = ''
+ body = text
+
+ #changeset
+ if revision:
if line_no:
line = _('on line %s') % line_no
subj = safe_unicode(
- h.link_to('Re commit: %(commit_desc)s %(line)s' % \
- {'commit_desc': desc, 'line': line},
+ h.link_to('Re commit: %(desc)s %(line)s' % \
+ {'desc': desc, 'line': line},
h.url('changeset_home', repo_name=repo.repo_name,
revision=revision,
anchor='comment-%s' % comment.comment_id,
@@ -93,31 +118,51 @@ class ChangesetCommentsModel(BaseModel):
)
)
)
-
- body = text
-
+ notification_type = Notification.TYPE_CHANGESET_COMMENT
# get the current participants of this changeset
recipients = ChangesetComment.get_users(revision=revision)
-
# add changeset author if it's in rhodecode system
recipients += [User.get_by_email(author_email)]
+ #pull request
+ elif pull_request:
+ subj = safe_unicode(
+ h.link_to('Re pull request: %(desc)s %(line)s' % \
+ {'desc': desc, 'line': line},
+ h.url('pullrequest_show',
+ repo_name=pull_request.other_repo.repo_name,
+ pull_request_id=pull_request.pull_request_id,
+ anchor='comment-%s' % comment.comment_id,
+ qualified=True,
+ )
+ )
+ )
+ notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
+ # get the current participants of this pull request
+ recipients = ChangesetComment.get_users(pull_request_id=
+ pull_request.pull_request_id)
+ # add pull request author
+ recipients += [pull_request.author]
+
+ # create notification objects, and emails
+ NotificationModel().create(
+ created_by=user, subject=subj, body=body,
+ recipients=recipients, type_=notification_type,
+ email_kwargs={'status_change': status_change}
+ )
+
+ mention_recipients = set(self._extract_mentions(body))\
+ .difference(recipients)
+ if mention_recipients:
+ subj = _('[Mention]') + ' ' + subj
NotificationModel().create(
- created_by=user_id, subject=subj, body=body,
- recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
+ created_by=user, subject=subj, body=body,
+ recipients=mention_recipients,
+ type_=notification_type,
+ email_kwargs={'status_change': status_change}
)
- mention_recipients = set(self._extract_mentions(body))\
- .difference(recipients)
- if mention_recipients:
- subj = _('[Mention]') + ' ' + subj
- NotificationModel().create(
- created_by=user_id, subject=subj, body=body,
- recipients=mention_recipients,
- type_=Notification.TYPE_CHANGESET_COMMENT
- )
-
- return comment
+ return comment
def delete(self, comment):
"""
@@ -130,21 +175,48 @@ class ChangesetCommentsModel(BaseModel):
return comment
- def get_comments(self, repo_id, revision):
- return ChangesetComment.query()\
+ def get_comments(self, repo_id, revision=None, pull_request=None):
+ """
+ Get's main comments based on revision or pull_request_id
+
+ :param repo_id:
+ :type repo_id:
+ :param revision:
+ :type revision:
+ :param pull_request:
+ :type pull_request:
+ """
+
+ q = ChangesetComment.query()\
.filter(ChangesetComment.repo_id == repo_id)\
- .filter(ChangesetComment.revision == revision)\
.filter(ChangesetComment.line_no == None)\
- .filter(ChangesetComment.f_path == None).all()
-
- def get_inline_comments(self, repo_id, revision):
- comments = self.sa.query(ChangesetComment)\
+ .filter(ChangesetComment.f_path == None)
+ if revision:
+ q = q.filter(ChangesetComment.revision == revision)
+ elif pull_request:
+ pull_request = self.__get_pull_request(pull_request)
+ q = q.filter(ChangesetComment.pull_request == pull_request)
+ else:
+ raise Exception('Please specify revision or pull_request')
+ q = q.order_by(ChangesetComment.created_on)
+ return q.all()
+
+ def get_inline_comments(self, repo_id, revision=None, pull_request=None):
+ q = self.sa.query(ChangesetComment)\
.filter(ChangesetComment.repo_id == repo_id)\
- .filter(ChangesetComment.revision == revision)\
.filter(ChangesetComment.line_no != None)\
.filter(ChangesetComment.f_path != None)\
.order_by(ChangesetComment.comment_id.asc())\
- .all()
+
+ if revision:
+ q = q.filter(ChangesetComment.revision == revision)
+ elif pull_request:
+ pull_request = self.__get_pull_request(pull_request)
+ q = q.filter(ChangesetComment.pull_request == pull_request)
+ else:
+ raise Exception('Please specify revision or pull_request_id')
+
+ comments = q.all()
paths = defaultdict(lambda: defaultdict(list))
diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py
index a3078cea..74715c29 100755
--- a/rhodecode/model/db.py
+++ b/rhodecode/model/db.py
@@ -27,12 +27,18 @@ import os
import logging
import datetime
import traceback
+import hashlib
+import time
from collections import defaultdict
from sqlalchemy import *
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
+from sqlalchemy.exc import DatabaseError
from beaker.cache import cache_region, region_invalidate
+from webob.exc import HTTPNotFound
+
+from pylons.i18n.translation import lazy_ugettext as _
from rhodecode.lib.vcs import get_backend
from rhodecode.lib.vcs.utils.helpers import get_scm
@@ -45,9 +51,8 @@ from rhodecode.lib.compat import json
from rhodecode.lib.caching_query import FromCache
from rhodecode.model.meta import Base, Session
-import hashlib
-
+URL_SEP = '/'
log = logging.getLogger(__name__)
#==============================================================================
@@ -57,37 +62,6 @@ log = logging.getLogger(__name__)
_hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
-class ModelSerializer(json.JSONEncoder):
- """
- Simple Serializer for JSON,
-
- usage::
-
- to make object customized for serialization implement a __json__
- method that will return a dict for serialization into json
-
- example::
-
- class Task(object):
-
- def __init__(self, name, value):
- self.name = name
- self.value = value
-
- def __json__(self):
- return dict(name=self.name,
- value=self.value)
-
- """
-
- def default(self, obj):
-
- if hasattr(obj, '__json__'):
- return obj.__json__()
- else:
- return json.JSONEncoder.default(self, obj)
-
-
class BaseModel(object):
"""
Base Model for all classess
@@ -108,8 +82,13 @@ class BaseModel(object):
d[k] = getattr(self, k)
# also use __json__() if present to get additional fields
- for k, val in getattr(self, '__json__', lambda: {})().iteritems():
- d[k] = val
+ _json_attr = getattr(self, '__json__', None)
+ if _json_attr:
+ # update with attributes from __json__
+ if callable(_json_attr):
+ _json_attr = _json_attr()
+ for k, val in _json_attr.iteritems():
+ d[k] = val
return d
def get_appstruct(self):
@@ -130,7 +109,7 @@ class BaseModel(object):
@classmethod
def query(cls):
- return Session.query(cls)
+ return Session().query(cls)
@classmethod
def get(cls, id_):
@@ -138,13 +117,21 @@ class BaseModel(object):
return cls.query().get(id_)
@classmethod
+ def get_or_404(cls, id_):
+ if id_:
+ res = cls.query().get(id_)
+ if not res:
+ raise HTTPNotFound
+ return res
+
+ @classmethod
def getAll(cls):
return cls.query().all()
@classmethod
def delete(cls, id_):
obj = cls.query().get(id_)
- Session.delete(obj)
+ Session().delete(obj)
def __repr__(self):
if hasattr(self, '__unicode__'):
@@ -152,16 +139,17 @@ class BaseModel(object):
return safe_str(self.__unicode__())
return '<DB:%s>' % (self.__class__.__name__)
+
class RhodeCodeSetting(Base, BaseModel):
__tablename__ = 'rhodecode_settings'
__table_args__ = (
UniqueConstraint('app_settings_name'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
def __init__(self, k='', v=''):
self.app_settings_name = k
@@ -195,9 +183,16 @@ class RhodeCodeSetting(Base, BaseModel):
)
@classmethod
- def get_by_name(cls, ldap_key):
+ def get_by_name(cls, key):
return cls.query()\
- .filter(cls.app_settings_name == ldap_key).scalar()
+ .filter(cls.app_settings_name == key).scalar()
+
+ @classmethod
+ def get_by_name_or_create(cls, key):
+ res = cls.get_by_name(key)
+ if not res:
+ res = cls(key)
+ return res
@classmethod
def get_app_settings(cls, cache=False):
@@ -222,7 +217,7 @@ class RhodeCodeSetting(Base, BaseModel):
.filter(cls.app_settings_name.startswith('ldap_')).all()
fd = {}
for row in ret:
- fd.update({row.app_settings_name:row.app_settings_value})
+ fd.update({row.app_settings_name: row.app_settings_value})
return fd
@@ -231,71 +226,82 @@ class RhodeCodeUi(Base, BaseModel):
__tablename__ = 'rhodecode_ui'
__table_args__ = (
UniqueConstraint('ui_key'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
HOOK_UPDATE = 'changegroup.update'
HOOK_REPO_SIZE = 'changegroup.repo_size'
- HOOK_PUSH = 'pretxnchangegroup.push_logger'
- HOOK_PULL = 'preoutgoing.pull_logger'
+ HOOK_PUSH = 'changegroup.push_logger'
+ HOOK_PRE_PUSH = 'prechangegroup.pre_push'
+ HOOK_PULL = 'outgoing.pull_logger'
+ HOOK_PRE_PULL = 'preoutgoing.pre_pull'
ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
@classmethod
def get_by_key(cls, key):
- return cls.query().filter(cls.ui_key == key)
+ return cls.query().filter(cls.ui_key == key).scalar()
@classmethod
def get_builtin_hooks(cls):
q = cls.query()
- q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
- cls.HOOK_REPO_SIZE,
- cls.HOOK_PUSH, cls.HOOK_PULL]))
+ q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
+ cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
+ cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
return q.all()
@classmethod
def get_custom_hooks(cls):
q = cls.query()
- q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
- cls.HOOK_REPO_SIZE,
- cls.HOOK_PUSH, cls.HOOK_PULL]))
+ q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
+ cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
+ cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
q = q.filter(cls.ui_section == 'hooks')
return q.all()
@classmethod
+ def get_repos_location(cls):
+ return cls.get_by_key('/').ui_value
+
+ @classmethod
def create_or_update_hook(cls, key, val):
- new_ui = cls.get_by_key(key).scalar() or cls()
+ new_ui = cls.get_by_key(key) or cls()
new_ui.ui_section = 'hooks'
new_ui.ui_active = True
new_ui.ui_key = key
new_ui.ui_value = val
- Session.add(new_ui)
+ Session().add(new_ui)
class User(Base, BaseModel):
__tablename__ = 'users'
__table_args__ = (
UniqueConstraint('username'), UniqueConstraint('email'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ Index('u_username_idx', 'username'),
+ Index('u_email_idx', 'email'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
+ DEFAULT_USER = 'default'
+
user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- active = Column("active", Boolean(), nullable=True, unique=None, default=None)
+ username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ active = Column("active", Boolean(), nullable=True, unique=None, default=True)
admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
- name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
- ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
user_log = relationship('UserLog', cascade='all')
user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
@@ -312,6 +318,8 @@ class User(Base, BaseModel):
user_created_notifications = relationship('Notification', cascade='all')
# comments created by this user
user_comments = relationship('ChangesetComment', cascade='all')
+ #extra emails for this user
+ user_emails = relationship('UserEmailMap', cascade='all')
@hybrid_property
def email(self):
@@ -322,21 +330,35 @@ class User(Base, BaseModel):
self._email = val.lower() if val else None
@property
+ def firstname(self):
+ # alias for future
+ return self.name
+
+ @property
+ def emails(self):
+ other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
+ return [self.email] + [x.email for x in other]
+
+ @property
+ def username_and_name(self):
+ return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
+
+ @property
def full_name(self):
- return '%s %s' % (self.name, self.lastname)
+ return '%s %s' % (self.firstname, self.lastname)
@property
def full_name_or_username(self):
- return ('%s %s' % (self.name, self.lastname)
- if (self.name and self.lastname) else self.username)
+ return ('%s %s' % (self.firstname, self.lastname)
+ if (self.firstname and self.lastname) else self.username)
@property
def full_contact(self):
- return '%s %s <%s>' % (self.name, self.lastname, self.email)
+ return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
@property
def short_contact(self):
- return '%s %s' % (self.name, self.lastname)
+ return '%s %s' % (self.firstname, self.lastname)
@property
def is_admin(self):
@@ -379,40 +401,105 @@ class User(Base, BaseModel):
if cache:
q = q.options(FromCache("sql_cache_short",
- "get_api_key_%s" % email))
- return q.scalar()
+ "get_email_key_%s" % email))
+
+ ret = q.scalar()
+ if ret is None:
+ q = UserEmailMap.query()
+ # try fetching in alternate email map
+ if case_insensitive:
+ q = q.filter(UserEmailMap.email.ilike(email))
+ else:
+ q = q.filter(UserEmailMap.email == email)
+ q = q.options(joinedload(UserEmailMap.user))
+ if cache:
+ q = q.options(FromCache("sql_cache_short",
+ "get_email_map_key_%s" % email))
+ ret = getattr(q.scalar(), 'user', None)
+
+ return ret
def update_lastlogin(self):
"""Update user lastlogin"""
self.last_login = datetime.datetime.now()
- Session.add(self)
+ Session().add(self)
log.debug('updated user %s lastlogin' % self.username)
+ def get_api_data(self):
+ """
+ Common function for generating user related data for API
+ """
+ user = self
+ data = dict(
+ user_id=user.user_id,
+ username=user.username,
+ firstname=user.name,
+ lastname=user.lastname,
+ email=user.email,
+ emails=user.emails,
+ api_key=user.api_key,
+ active=user.active,
+ admin=user.admin,
+ ldap_dn=user.ldap_dn,
+ last_login=user.last_login,
+ )
+ return data
+
def __json__(self):
- return dict(
- user_id=self.user_id,
- first_name=self.name,
- last_name=self.lastname,
- email=self.email,
+ data = dict(
full_name=self.full_name,
full_name_or_username=self.full_name_or_username,
short_contact=self.short_contact,
full_contact=self.full_contact
)
+ data.update(self.get_api_data())
+ return data
+
+
+class UserEmailMap(Base, BaseModel):
+ __tablename__ = 'user_email_map'
+ __table_args__ = (
+ Index('uem_email_idx', 'email'),
+ UniqueConstraint('email'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ __mapper_args__ = {}
+
+ email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
+ _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
+ user = relationship('User', lazy='joined')
+
+ @validates('_email')
+ def validate_email(self, key, email):
+ # check if this email is not main one
+ main_email = Session().query(User).filter(User.email == email).scalar()
+ if main_email is not None:
+ raise AttributeError('email %s is present is user table' % email)
+ return email
+
+ @hybrid_property
+ def email(self):
+ return self._email
+
+ @email.setter
+ def email(self, val):
+ self._email = val.lower() if val else None
class UserLog(Base, BaseModel):
__tablename__ = 'user_logs'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
- repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
@property
@@ -426,13 +513,14 @@ class UserLog(Base, BaseModel):
class UsersGroup(Base, BaseModel):
__tablename__ = 'users_groups'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+ users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
+ inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
@@ -464,11 +552,22 @@ class UsersGroup(Base, BaseModel):
"get_users_group_%s" % users_group_id))
return users_group.get(users_group_id)
+ def get_api_data(self):
+ users_group = self
+
+ data = dict(
+ users_group_id=users_group.users_group_id,
+ group_name=users_group.users_group_name,
+ active=users_group.users_group_active,
+ )
+
+ return data
+
class UsersGroupMember(Base, BaseModel):
__tablename__ = 'users_groups_members'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
@@ -488,20 +587,24 @@ class Repository(Base, BaseModel):
__tablename__ = 'repositories'
__table_args__ = (
UniqueConstraint('repo_name'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ Index('r_repo_name_idx', 'repo_name'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
- clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
- repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
+ repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+ clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
+ repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
private = Column("private", Boolean(), nullable=True, unique=None, default=None)
enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
- description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
+ landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
+ enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
+ _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
@@ -513,27 +616,58 @@ class Repository(Base, BaseModel):
users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
stats = relationship('Statistics', cascade='all', uselist=False)
- followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
+ followers = relationship('UserFollowing',
+ primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
+ cascade='all')
logs = relationship('UserLog')
+ comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
+
+ pull_requests_org = relationship('PullRequest',
+ primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
+ cascade="all, delete, delete-orphan")
+
+ pull_requests_other = relationship('PullRequest',
+ primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
+ cascade="all, delete, delete-orphan")
def __unicode__(self):
- return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
+ return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
self.repo_name)
+ @hybrid_property
+ def locked(self):
+ # always should return [user_id, timelocked]
+ if self._locked:
+ _lock_info = self._locked.split(':')
+ return int(_lock_info[0]), _lock_info[1]
+ return [None, None]
+
+ @locked.setter
+ def locked(self, val):
+ if val and isinstance(val, (list, tuple)):
+ self._locked = ':'.join(map(str, val))
+ else:
+ self._locked = None
+
@classmethod
def url_sep(cls):
- return '/'
+ return URL_SEP
@classmethod
def get_by_repo_name(cls, repo_name):
- q = Session.query(cls).filter(cls.repo_name == repo_name)
+ q = Session().query(cls).filter(cls.repo_name == repo_name)
q = q.options(joinedload(Repository.fork))\
.options(joinedload(Repository.user))\
.options(joinedload(Repository.group))
return q.scalar()
@classmethod
+ def get_by_full_path(cls, repo_full_path):
+ repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
+ return cls.get_by_repo_name(repo_name.strip(URL_SEP))
+
+ @classmethod
def get_repo_forks(cls, repo_id):
return cls.query().filter(Repository.fork_id == repo_id)
@@ -544,12 +678,26 @@ class Repository(Base, BaseModel):
:param cls:
"""
- q = Session.query(RhodeCodeUi)\
+ q = Session().query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == cls.url_sep())
q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
return q.one().ui_value
@property
+ def forks(self):
+ """
+ Return forks of this repo
+ """
+ return Repository.get_repo_forks(self.repo_id)
+
+ @property
+ def parent(self):
+ """
+ Returns fork parent
+ """
+ return self.fork
+
+ @property
def just_name(self):
return self.repo_name.split(Repository.url_sep())[-1]
@@ -580,7 +728,7 @@ class Repository(Base, BaseModel):
Returns base full path for that repository means where it actually
exists on a filesystem
"""
- q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
+ q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
Repository.url_sep())
q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
return q.one().ui_value
@@ -626,10 +774,26 @@ class Repository(Base, BaseModel):
log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
ui_.ui_key, ui_.ui_value)
baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
+ if ui_.ui_key == 'push_ssl':
+ # force set push_ssl requirement to False, rhodecode
+ # handles that
+ baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
return baseui
@classmethod
+ def inject_ui(cls, repo, extras={}):
+ from rhodecode.lib.vcs.backends.hg import MercurialRepository
+ from rhodecode.lib.vcs.backends.git import GitRepository
+ required = (MercurialRepository, GitRepository)
+ if not isinstance(repo, required):
+ raise Exception('repo must be instance of %s' % required)
+
+ # inject ui extra param to log this action via push logger
+ for k, v in extras.items():
+ repo._repo.ui.setconfig('rhodecode_extras', k, v)
+
+ @classmethod
def is_valid(cls, repo_name):
"""
returns True if given repo name is a valid filesystem repository
@@ -641,6 +805,39 @@ class Repository(Base, BaseModel):
return is_valid_repo(repo_name, cls.base_path())
+ def get_api_data(self):
+ """
+ Common function for generating repo api data
+
+ """
+ repo = self
+ data = dict(
+ repo_id=repo.repo_id,
+ repo_name=repo.repo_name,
+ repo_type=repo.repo_type,
+ clone_uri=repo.clone_uri,
+ private=repo.private,
+ created_on=repo.created_on,
+ description=repo.description,
+ landing_rev=repo.landing_rev,
+ owner=repo.user.username,
+ fork_of=repo.fork.repo_name if repo.fork else None
+ )
+
+ return data
+
+ @classmethod
+ def lock(cls, repo, user_id):
+ repo.locked = [user_id, time.time()]
+ Session().add(repo)
+ Session().commit()
+
+ @classmethod
+ def unlock(cls, repo):
+ repo.locked = None
+ Session().add(repo)
+ Session().commit()
+
#==========================================================================
# SCM PROPERTIES
#==========================================================================
@@ -648,6 +845,13 @@ class Repository(Base, BaseModel):
def get_changeset(self, rev=None):
return get_changeset_safe(self.scm_instance, rev)
+ def get_landing_changeset(self):
+ """
+ Returns landing changeset, or if that doesn't exist returns the tip
+ """
+ cs = self.get_changeset(self.landing_rev) or self.get_changeset()
+ return cs
+
@property
def tip(self):
return self.get_changeset('tip')
@@ -660,7 +864,7 @@ class Repository(Base, BaseModel):
def last_change(self):
return self.scm_instance.last_change
- def comments(self, revisions=None):
+ def get_comments(self, revisions=None):
"""
Returns comments for this repository grouped by revisions
@@ -675,6 +879,39 @@ class Repository(Base, BaseModel):
grouped[cmt.revision].append(cmt)
return grouped
+ def statuses(self, revisions=None):
+ """
+ Returns statuses for this repository
+
+ :param revisions: list of revisions to get statuses for
+ :type revisions: list
+ """
+
+ statuses = ChangesetStatus.query()\
+ .filter(ChangesetStatus.repo == self)\
+ .filter(ChangesetStatus.version == 0)
+ if revisions:
+ statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
+ grouped = {}
+
+ #maybe we have open new pullrequest without a status ?
+ stat = ChangesetStatus.STATUS_UNDER_REVIEW
+ status_lbl = ChangesetStatus.get_status_lbl(stat)
+ for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
+ for rev in pr.revisions:
+ pr_id = pr.pull_request_id
+ pr_repo = pr.other_repo.repo_name
+ grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
+
+ for stat in statuses.all():
+ pr_id = pr_repo = None
+ if stat.pull_request:
+ pr_id = stat.pull_request.pull_request_id
+ pr_repo = stat.pull_request.other_repo.repo_name
+ grouped[stat.revision] = [str(stat.status), stat.status_lbl,
+ pr_id, pr_repo]
+ return grouped
+
#==========================================================================
# SCM CACHE INSTANCE
#==========================================================================
@@ -693,18 +930,27 @@ class Repository(Base, BaseModel):
def scm_instance(self):
return self.__get_instance()
- @property
- def scm_instance_cached(self):
+ def scm_instance_cached(self, cache_map=None):
@cache_region('long_term')
def _c(repo_name):
return self.__get_instance()
rn = self.repo_name
log.debug('Getting cached instance of repo')
- inv = self.invalidate
- if inv is not None:
+
+ if cache_map:
+ # get using prefilled cache_map
+ invalidate_repo = cache_map[self.repo_name]
+ if invalidate_repo:
+ invalidate_repo = (None if invalidate_repo.cache_active
+ else invalidate_repo)
+ else:
+ # get from invalidate
+ invalidate_repo = self.invalidate
+
+ if invalidate_repo is not None:
region_invalidate(_c, None, rn)
# update our cache
- CacheInvalidation.set_valid(inv.cache_key)
+ CacheInvalidation.set_valid(invalidate_repo.cache_key)
return _c(rn)
def __get_instance(self):
@@ -738,15 +984,16 @@ class RepoGroup(Base, BaseModel):
__table_args__ = (
UniqueConstraint('group_name', 'group_parent_id'),
CheckConstraint('group_id != group_parent_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
__mapper_args__ = {'order_by': 'group_name'}
group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+ group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
- group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
@@ -776,7 +1023,7 @@ class RepoGroup(Base, BaseModel):
@classmethod
def url_sep(cls):
- return '/'
+ return URL_SEP
@classmethod
def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
@@ -853,6 +1100,24 @@ class RepoGroup(Base, BaseModel):
return cnt + children_count(self)
+ def recursive_groups_and_repos(self):
+ """
+ Recursive return all groups, with repositories in those groups
+ """
+ all_ = []
+
+ def _get_members(root_gr):
+ for r in root_gr.repositories:
+ all_.append(r)
+ childs = root_gr.children.all()
+ if childs:
+ for gr in childs:
+ all_.append(gr)
+ _get_members(gr)
+
+ _get_members(self)
+ return [self] + all_
+
def get_new_name(self, group_name):
"""
returns new full group name based on parent and new name
@@ -867,12 +1132,55 @@ class RepoGroup(Base, BaseModel):
class Permission(Base, BaseModel):
__tablename__ = 'permissions'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ Index('p_perm_name_idx', 'permission_name'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
+ PERMS = [
+ ('repository.none', _('Repository no access')),
+ ('repository.read', _('Repository read access')),
+ ('repository.write', _('Repository write access')),
+ ('repository.admin', _('Repository admin access')),
+
+ ('group.none', _('Repositories Group no access')),
+ ('group.read', _('Repositories Group read access')),
+ ('group.write', _('Repositories Group write access')),
+ ('group.admin', _('Repositories Group admin access')),
+
+ ('hg.admin', _('RhodeCode Administrator')),
+ ('hg.create.none', _('Repository creation disabled')),
+ ('hg.create.repository', _('Repository creation enabled')),
+ ('hg.fork.none', _('Repository forking disabled')),
+ ('hg.fork.repository', _('Repository forking enabled')),
+ ('hg.register.none', _('Register disabled')),
+ ('hg.register.manual_activate', _('Register new user with RhodeCode '
+ 'with manual activation')),
+
+ ('hg.register.auto_activate', _('Register new user with RhodeCode '
+ 'with auto activation')),
+ ]
+
+ # defines which permissions are more important higher the more important
+ PERM_WEIGHTS = {
+ 'repository.none': 0,
+ 'repository.read': 1,
+ 'repository.write': 3,
+ 'repository.admin': 4,
+
+ 'group.none': 0,
+ 'group.read': 1,
+ 'group.write': 3,
+ 'group.admin': 4,
+
+ 'hg.fork.none': 0,
+ 'hg.fork.repository': 1,
+ 'hg.create.none': 0,
+ 'hg.create.repository':1
+ }
+
permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
def __unicode__(self):
return u"<%s('%s:%s')>" % (
@@ -885,7 +1193,7 @@ class Permission(Base, BaseModel):
@classmethod
def get_default_perms(cls, default_user_id):
- q = Session.query(UserRepoToPerm, Repository, cls)\
+ q = Session().query(UserRepoToPerm, Repository, cls)\
.join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
.join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
.filter(UserRepoToPerm.user_id == default_user_id)
@@ -894,7 +1202,7 @@ class Permission(Base, BaseModel):
@classmethod
def get_default_group_perms(cls, default_user_id):
- q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
+ q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
.join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
.join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
.filter(UserRepoGroupToPerm.user_id == default_user_id)
@@ -906,7 +1214,7 @@ class UserRepoToPerm(Base, BaseModel):
__tablename__ = 'repo_to_perm'
__table_args__ = (
UniqueConstraint('user_id', 'repository_id', 'permission_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -924,7 +1232,7 @@ class UserRepoToPerm(Base, BaseModel):
n.user = user
n.repository = repository
n.permission = permission
- Session.add(n)
+ Session().add(n)
return n
def __unicode__(self):
@@ -935,7 +1243,7 @@ class UserToPerm(Base, BaseModel):
__tablename__ = 'user_to_perm'
__table_args__ = (
UniqueConstraint('user_id', 'permission_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -950,7 +1258,7 @@ class UsersGroupRepoToPerm(Base, BaseModel):
__tablename__ = 'users_group_repo_to_perm'
__table_args__ = (
UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -968,7 +1276,7 @@ class UsersGroupRepoToPerm(Base, BaseModel):
n.users_group = users_group
n.repository = repository
n.permission = permission
- Session.add(n)
+ Session().add(n)
return n
def __unicode__(self):
@@ -979,7 +1287,7 @@ class UsersGroupToPerm(Base, BaseModel):
__tablename__ = 'users_group_to_perm'
__table_args__ = (
UniqueConstraint('users_group_id', 'permission_id',),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -994,7 +1302,7 @@ class UserRepoGroupToPerm(Base, BaseModel):
__tablename__ = 'user_repo_group_to_perm'
__table_args__ = (
UniqueConstraint('user_id', 'group_id', 'permission_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
@@ -1012,7 +1320,7 @@ class UsersGroupRepoGroupToPerm(Base, BaseModel):
__tablename__ = 'users_group_repo_group_to_perm'
__table_args__ = (
UniqueConstraint('users_group_id', 'group_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
@@ -1030,7 +1338,7 @@ class Statistics(Base, BaseModel):
__tablename__ = 'statistics'
__table_args__ = (
UniqueConstraint('repository_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -1048,7 +1356,7 @@ class UserFollowing(Base, BaseModel):
__table_args__ = (
UniqueConstraint('user_id', 'follows_repository_id'),
UniqueConstraint('user_id', 'follows_user_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
@@ -1072,12 +1380,13 @@ class CacheInvalidation(Base, BaseModel):
__tablename__ = 'cache_invalidation'
__table_args__ = (
UniqueConstraint('cache_key'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ Index('key_idx', 'cache_key'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
- cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
- cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+ cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
def __init__(self, cache_key, cache_args=''):
@@ -1088,6 +1397,7 @@ class CacheInvalidation(Base, BaseModel):
def __unicode__(self):
return u"<%s('%s:%s')>" % (self.__class__.__name__,
self.cache_id, self.cache_key)
+
@classmethod
def clear_cache(cls):
cls.query().delete()
@@ -1112,15 +1422,15 @@ class CacheInvalidation(Base, BaseModel):
@classmethod
def _get_or_create_key(cls, key, prefix, org_key):
- inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
+ inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
if not inv_obj:
try:
inv_obj = CacheInvalidation(key, org_key)
- Session.add(inv_obj)
- Session.commit()
+ Session().add(inv_obj)
+ Session().commit()
except Exception:
log.error(traceback.format_exc())
- Session.rollback()
+ Session().rollback()
return inv_obj
@classmethod
@@ -1148,7 +1458,7 @@ class CacheInvalidation(Base, BaseModel):
"""
key, _prefix, _org_key = cls._get_key(key)
- inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
+ inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
_org_key))
try:
@@ -1156,11 +1466,11 @@ class CacheInvalidation(Base, BaseModel):
if inv_obj:
inv_obj.cache_active = False
- Session.add(inv_obj)
- Session.commit()
+ Session().add(inv_obj)
+ Session().commit()
except Exception:
log.error(traceback.format_exc())
- Session.rollback()
+ Session().rollback()
@classmethod
def set_valid(cls, key):
@@ -1171,46 +1481,211 @@ class CacheInvalidation(Base, BaseModel):
"""
inv_obj = cls.get_by_key(key)
inv_obj.cache_active = True
- Session.add(inv_obj)
- Session.commit()
+ Session().add(inv_obj)
+ Session().commit()
+
+ @classmethod
+ def get_cache_map(cls):
+
+ class cachemapdict(dict):
+
+ def __init__(self, *args, **kwargs):
+ fixkey = kwargs.get('fixkey')
+ if fixkey:
+ del kwargs['fixkey']
+ self.fixkey = fixkey
+ super(cachemapdict, self).__init__(*args, **kwargs)
+
+ def __getattr__(self, name):
+ key = name
+ if self.fixkey:
+ key, _prefix, _org_key = cls._get_key(key)
+ if key in self.__dict__:
+ return self.__dict__[key]
+ else:
+ return self[key]
+
+ def __getitem__(self, key):
+ if self.fixkey:
+ key, _prefix, _org_key = cls._get_key(key)
+ try:
+ return super(cachemapdict, self).__getitem__(key)
+ except KeyError:
+ return
+
+ cache_map = cachemapdict(fixkey=True)
+ for obj in cls.query().all():
+ cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
+ return cache_map
class ChangesetComment(Base, BaseModel):
__tablename__ = 'changeset_comments'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ Index('cc_revision_idx', 'revision'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
- revision = Column('revision', String(40), nullable=False)
+ revision = Column('revision', String(40), nullable=True)
+ pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
line_no = Column('line_no', Unicode(10), nullable=True)
+ hl_lines = Column('hl_lines', Unicode(512), nullable=True)
f_path = Column('f_path', Unicode(1000), nullable=True)
user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
text = Column('text', Unicode(25000), nullable=False)
- modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
+ created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+ modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
author = relationship('User', lazy='joined')
repo = relationship('Repository')
+ status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
+ pull_request = relationship('PullRequest', lazy='joined')
@classmethod
- def get_users(cls, revision):
+ def get_users(cls, revision=None, pull_request_id=None):
"""
- Returns user associated with this changesetComment. ie those
+ Returns user associated with this ChangesetComment. ie those
who actually commented
:param cls:
:param revision:
"""
- return Session.query(User)\
- .filter(cls.revision == revision)\
- .join(ChangesetComment.author).all()
+ q = Session().query(User)\
+ .join(ChangesetComment.author)
+ if revision:
+ q = q.filter(cls.revision == revision)
+ elif pull_request_id:
+ q = q.filter(cls.pull_request_id == pull_request_id)
+ return q.all()
+
+
+class ChangesetStatus(Base, BaseModel):
+ __tablename__ = 'changeset_statuses'
+ __table_args__ = (
+ Index('cs_revision_idx', 'revision'),
+ Index('cs_version_idx', 'version'),
+ UniqueConstraint('repo_id', 'revision', 'version'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
+ 'mysql_charset': 'utf8'}
+ )
+ STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
+ STATUS_APPROVED = 'approved'
+ STATUS_REJECTED = 'rejected'
+ STATUS_UNDER_REVIEW = 'under_review'
+
+ STATUSES = [
+ (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
+ (STATUS_APPROVED, _("Approved")),
+ (STATUS_REJECTED, _("Rejected")),
+ (STATUS_UNDER_REVIEW, _("Under Review")),
+ ]
+
+ changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
+ repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
+ revision = Column('revision', String(40), nullable=False)
+ status = Column('status', String(128), nullable=False, default=DEFAULT)
+ changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
+ modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
+ version = Column('version', Integer(), nullable=False, default=0)
+ pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
+
+ author = relationship('User', lazy='joined')
+ repo = relationship('Repository')
+ comment = relationship('ChangesetComment', lazy='joined')
+ pull_request = relationship('PullRequest', lazy='joined')
+
+ def __unicode__(self):
+ return u"<%s('%s:%s')>" % (
+ self.__class__.__name__,
+ self.status, self.author
+ )
+
+ @classmethod
+ def get_status_lbl(cls, value):
+ return dict(cls.STATUSES).get(value)
+
+ @property
+ def status_lbl(self):
+ return ChangesetStatus.get_status_lbl(self.status)
+
+
+class PullRequest(Base, BaseModel):
+ __tablename__ = 'pull_requests'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ STATUS_NEW = u'new'
+ STATUS_OPEN = u'open'
+ STATUS_CLOSED = u'closed'
+
+ pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
+ title = Column('title', Unicode(256), nullable=True)
+ description = Column('description', UnicodeText(10240), nullable=True)
+ status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
+ created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+ updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
+ _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
+ org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+ org_ref = Column('org_ref', Unicode(256), nullable=False)
+ other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+ other_ref = Column('other_ref', Unicode(256), nullable=False)
+
+ @hybrid_property
+ def revisions(self):
+ return self._revisions.split(':')
+
+ @revisions.setter
+ def revisions(self, val):
+ self._revisions = ':'.join(val)
+
+ author = relationship('User', lazy='joined')
+ reviewers = relationship('PullRequestReviewers',
+ cascade="all, delete, delete-orphan")
+ org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
+ other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
+ statuses = relationship('ChangesetStatus')
+ comments = relationship('ChangesetComment',
+ cascade="all, delete, delete-orphan")
+
+ def is_closed(self):
+ return self.status == self.STATUS_CLOSED
+
+ def __json__(self):
+ return dict(
+ revisions=self.revisions
+ )
+
+
+class PullRequestReviewers(Base, BaseModel):
+ __tablename__ = 'pull_request_reviewers'
+ __table_args__ = (
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
+ 'mysql_charset': 'utf8'},
+ )
+
+ def __init__(self, user=None, pull_request=None):
+ self.user = user
+ self.pull_request = pull_request
+
+ pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
+ pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
+ user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
+
+ user = relationship('User')
+ pull_request = relationship('PullRequest')
class Notification(Base, BaseModel):
__tablename__ = 'notifications'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ Index('notification_type_idx', 'type'),
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
@@ -1218,10 +1693,12 @@ class Notification(Base, BaseModel):
TYPE_MESSAGE = u'message'
TYPE_MENTION = u'mention'
TYPE_REGISTRATION = u'registration'
+ TYPE_PULL_REQUEST = u'pull_request'
+ TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
subject = Column('subject', Unicode(512), nullable=True)
- body = Column('body', Unicode(50000), nullable=True)
+ body = Column('body', UnicodeText(50000), nullable=True)
created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
type_ = Column('type', Unicode(256))
@@ -1234,7 +1711,7 @@ class Notification(Base, BaseModel):
def recipients(self):
return [x.user for x in UserNotification.query()\
.filter(UserNotification.notification == self)\
- .order_by(UserNotification.user).all()]
+ .order_by(UserNotification.user_id.asc()).all()]
@classmethod
def create(cls, created_by, subject, body, recipients, type_=None):
@@ -1252,7 +1729,7 @@ class Notification(Base, BaseModel):
assoc = UserNotification()
assoc.notification = notification
u.notifications.append(assoc)
- Session.add(notification)
+ Session().add(notification)
return notification
@property
@@ -1265,7 +1742,7 @@ class UserNotification(Base, BaseModel):
__tablename__ = 'user_to_notification'
__table_args__ = (
UniqueConstraint('user_id', 'notification_id'),
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'}
)
user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
@@ -1279,13 +1756,13 @@ class UserNotification(Base, BaseModel):
def mark_as_read(self):
self.read = True
- Session.add(self)
+ Session().add(self)
class DbMigrateVersion(Base, BaseModel):
__tablename__ = 'db_migrate_version'
__table_args__ = (
- {'extend_existing': True, 'mysql_engine':'InnoDB',
+ {'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'},
)
repository_id = Column('repository_id', String(250), primary_key=True)
diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py
index 7b399405..def1239d 100644
--- a/rhodecode/model/forms.py
+++ b/rhodecode/model/forms.py
@@ -19,573 +19,77 @@ list=[1,2,3,4,5]
for SELECT use formencode.All(OneOf(list), Int())
"""
-import os
-import re
import logging
-import traceback
import formencode
from formencode import All
-from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
- Email, Bool, StringBoolean, Set
from pylons.i18n.translation import _
-from webhelpers.pylonslib.secure_form import authentication_token
-from rhodecode.config.routing import ADMIN_PREFIX
-from rhodecode.lib.utils import repo_name_slug
-from rhodecode.lib.auth import authenticate, get_crypt_password
-from rhodecode.lib.exceptions import LdapImportError
-from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
+from rhodecode.model import validators as v
from rhodecode import BACKENDS
log = logging.getLogger(__name__)
-#this is needed to translate the messages using _() in validators
-class State_obj(object):
- _ = staticmethod(_)
-
-
-#==============================================================================
-# VALIDATORS
-#==============================================================================
-class ValidAuthToken(formencode.validators.FancyValidator):
- messages = {'invalid_token': _('Token mismatch')}
-
- def validate_python(self, value, state):
-
- if value != authentication_token():
- raise formencode.Invalid(
- self.message('invalid_token',
- state, search_number=value),
- value,
- state
- )
-
-
-def ValidUsername(edit, old_data):
- class _ValidUsername(formencode.validators.FancyValidator):
-
- def validate_python(self, value, state):
- if value in ['default', 'new_user']:
- raise formencode.Invalid(_('Invalid username'), value, state)
- #check if user is unique
- old_un = None
- if edit:
- old_un = User.get(old_data.get('user_id')).username
-
- if old_un != value or not edit:
- if User.get_by_username(value, case_insensitive=True):
- raise formencode.Invalid(_('This username already '
- 'exists') , value, state)
-
- if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
- raise formencode.Invalid(
- _('Username may only contain alphanumeric characters '
- 'underscores, periods or dashes and must begin with '
- 'alphanumeric character'),
- value,
- state
- )
-
- return _ValidUsername
-
-
-def ValidUsersGroup(edit, old_data):
-
- class _ValidUsersGroup(formencode.validators.FancyValidator):
-
- def validate_python(self, value, state):
- if value in ['default']:
- raise formencode.Invalid(_('Invalid group name'), value, state)
- #check if group is unique
- old_ugname = None
- if edit:
- old_ugname = UsersGroup.get(
- old_data.get('users_group_id')).users_group_name
-
- if old_ugname != value or not edit:
- if UsersGroup.get_by_group_name(value, cache=False,
- case_insensitive=True):
- raise formencode.Invalid(_('This users group '
- 'already exists'), value,
- state)
-
- if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
- raise formencode.Invalid(
- _('RepoGroup name may only contain alphanumeric characters '
- 'underscores, periods or dashes and must begin with '
- 'alphanumeric character'),
- value,
- state
- )
-
- return _ValidUsersGroup
-
-
-def ValidReposGroup(edit, old_data):
- class _ValidReposGroup(formencode.validators.FancyValidator):
-
- def validate_python(self, value, state):
- # TODO WRITE VALIDATIONS
- group_name = value.get('group_name')
- group_parent_id = value.get('group_parent_id')
-
- # slugify repo group just in case :)
- slug = repo_name_slug(group_name)
-
- # check for parent of self
- parent_of_self = lambda: (
- old_data['group_id'] == int(group_parent_id)
- if group_parent_id else False
- )
- if edit and parent_of_self():
- e_dict = {
- 'group_parent_id': _('Cannot assign this group as parent')
- }
- raise formencode.Invalid('', value, state,
- error_dict=e_dict)
-
- old_gname = None
- if edit:
- old_gname = RepoGroup.get(old_data.get('group_id')).group_name
-
- if old_gname != group_name or not edit:
-
- # check group
- gr = RepoGroup.query()\
- .filter(RepoGroup.group_name == slug)\
- .filter(RepoGroup.group_parent_id == group_parent_id)\
- .scalar()
-
- if gr:
- e_dict = {
- 'group_name': _('This group already exists')
- }
- raise formencode.Invalid('', value, state,
- error_dict=e_dict)
-
- # check for same repo
- repo = Repository.query()\
- .filter(Repository.repo_name == slug)\
- .scalar()
-
- if repo:
- e_dict = {
- 'group_name': _('Repository with this name already exists')
- }
- raise formencode.Invalid('', value, state,
- error_dict=e_dict)
-
- return _ValidReposGroup
-
-
-class ValidPassword(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
-
- if not value:
- return
-
- if value.get('password'):
- try:
- value['password'] = get_crypt_password(value['password'])
- except UnicodeEncodeError:
- e_dict = {'password': _('Invalid characters in password')}
- raise formencode.Invalid('', value, state, error_dict=e_dict)
-
- if value.get('password_confirmation'):
- try:
- value['password_confirmation'] = \
- get_crypt_password(value['password_confirmation'])
- except UnicodeEncodeError:
- e_dict = {
- 'password_confirmation': _('Invalid characters in password')
- }
- raise formencode.Invalid('', value, state, error_dict=e_dict)
-
- if value.get('new_password'):
- try:
- value['new_password'] = \
- get_crypt_password(value['new_password'])
- except UnicodeEncodeError:
- e_dict = {'new_password': _('Invalid characters in password')}
- raise formencode.Invalid('', value, state, error_dict=e_dict)
-
- return value
-
-
-class ValidPasswordsMatch(formencode.validators.FancyValidator):
-
- def validate_python(self, value, state):
-
- pass_val = value.get('password') or value.get('new_password')
- if pass_val != value['password_confirmation']:
- e_dict = {'password_confirmation':
- _('Passwords do not match')}
- raise formencode.Invalid('', value, state, error_dict=e_dict)
-
-
-class ValidAuth(formencode.validators.FancyValidator):
- messages = {
- 'invalid_password':_('invalid password'),
- 'invalid_login':_('invalid user name'),
- 'disabled_account':_('Your account is disabled')
- }
-
- # error mapping
- e_dict = {'username': messages['invalid_login'],
- 'password': messages['invalid_password']}
- e_dict_disable = {'username': messages['disabled_account']}
-
- def validate_python(self, value, state):
- password = value['password']
- username = value['username']
- user = User.get_by_username(username)
-
- if authenticate(username, password):
- return value
- else:
- if user and user.active is False:
- log.warning('user %s is disabled' % username)
- raise formencode.Invalid(
- self.message('disabled_account',
- state=State_obj),
- value, state,
- error_dict=self.e_dict_disable
- )
- else:
- log.warning('user %s failed to authenticate' % username)
- raise formencode.Invalid(
- self.message('invalid_password',
- state=State_obj), value, state,
- error_dict=self.e_dict
- )
-
-
-class ValidRepoUser(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
- try:
- User.query().filter(User.active == True)\
- .filter(User.username == value).one()
- except Exception:
- raise formencode.Invalid(_('This username is not valid'),
- value, state)
- return value
-
-
-def ValidRepoName(edit, old_data):
- class _ValidRepoName(formencode.validators.FancyValidator):
- def to_python(self, value, state):
-
- repo_name = value.get('repo_name')
-
- slug = repo_name_slug(repo_name)
- if slug in [ADMIN_PREFIX, '']:
- e_dict = {'repo_name': _('This repository name is disallowed')}
- raise formencode.Invalid('', value, state, error_dict=e_dict)
-
- if value.get('repo_group'):
- gr = RepoGroup.get(value.get('repo_group'))
- group_path = gr.full_path
- # value needs to be aware of group name in order to check
- # db key This is an actual just the name to store in the
- # database
- repo_name_full = group_path + RepoGroup.url_sep() + repo_name
-
- else:
- group_path = ''
- repo_name_full = repo_name
-
- value['repo_name_full'] = repo_name_full
- rename = old_data.get('repo_name') != repo_name_full
- create = not edit
- if rename or create:
-
- if group_path != '':
- if Repository.get_by_repo_name(repo_name_full):
- e_dict = {
- 'repo_name': _('This repository already exists in '
- 'a group "%s"') % gr.group_name
- }
- raise formencode.Invalid('', value, state,
- error_dict=e_dict)
- elif RepoGroup.get_by_group_name(repo_name_full):
- e_dict = {
- 'repo_name': _('There is a group with this name '
- 'already "%s"') % repo_name_full
- }
- raise formencode.Invalid('', value, state,
- error_dict=e_dict)
-
- elif Repository.get_by_repo_name(repo_name_full):
- e_dict = {'repo_name': _('This repository '
- 'already exists')}
- raise formencode.Invalid('', value, state,
- error_dict=e_dict)
-
- return value
-
- return _ValidRepoName
-
-
-def ValidForkName(*args, **kwargs):
- return ValidRepoName(*args, **kwargs)
-
-
-def SlugifyName():
- class _SlugifyName(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
- return repo_name_slug(value)
-
- return _SlugifyName
-
-
-def ValidCloneUri():
- from rhodecode.lib.utils import make_ui
-
- def url_handler(repo_type, url, proto, ui=None):
- if repo_type == 'hg':
- from mercurial.httprepo import httprepository, httpsrepository
- if proto == 'https':
- httpsrepository(make_ui('db'), url).capabilities
- elif proto == 'http':
- httprepository(make_ui('db'), url).capabilities
- elif repo_type == 'git':
- #TODO: write a git url validator
- pass
-
- class _ValidCloneUri(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
-
- repo_type = value.get('repo_type')
- url = value.get('clone_uri')
- e_dict = {'clone_uri': _('invalid clone url')}
-
- if not url:
- pass
- elif url.startswith('https'):
- try:
- url_handler(repo_type, url, 'https', make_ui('db'))
- except Exception:
- log.error(traceback.format_exc())
- raise formencode.Invalid('', value, state, error_dict=e_dict)
- elif url.startswith('http'):
- try:
- url_handler(repo_type, url, 'http', make_ui('db'))
- except Exception:
- log.error(traceback.format_exc())
- raise formencode.Invalid('', value, state, error_dict=e_dict)
- else:
- e_dict = {'clone_uri': _('Invalid clone url, provide a '
- 'valid clone http\s url')}
- raise formencode.Invalid('', value, state, error_dict=e_dict)
-
- return value
-
- return _ValidCloneUri
-
-
-def ValidForkType(old_data):
- class _ValidForkType(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
- if old_data['repo_type'] != value:
- raise formencode.Invalid(_('Fork have to be the same '
- 'type as original'), value, state)
-
- return value
- return _ValidForkType
-
-
-def ValidPerms(type_='repo'):
- if type_ == 'group':
- EMPTY_PERM = 'group.none'
- elif type_ == 'repo':
- EMPTY_PERM = 'repository.none'
-
- class _ValidPerms(formencode.validators.FancyValidator):
- messages = {
- 'perm_new_member_name':
- _('This username or users group name is not valid')
- }
-
- def to_python(self, value, state):
- perms_update = []
- perms_new = []
- # build a list of permission to update and new permission to create
- for k, v in value.items():
- # means new added member to permissions
- if k.startswith('perm_new_member'):
- new_perm = value.get('perm_new_member', False)
- new_member = value.get('perm_new_member_name', False)
- new_type = value.get('perm_new_member_type')
-
- if new_member and new_perm:
- if (new_member, new_perm, new_type) not in perms_new:
- perms_new.append((new_member, new_perm, new_type))
- elif k.startswith('u_perm_') or k.startswith('g_perm_'):
- member = k[7:]
- t = {'u': 'user',
- 'g': 'users_group'
- }[k[0]]
- if member == 'default':
- if value.get('private'):
- # set none for default when updating to private repo
- v = EMPTY_PERM
- perms_update.append((member, v, t))
-
- value['perms_updates'] = perms_update
- value['perms_new'] = perms_new
-
- # update permissions
- for k, v, t in perms_new:
- try:
- if t is 'user':
- self.user_db = User.query()\
- .filter(User.active == True)\
- .filter(User.username == k).one()
- if t is 'users_group':
- self.user_db = UsersGroup.query()\
- .filter(UsersGroup.users_group_active == True)\
- .filter(UsersGroup.users_group_name == k).one()
-
- except Exception:
- msg = self.message('perm_new_member_name',
- state=State_obj)
- raise formencode.Invalid(
- msg, value, state, error_dict={'perm_new_member_name': msg}
- )
- return value
- return _ValidPerms
-
-
-class ValidSettings(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
- # settings form can't edit user
- if 'user' in value:
- del['value']['user']
- return value
-
-
-class ValidPath(formencode.validators.FancyValidator):
- def to_python(self, value, state):
-
- if not os.path.isdir(value):
- msg = _('This is not a valid path')
- raise formencode.Invalid(msg, value, state,
- error_dict={'paths_root_path': msg})
- return value
-
-
-def UniqSystemEmail(old_data):
- class _UniqSystemEmail(formencode.validators.FancyValidator):
- def to_python(self, value, state):
- value = value.lower()
- if (old_data.get('email') or '').lower() != value:
- user = User.get_by_email(value, case_insensitive=True)
- if user:
- raise formencode.Invalid(
- _("This e-mail address is already taken"), value, state
- )
- return value
-
- return _UniqSystemEmail
-
-
-class ValidSystemEmail(formencode.validators.FancyValidator):
- def to_python(self, value, state):
- value = value.lower()
- user = User.get_by_email(value, case_insensitive=True)
- if user is None:
- raise formencode.Invalid(
- _("This e-mail address doesn't exist."), value, state
- )
-
- return value
-
-
-class LdapLibValidator(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
-
- try:
- import ldap
- except ImportError:
- raise LdapImportError
- return value
-
-
-class AttrLoginValidator(formencode.validators.FancyValidator):
-
- def to_python(self, value, state):
-
- if not value or not isinstance(value, (str, unicode)):
- raise formencode.Invalid(
- _("The LDAP Login attribute of the CN must be specified - "
- "this is the name of the attribute that is equivalent "
- "to 'username'"), value, state
- )
-
- return value
-
-
-#==============================================================================
-# FORMS
-#==============================================================================
class LoginForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
- username = UnicodeString(
+ username = v.UnicodeString(
strip=True,
min=1,
not_empty=True,
messages={
- 'empty': _('Please enter a login'),
- 'tooShort': _('Enter a value %(min)i characters long or more')}
+ 'empty': _(u'Please enter a login'),
+ 'tooShort': _(u'Enter a value %(min)i characters long or more')}
)
- password = UnicodeString(
+ password = v.UnicodeString(
strip=False,
min=3,
not_empty=True,
messages={
- 'empty': _('Please enter a password'),
- 'tooShort': _('Enter %(min)i characters or more')}
+ 'empty': _(u'Please enter a password'),
+ 'tooShort': _(u'Enter %(min)i characters or more')}
)
- remember = StringBoolean(if_missing=False)
+ remember = v.StringBoolean(if_missing=False)
- chained_validators = [ValidAuth]
+ chained_validators = [v.ValidAuth()]
def UserForm(edit=False, old_data={}):
class _UserForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
- username = All(UnicodeString(strip=True, min=1, not_empty=True),
- ValidUsername(edit, old_data))
+ username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
+ v.ValidUsername(edit, old_data))
if edit:
- new_password = All(UnicodeString(strip=False, min=6, not_empty=False))
- password_confirmation = All(UnicodeString(strip=False, min=6,
- not_empty=False))
- admin = StringBoolean(if_missing=False)
+ new_password = All(
+ v.ValidPassword(),
+ v.UnicodeString(strip=False, min=6, not_empty=False)
+ )
+ password_confirmation = All(
+ v.ValidPassword(),
+ v.UnicodeString(strip=False, min=6, not_empty=False),
+ )
+ admin = v.StringBoolean(if_missing=False)
else:
- password = All(UnicodeString(strip=False, min=6, not_empty=True))
- password_confirmation = All(UnicodeString(strip=False, min=6,
- not_empty=False))
+ password = All(
+ v.ValidPassword(),
+ v.UnicodeString(strip=False, min=6, not_empty=True)
+ )
+ password_confirmation = All(
+ v.ValidPassword(),
+ v.UnicodeString(strip=False, min=6, not_empty=False)
+ )
- active = StringBoolean(if_missing=False)
- name = UnicodeString(strip=True, min=1, not_empty=False)
- lastname = UnicodeString(strip=True, min=1, not_empty=False)
- email = All(Email(not_empty=True), UniqSystemEmail(old_data))
+ active = v.StringBoolean(if_missing=False)
+ firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
+ lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
+ email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
- chained_validators = [ValidPasswordsMatch, ValidPassword]
+ chained_validators = [v.ValidPasswordsMatch()]
return _UserForm
@@ -595,15 +99,18 @@ def UsersGroupForm(edit=False, old_data={}, available_members=[]):
allow_extra_fields = True
filter_extra_fields = True
- users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
- ValidUsersGroup(edit, old_data))
+ users_group_name = All(
+ v.UnicodeString(strip=True, min=1, not_empty=True),
+ v.ValidUsersGroup(edit, old_data)
+ )
- users_group_active = StringBoolean(if_missing=False)
+ users_group_active = v.StringBoolean(if_missing=False)
if edit:
- users_group_members = OneOf(available_members, hideList=False,
- testValueList=True,
- if_missing=None, not_empty=False)
+ users_group_members = v.OneOf(
+ available_members, hideList=False, testValueList=True,
+ if_missing=None, not_empty=False
+ )
return _UsersGroupForm
@@ -613,15 +120,16 @@ def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
allow_extra_fields = True
filter_extra_fields = False
- group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
- SlugifyName())
- group_description = UnicodeString(strip=True, min=1,
+ group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
+ v.SlugifyName())
+ group_description = v.UnicodeString(strip=True, min=1,
not_empty=True)
- group_parent_id = OneOf(available_groups, hideList=False,
+ group_parent_id = v.OneOf(available_groups, hideList=False,
testValueList=True,
if_missing=None, not_empty=False)
-
- chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
+ enable_locking = v.StringBoolean(if_missing=False)
+ chained_validators = [v.ValidReposGroup(edit, old_data),
+ v.ValidPerms('group')]
return _ReposGroupForm
@@ -630,16 +138,24 @@ def RegisterForm(edit=False, old_data={}):
class _RegisterForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
- username = All(ValidUsername(edit, old_data),
- UnicodeString(strip=True, min=1, not_empty=True))
- password = All(UnicodeString(strip=False, min=6, not_empty=True))
- password_confirmation = All(UnicodeString(strip=False, min=6, not_empty=True))
- active = StringBoolean(if_missing=False)
- name = UnicodeString(strip=True, min=1, not_empty=False)
- lastname = UnicodeString(strip=True, min=1, not_empty=False)
- email = All(Email(not_empty=True), UniqSystemEmail(old_data))
-
- chained_validators = [ValidPasswordsMatch, ValidPassword]
+ username = All(
+ v.ValidUsername(edit, old_data),
+ v.UnicodeString(strip=True, min=1, not_empty=True)
+ )
+ password = All(
+ v.ValidPassword(),
+ v.UnicodeString(strip=False, min=6, not_empty=True)
+ )
+ password_confirmation = All(
+ v.ValidPassword(),
+ v.UnicodeString(strip=False, min=6, not_empty=True)
+ )
+ active = v.StringBoolean(if_missing=False)
+ firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
+ lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
+ email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
+
+ chained_validators = [v.ValidPasswordsMatch()]
return _RegisterForm
@@ -648,67 +164,71 @@ def PasswordResetForm():
class _PasswordResetForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
- email = All(ValidSystemEmail(), Email(not_empty=True))
+ email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
return _PasswordResetForm
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
- repo_groups=[]):
+ repo_groups=[], landing_revs=[]):
class _RepoForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
- repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
- SlugifyName())
- clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
- repo_group = OneOf(repo_groups, hideList=True)
- repo_type = OneOf(supported_backends)
- description = UnicodeString(strip=True, min=1, not_empty=True)
- private = StringBoolean(if_missing=False)
- enable_statistics = StringBoolean(if_missing=False)
- enable_downloads = StringBoolean(if_missing=False)
+ repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
+ v.SlugifyName())
+ clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
+ repo_group = v.OneOf(repo_groups, hideList=True)
+ repo_type = v.OneOf(supported_backends)
+ description = v.UnicodeString(strip=True, min=1, not_empty=False)
+ private = v.StringBoolean(if_missing=False)
+ enable_statistics = v.StringBoolean(if_missing=False)
+ enable_downloads = v.StringBoolean(if_missing=False)
+ enable_locking = v.StringBoolean(if_missing=False)
+ landing_rev = v.OneOf(landing_revs, hideList=True)
if edit:
#this is repo owner
- user = All(UnicodeString(not_empty=True), ValidRepoUser)
+ user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
- chained_validators = [ValidCloneUri()(),
- ValidRepoName(edit, old_data),
- ValidPerms()]
+ chained_validators = [v.ValidCloneUri(),
+ v.ValidRepoName(edit, old_data),
+ v.ValidPerms()]
return _RepoForm
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
- repo_groups=[]):
+ repo_groups=[], landing_revs=[]):
class _RepoForkForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
- repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
- SlugifyName())
- repo_group = OneOf(repo_groups, hideList=True)
- repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
- description = UnicodeString(strip=True, min=1, not_empty=True)
- private = StringBoolean(if_missing=False)
- copy_permissions = StringBoolean(if_missing=False)
- update_after_clone = StringBoolean(if_missing=False)
- fork_parent_id = UnicodeString()
- chained_validators = [ValidForkName(edit, old_data)]
+ repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
+ v.SlugifyName())
+ repo_group = v.OneOf(repo_groups, hideList=True)
+ repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
+ description = v.UnicodeString(strip=True, min=1, not_empty=True)
+ private = v.StringBoolean(if_missing=False)
+ copy_permissions = v.StringBoolean(if_missing=False)
+ update_after_clone = v.StringBoolean(if_missing=False)
+ fork_parent_id = v.UnicodeString()
+ chained_validators = [v.ValidForkName(edit, old_data)]
+ landing_rev = v.OneOf(landing_revs, hideList=True)
return _RepoForkForm
-def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
- repo_groups=[]):
+def RepoSettingsForm(edit=False, old_data={},
+ supported_backends=BACKENDS.keys(), repo_groups=[],
+ landing_revs=[]):
class _RepoForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
- repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
- SlugifyName())
- description = UnicodeString(strip=True, min=1, not_empty=True)
- repo_group = OneOf(repo_groups, hideList=True)
- private = StringBoolean(if_missing=False)
-
- chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
- ValidSettings]
+ repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
+ v.SlugifyName())
+ description = v.UnicodeString(strip=True, min=1, not_empty=True)
+ repo_group = v.OneOf(repo_groups, hideList=True)
+ private = v.StringBoolean(if_missing=False)
+ landing_rev = v.OneOf(landing_revs, hideList=True)
+ chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
+ v.ValidSettings()]
return _RepoForm
@@ -716,58 +236,108 @@ def ApplicationSettingsForm():
class _ApplicationSettingsForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
- rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
- rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
- rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
+ rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
+ rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
+ rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
return _ApplicationSettingsForm
+def ApplicationVisualisationForm():
+ class _ApplicationVisualisationForm(formencode.Schema):
+ allow_extra_fields = True
+ filter_extra_fields = False
+ rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
+ rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
+ rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
+
+ return _ApplicationVisualisationForm
+
+
def ApplicationUiSettingsForm():
class _ApplicationUiSettingsForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
- web_push_ssl = OneOf(['true', 'false'], if_missing='false')
- paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
- hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
- hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
- hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
- hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
+ web_push_ssl = v.StringBoolean(if_missing=False)
+ paths_root_path = All(
+ v.ValidPath(),
+ v.UnicodeString(strip=True, min=1, not_empty=True)
+ )
+ hooks_changegroup_update = v.StringBoolean(if_missing=False)
+ hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
+ hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
+ hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
+
+ extensions_largefiles = v.StringBoolean(if_missing=False)
+ extensions_hgsubversion = v.StringBoolean(if_missing=False)
+ extensions_hggit = v.StringBoolean(if_missing=False)
return _ApplicationUiSettingsForm
-def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
+def DefaultPermissionsForm(perms_choices, register_choices, create_choices,
+ fork_choices):
class _DefaultPermissionsForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
- overwrite_default = StringBoolean(if_missing=False)
- anonymous = OneOf(['True', 'False'], if_missing=False)
- default_perm = OneOf(perms_choices)
- default_register = OneOf(register_choices)
- default_create = OneOf(create_choices)
+ overwrite_default = v.StringBoolean(if_missing=False)
+ anonymous = v.StringBoolean(if_missing=False)
+ default_perm = v.OneOf(perms_choices)
+ default_register = v.OneOf(register_choices)
+ default_create = v.OneOf(create_choices)
+ default_fork = v.OneOf(fork_choices)
return _DefaultPermissionsForm
-def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
+def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
+ tls_kind_choices):
class _LdapSettingsForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
#pre_validators = [LdapLibValidator]
- ldap_active = StringBoolean(if_missing=False)
- ldap_host = UnicodeString(strip=True,)
- ldap_port = Number(strip=True,)
- ldap_tls_kind = OneOf(tls_kind_choices)
- ldap_tls_reqcert = OneOf(tls_reqcert_choices)
- ldap_dn_user = UnicodeString(strip=True,)
- ldap_dn_pass = UnicodeString(strip=True,)
- ldap_base_dn = UnicodeString(strip=True,)
- ldap_filter = UnicodeString(strip=True,)
- ldap_search_scope = OneOf(search_scope_choices)
- ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
- ldap_attr_firstname = UnicodeString(strip=True,)
- ldap_attr_lastname = UnicodeString(strip=True,)
- ldap_attr_email = UnicodeString(strip=True,)
+ ldap_active = v.StringBoolean(if_missing=False)
+ ldap_host = v.UnicodeString(strip=True,)
+ ldap_port = v.Number(strip=True,)
+ ldap_tls_kind = v.OneOf(tls_kind_choices)
+ ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
+ ldap_dn_user = v.UnicodeString(strip=True,)
+ ldap_dn_pass = v.UnicodeString(strip=True,)
+ ldap_base_dn = v.UnicodeString(strip=True,)
+ ldap_filter = v.UnicodeString(strip=True,)
+ ldap_search_scope = v.OneOf(search_scope_choices)
+ ldap_attr_login = All(
+ v.AttrLoginValidator(),
+ v.UnicodeString(strip=True,)
+ )
+ ldap_attr_firstname = v.UnicodeString(strip=True,)
+ ldap_attr_lastname = v.UnicodeString(strip=True,)
+ ldap_attr_email = v.UnicodeString(strip=True,)
return _LdapSettingsForm
+
+
+def UserExtraEmailForm():
+ class _UserExtraEmailForm(formencode.Schema):
+ email = All(v.UniqSystemEmail(), v.Email)
+
+ return _UserExtraEmailForm
+
+
+def PullRequestForm():
+ class _PullRequestForm(formencode.Schema):
+ allow_extra_fields = True
+ filter_extra_fields = True
+
+ user = v.UnicodeString(strip=True, required=True)
+ org_repo = v.UnicodeString(strip=True, required=True)
+ org_ref = v.UnicodeString(strip=True, required=True)
+ other_repo = v.UnicodeString(strip=True, required=True)
+ other_ref = v.UnicodeString(strip=True, required=True)
+ revisions = All(v.NotReviewedRevisions()(), v.UniqueList(not_empty=True))
+ review_members = v.UniqueList(not_empty=True)
+
+ pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
+ pullrequest_desc = v.UnicodeString(strip=True, required=False)
+
+ return _PullRequestForm \ No newline at end of file
diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py
index 2fa54aa2..b7df794b 100644
--- a/rhodecode/model/notification.py
+++ b/rhodecode/model/notification.py
@@ -27,12 +27,10 @@
import os
import logging
import traceback
-import datetime
from pylons.i18n.translation import _
import rhodecode
-from rhodecode.config.conf import DATETIME_FORMAT
from rhodecode.lib import helpers as h
from rhodecode.model import BaseModel
from rhodecode.model.db import Notification, User, UserNotification
@@ -42,8 +40,7 @@ log = logging.getLogger(__name__)
class NotificationModel(BaseModel):
- def __get_user(self, user):
- return self._get_instance(User, user, callback=User.get_by_username)
+ cls = Notification
def __get_notification(self, notification):
if isinstance(notification, Notification):
@@ -77,12 +74,12 @@ class NotificationModel(BaseModel):
if recipients and not getattr(recipients, '__iter__', False):
raise Exception('recipients must be a list of iterable')
- created_by_obj = self.__get_user(created_by)
+ created_by_obj = self._get_user(created_by)
if recipients:
recipients_objs = []
for u in recipients:
- obj = self.__get_user(u)
+ obj = self._get_user(u)
if obj:
recipients_objs.append(obj)
recipients_objs = set(recipients_objs)
@@ -103,11 +100,15 @@ class NotificationModel(BaseModel):
if with_email is False:
return notif
- # send email with notification
- for rec in recipients_objs:
+ #don't send email to person who created this comment
+ rec_objs = set(recipients_objs).difference(set([created_by_obj]))
+
+ # send email with notification to all other participants
+ for rec in rec_objs:
email_subject = NotificationModel().make_description(notif, False)
type_ = type_
email_body = body
+ ## this is passed into template
kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
kwargs.update(email_kwargs)
email_body_html = EmailNotificationModel()\
@@ -122,7 +123,7 @@ class NotificationModel(BaseModel):
# we don't want to remove actual notification just the assignment
try:
notification = self.__get_notification(notification)
- user = self.__get_user(user)
+ user = self._get_user(user)
if notification and user:
obj = UserNotification.query()\
.filter(UserNotification.user == user)\
@@ -135,30 +136,73 @@ class NotificationModel(BaseModel):
log.error(traceback.format_exc())
raise
- def get_for_user(self, user):
- user = self.__get_user(user)
- return user.notifications
+ def get_for_user(self, user, filter_=None):
+ """
+ Get mentions for given user, filter them if filter dict is given
+
+ :param user:
+ :type user:
+ :param filter:
+ """
+ user = self._get_user(user)
+
+ q = UserNotification.query()\
+ .filter(UserNotification.user == user)\
+ .join((Notification, UserNotification.notification_id ==
+ Notification.notification_id))
+
+ if filter_:
+ q = q.filter(Notification.type_.in_(filter_))
- def mark_all_read_for_user(self, user):
- user = self.__get_user(user)
- UserNotification.query()\
- .filter(UserNotification.read==False)\
- .update({'read': True})
+ return q.all()
+
+ def mark_read(self, user, notification):
+ try:
+ notification = self.__get_notification(notification)
+ user = self._get_user(user)
+ if notification and user:
+ obj = UserNotification.query()\
+ .filter(UserNotification.user == user)\
+ .filter(UserNotification.notification
+ == notification)\
+ .one()
+ obj.read = True
+ self.sa.add(obj)
+ return True
+ except Exception:
+ log.error(traceback.format_exc())
+ raise
+
+ def mark_all_read_for_user(self, user, filter_=None):
+ user = self._get_user(user)
+ q = UserNotification.query()\
+ .filter(UserNotification.user == user)\
+ .filter(UserNotification.read == False)\
+ .join((Notification, UserNotification.notification_id ==
+ Notification.notification_id))
+ if filter_:
+ q = q.filter(Notification.type_.in_(filter_))
+
+ # this is a little inefficient but sqlalchemy doesn't support
+ # update on joined tables :(
+ for obj in q.all():
+ obj.read = True
+ self.sa.add(obj)
def get_unread_cnt_for_user(self, user):
- user = self.__get_user(user)
+ user = self._get_user(user)
return UserNotification.query()\
.filter(UserNotification.read == False)\
.filter(UserNotification.user == user).count()
def get_unread_for_user(self, user):
- user = self.__get_user(user)
+ user = self._get_user(user)
return [x.notification for x in UserNotification.query()\
.filter(UserNotification.read == False)\
.filter(UserNotification.user == user).all()]
def get_user_notification(self, user, notification):
- user = self.__get_user(user)
+ user = self._get_user(user)
notification = self.__get_notification(notification)
return UserNotification.query()\
@@ -170,20 +214,23 @@ class NotificationModel(BaseModel):
Creates a human readable description based on properties
of notification object
"""
-
+ #alias
+ _n = notification
_map = {
- notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
- notification.TYPE_MESSAGE: _('sent message'),
- notification.TYPE_MENTION: _('mentioned you'),
- notification.TYPE_REGISTRATION: _('registered in RhodeCode')
+ _n.TYPE_CHANGESET_COMMENT: _('commented on commit'),
+ _n.TYPE_MESSAGE: _('sent message'),
+ _n.TYPE_MENTION: _('mentioned you'),
+ _n.TYPE_REGISTRATION: _('registered in RhodeCode'),
+ _n.TYPE_PULL_REQUEST: _('opened new pull request'),
+ _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request')
}
- tmpl = "%(user)s %(action)s %(when)s"
+ # action == _map string
+ tmpl = "%(user)s %(action)s at %(when)s"
if show_age:
when = h.age(notification.created_on)
else:
- DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
- when = DTF(notification.created_on)
+ when = h.fmt_date(notification.created_on)
data = dict(
user=notification.created_by_user.username,
@@ -197,6 +244,7 @@ class EmailNotificationModel(BaseModel):
TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
TYPE_PASSWORD_RESET = 'passoword_link'
TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
+ TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
TYPE_DEFAULT = 'default'
def __init__(self):
diff --git a/rhodecode/model/permission.py b/rhodecode/model/permission.py
index 96f63e70..2d67c8c9 100644
--- a/rhodecode/model/permission.py
+++ b/rhodecode/model/permission.py
@@ -31,7 +31,8 @@ from sqlalchemy.exc import DatabaseError
from rhodecode.lib.caching_query import FromCache
from rhodecode.model import BaseModel
-from rhodecode.model.db import User, Permission, UserToPerm, UserRepoToPerm
+from rhodecode.model.db import User, Permission, UserToPerm, UserRepoToPerm,\
+ UserRepoGroupToPerm
log = logging.getLogger(__name__)
@@ -41,6 +42,8 @@ class PermissionModel(BaseModel):
Permissions model for RhodeCode
"""
+ cls = Permission
+
def get_permission(self, permission_id, cache=False):
"""
Get's permissions by id
@@ -74,8 +77,8 @@ class PermissionModel(BaseModel):
form_result['perm_user_name']).scalar()
u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
perm_user).all()
- if len(u2p) != 3:
- raise Exception('Defined: %s should be 3 permissions for default'
+ if len(u2p) != 4:
+ raise Exception('Defined: %s should be 4 permissions for default'
' user. This should not happen please verify'
' your database' % len(u2p))
@@ -87,23 +90,38 @@ class PermissionModel(BaseModel):
form_result['default_perm'])
self.sa.add(p)
- if p.permission.permission_name.startswith('hg.register.'):
+ elif p.permission.permission_name.startswith('hg.register.'):
p.permission = self.get_permission_by_name(
form_result['default_register'])
self.sa.add(p)
- if p.permission.permission_name.startswith('hg.create.'):
+ elif p.permission.permission_name.startswith('hg.create.'):
p.permission = self.get_permission_by_name(
form_result['default_create'])
self.sa.add(p)
+ elif p.permission.permission_name.startswith('hg.fork.'):
+ p.permission = self.get_permission_by_name(
+ form_result['default_fork'])
+ self.sa.add(p)
+
+ _def_name = form_result['default_perm'].split('repository.')[-1]
#stage 2 update all default permissions for repos if checked
if form_result['overwrite_default'] == True:
+ _def = self.get_permission_by_name('repository.' + _def_name)
+ # repos
for r2p in self.sa.query(UserRepoToPerm)\
- .filter(UserRepoToPerm.user == perm_user).all():
- r2p.permission = self.get_permission_by_name(
- form_result['default_perm'])
+ .filter(UserRepoToPerm.user == perm_user)\
+ .all():
+ r2p.permission = _def
self.sa.add(r2p)
+ # groups
+ _def = self.get_permission_by_name('group.' + _def_name)
+ for g2p in self.sa.query(UserRepoGroupToPerm)\
+ .filter(UserRepoGroupToPerm.user == perm_user)\
+ .all():
+ g2p.permission = _def
+ self.sa.add(g2p)
# stage 3 set anonymous access
if perm_user.username == 'default':
diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py
new file mode 100644
index 00000000..0d136275
--- /dev/null
+++ b/rhodecode/model/pull_request.py
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.model.pull_request
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ pull request model for RhodeCode
+
+ :created_on: Jun 6, 2012
+ :author: marcink
+ :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+import logging
+import binascii
+import datetime
+
+from pylons.i18n.translation import _
+
+from rhodecode.model.meta import Session
+from rhodecode.lib import helpers as h
+from rhodecode.model import BaseModel
+from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification
+from rhodecode.model.notification import NotificationModel
+from rhodecode.lib.utils2 import safe_unicode
+
+from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil
+
+log = logging.getLogger(__name__)
+
+
+class PullRequestModel(BaseModel):
+
+ cls = PullRequest
+
+ def __get_pull_request(self, pull_request):
+ return self._get_instance(PullRequest, pull_request)
+
+ def get_all(self, repo):
+ repo = self._get_repo(repo)
+ return PullRequest.query().filter(PullRequest.other_repo == repo).all()
+
+ def create(self, created_by, org_repo, org_ref, other_repo,
+ other_ref, revisions, reviewers, title, description=None):
+
+ created_by_user = self._get_user(created_by)
+ org_repo = self._get_repo(org_repo)
+ other_repo = self._get_repo(other_repo)
+
+ new = PullRequest()
+ new.org_repo = org_repo
+ new.org_ref = org_ref
+ new.other_repo = other_repo
+ new.other_ref = other_ref
+ new.revisions = revisions
+ new.title = title
+ new.description = description
+ new.author = created_by_user
+ self.sa.add(new)
+ Session().flush()
+ #members
+ for member in reviewers:
+ _usr = self._get_user(member)
+ reviewer = PullRequestReviewers(_usr, new)
+ self.sa.add(reviewer)
+
+ #notification to reviewers
+ notif = NotificationModel()
+
+ subject = safe_unicode(
+ h.link_to(
+ _('%(user)s wants you to review pull request #%(pr_id)s') % \
+ {'user': created_by_user.username,
+ 'pr_id': new.pull_request_id},
+ h.url('pullrequest_show', repo_name=other_repo.repo_name,
+ pull_request_id=new.pull_request_id,
+ qualified=True,
+ )
+ )
+ )
+ body = description
+ notif.create(created_by=created_by_user, subject=subject, body=body,
+ recipients=reviewers,
+ type_=Notification.TYPE_PULL_REQUEST,)
+
+ return new
+
+ def update_reviewers(self, pull_request, reviewers_ids):
+ reviewers_ids = set(reviewers_ids)
+ pull_request = self.__get_pull_request(pull_request)
+ current_reviewers = PullRequestReviewers.query()\
+ .filter(PullRequestReviewers.pull_request==
+ pull_request)\
+ .all()
+ current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
+
+ to_add = reviewers_ids.difference(current_reviewers_ids)
+ to_remove = current_reviewers_ids.difference(reviewers_ids)
+
+ log.debug("Adding %s reviewers" % to_add)
+ log.debug("Removing %s reviewers" % to_remove)
+
+ for uid in to_add:
+ _usr = self._get_user(uid)
+ reviewer = PullRequestReviewers(_usr, pull_request)
+ self.sa.add(reviewer)
+
+ for uid in to_remove:
+ reviewer = PullRequestReviewers.query()\
+ .filter(PullRequestReviewers.user_id==uid,
+ PullRequestReviewers.pull_request==pull_request)\
+ .scalar()
+ if reviewer:
+ self.sa.delete(reviewer)
+
+ def delete(self, pull_request):
+ pull_request = self.__get_pull_request(pull_request)
+ Session().delete(pull_request)
+
+ def close_pull_request(self, pull_request):
+ pull_request = self.__get_pull_request(pull_request)
+ pull_request.status = PullRequest.STATUS_CLOSED
+ pull_request.updated_on = datetime.datetime.now()
+ self.sa.add(pull_request)
+
+ def _get_changesets(self, org_repo, org_ref, other_repo, other_ref,
+ discovery_data):
+ """
+ Returns a list of changesets that are incoming from org_repo@org_ref
+ to other_repo@other_ref
+
+ :param org_repo:
+ :type org_repo:
+ :param org_ref:
+ :type org_ref:
+ :param other_repo:
+ :type other_repo:
+ :param other_ref:
+ :type other_ref:
+ :param tmp:
+ :type tmp:
+ """
+ changesets = []
+ #case two independent repos
+ common, incoming, rheads = discovery_data
+ if org_repo != other_repo and incoming:
+ revs = org_repo._repo.changelog.findmissing(common, rheads)
+
+ for cs in reversed(map(binascii.hexlify, revs)):
+ changesets.append(org_repo.get_changeset(cs))
+ else:
+ _revset_predicates = {
+ 'branch': 'branch',
+ 'book': 'bookmark',
+ 'tag': 'tag',
+ 'rev': 'id',
+ }
+
+ revs = [
+ "ancestors(%s('%s')) and not ancestors(%s('%s'))" % (
+ _revset_predicates[org_ref[0]], org_ref[1],
+ _revset_predicates[other_ref[0]], other_ref[1]
+ )
+ ]
+
+ out = scmutil.revrange(org_repo._repo, revs)
+ for cs in reversed(out):
+ changesets.append(org_repo.get_changeset(cs))
+
+ return changesets
+
+ def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
+ """
+ Get's mercurial discovery data used to calculate difference between
+ repos and refs
+
+ :param org_repo:
+ :type org_repo:
+ :param org_ref:
+ :type org_ref:
+ :param other_repo:
+ :type other_repo:
+ :param other_ref:
+ :type other_ref:
+ """
+
+ _org_repo = org_repo._repo
+ org_rev_type, org_rev = org_ref
+
+ _other_repo = other_repo._repo
+ other_rev_type, other_rev = other_ref
+
+ log.debug('Doing discovery for %s@%s vs %s@%s' % (
+ org_repo, org_ref, other_repo, other_ref)
+ )
+ #log.debug('Filter heads are %s[%s]' % ('', org_ref[1]))
+ org_peer = localrepo.locallegacypeer(_org_repo.local())
+ tmp = discovery.findcommonincoming(
+ repo=_other_repo, # other_repo we check for incoming
+ remote=org_peer, # org_repo source for incoming
+ heads=[_other_repo[other_rev].node(),
+ _org_repo[org_rev].node()],
+ force=False
+ )
+ return tmp
+
+ def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
+ """
+ Returns a tuple of incomming changesets, and discoverydata cache
+
+ :param org_repo:
+ :type org_repo:
+ :param org_ref:
+ :type org_ref:
+ :param other_repo:
+ :type other_repo:
+ :param other_ref:
+ :type other_ref:
+ """
+
+ if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
+ raise Exception('org_ref must be a two element list/tuple')
+
+ if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
+ raise Exception('other_ref must be a two element list/tuple')
+
+ discovery_data = self._get_discovery(org_repo.scm_instance,
+ org_ref,
+ other_repo.scm_instance,
+ other_ref)
+ cs_ranges = self._get_changesets(org_repo.scm_instance,
+ org_ref,
+ other_repo.scm_instance,
+ other_ref,
+ discovery_data)
+
+ return cs_ranges, discovery_data
diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py
index 9055e53c..60e78657 100644
--- a/rhodecode/model/repo.py
+++ b/rhodecode/model/repo.py
@@ -22,6 +22,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import with_statement
import os
import shutil
import logging
@@ -45,25 +46,17 @@ log = logging.getLogger(__name__)
class RepoModel(BaseModel):
- def __get_user(self, user):
- return self._get_instance(User, user, callback=User.get_by_username)
+ cls = Repository
+ URL_SEPARATOR = Repository.url_sep()
def __get_users_group(self, users_group):
return self._get_instance(UsersGroup, users_group,
callback=UsersGroup.get_by_group_name)
- def __get_repos_group(self, repos_group):
+ def _get_repos_group(self, repos_group):
return self._get_instance(RepoGroup, repos_group,
callback=RepoGroup.get_by_group_name)
- def __get_repo(self, repository):
- return self._get_instance(Repository, repository,
- callback=Repository.get_by_repo_name)
-
- def __get_perm(self, permission):
- return self._get_instance(Permission, permission,
- callback=Permission.get_by_key)
-
@LazyProperty
def repos_path(self):
"""
@@ -83,7 +76,7 @@ class RepoModel(BaseModel):
return repo.scalar()
def get_repo(self, repository):
- return self.__get_repo(repository)
+ return self._get_repo(repository)
def get_by_repo_name(self, repo_name, cache=False):
repo = self.sa.query(Repository)\
@@ -209,37 +202,45 @@ class RepoModel(BaseModel):
log.error(traceback.format_exc())
raise
- def create(self, form_data, cur_user, just_db=False, fork=False):
+ def create_repo(self, repo_name, repo_type, description, owner,
+ private=False, clone_uri=None, repos_group=None,
+ landing_rev='tip', just_db=False, fork_of=None,
+ copy_fork_permissions=False):
+ """
+ Create repository
+
+ """
from rhodecode.model.scm import ScmModel
+ owner = self._get_user(owner)
+ fork_of = self._get_repo(fork_of)
+ repos_group = self._get_repos_group(repos_group)
try:
- if fork:
- fork_parent_id = form_data['fork_parent_id']
# repo name is just a name of repository
# while repo_name_full is a full qualified name that is combined
# with name and path of group
- repo_name = form_data['repo_name']
- repo_name_full = form_data['repo_name_full']
+ repo_name_full = repo_name
+ repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
new_repo = Repository()
new_repo.enable_statistics = False
-
- for k, v in form_data.items():
- if k == 'repo_name':
- v = repo_name_full
- if k == 'repo_group':
- k = 'group_id'
- if k == 'description':
- v = v or repo_name
-
- setattr(new_repo, k, v)
-
- if fork:
- parent_repo = Repository.get(fork_parent_id)
+ new_repo.repo_name = repo_name_full
+ new_repo.repo_type = repo_type
+ new_repo.user = owner
+ new_repo.group = repos_group
+ new_repo.description = description or repo_name
+ new_repo.private = private
+ new_repo.clone_uri = clone_uri
+ new_repo.landing_rev = landing_rev
+
+ if repos_group:
+ new_repo.enable_locking = repos_group.enable_locking
+
+ if fork_of:
+ parent_repo = fork_of
new_repo.fork = parent_repo
- new_repo.user_id = cur_user.user_id
self.sa.add(new_repo)
def _create_default_perms():
@@ -251,7 +252,7 @@ class RepoModel(BaseModel):
default = p.permission.permission_name
break
- default_perm = 'repository.none' if form_data['private'] else default
+ default_perm = 'repository.none' if private else default
repo_to_perm.permission_id = self.sa.query(Permission)\
.filter(Permission.permission_name == default_perm)\
@@ -262,9 +263,9 @@ class RepoModel(BaseModel):
self.sa.add(repo_to_perm)
- if fork:
- if form_data.get('copy_permissions'):
- repo = Repository.get(fork_parent_id)
+ if fork_of:
+ if copy_fork_permissions:
+ repo = fork_of
user_perms = UserRepoToPerm.query()\
.filter(UserRepoToPerm.repository == repo).all()
group_perms = UsersGroupRepoToPerm.query()\
@@ -283,20 +284,45 @@ class RepoModel(BaseModel):
_create_default_perms()
if not just_db:
- self.__create_repo(repo_name, form_data['repo_type'],
- form_data['repo_group'],
- form_data['clone_uri'])
+ self.__create_repo(repo_name, repo_type,
+ repos_group,
+ clone_uri)
log_create_repository(new_repo.get_dict(),
- created_by=cur_user.username)
+ created_by=owner.username)
# now automatically start following this repository as owner
ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
- cur_user.user_id)
+ owner.user_id)
return new_repo
except:
log.error(traceback.format_exc())
raise
+ def create(self, form_data, cur_user, just_db=False, fork=None):
+ """
+ Backward compatibility function, just a wrapper on top of create_repo
+
+ :param form_data:
+ :param cur_user:
+ :param just_db:
+ :param fork:
+ """
+
+ repo_name = form_data['repo_name_full']
+ repo_type = form_data['repo_type']
+ description = form_data['description']
+ owner = cur_user
+ private = form_data['private']
+ clone_uri = form_data.get('clone_uri')
+ repos_group = form_data['repo_group']
+ landing_rev = form_data['landing_rev']
+ copy_fork_permissions = form_data.get('copy_permissions')
+ fork_of = form_data.get('fork_parent_id')
+ return self.create_repo(
+ repo_name, repo_type, description, owner, private, clone_uri,
+ repos_group, landing_rev, just_db, fork_of, copy_fork_permissions
+ )
+
def create_fork(self, form_data, cur_user):
"""
Simple wrapper into executing celery task for fork creation
@@ -308,13 +334,14 @@ class RepoModel(BaseModel):
run_task(tasks.create_repo_fork, form_data, cur_user)
def delete(self, repo):
- repo = self.__get_repo(repo)
- try:
- self.sa.delete(repo)
- self.__delete_repo(repo)
- except:
- log.error(traceback.format_exc())
- raise
+ repo = self._get_repo(repo)
+ if repo:
+ try:
+ self.sa.delete(repo)
+ self.__delete_repo(repo)
+ except:
+ log.error(traceback.format_exc())
+ raise
def grant_user_permission(self, repo, user, perm):
"""
@@ -325,9 +352,9 @@ class RepoModel(BaseModel):
:param user: Instance of User, user_id or username
:param perm: Instance of Permission, or permission_name
"""
- user = self.__get_user(user)
- repo = self.__get_repo(repo)
- permission = self.__get_perm(perm)
+ user = self._get_user(user)
+ repo = self._get_repo(repo)
+ permission = self._get_perm(perm)
# check if we have that permission already
obj = self.sa.query(UserRepoToPerm)\
@@ -350,8 +377,8 @@ class RepoModel(BaseModel):
:param user: Instance of User, user_id or username
"""
- user = self.__get_user(user)
- repo = self.__get_repo(repo)
+ user = self._get_user(user)
+ repo = self._get_repo(repo)
obj = self.sa.query(UserRepoToPerm)\
.filter(UserRepoToPerm.repository == repo)\
@@ -369,9 +396,9 @@ class RepoModel(BaseModel):
or users group name
:param perm: Instance of Permission, or permission_name
"""
- repo = self.__get_repo(repo)
+ repo = self._get_repo(repo)
group_name = self.__get_users_group(group_name)
- permission = self.__get_perm(perm)
+ permission = self._get_perm(perm)
# check if we have that permission already
obj = self.sa.query(UsersGroupRepoToPerm)\
@@ -396,7 +423,7 @@ class RepoModel(BaseModel):
:param group_name: Instance of UserGroup, users_group_id,
or users group name
"""
- repo = self.__get_repo(repo)
+ repo = self._get_repo(repo)
group_name = self.__get_users_group(group_name)
obj = self.sa.query(UsersGroupRepoToPerm)\
@@ -421,7 +448,7 @@ class RepoModel(BaseModel):
log.error(traceback.format_exc())
raise
- def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
+ def __create_repo(self, repo_name, alias, parent, clone_uri=False):
"""
makes repository on filesystem. It's group aware means it'll create
a repository within a group, and alter the paths accordingly of
@@ -433,11 +460,10 @@ class RepoModel(BaseModel):
:param clone_uri:
"""
from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
+ from rhodecode.model.scm import ScmModel
- if new_parent_id:
- paths = RepoGroup.get(new_parent_id)\
- .full_path.split(RepoGroup.url_sep())
- new_parent_path = os.sep.join(paths)
+ if parent:
+ new_parent_path = os.sep.join(parent.full_path_splitted)
else:
new_parent_path = ''
@@ -458,8 +484,14 @@ class RepoModel(BaseModel):
)
)
backend = get_backend(alias)
-
- backend(repo_path, create=True, src_url=clone_uri)
+ if alias == 'hg':
+ backend(repo_path, create=True, src_url=clone_uri)
+ elif alias == 'git':
+ r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
+ # add rhodecode hook into this repo
+ ScmModel().install_git_hook(repo=r)
+ else:
+ raise Exception('Undefined alias %s' % alias)
def __rename_repo(self, old, new):
"""
@@ -489,11 +521,18 @@ class RepoModel(BaseModel):
"""
rm_path = os.path.join(self.repos_path, repo.repo_name)
log.info("Removing %s" % (rm_path))
- # disable hg/git
+ # disable hg/git internal that it doesn't get detected as repo
alias = repo.repo_type
- shutil.move(os.path.join(rm_path, '.%s' % alias),
- os.path.join(rm_path, 'rm__.%s' % alias))
+
+ bare = getattr(repo.scm_instance, 'bare', False)
+
+ if not bare:
+ # skip this for bare git repos
+ shutil.move(os.path.join(rm_path, '.%s' % alias),
+ os.path.join(rm_path, 'rm__.%s' % alias))
# disable repo
- _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'),
+ _now = datetime.now()
+ _ms = str(_now.microsecond).rjust(6, '0')
+ _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
repo.repo_name)
shutil.move(rm_path, os.path.join(self.repos_path, _d))
diff --git a/rhodecode/model/repo_permission.py b/rhodecode/model/repo_permission.py
index 0f19ef56..6424e2a0 100644
--- a/rhodecode/model/repo_permission.py
+++ b/rhodecode/model/repo_permission.py
@@ -26,28 +26,19 @@
import logging
from rhodecode.model import BaseModel
-from rhodecode.model.db import UserRepoToPerm, UsersGroupRepoToPerm, Permission,\
- User, Repository
+from rhodecode.model.db import UserRepoToPerm, UsersGroupRepoToPerm, \
+ Permission
log = logging.getLogger(__name__)
class RepositoryPermissionModel(BaseModel):
- def __get_user(self, user):
- return self._get_instance(User, user, callback=User.get_by_username)
-
- def __get_repo(self, repository):
- return self._get_instance(Repository, repository,
- callback=Repository.get_by_repo_name)
-
- def __get_perm(self, permission):
- return self._get_instance(Permission, permission,
- callback=Permission.get_by_key)
+ cls = UserRepoToPerm
def get_user_permission(self, repository, user):
- repository = self.__get_repo(repository)
- user = self.__get_user(user)
+ repository = self._get_repo(repository)
+ user = self._get_user(user)
return UserRepoToPerm.query() \
.filter(UserRepoToPerm.user == user) \
diff --git a/rhodecode/model/repos_group.py b/rhodecode/model/repos_group.py
index 8791b21b..703d9873 100644
--- a/rhodecode/model/repos_group.py
+++ b/rhodecode/model/repos_group.py
@@ -39,28 +39,23 @@ log = logging.getLogger(__name__)
class ReposGroupModel(BaseModel):
- def __get_user(self, user):
- return self._get_instance(User, user, callback=User.get_by_username)
+ cls = RepoGroup
def __get_users_group(self, users_group):
return self._get_instance(UsersGroup, users_group,
callback=UsersGroup.get_by_group_name)
- def __get_repos_group(self, repos_group):
+ def _get_repos_group(self, repos_group):
return self._get_instance(RepoGroup, repos_group,
callback=RepoGroup.get_by_group_name)
- def __get_perm(self, permission):
- return self._get_instance(Permission, permission,
- callback=Permission.get_by_key)
-
@LazyProperty
def repos_path(self):
"""
Get's the repositories root path from database
"""
- q = RhodeCodeUi.get_by_key('/').one()
+ q = RhodeCodeUi.get_by_key('/')
return q.ui_value
def _create_default_perms(self, new_group):
@@ -134,11 +129,11 @@ class ReposGroupModel(BaseModel):
# delete only if that path really exists
os.rmdir(rm_path)
- def create(self, group_name, group_description, parent, just_db=False):
+ def create(self, group_name, group_description, parent=None, just_db=False):
try:
new_repos_group = RepoGroup()
new_repos_group.group_description = group_description
- new_repos_group.parent_group = self.__get_repos_group(parent)
+ new_repos_group.parent_group = self._get_repos_group(parent)
new_repos_group.group_name = new_repos_group.get_new_name(group_name)
self.sa.add(new_repos_group)
@@ -188,11 +183,20 @@ class ReposGroupModel(BaseModel):
repos_group.group_description = form_data['group_description']
repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
repos_group.group_parent_id = form_data['group_parent_id']
+ repos_group.enable_locking = form_data['enable_locking']
repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
new_path = repos_group.full_path
self.sa.add(repos_group)
+ # iterate over all members of this groups and set the locking !
+ # this can be potentially heavy operation
+
+ for obj in repos_group.recursive_groups_and_repos():
+ #set the value from it's parent
+ obj.enable_locking = repos_group.enable_locking
+ self.sa.add(obj)
+
# we need to get all repositories from this new group and
# rename them accordingly to new group path
for r in repos_group.repositories:
@@ -206,13 +210,13 @@ class ReposGroupModel(BaseModel):
log.error(traceback.format_exc())
raise
- def delete(self, users_group_id):
+ def delete(self, repos_group):
+ repos_group = self._get_repos_group(repos_group)
try:
- users_group = RepoGroup.get(users_group_id)
- self.sa.delete(users_group)
- self.__delete_group(users_group)
+ self.sa.delete(repos_group)
+ self.__delete_group(repos_group)
except:
- log.error(traceback.format_exc())
+ log.exception('Error removing repos_group %s' % repos_group)
raise
def grant_user_permission(self, repos_group, user, perm):
@@ -226,9 +230,9 @@ class ReposGroupModel(BaseModel):
:param perm: Instance of Permission, or permission_name
"""
- repos_group = self.__get_repos_group(repos_group)
- user = self.__get_user(user)
- permission = self.__get_perm(perm)
+ repos_group = self._get_repos_group(repos_group)
+ user = self._get_user(user)
+ permission = self._get_perm(perm)
# check if we have that permission already
obj = self.sa.query(UserRepoGroupToPerm)\
@@ -252,8 +256,8 @@ class ReposGroupModel(BaseModel):
:param user: Instance of User, user_id or username
"""
- repos_group = self.__get_repos_group(repos_group)
- user = self.__get_user(user)
+ repos_group = self._get_repos_group(repos_group)
+ user = self._get_user(user)
obj = self.sa.query(UserRepoGroupToPerm)\
.filter(UserRepoGroupToPerm.user == user)\
@@ -272,9 +276,9 @@ class ReposGroupModel(BaseModel):
or users group name
:param perm: Instance of Permission, or permission_name
"""
- repos_group = self.__get_repos_group(repos_group)
+ repos_group = self._get_repos_group(repos_group)
group_name = self.__get_users_group(group_name)
- permission = self.__get_perm(perm)
+ permission = self._get_perm(perm)
# check if we have that permission already
obj = self.sa.query(UsersGroupRepoGroupToPerm)\
@@ -300,7 +304,7 @@ class ReposGroupModel(BaseModel):
:param group_name: Instance of UserGroup, users_group_id,
or users group name
"""
- repos_group = self.__get_repos_group(repos_group)
+ repos_group = self._get_repos_group(repos_group)
group_name = self.__get_users_group(group_name)
obj = self.sa.query(UsersGroupRepoGroupToPerm)\
diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py
index 0cd79a60..62287bc4 100644
--- a/rhodecode/model/scm.py
+++ b/rhodecode/model/scm.py
@@ -22,26 +22,35 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import with_statement
import os
+import re
import time
import traceback
import logging
import cStringIO
+import pkg_resources
+from os.path import dirname as dn, join as jn
+from sqlalchemy import func
+from pylons.i18n.translation import _
+
+import rhodecode
from rhodecode.lib.vcs import get_backend
from rhodecode.lib.vcs.exceptions import RepositoryError
from rhodecode.lib.vcs.utils.lazy import LazyProperty
from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
from rhodecode import BACKENDS
from rhodecode.lib import helpers as h
from rhodecode.lib.utils2 import safe_str, safe_unicode
from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
- action_logger, EmptyChangeset, REMOVED_REPO_PAT
+ action_logger, REMOVED_REPO_PAT
from rhodecode.model import BaseModel
from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
- UserFollowing, UserLog, User, RepoGroup
+ UserFollowing, UserLog, User, RepoGroup, PullRequest
log = logging.getLogger(__name__)
@@ -63,6 +72,10 @@ class RepoTemp(object):
class CachedRepoList(object):
+ """
+ Cached repo list, uses in-memory cache after initialization, that is
+ super fast
+ """
def __init__(self, db_repo_list, repos_path, order_by=None):
self.db_repo_list = db_repo_list
@@ -77,8 +90,12 @@ class CachedRepoList(object):
return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
def __iter__(self):
+ # pre-propagated cache_map to save executing select statements
+ # for each repo
+ cache_map = CacheInvalidation.get_cache_map()
+
for dbr in self.db_repo_list:
- scmr = dbr.scm_instance_cached
+ scmr = dbr.scm_instance_cached(cache_map)
# check permission at this level
if not HasRepoPermissionAny(
'repository.read', 'repository.write', 'repository.admin'
@@ -99,7 +116,7 @@ class CachedRepoList(object):
tmp_d['name'] = dbr.repo_name
tmp_d['name_sort'] = tmp_d['name'].lower()
tmp_d['description'] = dbr.description
- tmp_d['description_sort'] = tmp_d['description']
+ tmp_d['description_sort'] = tmp_d['description'].lower()
tmp_d['last_change'] = last_change
tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
tmp_d['tip'] = tip.raw_id
@@ -116,6 +133,29 @@ class CachedRepoList(object):
yield tmp_d
+class SimpleCachedRepoList(CachedRepoList):
+ """
+ Lighter version of CachedRepoList without the scm initialisation
+ """
+
+ def __iter__(self):
+ for dbr in self.db_repo_list:
+ # check permission at this level
+ if not HasRepoPermissionAny(
+ 'repository.read', 'repository.write', 'repository.admin'
+ )(dbr.repo_name, 'get repo check'):
+ continue
+
+ tmp_d = {}
+ tmp_d['name'] = dbr.repo_name
+ tmp_d['name_sort'] = tmp_d['name'].lower()
+ tmp_d['description'] = dbr.description
+ tmp_d['description_sort'] = tmp_d['description'].lower()
+ tmp_d['dbrepo'] = dbr.get_dict()
+ tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
+ yield tmp_d
+
+
class GroupList(object):
def __init__(self, db_repo_group_list):
@@ -147,7 +187,7 @@ class ScmModel(BaseModel):
cls = Repository
if isinstance(instance, cls):
return instance
- elif isinstance(instance, int) or str(instance).isdigit():
+ elif isinstance(instance, int) or safe_str(instance).isdigit():
return cls.get(instance)
elif isinstance(instance, basestring):
return cls.get_by_repo_name(instance)
@@ -208,21 +248,29 @@ class ScmModel(BaseModel):
return repos
- def get_repos(self, all_repos=None, sort_key=None):
+ def get_repos(self, all_repos=None, sort_key=None, simple=False):
"""
Get all repos from db and for each repo create it's
backend instance and fill that backed with information from database
:param all_repos: list of repository names as strings
give specific repositories list, good for filtering
+
+ :param sort_key: initial sorting of repos
+ :param simple: use SimpleCachedList - one without the SCM info
"""
if all_repos is None:
all_repos = self.sa.query(Repository)\
.filter(Repository.group_id == None)\
- .order_by(Repository.repo_name).all()
-
- repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
- order_by=sort_key)
+ .order_by(func.lower(Repository.repo_name)).all()
+ if simple:
+ repo_iter = SimpleCachedRepoList(all_repos,
+ repos_path=self.repos_path,
+ order_by=sort_key)
+ else:
+ repo_iter = CachedRepoList(all_repos,
+ repos_path=self.repos_path,
+ order_by=sort_key)
return repo_iter
@@ -314,29 +362,33 @@ class ScmModel(BaseModel):
return f is not None
- def get_followers(self, repo_id):
- if not isinstance(repo_id, int):
- repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
+ def get_followers(self, repo):
+ repo = self._get_repo(repo)
return self.sa.query(UserFollowing)\
- .filter(UserFollowing.follows_repo_id == repo_id).count()
-
- def get_forks(self, repo_id):
- if not isinstance(repo_id, int):
- repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
+ .filter(UserFollowing.follows_repository == repo).count()
+ def get_forks(self, repo):
+ repo = self._get_repo(repo)
return self.sa.query(Repository)\
- .filter(Repository.fork_id == repo_id).count()
+ .filter(Repository.fork == repo).count()
+
+ def get_pull_requests(self, repo):
+ repo = self._get_repo(repo)
+ return self.sa.query(PullRequest)\
+ .filter(PullRequest.other_repo == repo).count()
def mark_as_fork(self, repo, fork, user):
repo = self.__get_repo(repo)
fork = self.__get_repo(fork)
+ if fork and repo.repo_id == fork.repo_id:
+ raise Exception("Cannot set repository as fork of itself")
repo.fork = fork
self.sa.add(repo)
return repo
- def pull_changes(self, repo_name, username):
- dbrepo = Repository.get_by_repo_name(repo_name)
+ def pull_changes(self, repo, username):
+ dbrepo = self.__get_repo(repo)
clone_uri = dbrepo.clone_uri
if not clone_uri:
raise Exception("This repository doesn't have a clone uri")
@@ -347,22 +399,28 @@ class ScmModel(BaseModel):
'ip': '',
'username': username,
'action': 'push_remote',
- 'repository': repo_name,
+ 'repository': dbrepo.repo_name,
'scm': repo.alias,
}
+ Repository.inject_ui(repo, extras=extras)
- # inject ui extra param to log this action via push logger
- for k, v in extras.items():
- repo._repo.ui.setconfig('rhodecode_extras', k, v)
-
- repo.pull(clone_uri)
- self.mark_for_invalidation(repo_name)
+ if repo.alias == 'git':
+ repo.fetch(clone_uri)
+ else:
+ repo.pull(clone_uri)
+ self.mark_for_invalidation(dbrepo.repo_name)
except:
log.error(traceback.format_exc())
raise
def commit_change(self, repo, repo_name, cs, user, author, message,
content, f_path):
+ """
+ Commits changes
+
+ :param repo: SCM instance
+
+ """
if repo.alias == 'hg':
from rhodecode.lib.vcs.backends.hg import \
@@ -385,12 +443,10 @@ class ScmModel(BaseModel):
author=author,
parents=[cs], branch=cs.branch)
- new_cs = tip.short_id
- action = 'push_local:%s' % new_cs
-
+ action = 'push_local:%s' % tip.raw_id
action_logger(user, action, repo_name)
-
self.mark_for_invalidation(repo_name)
+ return tip
def create_node(self, repo, repo_name, cs, user, author, message, content,
f_path):
@@ -425,12 +481,11 @@ class ScmModel(BaseModel):
tip = m.commit(message=message,
author=author,
parents=parents, branch=cs.branch)
- new_cs = tip.short_id
- action = 'push_local:%s' % new_cs
+ action = 'push_local:%s' % tip.raw_id
action_logger(user, action, repo_name)
-
self.mark_for_invalidation(repo_name)
+ return tip
def get_nodes(self, repo_name, revision, root_path='/', flat=True):
"""
@@ -464,3 +519,93 @@ class ScmModel(BaseModel):
def get_unread_journal(self):
return self.sa.query(UserLog).count()
+
+ def get_repo_landing_revs(self, repo=None):
+ """
+ Generates select option with tags branches and bookmarks (for hg only)
+ grouped by type
+
+ :param repo:
+ :type repo:
+ """
+
+ hist_l = []
+ choices = []
+ repo = self.__get_repo(repo)
+ hist_l.append(['tip', _('latest tip')])
+ choices.append('tip')
+ if not repo:
+ return choices, hist_l
+
+ repo = repo.scm_instance
+
+ branches_group = ([(k, k) for k, v in
+ repo.branches.iteritems()], _("Branches"))
+ hist_l.append(branches_group)
+ choices.extend([x[0] for x in branches_group[0]])
+
+ if repo.alias == 'hg':
+ bookmarks_group = ([(k, k) for k, v in
+ repo.bookmarks.iteritems()], _("Bookmarks"))
+ hist_l.append(bookmarks_group)
+ choices.extend([x[0] for x in bookmarks_group[0]])
+
+ tags_group = ([(k, k) for k, v in
+ repo.tags.iteritems()], _("Tags"))
+ hist_l.append(tags_group)
+ choices.extend([x[0] for x in tags_group[0]])
+
+ return choices, hist_l
+
+ def install_git_hook(self, repo, force_create=False):
+ """
+ Creates a rhodecode hook inside a git repository
+
+ :param repo: Instance of VCS repo
+ :param force_create: Create even if same name hook exists
+ """
+
+ loc = jn(repo.path, 'hooks')
+ if not repo.bare:
+ loc = jn(repo.path, '.git', 'hooks')
+ if not os.path.isdir(loc):
+ os.makedirs(loc)
+
+ tmpl_post = pkg_resources.resource_string(
+ 'rhodecode', jn('config', 'post_receive_tmpl.py')
+ )
+ tmpl_pre = pkg_resources.resource_string(
+ 'rhodecode', jn('config', 'pre_receive_tmpl.py')
+ )
+
+ for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
+ _hook_file = jn(loc, '%s-receive' % h_type)
+ _rhodecode_hook = False
+ log.debug('Installing git hook in repo %s' % repo)
+ if os.path.exists(_hook_file):
+ # let's take a look at this hook, maybe it's rhodecode ?
+ log.debug('hook exists, checking if it is from rhodecode')
+ _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
+ with open(_hook_file, 'rb') as f:
+ data = f.read()
+ matches = re.compile(r'(?:%s)\s*=\s*(.*)'
+ % 'RC_HOOK_VER').search(data)
+ if matches:
+ try:
+ ver = matches.groups()[0]
+ log.debug('got %s it is rhodecode' % (ver))
+ _rhodecode_hook = True
+ except:
+ log.error(traceback.format_exc())
+ else:
+ # there is no hook in this dir, so we want to create one
+ _rhodecode_hook = True
+
+ if _rhodecode_hook or force_create:
+ log.debug('writing %s hook file !' % h_type)
+ with open(_hook_file, 'wb') as f:
+ tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
+ f.write(tmpl)
+ os.chmod(_hook_file, 0755)
+ else:
+ log.debug('skipping writing hook file')
diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py
index 764ca911..bc23c817 100644
--- a/rhodecode/model/user.py
+++ b/rhodecode/model/user.py
@@ -25,48 +25,31 @@
import logging
import traceback
-
+import itertools
from pylons import url
from pylons.i18n.translation import _
+from sqlalchemy.exc import DatabaseError
+from sqlalchemy.orm import joinedload
+
from rhodecode.lib.utils2 import safe_unicode, generate_api_key
from rhodecode.lib.caching_query import FromCache
-
from rhodecode.model import BaseModel
from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
- Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup,\
- UsersGroupRepoGroupToPerm
+ Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
+ UserEmailMap
from rhodecode.lib.exceptions import DefaultUserException, \
UserOwnsReposException
-from sqlalchemy.exc import DatabaseError
-
-from sqlalchemy.orm import joinedload
log = logging.getLogger(__name__)
-
-PERM_WEIGHTS = {
- 'repository.none': 0,
- 'repository.read': 1,
- 'repository.write': 3,
- 'repository.admin': 4,
- 'group.none': 0,
- 'group.read': 1,
- 'group.write': 3,
- 'group.admin': 4,
-}
+PERM_WEIGHTS = Permission.PERM_WEIGHTS
class UserModel(BaseModel):
-
- def __get_user(self, user):
- return self._get_instance(User, user, callback=User.get_by_username)
-
- def __get_perm(self, permission):
- return self._get_instance(Permission, permission,
- callback=Permission.get_by_key)
+ cls = User
def get(self, user_id, cache=False):
user = self.sa.query(User)
@@ -76,7 +59,7 @@ class UserModel(BaseModel):
return user.get(user_id)
def get_user(self, user):
- return self.__get_user(user)
+ return self._get_user(user)
def get_by_username(self, username, cache=False, case_insensitive=False):
@@ -90,13 +73,21 @@ class UserModel(BaseModel):
"get_user_%s" % username))
return user.scalar()
+ def get_by_email(self, email, cache=False, case_insensitive=False):
+ return User.get_by_email(email, case_insensitive, cache)
+
def get_by_api_key(self, api_key, cache=False):
return User.get_by_api_key(api_key, cache)
def create(self, form_data):
+ from rhodecode.lib.auth import get_crypt_password
try:
new_user = User()
for k, v in form_data.items():
+ if k == 'password':
+ v = get_crypt_password(v)
+ if k == 'firstname':
+ k = 'name'
setattr(new_user, k, v)
new_user.api_key = generate_api_key(form_data['username'])
@@ -106,8 +97,8 @@ class UserModel(BaseModel):
log.error(traceback.format_exc())
raise
- def create_or_update(self, username, password, email, name, lastname,
- active=True, admin=False, ldap_dn=None):
+ def create_or_update(self, username, password, email, firstname='',
+ lastname='', active=True, admin=False, ldap_dn=None):
"""
Creates a new instance if not found, or updates current one
@@ -115,7 +106,7 @@ class UserModel(BaseModel):
:param password:
:param email:
:param active:
- :param name:
+ :param firstname:
:param lastname:
:param active:
:param admin:
@@ -129,19 +120,23 @@ class UserModel(BaseModel):
if user is None:
log.debug('creating new user %s' % username)
new_user = User()
+ edit = False
else:
log.debug('updating user %s' % username)
new_user = user
+ edit = True
try:
new_user.username = username
new_user.admin = admin
- new_user.password = get_crypt_password(password)
- new_user.api_key = generate_api_key(username)
+ # set password only if creating an user or password is changed
+ if edit is False or user.password != password:
+ new_user.password = get_crypt_password(password)
+ new_user.api_key = generate_api_key(username)
new_user.email = email
new_user.active = active
new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
- new_user.name = name
+ new_user.name = firstname
new_user.lastname = lastname
self.sa.add(new_user)
return new_user
@@ -252,6 +247,7 @@ class UserModel(BaseModel):
raise
def update(self, user_id, form_data):
+ from rhodecode.lib.auth import get_crypt_password
try:
user = self.get(user_id, cache=False)
if user.username == 'default':
@@ -260,29 +256,56 @@ class UserModel(BaseModel):
" crucial for entire application"))
for k, v in form_data.items():
- if k == 'new_password' and v != '':
- user.password = v
+ if k == 'new_password' and v:
+ user.password = get_crypt_password(v)
user.api_key = generate_api_key(user.username)
else:
+ if k == 'firstname':
+ k = 'name'
setattr(user, k, v)
+ self.sa.add(user)
+ except:
+ log.error(traceback.format_exc())
+ raise
+ def update_user(self, user, **kwargs):
+ from rhodecode.lib.auth import get_crypt_password
+ try:
+ user = self._get_user(user)
+ if user.username == 'default':
+ raise DefaultUserException(
+ _("You can't Edit this user since it's"
+ " crucial for entire application")
+ )
+
+ for k, v in kwargs.items():
+ if k == 'password' and v:
+ v = get_crypt_password(v)
+ user.api_key = generate_api_key(user.username)
+
+ setattr(user, k, v)
self.sa.add(user)
+ return user
except:
log.error(traceback.format_exc())
raise
def update_my_account(self, user_id, form_data):
+ from rhodecode.lib.auth import get_crypt_password
try:
user = self.get(user_id, cache=False)
if user.username == 'default':
raise DefaultUserException(
- _("You can't Edit this user since it's"
- " crucial for entire application"))
+ _("You can't Edit this user since it's"
+ " crucial for entire application")
+ )
for k, v in form_data.items():
- if k == 'new_password' and v != '':
- user.password = v
+ if k == 'new_password' and v:
+ user.password = get_crypt_password(v)
user.api_key = generate_api_key(user.username)
else:
+ if k == 'firstname':
+ k = 'name'
if k not in ['admin', 'active']:
setattr(user, k, v)
@@ -292,7 +315,7 @@ class UserModel(BaseModel):
raise
def delete(self, user):
- user = self.__get_user(user)
+ user = self._get_user(user)
try:
if user.username == 'default':
@@ -399,11 +422,11 @@ class UserModel(BaseModel):
return user
#==================================================================
- # set default permissions first for repositories and groups
+ # SET DEFAULTS GLOBAL, REPOS, REPOS GROUPS
#==================================================================
uid = user.user_id
- # default global permissions
+ # default global permissions taken fron the default user
default_global_perms = self.sa.query(UserToPerm)\
.filter(UserToPerm.user_id == default_user_id)
@@ -431,59 +454,70 @@ class UserModel(BaseModel):
p = perm.Permission.permission_name
user.permissions[GK][rg_k] = p
- #==================================================================
- # overwrite defaults with user permissions if any found
- #==================================================================
+ #======================================================================
+ # !! OVERRIDE GLOBALS !! with user permissions if any found
+ #======================================================================
+ # those can be configured from groups or users explicitly
+ _configurable = set(['hg.fork.none', 'hg.fork.repository',
+ 'hg.create.none', 'hg.create.repository'])
- # user global permissions
+ # USER GROUPS comes first
+ # users group global permissions
+ user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
+ .options(joinedload(UsersGroupToPerm.permission))\
+ .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
+ UsersGroupMember.users_group_id))\
+ .filter(UsersGroupMember.user_id == uid)\
+ .order_by(UsersGroupToPerm.users_group_id)\
+ .all()
+ #need to group here by groups since user can be in more than one group
+ _grouped = [[x, list(y)] for x, y in
+ itertools.groupby(user_perms_from_users_groups,
+ lambda x:x.users_group)]
+ for gr, perms in _grouped:
+ # since user can be in multiple groups iterate over them and
+ # select the lowest permissions first (more explicit)
+ ##TODO: do this^^
+ if not gr.inherit_default_permissions:
+ # NEED TO IGNORE all configurable permissions and
+ # replace them with explicitly set
+ user.permissions[GLOBAL] = user.permissions[GLOBAL]\
+ .difference(_configurable)
+ for perm in perms:
+ user.permissions[GLOBAL].add(perm.permission.permission_name)
+
+ # user specific global permissions
user_perms = self.sa.query(UserToPerm)\
.options(joinedload(UserToPerm.permission))\
.filter(UserToPerm.user_id == uid).all()
- for perm in user_perms:
- user.permissions[GLOBAL].add(perm.permission.permission_name)
-
- # user explicit permissions for repositories
- user_repo_perms = \
- self.sa.query(UserRepoToPerm, Permission, Repository)\
- .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
- .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
- .filter(UserRepoToPerm.user_id == uid)\
- .all()
+ if not user.inherit_default_permissions:
+ # NEED TO IGNORE all configurable permissions and
+ # replace them with explicitly set
+ user.permissions[GLOBAL] = user.permissions[GLOBAL]\
+ .difference(_configurable)
- for perm in user_repo_perms:
- # set admin if owner
- r_k = perm.UserRepoToPerm.repository.repo_name
- if perm.Repository.user_id == uid:
- p = 'repository.admin'
- else:
- p = perm.Permission.permission_name
- user.permissions[RK][r_k] = p
+ for perm in user_perms:
+ user.permissions[GLOBAL].add(perm.permission.permission_name)
- # USER GROUP
- #==================================================================
+ #======================================================================
+ # !! REPO PERMISSIONS !!
+ #======================================================================
+ #======================================================================
# check if user is part of user groups for this repository and
- # fill in (or replace with higher) permissions
- #==================================================================
-
- # users group global
- user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
- .options(joinedload(UsersGroupToPerm.permission))\
- .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
- UsersGroupMember.users_group_id))\
- .filter(UsersGroupMember.user_id == uid).all()
-
- for perm in user_perms_from_users_groups:
- user.permissions[GLOBAL].add(perm.permission.permission_name)
-
+ # fill in (or NOT replace with higher `or 1` permissions
+ #======================================================================
# users group for repositories permissions
user_repo_perms_from_users_groups = \
self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
- .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
- .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
- .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
- .filter(UsersGroupMember.user_id == uid)\
- .all()
+ .join((Repository, UsersGroupRepoToPerm.repository_id ==
+ Repository.repo_id))\
+ .join((Permission, UsersGroupRepoToPerm.permission_id ==
+ Permission.permission_id))\
+ .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
+ UsersGroupMember.users_group_id))\
+ .filter(UsersGroupMember.user_id == uid)\
+ .all()
for perm in user_repo_perms_from_users_groups:
r_k = perm.UsersGroupRepoToPerm.repository.repo_name
@@ -491,9 +525,28 @@ class UserModel(BaseModel):
cur_perm = user.permissions[RK][r_k]
# overwrite permission only if it's greater than permission
# given from other sources
- if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
+ if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm] or 1: # disable check
user.permissions[RK][r_k] = p
+ # user explicit permissions for repositories
+ user_repo_perms = \
+ self.sa.query(UserRepoToPerm, Permission, Repository)\
+ .join((Repository, UserRepoToPerm.repository_id ==
+ Repository.repo_id))\
+ .join((Permission, UserRepoToPerm.permission_id ==
+ Permission.permission_id))\
+ .filter(UserRepoToPerm.user_id == uid)\
+ .all()
+
+ for perm in user_repo_perms:
+ # set admin if owner
+ r_k = perm.UserRepoToPerm.repository.repo_name
+ if perm.Repository.user_id == uid:
+ p = 'repository.admin'
+ else:
+ p = perm.Permission.permission_name
+ user.permissions[RK][r_k] = p
+
# REPO GROUP
#==================================================================
# get access for this user for repos group and override defaults
@@ -541,11 +594,8 @@ class UserModel(BaseModel):
return user
def has_perm(self, user, perm):
- if not isinstance(perm, Permission):
- raise Exception('perm needs to be an instance of Permission class '
- 'got %s instead' % type(perm))
-
- user = self.__get_user(user)
+ perm = self._get_perm(perm)
+ user = self._get_user(user)
return UserToPerm.query().filter(UserToPerm.user == user)\
.filter(UserToPerm.permission == perm).scalar() is not None
@@ -557,8 +607,8 @@ class UserModel(BaseModel):
:param user:
:param perm:
"""
- user = self.__get_user(user)
- perm = self.__get_perm(perm)
+ user = self._get_user(user)
+ perm = self._get_perm(perm)
# if this permission is already granted skip it
_perm = UserToPerm.query()\
.filter(UserToPerm.user == user)\
@@ -578,8 +628,8 @@ class UserModel(BaseModel):
:param user:
:param perm:
"""
- user = self.__get_user(user)
- perm = self.__get_perm(perm)
+ user = self._get_user(user)
+ perm = self._get_perm(perm)
obj = UserToPerm.query()\
.filter(UserToPerm.user == user)\
@@ -587,3 +637,33 @@ class UserModel(BaseModel):
.scalar()
if obj:
self.sa.delete(obj)
+
+ def add_extra_email(self, user, email):
+ """
+ Adds email address to UserEmailMap
+
+ :param user:
+ :param email:
+ """
+ from rhodecode.model import forms
+ form = forms.UserExtraEmailForm()()
+ data = form.to_python(dict(email=email))
+ user = self._get_user(user)
+
+ obj = UserEmailMap()
+ obj.user = user
+ obj.email = data['email']
+ self.sa.add(obj)
+ return obj
+
+ def delete_extra_email(self, user, email_id):
+ """
+ Removes email address from UserEmailMap
+
+ :param user:
+ :param email_id:
+ """
+ user = self._get_user(user)
+ obj = UserEmailMap.query().get(email_id)
+ if obj:
+ self.sa.delete(obj)
diff --git a/rhodecode/model/users_group.py b/rhodecode/model/users_group.py
index 378edea5..f2c777f7 100644
--- a/rhodecode/model/users_group.py
+++ b/rhodecode/model/users_group.py
@@ -37,20 +37,18 @@ log = logging.getLogger(__name__)
class UsersGroupModel(BaseModel):
- def __get_user(self, user):
- return self._get_instance(User, user, callback=User.get_by_username)
+ cls = UsersGroup
def __get_users_group(self, users_group):
return self._get_instance(UsersGroup, users_group,
callback=UsersGroup.get_by_group_name)
- def __get_perm(self, permission):
- return self._get_instance(Permission, permission,
- callback=Permission.get_by_key)
-
def get(self, users_group_id, cache=False):
return UsersGroup.get(users_group_id)
+ def get_group(self, users_group):
+ return self.__get_users_group(users_group)
+
def get_by_name(self, name, cache=False, case_insensitive=False):
return UsersGroup.get_by_group_name(name, cache, case_insensitive)
@@ -115,7 +113,7 @@ class UsersGroupModel(BaseModel):
def add_user_to_group(self, users_group, user):
users_group = self.__get_users_group(users_group)
- user = self.__get_user(user)
+ user = self._get_user(user)
for m in users_group.members:
u = m.user
@@ -138,7 +136,7 @@ class UsersGroupModel(BaseModel):
def remove_user_from_group(self, users_group, user):
users_group = self.__get_users_group(users_group)
- user = self.__get_user(user)
+ user = self._get_user(user)
users_group_member = None
for m in users_group.members:
@@ -160,17 +158,15 @@ class UsersGroupModel(BaseModel):
def has_perm(self, users_group, perm):
users_group = self.__get_users_group(users_group)
- perm = self.__get_perm(perm)
+ perm = self._get_perm(perm)
return UsersGroupToPerm.query()\
.filter(UsersGroupToPerm.users_group == users_group)\
.filter(UsersGroupToPerm.permission == perm).scalar() is not None
def grant_perm(self, users_group, perm):
- if not isinstance(perm, Permission):
- raise Exception('perm needs to be an instance of Permission class')
-
users_group = self.__get_users_group(users_group)
+ perm = self._get_perm(perm)
# if this permission is already granted skip it
_perm = UsersGroupToPerm.query()\
@@ -187,7 +183,7 @@ class UsersGroupModel(BaseModel):
def revoke_perm(self, users_group, perm):
users_group = self.__get_users_group(users_group)
- perm = self.__get_perm(perm)
+ perm = self._get_perm(perm)
obj = UsersGroupToPerm.query()\
.filter(UsersGroupToPerm.users_group == users_group)\
diff --git a/rhodecode/model/validators.py b/rhodecode/model/validators.py
new file mode 100644
index 00000000..8ccf6b46
--- /dev/null
+++ b/rhodecode/model/validators.py
@@ -0,0 +1,676 @@
+"""
+Set of generic validators
+"""
+import os
+import re
+import formencode
+import logging
+from collections import defaultdict
+from pylons.i18n.translation import _
+from webhelpers.pylonslib.secure_form import authentication_token
+
+from formencode.validators import (
+ UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
+ NotEmpty
+)
+from rhodecode.lib.compat import OrderedSet
+from rhodecode.lib.utils import repo_name_slug
+from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
+ ChangesetStatus
+from rhodecode.lib.exceptions import LdapImportError
+from rhodecode.config.routing import ADMIN_PREFIX
+
+# silence warnings and pylint
+UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
+ NotEmpty
+
+log = logging.getLogger(__name__)
+
+
+class UniqueList(formencode.FancyValidator):
+ """
+ Unique List !
+ """
+ messages = dict(
+ empty=_('Value cannot be an empty list'),
+ missing_value=_('Value cannot be an empty list'),
+ )
+
+ def _to_python(self, value, state):
+ if isinstance(value, list):
+ return value
+ elif isinstance(value, set):
+ return list(value)
+ elif isinstance(value, tuple):
+ return list(value)
+ elif value is None:
+ return []
+ else:
+ return [value]
+
+ def empty_value(self, value):
+ return []
+
+
+class StateObj(object):
+ """
+ this is needed to translate the messages using _() in validators
+ """
+ _ = staticmethod(_)
+
+
+def M(self, key, state=None, **kwargs):
+ """
+ returns string from self.message based on given key,
+ passed kw params are used to substitute %(named)s params inside
+ translated strings
+
+ :param msg:
+ :param state:
+ """
+ if state is None:
+ state = StateObj()
+ else:
+ state._ = staticmethod(_)
+ #inject validator into state object
+ return self.message(key, state, **kwargs)
+
+
+def ValidUsername(edit=False, old_data={}):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'username_exists': _(u'Username "%(username)s" already exists'),
+ 'system_invalid_username':
+ _(u'Username "%(username)s" is forbidden'),
+ 'invalid_username':
+ _(u'Username may only contain alphanumeric characters '
+ 'underscores, periods or dashes and must begin with '
+ 'alphanumeric character')
+ }
+
+ def validate_python(self, value, state):
+ if value in ['default', 'new_user']:
+ msg = M(self, 'system_invalid_username', state, username=value)
+ raise formencode.Invalid(msg, value, state)
+ #check if user is unique
+ old_un = None
+ if edit:
+ old_un = User.get(old_data.get('user_id')).username
+
+ if old_un != value or not edit:
+ if User.get_by_username(value, case_insensitive=True):
+ msg = M(self, 'username_exists', state, username=value)
+ raise formencode.Invalid(msg, value, state)
+
+ if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
+ msg = M(self, 'invalid_username', state)
+ raise formencode.Invalid(msg, value, state)
+ return _validator
+
+
+def ValidRepoUser():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_username': _(u'Username %(username)s is not valid')
+ }
+
+ def validate_python(self, value, state):
+ try:
+ User.query().filter(User.active == True)\
+ .filter(User.username == value).one()
+ except Exception:
+ msg = M(self, 'invalid_username', state, username=value)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(username=msg)
+ )
+
+ return _validator
+
+
+def ValidUsersGroup(edit=False, old_data={}):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_group': _(u'Invalid users group name'),
+ 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
+ 'invalid_usersgroup_name':
+ _(u'users group name may only contain alphanumeric '
+ 'characters underscores, periods or dashes and must begin '
+ 'with alphanumeric character')
+ }
+
+ def validate_python(self, value, state):
+ if value in ['default']:
+ msg = M(self, 'invalid_group', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(users_group_name=msg)
+ )
+ #check if group is unique
+ old_ugname = None
+ if edit:
+ old_id = old_data.get('users_group_id')
+ old_ugname = UsersGroup.get(old_id).users_group_name
+
+ if old_ugname != value or not edit:
+ is_existing_group = UsersGroup.get_by_group_name(value,
+ case_insensitive=True)
+ if is_existing_group:
+ msg = M(self, 'group_exist', state, usersgroup=value)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(users_group_name=msg)
+ )
+
+ if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
+ msg = M(self, 'invalid_usersgroup_name', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(users_group_name=msg)
+ )
+
+ return _validator
+
+
+def ValidReposGroup(edit=False, old_data={}):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'group_parent_id': _(u'Cannot assign this group as parent'),
+ 'group_exists': _(u'Group "%(group_name)s" already exists'),
+ 'repo_exists':
+ _(u'Repository with name "%(group_name)s" already exists')
+ }
+
+ def validate_python(self, value, state):
+ # TODO WRITE VALIDATIONS
+ group_name = value.get('group_name')
+ group_parent_id = value.get('group_parent_id')
+
+ # slugify repo group just in case :)
+ slug = repo_name_slug(group_name)
+
+ # check for parent of self
+ parent_of_self = lambda: (
+ old_data['group_id'] == int(group_parent_id)
+ if group_parent_id else False
+ )
+ if edit and parent_of_self():
+ msg = M(self, 'group_parent_id', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(group_parent_id=msg)
+ )
+
+ old_gname = None
+ if edit:
+ old_gname = RepoGroup.get(old_data.get('group_id')).group_name
+
+ if old_gname != group_name or not edit:
+
+ # check group
+ gr = RepoGroup.query()\
+ .filter(RepoGroup.group_name == slug)\
+ .filter(RepoGroup.group_parent_id == group_parent_id)\
+ .scalar()
+
+ if gr:
+ msg = M(self, 'group_exists', state, group_name=slug)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(group_name=msg)
+ )
+
+ # check for same repo
+ repo = Repository.query()\
+ .filter(Repository.repo_name == slug)\
+ .scalar()
+
+ if repo:
+ msg = M(self, 'repo_exists', state, group_name=slug)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(group_name=msg)
+ )
+
+ return _validator
+
+
+def ValidPassword():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_password':
+ _(u'Invalid characters (non-ascii) in password')
+ }
+
+ def validate_python(self, value, state):
+ try:
+ (value or '').decode('ascii')
+ except UnicodeError:
+ msg = M(self, 'invalid_password', state)
+ raise formencode.Invalid(msg, value, state,)
+ return _validator
+
+
+def ValidPasswordsMatch():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'password_mismatch': _(u'Passwords do not match'),
+ }
+
+ def validate_python(self, value, state):
+
+ pass_val = value.get('password') or value.get('new_password')
+ if pass_val != value['password_confirmation']:
+ msg = M(self, 'password_mismatch', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(password_confirmation=msg)
+ )
+ return _validator
+
+
+def ValidAuth():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_password': _(u'invalid password'),
+ 'invalid_username': _(u'invalid user name'),
+ 'disabled_account': _(u'Your account is disabled')
+ }
+
+ def validate_python(self, value, state):
+ from rhodecode.lib.auth import authenticate
+
+ password = value['password']
+ username = value['username']
+
+ if not authenticate(username, password):
+ user = User.get_by_username(username)
+ if user and user.active is False:
+ log.warning('user %s is disabled' % username)
+ msg = M(self, 'disabled_account', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(username=msg)
+ )
+ else:
+ log.warning('user %s failed to authenticate' % username)
+ msg = M(self, 'invalid_username', state)
+ msg2 = M(self, 'invalid_password', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(username=msg, password=msg2)
+ )
+ return _validator
+
+
+def ValidAuthToken():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_token': _(u'Token mismatch')
+ }
+
+ def validate_python(self, value, state):
+ if value != authentication_token():
+ msg = M(self, 'invalid_token', state)
+ raise formencode.Invalid(msg, value, state)
+ return _validator
+
+
+def ValidRepoName(edit=False, old_data={}):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_repo_name':
+ _(u'Repository name %(repo)s is disallowed'),
+ 'repository_exists':
+ _(u'Repository named %(repo)s already exists'),
+ 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
+ 'exists in group "%(group)s"'),
+ 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
+ 'already exists')
+ }
+
+ def _to_python(self, value, state):
+ repo_name = repo_name_slug(value.get('repo_name', ''))
+ repo_group = value.get('repo_group')
+ if repo_group:
+ gr = RepoGroup.get(repo_group)
+ group_path = gr.full_path
+ group_name = gr.group_name
+ # value needs to be aware of group name in order to check
+ # db key This is an actual just the name to store in the
+ # database
+ repo_name_full = group_path + RepoGroup.url_sep() + repo_name
+ else:
+ group_name = group_path = ''
+ repo_name_full = repo_name
+
+ value['repo_name'] = repo_name
+ value['repo_name_full'] = repo_name_full
+ value['group_path'] = group_path
+ value['group_name'] = group_name
+ return value
+
+ def validate_python(self, value, state):
+
+ repo_name = value.get('repo_name')
+ repo_name_full = value.get('repo_name_full')
+ group_path = value.get('group_path')
+ group_name = value.get('group_name')
+
+ if repo_name in [ADMIN_PREFIX, '']:
+ msg = M(self, 'invalid_repo_name', state, repo=repo_name)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_name=msg)
+ )
+
+ rename = old_data.get('repo_name') != repo_name_full
+ create = not edit
+ if rename or create:
+
+ if group_path != '':
+ if Repository.get_by_repo_name(repo_name_full):
+ msg = M(self, 'repository_in_group_exists', state,
+ repo=repo_name, group=group_name)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_name=msg)
+ )
+ elif RepoGroup.get_by_group_name(repo_name_full):
+ msg = M(self, 'same_group_exists', state,
+ repo=repo_name)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_name=msg)
+ )
+
+ elif Repository.get_by_repo_name(repo_name_full):
+ msg = M(self, 'repository_exists', state,
+ repo=repo_name)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_name=msg)
+ )
+ return value
+ return _validator
+
+
+def ValidForkName(*args, **kwargs):
+ return ValidRepoName(*args, **kwargs)
+
+
+def SlugifyName():
+ class _validator(formencode.validators.FancyValidator):
+
+ def _to_python(self, value, state):
+ return repo_name_slug(value)
+
+ def validate_python(self, value, state):
+ pass
+
+ return _validator
+
+
+def ValidCloneUri():
+ from rhodecode.lib.utils import make_ui
+
+ def url_handler(repo_type, url, ui=None):
+ if repo_type == 'hg':
+ from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
+ from mercurial.httppeer import httppeer
+ if url.startswith('http'):
+ ## initially check if it's at least the proper URL
+ ## or does it pass basic auth
+ MercurialRepository._check_url(url)
+ httppeer(ui, url)._capabilities()
+ elif url.startswith('svn+http'):
+ from hgsubversion.svnrepo import svnremoterepo
+ svnremoterepo(ui, url).capabilities
+ elif url.startswith('git+http'):
+ raise NotImplementedError()
+
+ elif repo_type == 'git':
+ from rhodecode.lib.vcs.backends.git.repository import GitRepository
+ if url.startswith('http'):
+ ## initially check if it's at least the proper URL
+ ## or does it pass basic auth
+ GitRepository._check_url(url)
+ elif url.startswith('svn+http'):
+ raise NotImplementedError()
+ elif url.startswith('hg+http'):
+ raise NotImplementedError()
+
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'clone_uri': _(u'invalid clone url'),
+ 'invalid_clone_uri': _(u'Invalid clone url, provide a '
+ 'valid clone http(s)/svn+http(s) url')
+ }
+
+ def validate_python(self, value, state):
+ repo_type = value.get('repo_type')
+ url = value.get('clone_uri')
+
+ if not url:
+ pass
+ else:
+ try:
+ url_handler(repo_type, url, make_ui('db', clear_session=False))
+ except Exception:
+ log.exception('Url validation failed')
+ msg = M(self, 'clone_uri')
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(clone_uri=msg)
+ )
+ return _validator
+
+
+def ValidForkType(old_data={}):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_fork_type': _(u'Fork have to be the same type as parent')
+ }
+
+ def validate_python(self, value, state):
+ if old_data['repo_type'] != value:
+ msg = M(self, 'invalid_fork_type', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_type=msg)
+ )
+ return _validator
+
+
+def ValidPerms(type_='repo'):
+ if type_ == 'group':
+ EMPTY_PERM = 'group.none'
+ elif type_ == 'repo':
+ EMPTY_PERM = 'repository.none'
+
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'perm_new_member_name':
+ _(u'This username or users group name is not valid')
+ }
+
+ def to_python(self, value, state):
+ perms_update = OrderedSet()
+ perms_new = OrderedSet()
+ # build a list of permission to update and new permission to create
+
+ #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
+ new_perms_group = defaultdict(dict)
+ for k, v in value.copy().iteritems():
+ if k.startswith('perm_new_member'):
+ del value[k]
+ _type, part = k.split('perm_new_member_')
+ args = part.split('_')
+ if len(args) == 1:
+ new_perms_group[args[0]]['perm'] = v
+ elif len(args) == 2:
+ _key, pos = args
+ new_perms_group[pos][_key] = v
+
+ # fill new permissions in order of how they were added
+ for k in sorted(map(int, new_perms_group.keys())):
+ perm_dict = new_perms_group[str(k)]
+ new_member = perm_dict['name']
+ new_perm = perm_dict['perm']
+ new_type = perm_dict['type']
+ if new_member and new_perm and new_type:
+ perms_new.add((new_member, new_perm, new_type))
+
+ for k, v in value.iteritems():
+ if k.startswith('u_perm_') or k.startswith('g_perm_'):
+ member = k[7:]
+ t = {'u': 'user',
+ 'g': 'users_group'
+ }[k[0]]
+ if member == 'default':
+ if value.get('private'):
+ # set none for default when updating to
+ # private repo
+ v = EMPTY_PERM
+ perms_update.add((member, v, t))
+
+ value['perms_updates'] = list(perms_update)
+ value['perms_new'] = list(perms_new)
+
+ # update permissions
+ for k, v, t in perms_new:
+ try:
+ if t is 'user':
+ self.user_db = User.query()\
+ .filter(User.active == True)\
+ .filter(User.username == k).one()
+ if t is 'users_group':
+ self.user_db = UsersGroup.query()\
+ .filter(UsersGroup.users_group_active == True)\
+ .filter(UsersGroup.users_group_name == k).one()
+
+ except Exception:
+ log.exception('Updated permission failed')
+ msg = M(self, 'perm_new_member_type', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(perm_new_member_name=msg)
+ )
+ return value
+ return _validator
+
+
+def ValidSettings():
+ class _validator(formencode.validators.FancyValidator):
+ def _to_python(self, value, state):
+ # settings form can't edit user
+ if 'user' in value:
+ del value['user']
+ return value
+
+ def validate_python(self, value, state):
+ pass
+ return _validator
+
+
+def ValidPath():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_path': _(u'This is not a valid path')
+ }
+
+ def validate_python(self, value, state):
+ if not os.path.isdir(value):
+ msg = M(self, 'invalid_path', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(paths_root_path=msg)
+ )
+ return _validator
+
+
+def UniqSystemEmail(old_data={}):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'email_taken': _(u'This e-mail address is already taken')
+ }
+
+ def _to_python(self, value, state):
+ return value.lower()
+
+ def validate_python(self, value, state):
+ if (old_data.get('email') or '').lower() != value:
+ user = User.get_by_email(value, case_insensitive=True)
+ if user:
+ msg = M(self, 'email_taken', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(email=msg)
+ )
+ return _validator
+
+
+def ValidSystemEmail():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
+ }
+
+ def _to_python(self, value, state):
+ return value.lower()
+
+ def validate_python(self, value, state):
+ user = User.get_by_email(value, case_insensitive=True)
+ if user is None:
+ msg = M(self, 'non_existing_email', state, email=value)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(email=msg)
+ )
+
+ return _validator
+
+
+def LdapLibValidator():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+
+ }
+
+ def validate_python(self, value, state):
+ try:
+ import ldap
+ ldap # pyflakes silence !
+ except ImportError:
+ raise LdapImportError()
+
+ return _validator
+
+
+def AttrLoginValidator():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'invalid_cn':
+ _(u'The LDAP Login attribute of the CN must be specified - '
+ 'this is the name of the attribute that is equivalent '
+ 'to "username"')
+ }
+
+ def validate_python(self, value, state):
+ if not value or not isinstance(value, (str, unicode)):
+ msg = M(self, 'invalid_cn', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(ldap_attr_login=msg)
+ )
+
+ return _validator
+
+
+def NotReviewedRevisions():
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'rev_already_reviewed':
+ _(u'Revisions %(revs)s are already part of pull request '
+ 'or have set status')
+ }
+
+ def validate_python(self, value, state):
+ # check revisions if they are not reviewed, or a part of another
+ # pull request
+ statuses = ChangesetStatus.query()\
+ .filter(ChangesetStatus.revision.in_(value)).all()
+ errors = []
+ for cs in statuses:
+ if cs.pull_request_id:
+ errors.append(['pull_req', cs.revision[:12]])
+ elif cs.status:
+ errors.append(['status', cs.revision[:12]])
+
+ if errors:
+ revs = ','.join([x[1] for x in errors])
+ msg = M(self, 'rev_already_reviewed', state, revs=revs)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(revisions=revs)
+ )
+
+ return _validator
diff --git a/rhodecode/public/css/codemirror.css b/rhodecode/public/css/codemirror.css
index 9dcd86de..191ac25a 100644
--- a/rhodecode/public/css/codemirror.css
+++ b/rhodecode/public/css/codemirror.css
@@ -1,14 +1,57 @@
.CodeMirror {
- overflow: auto;
- height: 450px;
line-height: 1em;
font-family: monospace;
- _position: relative; /* IE6 hack */
- margin:20px;
+
+ /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */
+ position: relative;
+ /* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */
+ overflow: hidden;
+}
+
+.CodeMirror-scroll {
+ overflow-x: auto;
+ overflow-y: hidden;
+ height: 300px;
+ /* This is needed to prevent an IE[67] bug where the scrolled content
+ is visible outside of the scrolling box. */
+ position: relative;
+ outline: none;
+}
+
+/* Vertical scrollbar */
+.CodeMirror-scrollbar {
+ float: right;
+ overflow-x: hidden;
+ overflow-y: scroll;
+
+ /* This corrects for the 1px gap introduced to the left of the scrollbar
+ by the rule for .CodeMirror-scrollbar-inner. */
+ margin-left: -1px;
+}
+.CodeMirror-scrollbar-inner {
+ /* This needs to have a nonzero width in order for the scrollbar to appear
+ in Firefox and IE9. */
+ width: 1px;
+}
+.CodeMirror-scrollbar.cm-sb-overlap {
+ /* Ensure that the scrollbar appears in Lion, and that it overlaps the content
+ rather than sitting to the right of it. */
+ position: absolute;
+ z-index: 1;
+ float: none;
+ right: 0;
+ min-width: 12px;
+}
+.CodeMirror-scrollbar.cm-sb-nonoverlap {
+ min-width: 12px;
+}
+.CodeMirror-scrollbar.cm-sb-ie7 {
+ min-width: 18px;
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
+ z-index: 10;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
@@ -18,9 +61,16 @@
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
+ white-space: pre !important;
}
.CodeMirror-lines {
padding: .4em;
+ white-space: pre;
+ cursor: text;
+}
+.CodeMirror-lines * {
+ /* Necessary for throw-scrolling to decelerate properly on Safari. */
+ pointer-events: none;
}
.CodeMirror pre {
@@ -30,26 +80,89 @@
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
+ font-size: inherit;
+ padding: 0; margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
}
-.CodeMirror-cursor {
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+.CodeMirror-wrap .CodeMirror-scroll {
+ overflow-x: hidden;
+}
+
+.CodeMirror textarea {
+ outline: none !important;
+}
+
+.CodeMirror pre.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
- border-left: 1px solid black !important;
+ border-left: 1px solid black;
+ border-right: none;
+ width: 0;
+}
+.cm-keymap-fat-cursor pre.CodeMirror-cursor {
+ width: auto;
+ border: 0;
+ background: transparent;
+ background: rgba(0, 200, 0, .4);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
}
-.CodeMirror-focused .CodeMirror-cursor {
+/* Kludge to turn off filter in ie9+, which also accepts rgba */
+.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
+.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
-span.CodeMirror-selected {
- background: #ccc !important;
- color: HighlightText !important;
-}
-.CodeMirror-focused span.CodeMirror-selected {
- background: Highlight !important;
+div.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
+
+.CodeMirror-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
}
-.CodeMirror-matchingbracket {color: #0f0 !important;}
-.CodeMirror-nonmatchingbracket {color: #f22 !important;}
-.CodeMirror-gutter-text{color: #003367 !important;} \ No newline at end of file
+/* Default theme */
+
+.cm-s-default span.cm-keyword {color: #708;}
+.cm-s-default span.cm-atom {color: #219;}
+.cm-s-default span.cm-number {color: #164;}
+.cm-s-default span.cm-def {color: #00f;}
+.cm-s-default span.cm-variable {color: black;}
+.cm-s-default span.cm-variable-2 {color: #05a;}
+.cm-s-default span.cm-variable-3 {color: #085;}
+.cm-s-default span.cm-property {color: black;}
+.cm-s-default span.cm-operator {color: black;}
+.cm-s-default span.cm-comment {color: #a50;}
+.cm-s-default span.cm-string {color: #a11;}
+.cm-s-default span.cm-string-2 {color: #f50;}
+.cm-s-default span.cm-meta {color: #555;}
+.cm-s-default span.cm-error {color: #f00;}
+.cm-s-default span.cm-qualifier {color: #555;}
+.cm-s-default span.cm-builtin {color: #30a;}
+.cm-s-default span.cm-bracket {color: #cc7;}
+.cm-s-default span.cm-tag {color: #170;}
+.cm-s-default span.cm-attribute {color: #00c;}
+.cm-s-default span.cm-header {color: blue;}
+.cm-s-default span.cm-quote {color: #090;}
+.cm-s-default span.cm-hr {color: #999;}
+.cm-s-default span.cm-link {color: #00c;}
+
+span.cm-header, span.cm-strong {font-weight: bold;}
+span.cm-em {font-style: italic;}
+span.cm-emstrong {font-style: italic; font-weight: bold;}
+span.cm-link {text-decoration: underline;}
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css
index e7ace753..9574bee3 100644
--- a/rhodecode/public/css/style.css
+++ b/rhodecode/public/css/style.css
@@ -220,6 +220,28 @@ div.options a {
margin-top: 5px;
}
+.empty_data{
+ color:#B9B9B9;
+}
+
+a.permalink{
+ visibility: hidden;
+}
+
+a.permalink:hover{
+ text-decoration: none;
+}
+
+h1:hover > a.permalink,
+h2:hover > a.permalink,
+h3:hover > a.permalink,
+h4:hover > a.permalink,
+h5:hover > a.permalink,
+h6:hover > a.permalink,
+div:hover > a.permalink {
+ visibility: visible;
+}
+
#header {
margin: 0;
padding: 0 10px;
@@ -232,7 +254,7 @@ div.options a {
-moz-border-radius: 0px 0px 8px 8px;
border-radius: 0px 0px 8px 8px;
height: 37px;
- background-color: #003B76;
+ background-color: #003B76;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
background-image: -moz-linear-gradient(top, #003b76, #00376e);
@@ -330,6 +352,11 @@ div.options a {
z-index: auto !important;
}
+.header-pos-fix{
+ margin-top: -44px;
+ padding-top: 44px;
+}
+
#header #header-inner #home a {
height: 40px;
width: 46px;
@@ -631,6 +658,15 @@ div.options a {
padding: 12px 9px 7px 24px;
}
+#header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
+ {
+ background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
+ 9px;
+ width: 167px;
+ margin: 0;
+ padding: 12px 9px 7px 24px;
+}
+
#header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
{
background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
@@ -1065,6 +1101,10 @@ tbody .yui-dt-editable { cursor: pointer }
color: #FFFFFF;
}
+#content div.box div.title .link-white.current{
+ color: #BFE3FF;
+}
+
#content div.box div.title ul.links li {
list-style: none;
float: left;
@@ -1398,7 +1438,8 @@ tbody .yui-dt-editable { cursor: pointer }
margin: 0 0 0 0px;
}
-#content div.box div.form div.fields div.field div.input input {
+#content div.box div.form div.fields div.field div.input input,
+.reviewer_ac input {
background: #FFF;
border-top: 1px solid #b3b3b3;
border-left: 1px solid #b3b3b3;
@@ -1518,12 +1559,21 @@ input.disabled {
padding: 5px 5px 5px 0;
}
-#content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus
+#content div.box div.form div.fields div.field input[type=text]:focus,
+#content div.box div.form div.fields div.field input[type=password]:focus,
+#content div.box div.form div.fields div.field input[type=file]:focus,
+#content div.box div.form div.fields div.field textarea:focus,
+#content div.box div.form div.fields div.field select:focus,
+.reviewer_ac input:focus
{
background: #f6f6f6;
border-color: #666;
}
+.reviewer_ac {
+ padding:10px
+}
+
div.form div.fields div.field div.button {
margin: 0;
padding: 0 0 0 8px;
@@ -1689,7 +1739,12 @@ div.form div.fields div.field div.button {
float: right;
}
-#content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot
+#content div.box div.pagination-wh a,
+#content div.box div.pagination-wh span.pager_dotdot,
+#content div.box div.pagination-wh span.yui-pg-previous,
+#content div.box div.pagination-wh span.yui-pg-last,
+#content div.box div.pagination-wh span.yui-pg-next,
+#content div.box div.pagination-wh span.yui-pg-first
{
height: 1%;
float: left;
@@ -1777,6 +1832,81 @@ div.form div.fields div.field div.button {
}
+#summary .metatag {
+ display: inline-block;
+ padding: 3px 5px;
+ margin-bottom: 3px;
+ margin-right: 1px;
+ border-radius: 5px;
+}
+
+#content div.box #summary p {
+ margin-bottom: -5px;
+ width: 600px;
+ white-space: pre-wrap;
+}
+
+#content div.box #summary p:last-child {
+ margin-bottom: 9px;
+}
+
+#content div.box #summary p:first-of-type {
+ margin-top: 9px;
+}
+
+ .metatag {
+ display: inline-block;
+ margin-right: 1px;
+ -webkit-border-radius: 4px 4px 4px 4px;
+ -khtml-border-radius: 4px 4px 4px 4px;
+ -moz-border-radius: 4px 4px 4px 4px;
+ border-radius: 4px 4px 4px 4px;
+
+ border: solid 1px #9CF;
+ padding: 2px 3px 2px 3px !important;
+ background-color: #DEF;
+}
+
+.metatag[tag="dead"] {
+ background-color: #E44;
+}
+
+.metatag[tag="stale"] {
+ background-color: #EA4;
+}
+
+.metatag[tag="featured"] {
+ background-color: #AEA;
+}
+
+.metatag[tag="requires"] {
+ background-color: #9CF;
+}
+
+.metatag[tag="recommends"] {
+ background-color: #BDF;
+}
+
+.metatag[tag="lang"] {
+ background-color: #FAF474;
+}
+
+.metatag[tag="license"] {
+ border: solid 1px #9CF;
+ background-color: #DEF;
+ target-new: tab !important;
+}
+.metatag[tag="see"] {
+ border: solid 1px #CBD;
+ background-color: #EDF;
+}
+
+a.metatag[tag="license"]:hover {
+ background-color: #003367;
+ color: #FFF;
+ text-decoration: none;
+}
+
#summary .desc {
white-space: pre;
width: 100%;
@@ -1923,7 +2053,7 @@ div.form div.fields div.field div.button {
padding: 4px;
position: absolute;
width: 278px;
-
+ background-color: #003B76;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
background-image: -moz-linear-gradient(top, #003b76, #00376e);
@@ -2247,6 +2377,20 @@ h3.files_location {
padding: 5px !important;
}
+.file_history{
+ padding-top:10px;
+ font-size:16px;
+}
+.file_author{
+ float: left;
+}
+
+.file_author .item{
+ float:left;
+ padding:5px;
+ color: #888;
+}
+
.tablerow0 {
background-color: #F8F8F8;
}
@@ -2333,7 +2477,7 @@ h3.files_location {
padding: 2px 0px 2px 0px;
}
-.cs_files .cs_added {
+.cs_files .cs_added,.cs_files .cs_A {
background: url("../images/icons/page_white_add.png") no-repeat scroll
3px;
height: 16px;
@@ -2342,7 +2486,7 @@ h3.files_location {
text-align: left;
}
-.cs_files .cs_changed {
+.cs_files .cs_changed,.cs_files .cs_M {
background: url("../images/icons/page_white_edit.png") no-repeat scroll
3px;
height: 16px;
@@ -2351,7 +2495,7 @@ h3.files_location {
text-align: left;
}
-.cs_files .cs_removed {
+.cs_files .cs_removed,.cs_files .cs_D {
background: url("../images/icons/page_white_delete.png") no-repeat
scroll 3px;
height: 16px;
@@ -2447,6 +2591,31 @@ h3.files_location {
font-weight: bold !important;
}
+.changeset-status-container{
+ padding-right: 5px;
+ margin-top:1px;
+ float:right;
+ height:14px;
+}
+.code-header .changeset-status-container{
+ float:left;
+ padding:2px 0px 0px 2px;
+}
+.changeset-status-container .changeset-status-lbl{
+ color: rgb(136, 136, 136);
+ float: left;
+ padding: 3px 4px 0px 0px
+}
+.code-header .changeset-status-container .changeset-status-lbl{
+ float: left;
+ padding: 0px 4px 0px 0px;
+}
+.changeset-status-container .changeset-status-ico{
+ float: left;
+}
+.code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
+ float: left;
+}
.right .comments-container{
padding-right: 5px;
margin-top:1px;
@@ -2815,6 +2984,13 @@ table.code-browser .submodule-dir {
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
}
+.mentions-container{
+ width: 90% !important;
+}
+.mentions-container .yui-ac-content{
+ width: 100% !important;
+}
+
.ac {
vertical-align: top;
}
@@ -2925,6 +3101,13 @@ table.code-browser .submodule-dir {
text-align: left;
}
+.accept_icon {
+ background: url("../images/icons/accept.png") no-repeat scroll 3px;
+ padding-left: 20px;
+ padding-top: 0px;
+ text-align: left;
+}
+
.edit_icon {
background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
padding-left: 20px;
@@ -3042,6 +3225,10 @@ table.code-browser .submodule-dir {
}
+.error_red {
+ color:red;
+}
+
.error_msg {
background-color: #c43c35;
background-repeat: repeat-x;
@@ -3228,6 +3415,11 @@ div.gravatar img {
.ui-btn.xsmall{
padding: 1px 2px 1px 1px;
}
+
+.ui-btn.large{
+ padding: 6px 12px;
+}
+
.ui-btn.clone{
padding: 5px 2px 6px 1px;
margin: 0px -4px 3px 0px;
@@ -3268,6 +3460,7 @@ div.gravatar img {
.ui-btn.blue{
+ color:#fff;
background-color: #339bb9;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
@@ -3297,6 +3490,10 @@ div.gravatar img {
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
+.ui-btn.active{
+ font-weight: bold;
+}
+
ins,div.options a:hover {
text-decoration: none;
}
@@ -3452,12 +3649,14 @@ div#legend_data,div#legend_container,div#legend_choices
margin: 2px 0 0 4px;
}
-div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input
- {
- color: #000;
- font-size: 11px;
- font-weight: 700;
- margin: 0;
+div.form div.fields div.field div.button input,
+#content div.box div.form div.fields div.buttons input
+div.form div.fields div.buttons input,
+#content div.box div.action div.button input {
+ /*color: #000;*/
+ font-size: 11px;
+ font-weight: 700;
+ margin: 0;
}
input.ui-button {
@@ -3687,6 +3886,26 @@ div#legend_container table td,div#legend_choices table td {
padding:0px 0px 0px 10px;
}
+.reviewers_member{
+ height: 15px;
+ padding:0px 0px 0px 10px;
+}
+
+.emails_wrap{
+ padding: 0px 20px;
+}
+
+.emails_wrap .email_entry{
+ height: 30px;
+ padding:0px 0px 0px 10px;
+}
+.emails_wrap .email_entry .email{
+ float: left
+}
+.emails_wrap .email_entry .email_action{
+ float: left
+}
+
/*README STYLE*/
div.readme {
@@ -3891,6 +4110,7 @@ div.rst-block pre {
background: #f8f8f8;
padding: 4px;
border-bottom: 1px solid #ddd;
+ height: 18px;
}
.comments .comment .meta img {
@@ -3899,9 +4119,13 @@ div.rst-block pre {
.comments .comment .meta .user {
font-weight: bold;
+ float: left;
+ padding: 4px 2px 2px 2px;
}
.comments .comment .meta .date {
+ float: left;
+ padding:4px 4px 0px 4px;
}
.comments .comment .text {
@@ -3920,6 +4144,11 @@ div.rst-block pre {
/** comment form **/
+.status-block{
+ height:80px;
+ clear:both
+}
+
.comment-form .clearfix{
background: #EEE;
-webkit-border-radius: 4px;
@@ -4089,6 +4318,7 @@ form.comment-inline-form {
background: #f8f8f8;
padding: 4px;
border-bottom: 1px solid #ddd;
+ height: 20px;
}
.inline-comments .comment .meta img {
@@ -4097,9 +4327,13 @@ form.comment-inline-form {
.inline-comments .comment .meta .user {
font-weight: bold;
+ float:left;
+ padding: 3px;
}
.inline-comments .comment .meta .date {
+ float:left;
+ padding: 3px;
}
.inline-comments .comment .text {
@@ -4161,7 +4395,7 @@ form.comment-inline-form {
background: none repeat scroll 0 0 transparent;
padding: 0px 0px 0px 8px;
}
-.notification-header .desc.unread{
+.notification-list .container .notification-header .desc{
font-weight: bold;
font-size: 17px;
}
@@ -4178,6 +4412,11 @@ form.comment-inline-form {
padding-top: 8px;
cursor: pointer;
}
+.notification-header .read-notifications{
+ float: right;
+ padding-top: 8px;
+ cursor: pointer;
+}
.notification-subject{
clear:both;
border-bottom: 1px solid #eee;
@@ -4190,6 +4429,15 @@ form.comment-inline-form {
}
/****
+PULL REQUESTS
+*****/
+.pullrequests_section_head {
+ padding:10px 10px 10px 0px;
+ font-size:16px;
+ font-weight: bold;
+}
+
+/****
PERMS
*****/
#perms .perms_section_head {
diff --git a/rhodecode/public/images/arrow_right_64.png b/rhodecode/public/images/arrow_right_64.png
new file mode 100644
index 00000000..93ee3eb3
--- /dev/null
+++ b/rhodecode/public/images/arrow_right_64.png
Binary files differ
diff --git a/rhodecode/public/images/icons/flag_status_approved.png b/rhodecode/public/images/icons/flag_status_approved.png
new file mode 100644
index 00000000..e4bc611f
--- /dev/null
+++ b/rhodecode/public/images/icons/flag_status_approved.png
Binary files differ
diff --git a/rhodecode/public/images/icons/flag_status_not_reviewed.png b/rhodecode/public/images/icons/flag_status_not_reviewed.png
new file mode 100644
index 00000000..f4701231
--- /dev/null
+++ b/rhodecode/public/images/icons/flag_status_not_reviewed.png
Binary files differ
diff --git a/rhodecode/public/images/icons/flag_status_rejected.png b/rhodecode/public/images/icons/flag_status_rejected.png
new file mode 100644
index 00000000..e8a602da
--- /dev/null
+++ b/rhodecode/public/images/icons/flag_status_rejected.png
Binary files differ
diff --git a/rhodecode/public/images/icons/flag_status_under_review.png b/rhodecode/public/images/icons/flag_status_under_review.png
new file mode 100644
index 00000000..14c89a54
--- /dev/null
+++ b/rhodecode/public/images/icons/flag_status_under_review.png
Binary files differ
diff --git a/rhodecode/public/js/codemirror.js b/rhodecode/public/js/codemirror.js
index 058fb4ce..1d698a5d 100644
--- a/rhodecode/public/js/codemirror.js
+++ b/rhodecode/public/js/codemirror.js
@@ -4,7 +4,7 @@
// CodeMirror is the only global var we claim
var CodeMirror = (function() {
- // This is the function that produces an editor instance. It's
+ // This is the function that produces an editor instance. Its
// closure is used to store the editor state.
function CodeMirror(place, givenOptions) {
// Determine effective options based on given values and defaults.
@@ -13,41 +13,65 @@ var CodeMirror = (function() {
if (defaults.hasOwnProperty(opt))
options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
- var targetDocument = options["document"];
// The element in which the editor lives.
- var wrapper = targetDocument.createElement("div");
- wrapper.className = "CodeMirror";
+ var wrapper = document.createElement("div");
+ wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
// This mess creates the base DOM structure for the editor.
wrapper.innerHTML =
- '<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
- '<textarea style="position: absolute; width: 10000px;" wrap="off" ' +
+ '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
+ '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
'autocorrect="off" autocapitalize="off"></textarea></div>' +
- '<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
+ '<div class="CodeMirror-scrollbar">' + // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
+ '<div class="CodeMirror-scrollbar-inner">' + // The empty scrollbar content, used solely for managing the scrollbar thumb.
+ '</div></div>' + // This must be before the scroll area because it's float-right.
+ '<div class="CodeMirror-scroll" tabindex="-1">' +
'<div style="position: relative">' + // Set to the height of the text, causes scrolling
- '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
'<div style="position: relative">' + // Moved around its parent to cover visible view
'<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
// Provides positioning relative to (visible) text origin
- '<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
+ '<div class="CodeMirror-lines"><div style="position: relative; z-index: 0">' +
+ // Used to measure text size
+ '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden;"></div>' +
'<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
- '<div></div>' + // This DIV contains the actual code
+ '<pre class="CodeMirror-cursor" style="visibility: hidden">&#160;</pre>' + // Used to force a width
+ '<div style="position: relative; z-index: -1"></div><div></div>' + // DIVs containing the selection and the actual code
'</div></div></div></div></div>';
if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
// I've never seen more elegant code in my life.
var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
scroller = wrapper.lastChild, code = scroller.firstChild,
- measure = code.firstChild, mover = measure.nextSibling,
- gutter = mover.firstChild, gutterText = gutter.firstChild,
- lineSpace = gutter.nextSibling.firstChild,
- cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
- if (options.tabindex != null) input.tabindex = options.tabindex;
+ mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
+ lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
+ cursor = measure.nextSibling, widthForcer = cursor.nextSibling,
+ selectionDiv = widthForcer.nextSibling, lineDiv = selectionDiv.nextSibling,
+ scrollbar = inputDiv.nextSibling, scrollbarInner = scrollbar.firstChild;
+ themeChanged(); keyMapChanged();
+ // Needed to hide big blue blinking cursor on Mobile Safari
+ if (ios) input.style.width = "0px";
+ if (!webkit) scroller.draggable = true;
+ lineSpace.style.outline = "none";
+ if (options.tabindex != null) input.tabIndex = options.tabindex;
+ if (options.autofocus) focusInput();
if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
+ // Needed to handle Tab key in KHTML
+ if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
+
+ // Check for OS X >= 10.7. If so, we need to force a width on the scrollbar, and
+ // make it overlap the content. (But we only do this if the scrollbar doesn't already
+ // have a natural width. If the mouse is plugged in or the user sets the system pref
+ // to always show scrollbars, the scrollbar shouldn't overlap.)
+ if (mac_geLion) {
+ scrollbar.className += (overlapScrollbars() ? " cm-sb-overlap" : " cm-sb-nonoverlap");
+ } else if (ie_lt8) {
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+ scrollbar.className += " cm-sb-ie7";
+ }
// Check for problem with IE innerHTML not working when we have a
// P (or similar) parent node.
try { stringWidth("x"); }
catch (e) {
- if (e.message.match(/unknown runtime/i))
+ if (e.message.match(/runtime/i))
e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
throw e;
}
@@ -55,34 +79,32 @@ var CodeMirror = (function() {
// Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
var poll = new Delayed(), highlight = new Delayed(), blinker;
- // mode holds a mode API object. lines an array of Line objects
- // (see Line constructor), work an array of lines that should be
- // parsed, and history the undo history (instance of History
- // constructor).
- var mode, lines = [new Line("")], work, focused;
+ // mode holds a mode API object. doc is the tree of Line objects,
+ // work an array of lines that should be parsed, and history the
+ // undo history (instance of History constructor).
+ var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
loadMode();
// The selection. These are always maintained to point at valid
// positions. Inverted is used to remember that the user is
// selecting bottom-to-top.
var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
// Selection-related flags. shiftSelecting obviously tracks
- // whether the user is holding shift. reducedSelection is a hack
- // to get around the fact that we can't create inverted
- // selections. See below.
- var shiftSelecting, reducedSelection, lastClick, lastDoubleClick, draggingText;
+ // whether the user is holding shift.
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText,
+ overwrite = false, suppressEdits = false;
// Variables used by startOperation/endOperation to track what
// happened during the operation.
- var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
+ var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
+ gutterDirty, callbacks;
// Current visible range (may be bigger than the view window).
- var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
- // editing will hold an object describing the things we put in the
- // textarea, to help figure out whether something changed.
- // bracketHighlighted is used to remember that a backet has been
+ var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
+ // bracketHighlighted is used to remember that a bracket has been
// marked.
- var editing, bracketHighlighted;
+ var bracketHighlighted;
// Tracks the maximum line length so that the horizontal scrollbar
// can be kept static when scrolling.
- var maxLine = "", maxWidth;
+ var maxLine = "", updateMaxLine = false, maxLineChanged = true;
+ var tabCache = {};
// Initialize the content.
operation(function(){setValue(options.value || ""); updateInput = false;})();
@@ -91,38 +113,53 @@ var CodeMirror = (function() {
// Register our event handlers.
connect(scroller, "mousedown", operation(onMouseDown));
connect(scroller, "dblclick", operation(onDoubleClick));
- connect(lineSpace, "dragstart", onDragStart);
+ connect(lineSpace, "selectstart", e_preventDefault);
// Gecko browsers fire contextmenu *after* opening the menu, at
// which point we can't mess with it anymore. Context menu is
// handled in onMouseDown for Gecko.
if (!gecko) connect(scroller, "contextmenu", onContextMenu);
- connect(scroller, "scroll", function() {
- updateDisplay([]);
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
- if (options.onScroll) options.onScroll(instance);
- });
+ connect(scroller, "scroll", onScroll);
+ connect(scrollbar, "scroll", onScroll);
+ connect(scrollbar, "mousedown", function() {setTimeout(focusInput, 0);});
+ connect(scroller, "mousewheel", onMouseWheel);
+ connect(scroller, "DOMMouseScroll", onMouseWheel);
connect(window, "resize", function() {updateDisplay(true);});
connect(input, "keyup", operation(onKeyUp));
- connect(input, "input", function() {fastPoll(curKeyId);});
+ connect(input, "input", fastPoll);
connect(input, "keydown", operation(onKeyDown));
connect(input, "keypress", operation(onKeyPress));
connect(input, "focus", onFocus);
connect(input, "blur", onBlur);
- connect(scroller, "dragenter", e_stop);
- connect(scroller, "dragover", e_stop);
- connect(scroller, "drop", operation(onDrop));
+ if (options.dragDrop) {
+ connect(scroller, "dragstart", onDragStart);
+ function drag_(e) {
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
+ e_stop(e);
+ }
+ connect(scroller, "dragenter", drag_);
+ connect(scroller, "dragover", drag_);
+ connect(scroller, "drop", operation(onDrop));
+ }
connect(scroller, "paste", function(){focusInput(); fastPoll();});
- connect(input, "paste", function(){fastPoll();});
- connect(input, "cut", function(){fastPoll();});
+ connect(input, "paste", fastPoll);
+ connect(input, "cut", operation(function(){
+ if (!options.readOnly) replaceSelection("");
+ }));
+
+ // Needed to handle Tab key in KHTML
+ if (khtml) connect(code, "mouseup", function() {
+ if (document.activeElement == input) input.blur();
+ focusInput();
+ });
// IE throws unspecified error in certain cases, when
// trying to access activeElement before onload
- var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
- if (hasFocus) setTimeout(onFocus, 20);
+ var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { }
+ if (hasFocus || options.autofocus) setTimeout(onFocus, 20);
else onBlur();
- function isLine(l) {return l >= 0 && l < lines.length;}
+ function isLine(l) {return l >= 0 && l < doc.size;}
// The instance object that we'll return. Mostly calls out to
// local functions in the CodeMirror function. Some do some extra
// range checking and/or clipping. operation is used to wrap the
@@ -133,47 +170,74 @@ var CodeMirror = (function() {
setValue: operation(setValue),
getSelection: getSelection,
replaceSelection: operation(replaceSelection),
- focus: function(){focusInput(); onFocus(); fastPoll();},
+ focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
setOption: function(option, value) {
+ var oldVal = options[option];
options[option] = value;
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
- operation(gutterChanged)();
- else if (option == "mode" || option == "indentUnit") loadMode();
- else if (option == "readOnly" && value == "nocursor") input.blur();
- else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
+ if (option == "mode" || option == "indentUnit") loadMode();
+ else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
+ else if (option == "readOnly" && !value) {resetInput(true);}
+ else if (option == "theme") themeChanged();
+ else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
+ else if (option == "tabSize") updateDisplay(true);
+ else if (option == "keyMap") keyMapChanged();
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
+ gutterChanged();
+ updateDisplay(true);
+ }
},
getOption: function(option) {return options[option];},
undo: operation(undo),
redo: operation(redo),
indentLine: operation(function(n, dir) {
- if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
+ if (typeof dir != "string") {
+ if (dir == null) dir = options.smartIndent ? "smart" : "prev";
+ else dir = dir ? "add" : "subtract";
+ }
+ if (isLine(n)) indentLine(n, dir);
}),
+ indentSelection: operation(indentSelected),
historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
clearHistory: function() {history = new History();},
matchBrackets: operation(function(){matchBrackets(true);}),
- getTokenAt: function(pos) {
+ getTokenAt: operation(function(pos) {
pos = clipPos(pos);
- return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
- },
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
+ }),
getStateAfter: function(line) {
- line = clipLine(line == null ? lines.length - 1: line);
+ line = clipLine(line == null ? doc.size - 1: line);
return getStateBefore(line + 1);
},
- cursorCoords: function(start){
+ cursorCoords: function(start, mode) {
if (start == null) start = sel.inverted;
- return pageCoords(start ? sel.from : sel.to);
+ return this.charCoords(start ? sel.from : sel.to, mode);
+ },
+ charCoords: function(pos, mode) {
+ pos = clipPos(pos);
+ if (mode == "local") return localCoords(pos, false);
+ if (mode == "div") return localCoords(pos, true);
+ return pageCoords(pos);
},
- charCoords: function(pos){return pageCoords(clipPos(pos));},
coordsChar: function(coords) {
var off = eltOffset(lineSpace);
- var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
- return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
+ return coordsChar(coords.x - off.left, coords.y - off.top);
},
- getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
markText: operation(markText),
+ setBookmark: setBookmark,
+ findMarksAt: findMarksAt,
setMarker: operation(addGutterMarker),
clearMarker: operation(removeGutterMarker),
setLineClass: operation(setLineClass),
+ hideLine: operation(function(h) {return setLineHidden(h, true);}),
+ showLine: operation(function(h) {return setLineHidden(h, false);}),
+ onDeleteLine: function(line, f) {
+ if (typeof line == "number") {
+ if (!isLine(line)) return null;
+ line = getLine(line);
+ }
+ (line.handlers || (line.handlers = [])).push(f);
+ return line;
+ },
lineInfo: lineInfo,
addWidget: function(pos, node, scroll, vert, horiz) {
pos = localCoords(clipPos(pos));
@@ -182,7 +246,7 @@ var CodeMirror = (function() {
code.appendChild(node);
if (vert == "over") top = pos.y;
else if (vert == "near") {
- var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
+ var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
top = pos.y - node.offsetHeight;
@@ -203,20 +267,24 @@ var CodeMirror = (function() {
scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
},
- lineCount: function() {return lines.length;},
+ lineCount: function() {return doc.size;},
+ clipPos: clipPos,
getCursor: function(start) {
if (start == null) start = sel.inverted;
return copyPos(start ? sel.from : sel.to);
},
somethingSelected: function() {return !posEq(sel.from, sel.to);},
- setCursor: operation(function(line, ch) {
- if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch);
- else setCursor(line, ch);
+ setCursor: operation(function(line, ch, user) {
+ if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
+ else setCursor(line, ch, user);
}),
- setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
- getLine: function(line) {if (isLine(line)) return lines[line].text;},
+ setSelection: operation(function(from, to, user) {
+ (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
+ }),
+ getLine: function(line) {if (isLine(line)) return getLine(line).text;},
+ getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
setLine: operation(function(line, text) {
- if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: lines[line].text.length});
+ if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
}),
removeLine: operation(function(line) {
if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
@@ -224,44 +292,99 @@ var CodeMirror = (function() {
replaceRange: operation(replaceRange),
getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
- coordsFromIndex: function(index) {
- var total = lines.length, pos = 0, line, ch, len;
-
- for (line = 0; line < total; line++) {
- len = lines[line].text.length + 1;
- if (pos + len > index) { ch = index - pos; break; }
- pos += len;
+ triggerOnKeyDown: operation(onKeyDown),
+ execCommand: function(cmd) {return commands[cmd](instance);},
+ // Stuff used by commands, probably not much use to outside code.
+ moveH: operation(moveH),
+ deleteH: operation(deleteH),
+ moveV: operation(moveV),
+ toggleOverwrite: function() {
+ if(overwrite){
+ overwrite = false;
+ cursor.className = cursor.className.replace(" CodeMirror-overwrite", "");
+ } else {
+ overwrite = true;
+ cursor.className += " CodeMirror-overwrite";
}
- return clipPos({line: line, ch: ch});
+ },
+
+ posFromIndex: function(off) {
+ var lineNo = 0, ch;
+ doc.iter(0, doc.size, function(line) {
+ var sz = line.text.length + 1;
+ if (sz > off) { ch = off; return true; }
+ off -= sz;
+ ++lineNo;
+ });
+ return clipPos({line: lineNo, ch: ch});
+ },
+ indexFromPos: function (coords) {
+ if (coords.line < 0 || coords.ch < 0) return 0;
+ var index = coords.ch;
+ doc.iter(0, coords.line, function (line) {
+ index += line.text.length + 1;
+ });
+ return index;
+ },
+ scrollTo: function(x, y) {
+ if (x != null) scroller.scrollLeft = x;
+ if (y != null) scrollbar.scrollTop = y;
+ updateDisplay([]);
+ },
+ getScrollInfo: function() {
+ return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
+ height: scrollbar.scrollHeight, width: scroller.scrollWidth};
},
operation: function(f){return operation(f)();},
- refresh: function(){updateDisplay(true);},
+ compoundChange: function(f){return compoundChange(f);},
+ refresh: function(){
+ updateDisplay(true);
+ if (scrollbar.scrollHeight > lastScrollTop)
+ scrollbar.scrollTop = lastScrollTop;
+ },
getInputField: function(){return input;},
getWrapperElement: function(){return wrapper;},
getScrollerElement: function(){return scroller;},
getGutterElement: function(){return gutter;}
};
+ function getLine(n) { return getLineAt(doc, n); }
+ function updateLineHeight(line, height) {
+ gutterDirty = true;
+ var diff = height - line.height;
+ for (var n = line; n; n = n.parent) n.height += diff;
+ }
+
function setValue(code) {
var top = {line: 0, ch: 0};
- updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
+ updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
splitLines(code), top, top);
updateInput = true;
}
- function getValue(code) {
+ function getValue() {
var text = [];
- for (var i = 0, l = lines.length; i < l; ++i)
- text.push(lines[i].text);
+ doc.iter(0, doc.size, function(line) { text.push(line.text); });
return text.join("\n");
}
+ function onScroll(e) {
+ if (lastScrollTop != scrollbar.scrollTop || lastScrollLeft != scroller.scrollLeft) {
+ lastScrollTop = scrollbar.scrollTop;
+ lastScrollLeft = scroller.scrollLeft;
+ updateDisplay([]);
+ if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
+ if (options.onScroll) options.onScroll(instance);
+ }
+ }
+
function onMouseDown(e) {
+ setShift(e_prop(e, "shiftKey"));
// Check whether this is a click in a widget
for (var n = e_target(e); n != wrapper; n = n.parentNode)
if (n.parentNode == code && n != mover) return;
- // First, see if this is a click in the gutter
+ // See if this is a click in the gutter
for (var n = e_target(e); n != wrapper; n = n.parentNode)
if (n.parentNode == gutterText) {
if (options.onGutterClick)
@@ -277,6 +400,8 @@ var CodeMirror = (function() {
return;
case 2:
if (start) setCursor(start.line, start.ch, true);
+ setTimeout(focusInput, 20);
+ e_preventDefault(e);
return;
}
// For button 1, if it was clicked inside the editor
@@ -287,29 +412,36 @@ var CodeMirror = (function() {
if (!focused) onFocus();
var now = +new Date;
- if (lastDoubleClick > now - 400) {
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
e_preventDefault(e);
+ setTimeout(focusInput, 20);
return selectLine(start.line);
- } else if (lastClick > now - 400) {
- lastDoubleClick = now;
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
+ lastDoubleClick = {time: now, pos: start};
e_preventDefault(e);
return selectWordAt(start);
- } else { lastClick = now; }
+ } else { lastClick = {time: now, pos: start}; }
var last = start, going;
- if (dragAndDrop && !posEq(sel.from, sel.to) &&
+ if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
!posLess(start, sel.from) && !posLess(sel.to, start)) {
// Let the drag handler handle this.
- var up = connect(targetDocument, "mouseup", operation(function(e2) {
+ if (webkit) scroller.draggable = true;
+ function dragEnd(e2) {
+ if (webkit) scroller.draggable = false;
draggingText = false;
- up();
+ up(); drop();
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
e_preventDefault(e2);
setCursor(start.line, start.ch, true);
focusInput();
}
- }), true);
+ }
+ var up = connect(document, "mouseup", operation(dragEnd), true);
+ var drop = connect(scroller, "drop", operation(dragEnd), true);
draggingText = true;
+ // IE's approach to draggable
+ if (scroller.dragDrop) scroller.dragDrop();
return;
}
e_preventDefault(e);
@@ -328,12 +460,7 @@ var CodeMirror = (function() {
}
}
- var move = connect(targetDocument, "mousemove", operation(function(e) {
- clearTimeout(going);
- e_preventDefault(e);
- extend(e);
- }), true);
- var up = connect(targetDocument, "mouseup", operation(function(e) {
+ function done(e) {
clearTimeout(going);
var cur = posFromMouse(e);
if (cur) setSelectionUser(start, cur);
@@ -341,16 +468,26 @@ var CodeMirror = (function() {
focusInput();
updateInput = true;
move(); up();
+ }
+ var move = connect(document, "mousemove", operation(function(e) {
+ clearTimeout(going);
+ e_preventDefault(e);
+ if (!ie && !e_button(e)) done(e);
+ else extend(e);
}), true);
+ var up = connect(document, "mouseup", operation(done), true);
}
function onDoubleClick(e) {
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
+ if (n.parentNode == gutterText) return e_preventDefault(e);
var start = posFromMouse(e);
if (!start) return;
- lastDoubleClick = +new Date;
+ lastDoubleClick = {time: +new Date, pos: start};
e_preventDefault(e);
selectWordAt(start);
}
function onDrop(e) {
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
e.preventDefault();
var pos = posFromMouse(e, true), files = e.dataTransfer.files;
if (!pos || options.readOnly) return;
@@ -360,102 +497,147 @@ var CodeMirror = (function() {
reader.onload = function() {
text[i] = reader.result;
if (++read == n) {
- pos = clipPos(pos);
- var end = replaceRange(text.join(""), pos, pos);
- setSelectionUser(pos, end);
- }
+ pos = clipPos(pos);
+ operation(function() {
+ var end = replaceRange(text.join(""), pos, pos);
+ setSelectionUser(pos, end);
+ })();
+ }
};
reader.readAsText(file);
}
var n = files.length, text = Array(n), read = 0;
for (var i = 0; i < n; ++i) loadFile(files[i], i);
- }
- else {
+ } else {
+ // Don't do a replace if the drop happened inside of the selected text.
+ if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return;
try {
var text = e.dataTransfer.getData("Text");
if (text) {
- var end = replaceRange(text, pos, pos);
- var curFrom = sel.from, curTo = sel.to;
- setSelectionUser(pos, end);
- if (draggingText) replaceRange("", curFrom, curTo);
- focusInput();
- }
+ compoundChange(function() {
+ var curFrom = sel.from, curTo = sel.to;
+ setSelectionUser(pos, pos);
+ if (draggingText) replaceRange("", curFrom, curTo);
+ replaceSelection(text);
+ focusInput();
+ });
+ }
}
catch(e){}
}
}
function onDragStart(e) {
var txt = getSelection();
- // This will reset escapeElement
- htmlEscape(txt);
- e.dataTransfer.setDragImage(escapeElement, 0, 0);
e.dataTransfer.setData("Text", txt);
+
+ // Use dummy image instead of default browsers image.
+ if (gecko || chrome || opera) {
+ var img = document.createElement('img');
+ img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
+ e.dataTransfer.setDragImage(img, 0, 0);
+ }
+ }
+
+ function doHandleBinding(bound, dropShift) {
+ if (typeof bound == "string") {
+ bound = commands[bound];
+ if (!bound) return false;
+ }
+ var prevShift = shiftSelecting;
+ try {
+ if (options.readOnly) suppressEdits = true;
+ if (dropShift) shiftSelecting = null;
+ bound(instance);
+ } catch(e) {
+ if (e != Pass) throw e;
+ return false;
+ } finally {
+ shiftSelecting = prevShift;
+ suppressEdits = false;
+ }
+ return true;
}
- function onKeyDown(e) {
- if (!focused) onFocus();
-
- var code = e.keyCode;
- // IE does strange things with escape.
- if (ie && code == 27) { e.returnValue = false; }
- // Tries to detect ctrl on non-mac, cmd on mac.
- var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
- if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
- else shiftSelecting = null;
- // First give onKeyEvent option a chance to handle this.
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
-
- if (code == 33 || code == 34) {scrollPage(code == 34); return e_preventDefault(e);} // page up/down
- if (mod && ((code == 36 || code == 35) || // ctrl-home/end
- mac && (code == 38 || code == 40))) { // cmd-up/down
- scrollEnd(code == 36 || code == 38); return e_preventDefault(e);
- }
- if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
- if (!options.readOnly) {
- if (!anyMod && code == 13) {return;} // enter
- if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
- if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
- if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
- }
- if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
-
- // Key id to use in the movementKeys map. We also pass it to
- // fastPoll in order to 'self learn'. We need this because
- // reducedSelection, the hack where we collapse the selection to
- // its start when it is inverted and a movement key is pressed
- // (and later restore it again), shouldn't be used for
- // non-movement keys.
- curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
- if (sel.inverted && movementKeys[curKeyId] === true) {
- var range = selRange(input);
- if (range) {
- reducedSelection = {anchor: range.start};
- setSelRange(input, range.start, range.start);
+ function handleKeyBinding(e) {
+ // Handle auto keymap transitions
+ var startMap = getKeyMap(options.keyMap), next = startMap.auto;
+ clearTimeout(maybeTransition);
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
+ if (getKeyMap(options.keyMap) == startMap) {
+ options.keyMap = (next.call ? next.call(null, instance) : next);
}
+ }, 50);
+
+ var name = keyNames[e_prop(e, "keyCode")], handled = false;
+ if (name == null || e.altGraphKey) return false;
+ if (e_prop(e, "altKey")) name = "Alt-" + name;
+ if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
+ if (e_prop(e, "metaKey")) name = "Cmd-" + name;
+
+ var stopped = false;
+ function stop() { stopped = true; }
+
+ if (e_prop(e, "shiftKey")) {
+ handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
+ function(b) {return doHandleBinding(b, true);}, stop)
+ || lookupKey(name, options.extraKeys, options.keyMap, function(b) {
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
+ }, stop);
+ } else {
+ handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop);
+ }
+ if (stopped) handled = false;
+ if (handled) {
+ e_preventDefault(e);
+ restartBlink();
+ if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
}
- // Don't save the key as a movementkey unless it had a modifier
- if (!mod && !e.altKey) curKeyId = null;
- fastPoll(curKeyId);
+ return handled;
}
- function onKeyUp(e) {
+ function handleCharBinding(e, ch) {
+ var handled = lookupKey("'" + ch + "'", options.extraKeys,
+ options.keyMap, function(b) { return doHandleBinding(b, true); });
+ if (handled) {
+ e_preventDefault(e);
+ restartBlink();
+ }
+ return handled;
+ }
+
+ var lastStoppedKey = null, maybeTransition;
+ function onKeyDown(e) {
+ if (!focused) onFocus();
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
+ if (pollingFast) { if (readInput()) pollingFast = false; }
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
- if (reducedSelection) {
- reducedSelection = null;
- updateInput = true;
+ var code = e_prop(e, "keyCode");
+ // IE does strange things with escape.
+ setShift(code == 16 || e_prop(e, "shiftKey"));
+ // First give onKeyEvent option a chance to handle this.
+ var handled = handleKeyBinding(e);
+ if (opera) {
+ lastStoppedKey = handled ? code : null;
+ // Opera has no cut event... we try to at least catch the key combo
+ if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
+ replaceSelection("");
}
- if (e.keyCode == 16) shiftSelecting = null;
}
function onKeyPress(e) {
+ if (pollingFast) readInput();
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
- if (options.electricChars && mode.electricChars) {
- var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
+ var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
+ if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
if (mode.electricChars.indexOf(ch) > -1)
- setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
+ setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
}
- var code = e.keyCode;
- // Re-stop tab and enter. Necessary on some browsers.
- if (code == 13) {if (!options.readOnly) handleEnter(); e_preventDefault(e);}
- else if (!e.ctrlKey && !e.altKey && !e.metaKey && code == 9 && options.tabMode != "default") e_preventDefault(e);
- else fastPoll(curKeyId);
+ if (handleCharBinding(e, ch)) return;
+ fastPoll();
+ }
+ function onKeyUp(e) {
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
+ if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
}
function onFocus() {
@@ -463,9 +645,9 @@ var CodeMirror = (function() {
if (!focused) {
if (options.onFocus) options.onFocus(instance);
focused = true;
- if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
- wrapper.className += " CodeMirror-focused";
- if (!leaveInputAlone) prepareInput();
+ if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
+ scroller.className += " CodeMirror-focused";
+ if (!leaveInputAlone) resetInput(true);
}
slowPoll();
restartBlink();
@@ -474,90 +656,149 @@ var CodeMirror = (function() {
if (focused) {
if (options.onBlur) options.onBlur(instance);
focused = false;
- wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
+ if (bracketHighlighted)
+ operation(function(){
+ if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
+ })();
+ scroller.className = scroller.className.replace(" CodeMirror-focused", "");
}
clearInterval(blinker);
setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
}
+ function chopDelta(delta) {
+ // Make sure we always scroll a little bit for any nonzero delta.
+ if (delta > 0.0 && delta < 1.0) return 1;
+ else if (delta > -1.0 && delta < 0.0) return -1;
+ else return Math.round(delta);
+ }
+
+ function onMouseWheel(e) {
+ var deltaX = 0, deltaY = 0;
+ if (e.type == "DOMMouseScroll") { // Firefox
+ var delta = -e.detail * 8.0;
+ if (e.axis == e.HORIZONTAL_AXIS) deltaX = delta;
+ else if (e.axis == e.VERTICAL_AXIS) deltaY = delta;
+ } else if (e.wheelDeltaX !== undefined && e.wheelDeltaY !== undefined) { // WebKit
+ deltaX = e.wheelDeltaX / 3.0;
+ deltaY = e.wheelDeltaY / 3.0;
+ } else if (e.wheelDelta !== undefined) { // IE or Opera
+ deltaY = e.wheelDelta / 3.0;
+ }
+
+ var scrolled = false;
+ deltaX = chopDelta(deltaX);
+ deltaY = chopDelta(deltaY);
+ if ((deltaX > 0 && scroller.scrollLeft > 0) ||
+ (deltaX < 0 && scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth)) {
+ scroller.scrollLeft -= deltaX;
+ scrolled = true;
+ }
+ if ((deltaY > 0 && scrollbar.scrollTop > 0) ||
+ (deltaY < 0 && scrollbar.scrollTop + scrollbar.clientHeight < scrollbar.scrollHeight)) {
+ scrollbar.scrollTop -= deltaY;
+ scrolled = true;
+ }
+ if (scrolled) e_stop(e);
+ }
+
// Replace the range from from to to by the strings in newText.
// Afterwards, set the selection to selFrom, selTo.
function updateLines(from, to, newText, selFrom, selTo) {
+ if (suppressEdits) return;
if (history) {
var old = [];
- for (var i = from.line, e = to.line + 1; i < e; ++i) old.push(lines[i].text);
+ doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
history.addChange(from.line, newText.length, old);
while (history.done.length > options.undoDepth) history.done.shift();
}
updateLinesNoUndo(from, to, newText, selFrom, selTo);
}
function unredoHelper(from, to) {
- var change = from.pop();
- if (change) {
+ if (!from.length) return;
+ var set = from.pop(), out = [];
+ for (var i = set.length - 1; i >= 0; i -= 1) {
+ var change = set[i];
var replaced = [], end = change.start + change.added;
- for (var i = change.start; i < end; ++i) replaced.push(lines[i].text);
- to.push({start: change.start, added: change.old.length, old: replaced});
- var pos = clipPos({line: change.start + change.old.length - 1,
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
- updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
- updateInput = true;
+ doc.iter(change.start, end, function(line) { replaced.push(line.text); });
+ out.push({start: change.start, added: change.old.length, old: replaced});
+ var pos = {line: change.start + change.old.length - 1,
+ ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
+ updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
}
+ updateInput = true;
+ to.push(out);
}
function undo() {unredoHelper(history.done, history.undone);}
function redo() {unredoHelper(history.undone, history.done);}
function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
+ if (suppressEdits) return;
var recomputeMaxLength = false, maxLineLength = maxLine.length;
- for (var i = from.line; i <= to.line; ++i) {
- if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
- }
+ if (!options.lineWrapping)
+ doc.iter(from.line, to.line + 1, function(line) {
+ if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
+ });
+ if (from.line != to.line || newText.length > 1) gutterDirty = true;
- var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
+ var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
// First adjust the line structure, taking some care to leave highlighting intact.
- if (firstLine == lastLine) {
+ if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
+ // This is a whole-line replace. Treated specially to make
+ // sure line objects move the way they are supposed to.
+ var added = [], prevLine = null;
+ if (from.line) {
+ prevLine = getLine(from.line - 1);
+ prevLine.fixMarkEnds(lastLine);
+ } else lastLine.fixMarkStarts();
+ for (var i = 0, e = newText.length - 1; i < e; ++i)
+ added.push(Line.inheritMarks(newText[i], prevLine));
+ if (nlines) doc.remove(from.line, nlines, callbacks);
+ if (added.length) doc.insert(from.line, added);
+ } else if (firstLine == lastLine) {
if (newText.length == 1)
firstLine.replace(from.ch, to.ch, newText[0]);
else {
lastLine = firstLine.split(to.ch, newText[newText.length-1]);
- var spliceargs = [from.line + 1, nlines];
firstLine.replace(from.ch, null, newText[0]);
+ firstLine.fixMarkEnds(lastLine);
+ var added = [];
for (var i = 1, e = newText.length - 1; i < e; ++i)
- spliceargs.push(Line.inheritMarks(newText[i], firstLine));
- spliceargs.push(lastLine);
- lines.splice.apply(lines, spliceargs);
+ added.push(Line.inheritMarks(newText[i], firstLine));
+ added.push(lastLine);
+ doc.insert(from.line + 1, added);
}
- }
- else if (newText.length == 1) {
+ } else if (newText.length == 1) {
firstLine.replace(from.ch, null, newText[0]);
lastLine.replace(null, to.ch, "");
firstLine.append(lastLine);
- lines.splice(from.line + 1, nlines);
- }
- else {
- var spliceargs = [from.line + 1, nlines - 1];
+ doc.remove(from.line + 1, nlines, callbacks);
+ } else {
+ var added = [];
firstLine.replace(from.ch, null, newText[0]);
lastLine.replace(null, to.ch, newText[newText.length-1]);
+ firstLine.fixMarkEnds(lastLine);
for (var i = 1, e = newText.length - 1; i < e; ++i)
- spliceargs.push(Line.inheritMarks(newText[i], firstLine));
- lines.splice.apply(lines, spliceargs);
- }
-
-
- for (var i = from.line, e = i + newText.length; i < e; ++i) {
- var l = lines[i].text;
- if (l.length > maxLineLength) {
- maxLine = l; maxLineLength = l.length; maxWidth = null;
- recomputeMaxLength = false;
- }
- }
- if (recomputeMaxLength) {
- maxLineLength = 0; maxLine = ""; maxWidth = null;
- for (var i = 0, e = lines.length; i < e; ++i) {
- var l = lines[i].text;
- if (l.length > maxLineLength) {
- maxLineLength = l.length; maxLine = l;
+ added.push(Line.inheritMarks(newText[i], firstLine));
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
+ doc.insert(from.line + 1, added);
+ }
+ if (options.lineWrapping) {
+ var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
+ doc.iter(from.line, from.line + newText.length, function(line) {
+ if (line.hidden) return;
+ var guess = Math.ceil(line.text.length / perLine) || 1;
+ if (guess != line.height) updateLineHeight(line, guess);
+ });
+ } else {
+ doc.iter(from.line, from.line + newText.length, function(line) {
+ var l = line.text;
+ if (!line.hidden && l.length > maxLineLength) {
+ maxLine = l; maxLineLength = l.length; maxLineChanged = true;
+ recomputeMaxLength = false;
}
- }
+ });
+ if (recomputeMaxLength) updateMaxLine = true;
}
// Add these lines to the work array, so that they will be
@@ -568,24 +809,64 @@ var CodeMirror = (function() {
if (task < from.line) newWork.push(task);
else if (task > to.line) newWork.push(task + lendiff);
}
- if (newText.length < 5) {
- highlightLines(from.line, from.line + newText.length);
- newWork.push(from.line + newText.length);
- } else {
- newWork.push(from.line);
- }
+ var hlEnd = from.line + Math.min(newText.length, 500);
+ highlightLines(from.line, hlEnd);
+ newWork.push(hlEnd);
work = newWork;
startWorker(100);
// Remember that these lines changed, for updating the display
changes.push({from: from.line, to: to.line + 1, diff: lendiff});
- textChanged = {from: from, to: to, text: newText};
+ var changeObj = {from: from, to: to, text: newText};
+ if (textChanged) {
+ for (var cur = textChanged; cur.next; cur = cur.next) {}
+ cur.next = changeObj;
+ } else textChanged = changeObj;
// Update the selection
function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
- setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
+ setSelection(clipPos(selFrom), clipPos(selTo),
+ updateLine(sel.from.line), updateLine(sel.to.line));
+ }
+
+ function updateVerticalScroll(scrollTop) {
+ var th = textHeight(), virtualHeight = Math.floor(doc.height * th + 2 * paddingTop()), scrollbarHeight = scroller.clientHeight;
+ scrollbar.style.height = scrollbarHeight + "px";
+ if (scroller.clientHeight)
+ scrollbarInner.style.height = virtualHeight + "px";
+ // Position the mover div to align with the current virtual scroll position
+ if (scrollTop != null) scrollbar.scrollTop = scrollTop;
+ mover.style.top = (displayOffset * th - scrollbar.scrollTop) + "px";
+ scrollbar.style.display = (virtualHeight > scrollbarHeight) ? "block" : "none";
+ }
+
+ // On Mac OS X Lion and up, detect whether the mouse is plugged in by measuring
+ // the width of a div with a scrollbar in it. If the width is <= 1, then
+ // the mouse isn't plugged in and scrollbars should overlap the content.
+ function overlapScrollbars() {
+ var tmpSb = document.createElement('div'),
+ tmpSbInner = document.createElement('div');
+ tmpSb.className = "CodeMirror-scrollbar";
+ tmpSb.style.cssText = "position: absolute; left: -9999px; height: 100px;";
+ tmpSbInner.className = "CodeMirror-scrollbar-inner";
+ tmpSbInner.style.height = "200px";
+ tmpSb.appendChild(tmpSbInner);
+
+ document.body.appendChild(tmpSb);
+ var result = (tmpSb.offsetWidth <= 1);
+ document.body.removeChild(tmpSb);
+ return result;
+ }
- // Make sure the scroll-size div has the correct height.
- code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
+ function computeMaxLength() {
+ var maxLineLength = 0;
+ maxLine = ""; maxLineChanged = true;
+ doc.iter(0, doc.size, function(line) {
+ var l = line.text;
+ if (!line.hidden && l.length > maxLineLength) {
+ maxLineLength = l.length; maxLine = l;
+ }
+ });
+ updateMaxLine = false;
}
function replaceRange(code, from, to) {
@@ -623,10 +904,10 @@ var CodeMirror = (function() {
function getRange(from, to) {
var l1 = from.line, l2 = to.line;
- if (l1 == l2) return lines[l1].text.slice(from.ch, to.ch);
- var code = [lines[l1].text.slice(from.ch)];
- for (var i = l1 + 1; i < l2; ++i) code.push(lines[i].text);
- code.push(lines[l2].text.slice(0, to.ch));
+ if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
+ var code = [getLine(l1).text.slice(from.ch)];
+ doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
+ code.push(getLine(l2).text.slice(0, to.ch));
return code.join("\n");
}
function getSelection() {
@@ -636,115 +917,56 @@ var CodeMirror = (function() {
var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
function slowPoll() {
if (pollingFast) return;
- poll.set(2000, function() {
+ poll.set(options.pollInterval, function() {
startOperation();
readInput();
if (focused) slowPoll();
endOperation();
});
}
- function fastPoll(keyId) {
+ function fastPoll() {
var missed = false;
pollingFast = true;
function p() {
startOperation();
var changed = readInput();
- if (changed && keyId) {
- if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
- if (changed == "changed") movementKeys[keyId] = false;
- }
- if (!changed && !missed) {missed = true; poll.set(80, p);}
+ if (!changed && !missed) {missed = true; poll.set(60, p);}
else {pollingFast = false; slowPoll();}
endOperation();
}
poll.set(20, p);
}
- // Inspects the textarea, compares its state (content, selection)
- // to the data in the editing variable, and updates the editor
- // content or cursor if something changed.
+ // Previnput is a hack to work with IME. If we reset the textarea
+ // on every change, that breaks IME. So we look for changes
+ // compared to the previous content instead. (Modern browsers have
+ // events that indicate IME taking place, but these are not widely
+ // supported or compatible enough yet to rely on.)
+ var prevInput = "";
function readInput() {
- if (leaveInputAlone || !focused) return;
- var changed = false, text = input.value, sr = selRange(input);
- if (!sr) return false;
- var changed = editing.text != text, rs = reducedSelection;
- var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
- if (!moved && !rs) return false;
- if (changed) {
- shiftSelecting = reducedSelection = null;
- if (options.readOnly) {updateInput = true; return "changed";}
- }
-
- // Compute selection start and end based on start/end offsets in textarea
- function computeOffset(n, startLine) {
- var pos = 0;
- for (;;) {
- var found = text.indexOf("\n", pos);
- if (found == -1 || (text.charAt(found-1) == "\r" ? found - 1 : found) >= n)
- return {line: startLine, ch: n - pos};
- ++startLine;
- pos = found + 1;
- }
- }
- var from = computeOffset(sr.start, editing.from),
- to = computeOffset(sr.end, editing.from);
- // Here we have to take the reducedSelection hack into account,
- // so that you can, for example, press shift-up at the start of
- // your selection and have the right thing happen.
- if (rs) {
- var head = sr.start == rs.anchor ? to : from;
- var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
- if (sel.inverted = posLess(head, tail)) { from = head; to = tail; }
- else { reducedSelection = null; from = tail; to = head; }
- }
-
- // In some cases (cursor on same line as before), we don't have
- // to update the textarea content at all.
- if (from.line == to.line && from.line == sel.from.line && from.line == sel.to.line && !shiftSelecting)
- updateInput = false;
-
- // Magic mess to extract precise edited range from the changed
- // string.
- if (changed) {
- var start = 0, end = text.length, len = Math.min(end, editing.text.length);
- var c, line = editing.from, nl = -1;
- while (start < len && (c = text.charAt(start)) == editing.text.charAt(start)) {
- ++start;
- if (c == "\n") {line++; nl = start;}
- }
- var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
- for (;;) {
- c = editing.text.charAt(edend);
- if (text.charAt(end) != c) {++end; ++edend; break;}
- if (c == "\n") endline--;
- if (edend <= start || end <= start) break;
- --end; --edend;
- }
- var nl = editing.text.lastIndexOf("\n", edend - 1), endch = nl == -1 ? edend : edend - nl - 1;
- updateLines({line: line, ch: ch}, {line: endline, ch: endch}, splitLines(text.slice(start, end)), from, to);
- if (line != endline || from.line != line) updateInput = true;
- }
- else setSelection(from, to);
-
- editing.text = text; editing.start = sr.start; editing.end = sr.end;
- return changed ? "changed" : moved ? "moved" : false;
+ if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
+ var text = input.value;
+ if (text == prevInput) return false;
+ shiftSelecting = null;
+ var same = 0, l = Math.min(prevInput.length, text.length);
+ while (same < l && prevInput[same] == text[same]) ++same;
+ if (same < prevInput.length)
+ sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
+ else if (overwrite && posEq(sel.from, sel.to))
+ sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
+ replaceSelection(text.slice(same), "end");
+ if (text.length > 1000) { input.value = prevInput = ""; }
+ else prevInput = text;
+ return true;
}
-
- // Set the textarea content and selection range to match the
- // editor state.
- function prepareInput() {
- var text = [];
- var from = Math.max(0, sel.from.line - 1), to = Math.min(lines.length, sel.to.line + 2);
- for (var i = from; i < to; ++i) text.push(lines[i].text);
- text = input.value = text.join(lineSep);
- var startch = sel.from.ch, endch = sel.to.ch;
- for (var i = from; i < sel.from.line; ++i)
- startch += lineSep.length + lines[i].text.length;
- for (var i = from; i < sel.to.line; ++i)
- endch += lineSep.length + lines[i].text.length;
- editing = {text: text, from: from, to: to, start: startch, end: endch};
- setSelRange(input, startch, reducedSelection ? startch : endch);
+ function resetInput(user) {
+ if (!posEq(sel.from, sel.to)) {
+ prevInput = "";
+ input.value = getSelection();
+ selectInput(input);
+ } else if (user) prevInput = input.value = "";
}
+
function focusInput() {
if (options.readOnly != "nocursor") input.focus();
}
@@ -752,59 +974,156 @@ var CodeMirror = (function() {
function scrollEditorIntoView() {
if (!cursor.getBoundingClientRect) return;
var rect = cursor.getBoundingClientRect();
+ // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
+ if (ie && rect.top == rect.bottom) return;
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
- if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
+ if (rect.top < 0 || rect.bottom > winH) scrollCursorIntoView();
}
function scrollCursorIntoView() {
+ var coords = calculateCursorCoords();
+ return scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
+ }
+ function calculateCursorCoords() {
var cursor = localCoords(sel.inverted ? sel.from : sel.to);
- return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
+ var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
+ return {x: x, y: cursor.y, yBot: cursor.yBot};
}
function scrollIntoView(x1, y1, x2, y2) {
- var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2), scrolled = false;
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft; scrolled = true;}
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scrollPos.scrollTop; scrolled = true;}
+ if (scrolled && options.onScroll) options.onScroll(instance);
+ }
+ function calculateScrollPos(x1, y1, x2, y2) {
+ var pl = paddingLeft(), pt = paddingTop();
y1 += pt; y2 += pt; x1 += pl; x2 += pl;
- var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
- if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
- else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
+ var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
+ var atTop = y1 < paddingTop() + 10;
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
+ else if (y2 > screentop + screen) result.scrollTop = y2 - screen;
var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
- if (x1 < screenleft + gutterw) {
- if (x1 < 50) x1 = 0;
- scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
- scrolled = true;
+ var atLeft = x1 < gutterw + pl + 10;
+ if (x1 < screenleft + gutterw || atLeft) {
+ if (atLeft) x1 = 0;
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
+ } else if (x2 > screenw + screenleft - 3) {
+ result.scrollLeft = x2 + 10 - screenw;
}
- else if (x2 > screenw + screenleft) {
- scroller.scrollLeft = x2 + 10 - screenw;
- scrolled = true;
- if (x2 > code.clientWidth) result = false;
- }
- if (scrolled && options.onScroll) options.onScroll(instance);
return result;
}
- function visibleLines() {
- var lh = lineHeight(), top = scroller.scrollTop - paddingTop();
- return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
- to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))};
+ function visibleLines(scrollTop) {
+ var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop();
+ var fromHeight = Math.max(0, Math.floor(top / lh));
+ var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
+ return {from: lineAtHeight(doc, fromHeight),
+ to: lineAtHeight(doc, toHeight)};
}
// Uses a set of changes plus the current scroll position to
// determine which DOM updates have to be made, and makes the
// updates.
- function updateDisplay(changes) {
+ function updateDisplay(changes, suppressCallback, scrollTop) {
if (!scroller.clientWidth) {
- showingFrom = showingTo = 0;
+ showingFrom = showingTo = displayOffset = 0;
+ return;
+ }
+ // Compute the new visible window
+ // If scrollTop is specified, use that to determine which lines
+ // to render instead of the current scrollbar position.
+ var visible = visibleLines(scrollTop);
+ // Bail out if the visible area is already rendered and nothing changed.
+ if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) {
+ updateVerticalScroll(scrollTop);
+ return;
+ }
+ var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
+ if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
+ if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
+
+ // Create a range of theoretically intact lines, and punch holes
+ // in that using the change info.
+ var intact = changes === true ? [] :
+ computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
+ // Clip off the parts that won't be visible
+ var intactLines = 0;
+ for (var i = 0; i < intact.length; ++i) {
+ var range = intact[i];
+ if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
+ if (range.to > to) range.to = to;
+ if (range.from >= range.to) intact.splice(i--, 1);
+ else intactLines += range.to - range.from;
+ }
+ if (intactLines == to - from && from == showingFrom && to == showingTo) {
+ updateVerticalScroll(scrollTop);
return;
}
- // First create a range of theoretically intact lines, and punch
- // holes in that using the change info.
- var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}];
+ intact.sort(function(a, b) {return a.domStart - b.domStart;});
+
+ var th = textHeight(), gutterDisplay = gutter.style.display;
+ lineDiv.style.display = "none";
+ patchDisplay(from, to, intact);
+ lineDiv.style.display = gutter.style.display = "";
+
+ var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
+ // This is just a bogus formula that detects when the editor is
+ // resized or the font size changes.
+ if (different) lastSizeC = scroller.clientHeight + th;
+ showingFrom = from; showingTo = to;
+ displayOffset = heightAtLine(doc, from);
+
+ // Since this is all rather error prone, it is honoured with the
+ // only assertion in the whole file.
+ if (lineDiv.childNodes.length != showingTo - showingFrom)
+ throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
+ " nodes=" + lineDiv.childNodes.length);
+
+ function checkHeights() {
+ var curNode = lineDiv.firstChild, heightChanged = false;
+ doc.iter(showingFrom, showingTo, function(line) {
+ if (!line.hidden) {
+ var height = Math.round(curNode.offsetHeight / th) || 1;
+ if (line.height != height) {
+ updateLineHeight(line, height);
+ gutterDirty = heightChanged = true;
+ }
+ }
+ curNode = curNode.nextSibling;
+ });
+ return heightChanged;
+ }
+
+ if (options.lineWrapping) {
+ // Guess whether we're going to need the scrollbar, so that we don't end up changing the linewrapping
+ // after the scrollbar appears (during updateVerticalScroll()). Only do this if the scrollbar is
+ // appearing (if it's disappearing, we don't have to worry about the scroll position, and there are
+ // issues on IE7 if we turn it off too early).
+ var virtualHeight = Math.floor(doc.height * th + 2 * paddingTop()), scrollbarHeight = scroller.clientHeight;
+ if (virtualHeight > scrollbarHeight) scrollbar.style.display = "block";
+ checkHeights();
+ }
+
+ gutter.style.display = gutterDisplay;
+ if (different || gutterDirty) {
+ // If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
+ updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
+ }
+ updateSelection();
+ updateVerticalScroll(scrollTop);
+ if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
+ return true;
+ }
+
+ function computeIntact(intact, changes) {
for (var i = 0, l = changes.length || 0; i < l; ++i) {
var change = changes[i], intact2 = [], diff = change.diff || 0;
for (var j = 0, l2 = intact.length; j < l2; ++j) {
var range = intact[j];
- if (change.to <= range.from)
- intact2.push({from: range.from + diff, to: range.to + diff, domStart: range.domStart});
- else if (range.to <= change.from)
+ if (change.to <= range.from && change.diff)
+ intact2.push({from: range.from + diff, to: range.to + diff,
+ domStart: range.domStart});
+ else if (change.to <= range.from || change.from >= range.to)
intact2.push(range);
else {
if (change.from > range.from)
@@ -816,168 +1135,130 @@ var CodeMirror = (function() {
}
intact = intact2;
}
-
- // Then, determine which lines we'd want to see, and which
- // updates have to be made to get there.
- var visible = visibleLines();
- var from = Math.min(showingFrom, Math.max(visible.from - 3, 0)),
- to = Math.min(lines.length, Math.max(showingTo, visible.to + 3)),
- updates = [], domPos = 0, domEnd = showingTo - showingFrom, pos = from, changedLines = 0;
-
- for (var i = 0, l = intact.length; i < l; ++i) {
- var range = intact[i];
- if (range.to <= from) continue;
- if (range.from >= to) break;
- if (range.domStart > domPos || range.from > pos) {
- updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
- changedLines += range.from - pos;
- }
- pos = range.to;
- domPos = range.domStart + (range.to - range.from);
- }
- if (domPos != domEnd || pos != to) {
- changedLines += Math.abs(to - pos);
- updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
- if (to - pos != domEnd - domPos) gutterDirty = true;
- }
-
- if (!updates.length) return;
- lineDiv.style.display = "none";
- // If more than 30% of the screen needs update, just do a full
- // redraw (which is quicker than patching)
- if (changedLines > (visible.to - visible.from) * .3)
- refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
- // Otherwise, only update the stuff that needs updating.
- else
- patchDisplay(updates);
- lineDiv.style.display = "";
-
- // Position the mover div to align with the lines it's supposed
- // to be showing (which will cover the visible display)
- var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
- showingFrom = from; showingTo = to;
- mover.style.top = (from * lineHeight()) + "px";
- if (different) {
- lastHeight = scroller.clientHeight;
- code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
- }
- if (different || gutterDirty) updateGutter();
-
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
- if (maxWidth > scroller.clientWidth) {
- lineSpace.style.width = maxWidth + "px";
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
- code.style.width = "";
- code.style.width = scroller.scrollWidth + "px";
- } else {
- lineSpace.style.width = code.style.width = "";
- }
-
- // Since this is all rather error prone, it is honoured with the
- // only assertion in the whole file.
- if (lineDiv.childNodes.length != showingTo - showingFrom)
- throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) +
- " nodes=" + lineDiv.childNodes.length);
- updateCursor();
+ return intact;
}
- function refreshDisplay(from, to) {
- var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
- for (var i = from; i < to; ++i) {
- var ch1 = null, ch2 = null;
- if (inSel) {
- ch1 = 0;
- if (sel.to.line == i) {inSel = false; ch2 = sel.to.ch;}
- }
- else if (sel.from.line == i) {
- if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
- else {inSel = true; ch1 = sel.from.ch;}
+ function patchDisplay(from, to, intact) {
+ // The first pass removes the DOM nodes that aren't intact.
+ if (!intact.length) lineDiv.innerHTML = "";
+ else {
+ function killNode(node) {
+ var tmp = node.nextSibling;
+ node.parentNode.removeChild(node);
+ return tmp;
}
- html.push(lines[i].getHTML(ch1, ch2, true));
- }
- lineDiv.innerHTML = html.join("");
- }
- function patchDisplay(updates) {
- // Slightly different algorithm for IE (badInnerHTML), since
- // there .innerHTML on PRE nodes is dumb, and discards
- // whitespace.
- var sfrom = sel.from.line, sto = sel.to.line, off = 0,
- scratch = badInnerHTML && targetDocument.createElement("div");
- for (var i = 0, e = updates.length; i < e; ++i) {
- var rec = updates[i];
- var extra = (rec.to - rec.from) - rec.domSize;
- var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
- if (badInnerHTML)
- for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
- lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
- else if (extra) {
- for (var j = Math.max(0, extra); j > 0; --j)
- lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
- for (var j = Math.max(0, -extra); j > 0; --j)
- lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
+ var domPos = 0, curNode = lineDiv.firstChild, n;
+ for (var i = 0; i < intact.length; ++i) {
+ var cur = intact[i];
+ while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
+ for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
}
- var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
- for (var j = rec.from; j < rec.to; ++j) {
- var ch1 = null, ch2 = null;
- if (inSel) {
- ch1 = 0;
- if (sto == j) {inSel = false; ch2 = sel.to.ch;}
- }
- else if (sfrom == j) {
- if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
- else {inSel = true; ch1 = sel.from.ch;}
- }
- if (badInnerHTML) {
- scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
- lineDiv.insertBefore(scratch.firstChild, nodeAfter);
- }
+ while (curNode) curNode = killNode(curNode);
+ }
+ // This pass fills in the lines that actually changed.
+ var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
+ var scratch = document.createElement("div");
+ doc.iter(from, to, function(line) {
+ if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
+ if (!nextIntact || nextIntact.from > j) {
+ if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
else {
- node.innerHTML = lines[j].getHTML(ch1, ch2, false);
- node.className = lines[j].className || "";
- node = node.nextSibling;
+ var html = '<pre' + (line.className ? ' class="' + line.className + '"' : '') + '>'
+ + line.getHTML(makeTab) + '</pre>';
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
+ if (line.bgClassName)
+ html = '<div style="position: relative"><pre class="' + line.bgClassName +
+ '" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>' + html + "</div>";
}
+ scratch.innerHTML = html;
+ lineDiv.insertBefore(scratch.firstChild, curNode);
+ } else {
+ curNode = curNode.nextSibling;
}
- off += extra;
- }
+ ++j;
+ });
}
function updateGutter() {
if (!options.gutter && !options.lineNumbers) return;
var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
- var html = [];
- for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) {
- var marker = lines[i].gutterMarker;
- var text = options.lineNumbers ? i + options.firstLineNumber : null;
- if (marker && marker.text)
- text = marker.text.replace("%N%", text != null ? text : "");
- else if (text == null)
- text = "\u00a0";
- html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text, "</pre>");
- }
+ var html = [], i = showingFrom, normalNode;
+ doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
+ if (line.hidden) {
+ html.push("<pre></pre>");
+ } else {
+ var marker = line.gutterMarker;
+ var text = options.lineNumbers ? i + options.firstLineNumber : null;
+ if (marker && marker.text)
+ text = marker.text.replace("%N%", text != null ? text : "");
+ else if (text == null)
+ text = "\u00a0";
+ html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
+ for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
+ html.push("</pre>");
+ if (!marker) normalNode = i;
+ }
+ ++i;
+ });
gutter.style.display = "none";
gutterText.innerHTML = html.join("");
- var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
- while (val.length + pad.length < minwidth) pad += "\u00a0";
- if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
+ // Make sure scrolling doesn't cause number gutter size to pop
+ if (normalNode != null && options.lineNumbers) {
+ var node = gutterText.childNodes[normalNode - showingFrom];
+ var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = "";
+ while (val.length + pad.length < minwidth) pad += "\u00a0";
+ if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
+ }
gutter.style.display = "";
+ var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2;
lineSpace.style.marginLeft = gutter.offsetWidth + "px";
gutterDirty = false;
- }
- function updateCursor() {
- var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
- var x = charX(head.line, head.ch);
- var top = head.line * lh - scroller.scrollTop;
- inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
- inputDiv.style.left = (x - scroller.scrollLeft) + "px";
- if (posEq(sel.from, sel.to)) {
- cursor.style.top = (head.line - showingFrom) * lh + "px";
- cursor.style.left = x + "px";
+ return resized;
+ }
+ function updateSelection() {
+ var collapsed = posEq(sel.from, sel.to);
+ var fromPos = localCoords(sel.from, true);
+ var toPos = collapsed ? fromPos : localCoords(sel.to, true);
+ var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
+ var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
+ inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
+ inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
+ if (collapsed) {
+ cursor.style.top = headPos.y + "px";
+ cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
cursor.style.display = "";
+ selectionDiv.style.display = "none";
+ } else {
+ var sameLine = fromPos.y == toPos.y, html = "";
+ var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
+ var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
+ function add(left, top, right, height) {
+ var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
+ : "right: " + right + "px";
+ html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
+ 'px; top: ' + top + 'px; ' + rstyle + '; height: ' + height + 'px"></div>';
+ }
+ if (sel.from.ch && fromPos.y >= 0) {
+ var right = sameLine ? clientWidth - toPos.x : 0;
+ add(fromPos.x, fromPos.y, right, th);
+ }
+ var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
+ var middleHeight = Math.min(toPos.y, clientHeight) - middleStart;
+ if (middleHeight > 0.2 * th)
+ add(0, middleStart, 0, middleHeight);
+ if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
+ add(0, toPos.y, clientWidth - toPos.x, th);
+ selectionDiv.innerHTML = html;
+ cursor.style.display = "none";
+ selectionDiv.style.display = "";
}
- else cursor.style.display = "none";
}
+ function setShift(val) {
+ if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
+ else shiftSelecting = null;
+ }
function setSelectionUser(from, to) {
var sh = shiftSelecting && clipPos(shiftSelecting);
if (sh) {
@@ -985,130 +1266,166 @@ var CodeMirror = (function() {
else if (posLess(to, sh)) to = sh;
}
setSelection(from, to);
+ userSelChange = true;
}
// Update the selection. Last two args are only used by
// updateLines, since they have to be expressed in the line
// numbers before the update.
function setSelection(from, to, oldFrom, oldTo) {
+ goalColumn = null;
+ if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
if (posEq(sel.from, from) && posEq(sel.to, to)) return;
if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
+ // Skip over hidden lines.
+ if (from.line != oldFrom) {
+ var from1 = skipHidden(from, oldFrom, sel.from.ch);
+ // If there is no non-hidden line left, force visibility on current line
+ if (!from1) setLineHidden(from.line, false);
+ else from = from1;
+ }
+ if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
+
if (posEq(from, to)) sel.inverted = false;
else if (posEq(from, sel.to)) sel.inverted = false;
else if (posEq(to, sel.from)) sel.inverted = true;
- // Some ugly logic used to only mark the lines that actually did
- // see a change in selection as changed, rather than the whole
- // selected range.
- if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
- if (posEq(from, to)) {
- if (!posEq(sel.from, sel.to))
- changes.push({from: oldFrom, to: oldTo + 1});
- }
- else if (posEq(sel.from, sel.to)) {
- changes.push({from: from.line, to: to.line + 1});
- }
- else {
- if (!posEq(from, sel.from)) {
- if (from.line < oldFrom)
- changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
- else
- changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
- }
- if (!posEq(to, sel.to)) {
- if (to.line < oldTo)
- changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
- else
- changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
+ if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
+ var head = sel.inverted ? from : to;
+ if (head.line != sel.from.line && sel.from.line < doc.size) {
+ var oldLine = getLine(sel.from.line);
+ if (/^\s+$/.test(oldLine.text))
+ setTimeout(operation(function() {
+ if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
+ var no = lineNo(oldLine);
+ replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
+ }
+ }, 10));
}
}
+
sel.from = from; sel.to = to;
selectionChanged = true;
}
+ function skipHidden(pos, oldLine, oldCh) {
+ function getNonHidden(dir) {
+ var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
+ while (lNo != end) {
+ var line = getLine(lNo);
+ if (!line.hidden) {
+ var ch = pos.ch;
+ if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
+ return {line: lNo, ch: ch};
+ }
+ lNo += dir;
+ }
+ }
+ var line = getLine(pos.line);
+ var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
+ if (!line.hidden) return pos;
+ if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
+ else return getNonHidden(-1) || getNonHidden(1);
+ }
function setCursor(line, ch, user) {
var pos = clipPos({line: line, ch: ch || 0});
(user ? setSelectionUser : setSelection)(pos, pos);
}
- function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
+ function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
function clipPos(pos) {
if (pos.line < 0) return {line: 0, ch: 0};
- if (pos.line >= lines.length) return {line: lines.length-1, ch: lines[lines.length-1].text.length};
- var ch = pos.ch, linelen = lines[pos.line].text.length;
+ if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
+ var ch = pos.ch, linelen = getLine(pos.line).text.length;
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
else if (ch < 0) return {line: pos.line, ch: 0};
else return pos;
}
- function scrollPage(down) {
- var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
- setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
+ function findPosH(dir, unit) {
+ var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
+ var lineObj = getLine(line);
+ function findNextLine() {
+ for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
+ var lo = getLine(l);
+ if (!lo.hidden) { line = l; lineObj = lo; return true; }
+ }
+ }
+ function moveOnce(boundToLine) {
+ if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
+ if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
+ else return false;
+ } else ch += dir;
+ return true;
+ }
+ if (unit == "char") moveOnce();
+ else if (unit == "column") moveOnce(true);
+ else if (unit == "word") {
+ var sawWord = false;
+ for (;;) {
+ if (dir < 0) if (!moveOnce()) break;
+ if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
+ else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
+ if (dir > 0) if (!moveOnce()) break;
+ }
+ }
+ return {line: line, ch: ch};
+ }
+ function moveH(dir, unit) {
+ var pos = dir < 0 ? sel.from : sel.to;
+ if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
+ setCursor(pos.line, pos.ch, true);
}
- function scrollEnd(top) {
- var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length};
- setSelectionUser(pos, pos);
+ function deleteH(dir, unit) {
+ if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
+ else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
+ else replaceRange("", sel.from, findPosH(dir, unit));
+ userSelChange = true;
}
- function selectAll() {
- var endLine = lines.length - 1;
- setSelection({line: 0, ch: 0}, {line: endLine, ch: lines[endLine].text.length});
+ var goalColumn = null;
+ function moveV(dir, unit) {
+ var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
+ if (goalColumn != null) pos.x = goalColumn;
+ if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
+ else if (unit == "line") dist = textHeight();
+ var target = coordsChar(pos.x, pos.y + dist * dir + 2);
+ if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
+ setCursor(target.line, target.ch, true);
+ goalColumn = pos.x;
}
+
function selectWordAt(pos) {
- var line = lines[pos.line].text;
+ var line = getLine(pos.line).text;
var start = pos.ch, end = pos.ch;
- while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
- while (end < line.length && /\w/.test(line.charAt(end))) ++end;
+ while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
+ while (end < line.length && isWordChar(line.charAt(end))) ++end;
setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
}
function selectLine(line) {
- setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
+ setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
}
- function handleEnter() {
- replaceSelection("\n", "end");
- if (options.enterMode != "flat")
- indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
- }
- function handleTab(shift) {
- function indentSelected(mode) {
- if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
- var e = sel.to.line - (sel.to.ch ? 0 : 1);
- for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
- }
- shiftSelecting = null;
- switch (options.tabMode) {
- case "default":
- return false;
- case "indent":
- indentSelected("smart");
- break;
- case "classic":
- if (posEq(sel.from, sel.to)) {
- if (shift) indentLine(sel.from.line, "smart");
- else replaceSelection("\t", "end");
- break;
- }
- case "shift":
- indentSelected(shift ? "subtract" : "add");
- break;
- }
- return true;
- }
- function smartHome() {
- var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
- setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
+ function indentSelected(mode) {
+ if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
+ var e = sel.to.line - (sel.to.ch ? 0 : 1);
+ for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
}
function indentLine(n, how) {
+ if (!how) how = "add";
if (how == "smart") {
if (!mode.indent) how = "prev";
else var state = getStateBefore(n);
}
- var line = lines[n], curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
+ var line = getLine(n), curSpace = line.indentation(options.tabSize),
+ curSpaceString = line.text.match(/^\s*/)[0], indentation;
+ if (how == "smart") {
+ indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ if (indentation == Pass) how = "prev";
+ }
if (how == "prev") {
- if (n) indentation = lines[n-1].indentation();
+ if (n) indentation = getLine(n-1).indentation(options.tabSize);
else indentation = 0;
}
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
else if (how == "add") indentation = curSpace + options.indentUnit;
else if (how == "subtract") indentation = curSpace - options.indentUnit;
indentation = Math.max(0, indentation);
@@ -1117,11 +1434,10 @@ var CodeMirror = (function() {
if (!diff) {
if (sel.from.line != n && sel.to.line != n) return;
var indentString = curSpaceString;
- }
- else {
+ } else {
var indentString = "", pos = 0;
if (options.indentWithTabs)
- for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
+ for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
while (pos < indentation) {++pos; indentString += " ";}
}
@@ -1130,8 +1446,7 @@ var CodeMirror = (function() {
function loadMode() {
mode = CodeMirror.getMode(options, options.mode);
- for (var i = 0, l = lines.length; i < l; ++i)
- lines[i].stateAfter = null;
+ doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
work = [0];
startWorker();
}
@@ -1141,35 +1456,56 @@ var CodeMirror = (function() {
if (visible) gutterDirty = true;
else lineDiv.parentNode.style.marginLeft = 0;
}
-
- function markText(from, to, className) {
- from = clipPos(from); to = clipPos(to);
- var set = [];
- function add(line, from, to, className) {
- mark = lines[line].addMark(from, to, className, set);
- }
- if (from.line == to.line) add(from.line, from.ch, to.ch, className);
- else {
- add(from.line, from.ch, null, className);
- for (var i = from.line + 1, e = to.line; i < e; ++i)
- add(i, 0, null, className);
- add(to.line, 0, to.ch, className);
+ function wrappingChanged(from, to) {
+ if (options.lineWrapping) {
+ wrapper.className += " CodeMirror-wrap";
+ var perLine = scroller.clientWidth / charWidth() - 3;
+ doc.iter(0, doc.size, function(line) {
+ if (line.hidden) return;
+ var guess = Math.ceil(line.text.length / perLine) || 1;
+ if (guess != 1) updateLineHeight(line, guess);
+ });
+ lineSpace.style.width = code.style.width = "";
+ widthForcer.style.left = "";
+ } else {
+ wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
+ maxLine = ""; maxLineChanged = true;
+ doc.iter(0, doc.size, function(line) {
+ if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
+ if (line.text.length > maxLine.length) maxLine = line.text;
+ });
}
- changes.push({from: from.line, to: to.line + 1});
- return new TextMarker(set);
+ changes.push({from: 0, to: doc.size});
+ }
+ function makeTab(col) {
+ var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
+ if (cached) return cached;
+ for (var str = '<span class="cm-tab">', i = 0; i < w; ++i) str += " ";
+ return (tabCache[w] = {html: str + "</span>", width: w});
+ }
+ function themeChanged() {
+ scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
+ options.theme.replace(/(^|\s)\s*/g, " cm-s-");
+ }
+ function keyMapChanged() {
+ var style = keyMap[options.keyMap].style;
+ wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
+ (style ? " cm-keymap-" + style : "");
}
- function TextMarker(set) { this.set = set; }
+ function TextMarker() { this.set = []; }
TextMarker.prototype.clear = operation(function() {
+ var min = Infinity, max = -Infinity;
for (var i = 0, e = this.set.length; i < e; ++i) {
- var mk = this.set[i].marked;
- for (var j = 0; j < mk.length; ++j) {
- if (mk[j].set == this.set) mk.splice(j--, 1);
- }
- }
- // We don't know the exact lines that changed. Refreshing is
- // cheaper than finding them.
- changes.push({from: 0, to: lines.length});
+ var line = this.set[i], mk = line.marked;
+ if (!mk || !line.parent) continue;
+ var lineN = lineNo(line);
+ min = Math.min(min, lineN); max = Math.max(max, lineN);
+ for (var j = 0; j < mk.length; ++j)
+ if (mk[j].marker == this) mk.splice(j--, 1);
+ }
+ if (min != Infinity)
+ changes.push({from: min, to: max + 1});
});
TextMarker.prototype.find = function() {
var from, to;
@@ -1177,10 +1513,10 @@ var CodeMirror = (function() {
var line = this.set[i], mk = line.marked;
for (var j = 0; j < mk.length; ++j) {
var mark = mk[j];
- if (mark.set == this.set) {
+ if (mark.marker == this) {
if (mark.from != null || mark.to != null) {
- var found = indexOf(lines, line);
- if (found > -1) {
+ var found = lineNo(line);
+ if (found != null) {
if (mark.from != null) from = {line: found, ch: mark.from};
if (mark.to != null) to = {line: found, ch: mark.to};
}
@@ -1191,45 +1527,113 @@ var CodeMirror = (function() {
return {from: from, to: to};
};
+ function markText(from, to, className) {
+ from = clipPos(from); to = clipPos(to);
+ var tm = new TextMarker();
+ if (!posLess(from, to)) return tm;
+ function add(line, from, to, className) {
+ getLine(line).addMark(new MarkedText(from, to, className, tm));
+ }
+ if (from.line == to.line) add(from.line, from.ch, to.ch, className);
+ else {
+ add(from.line, from.ch, null, className);
+ for (var i = from.line + 1, e = to.line; i < e; ++i)
+ add(i, null, null, className);
+ add(to.line, null, to.ch, className);
+ }
+ changes.push({from: from.line, to: to.line + 1});
+ return tm;
+ }
+
+ function setBookmark(pos) {
+ pos = clipPos(pos);
+ var bm = new Bookmark(pos.ch);
+ getLine(pos.line).addMark(bm);
+ return bm;
+ }
+
+ function findMarksAt(pos) {
+ pos = clipPos(pos);
+ var markers = [], marked = getLine(pos.line).marked;
+ if (!marked) return markers;
+ for (var i = 0, e = marked.length; i < e; ++i) {
+ var m = marked[i];
+ if ((m.from == null || m.from <= pos.ch) &&
+ (m.to == null || m.to >= pos.ch))
+ markers.push(m.marker || m);
+ }
+ return markers;
+ }
+
function addGutterMarker(line, text, className) {
- if (typeof line == "number") line = lines[clipLine(line)];
+ if (typeof line == "number") line = getLine(clipLine(line));
line.gutterMarker = {text: text, style: className};
gutterDirty = true;
return line;
}
function removeGutterMarker(line) {
- if (typeof line == "number") line = lines[clipLine(line)];
+ if (typeof line == "number") line = getLine(clipLine(line));
line.gutterMarker = null;
gutterDirty = true;
}
- function setLineClass(line, className) {
- if (typeof line == "number") {
- var no = line;
- line = lines[clipLine(line)];
- }
- else {
- var no = indexOf(lines, line);
- if (no == -1) return null;
- }
- if (line.className != className) {
- line.className = className;
- changes.push({from: no, to: no + 1});
- }
+
+ function changeLine(handle, op) {
+ var no = handle, line = handle;
+ if (typeof handle == "number") line = getLine(clipLine(handle));
+ else no = lineNo(handle);
+ if (no == null) return null;
+ if (op(line, no)) changes.push({from: no, to: no + 1});
+ else return null;
return line;
}
+ function setLineClass(handle, className, bgClassName) {
+ return changeLine(handle, function(line) {
+ if (line.className != className || line.bgClassName != bgClassName) {
+ line.className = className;
+ line.bgClassName = bgClassName;
+ return true;
+ }
+ });
+ }
+ function setLineHidden(handle, hidden) {
+ return changeLine(handle, function(line, no) {
+ if (line.hidden != hidden) {
+ line.hidden = hidden;
+ if (!options.lineWrapping) {
+ var l = line.text;
+ if (hidden && l.length == maxLine.length) {
+ updateMaxLine = true;
+ } else if (!hidden && l.length > maxLine.length) {
+ maxLine = l; maxWidth = null; updateMaxLine = false;
+ }
+ }
+ updateLineHeight(line, hidden ? 0 : 1);
+ var fline = sel.from.line, tline = sel.to.line;
+ if (hidden && (fline == no || tline == no)) {
+ var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
+ var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
+ // Can't hide the last visible line, we'd have no place to put the cursor
+ if (!to) return;
+ setSelection(from, to);
+ }
+ return (gutterDirty = true);
+ }
+ });
+ }
function lineInfo(line) {
if (typeof line == "number") {
+ if (!isLine(line)) return null;
var n = line;
- line = lines[line];
+ line = getLine(line);
if (!line) return null;
- }
- else {
- var n = indexOf(lines, line);
- if (n == -1) return null;
+ } else {
+ var n = lineNo(line);
+ if (n == null) return null;
}
var marker = line.gutterMarker;
- return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
+ return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
+ markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
}
function stringWidth(str) {
@@ -1239,21 +1643,15 @@ var CodeMirror = (function() {
}
// These are used to go from pixel positions to character
// positions, taking varying character widths into account.
- function charX(line, pos) {
- if (pos == 0) return 0;
- measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
- return measure.firstChild.firstChild.offsetWidth;
- }
function charFromX(line, x) {
if (x <= 0) return 0;
- var lineObj = lines[line], text = lineObj.text;
+ var lineObj = getLine(line), text = lineObj.text;
function getX(len) {
- measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
- return measure.firstChild.firstChild.offsetWidth;
+ return measureLine(lineObj, len).left;
}
var from = 0, fromX = 0, to = text.length, toX;
// Guess a suitable upper bound for our search.
- var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
+ var estimated = Math.min(to, Math.ceil(x / charWidth()));
for (;;) {
var estX = getX(estimated);
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
@@ -1272,20 +1670,95 @@ var CodeMirror = (function() {
}
}
+ var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16);
+ function measureLine(line, ch) {
+ if (ch == 0) return {top: 0, left: 0};
+ var wbr = options.lineWrapping && ch < line.text.length &&
+ spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
+ measure.innerHTML = "<pre>" + line.getHTML(makeTab, ch, tempId, wbr) + "</pre>";
+ var elt = document.getElementById(tempId);
+ var top = elt.offsetTop, left = elt.offsetLeft;
+ // Older IEs report zero offsets for spans directly after a wrap
+ if (ie && top == 0 && left == 0) {
+ var backup = document.createElement("span");
+ backup.innerHTML = "x";
+ elt.parentNode.insertBefore(backup, elt.nextSibling);
+ top = backup.offsetTop;
+ }
+ return {top: top, left: left};
+ }
function localCoords(pos, inLineWrap) {
- var lh = lineHeight(), line = pos.line - (inLineWrap ? showingFrom : 0);
- return {x: charX(pos.line, pos.ch), y: line * lh, yBot: (line + 1) * lh};
+ var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
+ if (pos.ch == 0) x = 0;
+ else {
+ var sp = measureLine(getLine(pos.line), pos.ch);
+ x = sp.left;
+ if (options.lineWrapping) y += Math.max(0, sp.top);
+ }
+ return {x: x, y: y, yBot: y + lh};
+ }
+ // Coords must be lineSpace-local
+ function coordsChar(x, y) {
+ if (y < 0) y = 0;
+ var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
+ var lineNo = lineAtHeight(doc, heightPos);
+ if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
+ var lineObj = getLine(lineNo), text = lineObj.text;
+ var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
+ if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
+ function getX(len) {
+ var sp = measureLine(lineObj, len);
+ if (tw) {
+ var off = Math.round(sp.top / th);
+ return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
+ }
+ return sp.left;
+ }
+ var from = 0, fromX = 0, to = text.length, toX;
+ // Guess a suitable upper bound for our search.
+ var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
+ for (;;) {
+ var estX = getX(estimated);
+ if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
+ else {toX = estX; to = estimated; break;}
+ }
+ if (x > toX) return {line: lineNo, ch: to};
+ // Try to guess a suitable lower bound as well.
+ estimated = Math.floor(to * 0.8); estX = getX(estimated);
+ if (estX < x) {from = estimated; fromX = estX;}
+ // Do a binary search between these bounds.
+ for (;;) {
+ if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
+ var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX;}
+ else {from = middle; fromX = middleX;}
+ }
}
function pageCoords(pos) {
var local = localCoords(pos, true), off = eltOffset(lineSpace);
return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
}
- function lineHeight() {
- var nlines = lineDiv.childNodes.length;
- if (nlines) return (lineDiv.offsetHeight / nlines) || 1;
- measure.innerHTML = "<pre>x</pre>";
- return measure.firstChild.offsetHeight || 1;
+ var cachedHeight, cachedHeightFor, measureText;
+ function textHeight() {
+ if (measureText == null) {
+ measureText = "<pre>";
+ for (var i = 0; i < 49; ++i) measureText += "x<br/>";
+ measureText += "x</pre>";
+ }
+ var offsetHeight = lineDiv.clientHeight;
+ if (offsetHeight == cachedHeightFor) return cachedHeight;
+ cachedHeightFor = offsetHeight;
+ measure.innerHTML = measureText;
+ cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
+ measure.innerHTML = "";
+ return cachedHeight;
+ }
+ var cachedWidth, cachedWidthFor = 0;
+ function charWidth() {
+ if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
+ cachedWidthFor = scroller.clientWidth;
+ return (cachedWidth = stringWidth("x"));
}
function paddingTop() {return lineSpace.offsetTop;}
function paddingLeft() {return lineSpace.offsetLeft;}
@@ -1300,12 +1773,11 @@ var CodeMirror = (function() {
if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
return null;
var offL = eltOffset(lineSpace, true);
- var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
- return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
+ return coordsChar(x - offL.left, y - offL.top);
}
function onContextMenu(e) {
- var pos = posFromMouse(e);
- if (!pos || window.opera) return; // Opera is difficult.
+ var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
+ if (!pos || opera) return; // Opera is difficult.
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
operation(setCursor)(pos.line, pos.ch);
@@ -1317,14 +1789,15 @@ var CodeMirror = (function() {
leaveInputAlone = true;
var val = input.value = getSelection();
focusInput();
- setSelRange(input, 0, input.value.length);
+ selectInput(input);
function rehide() {
var newVal = splitLines(input.value).join("\n");
- if (newVal != val) operation(replaceSelection)(newVal, "end");
+ if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
inputDiv.style.position = "relative";
input.style.cssText = oldCSS;
+ if (ie_lt9) scrollbar.scrollTop = scrollPos;
leaveInputAlone = false;
- prepareInput();
+ resetInput(true);
slowPoll();
}
@@ -1334,8 +1807,7 @@ var CodeMirror = (function() {
mouseup();
setTimeout(rehide, 20);
}, true);
- }
- else {
+ } else {
setTimeout(rehide, 50);
}
}
@@ -1352,7 +1824,7 @@ var CodeMirror = (function() {
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
function matchBrackets(autoclear) {
- var head = sel.inverted ? sel.from : sel.to, line = lines[head.line], pos = head.ch - 1;
+ var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
if (!match) return;
var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
@@ -1365,7 +1837,7 @@ var CodeMirror = (function() {
var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
var text = st[i];
- if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
+ if (st[i+1] != style) {pos += d * text.length; continue;}
for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
var match = matching[cur];
@@ -1376,8 +1848,8 @@ var CodeMirror = (function() {
}
}
}
- for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) {
- var line = lines[i], first = i == head.line;
+ for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
+ var line = getLine(i), first = i == head.line;
var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
if (found) break;
}
@@ -1399,9 +1871,9 @@ var CodeMirror = (function() {
var minindent, minline;
for (var search = n, lim = n - 40; search > lim; --search) {
if (search == 0) return 0;
- var line = lines[search-1];
+ var line = getLine(search-1);
if (line.stateAfter) return search;
- var indented = line.indentation();
+ var indented = line.indentation(options.tabSize);
if (minline == null || minindent > indented) {
minline = search - 1;
minindent = indented;
@@ -1410,56 +1882,62 @@ var CodeMirror = (function() {
return minline;
}
function getStateBefore(n) {
- var start = findStartLine(n), state = start && lines[start-1].stateAfter;
+ var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
if (!state) state = startState(mode);
else state = copyState(mode, state);
- for (var i = start; i < n; ++i) {
- var line = lines[i];
- line.highlight(mode, state);
+ doc.iter(start, n, function(line) {
+ line.highlight(mode, state, options.tabSize);
line.stateAfter = copyState(mode, state);
- }
- changes.push({from: start, to: n});
- if (n < lines.length && !lines[n].stateAfter) work.push(n);
+ });
+ if (start < n) changes.push({from: start, to: n});
+ if (n < doc.size && !getLine(n).stateAfter) work.push(n);
return state;
}
function highlightLines(start, end) {
var state = getStateBefore(start);
- for (var i = start; i < end; ++i) {
- var line = lines[i];
- line.highlight(mode, state);
+ doc.iter(start, end, function(line) {
+ line.highlight(mode, state, options.tabSize);
line.stateAfter = copyState(mode, state);
- }
+ });
}
function highlightWorker() {
var end = +new Date + options.workTime;
var foundWork = work.length;
while (work.length) {
- if (!lines[showingFrom].stateAfter) var task = showingFrom;
+ if (!getLine(showingFrom).stateAfter) var task = showingFrom;
else var task = work.pop();
- if (task >= lines.length) continue;
- var start = findStartLine(task), state = start && lines[start-1].stateAfter;
+ if (task >= doc.size) continue;
+ var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
if (state) state = copyState(mode, state);
else state = startState(mode);
- var unchanged = 0, compare = mode.compareStates, realChange = false;
- for (var i = start, l = lines.length; i < l; ++i) {
- var line = lines[i], hadState = line.stateAfter;
+ var unchanged = 0, compare = mode.compareStates, realChange = false,
+ i = start, bail = false;
+ doc.iter(i, doc.size, function(line) {
+ var hadState = line.stateAfter;
if (+new Date > end) {
work.push(i);
startWorker(options.workDelay);
if (realChange) changes.push({from: task, to: i + 1});
- return;
+ return (bail = true);
}
- var changed = line.highlight(mode, state);
+ var changed = line.highlight(mode, state, options.tabSize);
if (changed) realChange = true;
line.stateAfter = copyState(mode, state);
+ var done = null;
if (compare) {
- if (hadState && compare(hadState, state)) break;
- } else {
+ var same = hadState && compare(hadState, state);
+ if (same != Pass) done = !!same;
+ }
+ if (done == null) {
if (changed !== false || !hadState) unchanged = 0;
- else if (++unchanged > 3) break;
+ else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
+ done = true;
}
- }
+ if (done) return true;
+ ++i;
+ });
+ if (bail) return;
if (realChange) changes.push({from: task, to: i + 1});
}
if (foundWork && options.onHighlightComplete)
@@ -1475,35 +1953,44 @@ var CodeMirror = (function() {
// be awkward, slow, and error-prone), but instead updates are
// batched and then all combined and executed at once.
function startOperation() {
- updateInput = null; changes = []; textChanged = selectionChanged = false;
+ updateInput = userSelChange = textChanged = null;
+ changes = []; selectionChanged = false; callbacks = [];
}
function endOperation() {
- var reScroll = false;
- if (selectionChanged) reScroll = !scrollCursorIntoView();
- if (changes.length) updateDisplay(changes);
+ if (updateMaxLine) computeMaxLength();
+ if (maxLineChanged && !options.lineWrapping) {
+ widthForcer.style.left = stringWidth(maxLine) + "px";
+ maxLineChanged = false;
+ }
+ var newScrollPos, updated;
+ if (selectionChanged) {
+ var coords = calculateCursorCoords();
+ newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
+ }
+ if (changes.length) updated = updateDisplay(changes, true, (newScrollPos ? newScrollPos.scrollTop : null));
else {
- if (selectionChanged) updateCursor();
+ if (selectionChanged) updateSelection();
if (gutterDirty) updateGutter();
}
- if (reScroll) scrollCursorIntoView();
+ if (newScrollPos) scrollCursorIntoView();
if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
- // updateInput can be set to a boolean value to force/prevent an
- // update.
if (focused && !leaveInputAlone &&
(updateInput === true || (updateInput !== false && selectionChanged)))
- prepareInput();
+ resetInput(userSelChange);
if (selectionChanged && options.matchBrackets)
setTimeout(operation(function() {
if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
- matchBrackets(false);
+ if (posEq(sel.from, sel.to)) matchBrackets(false);
}), 20);
- var tc = textChanged; // textChanged can be reset by cursoractivity callback
- if (selectionChanged && options.onCursorActivity)
+ var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks
+ if (textChanged && options.onChange && instance)
+ options.onChange(instance, textChanged);
+ if (sc && options.onCursorActivity)
options.onCursorActivity(instance);
- if (tc && options.onChange && instance)
- options.onChange(instance, tc);
+ for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
+ if (updated && options.onUpdate) options.onUpdate(instance);
}
var nestedOperation = 0;
function operation(f) {
@@ -1515,120 +2002,11 @@ var CodeMirror = (function() {
};
}
- function SearchCursor(query, pos, caseFold) {
- this.atOccurrence = false;
- if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
-
- if (pos && typeof pos == "object") pos = clipPos(pos);
- else pos = {line: 0, ch: 0};
- this.pos = {from: pos, to: pos};
-
- // The matches method is filled in based on the type of query.
- // It takes a position and a direction, and returns an object
- // describing the next occurrence of the query, or null if no
- // more matches were found.
- if (typeof query != "string") // Regexp match
- this.matches = function(reverse, pos) {
- if (reverse) {
- var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0;
- while (match) {
- var ind = line.indexOf(match[0]);
- start += ind;
- line = line.slice(ind + 1);
- var newmatch = line.match(query);
- if (newmatch) match = newmatch;
- else break;
- start++;
- }
- }
- else {
- var line = lines[pos.line].text.slice(pos.ch), match = line.match(query),
- start = match && pos.ch + line.indexOf(match[0]);
- }
- if (match)
- return {from: {line: pos.line, ch: start},
- to: {line: pos.line, ch: start + match[0].length},
- match: match};
- };
- else { // String query
- if (caseFold) query = query.toLowerCase();
- var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
- var target = query.split("\n");
- // Different methods for single-line and multi-line queries
- if (target.length == 1)
- this.matches = function(reverse, pos) {
- var line = fold(lines[pos.line].text), len = query.length, match;
- if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
- : (match = line.indexOf(query, pos.ch)) != -1)
- return {from: {line: pos.line, ch: match},
- to: {line: pos.line, ch: match + len}};
- };
- else
- this.matches = function(reverse, pos) {
- var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text);
- var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
- if (reverse ? offsetA >= pos.ch || offsetA != match.length
- : offsetA <= pos.ch || offsetA != line.length - match.length)
- return;
- for (;;) {
- if (reverse ? !ln : ln == lines.length - 1) return;
- line = fold(lines[ln += reverse ? -1 : 1].text);
- match = target[reverse ? --idx : ++idx];
- if (idx > 0 && idx < target.length - 1) {
- if (line != match) return;
- else continue;
- }
- var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
- if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
- return;
- var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
- return {from: reverse ? end : start, to: reverse ? start : end};
- }
- };
- }
+ function compoundChange(f) {
+ history.startCompound();
+ try { return f(); } finally { history.endCompound(); }
}
- SearchCursor.prototype = {
- findNext: function() {return this.find(false);},
- findPrevious: function() {return this.find(true);},
-
- find: function(reverse) {
- var self = this, pos = clipPos(reverse ? this.pos.from : this.pos.to);
- function savePosAndFail(line) {
- var pos = {line: line, ch: 0};
- self.pos = {from: pos, to: pos};
- self.atOccurrence = false;
- return false;
- }
-
- for (;;) {
- if (this.pos = this.matches(reverse, pos)) {
- this.atOccurrence = true;
- return this.pos.match || true;
- }
- if (reverse) {
- if (!pos.line) return savePosAndFail(0);
- pos = {line: pos.line-1, ch: lines[pos.line-1].text.length};
- }
- else {
- if (pos.line == lines.length - 1) return savePosAndFail(lines.length);
- pos = {line: pos.line+1, ch: 0};
- }
- }
- },
-
- from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
- to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
-
- replace: function(newText) {
- var self = this;
- if (this.atOccurrence)
- operation(function() {
- self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
- })();
- }
- };
-
for (var ext in extensions)
if (extensions.propertyIsEnumerable(ext) &&
!instance.propertyIsEnumerable(ext))
@@ -1643,51 +2021,66 @@ var CodeMirror = (function() {
theme: "default",
indentUnit: 2,
indentWithTabs: false,
- tabMode: "classic",
- enterMode: "indent",
+ smartIndent: true,
+ tabSize: 4,
+ keyMap: "default",
+ extraKeys: null,
electricChars: true,
+ autoClearEmptyLines: false,
onKeyEvent: null,
+ onDragEvent: null,
+ lineWrapping: false,
lineNumbers: false,
gutter: false,
fixedGutter: false,
firstLineNumber: 1,
readOnly: false,
- smartHome: true,
+ dragDrop: true,
onChange: null,
onCursorActivity: null,
onGutterClick: null,
onHighlightComplete: null,
+ onUpdate: null,
onFocus: null, onBlur: null, onScroll: null,
matchBrackets: false,
workTime: 100,
workDelay: 200,
+ pollInterval: 100,
undoDepth: 40,
tabindex: null,
- document: window.document
+ autofocus: null
};
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
+ var mac = ios || /Mac/.test(navigator.platform);
+ var win = /Win/.test(navigator.platform);
+
// Known modes, by name and by MIME
- var modes = {}, mimeModes = {};
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
CodeMirror.defineMode = function(name, mode) {
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
+ if (arguments.length > 2) {
+ mode.dependencies = [];
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
+ }
modes[name] = mode;
};
CodeMirror.defineMIME = function(mime, spec) {
mimeModes[mime] = spec;
};
- CodeMirror.getMode = function(options, spec) {
+ CodeMirror.resolveMode = function(spec) {
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
spec = mimeModes[spec];
- if (typeof spec == "string")
- var mname = spec, config = {};
- else if (spec != null)
- var mname = spec.name, config = spec;
- var mfactory = modes[mname];
- if (!mfactory) {
- if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
- return CodeMirror.getMode(options, "text/plain");
- }
- return mfactory(options, config || {});
+ else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
+ return CodeMirror.resolveMode("application/xml");
+ if (typeof spec == "string") return {name: spec};
+ else return spec || {name: "null"};
+ };
+ CodeMirror.getMode = function(options, spec) {
+ var spec = CodeMirror.resolveMode(spec);
+ var mfactory = modes[spec.name];
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
+ return mfactory(options, spec);
};
CodeMirror.listModes = function() {
var list = [];
@@ -1702,16 +2095,137 @@ var CodeMirror = (function() {
return list;
};
- var extensions = {};
+ var extensions = CodeMirror.extensions = {};
CodeMirror.defineExtension = function(name, func) {
extensions[name] = func;
};
+ var commands = CodeMirror.commands = {
+ selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
+ killLine: function(cm) {
+ var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
+ if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
+ else cm.replaceRange("", from, sel ? to : {line: from.line});
+ },
+ deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
+ undo: function(cm) {cm.undo();},
+ redo: function(cm) {cm.redo();},
+ goDocStart: function(cm) {cm.setCursor(0, 0, true);},
+ goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
+ goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
+ goLineStartSmart: function(cm) {
+ var cur = cm.getCursor();
+ var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
+ cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
+ },
+ goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
+ goLineDown: function(cm) {cm.moveV(1, "line");},
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
+ goPageDown: function(cm) {cm.moveV(1, "page");},
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
+ goCharRight: function(cm) {cm.moveH(1, "char");},
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
+ goWordRight: function(cm) {cm.moveH(1, "word");},
+ delCharLeft: function(cm) {cm.deleteH(-1, "char");},
+ delCharRight: function(cm) {cm.deleteH(1, "char");},
+ delWordLeft: function(cm) {cm.deleteH(-1, "word");},
+ delWordRight: function(cm) {cm.deleteH(1, "word");},
+ indentAuto: function(cm) {cm.indentSelection("smart");},
+ indentMore: function(cm) {cm.indentSelection("add");},
+ indentLess: function(cm) {cm.indentSelection("subtract");},
+ insertTab: function(cm) {cm.replaceSelection("\t", "end");},
+ defaultTab: function(cm) {
+ if (cm.somethingSelected()) cm.indentSelection("add");
+ else cm.replaceSelection("\t", "end");
+ },
+ transposeChars: function(cm) {
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
+ if (cur.ch > 0 && cur.ch < line.length - 1)
+ cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
+ {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
+ },
+ newlineAndIndent: function(cm) {
+ cm.replaceSelection("\n", "end");
+ cm.indentLine(cm.getCursor().line);
+ },
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
+ };
+
+ var keyMap = CodeMirror.keyMap = {};
+ keyMap.basic = {
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+ "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
+ };
+ // Note that the save and find-related commands aren't defined by
+ // default. Unknown commands are simply ignored.
+ keyMap.pcDefault = {
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
+ "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
+ fallthrough: "basic"
+ };
+ keyMap.macDefault = {
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
+ "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
+ "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore",
+ fallthrough: ["basic", "emacsy"]
+ };
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
+ keyMap.emacsy = {
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+ "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
+ "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
+ };
+
+ function getKeyMap(val) {
+ if (typeof val == "string") return keyMap[val];
+ else return val;
+ }
+ function lookupKey(name, extraMap, map, handle, stop) {
+ function lookup(map) {
+ map = getKeyMap(map);
+ var found = map[name];
+ if (found != null && handle(found)) return true;
+ if (map.nofallthrough) {
+ if (stop) stop();
+ return true;
+ }
+ var fallthrough = map.fallthrough;
+ if (fallthrough == null) return false;
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
+ return lookup(fallthrough);
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
+ if (lookup(fallthrough[i])) return true;
+ }
+ return false;
+ }
+ if (extraMap && lookup(extraMap)) return true;
+ return lookup(map);
+ }
+ function isModifierKey(event) {
+ var name = keyNames[e_prop(event, "keyCode")];
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
+ }
+
CodeMirror.fromTextArea = function(textarea, options) {
if (!options) options = {};
options.value = textarea.value;
if (!options.tabindex && textarea.tabindex)
options.tabindex = textarea.tabindex;
+ if (options.autofocus == null && textarea.getAttribute("autofocus") != null)
+ options.autofocus = true;
function save() {textarea.value = instance.getValue();}
if (textarea.form) {
@@ -1734,6 +2248,7 @@ var CodeMirror = (function() {
textarea.parentNode.insertBefore(node, textarea.nextSibling);
}, options);
instance.save = save;
+ instance.getTextArea = function() { return textarea; };
instance.toTextArea = function() {
save();
textarea.parentNode.removeChild(instance.getWrapperElement());
@@ -1760,16 +2275,17 @@ var CodeMirror = (function() {
}
return nstate;
}
- CodeMirror.startState = startState;
+ CodeMirror.copyState = copyState;
function startState(mode, a1, a2) {
return mode.startState ? mode.startState(a1, a2) : true;
}
- CodeMirror.copyState = copyState;
+ CodeMirror.startState = startState;
// The character stream used by a mode's parser.
- function StringStream(string) {
+ function StringStream(string, tabSize) {
this.pos = this.start = 0;
this.string = string;
+ this.tabSize = tabSize || 8;
}
StringStream.prototype = {
eol: function() {return this.pos >= this.string.length;},
@@ -1801,8 +2317,8 @@ var CodeMirror = (function() {
if (found > -1) {this.pos = found; return true;}
},
backUp: function(n) {this.pos -= n;},
- column: function() {return countColumn(this.string, this.start);},
- indentation: function() {return countColumn(this.string);},
+ column: function() {return countColumn(this.string, this.start, this.tabSize);},
+ indentation: function() {return countColumn(this.string, null, this.tabSize);},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
@@ -1810,8 +2326,7 @@ var CodeMirror = (function() {
if (consume !== false) this.pos += pattern.length;
return true;
}
- }
- else {
+ } else {
var match = this.string.slice(this.pos).match(pattern);
if (match && consume !== false) this.pos += match[0].length;
return match;
@@ -1821,22 +2336,86 @@ var CodeMirror = (function() {
};
CodeMirror.StringStream = StringStream;
+ function MarkedText(from, to, className, marker) {
+ this.from = from; this.to = to; this.style = className; this.marker = marker;
+ }
+ MarkedText.prototype = {
+ attach: function(line) { this.marker.set.push(line); },
+ detach: function(line) {
+ var ix = indexOf(this.marker.set, line);
+ if (ix > -1) this.marker.set.splice(ix, 1);
+ },
+ split: function(pos, lenBefore) {
+ if (this.to <= pos && this.to != null) return null;
+ var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
+ var to = this.to == null ? null : this.to - pos + lenBefore;
+ return new MarkedText(from, to, this.style, this.marker);
+ },
+ dup: function() { return new MarkedText(null, null, this.style, this.marker); },
+ clipTo: function(fromOpen, from, toOpen, to, diff) {
+ if (fromOpen && to > this.from && (to < this.to || this.to == null))
+ this.from = null;
+ else if (this.from != null && this.from >= from)
+ this.from = Math.max(to, this.from) + diff;
+ if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
+ this.to = null;
+ else if (this.to != null && this.to > from)
+ this.to = to < this.to ? this.to + diff : from;
+ },
+ isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
+ sameSet: function(x) { return this.marker == x.marker; }
+ };
+
+ function Bookmark(pos) {
+ this.from = pos; this.to = pos; this.line = null;
+ }
+ Bookmark.prototype = {
+ attach: function(line) { this.line = line; },
+ detach: function(line) { if (this.line == line) this.line = null; },
+ split: function(pos, lenBefore) {
+ if (pos < this.from) {
+ this.from = this.to = (this.from - pos) + lenBefore;
+ return this;
+ }
+ },
+ isDead: function() { return this.from > this.to; },
+ clipTo: function(fromOpen, from, toOpen, to, diff) {
+ if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
+ this.from = 0; this.to = -1;
+ } else if (this.from > from) {
+ this.from = this.to = Math.max(to, this.from) + diff;
+ }
+ },
+ sameSet: function(x) { return false; },
+ find: function() {
+ if (!this.line || !this.line.parent) return null;
+ return {line: lineNo(this.line), ch: this.from};
+ },
+ clear: function() {
+ if (this.line) {
+ var found = indexOf(this.line.marked, this);
+ if (found != -1) this.line.marked.splice(found, 1);
+ this.line = null;
+ }
+ }
+ };
+
// Line objects. These hold state related to a line, including
// highlighting info (the styles array).
function Line(text, styles) {
this.styles = styles || [text, null];
- this.stateAfter = null;
this.text = text;
- this.marked = this.gutterMarker = this.className = null;
+ this.height = 1;
+ this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null;
+ this.stateAfter = this.parent = this.hidden = null;
}
Line.inheritMarks = function(text, orig) {
- var ln = new Line(text), mk = orig.marked;
+ var ln = new Line(text), mk = orig && orig.marked;
if (mk) {
for (var i = 0; i < mk.length; ++i) {
- if (mk[i].to == null) {
+ if (mk[i].to == null && mk[i].style) {
var newmk = ln.marked || (ln.marked = []), mark = mk[i];
- newmk.push({from: null, to: null, style: mark.style, set: mark.set});
- mark.set.push(ln);
+ var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
}
}
}
@@ -1853,23 +2432,11 @@ var CodeMirror = (function() {
this.text = this.text.slice(0, from) + text + this.text.slice(to);
this.stateAfter = null;
if (mk) {
- var diff = text.length - (to - from), end = this.text.length;
- var changeStart = Math.min(from, from + diff);
+ var diff = text.length - (to - from);
for (var i = 0; i < mk.length; ++i) {
- var mark = mk[i], del = false;
- if (mark.from != null && mark.from >= end) del = true;
- else {
- if (mark.from != null && mark.from >= from) {
- mark.from += diff;
- if (mark.from <= 0) mark.from = from == null ? null : 0;
- }
- else if (to_ == null) mark.to = null;
- if (mark.to != null && mark.to > from) {
- mark.to += diff;
- if (mark.to < 0) del = true;
- }
- }
- if (del || (mark.from != null && mark.to != null && mark.from >= mark.to)) mk.splice(i--, 1);
+ var mark = mk[i];
+ mark.clipTo(from == null, from || 0, to_ == null, to, diff);
+ if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
}
}
},
@@ -1881,57 +2448,77 @@ var CodeMirror = (function() {
if (mk) {
for (var i = 0; i < mk.length; ++i) {
var mark = mk[i];
- if (mark.to > pos || mark.to == null) {
+ var newmark = mark.split(pos, textBefore.length);
+ if (newmark) {
if (!taken.marked) taken.marked = [];
- taken.marked.push({
- from: mark.from < pos || mark.from == null ? null : mark.from - pos + textBefore.length,
- to: mark.to == null ? null : mark.to - pos + textBefore.length,
- style: mark.style, set: mark.set
- });
- mark.set.push(taken);
+ taken.marked.push(newmark); newmark.attach(taken);
+ if (newmark == mark) mk.splice(i--, 1);
}
}
}
return taken;
},
append: function(line) {
- if (!line.text.length) return;
- var mylen = this.text.length, mk = line.marked;
+ var mylen = this.text.length, mk = line.marked, mymk = this.marked;
this.text += line.text;
copyStyles(0, line.text.length, line.styles, this.styles);
- if (mk && mk.length) {
- var mymk = this.marked || (this.marked = []);
+ if (mymk) {
for (var i = 0; i < mymk.length; ++i)
if (mymk[i].to == null) mymk[i].to = mylen;
+ }
+ if (mk && mk.length) {
+ if (!mymk) this.marked = mymk = [];
outer: for (var i = 0; i < mk.length; ++i) {
var mark = mk[i];
if (!mark.from) {
for (var j = 0; j < mymk.length; ++j) {
var mymark = mymk[j];
- if (mymark.to == mylen && mymark.set == mark.set) {
+ if (mymark.to == mylen && mymark.sameSet(mark)) {
mymark.to = mark.to == null ? null : mark.to + mylen;
+ if (mymark.isDead()) {
+ mymark.detach(this);
+ mk.splice(i--, 1);
+ }
continue outer;
}
}
}
mymk.push(mark);
- mark.set.push(this);
+ mark.attach(this);
mark.from += mylen;
if (mark.to != null) mark.to += mylen;
}
}
},
- addMark: function(from, to, style, set) {
- set.push(this);
+ fixMarkEnds: function(other) {
+ var mk = this.marked, omk = other.marked;
+ if (!mk) return;
+ for (var i = 0; i < mk.length; ++i) {
+ var mark = mk[i], close = mark.to == null;
+ if (close && omk) {
+ for (var j = 0; j < omk.length; ++j)
+ if (omk[j].sameSet(mark)) {close = false; break;}
+ }
+ if (close) mark.to = this.text.length;
+ }
+ },
+ fixMarkStarts: function() {
+ var mk = this.marked;
+ if (!mk) return;
+ for (var i = 0; i < mk.length; ++i)
+ if (mk[i].from == null) mk[i].from = 0;
+ },
+ addMark: function(mark) {
+ mark.attach(this);
if (this.marked == null) this.marked = [];
- this.marked.push({from: from, to: to, style: style, set: set});
+ this.marked.push(mark);
this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
},
// Run the given mode's parser over a line, update the styles
// array, which contains alternating fragments of text and CSS
// classes.
- highlight: function(mode, state) {
- var stream = new StringStream(this.text), st = this.styles, pos = 0;
+ highlight: function(mode, state, tabSize) {
+ var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
var changed = false, curWord = st[0], prevWord;
if (this.text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
@@ -1972,74 +2559,125 @@ var CodeMirror = (function() {
className: style || null,
state: state};
},
- indentation: function() {return countColumn(this.text);},
+ indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
// Produces an HTML fragment for the line, taking selection,
// marking, and highlighting into account.
- getHTML: function(sfrom, sto, includePre, endAt) {
- var html = [];
- if (includePre)
- html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
- function span(text, style) {
+ getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) {
+ var html = [], first = true, col = 0;
+ function span_(text, style) {
if (!text) return;
- if (style) html.push('<span class="', style, '">', htmlEscape(text), "</span>");
- else html.push(htmlEscape(text));
+ // Work around a bug where, in some compat modes, IE ignores leading spaces
+ if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
+ first = false;
+ if (text.indexOf("\t") == -1) {
+ col += text.length;
+ var escaped = htmlEscape(text);
+ } else {
+ var escaped = "";
+ for (var pos = 0;;) {
+ var idx = text.indexOf("\t", pos);
+ if (idx == -1) {
+ escaped += htmlEscape(text.slice(pos));
+ col += text.length - pos;
+ break;
+ } else {
+ col += idx - pos;
+ var tab = makeTab(col);
+ escaped += htmlEscape(text.slice(pos, idx)) + tab.html;
+ col += tab.width;
+ pos = idx + 1;
+ }
+ }
+ }
+ if (style) html.push('<span class="', style, '">', escaped, "</span>");
+ else html.push(escaped);
+ }
+ var span = span_;
+ if (wrapAt != null) {
+ var outPos = 0, open = "<span id=\"" + wrapId + "\">";
+ span = function(text, style) {
+ var l = text.length;
+ if (wrapAt >= outPos && wrapAt < outPos + l) {
+ if (wrapAt > outPos) {
+ span_(text.slice(0, wrapAt - outPos), style);
+ // See comment at the definition of spanAffectsWrapping
+ if (wrapWBR) html.push("<wbr>");
+ }
+ html.push(open);
+ var cut = wrapAt - outPos;
+ span_(opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
+ html.push("</span>");
+ if (opera) span_(text.slice(cut + 1), style);
+ wrapAt--;
+ outPos += l;
+ } else {
+ outPos += l;
+ span_(text, style);
+ // Output empty wrapper when at end of line
+ if (outPos == wrapAt && outPos == len) html.push(open + " </span>");
+ // Stop outputting HTML when gone sufficiently far beyond measure
+ else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
+ }
+ }
}
+
var st = this.styles, allText = this.text, marked = this.marked;
- if (sfrom == sto) sfrom = null;
var len = allText.length;
- if (endAt != null) len = Math.min(endAt, len);
+ function styleToClass(style) {
+ if (!style) return null;
+ return "cm-" + style.replace(/ +/g, " cm-");
+ }
- if (!allText && endAt == null)
- span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
- else if (!marked && sfrom == null)
+ if (!allText && wrapAt == null) {
+ span(" ");
+ } else if (!marked || !marked.length) {
for (var i = 0, ch = 0; ch < len; i+=2) {
var str = st[i], style = st[i+1], l = str.length;
if (ch + l > len) str = str.slice(0, len - ch);
ch += l;
- span(str, style && "cm-" + style);
+ span(str, styleToClass(style));
}
- else {
+ } else {
var pos = 0, i = 0, text = "", style, sg = 0;
- var markpos = -1, mark = null;
- function nextMark() {
- if (marked) {
- markpos += 1;
- mark = (markpos < marked.length) ? marked[markpos] : null;
+ var nextChange = marked[0].from || 0, marks = [], markpos = 0;
+ function advanceMarks() {
+ var m;
+ while (markpos < marked.length &&
+ ((m = marked[markpos]).from == pos || m.from == null)) {
+ if (m.style != null) marks.push(m);
+ ++markpos;
+ }
+ nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
+ for (var i = 0; i < marks.length; ++i) {
+ var to = marks[i].to || Infinity;
+ if (to == pos) marks.splice(i--, 1);
+ else nextChange = Math.min(to, nextChange);
}
}
- nextMark();
+ var m = 0;
while (pos < len) {
- var upto = len;
- var extraStyle = "";
- if (sfrom != null) {
- if (sfrom > pos) upto = sfrom;
- else if (sto == null || sto > pos) {
- extraStyle = " CodeMirror-selected";
- if (sto != null) upto = Math.min(upto, sto);
- }
- }
- while (mark && mark.to != null && mark.to <= pos) nextMark();
- if (mark) {
- if (mark.from > pos) upto = Math.min(upto, mark.from);
- else {
- extraStyle += " " + mark.style;
- if (mark.to != null) upto = Math.min(upto, mark.to);
+ if (nextChange == pos) advanceMarks();
+ var upto = Math.min(len, nextChange);
+ while (true) {
+ if (text) {
+ var end = pos + text.length;
+ var appliedStyle = style;
+ for (var j = 0; j < marks.length; ++j)
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
+ span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
+ pos = end;
}
- }
- for (;;) {
- var end = pos + text.length;
- var appliedStyle = style;
- if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
- pos = end;
- text = st[i++]; style = "cm-" + st[i++];
+ text = st[i++]; style = styleToClass(st[i++]);
}
}
- if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
}
- if (includePre) html.push("</pre>");
return html.join("");
+ },
+ cleanUp: function() {
+ this.parent = null;
+ if (this.marked)
+ for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
}
};
// Utility used by replace and split above
@@ -2049,8 +2687,7 @@ var CodeMirror = (function() {
if (state == 0) {
if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
if (end >= from) state = 1;
- }
- else if (state == 1) {
+ } else if (state == 1) {
if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
else dest.push(part, source[i+1]);
}
@@ -2058,36 +2695,229 @@ var CodeMirror = (function() {
}
}
+ // Data structure that holds the sequence of lines.
+ function LeafChunk(lines) {
+ this.lines = lines;
+ this.parent = null;
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
+ lines[i].parent = this;
+ height += lines[i].height;
+ }
+ this.height = height;
+ }
+ LeafChunk.prototype = {
+ chunkSize: function() { return this.lines.length; },
+ remove: function(at, n, callbacks) {
+ for (var i = at, e = at + n; i < e; ++i) {
+ var line = this.lines[i];
+ this.height -= line.height;
+ line.cleanUp();
+ if (line.handlers)
+ for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
+ }
+ this.lines.splice(at, n);
+ },
+ collapse: function(lines) {
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
+ },
+ insertHeight: function(at, lines, height) {
+ this.height += height;
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
+ },
+ iterN: function(at, n, op) {
+ for (var e = at + n; at < e; ++at)
+ if (op(this.lines[at])) return true;
+ }
+ };
+ function BranchChunk(children) {
+ this.children = children;
+ var size = 0, height = 0;
+ for (var i = 0, e = children.length; i < e; ++i) {
+ var ch = children[i];
+ size += ch.chunkSize(); height += ch.height;
+ ch.parent = this;
+ }
+ this.size = size;
+ this.height = height;
+ this.parent = null;
+ }
+ BranchChunk.prototype = {
+ chunkSize: function() { return this.size; },
+ remove: function(at, n, callbacks) {
+ this.size -= n;
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
+ child.remove(at, rm, callbacks);
+ this.height -= oldHeight - child.height;
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
+ if ((n -= rm) == 0) break;
+ at = 0;
+ } else at -= sz;
+ }
+ if (this.size - n < 25) {
+ var lines = [];
+ this.collapse(lines);
+ this.children = [new LeafChunk(lines)];
+ this.children[0].parent = this;
+ }
+ },
+ collapse: function(lines) {
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
+ },
+ insert: function(at, lines) {
+ var height = 0;
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
+ this.insertHeight(at, lines, height);
+ },
+ insertHeight: function(at, lines, height) {
+ this.size += lines.length;
+ this.height += height;
+ for (var i = 0, e = this.children.length; i < e; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at <= sz) {
+ child.insertHeight(at, lines, height);
+ if (child.lines && child.lines.length > 50) {
+ while (child.lines.length > 50) {
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
+ var newleaf = new LeafChunk(spilled);
+ child.height -= newleaf.height;
+ this.children.splice(i + 1, 0, newleaf);
+ newleaf.parent = this;
+ }
+ this.maybeSpill();
+ }
+ break;
+ }
+ at -= sz;
+ }
+ },
+ maybeSpill: function() {
+ if (this.children.length <= 10) return;
+ var me = this;
+ do {
+ var spilled = me.children.splice(me.children.length - 5, 5);
+ var sibling = new BranchChunk(spilled);
+ if (!me.parent) { // Become the parent node
+ var copy = new BranchChunk(me.children);
+ copy.parent = me;
+ me.children = [copy, sibling];
+ me = copy;
+ } else {
+ me.size -= sibling.size;
+ me.height -= sibling.height;
+ var myIndex = indexOf(me.parent.children, me);
+ me.parent.children.splice(myIndex + 1, 0, sibling);
+ }
+ sibling.parent = me.parent;
+ } while (me.children.length > 10);
+ me.parent.maybeSpill();
+ },
+ iter: function(from, to, op) { this.iterN(from, to - from, op); },
+ iterN: function(at, n, op) {
+ for (var i = 0, e = this.children.length; i < e; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var used = Math.min(n, sz - at);
+ if (child.iterN(at, used, op)) return true;
+ if ((n -= used) == 0) break;
+ at = 0;
+ } else at -= sz;
+ }
+ }
+ };
+
+ function getLineAt(chunk, n) {
+ while (!chunk.lines) {
+ for (var i = 0;; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; break; }
+ n -= sz;
+ }
+ }
+ return chunk.lines[n];
+ }
+ function lineNo(line) {
+ if (line.parent == null) return null;
+ var cur = line.parent, no = indexOf(cur.lines, line);
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+ for (var i = 0, e = chunk.children.length; ; ++i) {
+ if (chunk.children[i] == cur) break;
+ no += chunk.children[i].chunkSize();
+ }
+ }
+ return no;
+ }
+ function lineAtHeight(chunk, h) {
+ var n = 0;
+ outer: do {
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
+ var child = chunk.children[i], ch = child.height;
+ if (h < ch) { chunk = child; continue outer; }
+ h -= ch;
+ n += child.chunkSize();
+ }
+ return n;
+ } while (!chunk.lines);
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
+ var line = chunk.lines[i], lh = line.height;
+ if (h < lh) break;
+ h -= lh;
+ }
+ return n + i;
+ }
+ function heightAtLine(chunk, n) {
+ var h = 0;
+ outer: do {
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; continue outer; }
+ n -= sz;
+ h += child.height;
+ }
+ return h;
+ } while (!chunk.lines);
+ for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
+ return h;
+ }
+
// The history object 'chunks' changes that are made close together
// and at almost the same time into bigger undoable units.
function History() {
this.time = 0;
this.done = []; this.undone = [];
+ this.compound = 0;
+ this.closed = false;
}
History.prototype = {
addChange: function(start, added, old) {
this.undone.length = 0;
- var time = +new Date, last = this.done[this.done.length - 1];
- if (time - this.time > 400 || !last ||
- last.start > start + added || last.start + last.added < start - last.added + last.old.length)
- this.done.push({start: start, added: added, old: old});
- else {
- var oldoff = 0;
- if (start < last.start) {
- for (var i = last.start - start - 1; i >= 0; --i)
- last.old.unshift(old[i]);
- last.added += last.start - start;
- last.start = start;
- }
- else if (last.start < start) {
- oldoff = start - last.start;
- added += oldoff;
- }
- for (var i = last.added - oldoff, e = old.length; i < e; ++i)
- last.old.push(old[i]);
- if (last.added < added) last.added = added;
+ var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
+ var dtime = time - this.time;
+
+ if (this.compound && cur && !this.closed) {
+ cur.push({start: start, added: added, old: old});
+ } else if (dtime > 400 || !last || this.closed ||
+ last.start > start + old.length || last.start + last.added < start) {
+ this.done.push([{start: start, added: added, old: old}]);
+ this.closed = false;
+ } else {
+ var startBefore = Math.max(0, last.start - start),
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
+ if (startBefore) last.start = start;
+ last.added += added - (old.length - startBefore - endAfter);
}
this.time = time;
+ },
+ startCompound: function() {
+ if (!this.compound++) this.closed = true;
+ },
+ endCompound: function() {
+ if (!--this.compound) this.closed = true;
}
};
@@ -2107,6 +2937,10 @@ var CodeMirror = (function() {
else e.cancelBubble = true;
}
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
+ CodeMirror.e_stop = e_stop;
+ CodeMirror.e_preventDefault = e_preventDefault;
+ CodeMirror.e_stopPropagation = e_stopPropagation;
+
function e_target(e) {return e.target || e.srcElement;}
function e_button(e) {
if (e.which) return e.which;
@@ -2115,60 +2949,76 @@ var CodeMirror = (function() {
else if (e.button & 4) return 2;
}
+ // Allow 3rd-party code to override event properties by adding an override
+ // object to an event object.
+ function e_prop(e, prop) {
+ var overridden = e.override && e.override.hasOwnProperty(prop);
+ return overridden ? e.override[prop] : e[prop];
+ }
+
// Event handler registration. If disconnect is true, it'll return a
// function that unregisters the handler.
function connect(node, type, handler, disconnect) {
- function wrapHandler(event) {handler(event || window.event);}
if (typeof node.addEventListener == "function") {
- node.addEventListener(type, wrapHandler, false);
- if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
- }
- else {
+ node.addEventListener(type, handler, false);
+ if (disconnect) return function() {node.removeEventListener(type, handler, false);};
+ } else {
+ var wrapHandler = function(event) {handler(event || window.event);};
node.attachEvent("on" + type, wrapHandler);
if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
}
}
+ CodeMirror.connect = connect;
function Delayed() {this.id = null;}
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
- // Some IE versions don't preserve whitespace when setting the
- // innerHTML of a PRE tag.
- var badInnerHTML = (function() {
- var pre = document.createElement("pre");
- pre.innerHTML = " "; return !pre.innerHTML;
- })();
-
- // Detect drag-and-drop
- var dragAndDrop = (function() {
- // IE8 has ondragstart and ondrop properties, but doesn't seem to
- // actually support ondragstart the way it's supposed to work.
- if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
- var div = document.createElement('div');
- return "ondragstart" in div && "ondrop" in div;
- })();
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
var ie = /MSIE \d/.test(navigator.userAgent);
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
+ var quirksMode = ie && document.documentMode == 5;
+ var webkit = /WebKit\//.test(navigator.userAgent);
+ var chrome = /Chrome\//.test(navigator.userAgent);
+ var opera = /Opera\//.test(navigator.userAgent);
var safari = /Apple Computer/.test(navigator.vendor);
+ var khtml = /KHTML\//.test(navigator.userAgent);
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
+
+ // Detect drag-and-drop
+ var dragAndDrop = function() {
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
+ // couldn't get it to work yet.
+ if (ie_lt9) return false;
+ var div = document.createElement('div');
+ return "draggable" in div || "dragDrop" in div;
+ }();
- var lineSep = "\n";
// Feature-detect whether newlines in textareas are converted to \r\n
- (function () {
+ var lineSep = function () {
var te = document.createElement("textarea");
te.value = "foo\nbar";
- if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
- }());
-
- var tabSize = 8;
- var mac = /Mac/.test(navigator.platform);
- var movementKeys = {};
- for (var i = 35; i <= 40; ++i)
- movementKeys[i] = movementKeys["c" + i] = true;
+ if (te.value.indexOf("\r") > -1) return "\r\n";
+ return "\n";
+ }();
+
+ // For a reason I have yet to figure out, some browsers disallow
+ // word wrapping between certain characters *only* if a new inline
+ // element is started between them. This makes it hard to reliably
+ // measure the position of things, since that requires inserting an
+ // extra span. This terribly fragile set of regexps matches the
+ // character combinations that suffer from this phenomenon on the
+ // various browsers.
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
+ if (gecko) spanAffectsWrapping = /$'/;
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
// Counts the column offset in a string, taking tabs into account.
// Used mostly to find indentation.
- function countColumn(string, end) {
+ function countColumn(string, end, tabSize) {
if (end == null) {
end = string.search(/[^\s\u00a0]/);
if (end == -1) end = string.length;
@@ -2184,25 +3034,54 @@ var CodeMirror = (function() {
if (elt.currentStyle) return elt.currentStyle;
return window.getComputedStyle(elt, null);
}
+
// Find the position of an element by following the offsetParent chain.
// If screen==true, it returns screen (rather than page) coordinates.
function eltOffset(node, screen) {
- var doc = node.ownerDocument.body;
- var x = 0, y = 0, skipDoc = false;
+ var bod = node.ownerDocument.body;
+ var x = 0, y = 0, skipBody = false;
for (var n = node; n; n = n.offsetParent) {
- x += n.offsetLeft; y += n.offsetTop;
+ var ol = n.offsetLeft, ot = n.offsetTop;
+ // Firefox reports weird inverted offsets when the body has a border.
+ if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
+ else { x += ol, y += ot; }
if (screen && computedStyle(n).position == "fixed")
- skipDoc = true;
+ skipBody = true;
}
- var e = screen && !skipDoc ? null : doc;
+ var e = screen && !skipBody ? null : bod;
for (var n = node.parentNode; n != e; n = n.parentNode)
if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
return {left: x, top: y};
}
+ // Use the faster and saner getBoundingClientRect method when possible.
+ if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
+ // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
+ // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
+ try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
+ catch(e) { box = {top: 0, left: 0}; }
+ if (!screen) {
+ // Get the toplevel scroll, working around browser differences.
+ if (window.pageYOffset == null) {
+ var t = document.documentElement || document.body.parentNode;
+ if (t.scrollTop == null) t = document.body;
+ box.top += t.scrollTop; box.left += t.scrollLeft;
+ } else {
+ box.top += window.pageYOffset; box.left += window.pageXOffset;
+ }
+ }
+ return box;
+ };
+
// Get a node's text content.
function eltText(node) {
return node.textContent || node.innerText || node.nodeValue || "";
}
+ function selectInput(node) {
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
+ node.selectionStart = 0;
+ node.selectionEnd = node.value.length;
+ } else node.select();
+ }
// Operations on {line, ch} objects.
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
@@ -2211,21 +3090,30 @@ var CodeMirror = (function() {
var escapeElement = document.createElement("pre");
function htmlEscape(str) {
- if (badTextContent) {
+ escapeElement.textContent = str;
+ return escapeElement.innerHTML;
+ }
+ // Recent (late 2011) Opera betas insert bogus newlines at the start
+ // of the textContent, so we strip those.
+ if (htmlEscape("a") == "\na") {
+ htmlEscape = function(str) {
+ escapeElement.textContent = str;
+ return escapeElement.innerHTML.slice(1);
+ };
+ // Some IEs don't preserve tabs through innerHTML
+ } else if (htmlEscape("\t") != "\t") {
+ htmlEscape = function(str) {
escapeElement.innerHTML = "";
escapeElement.appendChild(document.createTextNode(str));
- } else {
- escapeElement.textContent = str;
- }
- return escapeElement.innerHTML;
+ return escapeElement.innerHTML;
+ };
}
- var badTextContent = htmlEscape("\t") != "\t";
CodeMirror.htmlEscape = htmlEscape;
// Used to position the cursor after an undo/redo by finding the
// last edited character.
function editEnd(from, to) {
- if (!to) return from ? from.length : 0;
+ if (!to) return 0;
if (!from) return to.length;
for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
if (from.charAt(i) != to.charAt(j)) break;
@@ -2238,95 +3126,54 @@ var CodeMirror = (function() {
if (collection[i] == elt) return i;
return -1;
}
+ function isWordChar(ch) {
+ return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
+ }
// See if "".split is the broken IE version, if so, provide an
// alternative way to split lines.
- var splitLines, selRange, setSelRange;
- if ("\n\nb".split(/\n/).length != 3)
- splitLines = function(string) {
- var pos = 0, nl, result = [];
- while ((nl = string.indexOf("\n", pos)) > -1) {
- result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
- pos = nl + 1;
- }
- result.push(string.slice(pos));
- return result;
- };
- else
- splitLines = function(string){return string.split(/\r?\n/);};
+ var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
+ var pos = 0, nl, result = [];
+ while ((nl = string.indexOf("\n", pos)) > -1) {
+ result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
+ pos = nl + 1;
+ }
+ result.push(string.slice(pos));
+ return result;
+ } : function(string){return string.split(/\r?\n/);};
CodeMirror.splitLines = splitLines;
- // Sane model of finding and setting the selection in a textarea
- if (window.getSelection) {
- selRange = function(te) {
- try {return {start: te.selectionStart, end: te.selectionEnd};}
- catch(e) {return null;}
- };
- if (safari)
- // On Safari, selection set with setSelectionRange are in a sort
- // of limbo wrt their anchor. If you press shift-left in them,
- // the anchor is put at the end, and the selection expanded to
- // the left. If you press shift-right, the anchor ends up at the
- // front. This is not what CodeMirror wants, so it does a
- // spurious modify() call to get out of limbo.
- setSelRange = function(te, start, end) {
- if (start == end)
- te.setSelectionRange(start, end);
- else {
- te.setSelectionRange(start, end - 1);
- window.getSelection().modify("extend", "forward", "character");
- }
- };
- else
- setSelRange = function(te, start, end) {
- try {te.setSelectionRange(start, end);}
- catch(e) {} // Fails on Firefox when textarea isn't part of the document
- };
- }
- // IE model. Don't ask.
- else {
- selRange = function(te) {
- try {var range = te.ownerDocument.selection.createRange();}
- catch(e) {return null;}
- if (!range || range.parentElement() != te) return null;
- var val = te.value, len = val.length, localRange = te.createTextRange();
- localRange.moveToBookmark(range.getBookmark());
- var endRange = te.createTextRange();
- endRange.collapse(false);
-
- if (localRange.compareEndPoints("StartToEnd", endRange) > -1)
- return {start: len, end: len};
-
- var start = -localRange.moveStart("character", -len);
- for (var i = val.indexOf("\r"); i > -1 && i < start; i = val.indexOf("\r", i+1), start++) {}
-
- if (localRange.compareEndPoints("EndToEnd", endRange) > -1)
- return {start: start, end: len};
-
- var end = -localRange.moveEnd("character", -len);
- for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
- return {start: start, end: end};
- };
- setSelRange = function(te, start, end) {
- var range = te.createTextRange();
- range.collapse(true);
- var endrange = range.duplicate();
- var newlines = 0, txt = te.value;
- for (var pos = txt.indexOf("\n"); pos > -1 && pos < start; pos = txt.indexOf("\n", pos + 1))
- ++newlines;
- range.move("character", start - newlines);
- for (; pos > -1 && pos < end; pos = txt.indexOf("\n", pos + 1))
- ++newlines;
- endrange.move("character", end - newlines);
- range.setEndPoint("EndToEnd", endrange);
- range.select();
- };
- }
+ var hasSelection = window.getSelection ? function(te) {
+ try { return te.selectionStart != te.selectionEnd; }
+ catch(e) { return false; }
+ } : function(te) {
+ try {var range = te.ownerDocument.selection.createRange();}
+ catch(e) {}
+ if (!range || range.parentElement() != te) return false;
+ return range.compareEndPoints("StartToEnd", range) != 0;
+ };
CodeMirror.defineMode("null", function() {
return {token: function(stream) {stream.skipToEnd();}};
});
CodeMirror.defineMIME("text/plain", "null");
+ var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
+ CodeMirror.keyNames = keyNames;
+ (function() {
+ // Number keys
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
+ // Alphabetic keys
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
+ // Function keys
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
+ })();
+
return CodeMirror;
})();
diff --git a/rhodecode/public/js/native.history.js b/rhodecode/public/js/native.history.js
new file mode 100644
index 00000000..31dbe500
--- /dev/null
+++ b/rhodecode/public/js/native.history.js
@@ -0,0 +1 @@
+(function(a,b){"use strict";var c=a.History=a.History||{};if(typeof c.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");c.Adapter={handlers:{},_uid:1,uid:function(a){return a._uid||(a._uid=c.Adapter._uid++)},bind:function(a,b,d){var e=c.Adapter.uid(a);c.Adapter.handlers[e]=c.Adapter.handlers[e]||{},c.Adapter.handlers[e][b]=c.Adapter.handlers[e][b]||[],c.Adapter.handlers[e][b].push(d),a["on"+b]=function(a,b){return function(d){c.Adapter.trigger(a,b,d)}}(a,b)},trigger:function(a,b,d){d=d||{};var e=c.Adapter.uid(a),f,g;c.Adapter.handlers[e]=c.Adapter.handlers[e]||{},c.Adapter.handlers[e][b]=c.Adapter.handlers[e][b]||[];for(f=0,g=c.Adapter.handlers[e][b].length;f<g;++f)c.Adapter.handlers[e][b][f].apply(this,[d])},extractEventData:function(a,c){var d=c&&c[a]||b;return d},onDomLoad:function(b){var c=a.setTimeout(function(){b()},2e3);a.onload=function(){clearTimeout(c),b()}}},typeof c.init!="undefined"&&c.init()})(window),function(a,b){"use strict";var c=a.console||b,d=a.document,e=a.navigator,f=a.sessionStorage||!1,g=a.setTimeout,h=a.clearTimeout,i=a.setInterval,j=a.clearInterval,k=a.JSON,l=a.alert,m=a.History=a.History||{},n=a.history;k.stringify=k.stringify||k.encode,k.parse=k.parse||k.decode;if(typeof m.init!="undefined")throw new Error("History.js Core has already been loaded...");m.init=function(){return typeof m.Adapter=="undefined"?!1:(typeof m.initCore!="undefined"&&m.initCore(),typeof m.initHtml4!="undefined"&&m.initHtml4(),!0)},m.initCore=function(){if(typeof m.initCore.initialized!="undefined")return!1;m.initCore.initialized=!0,m.options=m.options||{},m.options.hashChangeInterval=m.options.hashChangeInterval||100,m.options.safariPollInterval=m.options.safariPollInterval||500,m.options.doubleCheckInterval=m.options.doubleCheckInterval||500,m.options.storeInterval=m.options.storeInterval||1e3,m.options.busyDelay=m.options.busyDelay||250,m.options.debug=m.options.debug||!1,m.options.initialTitle=m.options.initialTitle||d.title,m.intervalList=[],m.clearAllIntervals=function(){var a,b=m.intervalList;if(typeof b!="undefined"&&b!==null){for(a=0;a<b.length;a++)j(b[a]);m.intervalList=null}},m.debug=function(){(m.options.debug||!1)&&m.log.apply(m,arguments)},m.log=function(){var a=typeof c!="undefined"&&typeof c.log!="undefined"&&typeof c.log.apply!="undefined",b=d.getElementById("log"),e,f,g,h,i;a?(h=Array.prototype.slice.call(arguments),e=h.shift(),typeof c.debug!="undefined"?c.debug.apply(c,[e,h]):c.log.apply(c,[e,h])):e="\n"+arguments[0]+"\n";for(f=1,g=arguments.length;f<g;++f){i=arguments[f];if(typeof i=="object"&&typeof k!="undefined")try{i=k.stringify(i)}catch(j){}e+="\n"+i+"\n"}return b?(b.value+=e+"\n-----\n",b.scrollTop=b.scrollHeight-b.clientHeight):a||l(e),!0},m.getInternetExplorerMajorVersion=function(){var a=m.getInternetExplorerMajorVersion.cached=typeof m.getInternetExplorerMajorVersion.cached!="undefined"?m.getInternetExplorerMajorVersion.cached:function(){var a=3,b=d.createElement("div"),c=b.getElementsByTagName("i");while((b.innerHTML="<!--[if gt IE "+ ++a+"]><i></i><![endif]-->")&&c[0]);return a>4?a:!1}();return a},m.isInternetExplorer=function(){var a=m.isInternetExplorer.cached=typeof m.isInternetExplorer.cached!="undefined"?m.isInternetExplorer.cached:Boolean(m.getInternetExplorerMajorVersion());return a},m.emulated={pushState:!Boolean(a.history&&a.history.pushState&&a.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(e.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(e.userAgent)),hashChange:Boolean(!("onhashchange"in a||"onhashchange"in d)||m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8)},m.enabled=!m.emulated.pushState,m.bugs={setHash:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),safariPoll:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),ieDoubleCheck:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<7)},m.isEmptyObject=function(a){for(var b in a)return!1;return!0},m.cloneObject=function(a){var b,c;return a?(b=k.stringify(a),c=k.parse(b)):c={},c},m.getRootUrl=function(){var a=d.location.protocol+"//"+(d.location.hostname||d.location.host);if(d.location.port||!1)a+=":"+d.location.port;return a+="/",a},m.getBaseHref=function(){var a=d.getElementsByTagName("base"),b=null,c="";return a.length===1&&(b=a[0],c=b.href.replace(/[^\/]+$/,"")),c=c.replace(/\/+$/,""),c&&(c+="/"),c},m.getBaseUrl=function(){var a=m.getBaseHref()||m.getBasePageUrl()||m.getRootUrl();return a},m.getPageUrl=function(){var a=m.getState(!1,!1),b=(a||{}).url||d.location.href,c;return c=b.replace(/\/+$/,"").replace(/[^\/]+$/,function(a,b,c){return/\./.test(a)?a:a+"/"}),c},m.getBasePageUrl=function(){var a=d.location.href.replace(/[#\?].*/,"").replace(/[^\/]+$/,function(a,b,c){return/[^\/]$/.test(a)?"":a}).replace(/\/+$/,"")+"/";return a},m.getFullUrl=function(a,b){var c=a,d=a.substring(0,1);return b=typeof b=="undefined"?!0:b,/[a-z]+\:\/\//.test(a)||(d==="/"?c=m.getRootUrl()+a.replace(/^\/+/,""):d==="#"?c=m.getPageUrl().replace(/#.*/,"")+a:d==="?"?c=m.getPageUrl().replace(/[\?#].*/,"")+a:b?c=m.getBaseUrl()+a.replace(/^(\.\/)+/,""):c=m.getBasePageUrl()+a.replace(/^(\.\/)+/,"")),c.replace(/\#$/,"")},m.getShortUrl=function(a){var b=a,c=m.getBaseUrl(),d=m.getRootUrl();return m.emulated.pushState&&(b=b.replace(c,"")),b=b.replace(d,"/"),m.isTraditionalAnchor(b)&&(b="./"+b),b=b.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),b},m.store={},m.idToState=m.idToState||{},m.stateToId=m.stateToId||{},m.urlToId=m.urlToId||{},m.storedStates=m.storedStates||[],m.savedStates=m.savedStates||[],m.normalizeStore=function(){m.store.idToState=m.store.idToState||{},m.store.urlToId=m.store.urlToId||{},m.store.stateToId=m.store.stateToId||{}},m.getState=function(a,b){typeof a=="undefined"&&(a=!0),typeof b=="undefined"&&(b=!0);var c=m.getLastSavedState();return!c&&b&&(c=m.createStateObject()),a&&(c=m.cloneObject(c),c.url=c.cleanUrl||c.url),c},m.getIdByState=function(a){var b=m.extractId(a.url),c;if(!b){c=m.getStateString(a);if(typeof m.stateToId[c]!="undefined")b=m.stateToId[c];else if(typeof m.store.stateToId[c]!="undefined")b=m.store.stateToId[c];else{for(;;){b=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof m.idToState[b]=="undefined"&&typeof m.store.idToState[b]=="undefined")break}m.stateToId[c]=b,m.idToState[b]=a}}return b},m.normalizeState=function(a){var b,c;if(!a||typeof a!="object")a={};if(typeof a.normalized!="undefined")return a;if(!a.data||typeof a.data!="object")a.data={};b={},b.normalized=!0,b.title=a.title||"",b.url=m.getFullUrl(m.unescapeString(a.url||d.location.href)),b.hash=m.getShortUrl(b.url),b.data=m.cloneObject(a.data),b.id=m.getIdByState(b),b.cleanUrl=b.url.replace(/\??\&_suid.*/,""),b.url=b.cleanUrl,c=!m.isEmptyObject(b.data);if(b.title||c)b.hash=m.getShortUrl(b.url).replace(/\??\&_suid.*/,""),/\?/.test(b.hash)||(b.hash+="?"),b.hash+="&_suid="+b.id;return b.hashedUrl=m.getFullUrl(b.hash),(m.emulated.pushState||m.bugs.safariPoll)&&m.hasUrlDuplicate(b)&&(b.url=b.hashedUrl),b},m.createStateObject=function(a,b,c){var d={data:a,title:b,url:c};return d=m.normalizeState(d),d},m.getStateById=function(a){a=String(a);var c=m.idToState[a]||m.store.idToState[a]||b;return c},m.getStateString=function(a){var b,c,d;return b=m.normalizeState(a),c={data:b.data,title:a.title,url:a.url},d=k.stringify(c),d},m.getStateId=function(a){var b,c;return b=m.normalizeState(a),c=b.id,c},m.getHashByState=function(a){var b,c;return b=m.normalizeState(a),c=b.hash,c},m.extractId=function(a){var b,c,d;return c=/(.*)\&_suid=([0-9]+)$/.exec(a),d=c?c[1]||a:a,b=c?String(c[2]||""):"",b||!1},m.isTraditionalAnchor=function(a){var b=!/[\/\?\.]/.test(a);return b},m.extractState=function(a,b){var c=null,d,e;return b=b||!1,d=m.extractId(a),d&&(c=m.getStateById(d)),c||(e=m.getFullUrl(a),d=m.getIdByUrl(e)||!1,d&&(c=m.getStateById(d)),!c&&b&&!m.isTraditionalAnchor(a)&&(c=m.createStateObject(null,null,e))),c},m.getIdByUrl=function(a){var c=m.urlToId[a]||m.store.urlToId[a]||b;return c},m.getLastSavedState=function(){return m.savedStates[m.savedStates.length-1]||b},m.getLastStoredState=function(){return m.storedStates[m.storedStates.length-1]||b},m.hasUrlDuplicate=function(a){var b=!1,c;return c=m.extractState(a.url),b=c&&c.id!==a.id,b},m.storeState=function(a){return m.urlToId[a.url]=a.id,m.storedStates.push(m.cloneObject(a)),a},m.isLastSavedState=function(a){var b=!1,c,d,e;return m.savedStates.length&&(c=a.id,d=m.getLastSavedState(),e=d.id,b=c===e),b},m.saveState=function(a){return m.isLastSavedState(a)?!1:(m.savedStates.push(m.cloneObject(a)),!0)},m.getStateByIndex=function(a){var b=null;return typeof a=="undefined"?b=m.savedStates[m.savedStates.length-1]:a<0?b=m.savedStates[m.savedStates.length+a]:b=m.savedStates[a],b},m.getHash=function(){var a=m.unescapeHash(d.location.hash);return a},m.unescapeString=function(b){var c=b,d;for(;;){d=a.decodeURI(c);if(d===c)break;c=d}return c},m.unescapeHash=function(a){var b=m.normalizeHash(a);return b=m.unescapeString(b),b},m.normalizeHash=function(a){var b=a.replace(/[^#]*#/,"").replace(/#.*/,"");return b},m.setHash=function(a,b){var c,e,f;return b!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.setHash,args:arguments,queue:b}),!1):(c=m.escapeHash(a),m.busy(!0),e=m.extractState(a,!0),e&&!m.emulated.pushState?m.pushState(e.data,e.title,e.url,!1):d.location.hash!==c&&(m.bugs.setHash?(f=m.getPageUrl(),m.pushState(null,null,f+"#"+c,!1)):d.location.hash=c),m)},m.escapeHash=function(b){var c=m.normalizeHash(b);return c=a.escape(c),m.bugs.hashEscape||(c=c.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),c},m.getHashByUrl=function(a){var b=String(a).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return b=m.unescapeHash(b),b},m.setTitle=function(a){var b=a.title,c;b||(c=m.getStateByIndex(0),c&&c.url===a.url&&(b=c.title||m.options.initialTitle));try{d.getElementsByTagName("title")[0].innerHTML=b.replace("<","&lt;").replace(">","&gt;").replace(" & "," &amp; ")}catch(e){}return d.title=b,m},m.queues=[],m.busy=function(a){typeof a!="undefined"?m.busy.flag=a:typeof m.busy.flag=="undefined"&&(m.busy.flag=!1);if(!m.busy.flag){h(m.busy.timeout);var b=function(){var a,c,d;if(m.busy.flag)return;for(a=m.queues.length-1;a>=0;--a){c=m.queues[a];if(c.length===0)continue;d=c.shift(),m.fireQueueItem(d),m.busy.timeout=g(b,m.options.busyDelay)}};m.busy.timeout=g(b,m.options.busyDelay)}return m.busy.flag},m.busy.flag=!1,m.fireQueueItem=function(a){return a.callback.apply(a.scope||m,a.args||[])},m.pushQueue=function(a){return m.queues[a.queue||0]=m.queues[a.queue||0]||[],m.queues[a.queue||0].push(a),m},m.queue=function(a,b){return typeof a=="function"&&(a={callback:a}),typeof b!="undefined"&&(a.queue=b),m.busy()?m.pushQueue(a):m.fireQueueItem(a),m},m.clearQueue=function(){return m.busy.flag=!1,m.queues=[],m},m.stateChanged=!1,m.doubleChecker=!1,m.doubleCheckComplete=function(){return m.stateChanged=!0,m.doubleCheckClear(),m},m.doubleCheckClear=function(){return m.doubleChecker&&(h(m.doubleChecker),m.doubleChecker=!1),m},m.doubleCheck=function(a){return m.stateChanged=!1,m.doubleCheckClear(),m.bugs.ieDoubleCheck&&(m.doubleChecker=g(function(){return m.doubleCheckClear(),m.stateChanged||a(),!0},m.options.doubleCheckInterval)),m},m.safariStatePoll=function(){var b=m.extractState(d.location.href),c;if(!m.isLastSavedState(b))c=b;else return;return c||(c=m.createStateObject()),m.Adapter.trigger(a,"popstate"),m},m.back=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.back,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.back(!1)}),n.go(-1),!0)},m.forward=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.forward,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.forward(!1)}),n.go(1),!0)},m.go=function(a,b){var c;if(a>0)for(c=1;c<=a;++c)m.forward(b);else{if(!(a<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(c=-1;c>=a;--c)m.back(b)}return m};if(m.emulated.pushState){var o=function(){};m.pushState=m.pushState||o,m.replaceState=m.replaceState||o}else m.onPopState=function(b,c){var e=!1,f=!1,g,h;return m.doubleCheckComplete(),g=m.getHash(),g?(h=m.extractState(g||d.location.href,!0),h?m.replaceState(h.data,h.title,h.url,!1):(m.Adapter.trigger(a,"anchorchange"),m.busy(!1)),m.expectedStateId=!1,!1):(e=m.Adapter.extractEventData("state",b,c)||!1,e?f=m.getStateById(e):m.expectedStateId?f=m.getStateById(m.expectedStateId):f=m.extractState(d.location.href),f||(f=m.createStateObject(null,null,d.location.href)),m.expectedStateId=!1,m.isLastSavedState(f)?(m.busy(!1),!1):(m.storeState(f),m.saveState(f),m.setTitle(f),m.Adapter.trigger(a,"statechange"),m.busy(!1),!0))},m.Adapter.bind(a,"popstate",m.onPopState),m.pushState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.pushState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.pushState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0},m.replaceState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.replaceState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.replaceState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0};if(f){try{m.store=k.parse(f.getItem("History.store"))||{}}catch(p){m.store={}}m.normalizeStore()}else m.store={},m.normalizeStore();m.Adapter.bind(a,"beforeunload",m.clearAllIntervals),m.Adapter.bind(a,"unload",m.clearAllIntervals),m.saveState(m.storeState(m.extractState(d.location.href,!0))),f&&(m.onUnload=function(){var a,b;try{a=k.parse(f.getItem("History.store"))||{}}catch(c){a={}}a.idToState=a.idToState||{},a.urlToId=a.urlToId||{},a.stateToId=a.stateToId||{};for(b in m.idToState){if(!m.idToState.hasOwnProperty(b))continue;a.idToState[b]=m.idToState[b]}for(b in m.urlToId){if(!m.urlToId.hasOwnProperty(b))continue;a.urlToId[b]=m.urlToId[b]}for(b in m.stateToId){if(!m.stateToId.hasOwnProperty(b))continue;a.stateToId[b]=m.stateToId[b]}m.store=a,m.normalizeStore(),f.setItem("History.store",k.stringify(a))},m.intervalList.push(i(m.onUnload,m.options.storeInterval)),m.Adapter.bind(a,"beforeunload",m.onUnload),m.Adapter.bind(a,"unload",m.onUnload));if(!m.emulated.pushState){m.bugs.safariPoll&&m.intervalList.push(i(m.safariStatePoll,m.options.safariPollInterval));if(e.vendor==="Apple Computer, Inc."||(e.appCodeName||"")==="Mozilla")m.Adapter.bind(a,"hashchange",function(){m.Adapter.trigger(a,"popstate")}),m.getHash()&&m.Adapter.onDomLoad(function(){m.Adapter.trigger(a,"hashchange")})}},m.init()}(window) \ No newline at end of file
diff --git a/rhodecode/public/js/rhodecode.js b/rhodecode/public/js/rhodecode.js
index 1496ada6..120d69e3 100644
--- a/rhodecode/public/js/rhodecode.js
+++ b/rhodecode/public/js/rhodecode.js
@@ -44,6 +44,50 @@ String.prototype.format = function() {
}();
+String.prototype.strip = function(char) {
+ if(char === undefined){
+ char = '\\s';
+ }
+ return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
+}
+String.prototype.lstrip = function(char) {
+ if(char === undefined){
+ char = '\\s';
+ }
+ return this.replace(new RegExp('^'+char+'+'),'');
+}
+String.prototype.rstrip = function(char) {
+ if(char === undefined){
+ char = '\\s';
+ }
+ return this.replace(new RegExp(''+char+'+$'),'');
+}
+
+
+if(!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(needle) {
+ for(var i = 0; i < this.length; i++) {
+ if(this[i] === needle) {
+ return i;
+ }
+ }
+ return -1;
+ };
+}
+
+// IE(CRAP) doesn't support previousElementSibling
+var prevElementSibling = function( el ) {
+ if( el.previousElementSibling ) {
+ return el.previousElementSibling;
+ } else {
+ while( el = el.previousSibling ) {
+ if( el.nodeType === 1 ) return el;
+ }
+ }
+}
+
+
+
/**
* SmartColorGenerator
@@ -187,10 +231,10 @@ function ypjax(url,container,s_call,f_call,args){
success:s_wrapper,
failure:function(o){
console.log(o);
- YUD.get(container).innerHTML='ERROR';
+ YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
YUD.setStyle(container,'opacity','1.0');
- YUD.setStyle(container,'color','red');
- }
+ },
+ cache:false
},args);
};
@@ -354,7 +398,7 @@ var createInlineForm = function(parent_tr, f_path, line) {
// create event for hide button
form = new YAHOO.util.Element(form);
- var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
+ var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
form_hide_button.on('click', function(e) {
var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
@@ -378,11 +422,12 @@ var injectInlineForm = function(tr){
return
}
var submit_url = AJAX_COMMENT_URL;
- if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(tr,'no-comment')){
+ var _td = YUD.getElementsByClassName('code',null,tr)[0];
+ if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
return
}
YUD.addClass(tr,'form-open');
- var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
+ var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
var f_path = YUD.getAttribute(node,'path');
var lineno = getLineNo(tr);
var form = createInlineForm(tr, f_path, lineno, submit_url);
@@ -400,11 +445,10 @@ var injectInlineForm = function(tr){
}
YUD.insertAfter(form,parent);
- YUD.get('text_'+lineno).focus();
var f = YUD.get(form);
- var overlay = f.getElementsByClassName('overlay')[0];
- var _form = f.getElementsByClassName('inline-form')[0];
+ var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
+ var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
form.on('submit',function(e){
YUE.preventDefault(e);
@@ -448,7 +492,16 @@ var injectInlineForm = function(tr){
ajaxPOST(submit_url, postData, success);
});
- tooltip_activate();
+ setTimeout(function(){
+ // callbacks
+ tooltip_activate();
+ MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
+ _USERS_AC_DATA, _GROUPS_AC_DATA);
+ var _e = YUD.get('text_'+lineno);
+ if(_e){
+ _e.focus();
+ }
+ },10)
};
var deleteComment = function(comment_id){
@@ -456,7 +509,7 @@ var deleteComment = function(comment_id){
var postData = {'_method':'delete'};
var success = function(o){
var n = YUD.get('comment-tr-'+comment_id);
- var root = n.previousElementSibling.previousElementSibling;
+ var root = prevElementSibling(prevElementSibling(n));
n.parentNode.removeChild(n);
// scann nodes, and attach add button to last one
@@ -465,6 +518,15 @@ var deleteComment = function(comment_id){
ajaxPOST(url,postData,success);
}
+var updateReviewers = function(reviewers_ids){
+ var url = AJAX_UPDATE_PULLREQUEST;
+ var postData = {'_method':'put',
+ 'reviewers_ids': reviewers_ids};
+ var success = function(o){
+ window.location.reload();
+ }
+ ajaxPOST(url,postData,success);
+}
var createInlineAddButton = function(tr){
@@ -506,8 +568,8 @@ var placeAddButton = function(target_tr){
// next element are comments !
if(YUD.hasClass(n,'inline-comments')){
last_node = n;
- //also remove the comment button from previos
- var comment_add_buttons = last_node.getElementsByClassName('add-comment');
+ //also remove the comment button from previous
+ var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
for(var i=0;i<comment_add_buttons.length;i++){
var b = comment_add_buttons[i];
b.parentNode.removeChild(b);
@@ -520,7 +582,7 @@ var placeAddButton = function(target_tr){
var add = createInlineAddButton(target_tr);
// get the comment div
- var comment_block = last_node.getElementsByClassName('comment')[0];
+ var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
// attach add button
YUD.insertAfter(add,comment_block);
}
@@ -592,9 +654,15 @@ var renderInlineComments = function(file_comments){
}
}
+var removeReviewer = function(reviewer_id){
+ var el = YUD.get('reviewer_{0}'.format(reviewer_id));
+ if (el.parentNode !== undefined){
+ el.parentNode.removeChild(el);
+ }
+}
-var fileBrowserListeners = function(current_url, node_list_url, url_base,
- truncated_lbl, nomatch_lbl){
+var fileBrowserListeners = function(current_url, node_list_url, url_base){
+
var current_url_branch = +"?branch=__BRANCH__";
var url = url_base;
var node_url = node_list_url;
@@ -623,7 +691,7 @@ var fileBrowserListeners = function(current_url, node_list_url, url_base,
YUC.initHeader('X-PARTIAL-XHR',true);
YUC.asyncRequest('GET',url,{
success:function(o){
- nodes = JSON.parse(o.responseText);
+ nodes = JSON.parse(o.responseText).nodes;
YUD.setStyle('node_filter_box_loading','display','none');
YUD.setStyle('node_filter_box','display','');
n_filter.focus();
@@ -663,13 +731,13 @@ var fileBrowserListeners = function(current_url, node_list_url, url_base,
var t = nodes[i].type;
var n_hl = n.substring(0,pos)
+"<b>{0}</b>".format(n.substring(pos,pos+query.length))
- +n.substring(pos+query.length)
- match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,node_url.replace('__FPATH__',n),n_hl));
+ +n.substring(pos+query.length)
+ node_url = node_url.replace('__FPATH__',n);
+ match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,node_url,n_hl));
}
if(match.length >= matches_max){
- match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(truncated_lbl));
+ match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['search truncated']));
}
-
}
}
if(query != ""){
@@ -677,7 +745,7 @@ var fileBrowserListeners = function(current_url, node_list_url, url_base,
YUD.setStyle('tbody_filtered','display','');
if (match.length==0){
- match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(nomatch_lbl));
+ match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['no matching files']));
}
YUD.get('tbody_filtered').innerHTML = match.join("");
@@ -816,10 +884,31 @@ var deleteNotification = function(url, notification_id,callbacks){
callback, postData);
};
+var readNotification = function(url, notification_id,callbacks){
+ var callback = {
+ success:function(o){
+ var obj = YUD.get(String("notification_"+notification_id));
+ YUD.removeClass(obj, 'unread');
+ var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
+
+ if(r_button.parentNode !== undefined){
+ r_button.parentNode.removeChild(r_button);
+ }
+ _run_callbacks(callbacks);
+ },
+ failure:function(o){
+ alert("error");
+ },
+ };
+ var postData = '_method=put';
+ var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
+ var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
+ callback, postData);
+};
/** MEMBERS AUTOCOMPLETE WIDGET **/
-var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_lbl) {
+var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
var myUsers = users_list;
var myGroups = groups_list;
@@ -834,9 +923,11 @@ var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_
// Match against each name of each contact
for (; i < l; i++) {
contact = myUsers[i];
- if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
- matches[matches.length] = contact;
- }
+ if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
+ ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
+ ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
+ matches[matches.length] = contact;
+ }
}
return matches;
};
@@ -879,15 +970,20 @@ var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_
};
// Instantiate AutoComplete for perms
- var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
+ var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
membersAC.useShadow = false;
membersAC.resultTypeList = false;
+ membersAC.animVert = false;
+ membersAC.animHoriz = false;
+ membersAC.animSpeed = 0.1;
// Instantiate AutoComplete for owner
var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
ownerAC.useShadow = false;
ownerAC.resultTypeList = false;
-
+ ownerAC.animVert = false;
+ ownerAC.animHoriz = false;
+ ownerAC.animSpeed = 0.1;
// Helper highlight function for the formatter
var highlightMatch = function (full, snippet, matchindex) {
@@ -912,21 +1008,22 @@ var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_
var grname = oResultData.grname;
var grmembers = oResultData.grmembers;
var grnameMatchIndex = grname.toLowerCase().indexOf(query);
- var grprefix = "{0}: ".format(group_lbl);
+ var grprefix = "{0}: ".format(_TM['Group']);
var grsuffix = " (" + grmembers + " )";
- var grsuffix = " ({0} {1})".format(grmembers, members_lbl);
+ var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
if (grnameMatchIndex > -1) {
return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
}
return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
// Users
- } else if (oResultData.fname != undefined) {
- var fname = oResultData.fname,
- lname = oResultData.lname,
- nname = oResultData.nname || "",
- // Guard against null value
- fnameMatchIndex = fname.toLowerCase().indexOf(query),
+ } else if (oResultData.nname != undefined) {
+ var fname = oResultData.fname || "";
+ var lname = oResultData.lname || "";
+ var nname = oResultData.nname;
+
+ // Guard against null value
+ var fnameMatchIndex = fname.toLowerCase().indexOf(query),
lnameMatchIndex = lname.toLowerCase().indexOf(query),
nnameMatchIndex = nname.toLowerCase().indexOf(query),
displayfname, displaylname, displaynname;
@@ -958,7 +1055,7 @@ var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_
ownerAC.formatResult = custom_formatter;
var myHandler = function (sType, aArgs) {
-
+ var nextId = divid.split('perm_new_member_name_')[1];
var myAC = aArgs[0]; // reference back to the AC instance
var elLI = aArgs[1]; // reference to the selected LI element
var oData = aArgs[2]; // object literal of selected item's result data
@@ -966,11 +1063,11 @@ var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_
if (oData.nname != undefined) {
//users
myAC.getInputEl().value = oData.nname;
- YUD.get('perm_new_member_type').value = 'user';
+ YUD.get('perm_new_member_type_'+nextId).value = 'user';
} else {
//groups
myAC.getInputEl().value = oData.grname;
- YUD.get('perm_new_member_type').value = 'users_group';
+ YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
}
};
@@ -988,6 +1085,359 @@ var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_
}
+var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
+ var myUsers = users_list;
+ var myGroups = groups_list;
+
+ // Define a custom search function for the DataSource of users
+ var matchUsers = function (sQuery) {
+ var org_sQuery = sQuery;
+ if(this.mentionQuery == null){
+ return []
+ }
+ sQuery = this.mentionQuery;
+ // Case insensitive matching
+ var query = sQuery.toLowerCase();
+ var i = 0;
+ var l = myUsers.length;
+ var matches = [];
+
+ // Match against each name of each contact
+ for (; i < l; i++) {
+ contact = myUsers[i];
+ if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
+ ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
+ ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
+ matches[matches.length] = contact;
+ }
+ }
+ return matches
+ };
+
+ //match all
+ var matchAll = function (sQuery) {
+ u = matchUsers(sQuery);
+ return u
+ };
+
+ // DataScheme for owner
+ var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
+
+ ownerDS.responseSchema = {
+ fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
+ };
+
+ // Instantiate AutoComplete for mentions
+ var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
+ ownerAC.useShadow = false;
+ ownerAC.resultTypeList = false;
+ ownerAC.suppressInputUpdate = true;
+ ownerAC.animVert = false;
+ ownerAC.animHoriz = false;
+ ownerAC.animSpeed = 0.1;
+
+ // Helper highlight function for the formatter
+ var highlightMatch = function (full, snippet, matchindex) {
+ return full.substring(0, matchindex)
+ + "<span class='match'>"
+ + full.substr(matchindex, snippet.length)
+ + "</span>" + full.substring(matchindex + snippet.length);
+ };
+
+ // Custom formatter to highlight the matching letters
+ ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
+ var org_sQuery = sQuery;
+ if(this.dataSource.mentionQuery != null){
+ sQuery = this.dataSource.mentionQuery;
+ }
+
+ var query = sQuery.toLowerCase();
+ var _gravatar = function(res, em, group){
+ if (group !== undefined){
+ em = '/images/icons/group.png'
+ }
+ tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
+ return tmpl.format(em,res)
+ }
+ if (oResultData.nname != undefined) {
+ var fname = oResultData.fname || "";
+ var lname = oResultData.lname || "";
+ var nname = oResultData.nname;
+
+ // Guard against null value
+ var fnameMatchIndex = fname.toLowerCase().indexOf(query),
+ lnameMatchIndex = lname.toLowerCase().indexOf(query),
+ nnameMatchIndex = nname.toLowerCase().indexOf(query),
+ displayfname, displaylname, displaynname;
+
+ if (fnameMatchIndex > -1) {
+ displayfname = highlightMatch(fname, query, fnameMatchIndex);
+ } else {
+ displayfname = fname;
+ }
+
+ if (lnameMatchIndex > -1) {
+ displaylname = highlightMatch(lname, query, lnameMatchIndex);
+ } else {
+ displaylname = lname;
+ }
+
+ if (nnameMatchIndex > -1) {
+ displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
+ } else {
+ displaynname = nname ? "(" + nname + ")" : "";
+ }
+
+ return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
+ } else {
+ return '';
+ }
+ };
+
+ if(ownerAC.itemSelectEvent){
+ ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
+
+ var myAC = aArgs[0]; // reference back to the AC instance
+ var elLI = aArgs[1]; // reference to the selected LI element
+ var oData = aArgs[2]; // object literal of selected item's result data
+ //fill the autocomplete with value
+ if (oData.nname != undefined) {
+ //users
+ //Replace the mention name with replaced
+ var re = new RegExp();
+ var org = myAC.getInputEl().value;
+ var chunks = myAC.dataSource.chunks
+ // replace middle chunk(the search term) with actuall match
+ chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
+ '@'+oData.nname+' ');
+ myAC.getInputEl().value = chunks.join('')
+ YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
+ } else {
+ //groups
+ myAC.getInputEl().value = oData.grname;
+ YUD.get('perm_new_member_type').value = 'users_group';
+ }
+ });
+ }
+
+ // in this keybuffer we will gather current value of search !
+ // since we need to get this just when someone does `@` then we do the
+ // search
+ ownerAC.dataSource.chunks = [];
+ ownerAC.dataSource.mentionQuery = null;
+
+ ownerAC.get_mention = function(msg, max_pos) {
+ var org = msg;
+ var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
+ var chunks = [];
+
+
+ // cut first chunk until curret pos
+ var to_max = msg.substr(0, max_pos);
+ var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
+ var msg2 = to_max.substr(at_pos);
+
+ chunks.push(org.substr(0,at_pos))// prefix chunk
+ chunks.push(msg2) // search chunk
+ chunks.push(org.substr(max_pos)) // postfix chunk
+
+ // clean up msg2 for filtering and regex match
+ var msg2 = msg2.lstrip(' ').lstrip('\n');
+
+ if(re.test(msg2)){
+ var unam = re.exec(msg2)[1];
+ return [unam, chunks];
+ }
+ return [null, null];
+ };
+
+ if (ownerAC.textboxKeyUpEvent){
+ ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
+
+ var ac_obj = args[0];
+ var currentMessage = args[1];
+ var currentCaretPosition = args[0]._elTextbox.selectionStart;
+
+ var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
+ var curr_search = null;
+ if(unam[0]){
+ curr_search = unam[0];
+ }
+
+ ownerAC.dataSource.chunks = unam[1];
+ ownerAC.dataSource.mentionQuery = curr_search;
+
+ })
+ }
+ return {
+ ownerDS: ownerDS,
+ ownerAC: ownerAC,
+ };
+}
+
+
+var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
+ var myUsers = users_list;
+ var myGroups = groups_list;
+
+ // Define a custom search function for the DataSource of users
+ var matchUsers = function (sQuery) {
+ // Case insensitive matching
+ var query = sQuery.toLowerCase();
+ var i = 0;
+ var l = myUsers.length;
+ var matches = [];
+
+ // Match against each name of each contact
+ for (; i < l; i++) {
+ contact = myUsers[i];
+ if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
+ ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
+ ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
+ matches[matches.length] = contact;
+ }
+ }
+ return matches;
+ };
+
+ // Define a custom search function for the DataSource of usersGroups
+ var matchGroups = function (sQuery) {
+ // Case insensitive matching
+ var query = sQuery.toLowerCase();
+ var i = 0;
+ var l = myGroups.length;
+ var matches = [];
+
+ // Match against each name of each contact
+ for (; i < l; i++) {
+ matched_group = myGroups[i];
+ if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
+ matches[matches.length] = matched_group;
+ }
+ }
+ return matches;
+ };
+
+ //match all
+ var matchAll = function (sQuery) {
+ u = matchUsers(sQuery);
+ return u
+ };
+
+ // DataScheme for owner
+ var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
+
+ ownerDS.responseSchema = {
+ fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
+ };
+
+ // Instantiate AutoComplete for mentions
+ var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
+ reviewerAC.useShadow = false;
+ reviewerAC.resultTypeList = false;
+ reviewerAC.suppressInputUpdate = true;
+ reviewerAC.animVert = false;
+ reviewerAC.animHoriz = false;
+ reviewerAC.animSpeed = 0.1;
+
+ // Helper highlight function for the formatter
+ var highlightMatch = function (full, snippet, matchindex) {
+ return full.substring(0, matchindex)
+ + "<span class='match'>"
+ + full.substr(matchindex, snippet.length)
+ + "</span>" + full.substring(matchindex + snippet.length);
+ };
+
+ // Custom formatter to highlight the matching letters
+ reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
+ var org_sQuery = sQuery;
+ if(this.dataSource.mentionQuery != null){
+ sQuery = this.dataSource.mentionQuery;
+ }
+
+ var query = sQuery.toLowerCase();
+ var _gravatar = function(res, em, group){
+ if (group !== undefined){
+ em = '/images/icons/group.png'
+ }
+ tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
+ return tmpl.format(em,res)
+ }
+ if (oResultData.nname != undefined) {
+ var fname = oResultData.fname || "";
+ var lname = oResultData.lname || "";
+ var nname = oResultData.nname;
+
+ // Guard against null value
+ var fnameMatchIndex = fname.toLowerCase().indexOf(query),
+ lnameMatchIndex = lname.toLowerCase().indexOf(query),
+ nnameMatchIndex = nname.toLowerCase().indexOf(query),
+ displayfname, displaylname, displaynname;
+
+ if (fnameMatchIndex > -1) {
+ displayfname = highlightMatch(fname, query, fnameMatchIndex);
+ } else {
+ displayfname = fname;
+ }
+
+ if (lnameMatchIndex > -1) {
+ displaylname = highlightMatch(lname, query, lnameMatchIndex);
+ } else {
+ displaylname = lname;
+ }
+
+ if (nnameMatchIndex > -1) {
+ displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
+ } else {
+ displaynname = nname ? "(" + nname + ")" : "";
+ }
+
+ return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
+ } else {
+ return '';
+ }
+ };
+
+ //members cache to catch duplicates
+ reviewerAC.dataSource.cache = [];
+ // hack into select event
+ if(reviewerAC.itemSelectEvent){
+ reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
+
+ var myAC = aArgs[0]; // reference back to the AC instance
+ var elLI = aArgs[1]; // reference to the selected LI element
+ var oData = aArgs[2]; // object literal of selected item's result data
+ var members = YUD.get('review_members');
+ //fill the autocomplete with value
+
+ if (oData.nname != undefined) {
+ if (myAC.dataSource.cache.indexOf(oData.id) != -1){
+ return
+ }
+
+ var tmpl = '<li id="reviewer_{2}">'+
+ '<div class="reviewers_member">'+
+ '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
+ '<div style="float:left">{1}</div>'+
+ '<input type="hidden" value="{2}" name="review_members" />'+
+ '<span class="delete_icon action_button" onclick="removeReviewer({2})"></span>'+
+ '</div>'+
+ '</li>'
+
+ var displayname = "{0} {1} ({2})".format(oData.fname,oData.lname,oData.nname);
+ var element = tmpl.format(oData.gravatar_lnk,displayname,oData.id);
+ members.innerHTML += element;
+ myAC.dataSource.cache.push(oData.id);
+ YUD.get('user').value = ''
+ }
+ });
+ }
+ return {
+ ownerDS: ownerDS,
+ reviewerAC: reviewerAC,
+ };
+}
+
/**
* QUICK REPO MENU
@@ -1041,10 +1491,18 @@ var get_group_name = function(node){
return name
}
var get_date = function(node){
- var date_ = node.firstElementChild.innerHTML;
+ var date_ = YUD.getAttribute(node.firstElementChild,'date');
return date_
}
+var get_age = function(node){
+ return node
+}
+
+var get_link = function(node){
+ return node.firstElementChild.text;
+}
+
var revisionSort = function(a, b, desc, field) {
var a_ = fromHTML(a.getData(field));
@@ -1059,8 +1517,21 @@ var revisionSort = function(a, b, desc, field) {
return compState;
};
var ageSort = function(a, b, desc, field) {
- var a_ = a.getData(field);
- var b_ = b.getData(field);
+ var a_ = fromHTML(a.getData(field));
+ var b_ = fromHTML(b.getData(field));
+
+ // extract name from table
+ a_ = get_date(a_)
+ b_ = get_date(b_)
+
+ var comp = YAHOO.util.Sort.compare;
+ var compState = comp(a_, b_, desc);
+ return compState;
+};
+
+var lastLoginSort = function(a, b, desc, field) {
+ var a_ = a.getData('last_login_raw') || 0;
+ var b_ = b.getData('last_login_raw') || 0;
var comp = YAHOO.util.Sort.compare;
var compState = comp(a_, b_, desc);
@@ -1070,7 +1541,7 @@ var ageSort = function(a, b, desc, field) {
var nameSort = function(a, b, desc, field) {
var a_ = fromHTML(a.getData(field));
var b_ = fromHTML(b.getData(field));
-
+
// extract name from table
a_ = get_name(a_)
b_ = get_name(b_)
@@ -1116,4 +1587,173 @@ var dateSort = function(a, b, desc, field) {
var comp = YAHOO.util.Sort.compare;
var compState = comp(a_, b_, desc);
return compState;
-}; \ No newline at end of file
+};
+
+var linkSort = function(a, b, desc, field) {
+ var a_ = fromHTML(a.getData(field));
+ var b_ = fromHTML(a.getData(field));
+
+ // extract url text from string nodes
+ a_ = get_link(a_)
+ b_ = get_link(b_)
+
+ var comp = YAHOO.util.Sort.compare;
+ var compState = comp(a_, b_, desc);
+ return compState;
+}
+
+var addPermAction = function(_html, users_list, groups_list){
+ var elmts = YUD.getElementsByClassName('last_new_member');
+ var last_node = elmts[elmts.length-1];
+ if (last_node){
+ var next_id = (YUD.getElementsByClassName('new_members')).length;
+ _html = _html.format(next_id);
+ last_node.innerHTML = _html;
+ YUD.setStyle(last_node, 'display', '');
+ YUD.removeClass(last_node, 'last_new_member');
+ MembersAutoComplete("perm_new_member_name_"+next_id,
+ "perm_container_"+next_id, users_list, groups_list);
+ //create new last NODE
+ var el = document.createElement('tr');
+ el.id = 'add_perm_input';
+ YUD.addClass(el,'last_new_member');
+ YUD.addClass(el,'new_members');
+ YUD.insertAfter(el, last_node);
+ }
+}
+
+/* Multi selectors */
+
+var MultiSelectWidget = function(selected_id, available_id, form_id){
+
+
+ //definition of containers ID's
+ var selected_container = selected_id;
+ var available_container = available_id;
+
+ //temp container for selected storage.
+ var cache = new Array();
+ var av_cache = new Array();
+ var c = YUD.get(selected_container);
+ var ac = YUD.get(available_container);
+
+ //get only selected options for further fullfilment
+ for(var i = 0;node =c.options[i];i++){
+ if(node.selected){
+ //push selected to my temp storage left overs :)
+ cache.push(node);
+ }
+ }
+
+ //get all available options to cache
+ for(var i = 0;node =ac.options[i];i++){
+ //push selected to my temp storage left overs :)
+ av_cache.push(node);
+ }
+
+ //fill available only with those not in choosen
+ ac.options.length=0;
+ tmp_cache = new Array();
+
+ for(var i = 0;node = av_cache[i];i++){
+ var add = true;
+ for(var i2 = 0;node_2 = cache[i2];i2++){
+ if(node.value == node_2.value){
+ add=false;
+ break;
+ }
+ }
+ if(add){
+ tmp_cache.push(new Option(node.text, node.value, false, false));
+ }
+ }
+
+ for(var i = 0;node = tmp_cache[i];i++){
+ ac.options[i] = node;
+ }
+
+ function prompts_action_callback(e){
+
+ var choosen = YUD.get(selected_container);
+ var available = YUD.get(available_container);
+
+ //get checked and unchecked options from field
+ function get_checked(from_field){
+ //temp container for storage.
+ var sel_cache = new Array();
+ var oth_cache = new Array();
+
+ for(var i = 0;node = from_field.options[i];i++){
+ if(node.selected){
+ //push selected fields :)
+ sel_cache.push(node);
+ }
+ else{
+ oth_cache.push(node)
+ }
+ }
+
+ return [sel_cache,oth_cache]
+ }
+
+ //fill the field with given options
+ function fill_with(field,options){
+ //clear firtst
+ field.options.length=0;
+ for(var i = 0;node = options[i];i++){
+ field.options[i]=new Option(node.text, node.value,
+ false, false);
+ }
+
+ }
+ //adds to current field
+ function add_to(field,options){
+ for(var i = 0;node = options[i];i++){
+ field.appendChild(new Option(node.text, node.value,
+ false, false));
+ }
+ }
+
+ // add action
+ if (this.id=='add_element'){
+ var c = get_checked(available);
+ add_to(choosen,c[0]);
+ fill_with(available,c[1]);
+ }
+ // remove action
+ if (this.id=='remove_element'){
+ var c = get_checked(choosen);
+ add_to(available,c[0]);
+ fill_with(choosen,c[1]);
+ }
+ // add all elements
+ if(this.id=='add_all_elements'){
+ for(var i=0; node = available.options[i];i++){
+ choosen.appendChild(new Option(node.text,
+ node.value, false, false));
+ }
+ available.options.length = 0;
+ }
+ //remove all elements
+ if(this.id=='remove_all_elements'){
+ for(var i=0; node = choosen.options[i];i++){
+ available.appendChild(new Option(node.text,
+ node.value, false, false));
+ }
+ choosen.options.length = 0;
+ }
+
+ }
+
+ YUE.addListener(['add_element','remove_element',
+ 'add_all_elements','remove_all_elements'],'click',
+ prompts_action_callback)
+ if (form_id !== undefined) {
+ YUE.addListener(form_id,'submit',function(){
+ var choosen = YUD.get(selected_container);
+ for (var i = 0; i < choosen.options.length; i++) {
+ choosen.options[i].selected = 'selected';
+ }
+ });
+ }
+}
diff --git a/rhodecode/public/js/yui.2.9.js b/rhodecode/public/js/yui.2.9.js
index 665d067f..1446734b 100644
--- a/rhodecode/public/js/yui.2.9.js
+++ b/rhodecode/public/js/yui.2.9.js
@@ -1,150 +1,877 @@
+if("undefined"==typeof YAHOO||!YAHOO)var YAHOO={};YAHOO.namespace=function(){var a=arguments,c=null,b,d,f;for(b=0;b<a.length;b+=1){f=(""+a[b]).split(".");c=YAHOO;for(d="YAHOO"==f[0]?1:0;d<f.length;d+=1)c[f[d]]=c[f[d]]||{},c=c[f[d]]}return c};YAHOO.log=function(a,c,b){var d=YAHOO.widget.Logger;return d&&d.log?d.log(a,c,b):!1};
+YAHOO.register=function(a,c,b){var d=YAHOO.env.modules,f,g,j;d[a]||(d[a]={versions:[],builds:[]});d=d[a];f=b.version;b=b.build;g=YAHOO.env.listeners;d.name=a;d.version=f;d.build=b;d.versions.push(f);d.builds.push(b);d.mainClass=c;for(j=0;j<g.length;j+=1)g[j](d);c?(c.VERSION=f,c.BUILD=b):YAHOO.log("mainClass is undefined for module "+a,"warn")};YAHOO.env=YAHOO.env||{modules:[],listeners:[]};YAHOO.env.getVersion=function(a){return YAHOO.env.modules[a]||null};
+YAHOO.env.parseUA=function(a){var c=function(a){var b=0;return parseFloat(a.replace(/\./g,function(){return 1==b++?"":"."}))},b=navigator,b={ie:0,opera:0,gecko:0,webkit:0,chrome:0,mobile:null,air:0,ipad:0,iphone:0,ipod:0,ios:null,android:0,webos:0,caja:b&&b.cajaVersion,secure:!1,os:null},a=a||navigator&&navigator.userAgent,d=window&&window.location,d=d&&d.href;b.secure=d&&0===d.toLowerCase().indexOf("https");if(a){/windows|win32/i.test(a)?b.os="windows":/macintosh/i.test(a)?b.os="macintosh":/rhino/i.test(a)&&
+(b.os="rhino");/KHTML/.test(a)&&(b.webkit=1);if((d=a.match(/AppleWebKit\/([^\s]*)/))&&d[1]){b.webkit=c(d[1]);if(/ Mobile\//.test(a)){if(b.mobile="Apple",(d=a.match(/OS ([^\s]*)/))&&d[1]&&(d=c(d[1].replace("_","."))),b.ios=d,b.ipad=b.ipod=b.iphone=0,(d=a.match(/iPad|iPod|iPhone/))&&d[0])b[d[0].toLowerCase()]=b.ios}else{if(d=a.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/))b.mobile=d[0];if(/webOS/.test(a)&&(b.mobile="WebOS",(d=a.match(/webOS\/([^\s]*);/))&&d[1]))b.webos=c(d[1]);if(/ Android/.test(a)&&
+(b.mobile="Android",(d=a.match(/Android ([^\s]*);/))&&d[1]))b.android=c(d[1])}if((d=a.match(/Chrome\/([^\s]*)/))&&d[1])b.chrome=c(d[1]);else if(d=a.match(/AdobeAIR\/([^\s]*)/))b.air=d[0]}if(!b.webkit)if((d=a.match(/Opera[\s\/]([^\s]*)/))&&d[1]){b.opera=c(d[1]);if((d=a.match(/Version\/([^\s]*)/))&&d[1])b.opera=c(d[1]);if(d=a.match(/Opera Mini[^;]*/))b.mobile=d[0]}else if((d=a.match(/MSIE\s([^;]*)/))&&d[1])b.ie=c(d[1]);else if(d=a.match(/Gecko\/([^\s]*)/))if(b.gecko=1,(d=a.match(/rv:([^\s\)]*)/))&&
+d[1])b.gecko=c(d[1])}return b};YAHOO.env.ua=YAHOO.env.parseUA();(function(){YAHOO.namespace("util","widget","example");if("undefined"!==typeof YAHOO_config){var a=YAHOO_config.listener,c=YAHOO.env.listeners,b=!0,d;if(a){for(d=0;d<c.length;d++)if(c[d]==a){b=!1;break}b&&c.push(a)}}})();YAHOO.lang=YAHOO.lang||{};
+(function(){var a=YAHOO.lang,c=Object.prototype,b=[],d={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;","`":"&#x60;"},f=["toString","valueOf"],g={isArray:function(a){return"[object Array]"===c.toString.apply(a)},isBoolean:function(a){return"boolean"===typeof a},isFunction:function(a){return"function"===typeof a||"[object Function]"===c.toString.apply(a)},isNull:function(a){return null===a},isNumber:function(a){return"number"===typeof a&&isFinite(a)},isObject:function(b){return b&&
+("object"===typeof b||a.isFunction(b))||!1},isString:function(a){return"string"===typeof a},isUndefined:function(a){return"undefined"===typeof a},_IEEnumFix:YAHOO.env.ua.ie?function(b,d){var e,i,k;for(e=0;e<f.length;e+=1)i=f[e],k=d[i],a.isFunction(k)&&k!=c[i]&&(b[i]=k)}:function(){},escapeHTML:function(a){return a.replace(/[&<>"'\/`]/g,function(a){return d[a]})},extend:function(b,d,e){if(!d||!b)throw Error("extend failed, please check that all dependencies are included.");var i=function(){},k;i.prototype=
+d.prototype;b.prototype=new i;b.prototype.constructor=b;b.superclass=d.prototype;d.prototype.constructor==c.constructor&&(d.prototype.constructor=d);if(e){for(k in e)a.hasOwnProperty(e,k)&&(b.prototype[k]=e[k]);a._IEEnumFix(b.prototype,e)}},augmentObject:function(b,d){if(!d||!b)throw Error("Absorb failed, verify dependencies.");var e=arguments,i,k=e[2];if(k&&!0!==k)for(i=2;i<e.length;i+=1)b[e[i]]=d[e[i]];else{for(i in d)if(k||!(i in b))b[i]=d[i];a._IEEnumFix(b,d)}return b},augmentProto:function(b,
+d){if(!d||!b)throw Error("Augment failed, verify dependencies.");var e=[b.prototype,d.prototype],i;for(i=2;i<arguments.length;i+=1)e.push(arguments[i]);a.augmentObject.apply(this,e);return b},dump:function(b,d){var e,i,k=[];if(a.isObject(b)){if(b instanceof Date||"nodeType"in b&&"tagName"in b)return b;if(a.isFunction(b))return"f(){...}"}else return b+"";d=a.isNumber(d)?d:3;if(a.isArray(b)){k.push("[");e=0;for(i=b.length;e<i;e+=1)a.isObject(b[e])?k.push(0<d?a.dump(b[e],d-1):"{...}"):k.push(b[e]),k.push(", ");
+1<k.length&&k.pop();k.push("]")}else{k.push("{");for(e in b)a.hasOwnProperty(b,e)&&(k.push(e+" => "),a.isObject(b[e])?k.push(0<d?a.dump(b[e],d-1):"{...}"):k.push(b[e]),k.push(", "));1<k.length&&k.pop();k.push("}")}return k.join("")},substitute:function(b,d,e,i){for(var k,f,c,g,n,o=[],m,r=b.length;;){k=b.lastIndexOf("{",r);if(0>k)break;f=b.indexOf("}",k);if(k+1>f)break;g=m=b.substring(k+1,f);n=null;c=g.indexOf(" ");-1<c&&(n=g.substring(c+1),g=g.substring(0,c));c=d[g];e&&(c=e(g,c,n));a.isObject(c)?
+a.isArray(c)?c=a.dump(c,parseInt(n,10)):(n=n||"",g=n.indexOf("dump"),-1<g&&(n=n.substring(4)),m=c.toString(),c="[object Object]"===m||-1<g?a.dump(c,parseInt(n,10)):m):!a.isString(c)&&!a.isNumber(c)&&(c="~-"+o.length+"-~",o[o.length]=m);b=b.substring(0,k)+c+b.substring(f+1);!1===i&&(r=k-1)}for(k=o.length-1;0<=k;k-=1)b=b.replace(RegExp("~-"+k+"-~"),"{"+o[k]+"}","g");return b},trim:function(a){try{return a.replace(/^\s+|\s+$/g,"")}catch(b){return a}},merge:function(){var b={},d=arguments,e=d.length,
+i;for(i=0;i<e;i+=1)a.augmentObject(b,d[i],!0);return b},later:function(d,f,e,i,k){var d=d||0,f=f||{},c=e,g=i,p;a.isString(e)&&(c=f[e]);if(!c)throw new TypeError("method undefined");!a.isUndefined(i)&&!a.isArray(g)&&(g=[i]);e=function(){c.apply(f,g||b)};p=k?setInterval(e,d):setTimeout(e,d);return{interval:k,cancel:function(){this.interval?clearInterval(p):clearTimeout(p)}}},isValue:function(b){return a.isObject(b)||a.isString(b)||a.isNumber(b)||a.isBoolean(b)}};a.hasOwnProperty=c.hasOwnProperty?function(a,
+b){return a&&a.hasOwnProperty&&a.hasOwnProperty(b)}:function(b,d){return!a.isUndefined(b[d])&&b.constructor.prototype[d]!==b[d]};g.augmentObject(a,g,!0);YAHOO.util.Lang=a;a.augment=a.augmentProto;YAHOO.augment=a.augmentProto;YAHOO.extend=a.extend})();YAHOO.register("yahoo",YAHOO,{version:"2.9.0",build:"2800"});
+YAHOO.util.Get=function(){var a={},c=0,b=0,d=!1,f=YAHOO.env.ua,g=YAHOO.lang,j,h,e,i=function(e,a,i){var e=(i||window).document.createElement(e),b;for(b in a)a.hasOwnProperty(b)&&e.setAttribute(b,a[b]);return e},k=function(e,a,k){e={id:"yui__dyn_"+b++,type:"text/javascript",src:e};k&&g.augmentObject(e,k);return i("script",e,a)},l=function(e,a){return{tId:e.tId,win:e.win,data:e.data,nodes:e.nodes,msg:a,purge:function(){h(this.tId)}}},q=function(e,i){var b=a[i];(b=g.isString(e)?b.win.document.getElementById(e):
+e)||j(i,"target node not found: "+e);return b},p=function(e){YAHOO.log("Finishing transaction "+e);var i=a[e];i.finished=!0;i.aborted?j(e,"transaction "+e+" was aborted"):i.onSuccess&&(e=i.scope||i.win,i.onSuccess.call(e,l(i)))},n=function(e){YAHOO.log("Timeout "+e,"info","get");var e=a[e],i;e.onTimeout&&(i=e.scope||e,e.onTimeout.call(i,l(e)))},o=function(d,c){YAHOO.log("_next: "+d+", loaded: "+c,"info","Get");var l=a[d],h=l.win,m=h.document.getElementsByTagName("head")[0],x,v;l.timer&&l.timer.cancel();
+if(l.aborted)j(d,"transaction "+d+" was aborted");else if(c?(l.url.shift(),l.varName&&l.varName.shift()):(l.url=g.isString(l.url)?[l.url]:l.url,l.varName&&(l.varName=g.isString(l.varName)?[l.varName]:l.varName)),0===l.url.length)"script"===l.type&&f.webkit&&420>f.webkit&&!l.finalpass&&!l.varName?(v=k(null,l.win,l.attributes),v.innerHTML='YAHOO.util.Get._finalize("'+d+'");',l.nodes.push(v),m.appendChild(v)):p(d);else{v=l.url[0];if(!v)return l.url.shift(),YAHOO.log("skipping empty url"),o(d);YAHOO.log("attempting to load "+
+v,"info","Get");l.timeout&&(l.timer=g.later(l.timeout,l,n,d));if("script"===l.type)x=k(v,h,l.attributes);else{x=l.attributes;var y={id:"yui__dyn_"+b++,type:"text/css",rel:"stylesheet",href:v};x&&g.augmentObject(y,x);x=i("link",y,h)}e(l.type,x,d,v,h,l.url.length);l.nodes.push(x);l.insertBefore?(m=q(l.insertBefore,d))&&m.parentNode.insertBefore(x,m):m.appendChild(x);YAHOO.log("Appending node: "+v,"info","Get");(f.webkit||f.gecko)&&"css"===l.type&&o(d,v)}},m=function(e,i,b){var k="q"+c++,b=b||{};if(0===
+c%YAHOO.util.Get.PURGE_THRESH&&!d){d=!0;var f,l;for(f in a)a.hasOwnProperty(f)&&(l=a[f],l.autopurge&&l.finished&&(h(l.tId),delete a[f]));d=!1}a[k]=g.merge(b,{tId:k,type:e,url:i,finished:!1,aborted:!1,nodes:[]});i=a[k];i.win=i.win||window;i.scope=i.scope||i.win;i.autopurge="autopurge"in i?i.autopurge:"script"===e?!0:!1;i.attributes=i.attributes||{};i.attributes.charset=b.charset||i.attributes.charset||"utf-8";g.later(0,i,o,k);return{tId:k}};e=function(e,i,b,k,d,l,c){var h=c||o,q,p,m,n,C,z;f.ie?i.onreadystatechange=
+function(){q=this.readyState;if("loaded"===q||"complete"===q)YAHOO.log(b+" onload "+k,"info","Get"),i.onreadystatechange=null,h(b,k)}:f.webkit?"script"===e&&(420<=f.webkit?i.addEventListener("load",function(){YAHOO.log(b+" DOM2 onload "+k,"info","Get");h(b,k)}):(p=a[b],p.varName?(e=YAHOO.util.Get.POLL_FREQ,YAHOO.log("Polling for "+p.varName[0]),p.maxattempts=YAHOO.util.Get.TIMEOUT/e,p.attempts=0,p._cache=p.varName[0].split("."),p.timer=g.later(e,p,function(){m=this._cache;C=m.length;n=this.win;for(z=
+0;z<C;z+=1)if(n=n[m[z]],!n){this.attempts++;this.attempts++>this.maxattempts?(p.timer.cancel(),j(b,"Over retry limit, giving up")):YAHOO.log(m[z]+" failed, retrying");return}YAHOO.log("Safari poll complete");p.timer.cancel();h(b,k)},null,!0)):g.later(YAHOO.util.Get.POLL_FREQ,null,h,[b,k]))):i.onload=function(){YAHOO.log(b+" onload "+k,"info","Get");h(b,k)}};j=function(e,i){YAHOO.log("get failure: "+i,"warn","Get");var b=a[e],k;b.onFailure&&(k=b.scope||b.win,b.onFailure.call(k,l(b,i)))};h=function(e){if(a[e]){var i=
+a[e],b=i.nodes,k=b.length,d=i.win.document.getElementsByTagName("head")[0],f,l;if(i.insertBefore&&(e=q(i.insertBefore,e)))d=e.parentNode;for(e=0;e<k;e+=1){f=b[e];if(f.clearAttributes)f.clearAttributes();else for(l in f)f.hasOwnProperty(l)&&delete f[l];d.removeChild(f)}i.nodes=[]}};return{POLL_FREQ:10,PURGE_THRESH:20,TIMEOUT:2E3,_finalize:function(e){YAHOO.log(e+" finalized ","info","Get");g.later(0,null,p,e)},abort:function(e){var e=g.isString(e)?e:e.tId,i=a[e];i&&(YAHOO.log("Aborting "+e,"info",
+"Get"),i.aborted=!0)},script:function(e,a){return m("script",e,a)},css:function(e,a){return m("css",e,a)}}}();YAHOO.register("get",YAHOO.util.Get,{version:"2.9.0",build:"2800"});
+(function(){var a,c,b,d,f,g=YAHOO,j=g.util,h=g.lang,e=g.env;f={yahoo:!0,get:!0};a={defaultSkin:"sam",base:"assets/skins/",path:"skin.css",after:["reset","fonts","grids","base"],rollup:3};c={animation:{type:"js",path:"animation/animation-min.js",requires:["dom","event"]},autocomplete:{type:"js",path:"autocomplete/autocomplete-min.js",requires:["dom","event","datasource"],optional:["connection","animation"],skinnable:!0},base:{type:"css",path:"base/base-min.css",after:["reset","fonts","grids"]},button:{type:"js",
+path:"button/button-min.js",requires:["element"],optional:["menu"],skinnable:!0},calendar:{type:"js",path:"calendar/calendar-min.js",requires:["event","dom"],supersedes:["datemath"],skinnable:!0},carousel:{type:"js",path:"carousel/carousel-min.js",requires:["element"],optional:["animation"],skinnable:!0},charts:{type:"js",path:"charts/charts-min.js",requires:["element","json","datasource","swf"]},colorpicker:{type:"js",path:"colorpicker/colorpicker-min.js",requires:["slider","element"],optional:["animation"],
+skinnable:!0},connection:{type:"js",path:"connection/connection-min.js",requires:["event"],supersedes:["connectioncore"]},connectioncore:{type:"js",path:"connection/connection_core-min.js",requires:["event"],pkg:"connection"},container:{type:"js",path:"container/container-min.js",requires:["dom","event"],optional:["dragdrop","animation","connection"],supersedes:["containercore"],skinnable:!0},containercore:{type:"js",path:"container/container_core-min.js",requires:["dom","event"],pkg:"container"},
+cookie:{type:"js",path:"cookie/cookie-min.js",requires:["yahoo"]},datasource:{type:"js",path:"datasource/datasource-min.js",requires:["event"],optional:["connection"]},datatable:{type:"js",path:"datatable/datatable-min.js",requires:["element","datasource"],optional:["calendar","dragdrop","paginator"],skinnable:!0},datemath:{type:"js",path:"datemath/datemath-min.js",requires:["yahoo"]},dom:{type:"js",path:"dom/dom-min.js",requires:["yahoo"]},dragdrop:{type:"js",path:"dragdrop/dragdrop-min.js",requires:["dom",
+"event"]},editor:{type:"js",path:"editor/editor-min.js",requires:["menu","element","button"],optional:["animation","dragdrop"],supersedes:["simpleeditor"],skinnable:!0},element:{type:"js",path:"element/element-min.js",requires:["dom","event"],optional:["event-mouseenter","event-delegate"]},"element-delegate":{type:"js",path:"element-delegate/element-delegate-min.js",requires:["element"]},event:{type:"js",path:"event/event-min.js",requires:["yahoo"]},"event-simulate":{type:"js",path:"event-simulate/event-simulate-min.js",
+requires:["event"]},"event-delegate":{type:"js",path:"event-delegate/event-delegate-min.js",requires:["event"],optional:["selector"]},"event-mouseenter":{type:"js",path:"event-mouseenter/event-mouseenter-min.js",requires:["dom","event"]},fonts:{type:"css",path:"fonts/fonts-min.css"},get:{type:"js",path:"get/get-min.js",requires:["yahoo"]},grids:{type:"css",path:"grids/grids-min.css",requires:["fonts"],optional:["reset"]},history:{type:"js",path:"history/history-min.js",requires:["event"]},imagecropper:{type:"js",
+path:"imagecropper/imagecropper-min.js",requires:["dragdrop","element","resize"],skinnable:!0},imageloader:{type:"js",path:"imageloader/imageloader-min.js",requires:["event","dom"]},json:{type:"js",path:"json/json-min.js",requires:["yahoo"]},layout:{type:"js",path:"layout/layout-min.js",requires:["element"],optional:["animation","dragdrop","resize","selector"],skinnable:!0},logger:{type:"js",path:"logger/logger-min.js",requires:["event","dom"],optional:["dragdrop"],skinnable:!0},menu:{type:"js",path:"menu/menu-min.js",
+requires:["containercore"],skinnable:!0},paginator:{type:"js",path:"paginator/paginator-min.js",requires:["element"],skinnable:!0},profiler:{type:"js",path:"profiler/profiler-min.js",requires:["yahoo"]},profilerviewer:{type:"js",path:"profilerviewer/profilerviewer-min.js",requires:["profiler","yuiloader","element"],skinnable:!0},progressbar:{type:"js",path:"progressbar/progressbar-min.js",requires:["element"],optional:["animation"],skinnable:!0},reset:{type:"css",path:"reset/reset-min.css"},"reset-fonts-grids":{type:"css",
+path:"reset-fonts-grids/reset-fonts-grids.css",supersedes:["reset","fonts","grids","reset-fonts"],rollup:4},"reset-fonts":{type:"css",path:"reset-fonts/reset-fonts.css",supersedes:["reset","fonts"],rollup:2},resize:{type:"js",path:"resize/resize-min.js",requires:["dragdrop","element"],optional:["animation"],skinnable:!0},selector:{type:"js",path:"selector/selector-min.js",requires:["yahoo","dom"]},simpleeditor:{type:"js",path:"editor/simpleeditor-min.js",requires:["element"],optional:["containercore",
+"menu","button","animation","dragdrop"],skinnable:!0,pkg:"editor"},slider:{type:"js",path:"slider/slider-min.js",requires:["dragdrop"],optional:["animation"],skinnable:!0},storage:{type:"js",path:"storage/storage-min.js",requires:["yahoo","event","cookie"],optional:["swfstore"]},stylesheet:{type:"js",path:"stylesheet/stylesheet-min.js",requires:["yahoo"]},swf:{type:"js",path:"swf/swf-min.js",requires:["element"],supersedes:["swfdetect"]},swfdetect:{type:"js",path:"swfdetect/swfdetect-min.js",requires:["yahoo"]},
+swfstore:{type:"js",path:"swfstore/swfstore-min.js",requires:["element","cookie","swf"]},tabview:{type:"js",path:"tabview/tabview-min.js",requires:["element"],optional:["connection"],skinnable:!0},treeview:{type:"js",path:"treeview/treeview-min.js",requires:["event","dom"],optional:["json","animation","calendar"],skinnable:!0},uploader:{type:"js",path:"uploader/uploader-min.js",requires:["element"]},utilities:{type:"js",path:"utilities/utilities.js",supersedes:"yahoo event dragdrop animation dom connection element yahoo-dom-event get yuiloader yuiloader-dom-event".split(" "),
+rollup:8},yahoo:{type:"js",path:"yahoo/yahoo-min.js"},"yahoo-dom-event":{type:"js",path:"yahoo-dom-event/yahoo-dom-event.js",supersedes:["yahoo","event","dom"],rollup:3},yuiloader:{type:"js",path:"yuiloader/yuiloader-min.js",supersedes:["yahoo","get"]},"yuiloader-dom-event":{type:"js",path:"yuiloader-dom-event/yuiloader-dom-event.js",supersedes:"yahoo dom event get yuiloader yahoo-dom-event".split(" "),rollup:5},yuitest:{type:"js",path:"yuitest/yuitest-min.js",requires:["logger"],optional:["event-simulate"],
+skinnable:!0}};b={appendArray:function(e,a){if(a)for(var b=0;b<a.length;b+=1)e[a[b]]=!0},keys:function(e){var a=[],b;for(b in e)h.hasOwnProperty(e,b)&&a.push(b);return a}};d={appendArray:function(e,a){Array.prototype.push.apply(e,a)},indexOf:function(e,a){for(var b=0;b<e.length;b+=1)if(e[b]===a)return b;return-1},toObject:function(e){for(var a={},b=0;b<e.length;b+=1)a[e[b]]=!0;return a},uniq:function(e){return b.keys(d.toObject(e))}};YAHOO.util.YUILoader=function(i){this._internalCallback=null;this._useYahooListener=
+!1;this.onSuccess=null;this.onFailure=g.log;this.onTimeout=this.onProgress=null;this.scope=this;this.varName=this.charset=this.insertBefore=this.data=null;this.base="http://yui.yahooapis.com/2.9.0/build/";this.comboBase="http://yui.yahooapis.com/combo?";this.combine=!1;this.root="2.9.0/build/";this.timeout=0;this.force=this.ignore=null;this.allowRollup=!0;this.filter=null;this.required={};this.moduleInfo=h.merge(c);this.rollups=null;this.loadOptional=!1;this.sorted=[];this.loaded={};this.dirty=!0;
+this.inserted={};var b=this;e.listeners.push(function(e){b._useYahooListener&&b.loadNext(e.name)});this.skin=h.merge(a);this._config(i)};g.util.YUILoader.prototype={FILTERS:{RAW:{searchExp:"-min\\.js",replaceStr:".js"},DEBUG:{searchExp:"-min\\.js",replaceStr:"-debug.js"}},SKIN_PREFIX:"skin-",_config:function(e){if(e)for(var a in e)h.hasOwnProperty(e,a)&&("require"==a?this.require(e[a]):this[a]=e[a]);e=this.filter;h.isString(e)&&(e=e.toUpperCase(),"DEBUG"===e&&this.require("logger"),g.widget.LogWriter||
+(g.widget.LogWriter=function(){return g}),this.filter=this.FILTERS[e])},addModule:function(e){if(!e||!e.name||!e.type||!e.path&&!e.fullpath)return!1;e.ext="ext"in e?e.ext:!0;e.requires=e.requires||[];this.moduleInfo[e.name]=e;return this.dirty=!0},require:function(e){var a="string"===typeof e?arguments:e;this.dirty=!0;b.appendArray(this.required,a)},_addSkin:function(e,a){var b=this.formatSkin(e),d=this.moduleInfo,f=this.skin,c=d[a]&&d[a].ext;d[b]||this.addModule({name:b,type:"css",path:f.base+e+
+"/"+f.path,after:f.after,rollup:f.rollup,ext:c});a&&(b=this.formatSkin(e,a),d[b]||this.addModule({name:b,type:"css",after:f.after,path:(d[a].pkg||a)+"/"+f.base+e+"/"+a+".css",ext:c}));return b},getRequires:function(e){if(!e)return[];if(!this.dirty&&e.expanded)return e.expanded;e.requires=e.requires||[];var a,b=[],f=e.requires,c=e.optional,g=this.moduleInfo,h;for(a=0;a<f.length;a+=1)b.push(f[a]),h=g[f[a]],d.appendArray(b,this.getRequires(h));if(c&&this.loadOptional)for(a=0;a<c.length;a+=1)b.push(c[a]),
+d.appendArray(b,this.getRequires(g[c[a]]));e.expanded=d.uniq(b);return e.expanded},getProvides:function(e,a){var b=!a?"_provides":"_supersedes",d=this.moduleInfo[e],f={};if(!d)return f;if(d[b])return d[b];var c=d.supersedes,g={};if(c)for(var j=0;j<c.length;j+=1){var r=c[j];g[r]||(g[r]=!0,h.augmentObject(f,this.getProvides(r)))}d._supersedes=f;d._provides=h.merge(f);d._provides[e]=!0;return d[b]},calculate:function(e){if(e||this.dirty)this._config(e),this._setup(),this._explode(),this.allowRollup&&
+this._rollup(),this._reduce(),this._sort(),this.dirty=!1},_setup:function(){var a=this.moduleInfo,k,f,c;for(k in a)if(h.hasOwnProperty(a,k)){var g=a[k];if(g&&g.skinnable){var j=this.skin.overrides,o;if(j&&j[k])for(f=0;f<j[k].length;f+=1)o=this._addSkin(j[k][f],k);else o=this._addSkin(this.skin.defaultSkin,k);-1==d.indexOf(g.requires,o)&&g.requires.push(o)}}a=h.merge(this.inserted);this._sandbox||(a=h.merge(a,e.modules));this.ignore&&b.appendArray(a,this.ignore);if(this.force)for(f=0;f<this.force.length;f+=
+1)this.force[f]in a&&delete a[this.force[f]];for(c in a)h.hasOwnProperty(a,c)&&h.augmentObject(a,this.getProvides(c));this.loaded=a},_explode:function(){var e=this.required,a,d;for(a in e)if(h.hasOwnProperty(e,a)&&(d=this.moduleInfo[a]))(d=this.getRequires(d))&&b.appendArray(e,d)},_skin:function(){},formatSkin:function(e,a){var b=this.SKIN_PREFIX+e;a&&(b=b+"-"+a);return b},parseSkin:function(e){return 0===e.indexOf(this.SKIN_PREFIX)?(e=e.split("-"),{skin:e[1],module:e[2]}):null},_rollup:function(){var e,
+a,b,d,c={},g=this.required,j,m=this.moduleInfo;if(this.dirty||!this.rollups){for(e in m)h.hasOwnProperty(m,e)&&(b=m[e])&&b.rollup&&(c[e]=b);this.rollups=c}for(;;){var r=!1;for(e in c)if(!g[e]&&!this.loaded[e]&&(b=m[e],d=b.supersedes,j=!1,b.rollup)){var s=0;if(b.ext?0:this.parseSkin(e))for(a in g){if(h.hasOwnProperty(g,a)&&(e!==a&&this.parseSkin(a))&&(s++,j=s>=b.rollup))break}else for(a=0;a<d.length;a+=1)if(this.loaded[d[a]]&&!f[d[a]]){j=!1;break}else if(g[d[a]]&&(s++,j=s>=b.rollup))break;j&&(r=g[e]=
+!0,this.getRequires(b))}if(!r)break}},_reduce:function(){var e,a,b,d=this.required;for(e in d)if(e in this.loaded)delete d[e];else if(b=this.parseSkin(e)){if(!b.module){var f=this.SKIN_PREFIX+b.skin;for(a in d)h.hasOwnProperty(d,a)&&(b=this.moduleInfo[a],(!b||!b.ext)&&(a!==e&&-1<a.indexOf(f))&&delete d[a])}}else if(b=(b=this.moduleInfo[e])&&b.supersedes)for(a=0;a<b.length;a+=1)b[a]in d&&delete d[b[a]]},_onFailure:function(e){YAHOO.log("Failure","info","loader");var a=this.onFailure;a&&a.call(this.scope,
+{msg:"failure: "+e,data:this.data,success:!1})},_onTimeout:function(){YAHOO.log("Timeout","info","loader");var e=this.onTimeout;e&&e.call(this.scope,{msg:"timeout",data:this.data,success:!1})},_sort:function(){var e=[],a=this.moduleInfo,b=this.loaded,f=!this.loadOptional,c=function(e,i){var g=a[e];if(b[i]||!g)return!1;var h;h=g.expanded;var j=g.after,o=a[i],m=g.optional;if(h&&-1<d.indexOf(h,i)||j&&-1<d.indexOf(j,i)||f&&m&&-1<d.indexOf(m,i))return!0;if(j=a[i]&&a[i].supersedes)for(h=0;h<j.length;h+=
+1)if(c(e,j[h]))return!0;return g.ext&&"css"==g.type&&!o.ext&&"css"==o.type?!0:!1},g;for(g in this.required)h.hasOwnProperty(this.required,g)&&e.push(g);for(g=0;;){var j=e.length,m,r,s,t=!1;for(r=g;r<j;r+=1){m=e[r];for(s=r+1;s<j;s+=1)if(c(m,e[s])){m=e.splice(s,1);e.splice(r,0,m[0]);t=!0;break}if(t)break;else g+=1}if(!t)break}this.sorted=e},toString:function(){h.dump({type:"YUILoader",base:this.base,filter:this.filter,required:this.required,loaded:this.loaded,inserted:this.inserted},1)},_combine:function(){this._combining=
+[];var e=this,a=this.sorted,b=a.length,d=this.comboBase,f=this.comboBase,c,g=d.length,h,j,s=this.loadType;YAHOO.log("type "+s);for(h=0;h<b;h+=1)if((j=this.moduleInfo[a[h]])&&!j.ext&&(!s||s===j.type))c=this.root+j.path,c+="&","js"==j.type?d+=c:f+=c,this._combining.push(a[h]);if(this._combining.length){YAHOO.log("Attempting to combine: "+this._combining,"info","loader");var t=function(e){var a=this._combining,i=a.length,b;for(b=0;b<i;b+=1)this.inserted[a[b]]=!0;this.loadNext(e.data)},a=function(){d.length>
+g?YAHOO.util.Get.script(e._filter(d),{data:e._loading,onSuccess:t,onFailure:e._onFailure,onTimeout:e._onTimeout,insertBefore:e.insertBefore,charset:e.charset,timeout:e.timeout,scope:e}):this.loadNext()};f.length>g?YAHOO.util.Get.css(this._filter(f),{data:this._loading,onSuccess:a,onFailure:this._onFailure,onTimeout:this._onTimeout,insertBefore:this.insertBefore,charset:this.charset,timeout:this.timeout,scope:e}):a()}else this.loadNext(this._loading)},insert:function(e,a){this.calculate(e);this._loading=
+!0;this.loadType=a;if(this.combine)return this._combine();if(a)this.loadNext();else{var b=this;this._internalCallback=function(){b._internalCallback=null;b.insert(null,"js")};this.insert(null,"css")}},sandbox:function(e,a){var b=this,d=function(e){var a=e.argument[2];b._scriptText[e.argument[0]]=e.responseText;b.onProgress&&b.onProgress.call(b.scope,{name:a,scriptText:e.responseText,xhrResponse:e,data:b.data});b._loadCount++;b._loadCount>=b._stopCount&&(e="\nreturn "+(b.varName||"YAHOO")+";\n})();",
+e=eval("(function() {\n"+b._scriptText.join("\n")+e),b._pushEvents(e),e?b.onSuccess.call(b.scope,{reference:e,data:b.data}):b._onFailure.call(b.varName+" reference failure"))},f=function(e){b.onFailure.call(b.scope,{msg:"XHR failure",xhrResponse:e,data:b.data})};b._config(e);if(!b.onSuccess)throw Error("You must supply an onSuccess handler for your sandbox");b._sandbox=!0;if(!a||"js"!==a)b._internalCallback=function(){b._internalCallback=null;b.sandbox(null,"js")},b.insert(null,"css");else if(j.Connect){b._scriptText=
+[];b._loadCount=0;b._stopCount=b.sorted.length;b._xhr=[];b.calculate();var c=b.sorted,g=c.length,h,r,s;for(h=0;h<g;h+=1){r=b.moduleInfo[c[h]];if(!r){b._onFailure("undefined module "+r);for(d=0;d<b._xhr.length;d+=1)b._xhr[d].abort();break}"js"!==r.type?b._loadCount++:(s=(s=r.fullpath)?b._filter(s):b._url(r.path),b._xhr.push(j.Connect.asyncRequest("GET",s,{success:d,failure:f,scope:b,argument:[h,s,c[h]]})))}}else(new YAHOO.util.YUILoader).insert({base:b.base,filter:b.filter,require:"connection",insertBefore:b.insertBefore,
+charset:b.charset,onSuccess:function(){b.sandbox(null,"js")},scope:b},"js")},loadNext:function(a){if(this._loading){var b=this,d=function(e){b.loadNext(e.data)},f=this.sorted,c=f.length,g,h;if(a){if(a!==this._loading)return;this.inserted[a]=!0;this.onProgress&&this.onProgress.call(this.scope,{name:a,data:this.data})}for(a=0;a<c;a+=1)if(!(f[a]in this.inserted)){if(f[a]===this._loading)return;g=this.moduleInfo[f[a]];if(!g){this.onFailure.call(this.scope,{msg:"undefined module "+g,data:this.data});return}if(!this.loadType||
+this.loadType===g.type){this._loading=f[a];c="css"===g.type?j.Get.css:j.Get.script;h=(h=g.fullpath)?this._filter(h):this._url(g.path);e.ua.webkit&&(420>e.ua.webkit&&"js"===g.type&&!g.varName)&&(d=null,this._useYahooListener=!0);c(h,{data:f[a],onSuccess:d,onFailure:this._onFailure,onTimeout:this._onTimeout,insertBefore:this.insertBefore,charset:this.charset,timeout:this.timeout,varName:g.varName,scope:b});return}}this._loading=null;this._internalCallback?(f=this._internalCallback,this._internalCallback=
+null,f.call(this)):this.onSuccess&&(this._pushEvents(),this.onSuccess.call(this.scope,{data:this.data}))}},_pushEvents:function(e){e=e||YAHOO;e.util&&e.util.Event&&e.util.Event._load()},_filter:function(e){var a=this.filter;return a?e.replace(RegExp(a.searchExp,"g"),a.replaceStr):e},_url:function(e){return this._filter((this.base||"")+e)}}})();YAHOO.register("yuiloader",YAHOO.util.YUILoader,{version:"2.9.0",build:"2800"});
+(function(){YAHOO.env._id_counter=YAHOO.env._id_counter||0;var a=YAHOO.util,c=YAHOO.lang,b=YAHOO.env.ua,d=YAHOO.lang.trim,f={},g={},j=/^t(?:able|d|h)$/i,h=/color$/i,e=window.document,i=e.documentElement,k=b.opera,l=b.webkit,q=b.gecko,p=b.ie;a.Dom={CUSTOM_ATTRIBUTES:!i.hasAttribute?{"for":"htmlFor","class":"className"}:{htmlFor:"for",className:"class"},DOT_ATTRIBUTES:{checked:!0},get:function(i){var b,d,f,k;b=null;if(i){if("string"==typeof i||"number"==typeof i){b=i+"";f=(i=e.getElementById(i))?i.attributes:
+null;if(i&&f&&f.id&&f.id.value===b)return i;if(i&&e.all&&(i=null,(d=e.all[b])&&d.length)){f=0;for(k=d.length;f<k;++f)if(d[f].id===b)return d[f]}}else if(a.Element&&i instanceof a.Element)i=i.get("element");else if(!i.nodeType&&"length"in i){b=[];f=0;for(k=i.length;f<k;++f)b[b.length]=a.Dom.get(i[f]);i=b}b=i}return b},getComputedStyle:function(e,i){if(window.getComputedStyle)return e.ownerDocument.defaultView.getComputedStyle(e,null)[i];if(e.currentStyle)return a.Dom.IE_ComputedStyle.get(e,i)},getStyle:function(e,
+i){return a.Dom.batch(e,a.Dom._getStyle,i)},_getStyle:function(){if(window.getComputedStyle)return function(e,i){var i="float"===i?i="cssFloat":a.Dom._toCamel(i),b=e.style[i],d;b||(d=e.ownerDocument.defaultView.getComputedStyle(e,null))&&(b=d[i]);return b};if(i.currentStyle)return function(e,i){var b;switch(i){case "opacity":b=100;try{b=e.filters["DXImageTransform.Microsoft.Alpha"].opacity}catch(d){try{b=e.filters("alpha").opacity}catch(f){}}return b/100;case "float":i="styleFloat";default:return i=
+a.Dom._toCamel(i),b=e.currentStyle?e.currentStyle[i]:null,e.style[i]||b}}}(),setStyle:function(e,i,b){a.Dom.batch(e,a.Dom._setStyle,{prop:i,val:b})},_setStyle:function(){return!window.getComputedStyle&&e.documentElement.currentStyle?function(e,i){var b=a.Dom._toCamel(i.prop),d=i.val;if(e)switch(b){case "opacity":if(""===d||null===d||1===d)e.style.removeAttribute("filter");else if(c.isString(e.style.filter)&&(e.style.filter="alpha(opacity="+100*d+")",!e.currentStyle||!e.currentStyle.hasLayout))e.style.zoom=
+1;break;case "float":b="styleFloat";default:e.style[b]=d}}:function(e,i){var b=a.Dom._toCamel(i.prop),d=i.val;e&&("float"==b&&(b="cssFloat"),e.style[b]=d)}}(),getXY:function(e){return a.Dom.batch(e,a.Dom._getXY)},_canPosition:function(e){return"none"!==a.Dom._getStyle(e,"display")&&a.Dom._inDoc(e)},_getXY:function(e){var i,b,d=Math.round;b=!1;if(a.Dom._canPosition(e)){b=e.getBoundingClientRect();i=e.ownerDocument;e=a.Dom.getDocumentScrollLeft(i);i=a.Dom.getDocumentScrollTop(i);b=[b.left,b.top];if(i||
+e)b[0]+=e,b[1]+=i;b[0]=d(b[0]);b[1]=d(b[1])}return b},getX:function(e){return a.Dom.batch(e,function(e){return a.Dom.getXY(e)[0]},a.Dom,!0)},getY:function(e){return a.Dom.batch(e,function(e){return a.Dom.getXY(e)[1]},a.Dom,!0)},setXY:function(e,i,b){a.Dom.batch(e,a.Dom._setXY,{pos:i,noRetry:b})},_setXY:function(e,i){var b=a.Dom._getStyle(e,"position"),d=a.Dom.setStyle,f=i.pos,k=i.noRetry,c=[parseInt(a.Dom.getComputedStyle(e,"left"),10),parseInt(a.Dom.getComputedStyle(e,"top"),10)],g;g=a.Dom._getXY(e);
+if(!f||!1===g)return!1;"static"==b&&(b="relative",d(e,"position",b));isNaN(c[0])&&(c[0]="relative"==b?0:e.offsetLeft);isNaN(c[1])&&(c[1]="relative"==b?0:e.offsetTop);null!==f[0]&&d(e,"left",f[0]-g[0]+c[0]+"px");null!==f[1]&&d(e,"top",f[1]-g[1]+c[1]+"px");k||(b=a.Dom._getXY(e),(null!==f[0]&&b[0]!=f[0]||null!==f[1]&&b[1]!=f[1])&&a.Dom._setXY(e,{pos:f,noRetry:!0}))},setX:function(e,i){a.Dom.setXY(e,[i,null])},setY:function(e,i){a.Dom.setXY(e,[null,i])},getRegion:function(e){return a.Dom.batch(e,function(e){var i=
+!1;a.Dom._canPosition(e)&&(i=a.Region.getRegion(e));return i},a.Dom,!0)},getClientWidth:function(){return a.Dom.getViewportWidth()},getClientHeight:function(){return a.Dom.getViewportHeight()},getElementsByClassName:function(i,b,d,f,k,c){b=b||"*";d=d?a.Dom.get(d):e;if(!d)return[];for(var g=[],b=d.getElementsByTagName(b),d=a.Dom.hasClass,l=0,h=b.length;l<h;++l)d(b[l],i)&&(g[g.length]=b[l]);f&&a.Dom.batch(g,f,k,c);return g},hasClass:function(e,i){return a.Dom.batch(e,a.Dom._hasClass,i)},_hasClass:function(e,
+i){var b=!1;e&&i&&((b=a.Dom._getAttribute(e,"className")||"")&&(b=b.replace(/\s+/g," ")),b=i.exec?i.test(b):i&&-1<(" "+b+" ").indexOf(" "+i+" "));return b},addClass:function(e,i){return a.Dom.batch(e,a.Dom._addClass,i)},_addClass:function(e,i){var b=!1,f;e&&i&&(f=a.Dom._getAttribute(e,"className")||"",a.Dom._hasClass(e,i)||(a.Dom.setAttribute(e,"className",d(f+" "+i)),b=!0));return b},removeClass:function(e,i){return a.Dom.batch(e,a.Dom._removeClass,i)},_removeClass:function(e,i){var b=!1,f,k;e&&
+i&&(f=a.Dom._getAttribute(e,"className")||"",a.Dom.setAttribute(e,"className",f.replace(a.Dom._getClassRegex(i),"")),k=a.Dom._getAttribute(e,"className"),f!==k&&(a.Dom.setAttribute(e,"className",d(k)),b=!0,""===a.Dom._getAttribute(e,"className")&&(f=e.hasAttribute&&e.hasAttribute("class")?"class":"className",e.removeAttribute(f))));return b},replaceClass:function(e,i,b){return a.Dom.batch(e,a.Dom._replaceClass,{from:i,to:b})},_replaceClass:function(e,i){var b,f,k=!1;e&&i&&(b=i.from,(f=i.to)?b?b!==
+f&&(k=a.Dom._getAttribute(e,"className")||"",b=(" "+k.replace(a.Dom._getClassRegex(b)," "+f).replace(/\s+/g," ")).split(a.Dom._getClassRegex(f)),b.splice(1,0," "+f),a.Dom.setAttribute(e,"className",d(b.join(""))),k=!0):k=a.Dom._addClass(e,i.to):k=!1);return k},generateId:function(e,i){var i=i||"yui-gen",b=function(e){if(e&&e.id)return e.id;var b=i+YAHOO.env._id_counter++;if(e){if(e.ownerDocument&&e.ownerDocument.getElementById(b))return a.Dom.generateId(e,b+i);e.id=b}return b};return a.Dom.batch(e,
+b,a.Dom,!0)||b.apply(a.Dom,arguments)},isAncestor:function(e,i){var e=a.Dom.get(e),i=a.Dom.get(i),b=!1;e&&i&&(e.nodeType&&i.nodeType)&&(e.contains&&e!==i?b=e.contains(i):e.compareDocumentPosition&&(b=!!(e.compareDocumentPosition(i)&16)));return b},inDocument:function(e,i){return a.Dom._inDoc(a.Dom.get(e),i)},_inDoc:function(e,i){var b=!1;e&&e.tagName&&(i=i||e.ownerDocument,b=a.Dom.isAncestor(i.documentElement,e));return b},getElementsBy:function(i,b,d,f,k,c,g){var b=b||"*",d=d?a.Dom.get(d):e,l=g?
+null:[];if(d){for(var b=d.getElementsByTagName(b),d=0,h=b.length;d<h;++d)if(i(b[d]))if(g){l=b[d];break}else l[l.length]=b[d];f&&a.Dom.batch(l,f,k,c)}return l},getElementBy:function(e,i,b){return a.Dom.getElementsBy(e,i,b,null,null,null,!0)},batch:function(e,i,b,d){var f=[],d=d?b:null;if((e=e&&(e.tagName||e.item)?e:a.Dom.get(e))&&i){if(e.tagName||void 0===e.length)return i.call(d,e,b);for(var k=0;k<e.length;++k)f[f.length]=i.call(d||e[k],e[k],b)}else return!1;return f},getDocumentHeight:function(){return Math.max("CSS1Compat"!=
+e.compatMode||l?e.body.scrollHeight:i.scrollHeight,a.Dom.getViewportHeight())},getDocumentWidth:function(){return Math.max("CSS1Compat"!=e.compatMode||l?e.body.scrollWidth:i.scrollWidth,a.Dom.getViewportWidth())},getViewportHeight:function(){var a=self.innerHeight,b=e.compatMode;if((b||p)&&!k)a="CSS1Compat"==b?i.clientHeight:e.body.clientHeight;return a},getViewportWidth:function(){var a=self.innerWidth,b=e.compatMode;if(b||p)a="CSS1Compat"==b?i.clientWidth:e.body.clientWidth;return a},getAncestorBy:function(e,
+i){for(;e=e.parentNode;)if(a.Dom._testElement(e,i))return e;return null},getAncestorByClassName:function(e,i){e=a.Dom.get(e);return!e?null:a.Dom.getAncestorBy(e,function(e){return a.Dom.hasClass(e,i)})},getAncestorByTagName:function(e,i){e=a.Dom.get(e);return!e?null:a.Dom.getAncestorBy(e,function(e){return e.tagName&&e.tagName.toUpperCase()==i.toUpperCase()})},getPreviousSiblingBy:function(e,i){for(;e;)if(e=e.previousSibling,a.Dom._testElement(e,i))return e;return null},getPreviousSibling:function(e){e=
+a.Dom.get(e);return!e?null:a.Dom.getPreviousSiblingBy(e)},getNextSiblingBy:function(e,i){for(;e;)if(e=e.nextSibling,a.Dom._testElement(e,i))return e;return null},getNextSibling:function(e){e=a.Dom.get(e);return!e?null:a.Dom.getNextSiblingBy(e)},getFirstChildBy:function(e,i){return(a.Dom._testElement(e.firstChild,i)?e.firstChild:null)||a.Dom.getNextSiblingBy(e.firstChild,i)},getFirstChild:function(e){e=a.Dom.get(e);return!e?null:a.Dom.getFirstChildBy(e)},getLastChildBy:function(e,i){return!e?null:
+(a.Dom._testElement(e.lastChild,i)?e.lastChild:null)||a.Dom.getPreviousSiblingBy(e.lastChild,i)},getLastChild:function(e){e=a.Dom.get(e);return a.Dom.getLastChildBy(e)},getChildrenBy:function(e,i){var b=a.Dom.getFirstChildBy(e,i),d=b?[b]:[];a.Dom.getNextSiblingBy(b,function(e){if(!i||i(e))d[d.length]=e;return!1});return d},getChildren:function(e){e=a.Dom.get(e);return a.Dom.getChildrenBy(e)},getDocumentScrollLeft:function(a){a=a||e;return Math.max(a.documentElement.scrollLeft,a.body.scrollLeft)},
+getDocumentScrollTop:function(a){a=a||e;return Math.max(a.documentElement.scrollTop,a.body.scrollTop)},insertBefore:function(e,i){e=a.Dom.get(e);i=a.Dom.get(i);return!e||!i||!i.parentNode?null:i.parentNode.insertBefore(e,i)},insertAfter:function(e,i){e=a.Dom.get(e);i=a.Dom.get(i);return!e||!i||!i.parentNode?null:i.nextSibling?i.parentNode.insertBefore(e,i.nextSibling):i.parentNode.appendChild(e)},getClientRegion:function(){var e=a.Dom.getDocumentScrollTop(),i=a.Dom.getDocumentScrollLeft(),b=a.Dom.getViewportWidth()+
+i,d=a.Dom.getViewportHeight()+e;return new a.Region(e,b,d,i)},setAttribute:function(e,i,b){a.Dom.batch(e,a.Dom._setAttribute,{attr:i,val:b})},_setAttribute:function(e,i){var b=a.Dom._toCamel(i.attr),d=i.val;e&&e.setAttribute&&(a.Dom.DOT_ATTRIBUTES[b]&&e.tagName&&"BUTTON"!=e.tagName?e[b]=d:(b=a.Dom.CUSTOM_ATTRIBUTES[b]||b,e.setAttribute(b,d)))},getAttribute:function(e,i){return a.Dom.batch(e,a.Dom._getAttribute,i)},_getAttribute:function(e,i){var b,i=a.Dom.CUSTOM_ATTRIBUTES[i]||i;a.Dom.DOT_ATTRIBUTES[i]?
+b=e[i]:e&&"getAttribute"in e&&(b=/^(?:href|src)$/.test(i)?e.getAttribute(i,2):e.getAttribute(i));return b},_toCamel:function(e){function a(e,i){return i.toUpperCase()}return f[e]||(f[e]=-1===e.indexOf("-")?e:e.replace(/-([a-z])/gi,a))},_getClassRegex:function(e){var i;void 0!==e&&(e.exec?i=e:(i=g[e],i||(e=e.replace(a.Dom._patterns.CLASS_RE_TOKENS,"\\$1"),e=e.replace(/\s+/g," "),i=g[e]=RegExp("(?:^|\\s)"+e+"(?= |$)","g"))));return i},_patterns:{ROOT_TAG:/^body|html$/i,CLASS_RE_TOKENS:/([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g},
+_testElement:function(e,a){return e&&1==e.nodeType&&(!a||a(e))},_calcBorders:function(e,i){var b=parseInt(a.Dom.getComputedStyle(e,"borderTopWidth"),10)||0,d=parseInt(a.Dom.getComputedStyle(e,"borderLeftWidth"),10)||0;q&&j.test(e.tagName)&&(d=b=0);i[0]+=d;i[1]+=b;return i}};var n=a.Dom.getComputedStyle;b.opera&&(a.Dom.getComputedStyle=function(e,i){var b=n(e,i);h.test(i)&&(b=a.Dom.Color.toRGB(b));return b});b.webkit&&(a.Dom.getComputedStyle=function(e,a){var i=n(e,a);"rgba(0, 0, 0, 0)"===i&&(i="transparent");
+return i});b.ie&&8<=b.ie&&(a.Dom.DOT_ATTRIBUTES.type=!0)})();YAHOO.util.Region=function(a,c,b,d){this.y=this.top=a;this[1]=a;this.right=c;this.bottom=b;this.x=this.left=d;this[0]=d;this.width=this.right-this.left;this.height=this.bottom-this.top};YAHOO.util.Region.prototype.contains=function(a){return a.left>=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom};YAHOO.util.Region.prototype.getArea=function(){return(this.bottom-this.top)*(this.right-this.left)};
+YAHOO.util.Region.prototype.intersect=function(a){var c=Math.max(this.top,a.top),b=Math.min(this.right,a.right),d=Math.min(this.bottom,a.bottom),a=Math.max(this.left,a.left);return d>=c&&b>=a?new YAHOO.util.Region(c,b,d,a):null};YAHOO.util.Region.prototype.union=function(a){var c=Math.min(this.top,a.top),b=Math.max(this.right,a.right),d=Math.max(this.bottom,a.bottom),a=Math.min(this.left,a.left);return new YAHOO.util.Region(c,b,d,a)};
+YAHOO.util.Region.prototype.toString=function(){return"Region {top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+", height: "+this.height+", width: "+this.width+"}"};YAHOO.util.Region.getRegion=function(a){var c=YAHOO.util.Dom.getXY(a);return new YAHOO.util.Region(c[1],c[0]+a.offsetWidth,c[1]+a.offsetHeight,c[0])};YAHOO.util.Point=function(a,c){YAHOO.lang.isArray(a)&&(c=a[1],a=a[0]);YAHOO.util.Point.superclass.constructor.call(this,c,a,c,a)};
+YAHOO.extend(YAHOO.util.Point,YAHOO.util.Region);
+(function(){var a=YAHOO.util,c=/^width|height$/,b=/^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,d={get:function(d,f){var c="",c=d.currentStyle[f];return c="opacity"===f?a.Dom.getStyle(d,"opacity"):!c||c.indexOf&&-1<c.indexOf("px")?c:a.Dom.IE_COMPUTED[f]?a.Dom.IE_COMPUTED[f](d,f):b.test(c)?a.Dom.IE.ComputedStyle.getPixel(d,f):c},getOffset:function(a,b){var d=a.currentStyle[b],e=b.charAt(0).toUpperCase()+b.substr(1),i="offset"+e,f="pixel"+e,e="";"auto"==d?(e=
+d=a[i],c.test(b)&&(a.style[b]=d,a[i]>d&&(e=d-(a[i]-d)),a.style[b]="auto")):(!a.style[f]&&!a.style[b]&&(a.style[b]=d),e=a.style[f]);return e+"px"},getBorderWidth:function(a,b){var d=null;a.currentStyle.hasLayout||(a.style.zoom=1);switch(b){case "borderTopWidth":d=a.clientTop;break;case "borderBottomWidth":d=a.offsetHeight-a.clientHeight-a.clientTop;break;case "borderLeftWidth":d=a.clientLeft;break;case "borderRightWidth":d=a.offsetWidth-a.clientWidth-a.clientLeft}return d+"px"},getPixel:function(a,
+b){var d=null,e=a.currentStyle.right;a.style.right=a.currentStyle[b];d=a.style.pixelRight;a.style.right=e;return d+"px"},getMargin:function(b,d){return"auto"==b.currentStyle[d]?"0px":a.Dom.IE.ComputedStyle.getPixel(b,d)},getVisibility:function(a,b){for(var d;(d=a.currentStyle)&&"inherit"==d[b];)a=a.parentNode;return d?d[b]:"visible"},getColor:function(b,d){return a.Dom.Color.toRGB(b.currentStyle[d])||"transparent"},getBorderColor:function(b,d){var f=b.currentStyle;return a.Dom.Color.toRGB(a.Dom.Color.toHex(f[d]||
+f.color))}},f={};f.top=f.right=f.bottom=f.left=f.width=f.height=d.getOffset;f.color=d.getColor;f.borderTopWidth=f.borderRightWidth=f.borderBottomWidth=f.borderLeftWidth=d.getBorderWidth;f.marginTop=f.marginRight=f.marginBottom=f.marginLeft=d.getMargin;f.visibility=d.getVisibility;f.borderColor=f.borderTopColor=f.borderRightColor=f.borderBottomColor=f.borderLeftColor=d.getBorderColor;a.Dom.IE_COMPUTED=f;a.Dom.IE_ComputedStyle=d})();
+(function(){var a=parseInt,c=RegExp,b=YAHOO.util;b.Dom.Color={KEYWORDS:{black:"000",silver:"c0c0c0",gray:"808080",white:"fff",maroon:"800000",red:"f00",purple:"800080",fuchsia:"f0f",green:"008000",lime:"0f0",olive:"808000",yellow:"ff0",navy:"000080",blue:"00f",teal:"008080",aqua:"0ff"},re_RGB:/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(d){b.Dom.Color.re_RGB.test(d)||(d=b.Dom.Color.toHex(d));b.Dom.Color.re_hex.exec(d)&&
+(d="rgb("+[a(c.$1,16),a(c.$2,16),a(c.$3,16)].join(", ")+")");return d},toHex:function(a){a=b.Dom.Color.KEYWORDS[a]||a;if(b.Dom.Color.re_RGB.exec(a)){for(var a=[Number(c.$1).toString(16),Number(c.$2).toString(16),Number(c.$3).toString(16)],f=0;f<a.length;f++)2>a[f].length&&(a[f]="0"+a[f]);a=a.join("")}6>a.length&&(a=a.replace(b.Dom.Color.re_hex3,"$1$1"));"transparent"!==a&&0>a.indexOf("#")&&(a="#"+a);return a.toUpperCase()}}})();YAHOO.register("dom",YAHOO.util.Dom,{version:"2.9.0",build:"2800"});
+YAHOO.util.CustomEvent=function(a,c,b,d,f){this.type=a;this.scope=c||window;this.silent=b;this.fireOnce=f;this.fired=!1;this.firedWith=null;this.signature=d||YAHOO.util.CustomEvent.LIST;this.subscribers=[];"_YUICEOnSubscribe"!==a&&(this.subscribeEvent=new YAHOO.util.CustomEvent("_YUICEOnSubscribe",this,!0));this.lastError=null};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;
+YAHOO.util.CustomEvent.prototype={subscribe:function(a,c,b){if(!a)throw Error("Invalid callback for subscriber to '"+this.type+"'");this.subscribeEvent&&this.subscribeEvent.fire(a,c,b);a=new YAHOO.util.Subscriber(a,c,b);this.fireOnce&&this.fired?this.notify(a,this.firedWith):this.subscribers.push(a)},unsubscribe:function(a,c){if(!a)return this.unsubscribeAll();for(var b=!1,d=0,f=this.subscribers.length;d<f;++d){var g=this.subscribers[d];g&&g.contains(a,c)&&(this._delete(d),b=!0)}return b},fire:function(){this.lastError=
+null;var a=this.subscribers.length,c=[].slice.call(arguments,0),b=!0,d;if(this.fireOnce){if(this.fired)return!0;this.firedWith=c}this.fired=!0;if(!a&&this.silent)return!0;var f=this.subscribers.slice();for(d=0;d<a;++d){var g=f[d];if(g&&g.fn&&(b=this.notify(g,c),!1===b))break}return!1!==b},notify:function(a,c){var b,d=null,f=a.getScope(this.scope),g=YAHOO.util.Event.throwErrors;if(this.signature==YAHOO.util.CustomEvent.FLAT){0<c.length&&(d=c[0]);try{b=a.fn.call(f,d,a.obj)}catch(j){if(this.lastError=
+j,g)throw j;}}else try{b=a.fn.call(f,this.type,c,a.obj)}catch(h){if(this.lastError=h,g)throw h;}return b},unsubscribeAll:function(){var a=this.subscribers.length,c;for(c=a-1;-1<c;c--)this._delete(c);this.subscribers=[];return a},_delete:function(a){var c=this.subscribers[a];c&&(delete c.fn,delete c.obj);this.subscribers.splice(a,1)},toString:function(){return"CustomEvent: '"+this.type+"', context: "+this.scope}};
+YAHOO.util.Subscriber=function(a,c,b){this.fn=a;this.obj=YAHOO.lang.isUndefined(c)?null:c;this.overrideContext=b};YAHOO.util.Subscriber.prototype.getScope=function(a){return this.overrideContext?!0===this.overrideContext?this.obj:this.overrideContext:a};YAHOO.util.Subscriber.prototype.contains=function(a,c){return c?this.fn==a&&this.obj==c:this.fn==a};YAHOO.util.Subscriber.prototype.toString=function(){return"Subscriber { obj: "+this.obj+", overrideContext: "+(this.overrideContext||"no")+" }"};
+YAHOO.util.Event||(YAHOO.util.Event=function(){var a=!1,c=[],b=[],d=0,f=[],g=0,j={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9},h=YAHOO.env.ua.ie;return{POLL_RETRYS:500,POLL_INTERVAL:40,EL:0,TYPE:1,FN:2,WFN:3,UNLOAD_OBJ:3,ADJ_SCOPE:4,OBJ:5,OVERRIDE:6,CAPTURE:7,lastError:null,isSafari:YAHOO.env.ua.webkit,webkit:YAHOO.env.ua.webkit,isIE:h,_interval:null,_dri:null,_specialTypes:{focusin:h?"focusin":"focus",focusout:h?"focusout":"blur"},DOMReady:!1,throwErrors:!1,startInterval:function(){this._interval||
+(this._interval=YAHOO.lang.later(this.POLL_INTERVAL,this,this._tryPreloadAttach,null,!0))},onAvailable:function(e,a,b,c,g){for(var e=YAHOO.lang.isString(e)?[e]:e,h=0;h<e.length;h+=1)f.push({id:e[h],fn:a,obj:b,overrideContext:c,checkReady:g});d=this.POLL_RETRYS;this.startInterval()},onContentReady:function(e,a,b,d){this.onAvailable(e,a,b,d,!0)},onDOMReady:function(){this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent,arguments)},_addListener:function(e,a,d,f,g,h){if(!d||!d.call)return!1;if(this._isValidCollection(e)){for(var j=
+!0,o=0,m=e.length;o<m;++o)j=this.on(e[o],a,d,f,g)&&j;return j}if(YAHOO.lang.isString(e))if(j=this.getEl(e))e=j;else return this.onAvailable(e,function(){YAHOO.util.Event._addListener(e,a,d,f,g,h)}),!0;if(!e)return!1;if("unload"==a&&f!==this)return b[b.length]=[e,a,d,f,g],!0;var r=e;g&&(r=!0===g?f:g);j=function(a){return d.call(r,YAHOO.util.Event.getEvent(a,e),f)};c[c.length]=[e,a,d,j,r,f,g,h];try{this._simpleAdd(e,a,j,h)}catch(s){return this.lastError=s,this.removeListener(e,a,d),!1}return!0},_getType:function(e){return this._specialTypes[e]||
+e},addListener:function(e,a,b,d,f){var c=("focusin"==a||"focusout"==a)&&!YAHOO.env.ua.ie?!0:!1;return this._addListener(e,this._getType(a),b,d,f,c)},addFocusListener:function(e,a,b,d){return this.on(e,"focusin",a,b,d)},removeFocusListener:function(e,a){return this.removeListener(e,"focusin",a)},addBlurListener:function(e,a,b,d){return this.on(e,"focusout",a,b,d)},removeBlurListener:function(e,a){return this.removeListener(e,"focusout",a)},removeListener:function(e,a,d,f){var g,a=this._getType(a);
+if("string"==typeof e)e=this.getEl(e);else if(this._isValidCollection(e)){f=!0;for(g=e.length-1;-1<g;g--)f=this.removeListener(e[g],a,d)&&f;return f}if(!d||!d.call)return this.purgeElement(e,!1,a);if("unload"==a){for(g=b.length-1;-1<g;g--)if((f=b[g])&&f[0]==e&&f[1]==a&&f[2]==d)return b.splice(g,1),!0;return!1}g=null;"undefined"===typeof f&&(f=this._getCacheIndex(c,e,a,d));0<=f&&(g=c[f]);if(!e||!g)return!1;d=!0===g[this.CAPTURE]?!0:!1;try{this._simpleRemove(e,a,g[this.WFN],d)}catch(h){return this.lastError=
+h,!1}delete c[f][this.WFN];delete c[f][this.FN];c.splice(f,1);return!0},getTarget:function(e){return this.resolveTextNode(e.target||e.srcElement)},resolveTextNode:function(e){try{if(e&&3==e.nodeType)return e.parentNode}catch(a){return null}return e},getPageX:function(e){var a=e.pageX;!a&&0!==a&&(a=e.clientX||0,this.isIE&&(a+=this._getScrollLeft()));return a},getPageY:function(e){var a=e.pageY;!a&&0!==a&&(a=e.clientY||0,this.isIE&&(a+=this._getScrollTop()));return a},getXY:function(e){return[this.getPageX(e),
+this.getPageY(e)]},getRelatedTarget:function(e){var a=e.relatedTarget;a||("mouseout"==e.type?a=e.toElement:"mouseover"==e.type&&(a=e.fromElement));return this.resolveTextNode(a)},getTime:function(e){if(!e.time){var a=(new Date).getTime();try{e.time=a}catch(b){return this.lastError=b,a}}return e.time},stopEvent:function(e){this.stopPropagation(e);this.preventDefault(e)},stopPropagation:function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},preventDefault:function(e){e.preventDefault?
+e.preventDefault():e.returnValue=!1},getEvent:function(e){e=e||window.event;if(!e)for(var a=this.getEvent.caller;a&&!((e=a.arguments[0])&&Event==e.constructor);)a=a.caller;return e},getCharCode:function(e){e=e.keyCode||e.charCode||0;YAHOO.env.ua.webkit&&e in j&&(e=j[e]);return e},_getCacheIndex:function(e,a,b,d){for(var f=0,c=e.length;f<c;f+=1){var g=e[f];if(g&&g[this.FN]==d&&g[this.EL]==a&&g[this.TYPE]==b)return f}return-1},generateId:function(e){var a=e.id;a||(a="yuievtautoid-"+g,++g,e.id=a);return a},
+_isValidCollection:function(e){try{return e&&"string"!==typeof e&&e.length&&!e.tagName&&!e.alert&&"undefined"!==typeof e[0]}catch(a){return!1}},elCache:{},getEl:function(e){return"string"===typeof e?document.getElementById(e):e},clearCache:function(){},DOMReadyEvent:new YAHOO.util.CustomEvent("DOMReady",YAHOO,0,0,1),_load:function(){if(!a){a=!0;var e=YAHOO.util.Event;e._ready();e._tryPreloadAttach()}},_ready:function(){var e=YAHOO.util.Event;e.DOMReady||(e.DOMReady=!0,e.DOMReadyEvent.fire(),e._simpleRemove(document,
+"DOMContentLoaded",e._ready))},_tryPreloadAttach:function(){if(0===f.length)d=0,this._interval&&(this._interval.cancel(),this._interval=null);else if(!this.locked)if(this.isIE&&!this.DOMReady)this.startInterval();else{this.locked=!0;var e=!a;e||(e=0<d&&0<f.length);var i=[],b=function(e,a){var i=e;a.overrideContext&&(i=!0===a.overrideContext?a.obj:a.overrideContext);a.fn.call(i,a.obj)},c,g,h,j,o=[];c=0;for(g=f.length;c<g;c+=1)if(h=f[c])if(j=this.getEl(h.id))if(h.checkReady){if(a||j.nextSibling||!e)o.push(h),
+f[c]=null}else b(j,h),f[c]=null;else i.push(h);c=0;for(g=o.length;c<g;c+=1)h=o[c],b(this.getEl(h.id),h);d--;if(e){for(c=f.length-1;-1<c;c--)h=f[c],(!h||!h.id)&&f.splice(c,1);this.startInterval()}else this._interval&&(this._interval.cancel(),this._interval=null);this.locked=!1}},purgeElement:function(e,a,b){var e=YAHOO.lang.isString(e)?this.getEl(e):e,d=this.getListeners(e,b),f;if(d)for(f=d.length-1;-1<f;f--){var c=d[f];this.removeListener(e,c.type,c.fn)}if(a&&e&&e.childNodes){f=0;for(d=e.childNodes.length;f<
+d;++f)this.purgeElement(e.childNodes[f],a,b)}},getListeners:function(e,a){var d=[],f;a?"unload"===a?f=[b]:(a=this._getType(a),f=[c]):f=[c,b];for(var g=YAHOO.lang.isString(e)?this.getEl(e):e,h=0;h<f.length;h+=1){var j=f[h];if(j)for(var o=0,m=j.length;o<m;++o){var r=j[o];r&&(r[this.EL]===g&&(!a||a===r[this.TYPE]))&&d.push({type:r[this.TYPE],fn:r[this.FN],obj:r[this.OBJ],adjust:r[this.OVERRIDE],scope:r[this.ADJ_SCOPE],index:o})}}return d.length?d:null},_unload:function(e){var a=YAHOO.util.Event,d,f,
+g,h=b.slice(),j;d=0;for(g=b.length;d<g;++d)if(f=h[d]){try{j=window,f[a.ADJ_SCOPE]&&(j=!0===f[a.ADJ_SCOPE]?f[a.UNLOAD_OBJ]:f[a.ADJ_SCOPE]),f[a.FN].call(j,a.getEvent(e,f[a.EL]),f[a.UNLOAD_OBJ])}catch(o){}h[d]=null}b=null;if(c)for(e=c.length-1;-1<e;e--)if(f=c[e])try{a.removeListener(f[a.EL],f[a.TYPE],f[a.FN],e)}catch(m){}try{a._simpleRemove(window,"unload",a._unload),a._simpleRemove(window,"load",a._load)}catch(r){}},_getScrollLeft:function(){return this._getScroll()[1]},_getScrollTop:function(){return this._getScroll()[0]},
+_getScroll:function(){var e=document.documentElement,a=document.body;return e&&(e.scrollTop||e.scrollLeft)?[e.scrollTop,e.scrollLeft]:a?[a.scrollTop,a.scrollLeft]:[0,0]},regCE:function(){},_simpleAdd:function(){return window.addEventListener?function(e,a,b,d){e.addEventListener(a,b,d)}:window.attachEvent?function(e,a,b){e.attachEvent("on"+a,b)}:function(){}}(),_simpleRemove:function(){return window.removeEventListener?function(e,a,b,d){e.removeEventListener(a,b,d)}:window.detachEvent?function(e,a,
+b){e.detachEvent("on"+a,b)}:function(){}}()}}(),function(){var a=YAHOO.util.Event;a.on=a.addListener;a.onFocus=a.addFocusListener;a.onBlur=a.addBlurListener;if(a.isIE)if(self!==self.top)document.onreadystatechange=function(){"complete"==document.readyState&&(document.onreadystatechange=null,a._ready())};else{YAHOO.util.Event.onDOMReady(YAHOO.util.Event._tryPreloadAttach,YAHOO.util.Event,!0);var c=document.createElement("p");a._dri=setInterval(function(){try{c.doScroll("left"),clearInterval(a._dri),
+a._dri=null,a._ready(),c=null}catch(b){}},a.POLL_INTERVAL)}else a.webkit&&525>a.webkit?a._dri=setInterval(function(){var b=document.readyState;if("loaded"==b||"complete"==b)clearInterval(a._dri),a._dri=null,a._ready()},a.POLL_INTERVAL):a._simpleAdd(document,"DOMContentLoaded",a._ready);a._simpleAdd(window,"load",a._load);a._simpleAdd(window,"unload",a._unload);a._tryPreloadAttach()}());YAHOO.util.EventProvider=function(){};
+YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(a,c,b,d){this.__yui_events=this.__yui_events||{};var f=this.__yui_events[a];if(f)f.subscribe(c,b,d);else{f=this.__yui_subscribers=this.__yui_subscribers||{};f[a]||(f[a]=[]);f[a].push({fn:c,obj:b,overrideContext:d})}},unsubscribe:function(a,c,b){var d=this.__yui_events=this.__yui_events||{};if(a){if(d=d[a])return d.unsubscribe(c,b)}else{var a=true,f;for(f in d)YAHOO.lang.hasOwnProperty(d,f)&&(a=a&&d[f].unsubscribe(c,
+b));return a}return false},unsubscribeAll:function(a){return this.unsubscribe(a)},createEvent:function(a,c){this.__yui_events=this.__yui_events||{};var b=c||{},d=this.__yui_events,f;if(!d[a]){f=new YAHOO.util.CustomEvent(a,b.scope||this,b.silent,YAHOO.util.CustomEvent.FLAT,b.fireOnce);d[a]=f;b.onSubscribeCallback&&f.subscribeEvent.subscribe(b.onSubscribeCallback);this.__yui_subscribers=this.__yui_subscribers||{};if(b=this.__yui_subscribers[a])for(var g=0;g<b.length;++g)f.subscribe(b[g].fn,b[g].obj,
+b[g].overrideContext)}return d[a]},fireEvent:function(a){this.__yui_events=this.__yui_events||{};var c=this.__yui_events[a];if(!c)return null;for(var b=[],d=1;d<arguments.length;++d)b.push(arguments[d]);return c.fire.apply(c,b)},hasEvent:function(a){return this.__yui_events&&this.__yui_events[a]?true:false}};
+(function(){var a=YAHOO.util.Event,c=YAHOO.lang;YAHOO.util.KeyListener=function(b,f,g,j){function h(b){if(!f.shift)f.shift=false;if(!f.alt)f.alt=false;if(!f.ctrl)f.ctrl=false;if(b.shiftKey==f.shift&&b.altKey==f.alt&&b.ctrlKey==f.ctrl){var d,c=f.keys,g;if(YAHOO.lang.isArray(c))for(var h=0;h<c.length;h++){d=c[h];g=a.getCharCode(b);if(d==g){e.fire(g,b);break}}else{g=a.getCharCode(b);c==g&&e.fire(g,b)}}}if(!j)j=YAHOO.util.KeyListener.KEYDOWN;var e=new YAHOO.util.CustomEvent("keyPressed");this.enabledEvent=
+new YAHOO.util.CustomEvent("enabled");this.disabledEvent=new YAHOO.util.CustomEvent("disabled");c.isString(b)&&(b=document.getElementById(b));c.isFunction(g)?e.subscribe(g):e.subscribe(g.fn,g.scope,g.correctScope);this.enable=function(){if(!this.enabled){a.on(b,j,h);this.enabledEvent.fire(f)}this.enabled=true};this.disable=function(){if(this.enabled){a.removeListener(b,j,h);this.disabledEvent.fire(f)}this.enabled=false};this.toString=function(){return"KeyListener ["+f.keys+"] "+b.tagName+(b.id?"["+
+b.id+"]":"")}};var b=YAHOO.util.KeyListener;b.KEYDOWN="keydown";b.KEYUP="keyup";b.KEY={ALT:18,BACK_SPACE:8,CAPS_LOCK:20,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,META:224,NUM_LOCK:144,PAGE_DOWN:34,PAGE_UP:33,PAUSE:19,PRINTSCREEN:44,RIGHT:39,SCROLL_LOCK:145,SHIFT:16,SPACE:32,TAB:9,UP:38}})();YAHOO.register("event",YAHOO.util.Event,{version:"2.9.0",build:"2800"});
+YAHOO.util.Connect={_msxml_progid:["Microsoft.XMLHTTP","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP"],_http_headers:{},_has_http_headers:!1,_use_default_post_header:!0,_default_post_header:"application/x-www-form-urlencoded; charset=UTF-8",_default_form_header:"application/x-www-form-urlencoded",_use_default_xhr_header:!0,_default_xhr_header:"XMLHttpRequest",_has_default_headers:!0,_isFormSubmit:!1,_default_headers:{},_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,startEvent:new YAHOO.util.CustomEvent("start"),
+completeEvent:new YAHOO.util.CustomEvent("complete"),successEvent:new YAHOO.util.CustomEvent("success"),failureEvent:new YAHOO.util.CustomEvent("failure"),abortEvent:new YAHOO.util.CustomEvent("abort"),_customEvents:{onStart:["startEvent","start"],onComplete:["completeEvent","complete"],onSuccess:["successEvent","success"],onFailure:["failureEvent","failure"],onUpload:["uploadEvent","upload"],onAbort:["abortEvent","abort"]},setProgId:function(a){this._msxml_progid.unshift(a)},setDefaultPostHeader:function(a){if(typeof a==
+"string"){this._default_post_header=a;this._use_default_post_header=true}else if(typeof a=="boolean")this._use_default_post_header=a},setDefaultXhrHeader:function(a){typeof a=="string"?this._default_xhr_header=a:this._use_default_xhr_header=a},setPollingInterval:function(a){if(typeof a=="number"&&isFinite(a))this._polling_interval=a},createXhrObject:function(a){var c,b,d;try{b=new XMLHttpRequest;c={conn:b,tId:a,xhr:true}}catch(f){for(d=0;d<this._msxml_progid.length;++d)try{b=new ActiveXObject(this._msxml_progid[d]);
+c={conn:b,tId:a,xhr:true};break}catch(g){}}finally{return c}},getConnectionObject:function(a){var c,b=this._transaction_id;try{if(a){c={tId:b};if(a==="xdr"){c.conn=this._transport;c.xdr=true}else if(a==="upload")c.upload=true}else c=this.createXhrObject(b);c&&this._transaction_id++}catch(d){}return c},asyncRequest:function(a,c,b,d){var f=b&&b.argument?b.argument:null,g=this,j,h;this._isFileUpload?h="upload":b&&b.xdr&&(h="xdr");j=this.getConnectionObject(h);if(!j)return null;b&&b.customevents&&this.initCustomEvents(j,
+b);if(this._isFormSubmit){if(this._isFileUpload){window.setTimeout(function(){g.uploadFile(j,b,c,d)},10);return j}a.toUpperCase()=="GET"?this._sFormData.length!==0&&(c=c+((c.indexOf("?")==-1?"?":"&")+this._sFormData)):a.toUpperCase()=="POST"&&(d=d?this._sFormData+"&"+d:this._sFormData)}a.toUpperCase()=="GET"&&(b&&b.cache===false)&&(c=c+((c.indexOf("?")==-1?"?":"&")+"rnd="+(new Date).valueOf().toString()));this._use_default_xhr_header&&(this._default_headers["X-Requested-With"]||this.initHeader("X-Requested-With",
+this._default_xhr_header,true));a.toUpperCase()==="POST"&&this._use_default_post_header&&this._isFormSubmit===false&&this.initHeader("Content-Type",this._default_post_header);if(j.xdr){this.xdr(j,a,c,b,d);return j}j.conn.open(a,c,true);(this._has_default_headers||this._has_http_headers)&&this.setHeader(j);this.handleReadyState(j,b);j.conn.send(d||"");this._isFormSubmit===true&&this.resetFormState();this.startEvent.fire(j,f);j.startEvent&&j.startEvent.fire(j,f);return j},initCustomEvents:function(a,
+c){for(var b in c.customevents)if(this._customEvents[b][0]){a[this._customEvents[b][0]]=new YAHOO.util.CustomEvent(this._customEvents[b][1],c.scope?c.scope:null);a[this._customEvents[b][0]].subscribe(c.customevents[b])}},handleReadyState:function(a,c){var b=this,d=c&&c.argument?c.argument:null;c&&c.timeout&&(this._timeOut[a.tId]=window.setTimeout(function(){b.abort(a,c,true)},c.timeout));this._poll[a.tId]=window.setInterval(function(){if(a.conn&&a.conn.readyState===4){window.clearInterval(b._poll[a.tId]);
+delete b._poll[a.tId];if(c&&c.timeout){window.clearTimeout(b._timeOut[a.tId]);delete b._timeOut[a.tId]}b.completeEvent.fire(a,d);a.completeEvent&&a.completeEvent.fire(a,d);b.handleTransactionResponse(a,c)}},this._polling_interval)},handleTransactionResponse:function(a,c,b){var d,f=c&&c.argument?c.argument:null,g=a.r&&a.r.statusText==="xdr:success"?true:false,j=a.r&&a.r.statusText==="xdr:failure"?true:false;try{d=a.conn.status!==void 0&&a.conn.status!==0||g?a.conn.status:j&&!b?0:13030}catch(h){d=13030}if(d>=
+200&&d<300||d===1223||g){b=a.xdr?a.r:this.createResponseObject(a,f);c&&c.success&&(c.scope?c.success.apply(c.scope,[b]):c.success(b));this.successEvent.fire(b);a.successEvent&&a.successEvent.fire(b)}else{switch(d){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:b=this.createExceptionObject(a.tId,f,b?b:false);c&&c.failure&&(c.scope?c.failure.apply(c.scope,[b]):c.failure(b));break;default:b=a.xdr?a.response:this.createResponseObject(a,f);c&&c.failure&&(c.scope?c.failure.apply(c.scope,
+[b]):c.failure(b))}this.failureEvent.fire(b);a.failureEvent&&a.failureEvent.fire(b)}this.releaseObject(a)},createResponseObject:function(a,c){var b={},d={},f,g,j,h;try{g=a.conn.getAllResponseHeaders();j=g.split("\n");for(f=0;f<j.length;f++){h=j[f].indexOf(":");h!=-1&&(d[j[f].substring(0,h)]=YAHOO.lang.trim(j[f].substring(h+2)))}}catch(e){}b.tId=a.tId;b.status=a.conn.status==1223?204:a.conn.status;b.statusText=a.conn.status==1223?"No Content":a.conn.statusText;b.getResponseHeader=d;b.getAllResponseHeaders=
+g;b.responseText=a.conn.responseText;b.responseXML=a.conn.responseXML;if(c)b.argument=c;return b},createExceptionObject:function(a,c,b){var d={};d.tId=a;if(b){d.status=-1;d.statusText="transaction aborted"}else{d.status=0;d.statusText="communication failure"}if(c)d.argument=c;return d},initHeader:function(a,c,b){(b?this._default_headers:this._http_headers)[a]=c;b?this._has_default_headers=true:this._has_http_headers=true},setHeader:function(a){var c;if(this._has_default_headers)for(c in this._default_headers)YAHOO.lang.hasOwnProperty(this._default_headers,
+c)&&a.conn.setRequestHeader(c,this._default_headers[c]);if(this._has_http_headers){for(c in this._http_headers)YAHOO.lang.hasOwnProperty(this._http_headers,c)&&a.conn.setRequestHeader(c,this._http_headers[c]);this._http_headers={};this._has_http_headers=false}},resetDefaultHeaders:function(){this._default_headers={};this._has_default_headers=false},abort:function(a,c,b){var d,f=c&&c.argument?c.argument:null,a=a||{};if(a.conn)if(a.xhr){if(this.isCallInProgress(a)){a.conn.abort();window.clearInterval(this._poll[a.tId]);
+delete this._poll[a.tId];if(b){window.clearTimeout(this._timeOut[a.tId]);delete this._timeOut[a.tId]}d=true}}else{if(a.xdr){a.conn.abort(a.tId);d=true}}else if(a.upload){var g=document.getElementById("yuiIO"+a.tId);if(g){YAHOO.util.Event.removeListener(g,"load");document.body.removeChild(g);if(b){window.clearTimeout(this._timeOut[a.tId]);delete this._timeOut[a.tId]}d=true}}else d=false;if(d===true){this.abortEvent.fire(a,f);a.abortEvent&&a.abortEvent.fire(a,f);this.handleTransactionResponse(a,c,true)}return d},
+isCallInProgress:function(a){a=a||{};return a.xhr&&a.conn?a.conn.readyState!==4&&a.conn.readyState!==0:a.xdr&&a.conn?a.conn.isCallInProgress(a.tId):a.upload===true?document.getElementById("yuiIO"+a.tId)?true:false:false},releaseObject:function(a){if(a&&a.conn)a.conn=null}};
+(function(){function a(a){var a='<object id="YUIConnectionSwf" type="application/x-shockwave-flash" data="'+a+'" width="0" height="0"><param name="movie" value="'+a+'"><param name="allowScriptAccess" value="always"></object>',b=document.createElement("div");document.body.appendChild(b);b.innerHTML=a}var c=YAHOO.util.Connect,b={};c.xdr=function(a,f,c,j,h){b[parseInt(a.tId)]={o:a,c:j};if(h){j.method=f;j.data=h}a.conn.send(c,j,a.tId)};c.swf=a;c.transport=function(b){a(b);c._transport=document.getElementById("YUIConnectionSwf")};
+c.xdrReadyEvent=new YAHOO.util.CustomEvent("xdrReady");c.xdrReady=function(){c.xdrReadyEvent.fire()};c.handleXdrResponse=function(a){var f=b[a.tId].o,g=b[a.tId].c;if(a.statusText==="xdr:start"){if(f){c.startEvent.fire(f,g.argument);f.startEvent&&f.startEvent.fire(f,g.argument)}}else{a.responseText=decodeURI(a.responseText);f.r=a;if(g.argument)f.r.argument=g.argument;this.handleTransactionResponse(f,g,a.statusText==="xdr:abort"?true:false);delete b[a.tId]}}})();
+(function(){var a=YAHOO.util.Connect,c=YAHOO.util.Event,b=document.documentMode?document.documentMode:false;a._isFileUpload=false;a._formNode=null;a._sFormData=null;a._submitElementValue=null;a.uploadEvent=new YAHOO.util.CustomEvent("upload");a._hasSubmitListener=function(){if(c){c.addListener(document,"click",function(b){var b=c.getTarget(b),f=b.nodeName.toLowerCase();if((f==="input"||f==="button")&&b.type&&b.type.toLowerCase()=="submit")a._submitElementValue=encodeURIComponent(b.name)+"="+encodeURIComponent(b.value)});
+return true}return false}();a.setForm=function(a,b,c){var j,h=false,e=[],i=0,k,l,q,p;this.resetFormState();if(typeof a=="string")a=document.getElementById(a)||document.forms[a];else if(typeof a!="object")return;if(b){this.createFrame(c?c:null);this._isFileUpload=this._isFormSubmit=true;this._formNode=a}else{k=0;for(l=a.elements.length;k<l;++k){b=a.elements[k];j=b.disabled;c=b.name;if(!j&&c){c=encodeURIComponent(c)+"=";j=encodeURIComponent(b.value);switch(b.type){case "select-one":if(b.selectedIndex>
+-1){p=b.options[b.selectedIndex];e[i++]=c+encodeURIComponent(p.attributes.value&&p.attributes.value.specified?p.value:p.text)}break;case "select-multiple":if(b.selectedIndex>-1){j=b.selectedIndex;for(q=b.options.length;j<q;++j){p=b.options[j];p.selected&&(e[i++]=c+encodeURIComponent(p.attributes.value&&p.attributes.value.specified?p.value:p.text))}}break;case "radio":case "checkbox":b.checked&&(e[i++]=c+j);break;case "file":case void 0:case "reset":case "button":break;case "submit":if(h===false){if(this._hasSubmitListener&&
+this._submitElementValue)e[i++]=this._submitElementValue;h=true}break;default:e[i++]=c+j}}}this._isFormSubmit=true;this._sFormData=e.join("&");this.initHeader("Content-Type",this._default_form_header);return this._sFormData}};a.resetFormState=function(){this._isFileUpload=this._isFormSubmit=false;this._formNode=null;this._sFormData=""};a.createFrame=function(a){var f="yuiIO"+this._transaction_id,c=b===9?true:false;if(YAHOO.env.ua.ie&&!c){c=document.createElement('<iframe id="'+f+'" name="'+f+'" />');
+if(typeof a=="boolean")c.src="javascript:false"}else{c=document.createElement("iframe");c.id=f;c.name=f}c.style.position="absolute";c.style.top="-1000px";c.style.left="-1000px";document.body.appendChild(c)};a.appendPostData=function(a){var b=[],a=a.split("&"),c,j;for(c=0;c<a.length;c++){j=a[c].indexOf("=");if(j!=-1){b[c]=document.createElement("input");b[c].type="hidden";b[c].name=decodeURIComponent(a[c].substring(0,j));b[c].value=decodeURIComponent(a[c].substring(j+1));this._formNode.appendChild(b[c])}}return b};
+a.uploadFile=function(a,f,g,j){var h="yuiIO"+a.tId,e=document.getElementById(h),i=b>=8?true:false,k=this,l=f&&f.argument?f.argument:null,q,p,n,o,m;o={action:this._formNode.getAttribute("action"),method:this._formNode.getAttribute("method"),target:this._formNode.getAttribute("target")};this._formNode.setAttribute("action",g);this._formNode.setAttribute("method","POST");this._formNode.setAttribute("target",h);YAHOO.env.ua.ie&&!i?this._formNode.setAttribute("encoding","multipart/form-data"):this._formNode.setAttribute("enctype",
+"multipart/form-data");j&&(q=this.appendPostData(j));this._formNode.submit();this.startEvent.fire(a,l);a.startEvent&&a.startEvent.fire(a,l);f&&f.timeout&&(this._timeOut[a.tId]=window.setTimeout(function(){k.abort(a,f,true)},f.timeout));if(q&&q.length>0)for(g=0;g<q.length;g++)this._formNode.removeChild(q[g]);for(p in o)YAHOO.lang.hasOwnProperty(o,p)&&(o[p]?this._formNode.setAttribute(p,o[p]):this._formNode.removeAttribute(p));this.resetFormState();m=function(){var b,i,g;if(f&&f.timeout){window.clearTimeout(k._timeOut[a.tId]);
+delete k._timeOut[a.tId]}k.completeEvent.fire(a,l);a.completeEvent&&a.completeEvent.fire(a,l);n={tId:a.tId,argument:l};try{b=e.contentWindow.document.getElementsByTagName("body")[0];i=e.contentWindow.document.getElementsByTagName("pre")[0];b&&(g=i?i.textContent?i.textContent:i.innerText:b.textContent?b.textContent:b.innerText);n.responseText=g;n.responseXML=e.contentWindow.document.XMLDocument?e.contentWindow.document.XMLDocument:e.contentWindow.document}catch(h){}f&&f.upload&&(f.scope?f.upload.apply(f.scope,
+[n]):f.upload(n));k.uploadEvent.fire(n);a.uploadEvent&&a.uploadEvent.fire(n);c.removeListener(e,"load",m);setTimeout(function(){document.body.removeChild(e);k.releaseObject(a)},100)};c.addListener(e,"load",m)}})();YAHOO.register("connection",YAHOO.util.Connect,{version:"2.9.0",build:"2800"});
+(function(){var a=YAHOO.util,c=function(a,d,f,c){this.init(a,d,f,c)};c.NAME="Anim";c.prototype={toString:function(){var a=this.getEl()||{};return this.constructor.NAME+": "+(a.id||a.tagName)},patterns:{noNegatives:/width|height|opacity|padding/i,offsetAttribute:/^((width|height)|(top|left))$/,defaultUnit:/width|height|top$|bottom$|left$|right$/i,offsetUnit:/\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i},doMethod:function(a,d,f){return this.method(this.currentFrame,d,f-d,this.totalFrames)},setAttribute:function(b,
+d,f){var c=this.getEl();this.patterns.noNegatives.test(b)&&(d=d>0?d:0);b in c&&!("style"in c&&b in c.style)?c[b]=d:a.Dom.setStyle(c,b,d+f)},getAttribute:function(b){var d=this.getEl(),f=a.Dom.getStyle(d,b);if(f!=="auto"&&!this.patterns.offsetUnit.test(f))return parseFloat(f);var c=this.patterns.offsetAttribute.exec(b)||[],j=!!c[3],h=!!c[2];"style"in d?f=h||a.Dom.getStyle(d,"position")=="absolute"&&j?d["offset"+c[0].charAt(0).toUpperCase()+c[0].substr(1)]:0:b in d&&(f=d[b]);return f},getDefaultUnit:function(a){return this.patterns.defaultUnit.test(a)?
+"px":""},setRuntimeAttribute:function(a){var d,f,c=this.attributes;this.runtimeAttributes[a]={};var j=function(e){return typeof e!=="undefined"};if(!j(c[a].to)&&!j(c[a].by))return false;d=j(c[a].from)?c[a].from:this.getAttribute(a);if(j(c[a].to))f=c[a].to;else if(j(c[a].by))if(d.constructor==Array){f=[];for(var h=0,e=d.length;h<e;++h)f[h]=d[h]+c[a].by[h]*1}else f=d+c[a].by*1;this.runtimeAttributes[a].start=d;this.runtimeAttributes[a].end=f;this.runtimeAttributes[a].unit=j(c[a].unit)?c[a].unit:this.getDefaultUnit(a);
+return true},init:function(b,d,f,c){var j=false,h=null,e=0,b=a.Dom.get(b);this.attributes=d||{};this.duration=!YAHOO.lang.isUndefined(f)?f:1;this.method=c||a.Easing.easeNone;this.useSeconds=true;this.currentFrame=0;this.totalFrames=a.AnimMgr.fps;this.setEl=function(e){b=a.Dom.get(e)};this.getEl=function(){return b};this.isAnimated=function(){return j};this.getStartTime=function(){return h};this.runtimeAttributes={};this.animate=function(){if(this.isAnimated())return false;this.currentFrame=0;this.totalFrames=
+this.useSeconds?Math.ceil(a.AnimMgr.fps*this.duration):this.duration;if(this.duration===0&&this.useSeconds)this.totalFrames=1;a.AnimMgr.registerElement(this);return true};this.stop=function(e){if(!this.isAnimated())return false;if(e){this.currentFrame=this.totalFrames;this._onTween.fire()}a.AnimMgr.stop(this)};this._handleStart=function(){this.onStart.fire();this.runtimeAttributes={};for(var a in this.attributes)this.attributes.hasOwnProperty(a)&&this.setRuntimeAttribute(a);j=true;e=0;h=new Date};
+this._handleTween=function(){var a={duration:new Date-this.getStartTime(),currentFrame:this.currentFrame,toString:function(){return"duration: "+a.duration+", currentFrame: "+a.currentFrame}};this.onTween.fire(a);var b=this.runtimeAttributes,d;for(d in b)b.hasOwnProperty(d)&&this.setAttribute(d,this.doMethod(d,b[d].start,b[d].end),b[d].unit);this.afterTween.fire(a);e=e+1};this._handleComplete=function(){var a=(new Date-h)/1E3,b={duration:a,frames:e,fps:e/a,toString:function(){return"duration: "+b.duration+
+", frames: "+b.frames+", fps: "+b.fps}};j=false;e=0;this.onComplete.fire(b)};this._onStart=new a.CustomEvent("_start",this,true);this.onStart=new a.CustomEvent("start",this);this.onTween=new a.CustomEvent("tween",this);this.afterTween=new a.CustomEvent("afterTween",this);this._onTween=new a.CustomEvent("_tween",this,true);this.onComplete=new a.CustomEvent("complete",this);this._onComplete=new a.CustomEvent("_complete",this,true);this._onStart.subscribe(this._handleStart);this._onTween.subscribe(this._handleTween);
+this._onComplete.subscribe(this._handleComplete)}};a.Anim=c})();
+YAHOO.util.AnimMgr=new function(){var a=null,c=[],b=0;this.fps=1E3;this.delay=20;this.registerElement=function(e){c[c.length]=e;b=b+1;e._onStart.fire();this.start()};var d=[],f=false,g=function(){var e=d.shift();j.apply(YAHOO.util.AnimMgr,e);d.length&&arguments.callee()},j=function(e,a){a=a||h(e);if(!e.isAnimated()||a===-1)return false;e._onComplete.fire();c.splice(a,1);b=b-1;b<=0&&this.stop();return true};this.unRegister=function(){d.push(arguments);if(!f){f=true;g();f=false}};this.start=function(){a===
+null&&(a=setInterval(this.run,this.delay))};this.stop=function(e){if(e)this.unRegister(e);else{clearInterval(a);for(var e=0,i=c.length;e<i;++e)this.unRegister(c[0],0);c=[];a=null;b=0}};this.run=function(){for(var e=0,a=c.length;e<a;++e){var b=c[e];if(b&&b.isAnimated())if(b.currentFrame<b.totalFrames||b.totalFrames===null){b.currentFrame=b.currentFrame+1;if(b.useSeconds){var d=b,f=d.totalFrames,g=d.currentFrame,h=d.currentFrame*d.duration*1E3/d.totalFrames,j=new Date-d.getStartTime(),m=0,m=j<d.duration*
+1E3?Math.round((j/h-1)*d.currentFrame):f-(g+1);if(m>0&&isFinite(m)){d.currentFrame+m>=f&&(m=f-(g+1));d.currentFrame=d.currentFrame+m}}b._onTween.fire()}else YAHOO.util.AnimMgr.stop(b,e)}};var h=function(e){for(var a=0,b=c.length;a<b;++a)if(c[a]===e)return a;return-1};this._queue=c;this._getIndex=h};
+YAHOO.util.Bezier=new function(){this.getPosition=function(a,c){for(var b=a.length,d=[],f=0;f<b;++f)d[f]=[a[f][0],a[f][1]];for(var g=1;g<b;++g)for(f=0;f<b-g;++f){d[f][0]=(1-c)*d[f][0]+c*d[parseInt(f+1,10)][0];d[f][1]=(1-c)*d[f][1]+c*d[parseInt(f+1,10)][1]}return[d[0][0],d[0][1]]}};
+(function(){var a=function(b,d,c,h){a.superclass.constructor.call(this,b,d,c,h)};a.NAME="ColorAnim";a.DEFAULT_BGCOLOR="#fff";var c=YAHOO.util;YAHOO.extend(a,c.Anim);var b=a.superclass,d=a.prototype;d.patterns.color=/color$/i;d.patterns.rgb=/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;d.patterns.hex=/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;d.patterns.hex3=/^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;d.patterns.transparent=/^transparent|rgba\(0, 0, 0, 0\)$/;d.parseColor=function(a){if(a.length==
+3)return a;var b=this.patterns.hex.exec(a);if(b&&b.length==4)return[parseInt(b[1],16),parseInt(b[2],16),parseInt(b[3],16)];if((b=this.patterns.rgb.exec(a))&&b.length==4)return[parseInt(b[1],10),parseInt(b[2],10),parseInt(b[3],10)];return(b=this.patterns.hex3.exec(a))&&b.length==4?[parseInt(b[1]+b[1],16),parseInt(b[2]+b[2],16),parseInt(b[3]+b[3],16)]:null};d.getAttribute=function(d){var g=this.getEl();if(this.patterns.color.test(d)){var j=YAHOO.util.Dom.getStyle(g,d),h=this;if(this.patterns.transparent.test(j))j=
+(g=YAHOO.util.Dom.getAncestorBy(g,function(){return!h.patterns.transparent.test(j)}))?c.Dom.getStyle(g,d):a.DEFAULT_BGCOLOR}else j=b.getAttribute.call(this,d);return j};d.doMethod=function(a,d,c){var h;if(this.patterns.color.test(a)){h=[];for(var e=0,i=d.length;e<i;++e)h[e]=b.doMethod.call(this,a,d[e],c[e]);h="rgb("+Math.floor(h[0])+","+Math.floor(h[1])+","+Math.floor(h[2])+")"}else h=b.doMethod.call(this,a,d,c);return h};d.setRuntimeAttribute=function(a){b.setRuntimeAttribute.call(this,a);if(this.patterns.color.test(a)){var d=
+this.attributes,c=this.parseColor(this.runtimeAttributes[a].start),h=this.parseColor(this.runtimeAttributes[a].end);if(typeof d[a].to==="undefined"&&typeof d[a].by!=="undefined")for(var h=this.parseColor(d[a].by),d=0,e=c.length;d<e;++d)h[d]=c[d]+h[d];this.runtimeAttributes[a].start=c;this.runtimeAttributes[a].end=h}};c.ColorAnim=a})();
+YAHOO.util.Easing={easeNone:function(a,c,b,d){return b*a/d+c},easeIn:function(a,c,b,d){return b*(a=a/d)*a+c},easeOut:function(a,c,b,d){return-b*(a=a/d)*(a-2)+c},easeBoth:function(a,c,b,d){return(a=a/(d/2))<1?b/2*a*a+c:-b/2*(--a*(a-2)-1)+c},easeInStrong:function(a,c,b,d){return b*(a=a/d)*a*a*a+c},easeOutStrong:function(a,c,b,d){return-b*((a=a/d-1)*a*a*a-1)+c},easeBothStrong:function(a,c,b,d){return(a=a/(d/2))<1?b/2*a*a*a*a+c:-b/2*((a=a-2)*a*a*a-2)+c},elasticIn:function(a,c,b,d,f,g){if(a==0)return c;
+if((a=a/d)==1)return c+b;g||(g=d*0.3);if(!f||f<Math.abs(b)){f=b;b=g/4}else b=g/(2*Math.PI)*Math.asin(b/f);return-(f*Math.pow(2,10*(a=a-1))*Math.sin((a*d-b)*2*Math.PI/g))+c},elasticOut:function(a,c,b,d,f,g){if(a==0)return c;if((a=a/d)==1)return c+b;g||(g=d*0.3);if(!f||f<Math.abs(b))var f=b,j=g/4;else j=g/(2*Math.PI)*Math.asin(b/f);return f*Math.pow(2,-10*a)*Math.sin((a*d-j)*2*Math.PI/g)+b+c},elasticBoth:function(a,c,b,d,f,g){if(a==0)return c;if((a=a/(d/2))==2)return c+b;g||(g=d*0.3*1.5);if(!f||f<Math.abs(b))var f=
+b,j=g/4;else j=g/(2*Math.PI)*Math.asin(b/f);return a<1?-0.5*f*Math.pow(2,10*(a=a-1))*Math.sin((a*d-j)*2*Math.PI/g)+c:f*Math.pow(2,-10*(a=a-1))*Math.sin((a*d-j)*2*Math.PI/g)*0.5+b+c},backIn:function(a,c,b,d,f){typeof f=="undefined"&&(f=1.70158);return b*(a=a/d)*a*((f+1)*a-f)+c},backOut:function(a,c,b,d,f){typeof f=="undefined"&&(f=1.70158);return b*((a=a/d-1)*a*((f+1)*a+f)+1)+c},backBoth:function(a,c,b,d,f){typeof f=="undefined"&&(f=1.70158);return(a=a/(d/2))<1?b/2*a*a*(((f=f*1.525)+1)*a-f)+c:b/2*
+((a=a-2)*a*(((f=f*1.525)+1)*a+f)+2)+c},bounceIn:function(a,c,b,d){return b-YAHOO.util.Easing.bounceOut(d-a,0,b,d)+c},bounceOut:function(a,c,b,d){return(a=a/d)<1/2.75?b*7.5625*a*a+c:a<2/2.75?b*(7.5625*(a=a-1.5/2.75)*a+0.75)+c:a<2.5/2.75?b*(7.5625*(a=a-2.25/2.75)*a+0.9375)+c:b*(7.5625*(a=a-2.625/2.75)*a+0.984375)+c},bounceBoth:function(a,c,b,d){return a<d/2?YAHOO.util.Easing.bounceIn(a*2,0,b,d)*0.5+c:YAHOO.util.Easing.bounceOut(a*2-d,0,b,d)*0.5+b*0.5+c}};
+(function(){var a=function(b,d,e,i){b&&a.superclass.constructor.call(this,b,d,e,i)};a.NAME="Motion";var c=YAHOO.util;YAHOO.extend(a,c.ColorAnim);var b=a.superclass,d=a.prototype;d.patterns.points=/^points$/i;d.setAttribute=function(a,d,e){if(this.patterns.points.test(a)){e=e||"px";b.setAttribute.call(this,"left",d[0],e);b.setAttribute.call(this,"top",d[1],e)}else b.setAttribute.call(this,a,d,e)};d.getAttribute=function(a){return this.patterns.points.test(a)?[b.getAttribute.call(this,"left"),b.getAttribute.call(this,
+"top")]:b.getAttribute.call(this,a)};d.doMethod=function(a,d,e){var i=null;if(this.patterns.points.test(a)){d=this.method(this.currentFrame,0,100,this.totalFrames)/100;i=c.Bezier.getPosition(this.runtimeAttributes[a],d)}else i=b.doMethod.call(this,a,d,e);return i};d.setRuntimeAttribute=function(a){if(this.patterns.points.test(a)){var d=this.getEl(),e=this.attributes,i=e.points.control||[],k,l,q;if(i.length>0&&!(i[0]instanceof Array))i=[i];else{var p=[];l=0;for(q=i.length;l<q;++l)p[l]=i[l];i=p}c.Dom.getStyle(d,
+"position")=="static"&&c.Dom.setStyle(d,"position","relative");g(e.points.from)?c.Dom.setXY(d,e.points.from):c.Dom.setXY(d,c.Dom.getXY(d));d=this.getAttribute("points");if(g(e.points.to)){k=f.call(this,e.points.to,d);c.Dom.getXY(this.getEl());l=0;for(q=i.length;l<q;++l)i[l]=f.call(this,i[l],d)}else if(g(e.points.by)){k=[d[0]+e.points.by[0],d[1]+e.points.by[1]];l=0;for(q=i.length;l<q;++l)i[l]=[d[0]+i[l][0],d[1]+i[l][1]]}this.runtimeAttributes[a]=[d];i.length>0&&(this.runtimeAttributes[a]=this.runtimeAttributes[a].concat(i));
+this.runtimeAttributes[a][this.runtimeAttributes[a].length]=k}else b.setRuntimeAttribute.call(this,a)};var f=function(a,b){var e=c.Dom.getXY(this.getEl());return a=[a[0]-e[0]+b[0],a[1]-e[1]+b[1]]},g=function(a){return typeof a!=="undefined"};c.Motion=a})();
+(function(){var a=function(b,d,c,h){b&&a.superclass.constructor.call(this,b,d,c,h)};a.NAME="Scroll";var c=YAHOO.util;YAHOO.extend(a,c.ColorAnim);var b=a.superclass,d=a.prototype;d.doMethod=function(a,d,c){var h=null;return h=a=="scroll"?[this.method(this.currentFrame,d[0],c[0]-d[0],this.totalFrames),this.method(this.currentFrame,d[1],c[1]-d[1],this.totalFrames)]:b.doMethod.call(this,a,d,c)};d.getAttribute=function(a){var d=null,d=this.getEl();return d=a=="scroll"?[d.scrollLeft,d.scrollTop]:b.getAttribute.call(this,
+a)};d.setAttribute=function(a,d,c){var h=this.getEl();if(a=="scroll"){h.scrollLeft=d[0];h.scrollTop=d[1]}else b.setAttribute.call(this,a,d,c)};c.Scroll=a})();YAHOO.register("animation",YAHOO.util.Anim,{version:"2.9.0",build:"2800"});
+YAHOO.util.DragDropMgr||(YAHOO.util.DragDropMgr=function(){var a=YAHOO.util.Event,c=YAHOO.util.Dom;return{useShim:false,_shimActive:false,_shimState:false,_debugShim:false,_createShim:function(){var b=document.createElement("div");b.id="yui-ddm-shim";document.body.firstChild?document.body.insertBefore(b,document.body.firstChild):document.body.appendChild(b);b.style.display="none";b.style.backgroundColor="red";b.style.position="absolute";b.style.zIndex="99999";c.setStyle(b,"opacity","0");this._shim=
+b;a.on(b,"mouseup",this.handleMouseUp,this,true);a.on(b,"mousemove",this.handleMouseMove,this,true);a.on(window,"scroll",this._sizeShim,this,true)},_sizeShim:function(){if(this._shimActive){var a=this._shim;a.style.height=c.getDocumentHeight()+"px";a.style.width=c.getDocumentWidth()+"px";a.style.top="0";a.style.left="0"}},_activateShim:function(){if(this.useShim){this._shim||this._createShim();this._shimActive=true;var a=this._shim,d="0";this._debugShim&&(d=".5");c.setStyle(a,"opacity",d);this._sizeShim();
+a.style.display="block"}},_deactivateShim:function(){this._shim.style.display="none";this._shimActive=false},_shim:null,ids:{},handleIds:{},dragCurrent:null,dragOvers:{},deltaX:0,deltaY:0,preventDefault:true,stopPropagation:true,initialized:false,locked:false,interactionInfo:null,init:function(){this.initialized=true},POINT:0,INTERSECT:1,STRICT_INTERSECT:2,mode:0,_execOnAll:function(a,d){for(var f in this.ids)for(var c in this.ids[f]){var j=this.ids[f][c];this.isTypeOfDD(j)&&j[a].apply(j,d)}},_onLoad:function(){this.init();
+a.on(document,"mouseup",this.handleMouseUp,this,true);a.on(document,"mousemove",this.handleMouseMove,this,true);a.on(window,"unload",this._onUnload,this,true);a.on(window,"resize",this._onResize,this,true)},_onResize:function(){this._execOnAll("resetConstraints",[])},lock:function(){this.locked=true},unlock:function(){this.locked=false},isLocked:function(){return this.locked},locationCache:{},useCache:true,clickPixelThresh:3,clickTimeThresh:1E3,dragThreshMet:false,clickTimeout:null,startX:0,startY:0,
+fromTimeout:false,regDragDrop:function(a,d){this.initialized||this.init();this.ids[d]||(this.ids[d]={});this.ids[d][a.id]=a},removeDDFromGroup:function(a,d){this.ids[d]||(this.ids[d]={});var f=this.ids[d];f&&f[a.id]&&delete f[a.id]},_remove:function(a){for(var d in a.groups)if(d){var f=this.ids[d];f&&f[a.id]&&delete f[a.id]}delete this.handleIds[a.id]},regHandle:function(a,d){this.handleIds[a]||(this.handleIds[a]={});this.handleIds[a][d]=d},isDragDrop:function(a){return this.getDDById(a)?true:false},
+getRelated:function(a,d){var f=[],c;for(c in a.groups)for(var j in this.ids[c]){var h=this.ids[c][j];if(this.isTypeOfDD(h)&&(!d||h.isTarget))f[f.length]=h}return f},isLegalTarget:function(a,d){for(var f=this.getRelated(a,true),c=0,j=f.length;c<j;++c)if(f[c].id==d.id)return true;return false},isTypeOfDD:function(a){return a&&a.__ygDragDrop},isHandle:function(a,d){return this.handleIds[a]&&this.handleIds[a][d]},getDDById:function(a){for(var d in this.ids)if(this.ids[d][a])return this.ids[d][a];return null},
+handleMouseDown:function(a,d){this.currentTarget=YAHOO.util.Event.getTarget(a);this.dragCurrent=d;var f=d.getEl();this.startX=YAHOO.util.Event.getPageX(a);this.startY=YAHOO.util.Event.getPageY(a);this.deltaX=this.startX-f.offsetLeft;this.deltaY=this.startY-f.offsetTop;this.dragThreshMet=false;this.clickTimeout=setTimeout(function(){var a=YAHOO.util.DDM;a.startDrag(a.startX,a.startY);a.fromTimeout=true},this.clickTimeThresh)},startDrag:function(a,d){if(this.dragCurrent&&this.dragCurrent.useShim){this._shimState=
+this.useShim;this.useShim=true}this._activateShim();clearTimeout(this.clickTimeout);var f=this.dragCurrent;if(f&&f.events.b4StartDrag){f.b4StartDrag(a,d);f.fireEvent("b4StartDragEvent",{x:a,y:d})}if(f&&f.events.startDrag){f.startDrag(a,d);f.fireEvent("startDragEvent",{x:a,y:d})}this.dragThreshMet=true},handleMouseUp:function(a){if(this.dragCurrent){clearTimeout(this.clickTimeout);if(this.dragThreshMet){if(this.fromTimeout){this.fromTimeout=false;this.handleMouseMove(a)}this.fromTimeout=false;this.fireEvents(a,
+true)}this.stopDrag(a);this.stopEvent(a)}},stopEvent:function(a){this.stopPropagation&&YAHOO.util.Event.stopPropagation(a);this.preventDefault&&YAHOO.util.Event.preventDefault(a)},stopDrag:function(a,d){var f=this.dragCurrent;if(f&&!d){if(this.dragThreshMet){if(f.events.b4EndDrag){f.b4EndDrag(a);f.fireEvent("b4EndDragEvent",{e:a})}if(f.events.endDrag){f.endDrag(a);f.fireEvent("endDragEvent",{e:a})}}if(f.events.mouseUp){f.onMouseUp(a);f.fireEvent("mouseUpEvent",{e:a})}}if(this._shimActive){this._deactivateShim();
+if(this.dragCurrent&&this.dragCurrent.useShim){this.useShim=this._shimState;this._shimState=false}}this.dragCurrent=null;this.dragOvers={}},handleMouseMove:function(a){var d=this.dragCurrent;if(d){if(YAHOO.env.ua.ie&&YAHOO.env.ua.ie<9&&!a.button){this.stopEvent(a);return this.handleMouseUp(a)}if(!this.dragThreshMet){var f=Math.abs(this.startX-YAHOO.util.Event.getPageX(a)),c=Math.abs(this.startY-YAHOO.util.Event.getPageY(a));(f>this.clickPixelThresh||c>this.clickPixelThresh)&&this.startDrag(this.startX,
+this.startY)}if(this.dragThreshMet){if(d&&d.events.b4Drag){d.b4Drag(a);d.fireEvent("b4DragEvent",{e:a})}if(d&&d.events.drag){d.onDrag(a);d.fireEvent("dragEvent",{e:a})}d&&this.fireEvents(a,false)}this.stopEvent(a)}},fireEvents:function(a,d){var f=this.dragCurrent;if(f&&!f.isLocked()&&!f.dragOnly){var c=YAHOO.util.Event.getPageX(a),j=YAHOO.util.Event.getPageY(a),h=new YAHOO.util.Point(c,j),j=f.getTargetCoord(h.x,h.y),e=f.getDragEl(),c=["out","over","drop","enter"],i=new YAHOO.util.Region(j.y,j.x+e.offsetWidth,
+j.y+e.offsetHeight,j.x),k=[],l={},j={},e=[],q={outEvts:[],overEvts:[],dropEvts:[],enterEvts:[]},p;for(p in this.dragOvers){var n=this.dragOvers[p];if(this.isTypeOfDD(n)){this.isOverTarget(h,n,this.mode,i)||q.outEvts.push(n);k[p]=true;delete this.dragOvers[p]}}for(var o in f.groups)if("string"==typeof o)for(p in this.ids[o]){n=this.ids[o][p];if(this.isTypeOfDD(n)&&n.isTarget&&(!n.isLocked()&&n!=f)&&this.isOverTarget(h,n,this.mode,i)){l[o]=true;if(d)q.dropEvts.push(n);else{k[n.id]?q.overEvts.push(n):
+q.enterEvts.push(n);this.dragOvers[n.id]=n}}}this.interactionInfo={out:q.outEvts,enter:q.enterEvts,over:q.overEvts,drop:q.dropEvts,point:h,draggedRegion:i,sourceRegion:this.locationCache[f.id],validDrop:d};for(var m in l)e.push(m);if(d&&!q.dropEvts.length){this.interactionInfo.validDrop=false;if(f.events.invalidDrop){f.onInvalidDrop(a);f.fireEvent("invalidDropEvent",{e:a})}}for(p=0;p<c.length;p++){o=null;q[c[p]+"Evts"]&&(o=q[c[p]+"Evts"]);if(o&&o.length){k=c[p].charAt(0).toUpperCase()+c[p].substr(1);
+m="onDrag"+k;h="b4Drag"+k;i="drag"+k+"Event";k="drag"+k;if(this.mode){if(f.events[h]){f[h](a,o,e);j[m]=f.fireEvent(h+"Event",{event:a,info:o,group:e})}if(f.events[k]&&j[m]!==false){f[m](a,o,e);f.fireEvent(i,{event:a,info:o,group:e})}}else{l=0;for(n=o.length;l<n;++l){if(f.events[h]){f[h](a,o[l].id,e[0]);j[m]=f.fireEvent(h+"Event",{event:a,info:o[l].id,group:e[0]})}if(f.events[k]&&j[m]!==false){f[m](a,o[l].id,e[0]);f.fireEvent(i,{event:a,info:o[l].id,group:e[0]})}}}}}}},getBestMatch:function(a){var d=
+null,f=a.length;if(f==1)d=a[0];else for(var c=0;c<f;++c){var j=a[c];if(this.mode==this.INTERSECT&&j.cursorIsOver){d=j;break}else if(!d||!d.overlap||j.overlap&&d.overlap.getArea()<j.overlap.getArea())d=j}return d},refreshCache:function(a){var a=a||this.ids,d;for(d in a)if("string"==typeof d)for(var f in this.ids[d]){var c=this.ids[d][f];if(this.isTypeOfDD(c)){var j=this.getLocation(c);j?this.locationCache[c.id]=j:delete this.locationCache[c.id]}}},verifyEl:function(a){try{if(a&&a.offsetParent)return true}catch(d){}return false},
+getLocation:function(a){if(!this.isTypeOfDD(a))return null;var d=a.getEl(),f,c,j;try{f=YAHOO.util.Dom.getXY(d)}catch(h){}if(!f)return null;c=f[0];j=c+d.offsetWidth;f=f[1];return new YAHOO.util.Region(f-a.padding[0],j+a.padding[1],f+d.offsetHeight+a.padding[2],c-a.padding[3])},isOverTarget:function(a,d,f,c){var j=this.locationCache[d.id];if(!j||!this.useCache){j=this.getLocation(d);this.locationCache[d.id]=j}if(!j)return false;d.cursorIsOver=j.contains(a);var h=this.dragCurrent;if(!h||!f&&!h.constrainX&&
+!h.constrainY)return d.cursorIsOver;d.overlap=null;if(!c){a=h.getTargetCoord(a.x,a.y);h=h.getDragEl();c=new YAHOO.util.Region(a.y,a.x+h.offsetWidth,a.y+h.offsetHeight,a.x)}if(j=c.intersect(j)){d.overlap=j;return f?true:d.cursorIsOver}return false},_onUnload:function(){this.unregAll()},unregAll:function(){if(this.dragCurrent){this.stopDrag();this.dragCurrent=null}this._execOnAll("unreg",[]);this.ids={}},elementCache:{},getElWrapper:function(a){var d=this.elementCache[a];if(!d||!d.el)d=this.elementCache[a]=
+new this.ElementWrapper(YAHOO.util.Dom.get(a));return d},getElement:function(a){return YAHOO.util.Dom.get(a)},getCss:function(a){return(a=YAHOO.util.Dom.get(a))?a.style:null},ElementWrapper:function(a){this.id=(this.el=a||null)&&a.id;this.css=this.el&&a.style},getPosX:function(a){return YAHOO.util.Dom.getX(a)},getPosY:function(a){return YAHOO.util.Dom.getY(a)},swapNode:function(a,d){if(a.swapNode)a.swapNode(d);else{var f=d.parentNode,c=d.nextSibling;if(c==a)f.insertBefore(a,d);else if(d==a.nextSibling)f.insertBefore(d,
+a);else{a.parentNode.replaceChild(d,a);f.insertBefore(a,c)}}},getScroll:function(){var a,d,f=document.documentElement,c=document.body;if(f&&(f.scrollTop||f.scrollLeft)){a=f.scrollTop;d=f.scrollLeft}else if(c){a=c.scrollTop;d=c.scrollLeft}return{top:a,left:d}},getStyle:function(a,d){return YAHOO.util.Dom.getStyle(a,d)},getScrollTop:function(){return this.getScroll().top},getScrollLeft:function(){return this.getScroll().left},moveToEl:function(a,d){var f=YAHOO.util.Dom.getXY(d);YAHOO.util.Dom.setXY(a,
+f)},getClientHeight:function(){return YAHOO.util.Dom.getViewportHeight()},getClientWidth:function(){return YAHOO.util.Dom.getViewportWidth()},numericSort:function(a,d){return a-d},_timeoutCount:0,_addListeners:function(){var a=YAHOO.util.DDM;if(YAHOO.util.Event&&document)a._onLoad();else if(!(a._timeoutCount>2E3)){setTimeout(a._addListeners,10);if(document&&document.body)a._timeoutCount=a._timeoutCount+1}},handleWasClicked:function(a,d){if(this.isHandle(d,a.id))return true;for(var f=a.parentNode;f;){if(this.isHandle(d,
+f.id))return true;f=f.parentNode}return false}}}(),YAHOO.util.DDM=YAHOO.util.DragDropMgr,YAHOO.util.DDM._addListeners());
+(function(){var a=YAHOO.util.Event,c=YAHOO.util.Dom;YAHOO.util.DragDrop=function(a,d,f){a&&this.init(a,d,f)};YAHOO.util.DragDrop.prototype={events:null,on:function(){this.subscribe.apply(this,arguments)},id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true},unlock:function(){this.locked=false},isTarget:true,padding:null,dragOnly:false,useShim:false,
+_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,deltaX:0,deltaY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,cursorIsOver:false,overlap:null,b4StartDrag:function(){},startDrag:function(){},b4Drag:function(){},onDrag:function(){},onDragEnter:function(){},b4DragOver:function(){},onDragOver:function(){},b4DragOut:function(){},onDragOut:function(){},b4DragDrop:function(){},onDragDrop:function(){},
+onInvalidDrop:function(){},b4EndDrag:function(){},endDrag:function(){},b4MouseDown:function(){},onMouseDown:function(){},onMouseUp:function(){},onAvailable:function(){},getEl:function(){if(!this._domRef)this._domRef=c.get(this.id);return this._domRef},getDragEl:function(){return c.get(this.dragElId)},init:function(b,d,f){this.initTarget(b,d,f);a.on(this._domRef||this.id,"mousedown",this.handleMouseDown,this,true);for(var c in this.events)this.createEvent(c+"Event")},initTarget:function(b,d,f){this.config=
+f||{};this.events={};this.DDM=YAHOO.util.DDM;this.groups={};if(typeof b!=="string"){this._domRef=b;b=c.generateId(b)}this.id=b;this.addToGroup(d?d:"default");this.handleElId=b;a.onAvailable(b,this.handleOnAvailable,this,true);this.setDragElId(b);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig()},applyConfig:function(){this.events={mouseDown:true,b4MouseDown:true,mouseUp:true,b4StartDrag:true,startDrag:true,b4EndDrag:true,endDrag:true,drag:true,
+b4Drag:true,invalidDrop:true,b4DragOut:true,dragOut:true,dragEnter:true,b4DragOver:true,dragOver:true,b4DragDrop:true,dragDrop:true};if(this.config.events)for(var a in this.config.events)this.config.events[a]===false&&(this.events[a]=false);this.padding=this.config.padding||[0,0,0,0];this.isTarget=this.config.isTarget!==false;this.maintainOffset=this.config.maintainOffset;this.primaryButtonOnly=this.config.primaryButtonOnly!==false;this.dragOnly=this.config.dragOnly===true?true:false;this.useShim=
+this.config.useShim===true?true:false},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable()},setPadding:function(a,d,f,c){this.padding=!d&&0!==d?[a,a,a,a]:!f&&0!==f?[a,d,a,d]:[a,d,f,c]},setInitPosition:function(a,d){var f=this.getEl();if(this.DDM.verifyEl(f)){var g=a||0,j=d||0,f=c.getXY(f);this.initPageX=f[0]-g;this.initPageY=f[1]-j;this.lastPageX=f[0];this.lastPageY=f[1];this.setStartPosition(f)}},setStartPosition:function(a){a=a||c.getXY(this.getEl());this.deltaSetXY=
+null;this.startPageX=a[0];this.startPageY=a[1]},addToGroup:function(a){this.groups[a]=true;this.DDM.regDragDrop(this,a)},removeFromGroup:function(a){this.groups[a]&&delete this.groups[a];this.DDM.removeDDFromGroup(this,a)},setDragElId:function(a){this.dragElId=a},setHandleElId:function(a){typeof a!=="string"&&(a=c.generateId(a));this.handleElId=a;this.DDM.regHandle(this.id,a)},setOuterHandleElId:function(b){typeof b!=="string"&&(b=c.generateId(b));a.on(b,"mousedown",this.handleMouseDown,this,true);
+this.setHandleElId(b);this.hasOuterHandles=true},unreg:function(){a.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this)},isLocked:function(){return this.DDM.isLocked()||this.locked},handleMouseDown:function(b){var d=b.which||b.button;if(!(this.primaryButtonOnly&&d>1)&&!this.isLocked()){var d=this.b4MouseDown(b),f=true;this.events.b4MouseDown&&(f=this.fireEvent("b4MouseDownEvent",b));var c=this.onMouseDown(b),j=true;this.events.mouseDown&&(j=c===false?
+false:this.fireEvent("mouseDownEvent",b));if(!(d===false||c===false||f===false||j===false)){this.DDM.refreshCache(this.groups);d=new YAHOO.util.Point(a.getPageX(b),a.getPageY(b));if((this.hasOuterHandles||this.DDM.isOverTarget(d,this))&&this.clickValidator(b)){this.setStartPosition();this.DDM.handleMouseDown(b,this);this.DDM.stopEvent(b)}}}},clickValidator:function(a){a=YAHOO.util.Event.getTarget(a);return this.isValidHandleChild(a)&&(this.id==this.handleElId||this.DDM.handleWasClicked(a,this.id))},
+getTargetCoord:function(a,d){var f=a-this.deltaX,c=d-this.deltaY;if(this.constrainX){if(f<this.minX)f=this.minX;if(f>this.maxX)f=this.maxX}if(this.constrainY){if(c<this.minY)c=this.minY;if(c>this.maxY)c=this.maxY}f=this.getTick(f,this.xTicks);c=this.getTick(c,this.yTicks);return{x:f,y:c}},addInvalidHandleType:function(a){a=a.toUpperCase();this.invalidHandleTypes[a]=a},addInvalidHandleId:function(a){typeof a!=="string"&&(a=c.generateId(a));this.invalidHandleIds[a]=a},addInvalidHandleClass:function(a){this.invalidHandleClasses.push(a)},
+removeInvalidHandleType:function(a){delete this.invalidHandleTypes[a.toUpperCase()]},removeInvalidHandleId:function(a){typeof a!=="string"&&(a=c.generateId(a));delete this.invalidHandleIds[a]},removeInvalidHandleClass:function(a){for(var d=0,f=this.invalidHandleClasses.length;d<f;++d)this.invalidHandleClasses[d]==a&&delete this.invalidHandleClasses[d]},isValidHandleChild:function(a){var d=true,f;try{f=a.nodeName.toUpperCase()}catch(g){f=a.nodeName}d=(d=d&&!this.invalidHandleTypes[f])&&!this.invalidHandleIds[a.id];
+f=0;for(var j=this.invalidHandleClasses.length;d&&f<j;++f)d=!c.hasClass(a,this.invalidHandleClasses[f]);return d},setXTicks:function(a,d){this.xTicks=[];this.xTickSize=d;for(var f={},c=this.initPageX;c>=this.minX;c=c-d)if(!f[c]){this.xTicks[this.xTicks.length]=c;f[c]=true}for(c=this.initPageX;c<=this.maxX;c=c+d)if(!f[c]){this.xTicks[this.xTicks.length]=c;f[c]=true}this.xTicks.sort(this.DDM.numericSort)},setYTicks:function(a,d){this.yTicks=[];this.yTickSize=d;for(var f={},c=this.initPageY;c>=this.minY;c=
+c-d)if(!f[c]){this.yTicks[this.yTicks.length]=c;f[c]=true}for(c=this.initPageY;c<=this.maxY;c=c+d)if(!f[c]){this.yTicks[this.yTicks.length]=c;f[c]=true}this.yTicks.sort(this.DDM.numericSort)},setXConstraint:function(a,d,f){this.leftConstraint=parseInt(a,10);this.rightConstraint=parseInt(d,10);this.minX=this.initPageX-this.leftConstraint;this.maxX=this.initPageX+this.rightConstraint;f&&this.setXTicks(this.initPageX,f);this.constrainX=true},clearConstraints:function(){this.constrainY=this.constrainX=
+false;this.clearTicks()},clearTicks:function(){this.yTicks=this.xTicks=null;this.yTickSize=this.xTickSize=0},setYConstraint:function(a,d,f){this.topConstraint=parseInt(a,10);this.bottomConstraint=parseInt(d,10);this.minY=this.initPageY-this.topConstraint;this.maxY=this.initPageY+this.bottomConstraint;f&&this.setYTicks(this.initPageY,f);this.constrainY=true},resetConstraints:function(){this.initPageX||this.initPageX===0?this.setInitPosition(this.maintainOffset?this.lastPageX-this.initPageX:0,this.maintainOffset?
+this.lastPageY-this.initPageY:0):this.setInitPosition();this.constrainX&&this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize);this.constrainY&&this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize)},getTick:function(a,d){if(d){if(d[0]>=a)return d[0];for(var f=0,c=d.length;f<c;++f){var j=f+1;if(d[j]&&d[j]>=a)return d[j]-a>a-d[f]?d[f]:d[j]}return d[d.length-1]}return a},toString:function(){return"DragDrop "+this.id}};YAHOO.augment(YAHOO.util.DragDrop,YAHOO.util.EventProvider)})();
+YAHOO.util.DD=function(a,c,b){a&&this.init(a,c,b)};
+YAHOO.extend(YAHOO.util.DD,YAHOO.util.DragDrop,{scroll:!0,autoOffset:function(a,c){this.setDelta(a-this.startPageX,c-this.startPageY)},setDelta:function(a,c){this.deltaX=a;this.deltaY=c},setDragElPos:function(a,c){this.alignElWithMouse(this.getDragEl(),a,c)},alignElWithMouse:function(a,c,b){var d=this.getTargetCoord(c,b);if(this.deltaSetXY){YAHOO.util.Dom.setStyle(a,"left",d.x+this.deltaSetXY[0]+"px");YAHOO.util.Dom.setStyle(a,"top",d.y+this.deltaSetXY[1]+"px")}else{YAHOO.util.Dom.setXY(a,[d.x,d.y]);
+c=parseInt(YAHOO.util.Dom.getStyle(a,"left"),10);b=parseInt(YAHOO.util.Dom.getStyle(a,"top"),10);this.deltaSetXY=[c-d.x,b-d.y]}this.cachePosition(d.x,d.y);var f=this;setTimeout(function(){f.autoScroll.call(f,d.x,d.y,a.offsetHeight,a.offsetWidth)},0)},cachePosition:function(a,c){if(a){this.lastPageX=a;this.lastPageY=c}else{var b=YAHOO.util.Dom.getXY(this.getEl());this.lastPageX=b[0];this.lastPageY=b[1]}},autoScroll:function(a,c,b,d){if(this.scroll){var f=this.DDM.getClientHeight(),g=this.DDM.getClientWidth(),
+j=this.DDM.getScrollTop(),h=this.DDM.getScrollLeft(),d=d+a,e=f+j-c-this.deltaY,i=g+h-a-this.deltaX,k=document.all?80:30;b+c>f&&e<40&&window.scrollTo(h,j+k);c<j&&(j>0&&c-j<40)&&window.scrollTo(h,j-k);d>g&&i<40&&window.scrollTo(h+k,j);a<h&&(h>0&&a-h<40)&&window.scrollTo(h-k,j)}},applyConfig:function(){YAHOO.util.DD.superclass.applyConfig.call(this);this.scroll=this.config.scroll!==false},b4MouseDown:function(a){this.setStartPosition();this.autoOffset(YAHOO.util.Event.getPageX(a),YAHOO.util.Event.getPageY(a))},
+b4Drag:function(a){this.setDragElPos(YAHOO.util.Event.getPageX(a),YAHOO.util.Event.getPageY(a))},toString:function(){return"DD "+this.id}});YAHOO.util.DDProxy=function(a,c,b){if(a){this.init(a,c,b);this.initFrame()}};YAHOO.util.DDProxy.dragElId="ygddfdiv";
+YAHOO.extend(YAHOO.util.DDProxy,YAHOO.util.DD,{resizeFrame:!0,centerFrame:!1,createFrame:function(){var a=this,c=document.body;if(!c||!c.firstChild)setTimeout(function(){a.createFrame()},50);else{var b=this.getDragEl(),d=YAHOO.util.Dom;if(!b){b=document.createElement("div");b.id=this.dragElId;var f=b.style;f.position="absolute";f.visibility="hidden";f.cursor="move";f.border="2px solid #aaa";f.zIndex=999;f.height="25px";f.width="25px";f=document.createElement("div");d.setStyle(f,"height","100%");d.setStyle(f,
+"width","100%");d.setStyle(f,"background-color","#ccc");d.setStyle(f,"opacity","0");b.appendChild(f);c.insertBefore(b,c.firstChild)}}},initFrame:function(){this.createFrame()},applyConfig:function(){YAHOO.util.DDProxy.superclass.applyConfig.call(this);this.resizeFrame=this.config.resizeFrame!==false;this.centerFrame=this.config.centerFrame;this.setDragElId(this.config.dragElId||YAHOO.util.DDProxy.dragElId)},showFrame:function(a,c){this.getEl();var b=this.getDragEl(),d=b.style;this._resizeProxy();
+this.centerFrame&&this.setDelta(Math.round(parseInt(d.width,10)/2),Math.round(parseInt(d.height,10)/2));this.setDragElPos(a,c);YAHOO.util.Dom.setStyle(b,"visibility","visible")},_resizeProxy:function(){if(this.resizeFrame){var a=YAHOO.util.Dom,c=this.getEl(),b=this.getDragEl(),d=parseInt(a.getStyle(b,"borderTopWidth"),10),f=parseInt(a.getStyle(b,"borderRightWidth"),10),g=parseInt(a.getStyle(b,"borderBottomWidth"),10),j=parseInt(a.getStyle(b,"borderLeftWidth"),10);isNaN(d)&&(d=0);isNaN(f)&&(f=0);isNaN(g)&&
+(g=0);isNaN(j)&&(j=0);f=Math.max(0,c.offsetWidth-f-j);c=Math.max(0,c.offsetHeight-d-g);a.setStyle(b,"width",f+"px");a.setStyle(b,"height",c+"px")}},b4MouseDown:function(a){this.setStartPosition();var c=YAHOO.util.Event.getPageX(a),a=YAHOO.util.Event.getPageY(a);this.autoOffset(c,a)},b4StartDrag:function(a,c){this.showFrame(a,c)},b4EndDrag:function(){YAHOO.util.Dom.setStyle(this.getDragEl(),"visibility","hidden")},endDrag:function(){var a=YAHOO.util.Dom,c=this.getEl(),b=this.getDragEl();a.setStyle(b,
+"visibility","");a.setStyle(c,"visibility","hidden");YAHOO.util.DDM.moveToEl(c,b);a.setStyle(b,"visibility","hidden");a.setStyle(c,"visibility","")},toString:function(){return"DDProxy "+this.id}});YAHOO.util.DDTarget=function(a,c,b){a&&this.initTarget(a,c,b)};YAHOO.extend(YAHOO.util.DDTarget,YAHOO.util.DragDrop,{toString:function(){return"DDTarget "+this.id}});YAHOO.register("dragdrop",YAHOO.util.DragDropMgr,{version:"2.9.0",build:"2800"});
+YAHOO.util.Attribute=function(a,c){if(c){this.owner=c;this.configure(a,true)}};YAHOO.util.Attribute.INVALID_VALUE={};
+YAHOO.util.Attribute.prototype={name:void 0,value:null,owner:null,readOnly:!1,writeOnce:!1,_initialConfig:null,_written:!1,method:null,setter:null,getter:null,validator:null,getValue:function(){var a=this.value;this.getter&&(a=this.getter.call(this.owner,this.name,a));return a},setValue:function(a,c){var b,d=this.owner,f=this.name,g=YAHOO.util.Attribute.INVALID_VALUE,j={type:f,prevValue:this.getValue(),newValue:a};if(this.readOnly||this.writeOnce&&this._written||this.validator&&!this.validator.call(d,
+a))return false;if(!c){b=d.fireBeforeChangeEvent(j);if(b===false)return false}if(this.setter){a=this.setter.call(d,a,this.name);if(a===g)return false}if(this.method&&this.method.call(d,a,this.name)===g)return false;this.value=a;this._written=true;j.type=f;c||this.owner.fireChangeEvent(j);return true},configure:function(a,c){a=a||{};if(c)this._written=false;this._initialConfig=this._initialConfig||{};for(var b in a)if(a.hasOwnProperty(b)){this[b]=a[b];c&&(this._initialConfig[b]=a[b])}},resetValue:function(){return this.setValue(this._initialConfig.value)},
+resetConfig:function(){this.configure(this._initialConfig,true)},refresh:function(a){this.setValue(this.value,a)}};
+(function(){var a=YAHOO.util.Lang;YAHOO.util.AttributeProvider=function(){};YAHOO.util.AttributeProvider.prototype={_configs:null,get:function(a){this._configs=this._configs||{};var b=this._configs[a];return!b||!this._configs.hasOwnProperty(a)?null:b.getValue()},set:function(a,b,d){this._configs=this._configs||{};a=this._configs[a];return!a?false:a.setValue(b,d)},getAttributeKeys:function(){this._configs=this._configs;var c=[],b;for(b in this._configs)a.hasOwnProperty(this._configs,b)&&!a.isUndefined(this._configs[b])&&
+(c[c.length]=b);return c},setAttributes:function(c,b){for(var d in c)a.hasOwnProperty(c,d)&&this.set(d,c[d],b)},resetValue:function(a,b){this._configs=this._configs||{};if(this._configs[a]){this.set(a,this._configs[a]._initialConfig.value,b);return true}return false},refresh:function(c,b){for(var d=this._configs=this._configs||{},c=(a.isString(c)?[c]:c)||this.getAttributeKeys(),f=0,g=c.length;f<g;++f)d.hasOwnProperty(c[f])&&this._configs[c[f]].refresh(b)},register:function(a,b){this.setAttributeConfig(a,
+b)},getAttributeConfig:function(c){this._configs=this._configs||{};var b=this._configs[c]||{},d={};for(c in b)a.hasOwnProperty(b,c)&&(d[c]=b[c]);return d},setAttributeConfig:function(a,b,d){this._configs=this._configs||{};b=b||{};if(this._configs[a])this._configs[a].configure(b,d);else{b.name=a;this._configs[a]=this.createAttribute(b)}},configureAttribute:function(a,b,d){this.setAttributeConfig(a,b,d)},resetAttributeConfig:function(a){this._configs=this._configs||{};this._configs[a].resetConfig()},
+subscribe:function(a,b){this._events=this._events||{};a in this._events||(this._events[a]=this.createEvent(a));YAHOO.util.EventProvider.prototype.subscribe.apply(this,arguments)},on:function(){this.subscribe.apply(this,arguments)},addListener:function(){this.subscribe.apply(this,arguments)},fireBeforeChangeEvent:function(a){var b;b="before"+(a.type.charAt(0).toUpperCase()+a.type.substr(1)+"Change");a.type=b;return this.fireEvent(a.type,a)},fireChangeEvent:function(a){a.type=a.type+"Change";return this.fireEvent(a.type,
+a)},createAttribute:function(a){return new YAHOO.util.Attribute(a,this)}};YAHOO.augment(YAHOO.util.AttributeProvider,YAHOO.util.EventProvider)})();
+(function(){var a=YAHOO.util.Dom,c=YAHOO.util.AttributeProvider,b={mouseenter:true,mouseleave:true},d=function(a,b){this.init.apply(this,arguments)};d.DOM_EVENTS={click:true,dblclick:true,keydown:true,keypress:true,keyup:true,mousedown:true,mousemove:true,mouseout:true,mouseover:true,mouseup:true,mouseenter:true,mouseleave:true,focus:true,blur:true,submit:true,change:true};d.prototype={DOM_EVENTS:null,DEFAULT_HTML_SETTER:function(a,b){var d=this.get("element");d&&(d[b]=a);return a},DEFAULT_HTML_GETTER:function(a){var b=
+this.get("element"),d;b&&(d=b[a]);return d},appendChild:function(a){a=a.get?a.get("element"):a;return this.get("element").appendChild(a)},getElementsByTagName:function(a){return this.get("element").getElementsByTagName(a)},hasChildNodes:function(){return this.get("element").hasChildNodes()},insertBefore:function(a,b){a=a.get?a.get("element"):a;b=b&&b.get?b.get("element"):b;return this.get("element").insertBefore(a,b)},removeChild:function(a){a=a.get?a.get("element"):a;return this.get("element").removeChild(a)},
+replaceChild:function(a,b){a=a.get?a.get("element"):a;b=b.get?b.get("element"):b;return this.get("element").replaceChild(a,b)},initAttributes:function(){},addListener:function(a,d,c,h){var h=h||this,e=YAHOO.util.Event,i=this.get("element")||this.get("id"),k=this;if(b[a]&&!e._createMouseDelegate)return false;if(!this._events[a]){if(i&&this.DOM_EVENTS[a])e.on(i,a,function(b,d){if(b.srcElement&&!b.target)b.target=b.srcElement;if(b.toElement&&!b.relatedTarget||b.fromElement&&!b.relatedTarget)b.relatedTarget=
+e.getRelatedTarget(b);if(!b.currentTarget)b.currentTarget=i;k.fireEvent(a,b,d)},c,h);this.createEvent(a,{scope:this})}return YAHOO.util.EventProvider.prototype.subscribe.apply(this,arguments)},on:function(){return this.addListener.apply(this,arguments)},subscribe:function(){return this.addListener.apply(this,arguments)},removeListener:function(a,b){return this.unsubscribe.apply(this,arguments)},addClass:function(b){a.addClass(this.get("element"),b)},getElementsByClassName:function(b,d){return a.getElementsByClassName(b,
+d,this.get("element"))},hasClass:function(b){return a.hasClass(this.get("element"),b)},removeClass:function(b){return a.removeClass(this.get("element"),b)},replaceClass:function(b,d){return a.replaceClass(this.get("element"),b,d)},setStyle:function(b,d){return a.setStyle(this.get("element"),b,d)},getStyle:function(b){return a.getStyle(this.get("element"),b)},fireQueue:function(){for(var a=this._queue,b=0,d=a.length;b<d;++b)this[a[b][0]].apply(this,a[b][1])},appendTo:function(b,d){b=b.get?b.get("element"):
+a.get(b);this.fireEvent("beforeAppendTo",{type:"beforeAppendTo",target:b});var d=d&&d.get?d.get("element"):a.get(d),c=this.get("element");if(!c||!b)return false;c.parent!=b&&(d?b.insertBefore(c,d):b.appendChild(c));this.fireEvent("appendTo",{type:"appendTo",target:b});return c},get:function(a){var b=this._configs||{},d=b.element;d&&(!b[a]&&!YAHOO.lang.isUndefined(d.value[a]))&&this._setHTMLAttrConfig(a);return c.prototype.get.call(this,a)},setAttributes:function(a,b){for(var d={},c=this._configOrder,
+e=0,i=c.length;e<i;++e)if(a[c[e]]!==void 0){d[c[e]]=true;this.set(c[e],a[c[e]],b)}for(var k in a)a.hasOwnProperty(k)&&!d[k]&&this.set(k,a[k],b)},set:function(a,b,d){var h=this.get("element");if(h){!this._configs[a]&&!YAHOO.lang.isUndefined(h[a])&&this._setHTMLAttrConfig(a);return c.prototype.set.apply(this,arguments)}this._queue[this._queue.length]=["set",arguments];if(this._configs[a])this._configs[a].value=b},setAttributeConfig:function(a,b,d){this._configOrder.push(a);c.prototype.setAttributeConfig.apply(this,
+arguments)},createEvent:function(a,b){this._events[a]=true;return c.prototype.createEvent.apply(this,arguments)},init:function(a,b){this._initElement(a,b)},destroy:function(){var a=this.get("element");YAHOO.util.Event.purgeElement(a,true);this.unsubscribeAll();a&&a.parentNode&&a.parentNode.removeChild(a);this._queue=[];this._events={};this._configs={};this._configOrder=[]},_initElement:function(b,c){this._queue=this._queue||[];this._events=this._events||{};this._configs=this._configs||{};this._configOrder=
+[];c=c||{};c.element=c.element||b||null;var j=false,h=d.DOM_EVENTS;this.DOM_EVENTS=this.DOM_EVENTS||{};for(var e in h)h.hasOwnProperty(e)&&(this.DOM_EVENTS[e]=h[e]);typeof c.element==="string"&&this._setHTMLAttrConfig("id",{value:c.element});if(a.get(c.element)){j=true;this._initHTMLElement(c);this._initContent(c)}YAHOO.util.Event.onAvailable(c.element,function(){j||this._initHTMLElement(c);this.fireEvent("available",{type:"available",target:a.get(c.element)})},this,true);YAHOO.util.Event.onContentReady(c.element,
+function(){j||this._initContent(c);this.fireEvent("contentReady",{type:"contentReady",target:a.get(c.element)})},this,true)},_initHTMLElement:function(b){this.setAttributeConfig("element",{value:a.get(b.element),readOnly:true})},_initContent:function(a){this.initAttributes(a);this.setAttributes(a,true);this.fireQueue()},_setHTMLAttrConfig:function(a,b){var d=this.get("element"),b=b||{};b.name=a;b.setter=b.setter||this.DEFAULT_HTML_SETTER;b.getter=b.getter||this.DEFAULT_HTML_GETTER;b.value=b.value||
+d[a];this._configs[a]=new YAHOO.util.Attribute(b,this)}};YAHOO.augment(d,c);YAHOO.util.Element=d})();YAHOO.register("element",YAHOO.util.Element,{version:"2.9.0",build:"2800"});YAHOO.register("utilities",YAHOO,{version:"2.9.0",build:"2800"});
+(function(){var a=YAHOO.lang,c=YAHOO.util;c.DataSourceBase=function(b,f){if(!(b===null||b===void 0)){this.liveData=b;this._oQueue={interval:null,conn:null,requests:[]};this.responseSchema={};if(f&&f.constructor==Object)for(var g in f)g&&(this[g]=f[g]);a.isNumber(this.maxCacheEntries);this._aIntervals=[];this.createEvent("cacheRequestEvent");this.createEvent("cacheResponseEvent");this.createEvent("requestEvent");this.createEvent("responseEvent");this.createEvent("responseParseEvent");this.createEvent("responseCacheEvent");
+this.createEvent("dataErrorEvent");this.createEvent("cacheFlushEvent");g=c.DataSourceBase;this._sName="DataSource instance"+g._nIndex;g._nIndex++}};var b=c.DataSourceBase;a.augmentObject(b,{TYPE_UNKNOWN:-1,TYPE_JSARRAY:0,TYPE_JSFUNCTION:1,TYPE_XHR:2,TYPE_JSON:3,TYPE_XML:4,TYPE_TEXT:5,TYPE_HTMLTABLE:6,TYPE_SCRIPTNODE:7,TYPE_LOCAL:8,ERROR_DATAINVALID:"Invalid data",ERROR_DATANULL:"Null data",_nIndex:0,_nTransactionId:0,_cloneObject:function(d){if(!a.isValue(d))return d;var c={};if(Object.prototype.toString.apply(d)===
+"[object RegExp]")c=d;else if(a.isFunction(d))c=d;else if(a.isArray(d))for(var c=[],g=0,j=d.length;g<j;g++)c[g]=b._cloneObject(d[g]);else if(a.isObject(d))for(g in d)a.hasOwnProperty(d,g)&&(c[g]=a.isValue(d[g])&&a.isObject(d[g])||a.isArray(d[g])?b._cloneObject(d[g]):d[g]);else c=d;return c},_getLocationValue:function(b,c){var g=b.locator||b.key||b,j=c.ownerDocument||c,h,e,i=null;try{if(a.isUndefined(j.evaluate)){j.setProperty("SelectionLanguage","XPath");h=c.selectNodes(g)[0];i=h.value||h.text||null}else for(h=
+j.evaluate(g,c,j.createNSResolver(!c.ownerDocument?c.documentElement:c.ownerDocument.documentElement),0,null);e=h.iterateNext();)i=e.textContent;return i}catch(k){}},issueCallback:function(b,c,g,j){if(a.isFunction(b))b.apply(j,c);else if(a.isObject(b)){var j=b.scope||j||window,h=b.success;if(g)h=b.failure;h&&h.apply(j,c.concat([b.argument]))}},parseString:function(b){if(!a.isValue(b))return null;b=b+"";return a.isString(b)?b:null},parseNumber:function(b){if(!a.isValue(b)||b==="")return null;b=b*1;
+return a.isNumber(b)?b:null},convertNumber:function(a){return b.parseNumber(a)},parseDate:function(b){var c=null;if(a.isValue(b)&&!(b instanceof Date))c=new Date(b);else return b;return c instanceof Date?c:null},convertDate:function(a){return b.parseDate(a)}});b.Parser={string:b.parseString,number:b.parseNumber,date:b.parseDate};b.prototype={_sName:null,_aCache:null,_oQueue:null,_aIntervals:null,maxCacheEntries:0,liveData:null,dataType:b.TYPE_UNKNOWN,responseType:b.TYPE_UNKNOWN,responseSchema:null,
+useXPath:false,cloneBeforeCaching:false,toString:function(){return this._sName},getCachedResponse:function(a,b,c){var j=this._aCache;if(this.maxCacheEntries>0)if(j){var h=j.length;if(h>0){var e=null;this.fireEvent("cacheRequestEvent",{request:a,callback:b,caller:c});for(var i=h-1;i>=0;i--){var k=j[i];if(this.isCacheHit(a,k.request)){e=k.response;this.fireEvent("cacheResponseEvent",{request:a,response:e,callback:b,caller:c});if(i<h-1){j.splice(i,1);this.addToCache(a,e)}e.cached=true;break}}return e}}else this._aCache=
+[];else if(j)this._aCache=null;return null},isCacheHit:function(a,b){return a===b},addToCache:function(a,c){var g=this._aCache;if(g){for(;g.length>=this.maxCacheEntries;)g.shift();c=this.cloneBeforeCaching?b._cloneObject(c):c;g[g.length]={request:a,response:c};this.fireEvent("responseCacheEvent",{request:a,response:c})}},flushCache:function(){if(this._aCache){this._aCache=[];this.fireEvent("cacheFlushEvent")}},setInterval:function(b,c,g,j){if(a.isNumber(b)&&b>=0){var h=this,b=setInterval(function(){h.makeConnection(c,
+g,j)},b);this._aIntervals.push(b);return b}},clearInterval:function(a){for(var b=this._aIntervals||[],c=b.length-1;c>-1;c--)if(b[c]===a){b.splice(c,1);clearInterval(a)}},clearAllIntervals:function(){for(var a=this._aIntervals||[],b=a.length-1;b>-1;b--)clearInterval(a[b])},sendRequest:function(a,c,g){var j=this.getCachedResponse(a,c,g);if(j){b.issueCallback(c,[a,j],false,g);return null}return this.makeConnection(a,c,g)},makeConnection:function(a,c,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",
+{tId:j,request:a,callback:c,caller:g});this.handleResponse(a,this.liveData,c,g,j);return j},handleResponse:function(d,c,g,j,h){this.fireEvent("responseEvent",{tId:h,request:d,response:c,callback:g,caller:j});var e=this.dataType==b.TYPE_XHR?true:false,i=null,k=c;if(this.responseType===b.TYPE_UNKNOWN)if(i=c&&c.getResponseHeader?c.getResponseHeader["Content-Type"]:null)if(i.indexOf("text/xml")>-1)this.responseType=b.TYPE_XML;else if(i.indexOf("application/json")>-1)this.responseType=b.TYPE_JSON;else{if(i.indexOf("text/plain")>
+-1)this.responseType=b.TYPE_TEXT}else if(YAHOO.lang.isArray(c))this.responseType=b.TYPE_JSARRAY;else if(c&&c.nodeType&&(c.nodeType===9||c.nodeType===1||c.nodeType===11))this.responseType=b.TYPE_XML;else if(c&&c.nodeName&&c.nodeName.toLowerCase()=="table")this.responseType=b.TYPE_HTMLTABLE;else if(YAHOO.lang.isObject(c))this.responseType=b.TYPE_JSON;else if(YAHOO.lang.isString(c))this.responseType=b.TYPE_TEXT;switch(this.responseType){case b.TYPE_JSARRAY:if(e&&c&&c.responseText)k=c.responseText;try{if(a.isString(k)){var l=
+[k].concat(this.parseJSONArgs);if(a.JSON)k=a.JSON.parse.apply(a.JSON,l);else if(window.JSON&&JSON.parse)k=JSON.parse.apply(JSON,l);else if(k.parseJSON)k=k.parseJSON.apply(k,l.slice(1));else{for(;k.length>0&&k.charAt(0)!="{"&&k.charAt(0)!="[";)k=k.substring(1,k.length);if(k.length>0)var q=Math.max(k.lastIndexOf("]"),k.lastIndexOf("}")),k=k.substring(0,q+1),k=eval("("+k+")")}}}catch(p){}k=this.doBeforeParseData(d,k,g);i=this.parseArrayData(d,k);break;case b.TYPE_JSON:if(e&&c&&c.responseText)k=c.responseText;
+try{if(a.isString(k)){l=[k].concat(this.parseJSONArgs);if(a.JSON)k=a.JSON.parse.apply(a.JSON,l);else if(window.JSON&&JSON.parse)k=JSON.parse.apply(JSON,l);else if(k.parseJSON)k=k.parseJSON.apply(k,l.slice(1));else{for(;k.length>0&&k.charAt(0)!="{"&&k.charAt(0)!="[";)k=k.substring(1,k.length);if(k.length>0)var n=Math.max(k.lastIndexOf("]"),k.lastIndexOf("}")),k=k.substring(0,n+1),k=eval("("+k+")")}}}catch(o){}k=this.doBeforeParseData(d,k,g);i=this.parseJSONData(d,k);break;case b.TYPE_HTMLTABLE:if(e&&
+c.responseText){e=document.createElement("div");e.innerHTML=c.responseText;k=e.getElementsByTagName("table")[0]}k=this.doBeforeParseData(d,k,g);i=this.parseHTMLTableData(d,k);break;case b.TYPE_XML:if(e&&c.responseXML)k=c.responseXML;k=this.doBeforeParseData(d,k,g);i=this.parseXMLData(d,k);break;case b.TYPE_TEXT:if(e&&a.isString(c.responseText))k=c.responseText;k=this.doBeforeParseData(d,k,g);i=this.parseTextData(d,k);break;default:k=this.doBeforeParseData(d,k,g);i=this.parseData(d,k)}i=i||{};if(!i.results)i.results=
+[];if(!i.meta)i.meta={};if(i.error){i.error=true;this.fireEvent("dataErrorEvent",{request:d,response:c,callback:g,caller:j,message:b.ERROR_DATANULL})}else{i=this.doBeforeCallback(d,k,i,g);this.fireEvent("responseParseEvent",{request:d,response:i,callback:g,caller:j});this.addToCache(d,i)}i.tId=h;b.issueCallback(g,[d,i],i.error,j)},doBeforeParseData:function(a,b){return b},doBeforeCallback:function(a,b,c){return c},parseData:function(b,c){return a.isValue(c)?{results:c,meta:{}}:null},parseArrayData:function(d,
+c){if(a.isArray(c)){var g=[],j,h,e,i,k;if(a.isArray(this.responseSchema.fields)){var l=this.responseSchema.fields;for(j=l.length-1;j>=0;--j)typeof l[j]!=="object"&&(l[j]={key:l[j]});var q={};for(j=l.length-1;j>=0;--j)(h=(typeof l[j].parser==="function"?l[j].parser:b.Parser[l[j].parser+""])||l[j].converter)&&(q[l[j].key]=h);var p=a.isArray(c[0]);for(j=c.length-1;j>-1;j--){var n={};e=c[j];if(typeof e==="object")for(h=l.length-1;h>-1;h--){i=l[h];k=p?e[h]:e[i.key];q[i.key]&&(k=q[i.key].call(this,k));
+k===void 0&&(k=null);n[i.key]=k}else if(a.isString(e))for(h=l.length-1;h>-1;h--){i=l[h];k=e;q[i.key]&&(k=q[i.key].call(this,k));k===void 0&&(k=null);n[i.key]=k}g[j]=n}}else g=c;return{results:g}}return null},parseTextData:function(d,c){if(a.isString(c)&&a.isString(this.responseSchema.recordDelim)&&a.isString(this.responseSchema.fieldDelim)){var g={results:[]},j=this.responseSchema.recordDelim,h=this.responseSchema.fieldDelim;if(c.length>0){var e=c.length-j.length;c.substr(e)==j&&(c=c.substr(0,e));
+if(c.length>0)for(var j=c.split(j),e=0,i=j.length,k=0;e<i;++e){var l=false,q=j[e];if(a.isString(q)&&q.length>0){var q=j[e].split(h),p={};if(a.isArray(this.responseSchema.fields))for(var n=this.responseSchema.fields,o=n.length-1;o>-1;o--)try{var m=q[o];if(a.isString(m)){m.charAt(0)=='"'&&(m=m.substr(1));m.charAt(m.length-1)=='"'&&(m=m.substr(0,m.length-1));var r=n[o],s=a.isValue(r.key)?r.key:r;if(!r.parser&&r.converter)r.parser=r.converter;var t=typeof r.parser==="function"?r.parser:b.Parser[r.parser+
+""];t&&(m=t.call(this,m));m===void 0&&(m=null);p[s]=m}else l=true}catch(u){l=true}else p=q;l||(g.results[k++]=p)}}}return g}return null},parseXMLResult:function(d){var c={},g=this.responseSchema;try{for(var j=g.fields.length-1;j>=0;j--){var h=g.fields[j],e=a.isValue(h.key)?h.key:h,i=null;if(this.useXPath)i=YAHOO.util.DataSource._getLocationValue(h,d);else{var k=d.attributes.getNamedItem(e);if(k)i=k.value;else{var l=d.getElementsByTagName(e);if(l&&l.item(0)){var q=l.item(0),i=q?q.text?q.text:q.textContent?
+q.textContent:null:null;if(!i){for(var p=[],n=0,o=q.childNodes.length;n<o;n++)if(q.childNodes[n].nodeValue)p[p.length]=q.childNodes[n].nodeValue;p.length>0&&(i=p.join(""))}}}}i===null&&(i="");if(!h.parser&&h.converter)h.parser=h.converter;var m=typeof h.parser==="function"?h.parser:b.Parser[h.parser+""];m&&(i=m.call(this,i));i===void 0&&(i=null);c[e]=i}}catch(r){}return c},parseXMLData:function(b,c){var g=false,j=this.responseSchema,h={meta:{}},e=null,i=j.metaNode,k=j.metaFields||{},l,q,p;try{if(this.useXPath)for(l in k)h.meta[l]=
+YAHOO.util.DataSource._getLocationValue(k[l],c);else if(i=i?c.getElementsByTagName(i)[0]:c)for(l in k)if(a.hasOwnProperty(k,l)){q=k[l];if(p=i.getElementsByTagName(q)[0])p=p.firstChild.nodeValue;else if(p=i.attributes.getNamedItem(q))p=p.value;a.isValue(p)&&(h.meta[l]=p)}e=j.resultNode?c.getElementsByTagName(j.resultNode):null}catch(n){}if(!e||!a.isArray(j.fields))g=true;else{h.results=[];for(j=e.length-1;j>=0;--j){i=this.parseXMLResult(e.item(j));h.results[j]=i}}if(g)h.error=true;return h},parseJSONData:function(d,
+c){var g={results:[],meta:{}};if(a.isObject(c)&&this.responseSchema.resultsList){var j=this.responseSchema,h=j.fields,e=c,i=[],k=j.metaFields||{},l=[],q=[],p=[],n=false,o,m,r,s=function(e){var a=null,b=[],i=0;if(e){e=e.replace(/\[(['"])(.*?)\1\]/g,function(e,a,d){b[i]=d;return".@"+i++}).replace(/\[(\d+)\]/g,function(e,a){b[i]=parseInt(a,10)|0;return".@"+i++}).replace(/^\./,"");if(!/[^\w\.\$@]/.test(e)){a=e.split(".");for(i=a.length-1;i>=0;--i)a[i].charAt(0)==="@"&&(a[i]=b[parseInt(a[i].substr(1),
+10)])}}return a},t=function(e,a){for(var b=a,i=0,d=e.length;i<d&&b;++i)b=b[e[i]];return b};if(r=s(j.resultsList)){e=t(r,c);e===void 0&&(n=true)}else n=true;e||(e=[]);a.isArray(e)||(e=[e]);if(n)g.error=true;else{if(j.fields){j=0;for(n=h.length;j<n;j++){r=h[j];o=r.key||r;m=(typeof r.parser==="function"?r.parser:b.Parser[r.parser+""])||r.converter;r=s(o);m&&(l[l.length]={key:o,parser:m});r&&(r.length>1?q[q.length]={key:o,path:r}:p[p.length]={key:o,path:r[0]})}for(j=e.length-1;j>=0;--j){n=e[j];r={};if(n){for(h=
+p.length-1;h>=0;--h)r[p[h].key]=n[p[h].path]!==void 0?n[p[h].path]:n[h];for(h=q.length-1;h>=0;--h)r[q[h].key]=t(q[h].path,n);for(h=l.length-1;h>=0;--h){n=l[h].key;r[n]=l[h].parser.call(this,r[n]);r[n]===void 0&&(r[n]=null)}}i[j]=r}}else i=e;for(o in k)if(a.hasOwnProperty(k,o))if(r=s(k[o])){e=t(r,c);g.meta[o]=e}}g.results=i}else g.error=true;return g},parseHTMLTableData:function(d,c){var g=false,j=this.responseSchema.fields,h={results:[]};if(a.isArray(j))for(var e=0;e<c.tBodies.length;e++)for(var i=
+c.tBodies[e],k=i.rows.length-1;k>-1;k--){for(var l=i.rows[k],q={},p=j.length-1;p>-1;p--){var n=j[p],o=a.isValue(n.key)?n.key:n,m=l.cells[p].innerHTML;if(!n.parser&&n.converter)n.parser=n.converter;(n=typeof n.parser==="function"?n.parser:b.Parser[n.parser+""])&&(m=n.call(this,m));m===void 0&&(m=null);q[o]=m}h.results[k]=q}else g=true;if(g)h.error=true;return h}};a.augmentProto(b,c.EventProvider);c.LocalDataSource=function(a,f){this.dataType=b.TYPE_LOCAL;if(a)if(YAHOO.lang.isArray(a))this.responseType=
+b.TYPE_JSARRAY;else if(a.nodeType&&a.nodeType==9)this.responseType=b.TYPE_XML;else if(a.nodeName&&a.nodeName.toLowerCase()=="table"){this.responseType=b.TYPE_HTMLTABLE;a=a.cloneNode(true)}else if(YAHOO.lang.isString(a))this.responseType=b.TYPE_TEXT;else{if(YAHOO.lang.isObject(a))this.responseType=b.TYPE_JSON}else{a=[];this.responseType=b.TYPE_JSARRAY}c.LocalDataSource.superclass.constructor.call(this,a,f)};a.extend(c.LocalDataSource,b);a.augmentObject(c.LocalDataSource,b);c.FunctionDataSource=function(a,
+f){this.dataType=b.TYPE_JSFUNCTION;c.FunctionDataSource.superclass.constructor.call(this,a||function(){},f)};a.extend(c.FunctionDataSource,b,{scope:null,makeConnection:function(a,c,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",{tId:j,request:a,callback:c,caller:g});var h=this.scope?this.liveData.call(this.scope,a,this,c):this.liveData(a,c);if(this.responseType===b.TYPE_UNKNOWN)if(YAHOO.lang.isArray(h))this.responseType=b.TYPE_JSARRAY;else if(h&&h.nodeType&&h.nodeType==9)this.responseType=
+b.TYPE_XML;else if(h&&h.nodeName&&h.nodeName.toLowerCase()=="table")this.responseType=b.TYPE_HTMLTABLE;else if(YAHOO.lang.isObject(h))this.responseType=b.TYPE_JSON;else if(YAHOO.lang.isString(h))this.responseType=b.TYPE_TEXT;this.handleResponse(a,h,c,g,j);return j}});a.augmentObject(c.FunctionDataSource,b);c.ScriptNodeDataSource=function(a,f){this.dataType=b.TYPE_SCRIPTNODE;c.ScriptNodeDataSource.superclass.constructor.call(this,a||"",f)};a.extend(c.ScriptNodeDataSource,b,{getUtility:c.Get,asyncMode:"allowAll",
+scriptCallbackParam:"callback",generateRequestCallback:function(a){return"&"+this.scriptCallbackParam+"=YAHOO.util.ScriptNodeDataSource.callbacks["+a+"]"},doBeforeGetScriptNode:function(a){return a},makeConnection:function(a,f,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",{tId:j,request:a,callback:f,caller:g});if(c.ScriptNodeDataSource._nPending===0){c.ScriptNodeDataSource.callbacks=[];c.ScriptNodeDataSource._nId=0}var h=c.ScriptNodeDataSource._nId;c.ScriptNodeDataSource._nId++;var e=
+this;c.ScriptNodeDataSource.callbacks[h]=function(i){if(e.asyncMode!=="ignoreStaleResponses"||h===c.ScriptNodeDataSource.callbacks.length-1){if(e.responseType===b.TYPE_UNKNOWN)if(YAHOO.lang.isArray(i))e.responseType=b.TYPE_JSARRAY;else if(i.nodeType&&i.nodeType==9)e.responseType=b.TYPE_XML;else if(i.nodeName&&i.nodeName.toLowerCase()=="table")e.responseType=b.TYPE_HTMLTABLE;else if(YAHOO.lang.isObject(i))e.responseType=b.TYPE_JSON;else if(YAHOO.lang.isString(i))e.responseType=b.TYPE_TEXT;e.handleResponse(a,
+i,f,g,j)}delete c.ScriptNodeDataSource.callbacks[h]};c.ScriptNodeDataSource._nPending++;var i=this.liveData+a+this.generateRequestCallback(h),i=this.doBeforeGetScriptNode(i);this.getUtility.script(i,{autopurge:true,onsuccess:c.ScriptNodeDataSource._bumpPendingDown,onfail:c.ScriptNodeDataSource._bumpPendingDown});return j}});a.augmentObject(c.ScriptNodeDataSource,b);a.augmentObject(c.ScriptNodeDataSource,{_nId:0,_nPending:0,callbacks:[]});c.XHRDataSource=function(a,f){this.dataType=b.TYPE_XHR;this.connMgr=
+this.connMgr||c.Connect;c.XHRDataSource.superclass.constructor.call(this,a||"",f)};a.extend(c.XHRDataSource,b,{connMgr:null,connXhrMode:"allowAll",connMethodPost:false,connTimeout:0,makeConnection:function(d,c,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",{tId:j,request:d,callback:c,caller:g});var h=this.connMgr,e=this._oQueue,i={success:function(a){if(a&&this.connXhrMode=="ignoreStaleResponses"&&a.tId!=e.conn.tId)return null;if(a){if(this.responseType===b.TYPE_UNKNOWN){var i=a.getResponseHeader?
+a.getResponseHeader["Content-Type"]:null;if(i)if(i.indexOf("text/xml")>-1)this.responseType=b.TYPE_XML;else if(i.indexOf("application/json")>-1)this.responseType=b.TYPE_JSON;else if(i.indexOf("text/plain")>-1)this.responseType=b.TYPE_TEXT}this.handleResponse(d,a,c,g,j)}else{this.fireEvent("dataErrorEvent",{request:d,response:null,callback:c,caller:g,message:b.ERROR_DATANULL});b.issueCallback(c,[d,{error:true}],true,g);return null}},failure:function(e){this.fireEvent("dataErrorEvent",{request:d,response:e,
+callback:c,caller:g,message:b.ERROR_DATAINVALID});a.isString(this.liveData)&&a.isString(d)&&this.liveData.lastIndexOf("?")!==this.liveData.length-1&&d.indexOf("?");e=e||{};e.error=true;b.issueCallback(c,[d,e],true,g);return null},scope:this};if(a.isNumber(this.connTimeout))i.timeout=this.connTimeout;if(this.connXhrMode=="cancelStaleRequests"&&e.conn&&h.abort){h.abort(e.conn);e.conn=null}if(h&&h.asyncRequest){var k=this.liveData,l=this.connMethodPost,q=l?"POST":"GET",p=l||!a.isValue(d)?k:k+d,n=l?d:
+null;if(this.connXhrMode!="queueRequests")e.conn=h.asyncRequest(q,p,i,n);else if(e.conn){var o=e.requests;o.push({request:d,callback:i});if(!e.interval)e.interval=setInterval(function(){if(!h.isCallInProgress(e.conn))if(o.length>0){p=l||!a.isValue(o[0].request)?k:k+o[0].request;n=l?o[0].request:null;e.conn=h.asyncRequest(q,p,o[0].callback,n);o.shift()}else{clearInterval(e.interval);e.interval=null}},50)}else e.conn=h.asyncRequest(q,p,i,n)}else b.issueCallback(c,[d,{error:true}],true,g);return j}});
+a.augmentObject(c.XHRDataSource,b);c.DataSource=function(a,f){var f=f||{},g=f.dataType;if(g){if(g==b.TYPE_LOCAL)return new c.LocalDataSource(a,f);if(g==b.TYPE_XHR)return new c.XHRDataSource(a,f);if(g==b.TYPE_SCRIPTNODE)return new c.ScriptNodeDataSource(a,f);if(g==b.TYPE_JSFUNCTION)return new c.FunctionDataSource(a,f)}return YAHOO.lang.isString(a)?new c.XHRDataSource(a,f):YAHOO.lang.isFunction(a)?new c.FunctionDataSource(a,f):new c.LocalDataSource(a,f)};a.augmentObject(c.DataSource,b)})();
+YAHOO.util.Number={format:function(a,c){if(a===""||a===null||!isFinite(a))return"";var a=+a,c=YAHOO.lang.merge(YAHOO.util.Number.format.defaults,c||{}),b=Math.abs(a),d=c.decimalPlaces||0,f=c.thousandsSeparator,g=c.negativeFormat||"-"+c.format,j;g.indexOf("#")>-1&&(g=g.replace(/#/,c.format));if(d<0){j=b-b%1+"";d=j.length+d;j=d>0?Number("."+j).toFixed(d).slice(2)+Array(j.length-d+1).join("0"):"0"}else if(d>0||(b+"").indexOf(".")>0){j=Math.pow(10,d);j=Math.round(b*j)/j+"";var h=j.indexOf(".");if(h<0){h=
+(Math.pow(10,d)+"").substring(1);d>0&&(j=j+"."+h)}else{d=d-(j.length-h-1);h=(Math.pow(10,d)+"").substring(1);j=j+h}}else j=b.toFixed(d)+"";j=j.split(/\D/);if(b>=1E3){d=j[0].length%3||3;j[0]=j[0].slice(0,d)+j[0].slice(d).replace(/(\d{3})/g,f+"$1")}return YAHOO.util.Number.format._applyFormat(a<0?g:c.format,j.join(c.decimalSeparator),c)}};YAHOO.util.Number.format.defaults={format:"{prefix}{number}{suffix}",negativeFormat:null,decimalSeparator:".",decimalPlaces:null,thousandsSeparator:""};
+YAHOO.util.Number.format._applyFormat=function(a,c,b){return a.replace(/\{(\w+)\}/g,function(a,f){return f==="number"?c:f in b?b[f]:""})};
+(function(){var a=function(a,d,c){for(typeof c==="undefined"&&(c=10);parseInt(a,10)<c&&c>1;c=c/10)a=d.toString()+a;return a.toString()},c={formats:{a:function(a,d){return d.a[a.getDay()]},A:function(a,d){return d.A[a.getDay()]},b:function(a,d){return d.b[a.getMonth()]},B:function(a,d){return d.B[a.getMonth()]},C:function(b){return a(parseInt(b.getFullYear()/100,10),0)},d:["getDate","0"],e:["getDate"," "],g:function(b){return a(parseInt(c.formats.G(b)%100,10),0)},G:function(a){var d=a.getFullYear(),
+f=parseInt(c.formats.V(a),10),a=parseInt(c.formats.W(a),10);a>f?d++:a===0&&f>=52&&d--;return d},H:["getHours","0"],I:function(b){b=b.getHours()%12;return a(b===0?12:b,0)},j:function(b){var d=new Date(""+b.getFullYear()+"/1/1 GMT"),b=new Date(""+b.getFullYear()+"/"+(b.getMonth()+1)+"/"+b.getDate()+" GMT")-d,b=parseInt(b/6E4/60/24,10)+1;return a(b,0,100)},k:["getHours"," "],l:function(b){b=b.getHours()%12;return a(b===0?12:b," ")},m:function(b){return a(b.getMonth()+1,0)},M:["getMinutes","0"],p:function(a,
+d){return d.p[a.getHours()>=12?1:0]},P:function(a,d){return d.P[a.getHours()>=12?1:0]},s:function(a){return parseInt(a.getTime()/1E3,10)},S:["getSeconds","0"],u:function(a){a=a.getDay();return a===0?7:a},U:function(b){var d=parseInt(c.formats.j(b),10),b=6-b.getDay(),d=parseInt((d+b)/7,10);return a(d,0)},V:function(b){var d=parseInt(c.formats.W(b),10),f=(new Date(""+b.getFullYear()+"/1/1")).getDay(),d=d+(f>4||f<=1?0:1);d===53&&(new Date(""+b.getFullYear()+"/12/31")).getDay()<4?d=1:d===0&&(d=c.formats.V(new Date(""+
+(b.getFullYear()-1)+"/12/31")));return a(d,0)},w:"getDay",W:function(b){var d=parseInt(c.formats.j(b),10),b=7-c.formats.u(b),d=parseInt((d+b)/7,10);return a(d,0,10)},y:function(b){return a(b.getFullYear()%100,0)},Y:"getFullYear",z:function(b){var b=b.getTimezoneOffset(),d=a(parseInt(Math.abs(b/60),10),0),c=a(Math.abs(b%60),0);return(b>0?"-":"+")+d+c},Z:function(a){var d=a.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");d.length>4&&(d=c.formats.z(a));
+return d},"%":function(){return"%"}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(b,d,f){d=d||{};if(!(b instanceof Date))return YAHOO.lang.isValue(b)?b:"";d=d.format||"%m/%d/%Y";d==="YYYY/MM/DD"?d="%Y/%m/%d":d==="DD/MM/YYYY"?d="%d/%m/%Y":d==="MM/DD/YYYY"&&(d="%m/%d/%Y");f=f||"en";f in YAHOO.util.DateLocale||(f=f.replace(/-[a-zA-Z]+$/,"")in YAHOO.util.DateLocale?f.replace(/-[a-zA-Z]+$/,""):"en");for(var g=
+YAHOO.util.DateLocale[f],f=function(a,e){var b=c.aggregates[e];return b==="locale"?g[e]:b},j=function(d,e){var i=c.formats[e];return typeof i==="string"?b[i]():typeof i==="function"?i.call(b,b,g):typeof i==="object"&&typeof i[0]==="string"?a(b[i[0]](),i[1]):e};d.match(/%[cDFhnrRtTxX]/);)d=d.replace(/%([cDFhnrRtTxX])/g,f);d=d.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,j);f=j=void 0;return d}};YAHOO.namespace("YAHOO.util");YAHOO.util.Date=c;YAHOO.util.DateLocale={a:["Sun","Mon","Tue","Wed","Thu",
+"Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};YAHOO.util.DateLocale.en=YAHOO.lang.merge(YAHOO.util.DateLocale,{});YAHOO.util.DateLocale["en-US"]=YAHOO.lang.merge(YAHOO.util.DateLocale.en,
+{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});YAHOO.util.DateLocale["en-GB"]=YAHOO.lang.merge(YAHOO.util.DateLocale.en,{r:"%l:%M:%S %P %Z"});YAHOO.util.DateLocale["en-AU"]=YAHOO.lang.merge(YAHOO.util.DateLocale.en)})();YAHOO.register("datasource",YAHOO.util.DataSource,{version:"2.9.0",build:"2800"});YAHOO.widget.DS_JSArray=YAHOO.util.LocalDataSource;YAHOO.widget.DS_JSFunction=YAHOO.util.FunctionDataSource;
+YAHOO.widget.DS_XHR=function(a,c,b){a=new YAHOO.util.XHRDataSource(a,b);a._aDeprecatedSchema=c;return a};YAHOO.widget.DS_ScriptNode=function(a,c,b){a=new YAHOO.util.ScriptNodeDataSource(a,b);a._aDeprecatedSchema=c;return a};YAHOO.widget.DS_XHR.TYPE_JSON=YAHOO.util.DataSourceBase.TYPE_JSON;YAHOO.widget.DS_XHR.TYPE_XML=YAHOO.util.DataSourceBase.TYPE_XML;YAHOO.widget.DS_XHR.TYPE_FLAT=YAHOO.util.DataSourceBase.TYPE_TEXT;
+YAHOO.widget.AutoComplete=function(a,c,b,d){if(a&&c&&b&&b&&YAHOO.lang.isFunction(b.sendRequest)){this.dataSource=b;this.key=0;var f=b.responseSchema;if(b._aDeprecatedSchema){var g=b._aDeprecatedSchema;if(YAHOO.lang.isArray(g)){if(b.responseType===YAHOO.util.DataSourceBase.TYPE_JSON||b.responseType===YAHOO.util.DataSourceBase.TYPE_UNKNOWN){f.resultsList=g[0];this.key=g[1];f.fields=g.length<3?null:g.slice(1)}else if(b.responseType===YAHOO.util.DataSourceBase.TYPE_XML){f.resultNode=g[0];this.key=g[1];
+f.fields=g.slice(1)}else if(b.responseType===YAHOO.util.DataSourceBase.TYPE_TEXT){f.recordDelim=g[0];f.fieldDelim=g[1]}b.responseSchema=f}}if(YAHOO.util.Dom.inDocument(a)){if(YAHOO.lang.isString(a)){this._sName="instance"+YAHOO.widget.AutoComplete._nIndex+" "+a;this._elTextbox=document.getElementById(a)}else{this._sName=a.id?"instance"+YAHOO.widget.AutoComplete._nIndex+" "+a.id:"instance"+YAHOO.widget.AutoComplete._nIndex;this._elTextbox=a}YAHOO.util.Dom.addClass(this._elTextbox,"yui-ac-input");if(YAHOO.util.Dom.inDocument(c)){this._elContainer=
+YAHOO.lang.isString(c)?document.getElementById(c):c;a=this._elContainer.parentNode;a.tagName.toLowerCase()=="div"&&YAHOO.util.Dom.addClass(a,"yui-ac");if(this.dataSource.dataType===YAHOO.util.DataSourceBase.TYPE_LOCAL)this.applyLocalFilter=true;if(d&&d.constructor==Object)for(var j in d)j&&(this[j]=d[j]);this._initContainerEl();this._initProps();this._initListEl();this._initContainerHelperEls();d=this._elTextbox;YAHOO.util.Event.addListener(d,"keyup",this._onTextboxKeyUp,this);YAHOO.util.Event.addListener(d,
+"keydown",this._onTextboxKeyDown,this);YAHOO.util.Event.addListener(d,"focus",this._onTextboxFocus,this);YAHOO.util.Event.addListener(d,"blur",this._onTextboxBlur,this);YAHOO.util.Event.addListener(c,"mouseover",this._onContainerMouseover,this);YAHOO.util.Event.addListener(c,"mouseout",this._onContainerMouseout,this);YAHOO.util.Event.addListener(c,"click",this._onContainerClick,this);YAHOO.util.Event.addListener(c,"scroll",this._onContainerScroll,this);YAHOO.util.Event.addListener(c,"resize",this._onContainerResize,
+this);YAHOO.util.Event.addListener(d,"keypress",this._onTextboxKeyPress,this);YAHOO.util.Event.addListener(window,"unload",this._onWindowUnload,this);this.textboxFocusEvent=new YAHOO.util.CustomEvent("textboxFocus",this);this.textboxKeyEvent=new YAHOO.util.CustomEvent("textboxKey",this);this.textboxKeyUpEvent=new YAHOO.util.CustomEvent("textboxKeyUp",this);this.dataRequestEvent=new YAHOO.util.CustomEvent("dataRequest",this);this.dataRequestCancelEvent=new YAHOO.util.CustomEvent("dataRequestCancel",
+this);this.dataReturnEvent=new YAHOO.util.CustomEvent("dataReturn",this);this.dataErrorEvent=new YAHOO.util.CustomEvent("dataError",this);this.containerPopulateEvent=new YAHOO.util.CustomEvent("containerPopulate",this);this.containerExpandEvent=new YAHOO.util.CustomEvent("containerExpand",this);this.typeAheadEvent=new YAHOO.util.CustomEvent("typeAhead",this);this.itemMouseOverEvent=new YAHOO.util.CustomEvent("itemMouseOver",this);this.itemMouseOutEvent=new YAHOO.util.CustomEvent("itemMouseOut",this);
+this.itemArrowToEvent=new YAHOO.util.CustomEvent("itemArrowTo",this);this.itemArrowFromEvent=new YAHOO.util.CustomEvent("itemArrowFrom",this);this.itemSelectEvent=new YAHOO.util.CustomEvent("itemSelect",this);this.unmatchedItemSelectEvent=new YAHOO.util.CustomEvent("unmatchedItemSelect",this);this.selectionEnforceEvent=new YAHOO.util.CustomEvent("selectionEnforce",this);this.containerCollapseEvent=new YAHOO.util.CustomEvent("containerCollapse",this);this.textboxBlurEvent=new YAHOO.util.CustomEvent("textboxBlur",
+this);this.textboxChangeEvent=new YAHOO.util.CustomEvent("textboxChange",this);d.setAttribute("autocomplete","off");YAHOO.widget.AutoComplete._nIndex++}}}};YAHOO.widget.AutoComplete.prototype.dataSource=null;YAHOO.widget.AutoComplete.prototype.applyLocalFilter=null;YAHOO.widget.AutoComplete.prototype.queryMatchCase=!1;YAHOO.widget.AutoComplete.prototype.queryMatchContains=!1;YAHOO.widget.AutoComplete.prototype.queryMatchSubset=!1;YAHOO.widget.AutoComplete.prototype.minQueryLength=1;
+YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed=10;YAHOO.widget.AutoComplete.prototype.queryDelay=0.2;YAHOO.widget.AutoComplete.prototype.typeAheadDelay=0.5;YAHOO.widget.AutoComplete.prototype.queryInterval=500;YAHOO.widget.AutoComplete.prototype.highlightClassName="yui-ac-highlight";YAHOO.widget.AutoComplete.prototype.prehighlightClassName=null;YAHOO.widget.AutoComplete.prototype.delimChar=null;YAHOO.widget.AutoComplete.prototype.autoHighlight=!0;
+YAHOO.widget.AutoComplete.prototype.typeAhead=!1;YAHOO.widget.AutoComplete.prototype.animHoriz=!1;YAHOO.widget.AutoComplete.prototype.animVert=!0;YAHOO.widget.AutoComplete.prototype.animSpeed=0.3;YAHOO.widget.AutoComplete.prototype.forceSelection=!1;YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete=!0;YAHOO.widget.AutoComplete.prototype.alwaysShowContainer=!1;YAHOO.widget.AutoComplete.prototype.useIFrame=!1;YAHOO.widget.AutoComplete.prototype.useShadow=!1;
+YAHOO.widget.AutoComplete.prototype.suppressInputUpdate=!1;YAHOO.widget.AutoComplete.prototype.resultTypeList=!0;YAHOO.widget.AutoComplete.prototype.queryQuestionMark=!0;YAHOO.widget.AutoComplete.prototype.autoSnapContainer=!0;YAHOO.widget.AutoComplete.prototype.toString=function(){return"AutoComplete "+this._sName};YAHOO.widget.AutoComplete.prototype.getInputEl=function(){return this._elTextbox};YAHOO.widget.AutoComplete.prototype.getContainerEl=function(){return this._elContainer};
+YAHOO.widget.AutoComplete.prototype.isFocused=function(){return this._bFocused};YAHOO.widget.AutoComplete.prototype.isContainerOpen=function(){return this._bContainerOpen};YAHOO.widget.AutoComplete.prototype.getListEl=function(){return this._elList};YAHOO.widget.AutoComplete.prototype.getListItemMatch=function(a){return a._sResultMatch?a._sResultMatch:null};YAHOO.widget.AutoComplete.prototype.getListItemData=function(a){return a._oResultData?a._oResultData:null};
+YAHOO.widget.AutoComplete.prototype.getListItemIndex=function(a){return YAHOO.lang.isNumber(a._nItemIndex)?a._nItemIndex:null};YAHOO.widget.AutoComplete.prototype.setHeader=function(a){if(this._elHeader){var c=this._elHeader;if(a){c.innerHTML=a;c.style.display=""}else{c.innerHTML="";c.style.display="none"}}};YAHOO.widget.AutoComplete.prototype.setFooter=function(a){if(this._elFooter){var c=this._elFooter;if(a){c.innerHTML=a;c.style.display=""}else{c.innerHTML="";c.style.display="none"}}};
+YAHOO.widget.AutoComplete.prototype.setBody=function(a){if(this._elBody){var c=this._elBody;YAHOO.util.Event.purgeElement(c,true);if(a){c.innerHTML=a;c.style.display=""}else{c.innerHTML="";c.style.display="none"}this._elList=null}};
+YAHOO.widget.AutoComplete.prototype.generateRequest=function(a){var c=this.dataSource.dataType;c===YAHOO.util.DataSourceBase.TYPE_XHR?a=this.dataSource.connMethodPost?(this.dataSource.scriptQueryParam||"query")+"="+a+(this.dataSource.scriptQueryAppend?"&"+this.dataSource.scriptQueryAppend:""):(this.queryQuestionMark?"?":"")+(this.dataSource.scriptQueryParam||"query")+"="+a+(this.dataSource.scriptQueryAppend?"&"+this.dataSource.scriptQueryAppend:""):c===YAHOO.util.DataSourceBase.TYPE_SCRIPTNODE&&(a=
+"&"+(this.dataSource.scriptQueryParam||"query")+"="+a+(this.dataSource.scriptQueryAppend?"&"+this.dataSource.scriptQueryAppend:""));return a};YAHOO.widget.AutoComplete.prototype.sendQuery=function(a){this._bFocused=true;this._sendQuery(this.delimChar?this._elTextbox.value+a:a)};YAHOO.widget.AutoComplete.prototype.snapContainer=function(){var a=this._elTextbox,c=YAHOO.util.Dom.getXY(a);c[1]=c[1]+(YAHOO.util.Dom.get(a).offsetHeight+2);YAHOO.util.Dom.setXY(this._elContainer,c)};
+YAHOO.widget.AutoComplete.prototype.expandContainer=function(){this._toggleContainer(true)};YAHOO.widget.AutoComplete.prototype.collapseContainer=function(){this._toggleContainer(false)};YAHOO.widget.AutoComplete.prototype.clearList=function(){for(var a=this._elList.childNodes,c=a.length-1;c>-1;c--)a[c].style.display="none"};
+YAHOO.widget.AutoComplete.prototype.getSubsetMatches=function(a){for(var c,b=a.length;b>=this.minQueryLength;b--){c=this.generateRequest(a.substr(0,b));this.dataRequestEvent.fire(this,void 0,c);if(c=this.dataSource.getCachedResponse(c))return this.filterResults.apply(this.dataSource,[a,c,c,{scope:this}])}return null};
+YAHOO.widget.AutoComplete.prototype.preparseRawResponse=function(a,c){var b=this.responseStripAfter!==""&&c.indexOf?c.indexOf(this.responseStripAfter):-1;b!=-1&&(c=c.substring(0,b));return c};
+YAHOO.widget.AutoComplete.prototype.filterResults=function(a,c,b,d){if(d&&d.argument&&YAHOO.lang.isValue(d.argument.query))a=d.argument.query;if(a&&a!==""){for(var b=YAHOO.widget.AutoComplete._cloneObject(b),f=d.scope,c=b.results,d=[],g=f.maxResultsDisplayed,j=this.queryMatchCase||f.queryMatchCase,f=this.queryMatchContains||f.queryMatchContains,h=0,e=c.length;h<e;h++){var i=c[h],k=null;YAHOO.lang.isString(i)?k=i:YAHOO.lang.isArray(i)?k=i[0]:this.responseSchema.fields?k=i[this.responseSchema.fields[0].key||
+this.responseSchema.fields[0]]:this.key&&(k=i[this.key]);if(YAHOO.lang.isString(k)){k=j?k.indexOf(decodeURIComponent(a)):k.toLowerCase().indexOf(decodeURIComponent(a).toLowerCase());(!f&&k===0||f&&k>-1)&&d.push(i)}if(e>g&&d.length===g)break}b.results=d}return b};YAHOO.widget.AutoComplete.prototype.handleResponse=function(a,c,b){this instanceof YAHOO.widget.AutoComplete&&this._sName&&this._populateList(a,c,b)};YAHOO.widget.AutoComplete.prototype.doBeforeLoadData=function(){return true};
+YAHOO.widget.AutoComplete.prototype.formatResult=function(a,c,b){return b?b:""};YAHOO.widget.AutoComplete.prototype.formatEscapedResult=function(a,c,b){return YAHOO.lang.escapeHTML(b?b:"")};YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer=function(){return true};
+YAHOO.widget.AutoComplete.prototype.destroy=function(){var a=this._elTextbox,c=this._elContainer;this.textboxFocusEvent.unsubscribeAll();this.textboxKeyEvent.unsubscribeAll();this.textboxKeyUpEvent.unsubscribeAll();this.dataRequestEvent.unsubscribeAll();this.dataReturnEvent.unsubscribeAll();this.dataErrorEvent.unsubscribeAll();this.containerPopulateEvent.unsubscribeAll();this.containerExpandEvent.unsubscribeAll();this.typeAheadEvent.unsubscribeAll();this.itemMouseOverEvent.unsubscribeAll();this.itemMouseOutEvent.unsubscribeAll();
+this.itemArrowToEvent.unsubscribeAll();this.itemArrowFromEvent.unsubscribeAll();this.itemSelectEvent.unsubscribeAll();this.unmatchedItemSelectEvent.unsubscribeAll();this.selectionEnforceEvent.unsubscribeAll();this.containerCollapseEvent.unsubscribeAll();this.textboxBlurEvent.unsubscribeAll();this.textboxChangeEvent.unsubscribeAll();YAHOO.util.Event.purgeElement(a,true);YAHOO.util.Event.purgeElement(c,true);c.innerHTML="";for(var b in this)YAHOO.lang.hasOwnProperty(this,b)&&(this[b]=null)};
+YAHOO.widget.AutoComplete.prototype.textboxFocusEvent=null;YAHOO.widget.AutoComplete.prototype.textboxKeyEvent=null;YAHOO.widget.AutoComplete.prototype.textboxKeyUpEvent=null;YAHOO.widget.AutoComplete.prototype.dataRequestEvent=null;YAHOO.widget.AutoComplete.prototype.dataRequestCancelEvent=null;YAHOO.widget.AutoComplete.prototype.dataReturnEvent=null;YAHOO.widget.AutoComplete.prototype.dataErrorEvent=null;YAHOO.widget.AutoComplete.prototype.containerPopulateEvent=null;
+YAHOO.widget.AutoComplete.prototype.containerExpandEvent=null;YAHOO.widget.AutoComplete.prototype.typeAheadEvent=null;YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent=null;YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent=null;YAHOO.widget.AutoComplete.prototype.itemArrowToEvent=null;YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent=null;YAHOO.widget.AutoComplete.prototype.itemSelectEvent=null;YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent=null;
+YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent=null;YAHOO.widget.AutoComplete.prototype.containerCollapseEvent=null;YAHOO.widget.AutoComplete.prototype.textboxBlurEvent=null;YAHOO.widget.AutoComplete.prototype.textboxChangeEvent=null;YAHOO.widget.AutoComplete._nIndex=0;YAHOO.widget.AutoComplete.prototype._sName=null;YAHOO.widget.AutoComplete.prototype._elTextbox=null;YAHOO.widget.AutoComplete.prototype._elContainer=null;YAHOO.widget.AutoComplete.prototype._elContent=null;
+YAHOO.widget.AutoComplete.prototype._elHeader=null;YAHOO.widget.AutoComplete.prototype._elBody=null;YAHOO.widget.AutoComplete.prototype._elFooter=null;YAHOO.widget.AutoComplete.prototype._elShadow=null;YAHOO.widget.AutoComplete.prototype._elIFrame=null;YAHOO.widget.AutoComplete.prototype._bFocused=!1;YAHOO.widget.AutoComplete.prototype._oAnim=null;YAHOO.widget.AutoComplete.prototype._bContainerOpen=!1;YAHOO.widget.AutoComplete.prototype._bOverContainer=!1;
+YAHOO.widget.AutoComplete.prototype._elList=null;YAHOO.widget.AutoComplete.prototype._nDisplayedItems=0;YAHOO.widget.AutoComplete.prototype._sCurQuery=null;YAHOO.widget.AutoComplete.prototype._sPastSelections="";YAHOO.widget.AutoComplete.prototype._sInitInputValue=null;YAHOO.widget.AutoComplete.prototype._elCurListItem=null;YAHOO.widget.AutoComplete.prototype._elCurPrehighlightItem=null;YAHOO.widget.AutoComplete.prototype._bItemSelected=!1;YAHOO.widget.AutoComplete.prototype._nKeyCode=null;
+YAHOO.widget.AutoComplete.prototype._nDelayID=-1;YAHOO.widget.AutoComplete.prototype._nTypeAheadDelayID=-1;YAHOO.widget.AutoComplete.prototype._iFrameSrc="javascript:false;";YAHOO.widget.AutoComplete.prototype._queryInterval=null;YAHOO.widget.AutoComplete.prototype._sLastTextboxValue=null;
+YAHOO.widget.AutoComplete.prototype._initProps=function(){if(!YAHOO.lang.isNumber(this.minQueryLength))this.minQueryLength=1;var a=this.maxResultsDisplayed;if(!YAHOO.lang.isNumber(a)||a<1)this.maxResultsDisplayed=10;a=this.queryDelay;if(!YAHOO.lang.isNumber(a)||a<0)this.queryDelay=0.2;a=this.typeAheadDelay;if(!YAHOO.lang.isNumber(a)||a<0)this.typeAheadDelay=0.2;a=this.delimChar;if(YAHOO.lang.isString(a)&&a.length>0)this.delimChar=[a];else if(!YAHOO.lang.isArray(a))this.delimChar=null;a=this.animSpeed;
+if((this.animHoriz||this.animVert)&&YAHOO.util.Anim){if(!YAHOO.lang.isNumber(a)||a<0)this.animSpeed=0.3;this._oAnim?this._oAnim.duration=this.animSpeed:this._oAnim=new YAHOO.util.Anim(this._elContent,{},this.animSpeed)}};
+YAHOO.widget.AutoComplete.prototype._initContainerHelperEls=function(){if(this.useShadow&&!this._elShadow){var a=document.createElement("div");a.className="yui-ac-shadow";a.style.width=0;a.style.height=0;this._elShadow=this._elContainer.appendChild(a)}if(this.useIFrame&&!this._elIFrame){a=document.createElement("iframe");a.src=this._iFrameSrc;a.frameBorder=0;a.scrolling="no";a.style.position="absolute";a.style.width=0;a.style.height=0;a.style.padding=0;a.tabIndex=-1;a.role="presentation";a.title=
+"Presentational iframe shim";this._elIFrame=this._elContainer.appendChild(a)}};
+YAHOO.widget.AutoComplete.prototype._initContainerEl=function(){YAHOO.util.Dom.addClass(this._elContainer,"yui-ac-container");if(!this._elContent){var a=document.createElement("div");a.className="yui-ac-content";a.style.display="none";this._elContent=this._elContainer.appendChild(a);a=document.createElement("div");a.className="yui-ac-hd";a.style.display="none";this._elHeader=this._elContent.appendChild(a);a=document.createElement("div");a.className="yui-ac-bd";this._elBody=this._elContent.appendChild(a);
+a=document.createElement("div");a.className="yui-ac-ft";a.style.display="none";this._elFooter=this._elContent.appendChild(a)}};
+YAHOO.widget.AutoComplete.prototype._initListEl=function(){for(var a=this.maxResultsDisplayed,c=this._elList||document.createElement("ul"),b;c.childNodes.length<a;){b=document.createElement("li");b.style.display="none";b._nItemIndex=c.childNodes.length;c.appendChild(b)}if(!this._elList){a=this._elBody;YAHOO.util.Event.purgeElement(a,true);a.innerHTML="";this._elList=a.appendChild(c)}this._elBody.style.display=""};
+YAHOO.widget.AutoComplete.prototype._focus=function(){var a=this;setTimeout(function(){try{a._elTextbox.focus()}catch(c){}},0)};YAHOO.widget.AutoComplete.prototype._enableIntervalDetection=function(){var a=this;if(!a._queryInterval&&a.queryInterval)a._queryInterval=setInterval(function(){a._onInterval()},a.queryInterval)};YAHOO.widget.AutoComplete.prototype.enableIntervalDetection=YAHOO.widget.AutoComplete.prototype._enableIntervalDetection;
+YAHOO.widget.AutoComplete.prototype._onInterval=function(){var a=this._elTextbox.value;if(a!=this._sLastTextboxValue){this._sLastTextboxValue=a;this._sendQuery(a)}};YAHOO.widget.AutoComplete.prototype._clearInterval=function(){if(this._queryInterval){clearInterval(this._queryInterval);this._queryInterval=null}};YAHOO.widget.AutoComplete.prototype._isIgnoreKey=function(a){return a==9||a==13||a==16||a==17||a>=18&&a<=20||a==27||a>=33&&a<=35||a>=36&&a<=40||a>=44&&a<=45||a==229?true:false};
+YAHOO.widget.AutoComplete.prototype._sendQuery=function(a){if(this.minQueryLength<0)this._toggleContainer(false);else{if(this.delimChar){var c=this._extractQuery(a),a=c.query;this._sPastSelections=c.previous}if(a&&a.length<this.minQueryLength||!a&&this.minQueryLength>0){this._nDelayID!=-1&&clearTimeout(this._nDelayID);this._toggleContainer(false)}else{a=encodeURIComponent(a);this._nDelayID=-1;if(this.dataSource.queryMatchSubset||this.queryMatchSubset)if(c=this.getSubsetMatches(a)){this.handleResponse(a,
+c,{query:a});return}if(this.dataSource.responseStripAfter)this.dataSource.doBeforeParseData=this.preparseRawResponse;if(this.applyLocalFilter)this.dataSource.doBeforeCallback=this.filterResults;c=this.generateRequest(a);if(c!==void 0){this.dataRequestEvent.fire(this,a,c);this.dataSource.sendRequest(c,{success:this.handleResponse,failure:this.handleResponse,scope:this,argument:{query:a}})}else this.dataRequestCancelEvent.fire(this,a)}}};
+YAHOO.widget.AutoComplete.prototype._populateListItem=function(a,c,b){a.innerHTML=this.formatResult(c,b,a._sResultMatch)};
+YAHOO.widget.AutoComplete.prototype._populateList=function(a,c,b){this._nTypeAheadDelayID!=-1&&clearTimeout(this._nTypeAheadDelayID);a=b&&b.query?b.query:a;if((b=this.doBeforeLoadData(a,c,b))&&!c.error){this.dataReturnEvent.fire(this,a,c.results);if(this._bFocused){var d=decodeURIComponent(a);this._sCurQuery=d;this._bItemSelected=false;var c=c.results,b=Math.min(c.length,this.maxResultsDisplayed),f=this.dataSource.responseSchema.fields?this.dataSource.responseSchema.fields[0].key||this.dataSource.responseSchema.fields[0]:
+0;if(b>0){(!this._elList||this._elList.childNodes.length<b)&&this._initListEl();this._initContainerHelperEls();for(var g=this._elList.childNodes,j=b-1;j>=0;j--){var h=g[j],e=c[j];if(this.resultTypeList){var i=[];i[0]=YAHOO.lang.isString(e)?e:e[f]||e[this.key];var k=this.dataSource.responseSchema.fields;if(YAHOO.lang.isArray(k)&&k.length>1)for(var l=1,q=k.length;l<q;l++)i[i.length]=e[k[l].key||k[l]];else YAHOO.lang.isArray(e)?i=e:YAHOO.lang.isString(e)?i=[e]:i[1]=e;e=i}h._sResultMatch=YAHOO.lang.isString(e)?
+e:YAHOO.lang.isArray(e)?e[0]:e[f]||"";h._oResultData=e;this._populateListItem(h,e,d);h.style.display=""}if(b<g.length)for(f=g.length-1;f>=b;f--){d=g[f];d.style.display="none"}this._nDisplayedItems=b;this.containerPopulateEvent.fire(this,a,c);if(this.autoHighlight){b=this._elList.firstChild;this._toggleHighlight(b,"to");this.itemArrowToEvent.fire(this,b);this._typeAhead(b,a)}else this._toggleHighlight(this._elCurListItem,"from");b=this._doBeforeExpandContainer(this._elTextbox,this._elContainer,a,c);
+this._toggleContainer(b)}else this._toggleContainer(false)}}else this.dataErrorEvent.fire(this,a,c)};YAHOO.widget.AutoComplete.prototype._doBeforeExpandContainer=function(a,c,b,d){this.autoSnapContainer&&this.snapContainer();return this.doBeforeExpandContainer(a,c,b,d)};
+YAHOO.widget.AutoComplete.prototype._clearSelection=function(){var a=this.delimChar?this._extractQuery(this._elTextbox.value):{previous:"",query:this._elTextbox.value};this._elTextbox.value=a.previous;this.selectionEnforceEvent.fire(this,a.query)};YAHOO.widget.AutoComplete.prototype._textMatchesOption=function(){for(var a=null,c=0;c<this._nDisplayedItems;c++){var b=this._elList.childNodes[c];if((""+b._sResultMatch).toLowerCase()==this._sCurQuery.toLowerCase()){a=b;break}}return a};
+YAHOO.widget.AutoComplete.prototype._typeAhead=function(a,c){if(this.typeAhead&&this._nKeyCode!=8){var b=this,d=this._elTextbox;if(d.setSelectionRange||d.createTextRange)this._nTypeAheadDelayID=setTimeout(function(){var f=d.value.length;b._updateValue(a);var g=d.value.length;b._selectText(d,f,g);f=d.value.substr(f,g);b._sCurQuery=a._sResultMatch;b.typeAheadEvent.fire(b,c,f)},this.typeAheadDelay*1E3)}};
+YAHOO.widget.AutoComplete.prototype._selectText=function(a,c,b){if(a.setSelectionRange)a.setSelectionRange(c,b);else if(a.createTextRange){var d=a.createTextRange();d.moveStart("character",c);d.moveEnd("character",b-a.value.length);d.select()}else a.select()};
+YAHOO.widget.AutoComplete.prototype._extractQuery=function(a){for(var c=this.delimChar,b=-1,d,f=c.length-1;f>=0;f--){d=a.lastIndexOf(c[f]);d>b&&(b=d)}if(c[f]==" ")for(d=c.length-1;d>=0;d--)if(a[b-1]==c[d]){b--;break}if(b>-1){for(c=b+1;a.charAt(c)==" ";)c=c+1;b=a.substring(0,c);a=a.substr(c)}else b="";return{previous:b,query:a}};
+YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers=function(a){var c=this._elContent.offsetWidth+"px",b=this._elContent.offsetHeight+"px";if(this.useIFrame&&this._elIFrame){var d=this._elIFrame;if(a){d.style.width=c;d.style.height=b;d.style.padding=""}else{d.style.width=0;d.style.height=0;d.style.padding=0}}if(this.useShadow&&this._elShadow){d=this._elShadow;if(a){d.style.width=c;d.style.height=b}else{d.style.width=0;d.style.height=0}}};
+YAHOO.widget.AutoComplete.prototype._toggleContainer=function(a){var c=this._elContainer;if(!this.alwaysShowContainer||!this._bContainerOpen){if(!a){this._toggleHighlight(this._elCurListItem,"from");this._nDisplayedItems=0;this._sCurQuery=null;if(this._elContent.style.display=="none")return}var b=this._oAnim;if(b&&b.getEl()&&(this.animHoriz||this.animVert)){b.isAnimated()&&b.stop(true);var d=this._elContent.cloneNode(true);c.appendChild(d);d.style.top="-9000px";d.style.width="";d.style.height="";
+d.style.display="";var f=d.offsetWidth,g=d.offsetHeight,j=this.animHoriz?0:f,h=this.animVert?0:g;b.attributes=a?{width:{to:f},height:{to:g}}:{width:{to:j},height:{to:h}};if(a&&!this._bContainerOpen){this._elContent.style.width=j+"px";this._elContent.style.height=h+"px"}else{this._elContent.style.width=f+"px";this._elContent.style.height=g+"px"}c.removeChild(d);var d=null,e=this;this._toggleContainerHelpers(false);this._elContent.style.display="";b.onComplete.subscribe(function(){b.onComplete.unsubscribeAll();
+if(a){e._toggleContainerHelpers(true);e._bContainerOpen=a;e.containerExpandEvent.fire(e)}else{e._elContent.style.display="none";e._bContainerOpen=a;e.containerCollapseEvent.fire(e)}});b.animate()}else if(a){this._elContent.style.display="";this._toggleContainerHelpers(true);this._bContainerOpen=a;this.containerExpandEvent.fire(this)}else{this._toggleContainerHelpers(false);this._elContent.style.display="none";this._bContainerOpen=a;this.containerCollapseEvent.fire(this)}}};
+YAHOO.widget.AutoComplete.prototype._toggleHighlight=function(a,c){if(a){var b=this.highlightClassName;if(this._elCurListItem){YAHOO.util.Dom.removeClass(this._elCurListItem,b);this._elCurListItem=null}if(c=="to"&&b){YAHOO.util.Dom.addClass(a,b);this._elCurListItem=a}}};
+YAHOO.widget.AutoComplete.prototype._togglePrehighlight=function(a,c){var b=this.prehighlightClassName;this._elCurPrehighlightItem&&YAHOO.util.Dom.removeClass(this._elCurPrehighlightItem,b);if(a!=this._elCurListItem)if(c=="mouseover"&&b){YAHOO.util.Dom.addClass(a,b);this._elCurPrehighlightItem=a}else YAHOO.util.Dom.removeClass(a,b)};
+YAHOO.widget.AutoComplete.prototype._updateValue=function(a){if(!this.suppressInputUpdate){var c=this._elTextbox,b=this.delimChar?this.delimChar[0]||this.delimChar:null,d=a._sResultMatch,f="";if(b){f=this._sPastSelections;f=f+(d+b);b!=" "&&(f=f+" ")}else f=d;c.value=f;if(c.type=="textarea")c.scrollTop=c.scrollHeight;b=c.value.length;this._selectText(c,b,b);this._elCurListItem=a}};
+YAHOO.widget.AutoComplete.prototype._selectItem=function(a){this._bItemSelected=true;this._updateValue(a);this._sPastSelections=this._elTextbox.value;this._clearInterval();this.itemSelectEvent.fire(this,a,a._oResultData);this._toggleContainer(false)};YAHOO.widget.AutoComplete.prototype._jumpSelection=function(){this._elCurListItem?this._selectItem(this._elCurListItem):this._toggleContainer(false)};
+YAHOO.widget.AutoComplete.prototype._moveSelection=function(a){if(this._bContainerOpen){var c=this._elCurListItem,b=-1;if(c)b=c._nItemIndex;b=a==40?b+1:b-1;if(!(b<-2||b>=this._nDisplayedItems)){if(c){this._toggleHighlight(c,"from");this.itemArrowFromEvent.fire(this,c)}if(b==-1)this._elTextbox.value=this.delimChar?this._sPastSelections+this._sCurQuery:this._sCurQuery;else if(b==-2)this._toggleContainer(false);else{var c=this._elList.childNodes[b],d=this._elContent,f=YAHOO.util.Dom.getStyle(d,"overflow"),
+g=YAHOO.util.Dom.getStyle(d,"overflowY");if((f=="auto"||f=="scroll"||g=="auto"||g=="scroll")&&b>-1&&b<this._nDisplayedItems)if(a==40)if(c.offsetTop+c.offsetHeight>d.scrollTop+d.offsetHeight)d.scrollTop=c.offsetTop+c.offsetHeight-d.offsetHeight;else{if(c.offsetTop+c.offsetHeight<d.scrollTop)d.scrollTop=c.offsetTop}else if(c.offsetTop<d.scrollTop)this._elContent.scrollTop=c.offsetTop;else if(c.offsetTop>d.scrollTop+d.offsetHeight)this._elContent.scrollTop=c.offsetTop+c.offsetHeight-d.offsetHeight;this._toggleHighlight(c,
+"to");this.itemArrowToEvent.fire(this,c);if(this.typeAhead){this._updateValue(c);this._sCurQuery=c._sResultMatch}}}}};
+YAHOO.widget.AutoComplete.prototype._onContainerMouseover=function(a,c){for(var b=YAHOO.util.Event.getTarget(a),d=b.nodeName.toLowerCase();b&&d!="table";){switch(d){case "body":return;case "li":c.prehighlightClassName?c._togglePrehighlight(b,"mouseover"):c._toggleHighlight(b,"to");c.itemMouseOverEvent.fire(c,b);break;case "div":if(YAHOO.util.Dom.hasClass(b,"yui-ac-container")){c._bOverContainer=true;return}}(b=b.parentNode)&&(d=b.nodeName.toLowerCase())}};
+YAHOO.widget.AutoComplete.prototype._onContainerMouseout=function(a,c){for(var b=YAHOO.util.Event.getTarget(a),d=b.nodeName.toLowerCase();b&&d!="table";){switch(d){case "body":return;case "li":c.prehighlightClassName?c._togglePrehighlight(b,"mouseout"):c._toggleHighlight(b,"from");c.itemMouseOutEvent.fire(c,b);break;case "ul":c._toggleHighlight(c._elCurListItem,"to");break;case "div":if(YAHOO.util.Dom.hasClass(b,"yui-ac-container")){c._bOverContainer=false;return}}(b=b.parentNode)&&(d=b.nodeName.toLowerCase())}};
+YAHOO.widget.AutoComplete.prototype._onContainerClick=function(a,c){for(var b=YAHOO.util.Event.getTarget(a),d=b.nodeName.toLowerCase();b&&d!="table";){switch(d){case "body":return;case "li":c._toggleHighlight(b,"to");c._selectItem(b);return}(b=b.parentNode)&&(d=b.nodeName.toLowerCase())}};YAHOO.widget.AutoComplete.prototype._onContainerScroll=function(a,c){c._focus()};YAHOO.widget.AutoComplete.prototype._onContainerResize=function(a,c){c._toggleContainerHelpers(c._bContainerOpen)};
+YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown=function(a,c){var b=a.keyCode;c._nTypeAheadDelayID!=-1&&clearTimeout(c._nTypeAheadDelayID);switch(b){case 9:if(!YAHOO.env.ua.opera&&navigator.userAgent.toLowerCase().indexOf("mac")==-1||YAHOO.env.ua.webkit>420)if(c._elCurListItem){c.delimChar&&c._nKeyCode!=b&&c._bContainerOpen&&YAHOO.util.Event.stopEvent(a);c._selectItem(c._elCurListItem)}else c._toggleContainer(false);break;case 13:if(!YAHOO.env.ua.opera&&navigator.userAgent.toLowerCase().indexOf("mac")==
+-1||YAHOO.env.ua.webkit>420)if(c._elCurListItem){c._nKeyCode!=b&&c._bContainerOpen&&YAHOO.util.Event.stopEvent(a);c._selectItem(c._elCurListItem)}else c._toggleContainer(false);break;case 27:c._toggleContainer(false);return;case 39:c._jumpSelection();break;case 38:if(c._bContainerOpen){YAHOO.util.Event.stopEvent(a);c._moveSelection(b)}break;case 40:if(c._bContainerOpen){YAHOO.util.Event.stopEvent(a);c._moveSelection(b)}break;default:c._bItemSelected=false;c._toggleHighlight(c._elCurListItem,"from");
+c.textboxKeyEvent.fire(c,b)}b===18&&c._enableIntervalDetection();c._nKeyCode=b};
+YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress=function(a,c){var b=a.keyCode;if(YAHOO.env.ua.opera||navigator.userAgent.toLowerCase().indexOf("mac")!=-1&&YAHOO.env.ua.webkit<420)switch(b){case 9:if(c._bContainerOpen){c.delimChar&&YAHOO.util.Event.stopEvent(a);c._elCurListItem?c._selectItem(c._elCurListItem):c._toggleContainer(false)}break;case 13:if(c._bContainerOpen){YAHOO.util.Event.stopEvent(a);c._elCurListItem?c._selectItem(c._elCurListItem):c._toggleContainer(false)}}else b==229&&c._enableIntervalDetection()};
+YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp=function(a,c){var b=this.value;c._initProps();if(!c._isIgnoreKey(a.keyCode)){c._nDelayID!=-1&&clearTimeout(c._nDelayID);c._nDelayID=setTimeout(function(){c._sendQuery(b)},c.queryDelay*1E3);c.textboxKeyUpEvent.fire(c,b)}};YAHOO.widget.AutoComplete.prototype._onTextboxFocus=function(a,c){if(!c._bFocused){c._elTextbox.setAttribute("autocomplete","off");c._bFocused=true;c._sInitInputValue=c._elTextbox.value;c.textboxFocusEvent.fire(c)}};
+YAHOO.widget.AutoComplete.prototype._onTextboxBlur=function(a,c){if(!c._bOverContainer||c._nKeyCode==9){if(!c._bItemSelected){var b=c._textMatchesOption();!c._bContainerOpen||c._bContainerOpen&&b===null?c.forceSelection?c._clearSelection():c.unmatchedItemSelectEvent.fire(c,c._sCurQuery):c.forceSelection&&c._selectItem(b)}c._clearInterval();c._bFocused=false;c._sInitInputValue!==c._elTextbox.value&&c.textboxChangeEvent.fire(c);c.textboxBlurEvent.fire(c);c._toggleContainer(false)}else c._focus()};
+YAHOO.widget.AutoComplete.prototype._onWindowUnload=function(a,c){c&&(c._elTextbox&&c.allowBrowserAutocomplete)&&c._elTextbox.setAttribute("autocomplete","on")};YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery=function(a){return this.generateRequest(a)};YAHOO.widget.AutoComplete.prototype.getListItems=function(){for(var a=[],c=this._elList.childNodes,b=c.length-1;b>=0;b--)a[b]=c[b];return a};
+YAHOO.widget.AutoComplete._cloneObject=function(a){if(!YAHOO.lang.isValue(a))return a;var c={};if(YAHOO.lang.isFunction(a))c=a;else if(YAHOO.lang.isArray(a))for(var c=[],b=0,d=a.length;b<d;b++)c[b]=YAHOO.widget.AutoComplete._cloneObject(a[b]);else if(YAHOO.lang.isObject(a))for(b in a)YAHOO.lang.hasOwnProperty(a,b)&&(c[b]=YAHOO.lang.isValue(a[b])&&YAHOO.lang.isObject(a[b])||YAHOO.lang.isArray(a[b])?YAHOO.widget.AutoComplete._cloneObject(a[b]):a[b]);else c=a;return c};
+YAHOO.register("autocomplete",YAHOO.widget.AutoComplete,{version:"2.9.0",build:"2800"});
+(function(){YAHOO.util.Config=function(a){a&&this.init(a)};var a=YAHOO.lang,c=YAHOO.util.CustomEvent,b=YAHOO.util.Config;b.CONFIG_CHANGED_EVENT="configChanged";b.BOOLEAN_TYPE="boolean";b.prototype={owner:null,queueInProgress:false,config:null,initialConfig:null,eventQueue:null,configChangedEvent:null,init:function(a){this.owner=a;this.configChangedEvent=this.createEvent(b.CONFIG_CHANGED_EVENT);this.configChangedEvent.signature=c.LIST;this.queueInProgress=false;this.config={};this.initialConfig={};
+this.eventQueue=[]},checkBoolean:function(a){return typeof a==b.BOOLEAN_TYPE},checkNumber:function(a){return!isNaN(a)},fireEvent:function(a,b){var c=this.config[a];c&&c.event&&c.event.fire(b)},addProperty:function(a,b){a=a.toLowerCase();this.config[a]=b;b.event=this.createEvent(a,{scope:this.owner});b.event.signature=c.LIST;b.key=a;b.handler&&b.event.subscribe(b.handler,this.owner);this.setProperty(a,b.value,true);b.suppressEvent||this.queueProperty(a,b.value)},getConfig:function(){var b={},c=this.config,
+g,j;for(g in c)if(a.hasOwnProperty(c,g))if((j=c[g])&&j.event)b[g]=j.value;return b},getProperty:function(a){if((a=this.config[a.toLowerCase()])&&a.event)return a.value},resetProperty:function(a){var a=a.toLowerCase(),b=this.config[a];if(b&&b.event){if(a in this.initialConfig){this.setProperty(a,this.initialConfig[a]);return true}}else return false},setProperty:function(a,b,c){var j,a=a.toLowerCase();if(this.queueInProgress&&!c){this.queueProperty(a,b);return true}if((j=this.config[a])&&j.event){if(j.validator&&
+!j.validator(b))return false;j.value=b;if(!c){this.fireEvent(a,b);this.configChangedEvent.fire([a,b])}return true}return false},queueProperty:function(b,c){var b=b.toLowerCase(),g=this.config[b],j=false,h,e,i,k,l,q;if(g&&g.event){if(!a.isUndefined(c)&&g.validator&&!g.validator(c))return false;a.isUndefined(c)?c=g.value:g.value=c;j=false;h=this.eventQueue.length;for(l=0;l<h;l++)if(e=this.eventQueue[l]){i=e[0];e=e[1];if(i==b){this.eventQueue[l]=null;this.eventQueue.push([b,!a.isUndefined(c)?c:e]);j=
+true;break}}!j&&!a.isUndefined(c)&&this.eventQueue.push([b,c]);if(g.supercedes){j=g.supercedes.length;for(e=0;e<j;e++){h=g.supercedes[e];i=this.eventQueue.length;for(q=0;q<i;q++)if(k=this.eventQueue[q]){l=k[0];k=k[1];if(l==h.toLowerCase()){this.eventQueue.push([l,k]);this.eventQueue[q]=null;break}}}}return true}return false},refireEvent:function(b){var b=b.toLowerCase(),c=this.config[b];c&&(c.event&&!a.isUndefined(c.value))&&(this.queueInProgress?this.queueProperty(b):this.fireEvent(b,c.value))},
+applyConfig:function(b,c){var g,j;if(c){j={};for(g in b)a.hasOwnProperty(b,g)&&(j[g.toLowerCase()]=b[g]);this.initialConfig=j}for(g in b)a.hasOwnProperty(b,g)&&this.queueProperty(g,b[g])},refresh:function(){for(var b in this.config)a.hasOwnProperty(this.config,b)&&this.refireEvent(b)},fireQueue:function(){var a,b,c,j;this.queueInProgress=true;for(a=0;a<this.eventQueue.length;a++)if(b=this.eventQueue[a]){c=b[0];b=b[1];j=this.config[c];j.value=b;this.eventQueue[a]=null;this.fireEvent(c,b)}this.queueInProgress=
+false;this.eventQueue=[]},subscribeToConfigEvent:function(a,c,g,j){if((a=this.config[a.toLowerCase()])&&a.event){b.alreadySubscribed(a.event,c,g)||a.event.subscribe(c,g,j);return true}return false},unsubscribeFromConfigEvent:function(a,b,c){return(a=this.config[a.toLowerCase()])&&a.event?a.event.unsubscribe(b,c):false},toString:function(){var a="Config";this.owner&&(a=a+(" ["+this.owner.toString()+"]"));return a},outputEventQueue:function(){var a="",b,c,j=this.eventQueue.length;for(c=0;c<j;c++)(b=
+this.eventQueue[c])&&(a=a+(b[0]+"="+b[1]+", "));return a},destroy:function(){var b=this.config,c,g;for(c in b)if(a.hasOwnProperty(b,c)){g=b[c];g.event.unsubscribeAll();g.event=null}this.configChangedEvent.unsubscribeAll();this.eventQueue=this.initialConfig=this.config=this.owner=this.configChangedEvent=null}};b.alreadySubscribed=function(a,b,c){var j=a.subscribers.length,h;if(j>0){h=j-1;do if((j=a.subscribers[h])&&j.obj==c&&j.fn==b)return true;while(h--)}return false};YAHOO.lang.augmentProto(b,YAHOO.util.EventProvider)})();
+(function(){function a(){if(!k){k=document.createElement("div");k.innerHTML='<div class="'+e.CSS_HEADER+'"></div><div class="'+e.CSS_BODY+'"></div><div class="'+e.CSS_FOOTER+'"></div>';l=k.firstChild;q=l.nextSibling;p=q.nextSibling}return k}function c(){l||a();return l.cloneNode(false)}function b(){q||a();return q.cloneNode(false)}function d(){p||a();return p.cloneNode(false)}YAHOO.widget.Module=function(a,e){a&&this.init(a,e)};var f=YAHOO.util.Dom,g=YAHOO.util.Config,j=YAHOO.util.Event,h=YAHOO.util.CustomEvent,
+e=YAHOO.widget.Module,i=YAHOO.env.ua,k,l,q,p,n=YAHOO.lang.isBoolean,o=["visible"];e.IMG_ROOT=null;e.IMG_ROOT_SSL=null;e.CSS_MODULE="yui-module";e.CSS_HEADER="hd";e.CSS_BODY="bd";e.CSS_FOOTER="ft";e.RESIZE_MONITOR_SECURE_URL="javascript:false;";e.RESIZE_MONITOR_BUFFER=1;e.textResizeEvent=new h("textResize");e.forceDocumentRedraw=function(){var a=document.documentElement;if(a){a.className=a.className+" ";a.className=YAHOO.lang.trim(a.className)}};var m=e,r=e,s=e.IMG_ROOT,t=function(){var a=navigator.userAgent.toLowerCase();
+return a.indexOf("windows")!=-1||a.indexOf("win32")!=-1?"windows":a.indexOf("macintosh")!=-1?"mac":false}(),u=function(){var a=navigator.userAgent.toLowerCase();return a.indexOf("opera")!=-1?"opera":a.indexOf("msie 7")!=-1?"ie7":a.indexOf("msie")!=-1?"ie":a.indexOf("safari")!=-1?"safari":a.indexOf("gecko")!=-1?"gecko":false}(),w;w=window.location.href.toLowerCase().indexOf("https")===0?true:false;m.prototype={constructor:r,element:null,header:null,body:null,footer:null,id:null,imageRoot:s,initEvents:function(){var a=
+h.LIST;this.beforeInitEvent=this.createEvent("beforeInit");this.beforeInitEvent.signature=a;this.initEvent=this.createEvent("init");this.initEvent.signature=a;this.appendEvent=this.createEvent("append");this.appendEvent.signature=a;this.beforeRenderEvent=this.createEvent("beforeRender");this.beforeRenderEvent.signature=a;this.renderEvent=this.createEvent("render");this.renderEvent.signature=a;this.changeHeaderEvent=this.createEvent("changeHeader");this.changeHeaderEvent.signature=a;this.changeBodyEvent=
+this.createEvent("changeBody");this.changeBodyEvent.signature=a;this.changeFooterEvent=this.createEvent("changeFooter");this.changeFooterEvent.signature=a;this.changeContentEvent=this.createEvent("changeContent");this.changeContentEvent.signature=a;this.destroyEvent=this.createEvent("destroy");this.destroyEvent.signature=a;this.beforeShowEvent=this.createEvent("beforeShow");this.beforeShowEvent.signature=a;this.showEvent=this.createEvent("show");this.showEvent.signature=a;this.beforeHideEvent=this.createEvent("beforeHide");
+this.beforeHideEvent.signature=a;this.hideEvent=this.createEvent("hide");this.hideEvent.signature=a},platform:t,browser:u,isSecure:w,initDefaultConfig:function(){this.cfg.addProperty("visible",{handler:this.configVisible,value:true,validator:n});this.cfg.addProperty("effect",{handler:this.configEffect,suppressEvent:true,supercedes:o});this.cfg.addProperty("monitorresize",{handler:this.configMonitorResize,value:true});this.cfg.addProperty("appendtodocumentbody",{value:false})},init:function(b,i){var c;
+this.initEvents();this.beforeInitEvent.fire(e);this.cfg=new g(this);if(this.isSecure)this.imageRoot=e.IMG_ROOT_SSL;if(typeof b=="string"){c=b;b=document.getElementById(b);if(!b){b=a().cloneNode(false);b.id=c}}this.id=f.generateId(b);this.element=b;if(c=this.element.firstChild){var d=false,k=false,l=false;do if(1==c.nodeType)if(!d&&f.hasClass(c,e.CSS_HEADER)){this.header=c;d=true}else if(!k&&f.hasClass(c,e.CSS_BODY)){this.body=c;k=true}else if(!l&&f.hasClass(c,e.CSS_FOOTER)){this.footer=c;l=true}while(c=
+c.nextSibling)}this.initDefaultConfig();f.addClass(this.element,e.CSS_MODULE);i&&this.cfg.applyConfig(i,true);g.alreadySubscribed(this.renderEvent,this.cfg.fireQueue,this.cfg)||this.renderEvent.subscribe(this.cfg.fireQueue,this.cfg,true);this.initEvent.fire(e)},initResizeMonitor:function(){if(i.gecko&&this.platform=="windows"){var a=this;setTimeout(function(){a._initResizeMonitor()},0)}else this._initResizeMonitor()},_initResizeMonitor:function(){function a(){e.textResizeEvent.fire()}var b,c;if(!i.opera){c=
+f.get("_yuiResizeMonitor");var d=this._supportsCWResize();if(!c){c=document.createElement("iframe");if(this.isSecure&&e.RESIZE_MONITOR_SECURE_URL&&i.ie)c.src=e.RESIZE_MONITOR_SECURE_URL;if(!d)c.src="data:text/html;charset=utf-8,"+encodeURIComponent('<html><head><script type="text/javascript">window.onresize=function(){window.parent.YAHOO.widget.Module.textResizeEvent.fire();};<\/script></head><body></body></html>');c.id="_yuiResizeMonitor";c.title="Text Resize Monitor";c.tabIndex=-1;c.setAttribute("role",
+"presentation");c.style.position="absolute";c.style.visibility="hidden";b=document.body;var k=b.firstChild;k?b.insertBefore(c,k):b.appendChild(c);c.style.backgroundColor="transparent";c.style.borderWidth="0";c.style.width="2em";c.style.height="2em";c.style.left="0";c.style.top=-1*(c.offsetHeight+e.RESIZE_MONITOR_BUFFER)+"px";c.style.visibility="visible";if(i.webkit){b=c.contentWindow.document;b.open();b.close()}}if(c&&c.contentWindow){e.textResizeEvent.subscribe(this.onDomResize,this,true);if(!e.textResizeInitialized){if(d&&
+!j.on(c.contentWindow,"resize",a))j.on(c,"resize",a);e.textResizeInitialized=true}this.resizeMonitor=c}}},_supportsCWResize:function(){var a=true;i.gecko&&i.gecko<=1.8&&(a=false);return a},onDomResize:function(){this.resizeMonitor.style.top=-1*(this.resizeMonitor.offsetHeight+e.RESIZE_MONITOR_BUFFER)+"px";this.resizeMonitor.style.left="0"},setHeader:function(a){var e=this.header||(this.header=c());if(a.nodeName){e.innerHTML="";e.appendChild(a)}else e.innerHTML=a;this._rendered&&this._renderHeader();
+this.changeHeaderEvent.fire(a);this.changeContentEvent.fire()},appendToHeader:function(a){(this.header||(this.header=c())).appendChild(a);this.changeHeaderEvent.fire(a);this.changeContentEvent.fire()},setBody:function(a){var e=this.body||(this.body=b());if(a.nodeName){e.innerHTML="";e.appendChild(a)}else e.innerHTML=a;this._rendered&&this._renderBody();this.changeBodyEvent.fire(a);this.changeContentEvent.fire()},appendToBody:function(a){(this.body||(this.body=b())).appendChild(a);this.changeBodyEvent.fire(a);
+this.changeContentEvent.fire()},setFooter:function(a){var e=this.footer||(this.footer=d());if(a.nodeName){e.innerHTML="";e.appendChild(a)}else e.innerHTML=a;this._rendered&&this._renderFooter();this.changeFooterEvent.fire(a);this.changeContentEvent.fire()},appendToFooter:function(a){(this.footer||(this.footer=d())).appendChild(a);this.changeFooterEvent.fire(a);this.changeContentEvent.fire()},render:function(a,e){this.beforeRenderEvent.fire();if(!e)e=this.element;if(a){var b=a;typeof b=="string"&&
+(b=document.getElementById(b));if(b){this._addToParent(b,this.element);this.appendEvent.fire()}}else if(!f.inDocument(this.element))return false;this._renderHeader(e);this._renderBody(e);this._renderFooter(e);this._rendered=true;this.renderEvent.fire();return true},_renderHeader:function(a){a=a||this.element;if(this.header&&!f.inDocument(this.header)){var e=a.firstChild;e?a.insertBefore(this.header,e):a.appendChild(this.header)}},_renderBody:function(a){a=a||this.element;this.body&&!f.inDocument(this.body)&&
+(this.footer&&f.isAncestor(a,this.footer)?a.insertBefore(this.body,this.footer):a.appendChild(this.body))},_renderFooter:function(a){a=a||this.element;this.footer&&!f.inDocument(this.footer)&&a.appendChild(this.footer)},destroy:function(a){var b,a=!a;if(this.element){j.purgeElement(this.element,a);b=this.element.parentNode}b&&b.removeChild(this.element);this.footer=this.body=this.header=this.element=null;e.textResizeEvent.unsubscribe(this.onDomResize,this);this.cfg.destroy();this.cfg=null;this.destroyEvent.fire()},
+show:function(){this.cfg.setProperty("visible",true)},hide:function(){this.cfg.setProperty("visible",false)},configVisible:function(a,e){if(e[0]){if(this.beforeShowEvent.fire()){f.setStyle(this.element,"display","block");this.showEvent.fire()}}else if(this.beforeHideEvent.fire()){f.setStyle(this.element,"display","none");this.hideEvent.fire()}},configEffect:function(a,e){this._cachedEffects=this.cacheEffects?this._createEffects(e[0]):null},cacheEffects:true,_createEffects:function(a){var e=null,b,
+i,c;if(a)if(a instanceof Array){e=[];b=a.length;for(i=0;i<b;i++){c=a[i];c.effect&&(e[e.length]=c.effect(this,c.duration))}}else a.effect&&(e=[a.effect(this,a.duration)]);return e},configMonitorResize:function(a,b){if(b[0])this.initResizeMonitor();else{e.textResizeEvent.unsubscribe(this.onDomResize,this,true);this.resizeMonitor=null}},_addToParent:function(a,e){!this.cfg.getProperty("appendtodocumentbody")&&a===document.body&&a.firstChild?a.insertBefore(e,a.firstChild):a.appendChild(e)},toString:function(){return"Module "+
+this.id}};YAHOO.lang.augmentProto(e,YAHOO.util.EventProvider)})();
+(function(){YAHOO.widget.Overlay=function(a,e){YAHOO.widget.Overlay.superclass.constructor.call(this,a,e)};var a=YAHOO.lang,c=YAHOO.util.CustomEvent,b=YAHOO.widget.Module,d=YAHOO.util.Event,f=YAHOO.util.Dom,g=YAHOO.util.Config,j=YAHOO.env.ua,h=YAHOO.widget.Overlay,e,i=a.isNumber,k=["iframe"],l=a.isNumber,q=["iframe"],p=["iframe"],n=["iframe"],o=["iframe","visible"],m=["context","fixedcenter","iframe"],r=["context","fixedcenter","iframe"],s=["height"],t=a.isBoolean,u=["iframe","x","y","xy"],w=j.ie==
+6?true:false,x=a.isBoolean,v=["zindex"],y=a.isBoolean,B=["constraintoviewport"];h.IFRAME_SRC="javascript:false;";h.IFRAME_OFFSET=3;h.VIEWPORT_OFFSET=10;h.TOP_LEFT="tl";h.TOP_RIGHT="tr";h.BOTTOM_LEFT="bl";h.BOTTOM_RIGHT="br";h.PREVENT_OVERLAP_X={tltr:true,blbr:true,brbl:true,trtl:true};h.PREVENT_OVERLAP_Y={trbr:true,tlbl:true,bltl:true,brtr:true};h.CSS_OVERLAY="yui-overlay";h.CSS_HIDDEN="yui-overlay-hidden";h.CSS_IFRAME="yui-overlay-iframe";h.STD_MOD_RE=/^\s*?(body|footer|header)\s*?$/i;h.windowScrollEvent=
+new c("windowScroll");h.windowResizeEvent=new c("windowResize");h.windowScrollHandler=function(a){a=d.getTarget(a);if(!a||a===window||a===window.document)if(j.ie){if(!window.scrollEnd)window.scrollEnd=-1;clearTimeout(window.scrollEnd);window.scrollEnd=setTimeout(function(){h.windowScrollEvent.fire()},1)}else h.windowScrollEvent.fire()};h.windowResizeHandler=function(){if(j.ie){if(!window.resizeEnd)window.resizeEnd=-1;clearTimeout(window.resizeEnd);window.resizeEnd=setTimeout(function(){h.windowResizeEvent.fire()},
+100)}else h.windowResizeEvent.fire()};h._initialized=null;if(h._initialized===null){d.on(window,"scroll",h.windowScrollHandler);d.on(window,"resize",h.windowResizeHandler);h._initialized=true}h._TRIGGER_MAP={windowScroll:h.windowScrollEvent,windowResize:h.windowResizeEvent,textResize:b.textResizeEvent};YAHOO.extend(h,b,{CONTEXT_TRIGGERS:[],init:function(a,e){h.superclass.init.call(this,a);this.beforeInitEvent.fire(h);f.addClass(this.element,h.CSS_OVERLAY);e&&this.cfg.applyConfig(e,true);if(this.platform==
+"mac"&&j.gecko){g.alreadySubscribed(this.showEvent,this.showMacGeckoScrollbars,this)||this.showEvent.subscribe(this.showMacGeckoScrollbars,this,true);g.alreadySubscribed(this.hideEvent,this.hideMacGeckoScrollbars,this)||this.hideEvent.subscribe(this.hideMacGeckoScrollbars,this,true)}this.initEvent.fire(h)},initEvents:function(){h.superclass.initEvents.call(this);var a=c.LIST;this.beforeMoveEvent=this.createEvent("beforeMove");this.beforeMoveEvent.signature=a;this.moveEvent=this.createEvent("move");
+this.moveEvent.signature=a},initDefaultConfig:function(){h.superclass.initDefaultConfig.call(this);var a=this.cfg;a.addProperty("x",{handler:this.configX,validator:i,suppressEvent:true,supercedes:k});a.addProperty("y",{handler:this.configY,validator:l,suppressEvent:true,supercedes:q});a.addProperty("xy",{handler:this.configXY,suppressEvent:true,supercedes:p});a.addProperty("context",{handler:this.configContext,suppressEvent:true,supercedes:n});a.addProperty("fixedcenter",{handler:this.configFixedCenter,
+value:false,validator:void 0,supercedes:o});a.addProperty("width",{handler:this.configWidth,suppressEvent:true,supercedes:m});a.addProperty("height",{handler:this.configHeight,suppressEvent:true,supercedes:r});a.addProperty("autofillheight",{handler:this.configAutoFillHeight,value:"body",validator:this._validateAutoFill,supercedes:s});a.addProperty("zindex",{handler:this.configzIndex,value:null});a.addProperty("constraintoviewport",{handler:this.configConstrainToViewport,value:false,validator:t,supercedes:u});
+a.addProperty("iframe",{handler:this.configIframe,value:w,validator:x,supercedes:v});a.addProperty("preventcontextoverlap",{value:false,validator:y,supercedes:B})},moveTo:function(a,e){this.cfg.setProperty("xy",[a,e])},hideMacGeckoScrollbars:function(){f.replaceClass(this.element,"show-scrollbars","hide-scrollbars")},showMacGeckoScrollbars:function(){f.replaceClass(this.element,"hide-scrollbars","show-scrollbars")},_setDomVisibility:function(a){f.setStyle(this.element,"visibility",a?"visible":"hidden");
+var e=h.CSS_HIDDEN;a?f.removeClass(this.element,e):f.addClass(this.element,e)},configVisible:function(a,e){var b=e[0],i=f.getStyle(this.element,"visibility"),c=this._cachedEffects||this._createEffects(this.cfg.getProperty("effect")),d=this.platform=="mac"&&j.gecko,k=g.alreadySubscribed,l;if(i=="inherit"){for(l=this.element.parentNode;l.nodeType!=9&&l.nodeType!=11;){i=f.getStyle(l,"visibility");if(i!="inherit")break;l=l.parentNode}i=="inherit"&&(i="visible")}if(b){d&&this.showMacGeckoScrollbars();
+if(c){if(b&&(i!="visible"||i===""||this._fadingOut)&&this.beforeShowEvent.fire()){b=c.length;for(d=0;d<b;d++){i=c[d];d===0&&!k(i.animateInCompleteEvent,this.showEvent.fire,this.showEvent)&&i.animateInCompleteEvent.subscribe(this.showEvent.fire,this.showEvent,true);i.animateIn()}}}else if(i!="visible"||i===""){if(this.beforeShowEvent.fire()){this._setDomVisibility(true);this.cfg.refireEvent("iframe");this.showEvent.fire()}}else this._setDomVisibility(true)}else{d&&this.hideMacGeckoScrollbars();if(c)if(i==
+"visible"||this._fadingIn){if(this.beforeHideEvent.fire()){b=c.length;for(i=0;i<b;i++){d=c[i];i===0&&!k(d.animateOutCompleteEvent,this.hideEvent.fire,this.hideEvent)&&d.animateOutCompleteEvent.subscribe(this.hideEvent.fire,this.hideEvent,true);d.animateOut()}}}else i===""&&this._setDomVisibility(false);else if(i=="visible"||i===""){if(this.beforeHideEvent.fire()){this._setDomVisibility(false);this.hideEvent.fire()}}else this._setDomVisibility(false)}},doCenterOnDOMEvent:function(){var a=this.cfg,
+e=a.getProperty("fixedcenter");a.getProperty("visible")&&e&&(e!=="contained"||this.fitsInViewport())&&this.center()},fitsInViewport:function(){var a=h.VIEWPORT_OFFSET,e=this.element,b=e.offsetWidth,e=e.offsetHeight,i=f.getViewportWidth(),c=f.getViewportHeight();return b+a<i&&e+a<c},configFixedCenter:function(a,e){var b=g.alreadySubscribed,i=h.windowResizeEvent,c=h.windowScrollEvent;if(e[0]){this.center();b(this.beforeShowEvent,this.center)||this.beforeShowEvent.subscribe(this.center);b(i,this.doCenterOnDOMEvent,
+this)||i.subscribe(this.doCenterOnDOMEvent,this,true);b(c,this.doCenterOnDOMEvent,this)||c.subscribe(this.doCenterOnDOMEvent,this,true)}else{this.beforeShowEvent.unsubscribe(this.center);i.unsubscribe(this.doCenterOnDOMEvent,this);c.unsubscribe(this.doCenterOnDOMEvent,this)}},configHeight:function(a,e){f.setStyle(this.element,"height",e[0]);this.cfg.refireEvent("iframe")},configAutoFillHeight:function(e,i){var c=i[0],d=this.cfg,k=d.getProperty("autofillheight"),l=this._autoFillOnHeightChange;d.unsubscribeFromConfigEvent("height",
+l);b.textResizeEvent.unsubscribe(l);this.changeContentEvent.unsubscribe(l);k&&(c!==k&&this[k])&&f.setStyle(this[k],"height","");if(c){c=a.trim(c.toLowerCase());d.subscribeToConfigEvent("height",l,this[c],this);b.textResizeEvent.subscribe(l,this[c],this);this.changeContentEvent.subscribe(l,this[c],this);d.setProperty("autofillheight",c,true)}},configWidth:function(a,e){f.setStyle(this.element,"width",e[0]);this.cfg.refireEvent("iframe")},configzIndex:function(a,e){var b=e[0],i=this.element;if(!b){b=
+f.getStyle(i,"zIndex");if(!b||isNaN(b))b=0}if(this.iframe||this.cfg.getProperty("iframe")===true)b<=0&&(b=1);f.setStyle(i,"zIndex",b);this.cfg.setProperty("zIndex",b,true);this.iframe&&this.stackIframe()},configXY:function(a,e){var b=e[0],i=b[0],b=b[1];this.cfg.setProperty("x",i);this.cfg.setProperty("y",b);this.beforeMoveEvent.fire([i,b]);i=this.cfg.getProperty("x");b=this.cfg.getProperty("y");this.cfg.refireEvent("iframe");this.moveEvent.fire([i,b])},configX:function(a,e){var b=e[0],i=this.cfg.getProperty("y");
+this.cfg.setProperty("x",b,true);this.cfg.setProperty("y",i,true);this.beforeMoveEvent.fire([b,i]);b=this.cfg.getProperty("x");i=this.cfg.getProperty("y");f.setX(this.element,b,true);this.cfg.setProperty("xy",[b,i],true);this.cfg.refireEvent("iframe");this.moveEvent.fire([b,i])},configY:function(a,e){var b=this.cfg.getProperty("x"),i=e[0];this.cfg.setProperty("x",b,true);this.cfg.setProperty("y",i,true);this.beforeMoveEvent.fire([b,i]);b=this.cfg.getProperty("x");i=this.cfg.getProperty("y");f.setY(this.element,
+i,true);this.cfg.setProperty("xy",[b,i],true);this.cfg.refireEvent("iframe");this.moveEvent.fire([b,i])},showIframe:function(){var a=this.iframe,e;if(a){e=this.element.parentNode;e!=a.parentNode&&this._addToParent(e,a);a.style.display="block"}},hideIframe:function(){if(this.iframe)this.iframe.style.display="none"},syncIframe:function(){var e=this.iframe,b=this.element,i=h.IFRAME_OFFSET,c=i*2;if(e){e.style.width=b.offsetWidth+c+"px";e.style.height=b.offsetHeight+c+"px";b=this.cfg.getProperty("xy");
+if(!a.isArray(b)||isNaN(b[0])||isNaN(b[1])){this.syncPosition();b=this.cfg.getProperty("xy")}f.setXY(e,[b[0]-i,b[1]-i])}},stackIframe:function(){if(this.iframe){var a=f.getStyle(this.element,"zIndex");!YAHOO.lang.isUndefined(a)&&!isNaN(a)&&f.setStyle(this.iframe,"zIndex",a-1)}},configIframe:function(a,b){function i(){var a=this.iframe,b=this.element;if(!a){if(!e){e=document.createElement("iframe");if(this.isSecure)e.src=h.IFRAME_SRC;if(j.ie){e.style.filter="alpha(opacity=0)";e.frameBorder=0}else e.style.opacity=
+"0";e.style.position="absolute";e.style.border="none";e.style.margin="0";e.style.padding="0";e.style.display="none";e.tabIndex=-1;e.className=h.CSS_IFRAME}a=e.cloneNode(false);a.id=this.id+"_f";b=b.parentNode;this._addToParent(b||document.body,a);this.iframe=a}this.showIframe();this.syncIframe();this.stackIframe();if(!this._hasIframeEventListeners){this.showEvent.subscribe(this.showIframe);this.hideEvent.subscribe(this.hideIframe);this.changeContentEvent.subscribe(this.syncIframe);this._hasIframeEventListeners=
+true}}function c(){i.call(this);this.beforeShowEvent.unsubscribe(c);this._iframeDeferred=false}if(b[0])if(this.cfg.getProperty("visible"))i.call(this);else{if(!this._iframeDeferred){this.beforeShowEvent.subscribe(c);this._iframeDeferred=true}}else{this.hideIframe();if(this._hasIframeEventListeners){this.showEvent.unsubscribe(this.showIframe);this.hideEvent.unsubscribe(this.hideIframe);this.changeContentEvent.unsubscribe(this.syncIframe);this._hasIframeEventListeners=false}}},_primeXYFromDOM:function(){if(YAHOO.lang.isUndefined(this.cfg.getProperty("xy"))){this.syncPosition();
+this.cfg.refireEvent("xy");this.beforeShowEvent.unsubscribe(this._primeXYFromDOM)}},configConstrainToViewport:function(a,e){if(e[0]){g.alreadySubscribed(this.beforeMoveEvent,this.enforceConstraints,this)||this.beforeMoveEvent.subscribe(this.enforceConstraints,this,true);g.alreadySubscribed(this.beforeShowEvent,this._primeXYFromDOM)||this.beforeShowEvent.subscribe(this._primeXYFromDOM)}else{this.beforeShowEvent.unsubscribe(this._primeXYFromDOM);this.beforeMoveEvent.unsubscribe(this.enforceConstraints,
+this)}},configContext:function(a,e){var b=e[0],i,c,d,k,f=this.CONTEXT_TRIGGERS;if(b){i=b[0];c=b[1];d=b[2];k=b[3];b=b[4];f&&f.length>0&&(k=(k||[]).concat(f));if(i){typeof i=="string"&&this.cfg.setProperty("context",[document.getElementById(i),c,d,k,b],true);c&&d&&this.align(c,d,b);this._contextTriggers&&this._processTriggers(this._contextTriggers,"unsubscribe",this._alignOnTrigger);if(k){this._processTriggers(k,"subscribe",this._alignOnTrigger);this._contextTriggers=k}}}},_alignOnTrigger:function(){this.align()},
+_findTriggerCE:function(a){var e=null;a instanceof c?e=a:h._TRIGGER_MAP[a]&&(e=h._TRIGGER_MAP[a]);return e},_processTriggers:function(a,e,b){for(var i,c,d=0,k=a.length;d<k;++d){i=a[d];if(c=this._findTriggerCE(i))c[e](b,this,true);else this[e](i,b)}},align:function(a,e,b){function i(e,c){var k=null,f=null;switch(a){case h.TOP_LEFT:k=c;f=e;break;case h.TOP_RIGHT:k=c-l.offsetWidth;f=e;break;case h.BOTTOM_LEFT:k=c;f=e-l.offsetHeight;break;case h.BOTTOM_RIGHT:k=c-l.offsetWidth;f=e-l.offsetHeight}if(k!==
+null&&f!==null){if(b){k=k+b[0];f=f+b[1]}d.moveTo(k,f)}}var c=this.cfg.getProperty("context"),d=this,k,l;if(c){k=c[0];l=this.element;d=this;a||(a=c[1]);e||(e=c[2]);!b&&c[4]&&(b=c[4]);if(l&&k){c=f.getRegion(k);switch(e){case h.TOP_LEFT:i(c.top,c.left);break;case h.TOP_RIGHT:i(c.top,c.right);break;case h.BOTTOM_LEFT:i(c.bottom,c.left);break;case h.BOTTOM_RIGHT:i(c.bottom,c.right)}}}},enforceConstraints:function(a,e){var b=e[0],b=this.getConstrainedXY(b[0],b[1]);this.cfg.setProperty("x",b[0],true);this.cfg.setProperty("y",
+b[1],true);this.cfg.setProperty("xy",b,true)},_getConstrainedPos:function(a,e){var b=this.element,i=h.VIEWPORT_OFFSET,c=a=="x",b=c?b.offsetWidth:b.offsetHeight,d=c?f.getViewportWidth():f.getViewportHeight(),k=c?f.getDocumentScrollLeft():f.getDocumentScrollTop(),l=c?h.PREVENT_OVERLAP_X:h.PREVENT_OVERLAP_Y,c=this.cfg.getProperty("context"),g=b+i<d,l=this.cfg.getProperty("preventcontextoverlap")&&c&&l[c[1]+c[2]],j=k+i,i=k+d-b-i,q=e;if(e<j||e>i)l?q=this._preventOverlap(a,c[0],b,d,k):g?e<j?q=j:e>i&&(q=
+i):q=j;return q},_preventOverlap:function(a,e,b,i,c){var d=a=="x",k=h.VIEWPORT_OFFSET,l=this,g=(d?f.getX(e):f.getY(e))-c,j=d?e.offsetWidth:e.offsetHeight,q=g-k,p=i-(g+j)-k,n=false,o=function(){var e;e=l.cfg.getProperty(a)-c>g?g-b:g+j;l.cfg.setProperty(a,e+c,true);return e},m=function(){var e=l.cfg.getProperty(a)-c>g?p:q,i;if(b>e)if(n)o();else{o();n=true;i=m()}return i};m();return this.cfg.getProperty(a)},getConstrainedX:function(a){return this._getConstrainedPos("x",a)},getConstrainedY:function(a){return this._getConstrainedPos("y",
+a)},getConstrainedXY:function(a,e){return[this.getConstrainedX(a),this.getConstrainedY(e)]},center:function(){var a=h.VIEWPORT_OFFSET,e=this.element.offsetWidth,b=this.element.offsetHeight,i=f.getViewportWidth(),c=f.getViewportHeight(),e=e<i?i/2-e/2+f.getDocumentScrollLeft():a+f.getDocumentScrollLeft(),a=b<c?c/2-b/2+f.getDocumentScrollTop():a+f.getDocumentScrollTop();this.cfg.setProperty("xy",[parseInt(e,10),parseInt(a,10)]);this.cfg.refireEvent("iframe");j.webkit&&this.forceContainerRedraw()},syncPosition:function(){var a=
+f.getXY(this.element);this.cfg.setProperty("x",a[0],true);this.cfg.setProperty("y",a[1],true);this.cfg.setProperty("xy",a,true)},onDomResize:function(a,e){var b=this;h.superclass.onDomResize.call(this,a,e);setTimeout(function(){b.syncPosition();b.cfg.refireEvent("iframe");b.cfg.refireEvent("context")},0)},_getComputedHeight:function(){return document.defaultView&&document.defaultView.getComputedStyle?function(e){var b=null;if(e.ownerDocument&&e.ownerDocument.defaultView)(e=e.ownerDocument.defaultView.getComputedStyle(e,
+""))&&(b=parseInt(e.height,10));return a.isNumber(b)?b:null}:function(e){var b=null;if(e.style.pixelHeight)b=e.style.pixelHeight;return a.isNumber(b)?b:null}}(),_validateAutoFillHeight:function(e){return!e||a.isString(e)&&h.STD_MOD_RE.test(e)},_autoFillOnHeightChange:function(a,e,b){((a=this.cfg.getProperty("height"))&&a!=="auto"||a===0)&&this.fillHeight(b)},_getPreciseHeight:function(a){var e=a.offsetHeight;if(a.getBoundingClientRect){a=a.getBoundingClientRect();e=a.bottom-a.top}return e},fillHeight:function(a){if(a){var e=
+this.innerElement||this.element,b=[this.header,this.body,this.footer],i,c=i=0;i=0;for(var d=false,k=0,l=b.length;k<l;k++)(i=b[k])&&(a!==i?c=c+this._getPreciseHeight(i):d=true);if(d){(j.ie||j.opera)&&f.setStyle(a,"height","0px");i=this._getComputedHeight(e);if(i===null){f.addClass(e,"yui-override-padding");i=e.clientHeight;f.removeClass(e,"yui-override-padding")}i=Math.max(i-c,0);f.setStyle(a,"height",i+"px");a.offsetHeight!=i&&(i=Math.max(i-(a.offsetHeight-i),0));f.setStyle(a,"height",i+"px")}}},
+bringToTop:function(){var a=[],e=this.element;f.getElementsBy(function(b){var i=f.hasClass(b,h.CSS_OVERLAY),c=YAHOO.widget.Panel;i&&!f.isAncestor(e,b)&&(a[a.length]=c&&f.hasClass(b,c.CSS_PANEL)?b.parentNode:b)},"div",document.body);a.sort(function(a,e){var b=f.getStyle(a,"zIndex"),i=f.getStyle(e,"zIndex"),b=!b||isNaN(b)?0:parseInt(b,10),i=!i||isNaN(i)?0:parseInt(i,10);return b>i?-1:b<i?1:0});var b=a[0],i;if(b){i=f.getStyle(b,"zIndex");if(!isNaN(i)){var c=false;if(b!=e)c=true;else if(a.length>1){b=
+f.getStyle(a[1],"zIndex");!isNaN(b)&&i==b&&(c=true)}c&&this.cfg.setProperty("zindex",parseInt(i,10)+2)}}},destroy:function(a){this.iframe&&this.iframe.parentNode.removeChild(this.iframe);this.iframe=null;h.windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent,this);h.windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent,this);b.textResizeEvent.unsubscribe(this._autoFillOnHeightChange);this._contextTriggers&&this._processTriggers(this._contextTriggers,"unsubscribe",this._alignOnTrigger);h.superclass.destroy.call(this,
+a)},forceContainerRedraw:function(){var a=this;f.addClass(a.element,"yui-force-redraw");setTimeout(function(){f.removeClass(a.element,"yui-force-redraw")},0)},toString:function(){return"Overlay "+this.id}})})();
+(function(){YAHOO.widget.OverlayManager=function(a){this.init(a)};var a=YAHOO.widget.Overlay,c=YAHOO.util.Event,b=YAHOO.util.Dom,d=YAHOO.util.Config,f=YAHOO.util.CustomEvent,g=YAHOO.widget.OverlayManager;g.CSS_FOCUSED="focused";g.prototype={constructor:g,overlays:null,initDefaultConfig:function(){this.cfg.addProperty("overlays",{suppressEvent:true});this.cfg.addProperty("focusevent",{value:"mousedown"})},init:function(a){this.cfg=new d(this);this.initDefaultConfig();a&&this.cfg.applyConfig(a,true);
+this.cfg.fireQueue();var f=null;this.getActive=function(){return f};this.focus=function(a){(a=this.find(a))&&a.focus()};this.remove=function(a){var a=this.find(a),i;if(a){f==a&&(f=null);var d=a.element===null&&a.cfg===null?true:false;if(!d){i=b.getStyle(a.element,"zIndex");a.cfg.setProperty("zIndex",-1E3,true)}this.overlays.sort(this.compareZIndexDesc);this.overlays=this.overlays.slice(0,this.overlays.length-1);a.hideEvent.unsubscribe(a.blur);a.destroyEvent.unsubscribe(this._onOverlayDestroy,a);a.focusEvent.unsubscribe(this._onOverlayFocusHandler,
+a);a.blurEvent.unsubscribe(this._onOverlayBlurHandler,a);if(!d){c.removeListener(a.element,this.cfg.getProperty("focusevent"),this._onOverlayElementFocus);a.cfg.setProperty("zIndex",i,true);a.cfg.setProperty("manager",null)}if(a.focusEvent._managed)a.focusEvent=null;if(a.blurEvent._managed)a.blurEvent=null;if(a.focus._managed)a.focus=null;if(a.blur._managed)a.blur=null}};this.blurAll=function(){var a=this.overlays.length;if(a>0){a=a-1;do this.overlays[a].blur();while(a--)}};this._manageBlur=function(a){var i=
+false;if(f==a){b.removeClass(f.element,g.CSS_FOCUSED);f=null;i=true}return i};this._manageFocus=function(a){var i=false;if(f!=a){f&&f.blur();f=a;this.bringToTop(f);b.addClass(f.element,g.CSS_FOCUSED);i=true}return i};a=this.cfg.getProperty("overlays");if(!this.overlays)this.overlays=[];if(a){this.register(a);this.overlays.sort(this.compareZIndexDesc)}},_onOverlayElementFocus:function(a){var a=c.getTarget(a),d=this.close;d&&(a==d||b.isAncestor(d,a))?this.blur():this.focus()},_onOverlayDestroy:function(a,
+b,e){this.remove(e)},_onOverlayFocusHandler:function(a,b,e){this._manageFocus(e)},_onOverlayBlurHandler:function(a,b,e){this._manageBlur(e)},_bindFocus:function(a){var b=this;if(a.focusEvent)a.focusEvent.subscribe(b._onOverlayFocusHandler,a,b);else{a.focusEvent=a.createEvent("focus");a.focusEvent.signature=f.LIST;a.focusEvent._managed=true}if(!a.focus){c.on(a.element,b.cfg.getProperty("focusevent"),b._onOverlayElementFocus,null,a);a.focus=function(){if(b._manageFocus(this)){this.cfg.getProperty("visible")&&
+this.focusFirst&&this.focusFirst();this.focusEvent.fire()}};a.focus._managed=true}},_bindBlur:function(a){var b=this;if(a.blurEvent)a.blurEvent.subscribe(b._onOverlayBlurHandler,a,b);else{a.blurEvent=a.createEvent("blur");a.blurEvent.signature=f.LIST;a.focusEvent._managed=true}if(!a.blur){a.blur=function(){b._manageBlur(this)&&this.blurEvent.fire()};a.blur._managed=true}a.hideEvent.subscribe(a.blur)},_bindDestroy:function(a){a.destroyEvent.subscribe(this._onOverlayDestroy,a,this)},_syncZIndex:function(a){var c=
+b.getStyle(a.element,"zIndex");isNaN(c)?a.cfg.setProperty("zIndex",0):a.cfg.setProperty("zIndex",parseInt(c,10))},register:function(b){var c=false,e,i;if(b instanceof a){b.cfg.addProperty("manager",{value:this});this._bindFocus(b);this._bindBlur(b);this._bindDestroy(b);this._syncZIndex(b);this.overlays.push(b);this.bringToTop(b);c=true}else if(b instanceof Array){e=0;for(i=b.length;e<i;e++)c=this.register(b[e])||c}return c},bringToTop:function(a){var a=this.find(a),c,e,i;if(a){i=this.overlays;i.sort(this.compareZIndexDesc);
+if(e=i[0]){c=b.getStyle(e.element,"zIndex");if(!isNaN(c)){var d=false;if(e!==a)d=true;else if(i.length>1){e=b.getStyle(i[1].element,"zIndex");!isNaN(e)&&c==e&&(d=true)}d&&a.cfg.setProperty("zindex",parseInt(c,10)+2)}i.sort(this.compareZIndexDesc)}}},find:function(b){var c=b instanceof a,e=this.overlays,i=e.length,d=null,f;if(c||typeof b=="string")for(f=i-1;f>=0;f--){i=e[f];if(c&&i===b||i.id==b){d=i;break}}return d},compareZIndexDesc:function(a,b){var e=a.cfg?a.cfg.getProperty("zIndex"):null,i=b.cfg?
+b.cfg.getProperty("zIndex"):null;return e===null&&i===null?0:e===null?1:i===null?-1:e>i?-1:e<i?1:0},showAll:function(){var a=this.overlays,b;for(b=a.length-1;b>=0;b--)a[b].show()},hideAll:function(){var a=this.overlays,b;for(b=a.length-1;b>=0;b--)a[b].hide()},toString:function(){return"OverlayManager"}}})();
+(function(){function a(){if("_originalWidth"in this){var a=this._originalWidth,e=this._forcedWidth,b=this.cfg;b.getProperty("width")==e&&b.setProperty("width",a)}var a=document.body,e=this.cfg,b=e.getProperty("width"),i,c;if((!b||b=="auto")&&(e.getProperty("container")!=a||e.getProperty("x")>=j.getViewportWidth()||e.getProperty("y")>=j.getViewportHeight())){c=this.element.cloneNode(true);c.style.visibility="hidden";c.style.top="0px";c.style.left="0px";a.appendChild(c);i=c.offsetWidth+"px";a.removeChild(c);
+e.setProperty("width",i);e.refireEvent("xy");this._originalWidth=b||"";this._forcedWidth=i}}function c(a,e,b){this.render(b)}function b(){f.onDOMReady(c,this.cfg.getProperty("container"),this)}YAHOO.widget.Tooltip=function(a,e){YAHOO.widget.Tooltip.superclass.constructor.call(this,a,e)};var d=YAHOO.lang,f=YAHOO.util.Event,g=YAHOO.util.CustomEvent,j=YAHOO.util.Dom,h=YAHOO.widget.Tooltip,e=YAHOO.env.ua,i=e.ie&&(e.ie<=6||document.compatMode=="BackCompat"),k,l=d.isBoolean,q=["x","y","xy"],p=d.isNumber,
+n=d.isNumber,o=d.isNumber,m=[0,25];h.CSS_TOOLTIP="yui-tt";YAHOO.extend(h,YAHOO.widget.Overlay,{init:function(e,i){h.superclass.init.call(this,e);this.beforeInitEvent.fire(h);j.addClass(this.element,h.CSS_TOOLTIP);i&&this.cfg.applyConfig(i,true);this.cfg.queueProperty("visible",false);this.cfg.queueProperty("constraintoviewport",true);this.setBody("");this.subscribe("changeContent",a);this.subscribe("init",b);this.subscribe("render",this.onRender);this.initEvent.fire(h)},initEvents:function(){h.superclass.initEvents.call(this);
+var a=g.LIST;this.contextMouseOverEvent=this.createEvent("contextMouseOver");this.contextMouseOverEvent.signature=a;this.contextMouseOutEvent=this.createEvent("contextMouseOut");this.contextMouseOutEvent.signature=a;this.contextTriggerEvent=this.createEvent("contextTrigger");this.contextTriggerEvent.signature=a},initDefaultConfig:function(){h.superclass.initDefaultConfig.call(this);this.cfg.addProperty("preventoverlap",{value:true,validator:l,supercedes:q});this.cfg.addProperty("showdelay",{handler:this.configShowDelay,
+value:200,validator:p});this.cfg.addProperty("autodismissdelay",{handler:this.configAutoDismissDelay,value:5E3,validator:n});this.cfg.addProperty("hidedelay",{handler:this.configHideDelay,value:250,validator:o});this.cfg.addProperty("text",{handler:this.configText,suppressEvent:true});this.cfg.addProperty("container",{handler:this.configContainer,value:document.body});this.cfg.addProperty("disabled",{handler:this.configContainer,value:false,supressEvent:true});this.cfg.addProperty("xyoffset",{value:m.concat(),
+supressEvent:true})},configText:function(a,e){var b=e[0];b&&this.setBody(b)},configContainer:function(a,e){var b=e[0];typeof b=="string"&&this.cfg.setProperty("container",document.getElementById(b),true)},_removeEventListeners:function(){var a=this._context,e,b;if(a){e=a.length;if(e>0){b=e-1;do{e=a[b];f.removeListener(e,"mouseover",this.onContextMouseOver);f.removeListener(e,"mousemove",this.onContextMouseMove);f.removeListener(e,"mouseout",this.onContextMouseOut)}while(b--)}}},configContext:function(a,
+e){var b=e[0],i,c;if(b){if(!(b instanceof Array)){typeof b=="string"?this.cfg.setProperty("context",[document.getElementById(b)],true):this.cfg.setProperty("context",[b],true);b=this.cfg.getProperty("context")}this._removeEventListeners();if(b=this._context=b){i=b.length;if(i>0){c=i-1;do{i=b[c];f.on(i,"mouseover",this.onContextMouseOver,this);f.on(i,"mousemove",this.onContextMouseMove,this);f.on(i,"mouseout",this.onContextMouseOut,this)}while(c--)}}}},onContextMouseMove:function(a,e){e.pageX=f.getPageX(a);
+e.pageY=f.getPageY(a)},onContextMouseOver:function(a,e){if(this.title){e._tempTitle=this.title;this.title=""}if(e.fireEvent("contextMouseOver",this,a)!==false&&!e.cfg.getProperty("disabled")){if(e.hideProcId){clearTimeout(e.hideProcId);e.hideProcId=null}f.on(this,"mousemove",e.onContextMouseMove,e);e.showProcId=e.doShow(a,this)}},onContextMouseOut:function(a,e){if(e._tempTitle){this.title=e._tempTitle;e._tempTitle=null}if(e.showProcId){clearTimeout(e.showProcId);e.showProcId=null}if(e.hideProcId){clearTimeout(e.hideProcId);
+e.hideProcId=null}e.fireEvent("contextMouseOut",this,a);e.hideProcId=setTimeout(function(){e.hide()},e.cfg.getProperty("hidedelay"))},doShow:function(a,b){var i=this.cfg.getProperty("xyoffset"),c=i[0],d=i[1],k=this;e.opera&&(b.tagName&&b.tagName.toUpperCase()=="A")&&(d=d+12);return setTimeout(function(){var a=k.cfg.getProperty("text");k._tempTitle&&(a===""||YAHOO.lang.isUndefined(a)||YAHOO.lang.isNull(a))?k.setBody(k._tempTitle):k.cfg.refireEvent("text");k.moveTo(k.pageX+c,k.pageY+d);k.cfg.getProperty("preventoverlap")&&
+k.preventOverlap(k.pageX,k.pageY);f.removeListener(b,"mousemove",k.onContextMouseMove);k.contextTriggerEvent.fire(b);k.show();k.hideProcId=k.doHide()},this.cfg.getProperty("showdelay"))},doHide:function(){var a=this;return setTimeout(function(){a.hide()},this.cfg.getProperty("autodismissdelay"))},preventOverlap:function(a,e){var b=this.element.offsetHeight,i=new YAHOO.util.Point(a,e),c=j.getRegion(this.element);c.top=c.top-5;c.left=c.left-5;c.right=c.right+5;c.bottom=c.bottom+5;c.contains(i)&&this.cfg.setProperty("y",
+e-b-5)},onRender:function(){function a(){var e=this.element,b=this.underlay;if(b){b.style.width=e.offsetWidth+6+"px";b.style.height=e.offsetHeight+1+"px"}}function b(){j.addClass(this.underlay,"yui-tt-shadow-visible");e.ie&&this.forceUnderlayRedraw()}function c(){j.removeClass(this.underlay,"yui-tt-shadow-visible")}function d(){var e=this.underlay,f,l,h;if(!e){f=this.element;l=YAHOO.widget.Module;h=this;if(!k){k=document.createElement("div");k.className="yui-tt-shadow"}e=k.cloneNode(false);f.appendChild(e);
+this._shadow=this.underlay=e;b.call(this);this.subscribe("beforeShow",b);this.subscribe("hide",c);if(i){window.setTimeout(function(){a.call(h)},0);this.cfg.subscribeToConfigEvent("width",a);this.cfg.subscribeToConfigEvent("height",a);this.subscribe("changeContent",a);l.textResizeEvent.subscribe(a,this,true);this.subscribe("destroy",function(){l.textResizeEvent.unsubscribe(a,this)})}}}function f(){d.call(this);this.unsubscribe("beforeShow",f)}this.cfg.getProperty("visible")?d.call(this):this.subscribe("beforeShow",
+f)},forceUnderlayRedraw:function(){var a=this;j.addClass(a.underlay,"yui-force-redraw");setTimeout(function(){j.removeClass(a.underlay,"yui-force-redraw")},0)},destroy:function(){this._removeEventListeners();h.superclass.destroy.call(this)},toString:function(){return"Tooltip "+this.id}})})();
+(function(){function a(){!this.header&&this.cfg.getProperty("draggable")&&this.setHeader("&#160;")}function c(a,e,b){var a=b[0],e=b[1],i=this.cfg;i.getProperty("width")==e&&i.setProperty("width",a);this.unsubscribe("hide",c,b)}function b(){var a,e,b;if(n){a=this.cfg;e=a.getProperty("width");if(!e||e=="auto"){b=this.element.offsetWidth+"px";a.setProperty("width",b);this.subscribe("hide",c,[e||"",b])}}}YAHOO.widget.Panel=function(a,e){YAHOO.widget.Panel.superclass.constructor.call(this,a,e)};var d=
+null,f=YAHOO.lang,g=YAHOO.util,j=g.Dom,h=g.Event,e=g.CustomEvent,i=YAHOO.util.KeyListener,k=g.Config,l=YAHOO.widget.Overlay,q=YAHOO.widget.Panel,p=YAHOO.env.ua,n=p.ie&&(p.ie<=6||document.compatMode=="BackCompat"),o,m,r,s=f.isBoolean,t=["visible"],u=f.isBoolean,w=["visible"],x=f.isBoolean,v=["draggable"],y=["visible"],B=f.isBoolean,A=["visible","zindex"],D=["visible"],E=["close"],C=f.isObject,z={close:"Close"};q.CSS_PANEL="yui-panel";q.CSS_PANEL_CONTAINER="yui-panel-container";q.FOCUSABLE=["a","button",
+"select","textarea","input","iframe"];YAHOO.extend(q,l,{init:function(e,b){q.superclass.init.call(this,e);this.beforeInitEvent.fire(q);j.addClass(this.element,q.CSS_PANEL);this.buildWrapper();b&&this.cfg.applyConfig(b,true);this.subscribe("showMask",this._addFocusHandlers);this.subscribe("hideMask",this._removeFocusHandlers);this.subscribe("beforeRender",a);this.subscribe("render",function(){this.setFirstLastFocusable();this.subscribe("changeContent",this.setFirstLastFocusable)});this.subscribe("show",
+this._focusOnShow);this.initEvent.fire(q)},_onElementFocus:function(a){if(d===this){var a=h.getTarget(a),e=a!==document.documentElement&&a!==window;if(e&&a!==this.element&&a!==this.mask&&!j.isAncestor(this.element,a))try{this._focusFirstModal()}catch(b){try{e&&a!==document.body&&a.blur()}catch(i){}}}},_focusFirstModal:function(){var a=this.firstElement;a?a.focus():this._modalFocus?this._modalFocus.focus():this.innerElement.focus()},_addFocusHandlers:function(){if(!this.firstElement)p.webkit||p.opera?
+this._modalFocus||this._createHiddenFocusElement():this.innerElement.tabIndex=0;this._setTabLoop(this.firstElement,this.lastElement);h.onFocus(document.documentElement,this._onElementFocus,this,true);d=this},_createHiddenFocusElement:function(){var a=document.createElement("button");a.style.height="1px";a.style.width="1px";a.style.position="absolute";a.style.left="-10000em";a.style.opacity=0;a.tabIndex=-1;this.innerElement.appendChild(a);this._modalFocus=a},_removeFocusHandlers:function(){h.removeFocusListener(document.documentElement,
+this._onElementFocus,this);d==this&&(d=null)},_focusOnShow:function(a,e,b){e&&e[1]&&h.stopEvent(e[1]);this.focusFirst(a,e,b)||this.cfg.getProperty("modal")&&this._focusFirstModal()},focusFirst:function(a,e){var b=this.firstElement,i=false;e&&e[1]&&h.stopEvent(e[1]);if(b)try{b.focus();i=true}catch(c){}return i},focusLast:function(a,e){var b=this.lastElement,i=false;e&&e[1]&&h.stopEvent(e[1]);if(b)try{b.focus();i=true}catch(c){}return i},_setTabLoop:function(a,e){this.setTabLoop(a,e)},setTabLoop:function(a,
+e){var b=this.preventBackTab,c=this.preventTabOut,d=this.showEvent,k=this.hideEvent;if(b){b.disable();d.unsubscribe(b.enable,b);k.unsubscribe(b.disable,b);this.preventBackTab=null}if(c){c.disable();d.unsubscribe(c.enable,c);k.unsubscribe(c.disable,c);this.preventTabOut=null}if(a){b=this.preventBackTab=new i(a,{shift:true,keys:9},{fn:this.focusLast,scope:this,correctScope:true});d.subscribe(b.enable,b,true);k.subscribe(b.disable,b,true)}if(e){c=this.preventTabOut=new i(e,{shift:false,keys:9},{fn:this.focusFirst,
+scope:this,correctScope:true});d.subscribe(c.enable,c,true);k.subscribe(c.disable,c,true)}},getFocusableElements:function(a){for(var a=a||this.innerElement,e={},b=this,i=0;i<q.FOCUSABLE.length;i++)e[q.FOCUSABLE[i]]=true;return j.getElementsBy(function(a){return b._testIfFocusable(a,e)},null,a)},_testIfFocusable:function(a,e){return a.focus&&a.type!=="hidden"&&!a.disabled&&e[a.tagName.toLowerCase()]?true:false},setFirstLastFocusable:function(){this.lastElement=this.firstElement=null;var a=this.getFocusableElements();
+this.focusableElements=a;if(a.length>0){this.firstElement=a[0];this.lastElement=a[a.length-1]}this.cfg.getProperty("modal")&&this._setTabLoop(this.firstElement,this.lastElement)},initEvents:function(){q.superclass.initEvents.call(this);var a=e.LIST;this.showMaskEvent=this.createEvent("showMask");this.showMaskEvent.signature=a;this.beforeShowMaskEvent=this.createEvent("beforeShowMask");this.beforeShowMaskEvent.signature=a;this.hideMaskEvent=this.createEvent("hideMask");this.hideMaskEvent.signature=
+a;this.beforeHideMaskEvent=this.createEvent("beforeHideMask");this.beforeHideMaskEvent.signature=a;this.dragEvent=this.createEvent("drag");this.dragEvent.signature=a},initDefaultConfig:function(){q.superclass.initDefaultConfig.call(this);this.cfg.addProperty("close",{handler:this.configClose,value:true,validator:s,supercedes:t});this.cfg.addProperty("draggable",{handler:this.configDraggable,value:g.DD?true:false,validator:u,supercedes:w});this.cfg.addProperty("dragonly",{value:false,validator:x,supercedes:v});
+this.cfg.addProperty("underlay",{handler:this.configUnderlay,value:"shadow",supercedes:y});this.cfg.addProperty("modal",{handler:this.configModal,value:false,validator:B,supercedes:A});this.cfg.addProperty("keylisteners",{handler:this.configKeyListeners,suppressEvent:true,supercedes:D});this.cfg.addProperty("strings",{value:z,handler:this.configStrings,validator:C,supercedes:E})},configClose:function(a,e){var b=e[0],i=this.close,c=this.cfg.getProperty("strings");if(b)if(i)i.style.display="block";
+else{if(!r){r=document.createElement("a");r.className="container-close";r.href="#"}i=r.cloneNode(true);(b=this.innerElement.firstChild)?this.innerElement.insertBefore(i,b):this.innerElement.appendChild(i);i.innerHTML=c&&c.close?c.close:"&#160;";h.on(i,"click",this._doClose,this,true);this.close=i}else if(i)i.style.display="none"},_doClose:function(a){h.preventDefault(a);this.hide()},configDraggable:function(a,e){if(e[0])if(g.DD){if(this.header){j.setStyle(this.header,"cursor","move");this.registerDragDrop()}this.subscribe("beforeShow",
+b)}else this.cfg.setProperty("draggable",false);else{this.dd&&this.dd.unreg();this.header&&j.setStyle(this.header,"cursor","auto");this.unsubscribe("beforeShow",b)}},configUnderlay:function(a,e){function b(){if(!f){if(!m){m=document.createElement("div");m.className="underlay"}f=m.cloneNode(false);this.element.appendChild(f);this.underlay=f;if(n){this.sizeUnderlay();this.cfg.subscribeToConfigEvent("width",this.sizeUnderlay);this.cfg.subscribeToConfigEvent("height",this.sizeUnderlay);this.changeContentEvent.subscribe(this.sizeUnderlay);
+YAHOO.widget.Module.textResizeEvent.subscribe(this.sizeUnderlay,this,true)}p.webkit&&p.webkit<420&&this.changeContentEvent.subscribe(this.forceUnderlayRedraw)}}function i(){!b.call(this)&&n&&this.sizeUnderlay();this._underlayDeferred=false;this.beforeShowEvent.unsubscribe(i)}function c(){if(this._underlayDeferred){this.beforeShowEvent.unsubscribe(i);this._underlayDeferred=false}if(f){this.cfg.unsubscribeFromConfigEvent("width",this.sizeUnderlay);this.cfg.unsubscribeFromConfigEvent("height",this.sizeUnderlay);
+this.changeContentEvent.unsubscribe(this.sizeUnderlay);this.changeContentEvent.unsubscribe(this.forceUnderlayRedraw);YAHOO.widget.Module.textResizeEvent.unsubscribe(this.sizeUnderlay,this,true);this.element.removeChild(f);this.underlay=null}}var d=this.platform=="mac"&&p.gecko,k=e[0].toLowerCase(),f=this.underlay,l=this.element;switch(k){case "shadow":j.removeClass(l,"matte");j.addClass(l,"shadow");break;case "matte":d||c.call(this);j.removeClass(l,"shadow");j.addClass(l,"matte");break;default:d||
+c.call(this);j.removeClass(l,"shadow");j.removeClass(l,"matte")}if(k=="shadow"||d&&!f)if(this.cfg.getProperty("visible"))!b.call(this)&&n&&this.sizeUnderlay();else if(!this._underlayDeferred){this.beforeShowEvent.subscribe(i);this._underlayDeferred=true}},configModal:function(a,e){if(e[0]){if(!this._hasModalityEventListeners){this.subscribe("beforeShow",this.buildMask);this.subscribe("beforeShow",this.bringToTop);this.subscribe("beforeShow",this.showMask);this.subscribe("hide",this.hideMask);l.windowResizeEvent.subscribe(this.sizeMask,
+this,true);this._hasModalityEventListeners=true}}else if(this._hasModalityEventListeners){if(this.cfg.getProperty("visible")){this.hideMask();this.removeMask()}this.unsubscribe("beforeShow",this.buildMask);this.unsubscribe("beforeShow",this.bringToTop);this.unsubscribe("beforeShow",this.showMask);this.unsubscribe("hide",this.hideMask);l.windowResizeEvent.unsubscribe(this.sizeMask,this);this._hasModalityEventListeners=false}},removeMask:function(){var a=this.mask,e;if(a){this.hideMask();(e=a.parentNode)&&
+e.removeChild(a);this.mask=null}},configKeyListeners:function(a,e){var b=e[0],i,c,d;if(b)if(b instanceof Array){c=b.length;for(d=0;d<c;d++){i=b[d];k.alreadySubscribed(this.showEvent,i.enable,i)||this.showEvent.subscribe(i.enable,i,true);if(!k.alreadySubscribed(this.hideEvent,i.disable,i)){this.hideEvent.subscribe(i.disable,i,true);this.destroyEvent.subscribe(i.disable,i,true)}}}else{k.alreadySubscribed(this.showEvent,b.enable,b)||this.showEvent.subscribe(b.enable,b,true);if(!k.alreadySubscribed(this.hideEvent,
+b.disable,b)){this.hideEvent.subscribe(b.disable,b,true);this.destroyEvent.subscribe(b.disable,b,true)}}},configStrings:function(a,e){this.cfg.setProperty("strings",f.merge(z,e[0]),true)},configHeight:function(a,e){j.setStyle(this.innerElement,"height",e[0]);this.cfg.refireEvent("iframe")},_autoFillOnHeightChange:function(a,e,b){q.superclass._autoFillOnHeightChange.apply(this,arguments);if(n){var i=this;setTimeout(function(){i.sizeUnderlay()},0)}},configWidth:function(a,e){j.setStyle(this.innerElement,
+"width",e[0]);this.cfg.refireEvent("iframe")},configzIndex:function(a,e,b){q.superclass.configzIndex.call(this,a,e,b);if(this.mask||this.cfg.getProperty("modal")===true){a=j.getStyle(this.element,"zIndex");if(!a||isNaN(a))a=0;a===0?this.cfg.setProperty("zIndex",1):this.stackMask()}},buildWrapper:function(){var a=this.element.parentNode,e=this.element,b=document.createElement("div");b.className=q.CSS_PANEL_CONTAINER;b.id=e.id+"_c";a&&a.insertBefore(b,e);b.appendChild(e);this.element=b;this.innerElement=
+e;j.setStyle(this.innerElement,"visibility","inherit")},sizeUnderlay:function(){var a=this.underlay,e;if(a){e=this.element;a.style.width=e.offsetWidth+"px";a.style.height=e.offsetHeight+"px"}},registerDragDrop:function(){var a=this;if(this.header&&g.DD){var e=this.cfg.getProperty("dragonly")===true;this.dd=new g.DD(this.element.id,this.id,{dragOnly:e});if(!this.header.id)this.header.id=this.id+"_h";this.dd.startDrag=function(){var e,b,i,c,d,k;YAHOO.env.ua.ie==6&&j.addClass(a.element,"drag");if(a.cfg.getProperty("constraintoviewport")){var f=
+l.VIEWPORT_OFFSET;e=a.element.offsetHeight;b=a.element.offsetWidth;i=j.getViewportWidth();c=j.getViewportHeight();d=j.getDocumentScrollLeft();k=j.getDocumentScrollTop();if(e+f<c){this.minY=k+f;this.maxY=k+c-e-f}else{this.minY=k+f;this.maxY=k+f}if(b+f<i){this.minX=d+f;this.maxX=d+i-b-f}else{this.minX=d+f;this.maxX=d+f}this.constrainY=this.constrainX=true}else this.constrainY=this.constrainX=false;a.dragEvent.fire("startDrag",arguments)};this.dd.onDrag=function(){a.syncPosition();a.cfg.refireEvent("iframe");
+this.platform=="mac"&&YAHOO.env.ua.gecko&&this.showMacGeckoScrollbars();a.dragEvent.fire("onDrag",arguments)};this.dd.endDrag=function(){YAHOO.env.ua.ie==6&&j.removeClass(a.element,"drag");a.dragEvent.fire("endDrag",arguments);a.moveEvent.fire(a.cfg.getProperty("xy"))};this.dd.setHandleElId(this.header.id);this.dd.addInvalidHandleType("INPUT");this.dd.addInvalidHandleType("SELECT");this.dd.addInvalidHandleType("TEXTAREA")}},buildMask:function(){var a=this.mask;if(!a){if(!o){o=document.createElement("div");
+o.className="mask";o.innerHTML="&#160;"}a=o.cloneNode(true);a.id=this.id+"_mask";document.body.insertBefore(a,document.body.firstChild);this.mask=a;YAHOO.env.ua.gecko&&this.platform=="mac"&&j.addClass(this.mask,"block-scrollbars");this.stackMask()}},hideMask:function(){if(this.cfg.getProperty("modal")&&this.mask&&this.beforeHideMaskEvent.fire()){this.mask.style.display="none";j.removeClass(document.body,"masked");this.hideMaskEvent.fire()}},showMask:function(){if(this.cfg.getProperty("modal")&&this.mask&&
+this.beforeShowMaskEvent.fire()){j.addClass(document.body,"masked");this.sizeMask();this.mask.style.display="block";this.showMaskEvent.fire()}},sizeMask:function(){if(this.mask){var a=this.mask,e=j.getViewportWidth(),b=j.getViewportHeight();if(a.offsetHeight>b)a.style.height=b+"px";if(a.offsetWidth>e)a.style.width=e+"px";a.style.height=j.getDocumentHeight()+"px";a.style.width=j.getDocumentWidth()+"px"}},stackMask:function(){if(this.mask){var a=j.getStyle(this.element,"zIndex");!YAHOO.lang.isUndefined(a)&&
+!isNaN(a)&&j.setStyle(this.mask,"zIndex",a-1)}},render:function(a){return q.superclass.render.call(this,a,this.innerElement)},_renderHeader:function(a){a=a||this.innerElement;q.superclass._renderHeader.call(this,a)},_renderBody:function(a){a=a||this.innerElement;q.superclass._renderBody.call(this,a)},_renderFooter:function(a){a=a||this.innerElement;q.superclass._renderFooter.call(this,a)},destroy:function(a){l.windowResizeEvent.unsubscribe(this.sizeMask,this);this.removeMask();this.close&&h.purgeElement(this.close);
+q.superclass.destroy.call(this,a)},forceUnderlayRedraw:function(){var a=this.underlay;j.addClass(a,"yui-force-redraw");setTimeout(function(){j.removeClass(a,"yui-force-redraw")},0)},toString:function(){return"Panel "+this.id}})})();
+(function(){function a(){var a=this._aButtons,e,b;if(g.isArray(a)){e=a.length;if(e>0){b=e-1;do{e=a[b];if(YAHOO.widget.Button&&e instanceof YAHOO.widget.Button)e.destroy();else if(e.tagName.toUpperCase()=="BUTTON"){c.purgeElement(e);c.purgeElement(e,false)}}while(b--)}}}YAHOO.widget.Dialog=function(a,e){YAHOO.widget.Dialog.superclass.constructor.call(this,a,e)};var c=YAHOO.util.Event,b=YAHOO.util.CustomEvent,d=YAHOO.util.Dom,f=YAHOO.widget.Dialog,g=YAHOO.lang,j=["visible"];f.CSS_DIALOG="yui-dialog";
+YAHOO.extend(f,YAHOO.widget.Panel,{form:null,initDefaultConfig:function(){f.superclass.initDefaultConfig.call(this);this.callback={success:null,failure:null,argument:null};this.cfg.addProperty("postmethod",{handler:this.configPostMethod,value:"async",validator:function(a){return a!="form"&&a!="async"&&a!="none"&&a!="manual"?false:true}});this.cfg.addProperty("postdata",{value:null});this.cfg.addProperty("hideaftersubmit",{value:true});this.cfg.addProperty("buttons",{handler:this.configButtons,value:"none",
+supercedes:j})},initEvents:function(){f.superclass.initEvents.call(this);var a=b.LIST;this.beforeSubmitEvent=this.createEvent("beforeSubmit");this.beforeSubmitEvent.signature=a;this.submitEvent=this.createEvent("submit");this.submitEvent.signature=a;this.manualSubmitEvent=this.createEvent("manualSubmit");this.manualSubmitEvent.signature=a;this.asyncSubmitEvent=this.createEvent("asyncSubmit");this.asyncSubmitEvent.signature=a;this.formSubmitEvent=this.createEvent("formSubmit");this.formSubmitEvent.signature=
+a;this.cancelEvent=this.createEvent("cancel");this.cancelEvent.signature=a},init:function(a,e){f.superclass.init.call(this,a);this.beforeInitEvent.fire(f);d.addClass(this.element,f.CSS_DIALOG);this.cfg.setProperty("visible",false);e&&this.cfg.applyConfig(e,true);this.beforeHideEvent.subscribe(this.blurButtons,this,true);this.subscribe("changeBody",this.registerForm);this.initEvent.fire(f)},doSubmit:function(){var a=YAHOO.util.Connect,e=this.form,b=false,c=false,d,f;switch(this.cfg.getProperty("postmethod")){case "async":d=
+e.elements;f=d.length;if(f>0){f=f-1;do if(d[f].type=="file"){b=true;break}while(f--)}b&&(YAHOO.env.ua.ie&&this.isSecure)&&(c=true);d=this._getFormAttributes(e);a.setForm(e,b,c);e=this.cfg.getProperty("postdata");this.asyncSubmitEvent.fire(a.asyncRequest(d.method,d.action,this.callback,e));break;case "form":e.submit();this.formSubmitEvent.fire();break;case "none":case "manual":this.manualSubmitEvent.fire()}},_getFormAttributes:function(a){var e={method:null,action:null};if(a)if(a.getAttributeNode){var b=
+a.getAttributeNode("action"),a=a.getAttributeNode("method");if(b)e.action=b.value;if(a)e.method=a.value}else{e.action=a.getAttribute("action");e.method=a.getAttribute("method")}e.method=(g.isString(e.method)?e.method:"POST").toUpperCase();e.action=g.isString(e.action)?e.action:"";return e},registerForm:function(){var a=this.element.getElementsByTagName("form")[0];if(this.form){if(this.form==a&&d.isAncestor(this.element,this.form))return;c.purgeElement(this.form);this.form=null}if(!a){a=document.createElement("form");
+a.name="frm_"+this.id;this.body.appendChild(a)}if(a){this.form=a;c.on(a,"submit",this._submitHandler,this,true)}},_submitHandler:function(a){c.stopEvent(a);this.submit();this.form.blur()},setTabLoop:function(a,e){a=a||this.firstButton;e=e||this.lastButton;f.superclass.setTabLoop.call(this,a,e)},_setTabLoop:function(a,e){a=a||this.firstButton;e=this.lastButton||e;this.setTabLoop(a,e)},setFirstLastFocusable:function(){f.superclass.setFirstLastFocusable.call(this);var a,e,b,c=this.focusableElements;
+this.lastFormElement=this.firstFormElement=null;if(this.form&&c&&c.length>0){e=c.length;for(a=0;a<e;++a){b=c[a];if(this.form===b.form){this.firstFormElement=b;break}}for(a=e-1;a>=0;--a){b=c[a];if(this.form===b.form){this.lastFormElement=b;break}}}},configClose:function(a,e,b){f.superclass.configClose.apply(this,arguments)},_doClose:function(a){c.preventDefault(a);this.cancel()},configButtons:function(b,e){var i=YAHOO.widget.Button,k=e[0],f=this.innerElement,j,p,n,o,m,r;a.call(this);this._aButtons=
+null;if(g.isArray(k)){m=document.createElement("span");m.className="button-group";o=k.length;this._aButtons=[];this.defaultHtmlButton=null;for(r=0;r<o;r++){j=k[r];if(i){n=new i({label:j.text,type:j.type});n.appendTo(m);p=n.get("element");if(j.isDefault){n.addClass("default");this.defaultHtmlButton=p}g.isFunction(j.handler)?n.set("onclick",{fn:j.handler,obj:this,scope:this}):g.isObject(j.handler)&&g.isFunction(j.handler.fn)&&n.set("onclick",{fn:j.handler.fn,obj:!g.isUndefined(j.handler.obj)?j.handler.obj:
+this,scope:j.handler.scope||this});this._aButtons[this._aButtons.length]=n}else{p=document.createElement("button");p.setAttribute("type","button");if(j.isDefault){p.className="default";this.defaultHtmlButton=p}p.innerHTML=j.text;if(g.isFunction(j.handler))c.on(p,"click",j.handler,this,true);else if(g.isObject(j.handler)&&g.isFunction(j.handler.fn))c.on(p,"click",j.handler.fn,!g.isUndefined(j.handler.obj)?j.handler.obj:this,j.handler.scope||this);m.appendChild(p);this._aButtons[this._aButtons.length]=
+p}j.htmlButton=p;if(r===0)this.firstButton=p;if(r==o-1)this.lastButton=p}this.setFooter(m);i=this.footer;d.inDocument(this.element)&&!d.isAncestor(f,i)&&f.appendChild(i);this.buttonSpan=m}else{m=this.buttonSpan;i=this.footer;if(m&&i){i.removeChild(m);this.defaultHtmlButton=this.lastButton=this.firstButton=this.buttonSpan=null}}this.changeContentEvent.fire()},getButtons:function(){return this._aButtons||null},focusFirst:function(a,e){var b=this.firstFormElement,d=false;if(e&&e[1]){c.stopEvent(e[1]);
+if(e[0]===9&&this.firstElement)b=this.firstElement}if(b)try{b.focus();d=true}catch(f){}else d=this.defaultHtmlButton?this.focusDefaultButton():this.focusFirstButton();return d},focusLast:function(a,e){var b=this.cfg.getProperty("buttons"),d=this.lastFormElement,f=false;if(e&&e[1]){c.stopEvent(e[1]);if(e[0]===9&&this.lastElement)d=this.lastElement}if(b&&g.isArray(b))f=this.focusLastButton();else if(d)try{d.focus();f=true}catch(j){}return f},_getButton:function(a){var e=YAHOO.widget.Button;e&&(a&&a.nodeName&&
+a.id)&&(a=e.getButton(a.id)||a);return a},focusDefaultButton:function(){var a=this._getButton(this.defaultHtmlButton),e=false;if(a)try{a.focus();e=true}catch(b){}return e},blurButtons:function(){var a=this.cfg.getProperty("buttons"),e,b;if(a&&g.isArray(a)){e=a.length;if(e>0){e=e-1;do if(b=a[e])if(b=this._getButton(b.htmlButton))try{b.blur()}catch(c){}while(e--)}}},focusFirstButton:function(){var a=this.cfg.getProperty("buttons"),e=false;if(a&&g.isArray(a))if(a=a[0])if(a=this._getButton(a.htmlButton))try{a.focus();
+e=true}catch(b){}return e},focusLastButton:function(){var a=this.cfg.getProperty("buttons"),e,b=false;if(a&&g.isArray(a)){e=a.length;if(e>0)if(a=a[e-1])if(a=this._getButton(a.htmlButton))try{a.focus();b=true}catch(c){}}return b},configPostMethod:function(){this.registerForm()},validate:function(){return true},submit:function(){if(this.validate()&&this.beforeSubmitEvent.fire()){this.doSubmit();this.submitEvent.fire();this.cfg.getProperty("hideaftersubmit")&&this.hide();return true}return false},cancel:function(){this.cancelEvent.fire();
+this.hide()},getData:function(){function a(e){var b=e.tagName.toUpperCase();return(b=="INPUT"||b=="TEXTAREA"||b=="SELECT")&&e.name==g}var e=this.form,b,c,f,g,j,n,o,m,r,s,t;if(e){b=e.elements;c=b.length;f={};for(t=0;t<c;t++){g=b[t].name;j=d.getElementsBy(a,"*",e);n=j.length;if(n>0)if(n==1){j=j[0];o=j.type;m=j.tagName.toUpperCase();switch(m){case "INPUT":if(o=="checkbox")f[g]=j.checked;else if(o!="radio")f[g]=j.value;break;case "TEXTAREA":f[g]=j.value;break;case "SELECT":j=j.options;n=j.length;m=[];
+for(o=0;o<n;o++){r=j[o];if(r.selected){s=r.attributes.value;m[m.length]=s&&s.specified?r.value:r.text}}f[g]=m}}else{o=j[0].type;switch(o){case "radio":for(o=0;o<n;o++){m=j[o];if(m.checked){f[g]=m.value;break}}break;case "checkbox":m=[];for(o=0;o<n;o++){r=j[o];if(r.checked)m[m.length]=r.value}f[g]=m}}}}return f},destroy:function(b){a.call(this);this._aButtons=null;var e=this.element.getElementsByTagName("form");if(e.length>0)if(e=e[0]){c.purgeElement(e);e.parentNode&&e.parentNode.removeChild(e);this.form=
+null}f.superclass.destroy.call(this,b)},toString:function(){return"Dialog "+this.id}})})();
+(function(){YAHOO.widget.SimpleDialog=function(a,b){YAHOO.widget.SimpleDialog.superclass.constructor.call(this,a,b)};var a=YAHOO.util.Dom,c=YAHOO.widget.SimpleDialog,b=["icon"];c.ICON_BLOCK="blckicon";c.ICON_ALARM="alrticon";c.ICON_HELP="hlpicon";c.ICON_INFO="infoicon";c.ICON_WARN="warnicon";c.ICON_TIP="tipicon";c.ICON_CSS_CLASSNAME="yui-icon";c.CSS_SIMPLEDIALOG="yui-simple-dialog";YAHOO.extend(c,YAHOO.widget.Dialog,{initDefaultConfig:function(){c.superclass.initDefaultConfig.call(this);this.cfg.addProperty("icon",
+{handler:this.configIcon,value:"none",suppressEvent:true});this.cfg.addProperty("text",{handler:this.configText,value:"",suppressEvent:true,supercedes:b})},init:function(b,f){c.superclass.init.call(this,b);this.beforeInitEvent.fire(c);a.addClass(this.element,c.CSS_SIMPLEDIALOG);this.cfg.queueProperty("postmethod","manual");f&&this.cfg.applyConfig(f,true);this.beforeRenderEvent.subscribe(function(){this.body||this.setBody("")},this,true);this.initEvent.fire(c)},registerForm:function(){c.superclass.registerForm.call(this);
+var a=this.form.ownerDocument.createElement("input");a.type="hidden";a.name=this.id;a.value="";this.form.appendChild(a)},configIcon:function(b,f){var g=f[0],j=this.body,h=c.ICON_CSS_CLASSNAME,e,i;if(g&&g!="none"){e=a.getElementsByClassName(h,"*",j);if(e.length===1){e=e[0];(i=e.parentNode)&&i.removeChild(e)}if(g.indexOf(".")==-1){e=document.createElement("span");e.className=h+" "+g;e.innerHTML="&#160;"}else{e=document.createElement("img");e.src=this.imageRoot+g;e.className=h}e&&j.insertBefore(e,j.firstChild)}},
+configText:function(a,b){var c=b[0];if(c){this.setBody(c);this.cfg.refireEvent("icon")}},toString:function(){return"SimpleDialog "+this.id}})})();
+(function(){YAHOO.widget.ContainerEffect=function(a,b,c,j,h){if(!h)h=YAHOO.util.Anim;this.overlay=a;this.attrIn=b;this.attrOut=c;this.targetElement=j||a.element;this.animClass=h};var a=YAHOO.util.Dom,c=YAHOO.util.CustomEvent,b=YAHOO.widget.ContainerEffect;b.FADE=function(c,f){var g=YAHOO.util.Easing,g=new b(c,{attributes:{opacity:{from:0,to:1}},duration:f,method:g.easeIn},{attributes:{opacity:{to:0}},duration:f,method:g.easeOut},c.element);g.handleUnderlayStart=function(){var b=this.overlay.underlay;
+b&&YAHOO.env.ua.ie&&b.filters&&b.filters.length>0&&a.addClass(c.element,"yui-effect-fade")};g.handleUnderlayComplete=function(){this.overlay.underlay&&YAHOO.env.ua.ie&&a.removeClass(c.element,"yui-effect-fade")};g.handleStartAnimateIn=function(b,c,e){e.overlay._fadingIn=true;a.addClass(e.overlay.element,"hide-select");e.overlay.underlay||e.overlay.cfg.refireEvent("underlay");e.handleUnderlayStart();e.overlay._setDomVisibility(true);a.setStyle(e.overlay.element,"opacity",0)};g.handleCompleteAnimateIn=
+function(b,c,e){e.overlay._fadingIn=false;a.removeClass(e.overlay.element,"hide-select");if(e.overlay.element.style.filter)e.overlay.element.style.filter=null;e.handleUnderlayComplete();e.overlay.cfg.refireEvent("iframe");e.animateInCompleteEvent.fire()};g.handleStartAnimateOut=function(b,c,e){e.overlay._fadingOut=true;a.addClass(e.overlay.element,"hide-select");e.handleUnderlayStart()};g.handleCompleteAnimateOut=function(b,c,e){e.overlay._fadingOut=false;a.removeClass(e.overlay.element,"hide-select");
+if(e.overlay.element.style.filter)e.overlay.element.style.filter=null;e.overlay._setDomVisibility(false);a.setStyle(e.overlay.element,"opacity",1);e.handleUnderlayComplete();e.overlay.cfg.refireEvent("iframe");e.animateOutCompleteEvent.fire()};g.init();return g};b.SLIDE=function(c,f){var g=YAHOO.util.Easing,j=c.cfg.getProperty("x")||a.getX(c.element),h=c.cfg.getProperty("y")||a.getY(c.element),e=a.getClientWidth(),i=c.element.offsetWidth,g=new b(c,{attributes:{points:{to:[j,h]}},duration:f,method:g.easeIn},
+{attributes:{points:{to:[e+25,h]}},duration:f,method:g.easeOut},c.element,YAHOO.util.Motion);g.handleStartAnimateIn=function(a,e,b){b.overlay.element.style.left=-25-i+"px";b.overlay.element.style.top=h+"px"};g.handleTweenAnimateIn=function(e,b,i){b=a.getXY(i.overlay.element);e=b[0];b=b[1];a.getStyle(i.overlay.element,"visibility")=="hidden"&&e<j&&i.overlay._setDomVisibility(true);i.overlay.cfg.setProperty("xy",[e,b],true);i.overlay.cfg.refireEvent("iframe")};g.handleCompleteAnimateIn=function(a,e,
+b){b.overlay.cfg.setProperty("xy",[j,h],true);b.startX=j;b.startY=h;b.overlay.cfg.refireEvent("iframe");b.animateInCompleteEvent.fire()};g.handleStartAnimateOut=function(e,b,i){e=a.getViewportWidth();b=a.getXY(i.overlay.element)[1];i.animOut.attributes.points.to=[e+25,b]};g.handleTweenAnimateOut=function(e,b,i){e=a.getXY(i.overlay.element);i.overlay.cfg.setProperty("xy",[e[0],e[1]],true);i.overlay.cfg.refireEvent("iframe")};g.handleCompleteAnimateOut=function(a,e,b){b.overlay._setDomVisibility(false);
+b.overlay.cfg.setProperty("xy",[j,h]);b.animateOutCompleteEvent.fire()};g.init();return g};b.prototype={init:function(){this.beforeAnimateInEvent=this.createEvent("beforeAnimateIn");this.beforeAnimateInEvent.signature=c.LIST;this.beforeAnimateOutEvent=this.createEvent("beforeAnimateOut");this.beforeAnimateOutEvent.signature=c.LIST;this.animateInCompleteEvent=this.createEvent("animateInComplete");this.animateInCompleteEvent.signature=c.LIST;this.animateOutCompleteEvent=this.createEvent("animateOutComplete");
+this.animateOutCompleteEvent.signature=c.LIST;this.animIn=new this.animClass(this.targetElement,this.attrIn.attributes,this.attrIn.duration,this.attrIn.method);this.animIn.onStart.subscribe(this.handleStartAnimateIn,this);this.animIn.onTween.subscribe(this.handleTweenAnimateIn,this);this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn,this);this.animOut=new this.animClass(this.targetElement,this.attrOut.attributes,this.attrOut.duration,this.attrOut.method);this.animOut.onStart.subscribe(this.handleStartAnimateOut,
+this);this.animOut.onTween.subscribe(this.handleTweenAnimateOut,this);this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut,this)},animateIn:function(){this._stopAnims(this.lastFrameOnStop);this.beforeAnimateInEvent.fire();this.animIn.animate()},animateOut:function(){this._stopAnims(this.lastFrameOnStop);this.beforeAnimateOutEvent.fire();this.animOut.animate()},lastFrameOnStop:true,_stopAnims:function(a){this.animOut&&this.animOut.isAnimated()&&this.animOut.stop(a);this.animIn&&this.animIn.isAnimated()&&
+this.animIn.stop(a)},handleStartAnimateIn:function(){},handleTweenAnimateIn:function(){},handleCompleteAnimateIn:function(){},handleStartAnimateOut:function(){},handleTweenAnimateOut:function(){},handleCompleteAnimateOut:function(){},toString:function(){var a="ContainerEffect";this.overlay&&(a=a+(" ["+this.overlay.toString()+"]"));return a}};YAHOO.lang.augmentProto(b,YAHOO.util.EventProvider)})();YAHOO.register("container",YAHOO.widget.Module,{version:"2.9.0",build:"2800"});
+var Y=YAHOO,Y_DOM=YAHOO.util.Dom,EMPTY_ARRAY=[],Y_UA=Y.env.ua,Y_Lang=Y.lang,Y_DOC=document,Y_DOCUMENT_ELEMENT=Y_DOC.documentElement,Y_DOM_inDoc=Y_DOM.inDocument,Y_mix=Y_Lang.augmentObject,Y_guid=Y_DOM.generateId,Y_getDoc=function(a){var c=Y_DOC;a&&(c=a.nodeType===9?a:a.ownerDocument||a.document||Y_DOC);return c},Y_Array=function(a,c){var b,d,f=c||0;try{return Array.prototype.slice.call(a,f)}catch(g){d=[];for(b=a.length;f<b;f++)d.push(a[f]);return d}},Y_DOM_allById=function(a,c){var c=c||Y_DOC,b=[],
+d=[],f,g;if(c.querySelectorAll)d=c.querySelectorAll('[id="'+a+'"]');else if(c.all){if(b=c.all(a)){if(b.nodeName)if(b.id===a){d.push(b);b=EMPTY_ARRAY}else b=[b];if(b.length)for(f=0;g=b[f++];)(g.id===a||g.attributes&&g.attributes.id&&g.attributes.id.value===a)&&d.push(g)}}else d=[Y_getDoc(c).getElementById(a)];return d},COMPARE_DOCUMENT_POSITION="compareDocumentPosition",OWNER_DOCUMENT="ownerDocument",Selector={_foundCache:[],useNative:!0,_compare:"sourceIndex"in Y_DOCUMENT_ELEMENT?function(a,c){var b=
+a.sourceIndex,d=c.sourceIndex;return b===d?0:b>d?1:-1}:Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION]?function(a,c){return a[COMPARE_DOCUMENT_POSITION](c)&4?-1:1}:function(a,c){var b,d;if(a&&c){b=a[OWNER_DOCUMENT].createRange();b.setStart(a,0);d=c[OWNER_DOCUMENT].createRange();d.setStart(c,0);b=b.compareBoundaryPoints(1,d)}return b},_sort:function(a){if(a){a=Y_Array(a,0,true);a.sort&&a.sort(Selector._compare)}return a},_deDupe:function(a){var c=[],b,d;for(b=0;d=a[b++];)if(!d._found){c[c.length]=d;
+d._found=true}for(b=0;d=c[b++];){d._found=null;d.removeAttribute("_found")}return c},query:function(a,c,b,d){if(c&&typeof c=="string"){c=Y_DOM.get(c);if(!c)return b?null:[]}else c=c||Y_DOC;var f=[],g=Selector.useNative&&Y_DOC.querySelector&&!d,j=[[a,c]],h=g?Selector._nativeQuery:Selector._bruteQuery;if(a&&h){if(!d&&(!g||c.tagName))j=Selector._splitQueries(a,c);for(a=0;c=j[a++];){c=h(c[0],c[1],b);b||(c=Y_Array(c,0,true));c&&(f=f.concat(c))}j.length>1&&(f=Selector._sort(Selector._deDupe(f)))}return b?
+f[0]||null:f},_splitQueries:function(a,c){var b=a.split(","),d=[],f="",g,j;if(c){if(c.tagName){c.id=c.id||Y_guid();f='[id="'+c.id+'"] '}g=0;for(j=b.length;g<j;++g){a=f+b[g];d.push([a,c])}}return d},_nativeQuery:function(a,c,b){if(Y_UA.webkit&&a.indexOf(":checked")>-1&&Selector.pseudos&&Selector.pseudos.checked)return Selector.query(a,c,b,true);try{return c["querySelector"+(b?"":"All")](a)}catch(d){return Selector.query(a,c,b,true)}},filter:function(a,c){var b=[],d,f;if(a&&c)for(d=0;f=a[d++];)Selector.test(f,
+c)&&(b[b.length]=f);return b},test:function(a,c,b){var d=false,c=c.split(","),f=false,g,j,h,e,i;if(a&&a.tagName){if(!b&&!Y_DOM_inDoc(a)){b=a.parentNode;if(!b){h=a[OWNER_DOCUMENT].createDocumentFragment();h.appendChild(a);b=h;f=true}}b=b||a[OWNER_DOCUMENT];if(!a.id)a.id=Y_guid();for(e=0;g=c[e++];){g=g+('[id="'+a.id+'"]');j=Selector.query(g,b);for(i=0;g=j[i++];)if(g===a){d=true;break}if(d)break}f&&h.removeChild(a)}return d}};YAHOO.util.Selector=Selector;
+var PARENT_NODE="parentNode",TAG_NAME="tagName",ATTRIBUTES="attributes",COMBINATOR="combinator",PSEUDOS="pseudos",SelectorCSS2={_reRegExpTokens:/([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,SORT_RESULTS:!0,_children:function(a,c){var b=a.children,d,f,g;if(a.children&&c&&a.children.tags)a.children.tags(c);else if(!b&&a[TAG_NAME]||b&&c){f=b||a.childNodes;b=[];for(d=0;g=f[d++];)g.tagName&&(!c||c===g.tagName)&&b.push(g)}return b||[]},_re:{attr:/(\[[^\]]*\])/g,esc:/\\[:\[\]\(\)#\.\'\>+~"]/gi,pseudos:/(\([^\)]*\))/g},
+shorthand:{"\\#(-?[_a-z]+[-\\w\\uE000]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w\\uE000]*)":"[className~=$1]"},operators:{"":function(a,c){return!!a.getAttribute(c)},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}(?:-|$)"},pseudos:{"first-child":function(a){return Selector._children(a[PARENT_NODE])[0]===a}},_bruteQuery:function(a,c,b){var d=[],f=[],a=Selector._tokenize(a),g=a[a.length-1];Y_getDoc(c);var j,h;if(g){j=g.id;h=g.className;g=g.tagName||"*";if(c.getElementsByTagName)f=j&&(c.all||c.nodeType===9||Y_DOM_inDoc(c))?
+Y_DOM_allById(j,c):h?c.getElementsByClassName(h):c.getElementsByTagName(g);else for(c=c.firstChild;c;){c.tagName&&f.push(c);c=c.nextSilbing||c.firstChild}f.length&&(d=Selector._filterNodes(f,a,b))}return d},_filterNodes:function(a,c,b){for(var d=0,f,g=c.length,j=g-1,h=[],e=a[0],i=e,k=Selector.getters,l,q,p,n,o,m,d=0;i=e=a[d++];){j=g-1;p=null;a:for(;i&&i.tagName;){q=c[j];o=q.tests;if(f=o.length)for(;m=o[--f];){l=m[1];if(k[m[0]])n=k[m[0]](i,m[0]);else{n=i[m[0]];n===void 0&&i.getAttribute&&(n=i.getAttribute(m[0]))}if(l===
+"="&&n!==m[2]||typeof l!=="string"&&l.test&&!l.test(n)||!l.test&&typeof l==="function"&&!l(i,m[0],m[2])){if(i=i[p])for(;i&&(!i.tagName||q.tagName&&q.tagName!==i.tagName);)i=i[p];continue a}}j--;if(f=q.combinator){p=f.axis;for(i=i[p];i&&!i.tagName;)i=i[p];f.direct&&(p=null)}else{h.push(e);if(b)return h;break}}}return h},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:!0},"+":{axis:"previousSibling",direct:!0}},_parsers:[{name:ATTRIBUTES,re:/^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
+fn:function(a,c){var b=a[2]||"",d=Selector.operators,f=a[3]?a[3].replace(/\\/g,""):"";if(a[1]==="id"&&b==="="||a[1]==="className"&&Y_DOCUMENT_ELEMENT.getElementsByClassName&&(b==="~="||b==="=")){c.prefilter=a[1];a[3]=f;c[a[1]]=a[1]==="id"?a[3]:f}if(b in d){b=d[b];if(typeof b==="string"){a[3]=f.replace(Selector._reRegExpTokens,"\\$1");b=RegExp(b.replace("{val}",a[3]))}a[2]=b}if(!c.last||c.prefilter!==a[1])return a.slice(1)}},{name:TAG_NAME,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(a,c){var b=a[1].toUpperCase();
+c.tagName=b;if(b!=="*"&&(!c.last||c.prefilter))return[TAG_NAME,"=",b];if(!c.prefilter)c.prefilter="tagName"}},{name:COMBINATOR,re:/^\s*([>+~]|\s)\s*/,fn:function(){}},{name:PSEUDOS,re:/^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,fn:function(a){var c=Selector[PSEUDOS][a[1]];if(c){a[2]&&(a[2]=a[2].replace(/\\/g,""));return[a[2],c]}return false}}],_getToken:function(){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]}},_tokenize:function(a){var a=Selector._replaceShorthand(Y_Lang.trim(a||
+"")),c=Selector._getToken(),b=[],d=false,f,g,j;a:do{d=false;for(g=0;j=Selector._parsers[g++];)if(f=j.re.exec(a)){if(j.name!==COMBINATOR)c.selector=a;a=a.replace(f[0],"");if(!a.length)c.last=true;Selector._attrFilters[f[1]]&&(f[1]=Selector._attrFilters[f[1]]);d=j.fn(f,c);if(d===false){d=false;break a}else d&&c.tests.push(d);if(!a.length||j.name===COMBINATOR){b.push(c);c=Selector._getToken(c);if(j.name===COMBINATOR)c.combinator=Selector.combinators[f[1]]}d=true}}while(d&&a.length);if(!d||a.length)b=
+[];return b},_replaceShorthand:function(a){var c=Selector.shorthand,b=a.match(Selector._re.esc),d,f,g;b&&(a=a.replace(Selector._re.esc,"\ue000"));d=a.match(Selector._re.attr);f=a.match(Selector._re.pseudos);d&&(a=a.replace(Selector._re.attr,"\ue001"));f&&(a=a.replace(Selector._re.pseudos,"\ue002"));for(g in c)c.hasOwnProperty(g)&&(a=a.replace(RegExp(g,"gi"),c[g]));if(d){c=0;for(g=d.length;c<g;++c)a=a.replace(/\uE001/,d[c])}if(f){c=0;for(g=f.length;c<g;++c)a=a.replace(/\uE002/,f[c])}a=a.replace(/\[/g,
+"\ue003");a=a.replace(/\]/g,"\ue004");a=a.replace(/\(/g,"\ue005");a=a.replace(/\)/g,"\ue006");if(b){c=0;for(g=b.length;c<g;++c)a=a.replace("\ue000",b[c])}return a},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(a,c){return Y_DOM.getAttribute(a,c)}}};Y_mix(Selector,SelectorCSS2,!0);Selector.getters.src=Selector.getters.rel=Selector.getters.href;Selector.useNative&&Y_DOC.querySelector&&(Selector.shorthand["\\.([^\\s\\\\(\\[:]*)"]="[class~=$1]");Selector._reNth=/^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
+Selector._getNth=function(a,c,b,d){Selector._reNth.test(c);var c=parseInt(RegExp.$1,10),f=RegExp.$2,g=RegExp.$3,j=parseInt(RegExp.$4,10)||0,b=Selector._children(a.parentNode,b);if(g){c=2;j=g==="odd"?1:0}else isNaN(c)&&(c=f?1:0);if(c===0){d&&(j=b.length-j+1);return b[j-1]===a?true:false}if(c<0){d=!!d;c=Math.abs(c)}if(d){d=b.length-j;for(f=b.length;d>=0;d=d-c)if(d<f&&b[d]===a)return true}else{d=j-1;for(f=b.length;d<f;d=d+c)if(d>=0&&b[d]===a)return true}return false};
+Y_mix(Selector.pseudos,{root:function(a){return a===a.ownerDocument.documentElement},"nth-child":function(a,c){return Selector._getNth(a,c)},"nth-last-child":function(a,c){return Selector._getNth(a,c,null,true)},"nth-of-type":function(a,c){return Selector._getNth(a,c,a.tagName)},"nth-last-of-type":function(a,c){return Selector._getNth(a,c,a.tagName,true)},"last-child":function(a){var c=Selector._children(a.parentNode);return c[c.length-1]===a},"first-of-type":function(a){return Selector._children(a.parentNode,
+a.tagName)[0]===a},"last-of-type":function(a){var c=Selector._children(a.parentNode,a.tagName);return c[c.length-1]===a},"only-child":function(a){var c=Selector._children(a.parentNode);return c.length===1&&c[0]===a},"only-of-type":function(a){var c=Selector._children(a.parentNode,a.tagName);return c.length===1&&c[0]===a},empty:function(a){return a.childNodes.length===0},not:function(a,c){return!Selector.test(a,c)},contains:function(a,c){return(a.innerText||a.textContent||"").indexOf(c)>-1},checked:function(a){return a.checked===
+true||a.selected===true},enabled:function(a){return a.disabled!==void 0&&!a.disabled},disabled:function(a){return a.disabled}});Y_mix(Selector.operators,{"^=":"^{val}","!=":function(a,c,b){return a[c]!==b},"$=":"{val}$","*=":"{val}"});Selector.combinators["~"]={axis:"previousSibling"};YAHOO.register("selector",YAHOO.util.Selector,{version:"2.9.0",build:"2800"});
+(function(){var a=YAHOO.util.Event,c=YAHOO.lang,b=[],d=function(a,b,c){return!a||a===c?false:YAHOO.util.Selector.test(a,b)?a:d(a.parentNode,b,c)};c.augmentObject(a,{_createDelegate:function(b,g,j,h){return function(e){var i=a.getTarget(e),k=g,l=this.nodeType===9,q;if(c.isFunction(g))q=g(i);else if(c.isString(g)){if(!l){(k=this.id)||(k=a.generateId(this));k="#"+k+" ";k=(k+g).replace(/,/gi,","+k)}YAHOO.util.Selector.test(i,k)?q=i:YAHOO.util.Selector.test(i,k.replace(/,/gi," *,")+" *")&&(q=d(i,k,this))}if(q){i=
+q;h&&(i=h===true?j:h);return b.call(i,e,q,this,j)}}},delegate:function(d,g,j,h,e,i){var k=g,l;if(c.isString(h)&&!YAHOO.util.Selector)return false;if(g=="mouseenter"||g=="mouseleave"){if(!a._createMouseDelegate)return false;k=a._getType(g);l=a._createMouseDelegate(j,e,i);g=a._createDelegate(function(a,e,b){return l.call(e,a,b)},h,e,i)}else g=a._createDelegate(j,h,e,i);b.push([d,k,j,g]);return a.on(d,k,g)},removeDelegate:function(c,d,j){var h=d,e=false,i;if(d=="mouseenter"||d=="mouseleave")h=a._getType(d);
+d=a._getCacheIndex(b,c,h,j);d>=0&&(i=b[d]);if(c&&i)if(e=a.removeListener(i[0],i[1],i[3])){delete b[d][2];delete b[d][3];b.splice(d,1)}return e}})})();YAHOO.register("event-delegate",YAHOO.util.Event,{version:"2.9.0",build:"2800"});
+(function(){function a(a){s[a]||(s[a]="\\u"+("0000"+(+a.charCodeAt(0)).toString(16)).slice(-4));return s[a]}function c(a,e){var b=function(a,i){var c,d,k=a[i];if(k&&typeof k==="object")for(c in k)if(j.hasOwnProperty(k,c)){d=b(k,c);d===void 0?delete k[c]:k[c]=d}return e.call(a,i,k)};return typeof e==="function"?b({"":a},""):a}function b(a){return j.isString(a)&&!m.test(a.replace(p,"@").replace(n,"]").replace(o,""))}function d(e,i){e=e.replace(q,a);if(b(e))return c(eval("("+e+")"),i);throw new SyntaxError("JSON.parse");
+}function f(a){var e=typeof a;return A[e]||A[k.call(a)]||(e===u?a?u:w:t)}function g(b,c,d){function l(b,k){var q=b[k],o=f(q),m=[],s=d?N:H,t,I,A,M;e(q)&&h(q.toJSON)?q=q.toJSON(k):o===B&&(q=p(q));h(g)&&(q=g.call(b,k,q));q!==b[k]&&(o=f(q));switch(o){case B:case u:break;case x:return G+q.replace(r,a)+G;case v:return isFinite(q)?q+D:w;case y:return q+D;case w:return w;default:return}for(t=n.length-1;t>=0;--t)if(n[t]===q)throw Error("JSON.stringify. Cyclical reference");o=i(q);n.push(q);if(o)for(t=q.length-
+1;t>=0;--t)m[t]=l(q,t)||w;else{I=c||q;t=0;for(A in I)if(j.hasOwnProperty(I,A))(M=l(q,A))&&(m[t++]=G+A.replace(r,a)+G+s+M)}n.pop();if(d&&m.length){if(o){q=z+F;m=m.join(L).replace(/^/gm,d);m=q+m+F+J}else{q=E+F;m=m.join(L).replace(/^/gm,d);m=q+m+F+C}return m}return o?z+m.join(K)+J:E+m.join(K)+C}if(b!==void 0){var g=h(c)?c:null,q=k.call(d).match(/String|Number/)||[],p=YAHOO.lang.JSON.dateToString,n=[],o,m,s;if(g||!i(c))c=void 0;if(c){o={};m=0;for(s=c.length;m<s;++m)o[c[m]]=true;c=o}d=q[0]==="Number"?
+Array(Math.min(Math.max(0,d),10)+1).join(" "):(d||D).slice(0,10);return l({"":b},"")}}var j=YAHOO.lang,h=j.isFunction,e=j.isObject,i=j.isArray,k=Object.prototype.toString,l=(YAHOO.env.ua.caja?window:this).JSON,q=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,n=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,o=/(?:^|:|,)(?:\s*\[)+/g,m=/[^\],:{}\s]/,r=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+s={"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},t="undefined",u="object",w="null",x="string",v="number",y="boolean",B="date",A={undefined:t,string:x,"[object String]":x,number:v,"[object Number]":v,"boolean":y,"[object Boolean]":y,"[object Date]":B,"[object RegExp]":u},D="",E="{",C="}",z="[",J="]",K=",",L=",\n",F="\n",H=":",N=": ",G='"',l=k.call(l)==="[object JSON]"&&l;YAHOO.lang.JSON={useNativeParse:!!l,useNativeStringify:!!l,isSafe:function(e){return b(e.replace(q,
+a))},parse:function(a,e){typeof a!=="string"&&(a=a+"");return l&&YAHOO.lang.JSON.useNativeParse?l.parse(a,e):d(a,e)},stringify:function(a,e,b){return l&&YAHOO.lang.JSON.useNativeStringify?l.stringify(a,e,b):g(a,e,b)},dateToString:function(a){function e(a){return a<10?"0"+a:a}return a.getUTCFullYear()+"-"+e(a.getUTCMonth()+1)+"-"+e(a.getUTCDate())+"T"+e(a.getUTCHours())+H+e(a.getUTCMinutes())+H+e(a.getUTCSeconds())+"Z"},stringToDate:function(a){var e=a.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?Z$/);
+if(e){a=new Date;a.setUTCFullYear(e[1],e[2]-1,e[3]);a.setUTCHours(e[4],e[5],e[6],e[7]||0)}return a}};YAHOO.lang.JSON.isValid=YAHOO.lang.JSON.isSafe})();YAHOO.register("json",YAHOO.lang.JSON,{version:"2.9.0",build:"2800"});
+(function(){var a=YAHOO.util.Event,c=YAHOO.lang,b=a.addListener,d=a.removeListener,f=a.getListeners,g=[],j={mouseenter:"mouseover",mouseleave:"mouseout"},h=function(e,b,c){var c=a._getCacheIndex(g,e,b,c),f,h;c>=0&&(f=g[c]);if(e&&f)if(h=d.call(a,f[0],b,f[3])){delete g[c][2];delete g[c][3];g.splice(c,1)}return h};c.augmentObject(a._specialTypes,j);c.augmentObject(a,{_createMouseDelegate:function(e,b,c){return function(d,f){var g=a.getRelatedTarget(d),h;if(this!=g&&!YAHOO.util.Dom.isAncestor(this,g)){g=
+this;c&&(g=c===true?b:c);h=[d,b];f&&h.splice(1,0,this,f);return e.apply(g,h)}}},addListener:function(e,i,c,d,f){var h;if(j[i]){h=a._createMouseDelegate(c,d,f);h.mouseDelegate=true;g.push([e,i,c,h]);h=b.call(a,e,i,h)}else h=b.apply(a,arguments);return h},removeListener:function(e,b,c){return j[b]?h.apply(a,arguments):d.apply(a,arguments)},getListeners:function(e,b){var c=[],d,g=b==="mouseover"||b==="mouseout",h,n,o;if(b&&(g||j[b])){if(d=f.call(a,e,this._getType(b)))for(n=d.length-1;n>-1;n--){o=d[n];
+h=o.fn.mouseDelegate;(j[b]&&h||g&&!h)&&c.push(o)}}else c=f.apply(a,arguments);return c&&c.length?c:null}},true);a.on=a.addListener})();YAHOO.register("event-mouseenter",YAHOO.util.Event,{version:"2.9.0",build:"2800"});
+(function(){var a=YAHOO.lang,c=YAHOO.util;c.DataSourceBase=function(b,f){if(!(b===null||b===void 0)){this.liveData=b;this._oQueue={interval:null,conn:null,requests:[]};this.responseSchema={};if(f&&f.constructor==Object)for(var g in f)g&&(this[g]=f[g]);a.isNumber(this.maxCacheEntries);this._aIntervals=[];this.createEvent("cacheRequestEvent");this.createEvent("cacheResponseEvent");this.createEvent("requestEvent");this.createEvent("responseEvent");this.createEvent("responseParseEvent");this.createEvent("responseCacheEvent");
+this.createEvent("dataErrorEvent");this.createEvent("cacheFlushEvent");g=c.DataSourceBase;this._sName="DataSource instance"+g._nIndex;g._nIndex++}};var b=c.DataSourceBase;a.augmentObject(b,{TYPE_UNKNOWN:-1,TYPE_JSARRAY:0,TYPE_JSFUNCTION:1,TYPE_XHR:2,TYPE_JSON:3,TYPE_XML:4,TYPE_TEXT:5,TYPE_HTMLTABLE:6,TYPE_SCRIPTNODE:7,TYPE_LOCAL:8,ERROR_DATAINVALID:"Invalid data",ERROR_DATANULL:"Null data",_nIndex:0,_nTransactionId:0,_cloneObject:function(c){if(!a.isValue(c))return c;var f={};if(Object.prototype.toString.apply(c)===
+"[object RegExp]")f=c;else if(a.isFunction(c))f=c;else if(a.isArray(c))for(var f=[],g=0,j=c.length;g<j;g++)f[g]=b._cloneObject(c[g]);else if(a.isObject(c))for(g in c)a.hasOwnProperty(c,g)&&(f[g]=a.isValue(c[g])&&a.isObject(c[g])||a.isArray(c[g])?b._cloneObject(c[g]):c[g]);else f=c;return f},_getLocationValue:function(b,c){var g=b.locator||b.key||b,j=c.ownerDocument||c,h,e,i=null;try{if(a.isUndefined(j.evaluate)){j.setProperty("SelectionLanguage","XPath");h=c.selectNodes(g)[0];i=h.value||h.text||null}else for(h=
+j.evaluate(g,c,j.createNSResolver(!c.ownerDocument?c.documentElement:c.ownerDocument.documentElement),0,null);e=h.iterateNext();)i=e.textContent;return i}catch(k){}},issueCallback:function(b,c,g,j){if(a.isFunction(b))b.apply(j,c);else if(a.isObject(b)){var j=b.scope||j||window,h=b.success;if(g)h=b.failure;h&&h.apply(j,c.concat([b.argument]))}},parseString:function(b){if(!a.isValue(b))return null;b=b+"";return a.isString(b)?b:null},parseNumber:function(b){if(!a.isValue(b)||b==="")return null;b=b*1;
+return a.isNumber(b)?b:null},convertNumber:function(a){return b.parseNumber(a)},parseDate:function(b){var c=null;if(a.isValue(b)&&!(b instanceof Date))c=new Date(b);else return b;return c instanceof Date?c:null},convertDate:function(a){return b.parseDate(a)}});b.Parser={string:b.parseString,number:b.parseNumber,date:b.parseDate};b.prototype={_sName:null,_aCache:null,_oQueue:null,_aIntervals:null,maxCacheEntries:0,liveData:null,dataType:b.TYPE_UNKNOWN,responseType:b.TYPE_UNKNOWN,responseSchema:null,
+useXPath:false,cloneBeforeCaching:false,toString:function(){return this._sName},getCachedResponse:function(a,b,c){var j=this._aCache;if(this.maxCacheEntries>0)if(j){var h=j.length;if(h>0){var e=null;this.fireEvent("cacheRequestEvent",{request:a,callback:b,caller:c});for(var i=h-1;i>=0;i--){var k=j[i];if(this.isCacheHit(a,k.request)){e=k.response;this.fireEvent("cacheResponseEvent",{request:a,response:e,callback:b,caller:c});if(i<h-1){j.splice(i,1);this.addToCache(a,e)}e.cached=true;break}}return e}}else this._aCache=
+[];else if(j)this._aCache=null;return null},isCacheHit:function(a,b){return a===b},addToCache:function(a,c){var g=this._aCache;if(g){for(;g.length>=this.maxCacheEntries;)g.shift();c=this.cloneBeforeCaching?b._cloneObject(c):c;g[g.length]={request:a,response:c};this.fireEvent("responseCacheEvent",{request:a,response:c})}},flushCache:function(){if(this._aCache){this._aCache=[];this.fireEvent("cacheFlushEvent")}},setInterval:function(b,c,g,j){if(a.isNumber(b)&&b>=0){var h=this,b=setInterval(function(){h.makeConnection(c,
+g,j)},b);this._aIntervals.push(b);return b}},clearInterval:function(a){for(var b=this._aIntervals||[],c=b.length-1;c>-1;c--)if(b[c]===a){b.splice(c,1);clearInterval(a)}},clearAllIntervals:function(){for(var a=this._aIntervals||[],b=a.length-1;b>-1;b--)clearInterval(a[b])},sendRequest:function(a,c,g){var j=this.getCachedResponse(a,c,g);if(j){b.issueCallback(c,[a,j],false,g);return null}return this.makeConnection(a,c,g)},makeConnection:function(a,c,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",
+{tId:j,request:a,callback:c,caller:g});this.handleResponse(a,this.liveData,c,g,j);return j},handleResponse:function(c,f,g,j,h){this.fireEvent("responseEvent",{tId:h,request:c,response:f,callback:g,caller:j});var e=this.dataType==b.TYPE_XHR?true:false,i=null,k=f;if(this.responseType===b.TYPE_UNKNOWN)if(i=f&&f.getResponseHeader?f.getResponseHeader["Content-Type"]:null)if(i.indexOf("text/xml")>-1)this.responseType=b.TYPE_XML;else if(i.indexOf("application/json")>-1)this.responseType=b.TYPE_JSON;else{if(i.indexOf("text/plain")>
+-1)this.responseType=b.TYPE_TEXT}else if(YAHOO.lang.isArray(f))this.responseType=b.TYPE_JSARRAY;else if(f&&f.nodeType&&(f.nodeType===9||f.nodeType===1||f.nodeType===11))this.responseType=b.TYPE_XML;else if(f&&f.nodeName&&f.nodeName.toLowerCase()=="table")this.responseType=b.TYPE_HTMLTABLE;else if(YAHOO.lang.isObject(f))this.responseType=b.TYPE_JSON;else if(YAHOO.lang.isString(f))this.responseType=b.TYPE_TEXT;switch(this.responseType){case b.TYPE_JSARRAY:if(e&&f&&f.responseText)k=f.responseText;try{if(a.isString(k)){var l=
+[k].concat(this.parseJSONArgs);if(a.JSON)k=a.JSON.parse.apply(a.JSON,l);else if(window.JSON&&JSON.parse)k=JSON.parse.apply(JSON,l);else if(k.parseJSON)k=k.parseJSON.apply(k,l.slice(1));else{for(;k.length>0&&k.charAt(0)!="{"&&k.charAt(0)!="[";)k=k.substring(1,k.length);if(k.length>0)var q=Math.max(k.lastIndexOf("]"),k.lastIndexOf("}")),k=k.substring(0,q+1),k=eval("("+k+")")}}}catch(p){}k=this.doBeforeParseData(c,k,g);i=this.parseArrayData(c,k);break;case b.TYPE_JSON:if(e&&f&&f.responseText)k=f.responseText;
+try{if(a.isString(k)){l=[k].concat(this.parseJSONArgs);if(a.JSON)k=a.JSON.parse.apply(a.JSON,l);else if(window.JSON&&JSON.parse)k=JSON.parse.apply(JSON,l);else if(k.parseJSON)k=k.parseJSON.apply(k,l.slice(1));else{for(;k.length>0&&k.charAt(0)!="{"&&k.charAt(0)!="[";)k=k.substring(1,k.length);if(k.length>0)var n=Math.max(k.lastIndexOf("]"),k.lastIndexOf("}")),k=k.substring(0,n+1),k=eval("("+k+")")}}}catch(o){}k=this.doBeforeParseData(c,k,g);i=this.parseJSONData(c,k);break;case b.TYPE_HTMLTABLE:if(e&&
+f.responseText){e=document.createElement("div");e.innerHTML=f.responseText;k=e.getElementsByTagName("table")[0]}k=this.doBeforeParseData(c,k,g);i=this.parseHTMLTableData(c,k);break;case b.TYPE_XML:if(e&&f.responseXML)k=f.responseXML;k=this.doBeforeParseData(c,k,g);i=this.parseXMLData(c,k);break;case b.TYPE_TEXT:if(e&&a.isString(f.responseText))k=f.responseText;k=this.doBeforeParseData(c,k,g);i=this.parseTextData(c,k);break;default:k=this.doBeforeParseData(c,k,g);i=this.parseData(c,k)}i=i||{};if(!i.results)i.results=
+[];if(!i.meta)i.meta={};if(i.error){i.error=true;this.fireEvent("dataErrorEvent",{request:c,response:f,callback:g,caller:j,message:b.ERROR_DATANULL})}else{i=this.doBeforeCallback(c,k,i,g);this.fireEvent("responseParseEvent",{request:c,response:i,callback:g,caller:j});this.addToCache(c,i)}i.tId=h;b.issueCallback(g,[c,i],i.error,j)},doBeforeParseData:function(a,b){return b},doBeforeCallback:function(a,b,c){return c},parseData:function(b,c){return a.isValue(c)?{results:c,meta:{}}:null},parseArrayData:function(c,
+f){if(a.isArray(f)){var g=[],j,h,e,i,k;if(a.isArray(this.responseSchema.fields)){var l=this.responseSchema.fields;for(j=l.length-1;j>=0;--j)typeof l[j]!=="object"&&(l[j]={key:l[j]});var q={};for(j=l.length-1;j>=0;--j)(h=(typeof l[j].parser==="function"?l[j].parser:b.Parser[l[j].parser+""])||l[j].converter)&&(q[l[j].key]=h);var p=a.isArray(f[0]);for(j=f.length-1;j>-1;j--){var n={};e=f[j];if(typeof e==="object")for(h=l.length-1;h>-1;h--){i=l[h];k=p?e[h]:e[i.key];q[i.key]&&(k=q[i.key].call(this,k));
+k===void 0&&(k=null);n[i.key]=k}else if(a.isString(e))for(h=l.length-1;h>-1;h--){i=l[h];k=e;q[i.key]&&(k=q[i.key].call(this,k));k===void 0&&(k=null);n[i.key]=k}g[j]=n}}else g=f;return{results:g}}return null},parseTextData:function(c,f){if(a.isString(f)&&a.isString(this.responseSchema.recordDelim)&&a.isString(this.responseSchema.fieldDelim)){var g={results:[]},j=this.responseSchema.recordDelim,h=this.responseSchema.fieldDelim;if(f.length>0){var e=f.length-j.length;f.substr(e)==j&&(f=f.substr(0,e));
+if(f.length>0)for(var j=f.split(j),e=0,i=j.length,k=0;e<i;++e){var l=false,q=j[e];if(a.isString(q)&&q.length>0){var q=j[e].split(h),p={};if(a.isArray(this.responseSchema.fields))for(var n=this.responseSchema.fields,o=n.length-1;o>-1;o--)try{var m=q[o];if(a.isString(m)){m.charAt(0)=='"'&&(m=m.substr(1));m.charAt(m.length-1)=='"'&&(m=m.substr(0,m.length-1));var r=n[o],s=a.isValue(r.key)?r.key:r;if(!r.parser&&r.converter)r.parser=r.converter;var t=typeof r.parser==="function"?r.parser:b.Parser[r.parser+
+""];t&&(m=t.call(this,m));m===void 0&&(m=null);p[s]=m}else l=true}catch(u){l=true}else p=q;l||(g.results[k++]=p)}}}return g}return null},parseXMLResult:function(c){var f={},g=this.responseSchema;try{for(var j=g.fields.length-1;j>=0;j--){var h=g.fields[j],e=a.isValue(h.key)?h.key:h,i=null;if(this.useXPath)i=YAHOO.util.DataSource._getLocationValue(h,c);else{var k=c.attributes.getNamedItem(e);if(k)i=k.value;else{var l=c.getElementsByTagName(e);if(l&&l.item(0)){var q=l.item(0),i=q?q.text?q.text:q.textContent?
+q.textContent:null:null;if(!i){for(var p=[],n=0,o=q.childNodes.length;n<o;n++)if(q.childNodes[n].nodeValue)p[p.length]=q.childNodes[n].nodeValue;p.length>0&&(i=p.join(""))}}}}i===null&&(i="");if(!h.parser&&h.converter)h.parser=h.converter;var m=typeof h.parser==="function"?h.parser:b.Parser[h.parser+""];m&&(i=m.call(this,i));i===void 0&&(i=null);f[e]=i}}catch(r){}return f},parseXMLData:function(b,c){var g=false,j=this.responseSchema,h={meta:{}},e=null,i=j.metaNode,k=j.metaFields||{},l,q,p;try{if(this.useXPath)for(l in k)h.meta[l]=
+YAHOO.util.DataSource._getLocationValue(k[l],c);else if(i=i?c.getElementsByTagName(i)[0]:c)for(l in k)if(a.hasOwnProperty(k,l)){q=k[l];if(p=i.getElementsByTagName(q)[0])p=p.firstChild.nodeValue;else if(p=i.attributes.getNamedItem(q))p=p.value;a.isValue(p)&&(h.meta[l]=p)}e=j.resultNode?c.getElementsByTagName(j.resultNode):null}catch(n){}if(!e||!a.isArray(j.fields))g=true;else{h.results=[];for(j=e.length-1;j>=0;--j){i=this.parseXMLResult(e.item(j));h.results[j]=i}}if(g)h.error=true;return h},parseJSONData:function(c,
+f){var g={results:[],meta:{}};if(a.isObject(f)&&this.responseSchema.resultsList){var j=this.responseSchema,h=j.fields,e=f,i=[],k=j.metaFields||{},l=[],q=[],p=[],n=false,o,m,r,s=function(a){var e=null,b=[],i=0;if(a){a=a.replace(/\[(['"])(.*?)\1\]/g,function(a,e,c){b[i]=c;return".@"+i++}).replace(/\[(\d+)\]/g,function(a,e){b[i]=parseInt(e,10)|0;return".@"+i++}).replace(/^\./,"");if(!/[^\w\.\$@]/.test(a)){e=a.split(".");for(i=e.length-1;i>=0;--i)e[i].charAt(0)==="@"&&(e[i]=b[parseInt(e[i].substr(1),
+10)])}}return e},t=function(a,e){for(var b=e,i=0,c=a.length;i<c&&b;++i)b=b[a[i]];return b};if(r=s(j.resultsList)){e=t(r,f);e===void 0&&(n=true)}else n=true;e||(e=[]);a.isArray(e)||(e=[e]);if(n)g.error=true;else{if(j.fields){j=0;for(n=h.length;j<n;j++){r=h[j];o=r.key||r;m=(typeof r.parser==="function"?r.parser:b.Parser[r.parser+""])||r.converter;r=s(o);m&&(l[l.length]={key:o,parser:m});r&&(r.length>1?q[q.length]={key:o,path:r}:p[p.length]={key:o,path:r[0]})}for(j=e.length-1;j>=0;--j){n=e[j];r={};if(n){for(h=
+p.length-1;h>=0;--h)r[p[h].key]=n[p[h].path]!==void 0?n[p[h].path]:n[h];for(h=q.length-1;h>=0;--h)r[q[h].key]=t(q[h].path,n);for(h=l.length-1;h>=0;--h){n=l[h].key;r[n]=l[h].parser.call(this,r[n]);r[n]===void 0&&(r[n]=null)}}i[j]=r}}else i=e;for(o in k)if(a.hasOwnProperty(k,o))if(r=s(k[o])){e=t(r,f);g.meta[o]=e}}g.results=i}else g.error=true;return g},parseHTMLTableData:function(c,f){var g=false,j=this.responseSchema.fields,h={results:[]};if(a.isArray(j))for(var e=0;e<f.tBodies.length;e++)for(var i=
+f.tBodies[e],k=i.rows.length-1;k>-1;k--){for(var l=i.rows[k],q={},p=j.length-1;p>-1;p--){var n=j[p],o=a.isValue(n.key)?n.key:n,m=l.cells[p].innerHTML;if(!n.parser&&n.converter)n.parser=n.converter;(n=typeof n.parser==="function"?n.parser:b.Parser[n.parser+""])&&(m=n.call(this,m));m===void 0&&(m=null);q[o]=m}h.results[k]=q}else g=true;if(g)h.error=true;return h}};a.augmentProto(b,c.EventProvider);c.LocalDataSource=function(a,f){this.dataType=b.TYPE_LOCAL;if(a)if(YAHOO.lang.isArray(a))this.responseType=
+b.TYPE_JSARRAY;else if(a.nodeType&&a.nodeType==9)this.responseType=b.TYPE_XML;else if(a.nodeName&&a.nodeName.toLowerCase()=="table"){this.responseType=b.TYPE_HTMLTABLE;a=a.cloneNode(true)}else if(YAHOO.lang.isString(a))this.responseType=b.TYPE_TEXT;else{if(YAHOO.lang.isObject(a))this.responseType=b.TYPE_JSON}else{a=[];this.responseType=b.TYPE_JSARRAY}c.LocalDataSource.superclass.constructor.call(this,a,f)};a.extend(c.LocalDataSource,b);a.augmentObject(c.LocalDataSource,b);c.FunctionDataSource=function(a,
+f){this.dataType=b.TYPE_JSFUNCTION;c.FunctionDataSource.superclass.constructor.call(this,a||function(){},f)};a.extend(c.FunctionDataSource,b,{scope:null,makeConnection:function(a,c,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",{tId:j,request:a,callback:c,caller:g});var h=this.scope?this.liveData.call(this.scope,a,this,c):this.liveData(a,c);if(this.responseType===b.TYPE_UNKNOWN)if(YAHOO.lang.isArray(h))this.responseType=b.TYPE_JSARRAY;else if(h&&h.nodeType&&h.nodeType==9)this.responseType=
+b.TYPE_XML;else if(h&&h.nodeName&&h.nodeName.toLowerCase()=="table")this.responseType=b.TYPE_HTMLTABLE;else if(YAHOO.lang.isObject(h))this.responseType=b.TYPE_JSON;else if(YAHOO.lang.isString(h))this.responseType=b.TYPE_TEXT;this.handleResponse(a,h,c,g,j);return j}});a.augmentObject(c.FunctionDataSource,b);c.ScriptNodeDataSource=function(a,f){this.dataType=b.TYPE_SCRIPTNODE;c.ScriptNodeDataSource.superclass.constructor.call(this,a||"",f)};a.extend(c.ScriptNodeDataSource,b,{getUtility:c.Get,asyncMode:"allowAll",
+scriptCallbackParam:"callback",generateRequestCallback:function(a){return"&"+this.scriptCallbackParam+"=YAHOO.util.ScriptNodeDataSource.callbacks["+a+"]"},doBeforeGetScriptNode:function(a){return a},makeConnection:function(a,f,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",{tId:j,request:a,callback:f,caller:g});if(c.ScriptNodeDataSource._nPending===0){c.ScriptNodeDataSource.callbacks=[];c.ScriptNodeDataSource._nId=0}var h=c.ScriptNodeDataSource._nId;c.ScriptNodeDataSource._nId++;var e=
+this;c.ScriptNodeDataSource.callbacks[h]=function(i){if(e.asyncMode!=="ignoreStaleResponses"||h===c.ScriptNodeDataSource.callbacks.length-1){if(e.responseType===b.TYPE_UNKNOWN)if(YAHOO.lang.isArray(i))e.responseType=b.TYPE_JSARRAY;else if(i.nodeType&&i.nodeType==9)e.responseType=b.TYPE_XML;else if(i.nodeName&&i.nodeName.toLowerCase()=="table")e.responseType=b.TYPE_HTMLTABLE;else if(YAHOO.lang.isObject(i))e.responseType=b.TYPE_JSON;else if(YAHOO.lang.isString(i))e.responseType=b.TYPE_TEXT;e.handleResponse(a,
+i,f,g,j)}delete c.ScriptNodeDataSource.callbacks[h]};c.ScriptNodeDataSource._nPending++;var i=this.liveData+a+this.generateRequestCallback(h),i=this.doBeforeGetScriptNode(i);this.getUtility.script(i,{autopurge:true,onsuccess:c.ScriptNodeDataSource._bumpPendingDown,onfail:c.ScriptNodeDataSource._bumpPendingDown});return j}});a.augmentObject(c.ScriptNodeDataSource,b);a.augmentObject(c.ScriptNodeDataSource,{_nId:0,_nPending:0,callbacks:[]});c.XHRDataSource=function(a,f){this.dataType=b.TYPE_XHR;this.connMgr=
+this.connMgr||c.Connect;c.XHRDataSource.superclass.constructor.call(this,a||"",f)};a.extend(c.XHRDataSource,b,{connMgr:null,connXhrMode:"allowAll",connMethodPost:false,connTimeout:0,makeConnection:function(c,f,g){var j=b._nTransactionId++;this.fireEvent("requestEvent",{tId:j,request:c,callback:f,caller:g});var h=this.connMgr,e=this._oQueue,i={success:function(a){if(a&&this.connXhrMode=="ignoreStaleResponses"&&a.tId!=e.conn.tId)return null;if(a){if(this.responseType===b.TYPE_UNKNOWN){var i=a.getResponseHeader?
+a.getResponseHeader["Content-Type"]:null;if(i)if(i.indexOf("text/xml")>-1)this.responseType=b.TYPE_XML;else if(i.indexOf("application/json")>-1)this.responseType=b.TYPE_JSON;else if(i.indexOf("text/plain")>-1)this.responseType=b.TYPE_TEXT}this.handleResponse(c,a,f,g,j)}else{this.fireEvent("dataErrorEvent",{request:c,response:null,callback:f,caller:g,message:b.ERROR_DATANULL});b.issueCallback(f,[c,{error:true}],true,g);return null}},failure:function(e){this.fireEvent("dataErrorEvent",{request:c,response:e,
+callback:f,caller:g,message:b.ERROR_DATAINVALID});a.isString(this.liveData)&&a.isString(c)&&this.liveData.lastIndexOf("?")!==this.liveData.length-1&&c.indexOf("?");e=e||{};e.error=true;b.issueCallback(f,[c,e],true,g);return null},scope:this};if(a.isNumber(this.connTimeout))i.timeout=this.connTimeout;if(this.connXhrMode=="cancelStaleRequests"&&e.conn&&h.abort){h.abort(e.conn);e.conn=null}if(h&&h.asyncRequest){var k=this.liveData,l=this.connMethodPost,q=l?"POST":"GET",p=l||!a.isValue(c)?k:k+c,n=l?c:
+null;if(this.connXhrMode!="queueRequests")e.conn=h.asyncRequest(q,p,i,n);else if(e.conn){var o=e.requests;o.push({request:c,callback:i});if(!e.interval)e.interval=setInterval(function(){if(!h.isCallInProgress(e.conn))if(o.length>0){p=l||!a.isValue(o[0].request)?k:k+o[0].request;n=l?o[0].request:null;e.conn=h.asyncRequest(q,p,o[0].callback,n);o.shift()}else{clearInterval(e.interval);e.interval=null}},50)}else e.conn=h.asyncRequest(q,p,i,n)}else b.issueCallback(f,[c,{error:true}],true,g);return j}});
+a.augmentObject(c.XHRDataSource,b);c.DataSource=function(a,f){var f=f||{},g=f.dataType;if(g){if(g==b.TYPE_LOCAL)return new c.LocalDataSource(a,f);if(g==b.TYPE_XHR)return new c.XHRDataSource(a,f);if(g==b.TYPE_SCRIPTNODE)return new c.ScriptNodeDataSource(a,f);if(g==b.TYPE_JSFUNCTION)return new c.FunctionDataSource(a,f)}return YAHOO.lang.isString(a)?new c.XHRDataSource(a,f):YAHOO.lang.isFunction(a)?new c.FunctionDataSource(a,f):new c.LocalDataSource(a,f)};a.augmentObject(c.DataSource,b)})();
+YAHOO.util.Number={format:function(a,c){if(a===""||a===null||!isFinite(a))return"";var a=+a,c=YAHOO.lang.merge(YAHOO.util.Number.format.defaults,c||{}),b=Math.abs(a),d=c.decimalPlaces||0,f=c.thousandsSeparator,g=c.negativeFormat||"-"+c.format,j;g.indexOf("#")>-1&&(g=g.replace(/#/,c.format));if(d<0){j=b-b%1+"";d=j.length+d;j=d>0?Number("."+j).toFixed(d).slice(2)+Array(j.length-d+1).join("0"):"0"}else if(d>0||(b+"").indexOf(".")>0){j=Math.pow(10,d);j=Math.round(b*j)/j+"";var h=j.indexOf(".");if(h<0){h=
+(Math.pow(10,d)+"").substring(1);d>0&&(j=j+"."+h)}else{d=d-(j.length-h-1);h=(Math.pow(10,d)+"").substring(1);j=j+h}}else j=b.toFixed(d)+"";j=j.split(/\D/);if(b>=1E3){d=j[0].length%3||3;j[0]=j[0].slice(0,d)+j[0].slice(d).replace(/(\d{3})/g,f+"$1")}return YAHOO.util.Number.format._applyFormat(a<0?g:c.format,j.join(c.decimalSeparator),c)}};YAHOO.util.Number.format.defaults={format:"{prefix}{number}{suffix}",negativeFormat:null,decimalSeparator:".",decimalPlaces:null,thousandsSeparator:""};
+YAHOO.util.Number.format._applyFormat=function(a,c,b){return a.replace(/\{(\w+)\}/g,function(a,f){return f==="number"?c:f in b?b[f]:""})};
+(function(){var a=function(a,c,f){for(typeof f==="undefined"&&(f=10);parseInt(a,10)<f&&f>1;f=f/10)a=c.toString()+a;return a.toString()},c={formats:{a:function(a,c){return c.a[a.getDay()]},A:function(a,c){return c.A[a.getDay()]},b:function(a,c){return c.b[a.getMonth()]},B:function(a,c){return c.B[a.getMonth()]},C:function(b){return a(parseInt(b.getFullYear()/100,10),0)},d:["getDate","0"],e:["getDate"," "],g:function(b){return a(parseInt(c.formats.G(b)%100,10),0)},G:function(a){var d=a.getFullYear(),
+f=parseInt(c.formats.V(a),10),a=parseInt(c.formats.W(a),10);a>f?d++:a===0&&f>=52&&d--;return d},H:["getHours","0"],I:function(b){b=b.getHours()%12;return a(b===0?12:b,0)},j:function(b){var c=new Date(""+b.getFullYear()+"/1/1 GMT"),b=new Date(""+b.getFullYear()+"/"+(b.getMonth()+1)+"/"+b.getDate()+" GMT")-c,b=parseInt(b/6E4/60/24,10)+1;return a(b,0,100)},k:["getHours"," "],l:function(b){b=b.getHours()%12;return a(b===0?12:b," ")},m:function(b){return a(b.getMonth()+1,0)},M:["getMinutes","0"],p:function(a,
+c){return c.p[a.getHours()>=12?1:0]},P:function(a,c){return c.P[a.getHours()>=12?1:0]},s:function(a){return parseInt(a.getTime()/1E3,10)},S:["getSeconds","0"],u:function(a){a=a.getDay();return a===0?7:a},U:function(b){var d=parseInt(c.formats.j(b),10),b=6-b.getDay(),d=parseInt((d+b)/7,10);return a(d,0)},V:function(b){var d=parseInt(c.formats.W(b),10),f=(new Date(""+b.getFullYear()+"/1/1")).getDay(),d=d+(f>4||f<=1?0:1);d===53&&(new Date(""+b.getFullYear()+"/12/31")).getDay()<4?d=1:d===0&&(d=c.formats.V(new Date(""+
+(b.getFullYear()-1)+"/12/31")));return a(d,0)},w:"getDay",W:function(b){var d=parseInt(c.formats.j(b),10),b=7-c.formats.u(b),d=parseInt((d+b)/7,10);return a(d,0,10)},y:function(b){return a(b.getFullYear()%100,0)},Y:"getFullYear",z:function(b){var b=b.getTimezoneOffset(),c=a(parseInt(Math.abs(b/60),10),0),f=a(Math.abs(b%60),0);return(b>0?"-":"+")+c+f},Z:function(a){var d=a.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");d.length>4&&(d=c.formats.z(a));
+return d},"%":function(){return"%"}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(b,d,f){d=d||{};if(!(b instanceof Date))return YAHOO.lang.isValue(b)?b:"";d=d.format||"%m/%d/%Y";d==="YYYY/MM/DD"?d="%Y/%m/%d":d==="DD/MM/YYYY"?d="%d/%m/%Y":d==="MM/DD/YYYY"&&(d="%m/%d/%Y");f=f||"en";f in YAHOO.util.DateLocale||(f=f.replace(/-[a-zA-Z]+$/,"")in YAHOO.util.DateLocale?f.replace(/-[a-zA-Z]+$/,""):"en");for(var g=
+YAHOO.util.DateLocale[f],f=function(a,e){var b=c.aggregates[e];return b==="locale"?g[e]:b},j=function(d,e){var i=c.formats[e];return typeof i==="string"?b[i]():typeof i==="function"?i.call(b,b,g):typeof i==="object"&&typeof i[0]==="string"?a(b[i[0]](),i[1]):e};d.match(/%[cDFhnrRtTxX]/);)d=d.replace(/%([cDFhnrRtTxX])/g,f);d=d.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,j);f=j=void 0;return d}};YAHOO.namespace("YAHOO.util");YAHOO.util.Date=c;YAHOO.util.DateLocale={a:["Sun","Mon","Tue","Wed","Thu",
+"Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};YAHOO.util.DateLocale.en=YAHOO.lang.merge(YAHOO.util.DateLocale,{});YAHOO.util.DateLocale["en-US"]=YAHOO.lang.merge(YAHOO.util.DateLocale.en,
+{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});YAHOO.util.DateLocale["en-GB"]=YAHOO.lang.merge(YAHOO.util.DateLocale.en,{r:"%l:%M:%S %P %Z"});YAHOO.util.DateLocale["en-AU"]=YAHOO.lang.merge(YAHOO.util.DateLocale.en)})();YAHOO.register("datasource",YAHOO.util.DataSource,{version:"2.9.0",build:"2800"});YAHOO.util.Chain=function(){this.q=[].slice.call(arguments);this.createEvent("end")};
+YAHOO.util.Chain.prototype={id:0,run:function(){var a=this.q[0],c;if(a){if(this.id)return this}else{this.fireEvent("end");return this}c=a.method||a;if(typeof c==="function"){var b=a.scope||{},d=a.argument||[],f=a.timeout||0,g=this;d instanceof Array||(d=[d]);if(f<0){this.id=f;if(a.until)for(;!a.until();)c.apply(b,d);else if(a.iterations)for(;a.iterations-- >0;)c.apply(b,d);else c.apply(b,d);this.q.shift();this.id=0;return this.run()}if(a.until){if(a.until()){this.q.shift();return this.run()}}else(!a.iterations||
+!--a.iterations)&&this.q.shift();this.id=setTimeout(function(){c.apply(b,d);if(g.id){g.id=0;g.run()}},f)}return this},add:function(a){this.q.push(a);return this},pause:function(){this.id>0&&clearTimeout(this.id);this.id=0;return this},stop:function(){this.pause();this.q=[];return this}};YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);
+(function(){var a=YAHOO.util.Event,c=YAHOO.lang,b=[],d=function(a,b,c){return!a||a===c?false:YAHOO.util.Selector.test(a,b)?a:d(a.parentNode,b,c)};c.augmentObject(a,{_createDelegate:function(b,g,j,h){return function(e){var i=a.getTarget(e),k=g,l=this.nodeType===9,q;if(c.isFunction(g))q=g(i);else if(c.isString(g)){if(!l){(k=this.id)||(k=a.generateId(this));k="#"+k+" ";k=(k+g).replace(/,/gi,","+k)}YAHOO.util.Selector.test(i,k)?q=i:YAHOO.util.Selector.test(i,k.replace(/,/gi," *,")+" *")&&(q=d(i,k,this))}if(q){i=
+q;h&&(i=h===true?j:h);return b.call(i,e,q,this,j)}}},delegate:function(d,g,j,h,e,i){var k=g,l;if(c.isString(h)&&!YAHOO.util.Selector)return false;if(g=="mouseenter"||g=="mouseleave"){if(!a._createMouseDelegate)return false;k=a._getType(g);l=a._createMouseDelegate(j,e,i);g=a._createDelegate(function(a,e,b){return l.call(e,a,b)},h,e,i)}else g=a._createDelegate(j,h,e,i);b.push([d,k,j,g]);return a.on(d,k,g)},removeDelegate:function(c,d,j){var h=d,e=false,i;if(d=="mouseenter"||d=="mouseleave")h=a._getType(d);
+d=a._getCacheIndex(b,c,h,j);d>=0&&(i=b[d]);if(c&&i)if(e=a.removeListener(i[0],i[1],i[3])){delete b[d][2];delete b[d][3];b.splice(d,1)}return e}})})();
+(function(){var a=YAHOO.util.Event,c=YAHOO.lang,b=a.addListener,d=a.removeListener,f=a.getListeners,g=[],j={mouseenter:"mouseover",mouseleave:"mouseout"},h=function(e,b,c){var c=a._getCacheIndex(g,e,b,c),f,h;c>=0&&(f=g[c]);if(e&&f)if(h=d.call(a,f[0],b,f[3])){delete g[c][2];delete g[c][3];g.splice(c,1)}return h};c.augmentObject(a._specialTypes,j);c.augmentObject(a,{_createMouseDelegate:function(e,b,c){return function(d,f){var g=a.getRelatedTarget(d),h;if(this!=g&&!YAHOO.util.Dom.isAncestor(this,g)){g=
+this;c&&(g=c===true?b:c);h=[d,b];f&&h.splice(1,0,this,f);return e.apply(g,h)}}},addListener:function(e,c,d,f,h){var p;if(j[c]){p=a._createMouseDelegate(d,f,h);p.mouseDelegate=true;g.push([e,c,d,p]);p=b.call(a,e,c,p)}else p=b.apply(a,arguments);return p},removeListener:function(e,b,c){return j[b]?h.apply(a,arguments):d.apply(a,arguments)},getListeners:function(e,b){var c=[],d,g=b==="mouseover"||b==="mouseout",h,n,o;if(b&&(g||j[b])){if(d=f.call(a,e,this._getType(b)))for(n=d.length-1;n>-1;n--){o=d[n];
+h=o.fn.mouseDelegate;(j[b]&&h||g&&!h)&&c.push(o)}}else c=f.apply(a,arguments);return c&&c.length?c:null}},true);a.on=a.addListener})();YAHOO.register("event-mouseenter",YAHOO.util.Event,{version:"2.9.0",build:"2800"});Y=YAHOO;Y_DOM=YAHOO.util.Dom;EMPTY_ARRAY=[];Y_UA=Y.env.ua;Y_Lang=Y.lang;Y_DOC=document;Y_DOCUMENT_ELEMENT=Y_DOC.documentElement;Y_DOM_inDoc=Y_DOM.inDocument;Y_mix=Y_Lang.augmentObject;Y_guid=Y_DOM.generateId;
+Y_getDoc=function(a){var c=Y_DOC;a&&(c=a.nodeType===9?a:a.ownerDocument||a.document||Y_DOC);return c};Y_Array=function(a,c){var b,d,f=c||0;try{return Array.prototype.slice.call(a,f)}catch(g){d=[];for(b=a.length;f<b;f++)d.push(a[f]);return d}};
+Y_DOM_allById=function(a,c){var c=c||Y_DOC,b=[],d=[],f,g;if(c.querySelectorAll)d=c.querySelectorAll('[id="'+a+'"]');else if(c.all){if(b=c.all(a)){if(b.nodeName)if(b.id===a){d.push(b);b=EMPTY_ARRAY}else b=[b];if(b.length)for(f=0;g=b[f++];)(g.id===a||g.attributes&&g.attributes.id&&g.attributes.id.value===a)&&d.push(g)}}else d=[Y_getDoc(c).getElementById(a)];return d};COMPARE_DOCUMENT_POSITION="compareDocumentPosition";OWNER_DOCUMENT="ownerDocument";
+Selector={_foundCache:[],useNative:!0,_compare:"sourceIndex"in Y_DOCUMENT_ELEMENT?function(a,c){var b=a.sourceIndex,d=c.sourceIndex;return b===d?0:b>d?1:-1}:Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION]?function(a,c){return a[COMPARE_DOCUMENT_POSITION](c)&4?-1:1}:function(a,c){var b,d;if(a&&c){b=a[OWNER_DOCUMENT].createRange();b.setStart(a,0);d=c[OWNER_DOCUMENT].createRange();d.setStart(c,0);b=b.compareBoundaryPoints(1,d)}return b},_sort:function(a){if(a){a=Y_Array(a,0,true);a.sort&&a.sort(Selector._compare)}return a},
+_deDupe:function(a){var c=[],b,d;for(b=0;d=a[b++];)if(!d._found){c[c.length]=d;d._found=true}for(b=0;d=c[b++];){d._found=null;d.removeAttribute("_found")}return c},query:function(a,c,b,d){if(typeof c=="string"){c=Y_DOM.get(c);if(!c)return b?null:[]}else c=c||Y_DOC;var f=[],g=Selector.useNative&&Y_DOC.querySelector&&!d,j=[[a,c]],h=g?Selector._nativeQuery:Selector._bruteQuery;if(a&&h){if(!d&&(!g||c.tagName))j=Selector._splitQueries(a,c);for(c=0;d=j[c++];){d=h(d[0],d[1],b);b||(d=Y_Array(d,0,true));d&&
+(f=f.concat(d))}j.length>1&&(f=Selector._sort(Selector._deDupe(f)))}Y.log("query: "+a+" returning: "+f.length,"info","Selector");return b?f[0]||null:f},_splitQueries:function(a,c){var b=a.split(","),d=[],f="",g,j;if(c){if(c.tagName){c.id=c.id||Y_guid();f='[id="'+c.id+'"] '}g=0;for(j=b.length;g<j;++g){a=f+b[g];d.push([a,c])}}return d},_nativeQuery:function(a,c,b){if(Y_UA.webkit&&a.indexOf(":checked")>-1&&Selector.pseudos&&Selector.pseudos.checked)return Selector.query(a,c,b,true);try{return c["querySelector"+
+(b?"":"All")](a)}catch(d){return Selector.query(a,c,b,true)}},filter:function(a,c){var b=[],d,f;if(a&&c)for(d=0;f=a[d++];)Selector.test(f,c)&&(b[b.length]=f);else Y.log("invalid filter input (nodes: "+a+", selector: "+c+")","warn","Selector");return b},test:function(a,c,b){var d=false,c=c.split(","),f=false,g,j,h,e,i;if(a&&a.tagName){if(!b&&!Y_DOM_inDoc(a)){b=a.parentNode;if(!b){h=a[OWNER_DOCUMENT].createDocumentFragment();h.appendChild(a);b=h;f=true}}b=b||a[OWNER_DOCUMENT];if(!a.id)a.id=Y_guid();
+for(e=0;g=c[e++];){g=g+('[id="'+a.id+'"]');j=Selector.query(g,b);for(i=0;g=j[i++];)if(g===a){d=true;break}if(d)break}f&&h.removeChild(a)}return d}};YAHOO.util.Selector=Selector;PARENT_NODE="parentNode";TAG_NAME="tagName";ATTRIBUTES="attributes";COMBINATOR="combinator";PSEUDOS="pseudos";
+SelectorCSS2={_reRegExpTokens:/([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,SORT_RESULTS:!0,_children:function(a,c){var b=a.children,d,f,g;if(a.children&&c&&a.children.tags)a.children.tags(c);else if(!b&&a[TAG_NAME]||b&&c){f=b||a.childNodes;b=[];for(d=0;g=f[d++];)g.tagName&&(!c||c===g.tagName)&&b.push(g)}return b||[]},_re:{attr:/(\[[^\]]*\])/g,esc:/\\[:\[\]\(\)#\.\'\>+~"]/gi,pseudos:/(\([^\)]*\))/g},shorthand:{"\\#(-?[_a-z]+[-\\w\\uE000]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w\\uE000]*)":"[className~=$1]"},operators:{"":function(a,
+c){return!!a.getAttribute(c)},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}(?:-|$)"},pseudos:{"first-child":function(a){return Selector._children(a[PARENT_NODE])[0]===a}},_bruteQuery:function(a,c,b){var d=[],f=[],a=Selector._tokenize(a),g=a[a.length-1];Y_getDoc(c);var j,h;if(g){j=g.id;h=g.className;g=g.tagName||"*";if(c.getElementsByTagName)f=j&&(c.all||c.nodeType===9||Y_DOM_inDoc(c))?Y_DOM_allById(j,c):h?c.getElementsByClassName(h):c.getElementsByTagName(g);else for(c=c.firstChild;c;){c.tagName&&
+f.push(c);c=c.nextSilbing||c.firstChild}f.length&&(d=Selector._filterNodes(f,a,b))}return d},_filterNodes:function(a,c,b){for(var d=0,f,g=c.length,j=g-1,h=[],e=a[0],i=e,k=Selector.getters,l,q,p,n,o,m,d=0;i=e=a[d++];){j=g-1;p=null;a:for(;i&&i.tagName;){q=c[j];o=q.tests;if(f=o.length)for(;m=o[--f];){l=m[1];if(k[m[0]])n=k[m[0]](i,m[0]);else{n=i[m[0]];n===void 0&&i.getAttribute&&(n=i.getAttribute(m[0]))}if(l==="="&&n!==m[2]||typeof l!=="string"&&l.test&&!l.test(n)||!l.test&&typeof l==="function"&&!l(i,
+m[0],m[2])){if(i=i[p])for(;i&&(!i.tagName||q.tagName&&q.tagName!==i.tagName);)i=i[p];continue a}}j--;if(f=q.combinator){p=f.axis;for(i=i[p];i&&!i.tagName;)i=i[p];f.direct&&(p=null)}else{h.push(e);if(b)return h;break}}}return h},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:!0},"+":{axis:"previousSibling",direct:!0}},_parsers:[{name:ATTRIBUTES,re:/^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,fn:function(a,c){var b=a[2]||"",d=Selector.operators,f=
+a[3]?a[3].replace(/\\/g,""):"";if(a[1]==="id"&&b==="="||a[1]==="className"&&Y_DOCUMENT_ELEMENT.getElementsByClassName&&(b==="~="||b==="=")){c.prefilter=a[1];a[3]=f;c[a[1]]=a[1]==="id"?a[3]:f}if(b in d){b=d[b];if(typeof b==="string"){a[3]=f.replace(Selector._reRegExpTokens,"\\$1");b=RegExp(b.replace("{val}",a[3]))}a[2]=b}if(!c.last||c.prefilter!==a[1])return a.slice(1)}},{name:TAG_NAME,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(a,c){var b=a[1].toUpperCase();c.tagName=b;if(b!=="*"&&(!c.last||c.prefilter))return[TAG_NAME,
+"=",b];if(!c.prefilter)c.prefilter="tagName"}},{name:COMBINATOR,re:/^\s*([>+~]|\s)\s*/,fn:function(){}},{name:PSEUDOS,re:/^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,fn:function(a){var c=Selector[PSEUDOS][a[1]];if(c){a[2]&&(a[2]=a[2].replace(/\\/g,""));return[a[2],c]}return false}}],_getToken:function(){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]}},_tokenize:function(a){var a=Selector._replaceShorthand(Y_Lang.trim(a||"")),c=Selector._getToken(),b=a,d=
+[],f=false,g,j,h;a:do{f=false;for(j=0;h=Selector._parsers[j++];)if(g=h.re.exec(a)){if(h.name!==COMBINATOR)c.selector=a;a=a.replace(g[0],"");if(!a.length)c.last=true;Selector._attrFilters[g[1]]&&(g[1]=Selector._attrFilters[g[1]]);f=h.fn(g,c);if(f===false){f=false;break a}else f&&c.tests.push(f);if(!a.length||h.name===COMBINATOR){d.push(c);c=Selector._getToken(c);if(h.name===COMBINATOR)c.combinator=Selector.combinators[g[1]]}f=true}}while(f&&a.length);if(!f||a.length){Y.log("query: "+b+" contains unsupported token in: "+
+a,"warn","Selector");d=[]}return d},_replaceShorthand:function(a){var c=Selector.shorthand,b=a.match(Selector._re.esc),d,f,g;b&&(a=a.replace(Selector._re.esc,"\ue000"));d=a.match(Selector._re.attr);f=a.match(Selector._re.pseudos);d&&(a=a.replace(Selector._re.attr,"\ue001"));f&&(a=a.replace(Selector._re.pseudos,"\ue002"));for(g in c)c.hasOwnProperty(g)&&(a=a.replace(RegExp(g,"gi"),c[g]));if(d){c=0;for(g=d.length;c<g;++c)a=a.replace(/\uE001/,d[c])}if(f){c=0;for(g=f.length;c<g;++c)a=a.replace(/\uE002/,
+f[c])}a=a.replace(/\[/g,"\ue003");a=a.replace(/\]/g,"\ue004");a=a.replace(/\(/g,"\ue005");a=a.replace(/\)/g,"\ue006");if(b){c=0;for(g=b.length;c<g;++c)a=a.replace("\ue000",b[c])}return a},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(a,c){return Y_DOM.getAttribute(a,c)}}};Y_mix(Selector,SelectorCSS2,!0);Selector.getters.src=Selector.getters.rel=Selector.getters.href;Selector.useNative&&Y_DOC.querySelector&&(Selector.shorthand["\\.([^\\s\\\\(\\[:]*)"]="[class~=$1]");
+Selector._reNth=/^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
+Selector._getNth=function(a,c,b,d){Selector._reNth.test(c);var c=parseInt(RegExp.$1,10),f=RegExp.$2,g=RegExp.$3,j=parseInt(RegExp.$4,10)||0,b=Selector._children(a.parentNode,b);if(g){c=2;j=g==="odd"?1:0}else isNaN(c)&&(c=f?1:0);if(c===0){d&&(j=b.length-j+1);return b[j-1]===a?true:false}if(c<0){d=!!d;c=Math.abs(c)}if(d){d=b.length-j;for(f=b.length;d>=0;d=d-c)if(d<f&&b[d]===a)return true}else{d=j-1;for(f=b.length;d<f;d=d+c)if(d>=0&&b[d]===a)return true}return false};
+Y_mix(Selector.pseudos,{root:function(a){return a===a.ownerDocument.documentElement},"nth-child":function(a,c){return Selector._getNth(a,c)},"nth-last-child":function(a,c){return Selector._getNth(a,c,null,true)},"nth-of-type":function(a,c){return Selector._getNth(a,c,a.tagName)},"nth-last-of-type":function(a,c){return Selector._getNth(a,c,a.tagName,true)},"last-child":function(a){var c=Selector._children(a.parentNode);return c[c.length-1]===a},"first-of-type":function(a){return Selector._children(a.parentNode,
+a.tagName)[0]===a},"last-of-type":function(a){var c=Selector._children(a.parentNode,a.tagName);return c[c.length-1]===a},"only-child":function(a){var c=Selector._children(a.parentNode);return c.length===1&&c[0]===a},"only-of-type":function(a){var c=Selector._children(a.parentNode,a.tagName);return c.length===1&&c[0]===a},empty:function(a){return a.childNodes.length===0},not:function(a,c){return!Selector.test(a,c)},contains:function(a,c){return(a.innerText||a.textContent||"").indexOf(c)>-1},checked:function(a){return a.checked===
+true||a.selected===true},enabled:function(a){return a.disabled!==void 0&&!a.disabled},disabled:function(a){return a.disabled}});Y_mix(Selector.operators,{"^=":"^{val}","!=":function(a,c,b){return a[c]!==b},"$=":"{val}$","*=":"{val}"});Selector.combinators["~"]={axis:"previousSibling"};YAHOO.register("selector",YAHOO.util.Selector,{version:"2.9.0",build:"2800"});var Dom=YAHOO.util.Dom;
+YAHOO.widget.ColumnSet=function(a){this._sId=Dom.generateId(null,"yui-cs");a=YAHOO.widget.DataTable._cloneObject(a);this._init(a);YAHOO.widget.ColumnSet._nCount++};YAHOO.widget.ColumnSet._nCount=0;
+YAHOO.widget.ColumnSet.prototype={_sId:null,_aDefinitions:null,tree:null,flat:null,keys:null,headers:null,_init:function(a){var c=[],b=[],d=[],f=[],g=-1,j=function(a,i){g++;c[g]||(c[g]=[]);for(var k=0;k<a.length;k++){var f=a[k],h=new YAHOO.widget.Column(f);f.yuiColumnId=h._sId;b.push(h);if(i)h._oParent=i;if(YAHOO.lang.isArray(f.children)){h.children=f.children;var p=0,n=function(a){for(var a=a.children,e=0;e<a.length;e++)YAHOO.lang.isArray(a[e].children)?n(a[e]):p++};n(f);h._nColspan=p;for(var f=
+f.children,o=0;o<f.length;o++){var m=f[o];if(h.className&&m.className===void 0)m.className=h.className;if(h.editor&&m.editor===void 0)m.editor=h.editor;if(h.editorOptions&&m.editorOptions===void 0)m.editorOptions=h.editorOptions;if(h.formatter&&m.formatter===void 0)m.formatter=h.formatter;if(h.resizeable&&m.resizeable===void 0)m.resizeable=h.resizeable;if(h.sortable&&m.sortable===void 0)m.sortable=h.sortable;if(h.hidden)m.hidden=true;if(h.width&&m.width===void 0)m.width=h.width;if(h.minWidth&&m.minWidth===
+void 0)m.minWidth=h.minWidth;if(h.maxAutoWidth&&m.maxAutoWidth===void 0)m.maxAutoWidth=h.maxAutoWidth;if(h.type&&m.type===void 0)m.type=h.type;if(h.type&&!h.formatter)h.formatter=h.type;if(h.text&&!YAHOO.lang.isValue(h.label))h.label=h.text}c[g+1]||(c[g+1]=[]);j(f,h)}else{h._nKeyIndex=d.length;h._nColspan=1;d.push(h)}c[g].push(h)}g--};if(YAHOO.lang.isArray(a)){j(a);this._aDefinitions=a}else return null;(function(a){for(var b=1,c,d,f=function(a,e){for(var e=e||1,c=0;c<a.length;c++){var d=a[c];if(YAHOO.lang.isArray(d.children)){e++;
+f(d.children,e);e--}else e>b&&(b=e)}},g=0;g<a.length;g++){c=a[g];f(c);for(var h=0;h<c.length;h++){d=c[h];d._nRowspan=YAHOO.lang.isArray(d.children)?1:b}b=1}})(c);for(a=0;a<c[0].length;a++)c[0][a]._nTreeIndex=a;for(var h=function(a,b){f[a].push(b.getSanitizedKey());b._oParent&&h(a,b._oParent)},a=0;a<d.length;a++){f[a]=[];h(a,d[a]);f[a]=f[a].reverse()}this.tree=c;this.flat=b;this.keys=d;this.headers=f},getId:function(){return this._sId},toString:function(){return"ColumnSet instance "+this._sId},getDefinitions:function(){var a=
+this._aDefinitions,c=function(a,d){for(var f=0;f<a.length;f++){var g=a[f],j=d.getColumnById(g.yuiColumnId);if(j){var j=j.getDefinition(),h;for(h in j)YAHOO.lang.hasOwnProperty(j,h)&&(g[h]=j[h])}YAHOO.lang.isArray(g.children)&&c(g.children,d)}};c(a,this);return this._aDefinitions=a},getColumnById:function(a){if(YAHOO.lang.isString(a))for(var c=this.flat,b=c.length-1;b>-1;b--)if(c[b]._sId===a)return c[b];return null},getColumn:function(a){if(YAHOO.lang.isNumber(a)&&this.keys[a])return this.keys[a];
+if(YAHOO.lang.isString(a)){for(var c=this.flat,b=[],d=0;d<c.length;d++)c[d].key===a&&b.push(c[d]);if(b.length===1)return b[0];if(b.length>1)return b}return null},getDescendants:function(a){var c=this,b=[],d,f=function(a){b.push(a);if(a.children)for(d=0;d<a.children.length;d++)f(c.getColumn(a.children[d].key))};f(a);return b}};
+YAHOO.widget.Column=function(a){this._sId=Dom.generateId(null,"yui-col");if(a&&YAHOO.lang.isObject(a))for(var c in a)c&&(this[c]=a[c]);if(!YAHOO.lang.isValue(this.key))this.key=Dom.generateId(null,"yui-dt-col");if(!YAHOO.lang.isValue(this.field))this.field=this.key;YAHOO.widget.Column._nCount++;if(this.width&&!YAHOO.lang.isNumber(this.width))this.width=null;if(this.editor&&YAHOO.lang.isString(this.editor))this.editor=new YAHOO.widget.CellEditor(this.editor,this.editorOptions)};
+YAHOO.lang.augmentObject(YAHOO.widget.Column,{_nCount:0,formatCheckbox:function(a,c,b,d){YAHOO.widget.DataTable.formatCheckbox(a,c,b,d)},formatCurrency:function(a,c,b,d){YAHOO.widget.DataTable.formatCurrency(a,c,b,d)},formatDate:function(a,c,b,d){YAHOO.widget.DataTable.formatDate(a,c,b,d)},formatEmail:function(a,c,b,d){YAHOO.widget.DataTable.formatEmail(a,c,b,d)},formatLink:function(a,c,b,d){YAHOO.widget.DataTable.formatLink(a,c,b,d)},formatNumber:function(a,c,b,d){YAHOO.widget.DataTable.formatNumber(a,
+c,b,d)},formatSelect:function(a,c,b,d){YAHOO.widget.DataTable.formatDropdown(a,c,b,d)}});
+YAHOO.widget.Column.prototype={_sId:null,_nKeyIndex:null,_nTreeIndex:null,_nColspan:1,_nRowspan:1,_oParent:null,_elTh:null,_elThLiner:null,_elThLabel:null,_elResizer:null,_nWidth:null,_dd:null,_ddResizer:null,key:null,field:null,label:null,abbr:null,children:null,width:null,minWidth:null,maxAutoWidth:null,hidden:!1,selected:!1,className:null,formatter:null,currencyOptions:null,dateOptions:null,dropdownOptions:null,editor:null,resizeable:!1,sortable:!1,sortOptions:null,getId:function(){return this._sId},
+toString:function(){return"Column instance "+this._sId},getDefinition:function(){var a={};a.abbr=this.abbr;a.className=this.className;a.editor=this.editor;a.editorOptions=this.editorOptions;a.field=this.field;a.formatter=this.formatter;a.hidden=this.hidden;a.key=this.key;a.label=this.label;a.minWidth=this.minWidth;a.maxAutoWidth=this.maxAutoWidth;a.resizeable=this.resizeable;a.selected=this.selected;a.sortable=this.sortable;a.sortOptions=this.sortOptions;a.width=this.width;a._calculatedWidth=this._calculatedWidth;
+return a},getKey:function(){return this.key},getField:function(){return this.field},getSanitizedKey:function(){return this.getKey().replace(/[^\w\-]/g,"")},getKeyIndex:function(){return this._nKeyIndex},getTreeIndex:function(){return this._nTreeIndex},getParent:function(){return this._oParent},getColspan:function(){return this._nColspan},getColSpan:function(){return this.getColspan()},getRowspan:function(){return this._nRowspan},getThEl:function(){return this._elTh},getThLinerEl:function(){return this._elThLiner},
+getResizerEl:function(){return this._elResizer},getColEl:function(){return this.getThEl()},getIndex:function(){return this.getKeyIndex()},format:function(){}};YAHOO.util.Sort={compare:function(a,c,b){if(a===null||typeof a=="undefined")return c===null||typeof c=="undefined"?0:1;if(c===null||typeof c=="undefined")return-1;a.constructor==String&&(a=a.toLowerCase());c.constructor==String&&(c=c.toLowerCase());return a<c?b?1:-1:a>c?b?-1:1:0}};
+YAHOO.widget.ColumnDD=function(a,c,b,d){if(a&&c&&b&&d){this.datatable=a;this.table=a.getTableEl();this.column=c;this.headCell=b;this.pointer=d;this.newIndex=null;this.init(b);this.initFrame();this.invalidHandleTypes={};this.setPadding(10,0,this.datatable.getTheadEl().offsetHeight+10,0);YAHOO.util.Event.on(window,"resize",function(){this.initConstraints()},this,true)}};
+YAHOO.util.DDProxy&&YAHOO.extend(YAHOO.widget.ColumnDD,YAHOO.util.DDProxy,{initConstraints:function(){var a=YAHOO.util.Dom.getRegion(this.table),c=this.getEl(),b=YAHOO.util.Dom.getXY(c),d=parseInt(YAHOO.util.Dom.getStyle(c,"width"),10);parseInt(YAHOO.util.Dom.getStyle(c,"height"),10);this.setXConstraint(b[0]-a.left+15,a.right-b[0]-d+15);this.setYConstraint(10,10)},_resizeProxy:function(){YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this,arguments);var a=this.getDragEl(),c=this.getEl();YAHOO.util.Dom.setStyle(this.pointer,
+"height",this.table.parentNode.offsetHeight+10+"px");YAHOO.util.Dom.setStyle(this.pointer,"display","block");c=YAHOO.util.Dom.getXY(c);YAHOO.util.Dom.setXY(this.pointer,[c[0],c[1]-5]);YAHOO.util.Dom.setStyle(a,"height",this.datatable.getContainerEl().offsetHeight+"px");YAHOO.util.Dom.setStyle(a,"width",parseInt(YAHOO.util.Dom.getStyle(a,"width"),10)+4+"px");YAHOO.util.Dom.setXY(this.dragEl,c)},onMouseDown:function(){this.initConstraints();this.resetConstraints()},clickValidator:function(a){if(!this.column.hidden){a=
+YAHOO.util.Event.getTarget(a);return this.isValidHandleChild(a)&&(this.id==this.handleElId||this.DDM.handleWasClicked(a,this.id))}},onDragOver:function(a,c){var b=this.datatable.getColumn(c);if(b){for(var d=b.getTreeIndex();d===null&&b.getParent();){b=b.getParent();d=b.getTreeIndex()}if(d!==null){var f=b.getThEl(),b=d,g=YAHOO.util.Event.getPageX(a),j=YAHOO.util.Dom.getX(f),h=j+YAHOO.util.Dom.get(f).offsetWidth/2,e=this.column.getTreeIndex();if(g<h)YAHOO.util.Dom.setX(this.pointer,j);else{f=parseInt(f.offsetWidth,
+10);YAHOO.util.Dom.setX(this.pointer,j+f);b++}d>e&&b--;if(b<0)b=0;else if(b>this.datatable.getColumnSet().tree[0].length)b=this.datatable.getColumnSet().tree[0].length;this.newIndex=b}}},onDragDrop:function(){this.datatable.reorderColumn(this.column,this.newIndex)},endDrag:function(){this.newIndex=null;YAHOO.util.Dom.setStyle(this.pointer,"display","none")}});
+YAHOO.util.ColumnResizer=function(a,c,b,d,f){if(a&&c&&b&&d){this.datatable=a;this.column=c;this.headCell=b;this.headCellLiner=c.getThLinerEl();this.resizerLiner=b.firstChild;this.init(d,d,{dragOnly:true,dragElId:f.id});this.initFrame();this.resetResizerEl();this.setPadding(0,1,0,0)}};
+YAHOO.util.DD&&YAHOO.extend(YAHOO.util.ColumnResizer,YAHOO.util.DDProxy,{resetResizerEl:function(){var a=YAHOO.util.Dom.get(this.handleElId).style;a.left="auto";a.right=0;a.top="auto";a.bottom=0;a.height=this.headCell.offsetHeight+"px"},onMouseUp:function(){for(var a=this.datatable.getColumnSet().keys,c,b=0,d=a.length;b<d;b++){c=a[b];c._ddResizer&&c._ddResizer.resetResizerEl()}this.resetResizerEl();a=this.headCellLiner;this.datatable.fireEvent("columnResizeEvent",{column:this.column,target:this.headCell,
+width:a.offsetWidth-(parseInt(YAHOO.util.Dom.getStyle(a,"paddingLeft"),10)|0)-(parseInt(YAHOO.util.Dom.getStyle(a,"paddingRight"),10)|0)})},onMouseDown:function(a){this.startWidth=this.headCellLiner.offsetWidth;this.startX=YAHOO.util.Event.getXY(a)[0];this.nLinerPadding=(parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0)+(parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0)},clickValidator:function(a){if(!this.column.hidden){a=YAHOO.util.Event.getTarget(a);
+return this.isValidHandleChild(a)&&(this.id==this.handleElId||this.DDM.handleWasClicked(a,this.id))}},startDrag:function(){var a=this.datatable.getColumnSet().keys;this.column.getKeyIndex();for(var c,b=0,d=a.length;b<d;b++){c=a[b];if(c._ddResizer)YAHOO.util.Dom.get(c._ddResizer.handleElId).style.height="1em"}},onDrag:function(a){a=YAHOO.util.Event.getXY(a)[0];if(a>YAHOO.util.Dom.getX(this.headCellLiner)){a=this.startWidth+(a-this.startX)-this.nLinerPadding;a>0&&this.datatable.setColumnWidth(this.column,
+a)}}});
+(function(){var a=YAHOO.lang,c=YAHOO.util,b=YAHOO.widget,d=c.Dom;YAHOO.widget.RecordSet=function(a){this._init(a)};var f=b.RecordSet;f._nCount=0;f.prototype={_sId:null,_init:function(c){this._sId=d.generateId(null,"yui-rs");b.RecordSet._nCount++;this._records=[];this._initEvents();c&&(a.isArray(c)?this.addRecords(c):a.isObject(c)&&this.addRecord(c))},_initEvents:function(){this.createEvent("recordAddEvent");this.createEvent("recordsAddEvent");this.createEvent("recordSetEvent");this.createEvent("recordsSetEvent");this.createEvent("recordUpdateEvent");
+this.createEvent("recordDeleteEvent");this.createEvent("recordsDeleteEvent");this.createEvent("resetEvent");this.createEvent("recordValueUpdateEvent")},_addRecord:function(a,b){var c=new YAHOO.widget.Record(a);YAHOO.lang.isNumber(b)&&b>-1?this._records.splice(b,0,c):this._records[this._records.length]=c;return c},_setRecord:function(c,d){if(!a.isNumber(d)||d<0)d=this._records.length;return this._records[d]=new b.Record(c)},_deleteRecord:function(b,c){if(!a.isNumber(c)||c<0)c=1;this._records.splice(b,
+c)},getId:function(){return this._sId},toString:function(){return"RecordSet instance "+this._sId},getLength:function(){return this._records.length},getRecord:function(c){var d;if(c instanceof b.Record)for(d=0;d<this._records.length;d++){if(this._records[d]&&this._records[d]._sId===c._sId)return c}else if(a.isNumber(c)){if(c>-1&&c<this.getLength())return this._records[c]}else if(a.isString(c))for(d=0;d<this._records.length;d++)if(this._records[d]&&this._records[d]._sId===c)return this._records[d];
+return null},getRecords:function(b,c){return!a.isNumber(b)?this._records:!a.isNumber(c)?this._records.slice(b):this._records.slice(b,b+c)},hasRecords:function(a,b){for(var c=this.getRecords(a,b),e=0;e<b;++e)if(typeof c[e]==="undefined")return false;return true},getRecordIndex:function(a){if(a)for(var b=this._records.length-1;b>-1;b--)if(this._records[b]&&a.getId()===this._records[b].getId())return b;return null},addRecord:function(b,c){if(a.isObject(b)){var d=this._addRecord(b,c);this.fireEvent("recordAddEvent",
+{record:d,data:b});return d}return null},addRecords:function(b,c){if(a.isArray(b)){var d=[],e,i,k;e=c=a.isNumber(c)?c:this._records.length;i=0;for(k=b.length;i<k;++i)if(a.isObject(b[i])){var f=this._addRecord(b[i],e++);d.push(f)}this.fireEvent("recordsAddEvent",{records:d,data:b});return d}if(a.isObject(b)){d=this._addRecord(b);this.fireEvent("recordsAddEvent",{records:[d],data:b});return d}return null},setRecord:function(b,c){if(a.isObject(b)){var d=this._setRecord(b,c);this.fireEvent("recordSetEvent",
+{record:d,data:b});return d}return null},setRecords:function(c,d){for(var f=b.Record,e=a.isArray(c)?c:[c],i=[],k=0,l=e.length,q=0,d=parseInt(d,10)|0;k<l;++k)typeof e[k]==="object"&&e[k]&&(i[q++]=this._records[d+k]=new f(e[k]));this.fireEvent("recordsSetEvent",{records:i,data:c});this.fireEvent("recordsSet",{records:i,data:c});return i},updateRecord:function(b,c){var d=this.getRecord(b);if(d&&a.isObject(c)){var e={},i;for(i in d._oData)a.hasOwnProperty(d._oData,i)&&(e[i]=d._oData[i]);d._oData=c;this.fireEvent("recordUpdateEvent",
+{record:d,newData:c,oldData:e});return d}return null},updateKey:function(a,b,c){this.updateRecordValue(a,b,c)},updateRecordValue:function(b,c,d){if(b=this.getRecord(b)){var e=null,i=b._oData[c];if(i&&a.isObject(i)){var e={},k;for(k in i)a.hasOwnProperty(i,k)&&(e[k]=i[k])}else e=i;b._oData[c]=d;this.fireEvent("keyUpdateEvent",{record:b,key:c,newData:d,oldData:e});this.fireEvent("recordValueUpdateEvent",{record:b,key:c,newData:d,oldData:e})}},replaceRecords:function(a){this.reset();return this.addRecords(a)},
+sortRecords:function(a,b,c){return this._records.sort(function(e,i){return a(e,i,b,c)})},reverseRecords:function(){return this._records.reverse()},deleteRecord:function(b){if(a.isNumber(b)&&b>-1&&b<this.getLength()){var c=this.getRecord(b).getData();this._deleteRecord(b);this.fireEvent("recordDeleteEvent",{data:c,index:b});return c}return null},deleteRecords:function(b,c){a.isNumber(c)||(c=1);if(a.isNumber(b)&&b>-1&&b<this.getLength()){for(var d=this.getRecords(b,c),e=[],i=[],k=0;k<d.length;k++){e[e.length]=
+d[k];i[i.length]=d[k].getData()}this._deleteRecord(b,c);this.fireEvent("recordsDeleteEvent",{data:e,deletedData:i,index:b});return e}return null},reset:function(){this._records=[];this.fireEvent("resetEvent")}};a.augmentProto(f,c.EventProvider);YAHOO.widget.Record=function(c){this._nCount=b.Record._nCount;this._sId=d.generateId(null,"yui-rec");b.Record._nCount++;this._oData={};if(a.isObject(c))for(var f in c)a.hasOwnProperty(c,f)&&(this._oData[f]=c[f])};YAHOO.widget.Record._nCount=0;YAHOO.widget.Record.prototype=
+{_nCount:null,_sId:null,_oData:null,getCount:function(){return this._nCount},getId:function(){return this._sId},getData:function(b){return a.isString(b)?this._oData[b]:this._oData},setData:function(a,b){this._oData[a]=b}}})();
+(function(){var a=YAHOO.lang,c=YAHOO.util,b=YAHOO.widget,d=YAHOO.env.ua,f=c.Dom,g=c.Event,j=c.DataSourceBase;YAHOO.widget.DataTable=function(a,c,d,l){var h=b.DataTable;if(l&&l.scrollable)return new YAHOO.widget.ScrollingDataTable(a,c,d,l);this._nIndex=h._nCount;this._sId=f.generateId(null,"yui-dt");this._oChainRender=new YAHOO.util.Chain;this._oChainRender.subscribe("end",this._onRenderChainEnd,this,true);this._initConfigs(l);this._initDataSource(d);if(this._oDataSource){this._initColumnSet(c);if(this._oColumnSet){this._initRecordSet();
+h.superclass.constructor.call(this,a,this.configs);if(this._initDomElements(a)){this.showTableMessage(this.get("MSG_LOADING"),h.CLASS_LOADING);this._initEvents();h._nCount++;h._nCurrentCount++;a={success:this.onDataReturnSetRows,failure:this.onDataReturnSetRows,scope:this,argument:this.getState()};c=this.get("initialLoad");if(c===true)this._oDataSource.sendRequest(this.get("initialRequest"),a);else if(c===false)this.showTableMessage(this.get("MSG_EMPTY"),h.CLASS_EMPTY);else{h=c||{};a.argument=h.argument||
+{};this._oDataSource.sendRequest(h.request,a)}}}}};var h=b.DataTable;a.augmentObject(h,{CLASS_DATATABLE:"yui-dt",CLASS_LINER:"yui-dt-liner",CLASS_LABEL:"yui-dt-label",CLASS_MESSAGE:"yui-dt-message",CLASS_MASK:"yui-dt-mask",CLASS_DATA:"yui-dt-data",CLASS_COLTARGET:"yui-dt-coltarget",CLASS_RESIZER:"yui-dt-resizer",CLASS_RESIZERLINER:"yui-dt-resizerliner",CLASS_RESIZERPROXY:"yui-dt-resizerproxy",CLASS_EDITOR:"yui-dt-editor",CLASS_EDITOR_SHIM:"yui-dt-editor-shim",CLASS_PAGINATOR:"yui-dt-paginator",CLASS_PAGE:"yui-dt-page",
+CLASS_DEFAULT:"yui-dt-default",CLASS_PREVIOUS:"yui-dt-previous",CLASS_NEXT:"yui-dt-next",CLASS_FIRST:"yui-dt-first",CLASS_LAST:"yui-dt-last",CLASS_REC:"yui-dt-rec",CLASS_EVEN:"yui-dt-even",CLASS_ODD:"yui-dt-odd",CLASS_SELECTED:"yui-dt-selected",CLASS_HIGHLIGHTED:"yui-dt-highlighted",CLASS_HIDDEN:"yui-dt-hidden",CLASS_DISABLED:"yui-dt-disabled",CLASS_EMPTY:"yui-dt-empty",CLASS_LOADING:"yui-dt-loading",CLASS_ERROR:"yui-dt-error",CLASS_EDITABLE:"yui-dt-editable",CLASS_DRAGGABLE:"yui-dt-draggable",CLASS_RESIZEABLE:"yui-dt-resizeable",
+CLASS_SCROLLABLE:"yui-dt-scrollable",CLASS_SORTABLE:"yui-dt-sortable",CLASS_ASC:"yui-dt-asc",CLASS_DESC:"yui-dt-desc",CLASS_BUTTON:"yui-dt-button",CLASS_CHECKBOX:"yui-dt-checkbox",CLASS_DROPDOWN:"yui-dt-dropdown",CLASS_RADIO:"yui-dt-radio",_nCount:0,_nCurrentCount:0,_elDynStyleNode:null,_bDynStylesFallback:d.ie?true:false,_oDynStyles:{},_cloneObject:function(e){if(!a.isValue(e))return e;var b={};if(e instanceof YAHOO.widget.BaseCellEditor)b=e;else if(Object.prototype.toString.apply(e)==="[object RegExp]")b=
+e;else if(a.isFunction(e))b=e;else if(a.isArray(e))for(var b=[],c=0,d=e.length;c<d;c++)b[c]=h._cloneObject(e[c]);else if(a.isObject(e))for(c in e)a.hasOwnProperty(e,c)&&(b[c]=a.isValue(e[c])&&a.isObject(e[c])||a.isArray(e[c])?h._cloneObject(e[c]):e[c]);else b=e;return b},formatButton:function(e,b,c,d){b=a.isValue(d)?d:"Click";e.innerHTML='<button type="button" class="'+h.CLASS_BUTTON+'">'+b+"</button>"},formatCheckbox:function(a,b,c,d){a.innerHTML='<input type="checkbox"'+(d?' checked="checked"':
+"")+' class="'+h.CLASS_CHECKBOX+'" />'},formatCurrency:function(a,b,d,f,h){a.innerHTML=c.Number.format(f,d.currencyOptions||(h||this).get("currencyOptions"))},formatDate:function(a,b,d,f,h){b=d.dateOptions||(h||this).get("dateOptions");a.innerHTML=c.Date.format(f,b,b.locale)},formatDropdown:function(e,b,c,d,f){var j=f||this,b=a.isValue(d)?d:b.getData(c.field),c=a.isArray(c.dropdownOptions)?c.dropdownOptions:null,n=e.getElementsByTagName("select");if(n.length===0){f=document.createElement("select");
+f.className=h.CLASS_DROPDOWN;f=e.appendChild(f);g.addListener(f,"change",j._onDropdownChange,j)}if(f=n[0]){f.innerHTML="";if(c)for(e=0;e<c.length;e++){d=c[e];j=document.createElement("option");j.value=a.isValue(d.value)?d.value:d;j.innerHTML=a.isValue(d.text)?d.text:a.isValue(d.label)?d.label:d;j=f.appendChild(j);if(j.value==b)j.selected=true}else f.innerHTML='<option selected value="'+b+'">'+b+"</option>"}else e.innerHTML=a.isValue(d)?d:""},formatEmail:function(e,b,c,d){if(a.isString(d)){d=a.escapeHTML(d);
+e.innerHTML='<a href="mailto:'+d+'">'+d+"</a>"}else e.innerHTML=a.isValue(d)?a.escapeHTML(d.toString()):""},formatLink:function(e,b,c,d){if(a.isString(d)){d=a.escapeHTML(d);e.innerHTML='<a href="'+d+'">'+d+"</a>"}else e.innerHTML=a.isValue(d)?a.escapeHTML(d.toString()):""},formatNumber:function(a,b,d,f,h){a.innerHTML=c.Number.format(f,d.numberOptions||(h||this).get("numberOptions"))},formatRadio:function(a,b,c,d,f){a.innerHTML='<input type="radio"'+(d?' checked="checked"':"")+' name="'+(f||this).getId()+
+"-col-"+c.getSanitizedKey()+'" class="'+h.CLASS_RADIO+'" />'},formatText:function(e,b,c,d){b=a.isValue(d)?d:"";e.innerHTML=a.escapeHTML(b.toString())},formatTextarea:function(e,b,c,d){b="<textarea>"+(a.isValue(d)?a.escapeHTML(d.toString()):"")+"</textarea>";e.innerHTML=b},formatTextbox:function(e,b,c,d){b='<input type="text" value="'+(a.isValue(d)?a.escapeHTML(d.toString()):"")+'" />';e.innerHTML=b},formatDefault:function(e,b,c,d){e.innerHTML=a.isValue(d)&&d!==""?d.toString():"&#160;"},validateNumber:function(e){e=
+e*1;if(a.isNumber(e))return e}});h.Formatter={button:h.formatButton,checkbox:h.formatCheckbox,currency:h.formatCurrency,date:h.formatDate,dropdown:h.formatDropdown,email:h.formatEmail,link:h.formatLink,number:h.formatNumber,radio:h.formatRadio,text:h.formatText,textarea:h.formatTextarea,textbox:h.formatTextbox,defaultFormatter:h.formatDefault};a.extend(h,c.Element,{initAttributes:function(e){e=e||{};h.superclass.initAttributes.call(this,e);this.setAttributeConfig("summary",{value:"",validator:a.isString,
+method:function(a){if(this._elTable)this._elTable.summary=a}});this.setAttributeConfig("selectionMode",{value:"standard",validator:a.isString});this.setAttributeConfig("sortedBy",{value:null,validator:function(e){return e?a.isObject(e)&&e.key:e===null},method:function(a){var e=this.get("sortedBy");this._configs.sortedBy.value=a;var b,c,d;if(this._elThead){if(e&&e.key&&e.dir){b=this._oColumnSet.getColumn(e.key);c=b.getKeyIndex();var g=b.getThEl();f.removeClass(g,e.dir);this.formatTheadCell(b.getThLinerEl().firstChild,
+b,a)}if(a){b=a.column?a.column:this._oColumnSet.getColumn(a.key);d=b.getKeyIndex();g=b.getThEl();a.dir&&(a.dir=="asc"||a.dir=="desc")?f.addClass(g,a.dir=="desc"?h.CLASS_DESC:h.CLASS_ASC):f.addClass(g,a.dir||h.CLASS_ASC);this.formatTheadCell(b.getThLinerEl().firstChild,b,a)}}if(this._elTbody){this._elTbody.style.display="none";b=this._elTbody.rows;for(var j=b.length-1;j>-1;j--){g=b[j].childNodes;g[c]&&f.removeClass(g[c],e.dir);g[d]&&f.addClass(g[d],a.dir)}this._elTbody.style.display=""}this._clearTrTemplateEl()}});
+this.setAttributeConfig("paginator",{value:null,validator:function(a){return a===null||a instanceof b.Paginator},method:function(){this._updatePaginator.apply(this,arguments)}});this.setAttributeConfig("caption",{value:null,validator:a.isString,method:function(a){this._initCaptionEl(a)}});this.setAttributeConfig("draggableColumns",{value:false,validator:a.isBoolean,method:function(a){this._elThead&&(a?this._initDraggableColumns():this._destroyDraggableColumns())}});this.setAttributeConfig("renderLoopSize",
+{value:0,validator:a.isNumber});this.setAttributeConfig("sortFunction",{value:function(a,e,b,c){var d=YAHOO.util.Sort.compare,c=d(a.getData(c),e.getData(c),b);return c===0?d(a.getCount(),e.getCount(),b):c}});this.setAttributeConfig("formatRow",{value:null,validator:a.isFunction});this.setAttributeConfig("generateRequest",{value:function(a,e){var a=a||{pagination:null,sortedBy:null},b=encodeURIComponent(a.sortedBy?a.sortedBy.key:e.getColumnSet().keys[0].getKey()),c=a.pagination?a.pagination.rowsPerPage:
+null;return"sort="+b+"&dir="+(a.sortedBy&&a.sortedBy.dir===YAHOO.widget.DataTable.CLASS_DESC?"desc":"asc")+"&startIndex="+(a.pagination?a.pagination.recordOffset:0)+(c!==null?"&results="+c:"")},validator:a.isFunction});this.setAttributeConfig("initialRequest",{value:null});this.setAttributeConfig("initialLoad",{value:true});this.setAttributeConfig("dynamicData",{value:false,validator:a.isBoolean});this.setAttributeConfig("MSG_EMPTY",{value:"No records found.",validator:a.isString});this.setAttributeConfig("MSG_LOADING",
+{value:"Loading...",validator:a.isString});this.setAttributeConfig("MSG_ERROR",{value:"Data error.",validator:a.isString});this.setAttributeConfig("MSG_SORTASC",{value:"Click to sort ascending",validator:a.isString,method:function(a){if(this._elThead)for(var e=0,b=this.getColumnSet().keys,c=b.length;e<c;e++)if(b[e].sortable&&this.getColumnSortDir(b[e])===h.CLASS_ASC)b[e]._elThLabel.firstChild.title=a}});this.setAttributeConfig("MSG_SORTDESC",{value:"Click to sort descending",validator:a.isString,
+method:function(a){if(this._elThead)for(var e=0,b=this.getColumnSet().keys,c=b.length;e<c;e++)if(b[e].sortable&&this.getColumnSortDir(b[e])===h.CLASS_DESC)b[e]._elThLabel.firstChild.title=a}});this.setAttributeConfig("currencySymbol",{value:"$",validator:a.isString});this.setAttributeConfig("currencyOptions",{value:{prefix:this.get("currencySymbol"),decimalPlaces:2,decimalSeparator:".",thousandsSeparator:","}});this.setAttributeConfig("dateOptions",{value:{format:"%m/%d/%Y",locale:"en"}});this.setAttributeConfig("numberOptions",
+{value:{decimalPlaces:0,thousandsSeparator:","}})},_bInit:true,_nIndex:null,_nTrCount:0,_nTdCount:0,_sId:null,_oChainRender:null,_elContainer:null,_elMask:null,_elTable:null,_elCaption:null,_elColgroup:null,_elThead:null,_elTbody:null,_elMsgTbody:null,_elMsgTr:null,_elMsgTd:null,_elColumnDragTarget:null,_elColumnResizerProxy:null,_oDataSource:null,_oColumnSet:null,_oRecordSet:null,_oCellEditor:null,_sFirstTrId:null,_sLastTrId:null,_elTrTemplate:null,_aDynFunctions:[],_disabled:false,clearTextSelection:function(){var a;
+if(window.getSelection)a=window.getSelection();else if(document.getSelection)a=document.getSelection();else if(document.selection)a=document.selection;a&&(a.empty?a.empty():a.removeAllRanges?a.removeAllRanges():a.collapse&&a.collapse())},_focusEl:function(a){a=a||this._elTbody;setTimeout(function(){try{a.focus()}catch(b){}},0)},_repaintGecko:d.gecko?function(a){var a=a||this._elContainer,b=a.parentNode,c=a.nextSibling;b.insertBefore(b.removeChild(a),c)}:function(){},_repaintOpera:d.opera?function(){if(d.opera){document.documentElement.className=
+document.documentElement.className+" ";document.documentElement.className=YAHOO.lang.trim(document.documentElement.className)}}:function(){},_repaintWebkit:d.webkit?function(a){var a=a||this._elContainer,b=a.parentNode,c=a.nextSibling;b.insertBefore(b.removeChild(a),c)}:function(){},_initConfigs:function(e){if(!e||!a.isObject(e))e={};this.configs=e},_initColumnSet:function(e){var b,c,d;if(this._oColumnSet){c=0;for(d=this._oColumnSet.keys.length;c<d;c++){b=this._oColumnSet.keys[c];h._oDynStyles["."+
+this.getId()+"-col-"+b.getSanitizedKey()+" ."+h.CLASS_LINER]=void 0;b.editor&&b.editor.unsubscribeAll&&b.editor.unsubscribeAll()}this._oColumnSet=null;this._clearTrTemplateEl()}if(a.isArray(e))this._oColumnSet=new YAHOO.widget.ColumnSet(e);else if(e instanceof YAHOO.widget.ColumnSet)this._oColumnSet=e;e=this._oColumnSet.keys;c=0;for(d=e.length;c<d;c++){b=e[c];if(b.editor&&b.editor.subscribe){b.editor.subscribe("showEvent",this._onEditorShowEvent,this,true);b.editor.subscribe("keydownEvent",this._onEditorKeydownEvent,
+this,true);b.editor.subscribe("revertEvent",this._onEditorRevertEvent,this,true);b.editor.subscribe("saveEvent",this._onEditorSaveEvent,this,true);b.editor.subscribe("cancelEvent",this._onEditorCancelEvent,this,true);b.editor.subscribe("blurEvent",this._onEditorBlurEvent,this,true);b.editor.subscribe("blockEvent",this._onEditorBlockEvent,this,true);b.editor.subscribe("unblockEvent",this._onEditorUnblockEvent,this,true)}}},_initDataSource:function(e){this._oDataSource=null;if(e&&a.isFunction(e.sendRequest))this._oDataSource=
+e;else{var e=null,b=this._elContainer,c=0;if(b.hasChildNodes()){b=b.childNodes;for(c=0;c<b.length;c++)if(b[c].nodeName&&b[c].nodeName.toLowerCase()=="table"){e=b[c];break}if(e){for(b=[];c<this._oColumnSet.keys.length;c++)b.push({key:this._oColumnSet.keys[c].key});this._oDataSource=new j(e);this._oDataSource.responseType=j.TYPE_HTMLTABLE;this._oDataSource.responseSchema={fields:b}}}}},_initRecordSet:function(){this._oRecordSet?this._oRecordSet.reset():this._oRecordSet=new YAHOO.widget.RecordSet},_initDomElements:function(a){this._initContainerEl(a);
+this._initTableEl(this._elContainer);this._initColgroupEl(this._elTable);this._initTheadEl(this._elTable);this._initMsgTbodyEl(this._elTable);this._initTbodyEl(this._elTable);return!this._elContainer||!this._elTable||!this._elColgroup||!this._elThead||!this._elTbody||!this._elMsgTbody?false:true},_destroyContainerEl:function(a){var b=this._oColumnSet.keys,c;f.removeClass(a,h.CLASS_DATATABLE);g.purgeElement(a);g.purgeElement(this._elThead,true);g.purgeElement(this._elTbody);g.purgeElement(this._elMsgTbody);
+c=a.getElementsByTagName("select");c.length&&g.detachListener(c,"change");for(c=b.length-1;c>=0;--c)b[c].editor&&g.purgeElement(b[c].editor._elContainer);a.innerHTML="";this._elTbody=this._elThead=this._elColgroup=this._elContainer=null},_initContainerEl:function(a){if((a=f.get(a))&&a.nodeName&&a.nodeName.toLowerCase()=="div"){this._destroyContainerEl(a);f.addClass(a,h.CLASS_DATATABLE);g.addListener(a,"focus",this._onTableFocus,this);g.addListener(a,"dblclick",this._onTableDblclick,this);this._elContainer=
+a;var b=document.createElement("div");b.className=h.CLASS_MASK;b.style.display="none";this._elMask=a.appendChild(b)}},_destroyTableEl:function(){var a=this._elTable;if(a){g.purgeElement(a,true);a.parentNode.removeChild(a);this._elTbody=this._elThead=this._elColgroup=this._elCaption=null}},_initCaptionEl:function(a){if(this._elTable&&a){if(!this._elCaption)this._elCaption=this._elTable.createCaption();this._elCaption.innerHTML=a}else this._elCaption&&this._elCaption.parentNode.removeChild(this._elCaption)},
+_initTableEl:function(a){if(a){this._destroyTableEl();this._elTable=a.appendChild(document.createElement("table"));this._elTable.summary=this.get("summary");this.get("caption")&&this._initCaptionEl(this.get("caption"));g.delegate(this._elTable,"mouseenter",this._onTableMouseover,"thead ."+h.CLASS_LABEL,this);g.delegate(this._elTable,"mouseleave",this._onTableMouseout,"thead ."+h.CLASS_LABEL,this);g.delegate(this._elTable,"mouseenter",this._onTableMouseover,"tbody.yui-dt-data>tr>td",this);g.delegate(this._elTable,
+"mouseleave",this._onTableMouseout,"tbody.yui-dt-data>tr>td",this);g.delegate(this._elTable,"mouseenter",this._onTableMouseover,"tbody.yui-dt-message>tr>td",this);g.delegate(this._elTable,"mouseleave",this._onTableMouseout,"tbody.yui-dt-message>tr>td",this)}},_destroyColgroupEl:function(){var a=this._elColgroup;if(a){var b=a.parentNode;g.purgeElement(a,true);b.removeChild(a);this._elColgroup=null}},_initColgroupEl:function(a){if(a){this._destroyColgroupEl();for(var b=this._oColumnSet.keys,c=0,d=(this._aColIds||
+[]).length,f=document.createDocumentFragment(),h=document.createElement("col"),c=0,d=b.length;c<d;c++)f.appendChild(h.cloneNode(false));a=a.insertBefore(document.createElement("colgroup"),a.firstChild);a.appendChild(f);this._elColgroup=a}},_insertColgroupColEl:function(e){if(a.isNumber(e)&&this._elColgroup){e=this._elColgroup.childNodes[e]||null;this._elColgroup.insertBefore(document.createElement("col"),e)}},_removeColgroupColEl:function(e){a.isNumber(e)&&(this._elColgroup&&this._elColgroup.childNodes[e])&&
+this._elColgroup.removeChild(this._elColgroup.childNodes[e])},_reorderColgroupColEl:function(e,b){if(a.isArray(e)&&a.isNumber(b)&&this._elColgroup&&this._elColgroup.childNodes.length>e[e.length-1]){var c,d=[];for(c=e.length-1;c>-1;c--)d.push(this._elColgroup.removeChild(this._elColgroup.childNodes[e[c]]));var f=this._elColgroup.childNodes[b]||null;for(c=d.length-1;c>-1;c--)this._elColgroup.insertBefore(d[c],f)}},_destroyTheadEl:function(){var a=this._elThead;if(a){var b=a.parentNode;g.purgeElement(a,
+true);this._destroyColumnHelpers();b.removeChild(a);this._elThead=null}},_initTheadEl:function(a){if(a=a||this._elTable){this._destroyTheadEl();var b=this._elColgroup?a.insertBefore(document.createElement("thead"),this._elColgroup.nextSibling):a.appendChild(document.createElement("thead"));g.addListener(b,"focus",this._onTheadFocus,this);g.addListener(b,"keydown",this._onTheadKeydown,this);g.addListener(b,"mousedown",this._onTableMousedown,this);g.addListener(b,"mouseup",this._onTableMouseup,this);
+g.addListener(b,"click",this._onTheadClick,this);for(var c=this._oColumnSet,l,j,p=c.tree,n,a=0;a<p.length;a++){var o=b.appendChild(document.createElement("tr"));for(j=0;j<p[a].length;j++){l=p[a][j];n=o.appendChild(document.createElement("th"));this._initThEl(n,l)}a===0&&f.addClass(o,h.CLASS_FIRST);a===p.length-1&&f.addClass(o,h.CLASS_LAST)}l=c.headers[0]||[];for(a=0;a<l.length;a++)f.addClass(f.get(this.getId()+"-th-"+l[a]),h.CLASS_FIRST);c=c.headers[c.headers.length-1]||[];for(a=0;a<c.length;a++)f.addClass(f.get(this.getId()+
+"-th-"+c[a]),h.CLASS_LAST);if(d.webkit&&d.webkit<420){setTimeout(function(){b.style.display=""},0);b.style.display="none"}this._elThead=b;this._initColumnHelpers()}},_initThEl:function(a,b){a.id=this.getId()+"-th-"+b.getSanitizedKey();a.innerHTML="";a.rowSpan=b.getRowspan();a.colSpan=b.getColspan();b._elTh=a;var c=a.appendChild(document.createElement("div"));c.id=a.id+"-liner";c.className=h.CLASS_LINER;b._elThLiner=c;c=c.appendChild(document.createElement("span"));c.className=h.CLASS_LABEL;if(b.abbr)a.abbr=
+b.abbr;b.hidden&&this._clearMinWidth(b);a.className=this._getColumnClassNames(b);if(b.width){var d=b.minWidth&&b.width<b.minWidth?b.minWidth:b.width;if(h._bDynStylesFallback){a.firstChild.style.overflow="hidden";a.firstChild.style.width=d+"px"}else this._setColumnWidthDynStyles(b,d+"px","hidden")}this.formatTheadCell(c,b,this.get("sortedBy"));b._elThLabel=c},formatTheadCell:function(e,b,c){var d=b.getKey(),d=a.isValue(b.label)?b.label:d;if(b.sortable){var f=this.getColumnSortDir(b,c)===h.CLASS_DESC;
+c&&b.key===c.key&&(f=c.dir!==h.CLASS_DESC);b=this.getId()+"-href-"+b.getSanitizedKey();c=f?this.get("MSG_SORTDESC"):this.get("MSG_SORTASC");e.innerHTML='<a href="'+b+'" title="'+c+'" class="'+h.CLASS_SORTABLE+'">'+d+"</a>"}else e.innerHTML=d},_destroyDraggableColumns:function(){for(var a,b=0,c=this._oColumnSet.tree[0].length;b<c;b++){a=this._oColumnSet.tree[0][b];if(a._dd){a._dd=a._dd.unreg();f.removeClass(a.getThEl(),h.CLASS_DRAGGABLE)}}this._destroyColumnDragTargetEl()},_initDraggableColumns:function(){this._destroyDraggableColumns();
+if(c.DD)for(var a,b,d,l=0,g=this._oColumnSet.tree[0].length;l<g;l++){a=this._oColumnSet.tree[0][l];b=a.getThEl();f.addClass(b,h.CLASS_DRAGGABLE);d=this._initColumnDragTargetEl();a._dd=new YAHOO.widget.ColumnDD(this,a,b,d)}},_destroyColumnDragTargetEl:function(){if(this._elColumnDragTarget){var a=this._elColumnDragTarget;YAHOO.util.Event.purgeElement(a);a.parentNode.removeChild(a);this._elColumnDragTarget=null}},_initColumnDragTargetEl:function(){if(!this._elColumnDragTarget){var a=document.createElement("div");
+a.id=this.getId()+"-coltarget";a.className=h.CLASS_COLTARGET;a.style.display="none";document.body.insertBefore(a,document.body.firstChild);this._elColumnDragTarget=a}return this._elColumnDragTarget},_destroyResizeableColumns:function(){for(var a=this._oColumnSet.keys,b=0,c=a.length;b<c;b++)if(a[b]._ddResizer){a[b]._ddResizer=a[b]._ddResizer.unreg();f.removeClass(a[b].getThEl(),h.CLASS_RESIZEABLE)}this._destroyColumnResizerProxyEl()},_initResizeableColumns:function(){this._destroyResizeableColumns();
+if(c.DD)for(var a,b,d,l,j=0,p=this._oColumnSet.keys.length;j<p;j++){a=this._oColumnSet.keys[j];if(a.resizeable){b=a.getThEl();f.addClass(b,h.CLASS_RESIZEABLE);d=a.getThLinerEl();l=b.appendChild(document.createElement("div"));l.className=h.CLASS_RESIZERLINER;l.appendChild(d);d=l.appendChild(document.createElement("div"));d.id=b.id+"-resizer";d.className=h.CLASS_RESIZER;a._elResizer=d;l=this._initColumnResizerProxyEl();a._ddResizer=new YAHOO.util.ColumnResizer(this,a,b,d,l);a=function(a){g.stopPropagation(a)};
+g.addListener(d,"click",a)}}},_destroyColumnResizerProxyEl:function(){if(this._elColumnResizerProxy){var a=this._elColumnResizerProxy;YAHOO.util.Event.purgeElement(a);a.parentNode.removeChild(a);this._elColumnResizerProxy=null}},_initColumnResizerProxyEl:function(){if(!this._elColumnResizerProxy){var a=document.createElement("div");a.id=this.getId()+"-colresizerproxy";a.className=h.CLASS_RESIZERPROXY;document.body.insertBefore(a,document.body.firstChild);this._elColumnResizerProxy=a}return this._elColumnResizerProxy},
+_destroyColumnHelpers:function(){this._destroyDraggableColumns();this._destroyResizeableColumns()},_initColumnHelpers:function(){this.get("draggableColumns")&&this._initDraggableColumns();this._initResizeableColumns()},_destroyTbodyEl:function(){var a=this._elTbody;if(a){var b=a.parentNode;g.purgeElement(a,true);b.removeChild(a);this._elTbody=null}},_initTbodyEl:function(a){if(a){this._destroyTbodyEl();a=a.appendChild(document.createElement("tbody"));a.tabIndex=0;a.className=h.CLASS_DATA;g.addListener(a,
+"focus",this._onTbodyFocus,this);g.addListener(a,"mousedown",this._onTableMousedown,this);g.addListener(a,"mouseup",this._onTableMouseup,this);g.addListener(a,"keydown",this._onTbodyKeydown,this);g.addListener(a,"click",this._onTbodyClick,this);if(d.ie)a.hideFocus=true;this._elTbody=a}},_destroyMsgTbodyEl:function(){var a=this._elMsgTbody;if(a){var b=a.parentNode;g.purgeElement(a,true);b.removeChild(a);this._elTbody=null}},_initMsgTbodyEl:function(a){if(a){var b=document.createElement("tbody");b.className=
+h.CLASS_MESSAGE;var c=b.appendChild(document.createElement("tr"));c.className=h.CLASS_FIRST+" "+h.CLASS_LAST;this._elMsgTr=c;c=c.appendChild(document.createElement("td"));c.colSpan=this._oColumnSet.keys.length||1;c.className=h.CLASS_FIRST+" "+h.CLASS_LAST;this._elMsgTd=c;b=a.insertBefore(b,this._elTbody);c.appendChild(document.createElement("div")).className=h.CLASS_LINER;this._elMsgTbody=b;g.addListener(b,"focus",this._onTbodyFocus,this);g.addListener(b,"mousedown",this._onTableMousedown,this);g.addListener(b,
+"mouseup",this._onTableMouseup,this);g.addListener(b,"keydown",this._onTbodyKeydown,this);g.addListener(b,"click",this._onTbodyClick,this)}},_initEvents:function(){this._initColumnSort();YAHOO.util.Event.addListener(document,"click",this._onDocumentClick,this);this.subscribe("paginatorChange",function(){this._handlePaginatorChange.apply(this,arguments)});this.subscribe("initEvent",function(){this.renderPaginator()});this._initCellEditing()},_initColumnSort:function(){this.subscribe("theadCellClickEvent",
+this.onEventSortColumn);var a=this.get("sortedBy");if(a)if(a.dir=="desc")this._configs.sortedBy.value.dir=h.CLASS_DESC;else if(a.dir=="asc")this._configs.sortedBy.value.dir=h.CLASS_ASC},_initCellEditing:function(){this.subscribe("editorBlurEvent",function(){this.onEditorBlurEvent.apply(this,arguments)});this.subscribe("editorBlockEvent",function(){this.onEditorBlockEvent.apply(this,arguments)});this.subscribe("editorUnblockEvent",function(){this.onEditorUnblockEvent.apply(this,arguments)})},_getColumnClassNames:function(e,
+b){var c;c=a.isString(e.className)?[e.className]:a.isArray(e.className)?e.className:[];c[c.length]=this.getId()+"-col-"+e.getSanitizedKey();c[c.length]="yui-dt-col-"+e.getSanitizedKey();var d=this.get("sortedBy")||{};e.key===d.key&&(c[c.length]=d.dir||"");if(e.hidden)c[c.length]=h.CLASS_HIDDEN;if(e.selected)c[c.length]=h.CLASS_SELECTED;if(e.sortable)c[c.length]=h.CLASS_SORTABLE;if(e.resizeable)c[c.length]=h.CLASS_RESIZEABLE;if(e.editor)c[c.length]=h.CLASS_EDITABLE;b&&(c=c.concat(b));return c.join(" ")},
+_clearTrTemplateEl:function(){this._elTrTemplate=null},_getTrTemplateEl:function(){if(this._elTrTemplate)return this._elTrTemplate;var a=document,b=a.createElement("tr"),c=a.createElement("td"),a=a.createElement("div");c.appendChild(a);for(var a=document.createDocumentFragment(),d=this._oColumnSet.keys,f,g=0,j=d.length;g<j;g++){f=c.cloneNode(true);f=this._formatTdEl(d[g],f,g,g===j-1);a.appendChild(f)}b.appendChild(a);b.className=h.CLASS_REC;return this._elTrTemplate=b},_formatTdEl:function(a,b,c,
+d){for(var f=this._oColumnSet.headers[c],g="",j,o=0,m=f.length;o<m;o++){j=this._sId+"-th-"+f[o]+" ";g=g+j}b.headers=g;f=[];if(c===0)f[f.length]=h.CLASS_FIRST;if(d)f[f.length]=h.CLASS_LAST;b.className=this._getColumnClassNames(a,f);b.firstChild.className=h.CLASS_LINER;if(a.width&&h._bDynStylesFallback){a=a.minWidth&&a.width<a.minWidth?a.minWidth:a.width;b.firstChild.style.overflow="hidden";b.firstChild.style.width=a+"px"}return b},_addTrEl:function(a){return this._updateTrEl(this._getTrTemplateEl().cloneNode(true),
+a)},_updateTrEl:function(a,b){if(this.get("formatRow")?this.get("formatRow").call(this,a,b):1){a.style.display="none";for(var c=a.childNodes,d=0,f=c.length;d<f;++d)this.formatCell(c[d].firstChild,b,this._oColumnSet.keys[d]);a.style.display=""}c=a.id;d=b.getId();if(this._sFirstTrId===c)this._sFirstTrId=d;if(this._sLastTrId===c)this._sLastTrId=d;a.id=d;return a},_deleteTrEl:function(e){var b;b=a.isNumber(e)?e:f.get(e).sectionRowIndex;return a.isNumber(b)&&b>-2&&b<this._elTbody.rows.length?this._elTbody.removeChild(this._elTbody.rows[e]):
+null},_unsetFirstRow:function(){if(this._sFirstTrId){f.removeClass(this._sFirstTrId,h.CLASS_FIRST);this._sFirstTrId=null}},_setFirstRow:function(){this._unsetFirstRow();var a=this.getFirstTrEl();if(a){f.addClass(a,h.CLASS_FIRST);this._sFirstTrId=a.id}},_unsetLastRow:function(){if(this._sLastTrId){f.removeClass(this._sLastTrId,h.CLASS_LAST);this._sLastTrId=null}},_setLastRow:function(){this._unsetLastRow();var a=this.getLastTrEl();if(a){f.addClass(a,h.CLASS_LAST);this._sLastTrId=a.id}},_setRowStripes:function(e,
+b){var c=this._elTbody.rows,d=0,g=c.length,j=[],n=0,o=[],m=0;if(e!==null&&e!==void 0){var r=this.getTrEl(e);if(r){d=r.sectionRowIndex;a.isNumber(b)&&b>1&&(g=d+b)}}for(;d<g;d++)d%2?j[n++]=c[d]:o[m++]=c[d];j.length&&f.replaceClass(j,h.CLASS_EVEN,h.CLASS_ODD);o.length&&f.replaceClass(o,h.CLASS_ODD,h.CLASS_EVEN)},_setSelections:function(){var a=this.getSelectedRows(),b=this.getSelectedCells();if(a.length>0||b.length>0){for(var c=this._oColumnSet,d,g=0;g<a.length;g++)(d=f.get(a[g]))&&f.addClass(d,h.CLASS_SELECTED);
+for(g=0;g<b.length;g++)(d=f.get(b[g].recordId))&&f.addClass(d.childNodes[c.getColumn(b[g].columnKey).getKeyIndex()],h.CLASS_SELECTED)}},_onRenderChainEnd:function(){this.hideTableMessage();this._elTbody.rows.length===0&&this.showTableMessage(this.get("MSG_EMPTY"),h.CLASS_EMPTY);var a=this;setTimeout(function(){if(a instanceof h&&a._sId){if(a._bInit){a._bInit=false;a.fireEvent("initEvent")}a.fireEvent("renderEvent");a.fireEvent("refreshEvent");a.validateColumnWidths();a.fireEvent("postRenderEvent")}},
+0)},_onDocumentClick:function(a,b){var c=g.getTarget(a);c.nodeName.toLowerCase();if(!f.isAncestor(b._elContainer,c)){b.fireEvent("tableBlurEvent");if(b._oCellEditor)if(b._oCellEditor.getContainerEl){var d=b._oCellEditor.getContainerEl();!f.isAncestor(d,c)&&d.id!==c.id&&b._oCellEditor.fireEvent("blurEvent",{editor:b._oCellEditor})}else b._oCellEditor.isActive&&!f.isAncestor(b._oCellEditor.container,c)&&b._oCellEditor.container.id!==c.id&&b.fireEvent("editorBlurEvent",{editor:b._oCellEditor})}},_onTableFocus:function(a,
+b){b.fireEvent("tableFocusEvent")},_onTheadFocus:function(a,b){b.fireEvent("theadFocusEvent");b.fireEvent("tableFocusEvent")},_onTbodyFocus:function(a,b){b.fireEvent("tbodyFocusEvent");b.fireEvent("tableFocusEvent")},_onTableMouseover:function(a,b,c,d){for(var c=b.nodeName&&b.nodeName.toLowerCase(),g=true;b&&c!="table";){switch(c){case "body":return;case "td":g=d.fireEvent("cellMouseoverEvent",{target:b,event:a});break;case "span":if(f.hasClass(b,h.CLASS_LABEL)){d.fireEvent("theadLabelMouseoverEvent",
+{target:b,event:a});g=d.fireEvent("headerLabelMouseoverEvent",{target:b,event:a})}break;case "th":d.fireEvent("theadCellMouseoverEvent",{target:b,event:a});g=d.fireEvent("headerCellMouseoverEvent",{target:b,event:a});break;case "tr":if(b.parentNode.nodeName.toLowerCase()=="thead"){d.fireEvent("theadRowMouseoverEvent",{target:b,event:a});g=d.fireEvent("headerRowMouseoverEvent",{target:b,event:a})}else g=d.fireEvent("rowMouseoverEvent",{target:b,event:a})}if(g===false)return;(b=b.parentNode)&&(c=b.nodeName.toLowerCase())}d.fireEvent("tableMouseoverEvent",
+{target:b||d._elContainer,event:a})},_onTableMouseout:function(a,b,c,d){for(var c=b.nodeName&&b.nodeName.toLowerCase(),g=true;b&&c!="table";){switch(c){case "body":return;case "td":g=d.fireEvent("cellMouseoutEvent",{target:b,event:a});break;case "span":if(f.hasClass(b,h.CLASS_LABEL)){d.fireEvent("theadLabelMouseoutEvent",{target:b,event:a});g=d.fireEvent("headerLabelMouseoutEvent",{target:b,event:a})}break;case "th":d.fireEvent("theadCellMouseoutEvent",{target:b,event:a});g=d.fireEvent("headerCellMouseoutEvent",
+{target:b,event:a});break;case "tr":if(b.parentNode.nodeName.toLowerCase()=="thead"){d.fireEvent("theadRowMouseoutEvent",{target:b,event:a});g=d.fireEvent("headerRowMouseoutEvent",{target:b,event:a})}else g=d.fireEvent("rowMouseoutEvent",{target:b,event:a})}if(g===false)return;(b=b.parentNode)&&(c=b.nodeName.toLowerCase())}d.fireEvent("tableMouseoutEvent",{target:b||d._elContainer,event:a})},_onTableMousedown:function(a,b){for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),j=true;c&&
+d!="table";){switch(d){case "body":return;case "td":j=b.fireEvent("cellMousedownEvent",{target:c,event:a});break;case "span":if(f.hasClass(c,h.CLASS_LABEL)){b.fireEvent("theadLabelMousedownEvent",{target:c,event:a});j=b.fireEvent("headerLabelMousedownEvent",{target:c,event:a})}break;case "th":b.fireEvent("theadCellMousedownEvent",{target:c,event:a});j=b.fireEvent("headerCellMousedownEvent",{target:c,event:a});break;case "tr":if(c.parentNode.nodeName.toLowerCase()=="thead"){b.fireEvent("theadRowMousedownEvent",
+{target:c,event:a});j=b.fireEvent("headerRowMousedownEvent",{target:c,event:a})}else j=b.fireEvent("rowMousedownEvent",{target:c,event:a})}if(j===false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableMousedownEvent",{target:c||b._elContainer,event:a})},_onTableMouseup:function(a,b){for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),j=true;c&&d!="table";){switch(d){case "body":return;case "td":j=b.fireEvent("cellMouseupEvent",{target:c,event:a});break;case "span":if(f.hasClass(c,
+h.CLASS_LABEL)){b.fireEvent("theadLabelMouseupEvent",{target:c,event:a});j=b.fireEvent("headerLabelMouseupEvent",{target:c,event:a})}break;case "th":b.fireEvent("theadCellMouseupEvent",{target:c,event:a});j=b.fireEvent("headerCellMouseupEvent",{target:c,event:a});break;case "tr":if(c.parentNode.nodeName.toLowerCase()=="thead"){b.fireEvent("theadRowMouseupEvent",{target:c,event:a});j=b.fireEvent("headerRowMouseupEvent",{target:c,event:a})}else j=b.fireEvent("rowMouseupEvent",{target:c,event:a})}if(j===
+false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableMouseupEvent",{target:c||b._elContainer,event:a})},_onTableDblclick:function(a,b){for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),j=true;c&&d!="table";){switch(d){case "body":return;case "td":j=b.fireEvent("cellDblclickEvent",{target:c,event:a});break;case "span":if(f.hasClass(c,h.CLASS_LABEL)){b.fireEvent("theadLabelDblclickEvent",{target:c,event:a});j=b.fireEvent("headerLabelDblclickEvent",{target:c,event:a})}break;
+case "th":b.fireEvent("theadCellDblclickEvent",{target:c,event:a});j=b.fireEvent("headerCellDblclickEvent",{target:c,event:a});break;case "tr":if(c.parentNode.nodeName.toLowerCase()=="thead"){b.fireEvent("theadRowDblclickEvent",{target:c,event:a});j=b.fireEvent("headerRowDblclickEvent",{target:c,event:a})}else j=b.fireEvent("rowDblclickEvent",{target:c,event:a})}if(j===false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableDblclickEvent",{target:c||b._elContainer,event:a})},
+_onTheadKeydown:function(a,b){for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),f=true;c&&d!="table";){switch(d){case "body":return;case "thead":f=b.fireEvent("theadKeyEvent",{target:c,event:a})}if(f===false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableKeyEvent",{target:c||b._elContainer,event:a})},_onTbodyKeydown:function(a,b){var c=b.get("selectionMode");c=="standard"?b._handleStandardSelectionByKey(a):c=="single"?b._handleSingleSelectionByKey(a):c=="cellblock"?
+b._handleCellBlockSelectionByKey(a):c=="cellrange"?b._handleCellRangeSelectionByKey(a):c=="singlecell"&&b._handleSingleCellSelectionByKey(a);b._oCellEditor&&(b._oCellEditor.fireEvent?b._oCellEditor.fireEvent("blurEvent",{editor:b._oCellEditor}):b._oCellEditor.isActive&&b.fireEvent("editorBlurEvent",{editor:b._oCellEditor}));for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),f=true;c&&d!="table";){switch(d){case "body":return;case "tbody":f=b.fireEvent("tbodyKeyEvent",{target:c,event:a})}if(f===
+false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableKeyEvent",{target:c||b._elContainer,event:a})},_onTheadClick:function(a,b){b._oCellEditor&&(b._oCellEditor.fireEvent?b._oCellEditor.fireEvent("blurEvent",{editor:b._oCellEditor}):b._oCellEditor.isActive&&b.fireEvent("editorBlurEvent",{editor:b._oCellEditor}));for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),j=true;c&&d!="table";){switch(d){case "body":return;case "input":var p=c.type.toLowerCase();p=="checkbox"?
+j=b.fireEvent("theadCheckboxClickEvent",{target:c,event:a}):p=="radio"?j=b.fireEvent("theadRadioClickEvent",{target:c,event:a}):p=="button"||p=="image"||p=="submit"||p=="reset"?j=c.disabled?false:b.fireEvent("theadButtonClickEvent",{target:c,event:a}):c.disabled&&(j=false);break;case "a":j=b.fireEvent("theadLinkClickEvent",{target:c,event:a});break;case "button":j=c.disabled?false:b.fireEvent("theadButtonClickEvent",{target:c,event:a});break;case "span":if(f.hasClass(c,h.CLASS_LABEL)){b.fireEvent("theadLabelClickEvent",
+{target:c,event:a});j=b.fireEvent("headerLabelClickEvent",{target:c,event:a})}break;case "th":b.fireEvent("theadCellClickEvent",{target:c,event:a});j=b.fireEvent("headerCellClickEvent",{target:c,event:a});break;case "tr":b.fireEvent("theadRowClickEvent",{target:c,event:a});j=b.fireEvent("headerRowClickEvent",{target:c,event:a})}if(j===false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableClickEvent",{target:c||b._elContainer,event:a})},_onTbodyClick:function(a,b){b._oCellEditor&&
+(b._oCellEditor.fireEvent?b._oCellEditor.fireEvent("blurEvent",{editor:b._oCellEditor}):b._oCellEditor.isActive&&b.fireEvent("editorBlurEvent",{editor:b._oCellEditor}));for(var c=g.getTarget(a),d=c.nodeName&&c.nodeName.toLowerCase(),f=true;c&&d!="table";){switch(d){case "body":return;case "input":var h=c.type.toLowerCase();h=="checkbox"?f=b.fireEvent("checkboxClickEvent",{target:c,event:a}):h=="radio"?f=b.fireEvent("radioClickEvent",{target:c,event:a}):h=="button"||h=="image"||h=="submit"||h=="reset"?
+f=c.disabled?false:b.fireEvent("buttonClickEvent",{target:c,event:a}):c.disabled&&(f=false);break;case "a":f=b.fireEvent("linkClickEvent",{target:c,event:a});break;case "button":f=c.disabled?false:b.fireEvent("buttonClickEvent",{target:c,event:a});break;case "td":f=b.fireEvent("cellClickEvent",{target:c,event:a});break;case "tr":f=b.fireEvent("rowClickEvent",{target:c,event:a})}if(f===false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableClickEvent",{target:c||b._elContainer,
+event:a})},_onDropdownChange:function(a,b){var c=g.getTarget(a);b.fireEvent("dropdownChangeEvent",{event:a,target:c})},configs:null,getId:function(){return this._sId},toString:function(){return"DataTable instance "+this._sId},getDataSource:function(){return this._oDataSource},getColumnSet:function(){return this._oColumnSet},getRecordSet:function(){return this._oRecordSet},getState:function(){return{totalRecords:this.get("paginator")?this.get("paginator").get("totalRecords"):this._oRecordSet.getLength(),
+pagination:this.get("paginator")?this.get("paginator").getState():null,sortedBy:this.get("sortedBy"),selectedRows:this.getSelectedRows(),selectedCells:this.getSelectedCells()}},getContainerEl:function(){return this._elContainer},getTableEl:function(){return this._elTable},getTheadEl:function(){return this._elThead},getTbodyEl:function(){return this._elTbody},getMsgTbodyEl:function(){return this._elMsgTbody},getMsgTdEl:function(){return this._elMsgTd},getTrEl:function(e){if(e instanceof YAHOO.widget.Record)return document.getElementById(e.getId());
+if(a.isNumber(e)){var b=f.getElementsByClassName(h.CLASS_REC,"tr",this._elTbody);return b&&b[e]?b[e]:null}if(e)if((e=a.isString(e)?document.getElementById(e):e)&&e.ownerDocument==document){e.nodeName.toLowerCase()!="tr"&&(e=f.getAncestorByTagName(e,"tr"));return e}return null},getFirstTrEl:function(){for(var a=this._elTbody.rows,b=0;a[b];){if(this.getRecord(a[b]))return a[b];b++}return null},getLastTrEl:function(){for(var a=this._elTbody.rows,b=a.length-1;b>-1;){if(this.getRecord(a[b]))return a[b];
+b--}return null},getNextTrEl:function(a,b){var c=this.getTrIndex(a);if(c!==null){var d=this._elTbody.rows;if(b)for(;c<d.length-1;){a=d[c+1];if(this.getRecord(a))return a;c++}else if(c<d.length-1)return d[c+1]}return null},getPreviousTrEl:function(a,b){var c=this.getTrIndex(a);if(c!==null){var d=this._elTbody.rows;if(b)for(;c>0;){a=d[c-1];if(this.getRecord(a))return a;c--}else if(c>0)return d[c-1]}return null},getCellIndex:function(a){if(a=this.getTdEl(a))if(d.ie>0)for(var b=0,c=a.parentNode.childNodes,
+f=c.length;b<f;b++){if(c[b]==a)return b}else return a.cellIndex},getTdLinerEl:function(a){return this.getTdEl(a).firstChild||null},getTdEl:function(e){var b,c=f.get(e);if(c&&c.ownerDocument==document){if((b=c.nodeName.toLowerCase()!="td"?f.getAncestorByTagName(c,"td"):c)&&(b.parentNode.parentNode==this._elTbody||b.parentNode.parentNode===null||b.parentNode.parentNode.nodeType===11))return b}else if(e){var d;if(a.isString(e.columnKey)&&a.isString(e.recordId)){d=this.getRecord(e.recordId);(c=this.getColumn(e.columnKey))&&
+(b=c.getKeyIndex())}if(e.record&&e.column&&e.column.getKeyIndex){d=e.record;b=e.column.getKeyIndex()}e=this.getTrEl(d);if(b!==null&&e&&e.cells&&e.cells.length>0)return e.cells[b]||null}return null},getFirstTdEl:function(e){if(e=a.isValue(e)?this.getTrEl(e):this.getFirstTrEl()){if(e.cells&&e.cells.length>0)return e.cells[0];if(e.childNodes&&e.childNodes.length>0)return e.childNodes[0]}return null},getLastTdEl:function(e){if(e=a.isValue(e)?this.getTrEl(e):this.getLastTrEl()){if(e.cells&&e.cells.length>
+0)return e.cells[e.cells.length-1];if(e.childNodes&&e.childNodes.length>0)return e.childNodes[e.childNodes.length-1]}return null},getNextTdEl:function(a){var b=this.getTdEl(a);if(b){a=this.getCellIndex(b);b=this.getTrEl(b);if(b.cells&&b.cells.length>0&&a<b.cells.length-1)return b.cells[a+1];if(b.childNodes&&b.childNodes.length>0&&a<b.childNodes.length-1)return b.childNodes[a+1];if(a=this.getNextTrEl(b))return a.cells[0]}return null},getPreviousTdEl:function(a){var b=this.getTdEl(a);if(b){a=this.getCellIndex(b);
+b=this.getTrEl(b);if(a>0){if(b.cells&&b.cells.length>0)return b.cells[a-1];if(b.childNodes&&b.childNodes.length>0)return b.childNodes[a-1]}else if(a=this.getPreviousTrEl(b))return this.getLastTdEl(a)}return null},getAboveTdEl:function(a,b){var c=this.getTdEl(a);if(c){var d=this.getPreviousTrEl(c,b);if(d){c=this.getCellIndex(c);if(d.cells&&d.cells.length>0)return d.cells[c]?d.cells[c]:null;if(d.childNodes&&d.childNodes.length>0)return d.childNodes[c]?d.childNodes[c]:null}}return null},getBelowTdEl:function(a,
+b){var c=this.getTdEl(a);if(c){var d=this.getNextTrEl(c,b);if(d){c=this.getCellIndex(c);if(d.cells&&d.cells.length>0)return d.cells[c]?d.cells[c]:null;if(d.childNodes&&d.childNodes.length>0)return d.childNodes[c]?d.childNodes[c]:null}}return null},getThLinerEl:function(a){return(a=this.getColumn(a))?a.getThLinerEl():null},getThEl:function(a){if(a instanceof YAHOO.widget.Column){if(a=a.getThEl())return a}else if((a=f.get(a))&&a.ownerDocument==document)return a=a.nodeName.toLowerCase()!="th"?f.getAncestorByTagName(a,
+"th"):a;return null},getTrIndex:function(a){var b=this.getRecord(a),a=this.getRecordIndex(b);if(b){if(b=this.getTrEl(b))return b.sectionRowIndex;return(b=this.get("paginator"))?b.get("recordOffset")+a:a}return null},load:function(a){a=a||{};(a.datasource||this._oDataSource).sendRequest(a.request||this.get("initialRequest"),a.callback||{success:this.onDataReturnInitializeTable,failure:this.onDataReturnInitializeTable,scope:this,argument:this.getState()})},initializeTable:function(){this._bInit=true;
+this._oRecordSet.reset();var a=this.get("paginator");a&&a.set("totalRecords",0);this._unselectAllTrEls();this._unselectAllTdEls();this._oAnchorCell=this._oAnchorRecord=this._aSelections=null;this.set("sortedBy",null)},_runRenderChain:function(){this._oChainRender.run()},_getViewRecords:function(){var a=this.get("paginator");return a?this._oRecordSet.getRecords(a.getStartIndex(),a.getRowsPerPage()):this._oRecordSet.getRecords()},render:function(){this._oChainRender.stop();this.fireEvent("beforeRenderEvent");
+var a=this._getViewRecords(),b=this._elTbody,c=this.get("renderLoopSize"),d=a.length;if(d>0){for(b.style.display="none";b.lastChild;)b.removeChild(b.lastChild);b.style.display="";this._oChainRender.add({method:function(c){if(this instanceof h&&this._sId){var k=c.nCurrentRecord,g=c.nCurrentRecord+c.nLoopLength>d?d:c.nCurrentRecord+c.nLoopLength,j,q;for(b.style.display="none";k<g;k++){j=(j=f.get(a[k].getId()))||this._addTrEl(a[k]);q=b.childNodes[k]||null;b.insertBefore(j,q)}b.style.display="";c.nCurrentRecord=
+k}},scope:this,iterations:c>0?Math.ceil(d/c):1,argument:{nCurrentRecord:0,nLoopLength:c>0?c:d},timeout:c>0?0:-1});this._oChainRender.add({method:function(){if(this instanceof h&&this._sId){for(;b.rows.length>d;)b.removeChild(b.lastChild);this._setFirstRow();this._setLastRow();this._setRowStripes();this._setSelections()}},scope:this,timeout:c>0?0:-1})}else{var g=b.rows.length;g>0&&this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){var e=a.nCurrent,c=a.nLoopLength,c=e-c<0?0:
+e-c;for(b.style.display="none";e>c;e--)b.deleteRow(-1);b.style.display="";a.nCurrent=e}},scope:this,iterations:c>0?Math.ceil(g/c):1,argument:{nCurrent:g,nLoopLength:c>0?c:g},timeout:c>0?0:-1})}this._runRenderChain()},disable:function(){this._disabled=true;var a=this._elTable,b=this._elMask;b.style.width=a.offsetWidth+"px";b.style.height=a.offsetHeight+"px";b.style.left=a.offsetLeft+"px";b.style.display="";this.fireEvent("disableEvent")},undisable:function(){this._disabled=false;this._elMask.style.display=
+"none";this.fireEvent("undisableEvent")},isDisabled:function(){return this._disabled},destroy:function(){this._oChainRender.stop();this._destroyColumnHelpers();for(var b,c=0,d=this._oColumnSet.flat.length;c<d;c++)if((b=this._oColumnSet.flat[c].editor)&&b.destroy){b.destroy();this._oColumnSet.flat[c].editor=null}this._destroyPaginator();this._oRecordSet.unsubscribeAll();this.unsubscribeAll();g.removeListener(document,"click",this._onDocumentClick);this._destroyContainerEl(this._elContainer);for(var f in this)a.hasOwnProperty(this,
+f)&&(this[f]=null);h._nCurrentCount--;if(h._nCurrentCount<1&&h._elDynStyleNode){document.getElementsByTagName("head")[0].removeChild(h._elDynStyleNode);h._elDynStyleNode=null}},showTableMessage:function(b,c){var d=this._elMsgTd;if(a.isString(b))d.firstChild.innerHTML=b;if(a.isString(c))d.className=c;this._elMsgTbody.style.display="";this.fireEvent("tableMsgShowEvent",{html:b,className:c})},hideTableMessage:function(){if(this._elMsgTbody.style.display!="none"){this._elMsgTbody.style.display="none";
+this._elMsgTbody.parentNode.style.width="";this.fireEvent("tableMsgHideEvent")}},focus:function(){this.focusTbodyEl()},focusTheadEl:function(){this._focusEl(this._elThead)},focusTbodyEl:function(){this._focusEl(this._elTbody)},onShow:function(){this.validateColumnWidths();for(var a=this._oColumnSet.keys,b=0,c=a.length,d;b<c;b++){d=a[b];d._ddResizer&&d._ddResizer.resetResizerEl()}},getRecordIndex:function(b){var c;if(a.isNumber(b))c=b;else{if(b instanceof YAHOO.widget.Record)return this._oRecordSet.getRecordIndex(b);
+if(b=this.getTrEl(b))c=b.sectionRowIndex}if(a.isNumber(c))return(b=this.get("paginator"))?b.get("recordOffset")+c:c;return null},getRecord:function(a){var b=this._oRecordSet.getRecord(a);if(!b)(a=this.getTrEl(a))&&(b=this._oRecordSet.getRecord(a.id));return b instanceof YAHOO.widget.Record?this._oRecordSet.getRecord(b):null},getColumn:function(a){var b=this._oColumnSet.getColumn(a);if(!b){var c=this.getTdEl(a);if(c)b=this._oColumnSet.getColumn(this.getCellIndex(c));else if(c=this.getThEl(a))for(var a=
+this._oColumnSet.flat,d=0,f=a.length;d<f;d++)a[d].getThEl().id===c.id&&(b=a[d])}return b},getColumnById:function(a){return this._oColumnSet.getColumnById(a)},getColumnSortDir:function(a,b){if(a.sortOptions&&a.sortOptions.defaultDir)if(a.sortOptions.defaultDir=="asc")a.sortOptions.defaultDir=h.CLASS_ASC;else if(a.sortOptions.defaultDir=="desc")a.sortOptions.defaultDir=h.CLASS_DESC;var c=a.sortOptions&&a.sortOptions.defaultDir?a.sortOptions.defaultDir:h.CLASS_ASC;(b=b||this.get("sortedBy"))&&b.key===
+a.key&&(c=b.dir?b.dir===h.CLASS_ASC?h.CLASS_DESC:h.CLASS_ASC:c===h.CLASS_ASC?h.CLASS_DESC:h.CLASS_ASC);return c},doBeforeSortColumn:function(){this.showTableMessage(this.get("MSG_LOADING"),h.CLASS_LOADING);return true},sortColumn:function(b,c){if(b&&b instanceof YAHOO.widget.Column){b.sortable||f.addClass(this.getThEl(b),h.CLASS_SORTABLE);c&&(c!==h.CLASS_ASC&&c!==h.CLASS_DESC)&&(c=null);var d=c||this.getColumnSortDir(b),g=(this.get("sortedBy")||{}).key===b.key?true:false;if(this.doBeforeSortColumn(b,
+d)){if(this.get("dynamicData")){g=this.getState();if(g.pagination)g.pagination.recordOffset=0;g.sortedBy={key:b.key,dir:d};var j=this.get("generateRequest")(g,this);this.unselectAllRows();this.unselectAllCells();this._oDataSource.sendRequest(j,{success:this.onDataReturnSetRows,failure:this.onDataReturnSetRows,argument:g,scope:this})}else{j=b.sortOptions&&a.isFunction(b.sortOptions.sortFunction)?b.sortOptions.sortFunction:null;if(!g||c||j){j=j||this.get("sortFunction");this._oRecordSet.sortRecords(j,
+d==h.CLASS_DESC?true:false,b.sortOptions&&b.sortOptions.field?b.sortOptions.field:b.field)}else this._oRecordSet.reverseRecords();(g=this.get("paginator"))&&g.setPage(1,true);this.render();this.set("sortedBy",{key:b.key,dir:d,column:b})}this.fireEvent("columnSortEvent",{column:b,dir:d})}}},setColumnWidth:function(b,c){b instanceof YAHOO.widget.Column||(b=this.getColumn(b));if(b){if(a.isNumber(c)){c=c>b.minWidth?c:b.minWidth;b.width=c;this._setColumnWidth(b,c+"px");this.fireEvent("columnSetWidthEvent",
+{column:b,width:c})}else if(c===null){b.width=c;this._setColumnWidth(b,"auto");this.validateColumnWidths(b);this.fireEvent("columnUnsetWidthEvent",{column:b})}this._clearTrTemplateEl()}},_setColumnWidth:function(a,b,c){if(a&&a.getKeyIndex()!==null){c=c||(b===""||b==="auto"?"visible":"hidden");h._bDynStylesFallback?this._setColumnWidthDynFunction(a,b,c):this._setColumnWidthDynStyles(a,b,c)}},_setColumnWidthDynStyles:function(a,b,c){var d=h._elDynStyleNode,f;if(!d){d=document.createElement("style");
+d.type="text/css";d=document.getElementsByTagName("head").item(0).appendChild(d);h._elDynStyleNode=d}if(d){var g="."+this.getId()+"-col-"+a.getSanitizedKey()+" ."+h.CLASS_LINER;if(this._elTbody)this._elTbody.style.display="none";if(f=h._oDynStyles[g]){f.style.overflow=c;f.style.width=b}else if(d.styleSheet&&d.styleSheet.addRule){d.styleSheet.addRule(g,"overflow:"+c);d.styleSheet.addRule(g,"width:"+b);f=d.styleSheet.rules[d.styleSheet.rules.length-1];h._oDynStyles[g]=f}else if(d.sheet&&d.sheet.insertRule){d.sheet.insertRule(g+
+" {overflow:"+c+";width:"+b+";}",d.sheet.cssRules.length);f=d.sheet.cssRules[d.sheet.cssRules.length-1];h._oDynStyles[g]=f}if(this._elTbody)this._elTbody.style.display=""}if(!f){h._bDynStylesFallback=true;this._setColumnWidthDynFunction(a,b)}},_setColumnWidthDynFunction:function(a,b,c){b=="auto"&&(b="");var d=this._elTbody?this._elTbody.rows.length:0;if(!this._aDynFunctions[d]){var f,h,g=["var colIdx=oColumn.getKeyIndex();","oColumn.getThLinerEl().style.overflow="];f=d-1;for(h=2;f>=0;--f){g[h++]=
+"this._elTbody.rows[";g[h++]=f;g[h++]="].cells[colIdx].firstChild.style.overflow="}g[h]="sOverflow;";g[h+1]="oColumn.getThLinerEl().style.width=";f=d-1;for(h=h+2;f>=0;--f){g[h++]="this._elTbody.rows[";g[h++]=f;g[h++]="].cells[colIdx].firstChild.style.width="}g[h]="sWidth;";this._aDynFunctions[d]=new Function("oColumn","sWidth","sOverflow",g.join(""))}(d=this._aDynFunctions[d])&&d.call(this,a,b,c)},validateColumnWidths:function(a){var b=this._elColgroup,c=b.cloneNode(true),d=false,h=this._oColumnSet.keys,
+g;if(a&&!a.hidden&&!a.width&&a.getKeyIndex()!==null){g=a.getThLinerEl();if(a.minWidth>0&&g.offsetWidth<a.minWidth){c.childNodes[a.getKeyIndex()].style.width=a.minWidth+(parseInt(f.getStyle(g,"paddingLeft"),10)|0)+(parseInt(f.getStyle(g,"paddingRight"),10)|0)+"px";d=true}else a.maxAutoWidth>0&&g.offsetWidth>a.maxAutoWidth&&this._setColumnWidth(a,a.maxAutoWidth+"px","hidden")}else for(var j=0,o=h.length;j<o;j++){a=h[j];if(!a.hidden&&!a.width){g=a.getThLinerEl();if(a.minWidth>0&&g.offsetWidth<a.minWidth){c.childNodes[j].style.width=
+a.minWidth+(parseInt(f.getStyle(g,"paddingLeft"),10)|0)+(parseInt(f.getStyle(g,"paddingRight"),10)|0)+"px";d=true}else a.maxAutoWidth>0&&g.offsetWidth>a.maxAutoWidth&&this._setColumnWidth(a,a.maxAutoWidth+"px","hidden")}}if(d){b.parentNode.replaceChild(c,b);this._elColgroup=c}},_clearMinWidth:function(a){if(a.getKeyIndex()!==null)this._elColgroup.childNodes[a.getKeyIndex()].style.width=""},_restoreMinWidth:function(a){if(a.minWidth&&a.getKeyIndex()!==null)this._elColgroup.childNodes[a.getKeyIndex()].style.width=
+a.minWidth+"px"},hideColumn:function(a){a instanceof YAHOO.widget.Column||(a=this.getColumn(a));if(a&&!a.hidden&&a.getTreeIndex()!==null){for(var b=this.getTbodyEl().rows,c=b.length,d=this._oColumnSet.getDescendants(a),g=0,j=d.length;g<j;g++){var n=d[g];n.hidden=true;f.addClass(n.getThEl(),h.CLASS_HIDDEN);var o=n.getKeyIndex();if(o!==null){this._clearMinWidth(a);for(var m=0;m<c;m++)f.addClass(b[m].cells[o],h.CLASS_HIDDEN)}this.fireEvent("columnHideEvent",{column:n})}this._repaintOpera();this._clearTrTemplateEl()}},
+showColumn:function(a){a instanceof YAHOO.widget.Column||(a=this.getColumn(a));if(a&&a.hidden&&a.getTreeIndex()!==null){for(var b=this.getTbodyEl().rows,c=b.length,d=this._oColumnSet.getDescendants(a),g=0,j=d.length;g<j;g++){var n=d[g];n.hidden=false;f.removeClass(n.getThEl(),h.CLASS_HIDDEN);var o=n.getKeyIndex();if(o!==null){this._restoreMinWidth(a);for(var m=0;m<c;m++)f.removeClass(b[m].cells[o],h.CLASS_HIDDEN)}this.fireEvent("columnShowEvent",{column:n})}this._clearTrTemplateEl()}},removeColumn:function(a){a instanceof
+YAHOO.widget.Column||(a=this.getColumn(a));if(a){var b=a.getTreeIndex();if(b!==null){var c,d=a.getKeyIndex();if(d===null){var f=[],g=this._oColumnSet.getDescendants(a);c=0;for(a=g.length;c<a;c++){var j=g[c].getKeyIndex();j!==null&&(f[f.length]=j)}f.length>0&&(d=f)}else d=[d];if(d!==null){d.sort(function(a,b){return YAHOO.util.Sort.compare(a,b)});this._destroyTheadEl();c=this._oColumnSet.getDefinitions();a=c.splice(b,1)[0];this._initColumnSet(c);this._initTheadEl();for(c=d.length-1;c>-1;c--)this._removeColgroupColEl(d[c]);
+var o=this._elTbody.rows;if(o.length>0){var m=this.get("renderLoopSize"),b=o.length;this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){for(var b=a.nCurrentRow,e=m>0?Math.min(b+m,o.length):o.length,c=a.aIndexes,d;b<e;++b)for(d=c.length-1;d>-1;d--)o[b].removeChild(o[b].childNodes[c[d]]);a.nCurrentRow=b}},iterations:m>0?Math.ceil(b/m):1,argument:{nCurrentRow:0,aIndexes:d},scope:this,timeout:m>0?0:-1});this._runRenderChain()}this.fireEvent("columnRemoveEvent",{column:a});return a}}}},
+insertColumn:function(b,c){if(b instanceof YAHOO.widget.Column)b=b.getDefinition();else if(b.constructor!==Object)return;var d=this._oColumnSet;if(!a.isValue(c)||!a.isNumber(c))c=d.tree[0].length;this._destroyTheadEl();var f=this._oColumnSet.getDefinitions();f.splice(c,0,b);this._initColumnSet(f);this._initTheadEl();var d=this._oColumnSet,f=d.tree[0][c],g,j=[],n=d.getDescendants(f),d=0;for(g=n.length;d<g;d++){var o=n[d].getKeyIndex();o!==null&&(j[j.length]=o)}if(j.length>0){for(var m=j.sort(function(a,
+b){return YAHOO.util.Sort.compare(a,b)})[0],d=j.length-1;d>-1;d--)this._insertColgroupColEl(j[d]);var r=this._elTbody.rows;if(r.length>0){var s=this.get("renderLoopSize"),n=r.length,o=[],t,d=0;for(g=j.length;d<g;d++){var u=j[d];t=this._getTrTemplateEl().childNodes[d].cloneNode(true);t=this._formatTdEl(this._oColumnSet.keys[u],t,u,u===this._oColumnSet.keys.length-1);o[u]=t}this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){for(var b=a.nCurrentRow,e,c=a.descKeyIndexes,d=s>0?
+Math.min(b+s,r.length):r.length,i;b<d;++b){i=r[b].childNodes[m]||null;for(e=c.length-1;e>-1;e--)r[b].insertBefore(a.aTdTemplates[c[e]].cloneNode(true),i)}a.nCurrentRow=b}},iterations:s>0?Math.ceil(n/s):1,argument:{nCurrentRow:0,aTdTemplates:o,descKeyIndexes:j},scope:this,timeout:s>0?0:-1});this._runRenderChain()}this.fireEvent("columnInsertEvent",{column:b,index:c});return f}},reorderColumn:function(a,b){a instanceof YAHOO.widget.Column||(a=this.getColumn(a));if(a&&YAHOO.lang.isNumber(b)){var c=a.getTreeIndex();
+if(c!==null&&c!==b){var d,f,g=a.getKeyIndex(),j,o=[],m;if(g===null){j=this._oColumnSet.getDescendants(a);d=0;for(f=j.length;d<f;d++){m=j[d].getKeyIndex();m!==null&&(o[o.length]=m)}o.length>0&&(g=o)}else g=[g];if(g!==null){g.sort(function(a,b){return YAHOO.util.Sort.compare(a,b)});this._destroyTheadEl();var r=this._oColumnSet.getDefinitions();d=r.splice(c,1)[0];r.splice(b,0,d);this._initColumnSet(r);this._initTheadEl();var r=this._oColumnSet.tree[0][b],s=r.getKeyIndex();if(s===null){o=[];j=this._oColumnSet.getDescendants(r);
+d=0;for(f=j.length;d<f;d++){m=j[d].getKeyIndex();m!==null&&(o[o.length]=m)}o.length>0&&(s=o)}else s=[s];var t=s.sort(function(a,b){return YAHOO.util.Sort.compare(a,b)})[0];this._reorderColgroupColEl(g,t);var u=this._elTbody.rows;if(u.length>0){var w=this.get("renderLoopSize");d=u.length;this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){for(var b=a.nCurrentRow,e,c,d,i=w>0?Math.min(b+w,u.length):u.length,f=a.aIndexes,k;b<i;++b){c=[];k=u[b];for(e=f.length-1;e>-1;e--)c.push(k.removeChild(k.childNodes[f[e]]));
+d=k.childNodes[t]||null;for(e=c.length-1;e>-1;e--)k.insertBefore(c[e],d)}a.nCurrentRow=b}},iterations:w>0?Math.ceil(d/w):1,argument:{nCurrentRow:0,aIndexes:g},scope:this,timeout:w>0?0:-1});this._runRenderChain()}this.fireEvent("columnReorderEvent",{column:r,oldIndex:c});return r}}}},selectColumn:function(a){if((a=this.getColumn(a))&&!a.selected&&a.getKeyIndex()!==null){a.selected=true;var b=a.getThEl();f.addClass(b,h.CLASS_SELECTED);var c=this.getTbodyEl().rows;this._oChainRender.add({method:function(a){this instanceof
+h&&(this._sId&&c[a.rowIndex]&&c[a.rowIndex].cells[a.cellIndex])&&f.addClass(c[a.rowIndex].cells[a.cellIndex],h.CLASS_SELECTED);a.rowIndex++},scope:this,iterations:c.length,argument:{rowIndex:0,cellIndex:a.getKeyIndex()}});this._clearTrTemplateEl();this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnSelectEvent",{column:a})}},unselectColumn:function(a){if((a=this.getColumn(a))&&a.selected&&a.getKeyIndex()!==null){a.selected=false;var b=a.getThEl();
+f.removeClass(b,h.CLASS_SELECTED);var c=this.getTbodyEl().rows;this._oChainRender.add({method:function(a){this instanceof h&&(this._sId&&c[a.rowIndex]&&c[a.rowIndex].cells[a.cellIndex])&&f.removeClass(c[a.rowIndex].cells[a.cellIndex],h.CLASS_SELECTED);a.rowIndex++},scope:this,iterations:c.length,argument:{rowIndex:0,cellIndex:a.getKeyIndex()}});this._clearTrTemplateEl();this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnUnselectEvent",{column:a})}},
+getSelectedColumns:function(){for(var a=[],b=this._oColumnSet.keys,c=0,d=b.length;c<d;c++)b[c].selected&&(a[a.length]=b[c]);return a},highlightColumn:function(a){if((a=this.getColumn(a))&&a.getKeyIndex()!==null){var b=a.getThEl();f.addClass(b,h.CLASS_HIGHLIGHTED);var c=this.getTbodyEl().rows;this._oChainRender.add({method:function(a){this instanceof h&&(this._sId&&c[a.rowIndex]&&c[a.rowIndex].cells[a.cellIndex])&&f.addClass(c[a.rowIndex].cells[a.cellIndex],h.CLASS_HIGHLIGHTED);a.rowIndex++},scope:this,
+iterations:c.length,argument:{rowIndex:0,cellIndex:a.getKeyIndex()},timeout:-1});this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnHighlightEvent",{column:a})}},unhighlightColumn:function(a){if((a=this.getColumn(a))&&a.getKeyIndex()!==null){var b=a.getThEl();f.removeClass(b,h.CLASS_HIGHLIGHTED);var c=this.getTbodyEl().rows;this._oChainRender.add({method:function(a){this instanceof h&&(this._sId&&c[a.rowIndex]&&c[a.rowIndex].cells[a.cellIndex])&&
+f.removeClass(c[a.rowIndex].cells[a.cellIndex],h.CLASS_HIGHLIGHTED);a.rowIndex++},scope:this,iterations:c.length,argument:{rowIndex:0,cellIndex:a.getKeyIndex()},timeout:-1});this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnUnhighlightEvent",{column:a})}},addRow:function(e,c){if((!a.isNumber(c)||!(c<0||c>this._oRecordSet.getLength()))&&e&&a.isObject(e)){var d=this._oRecordSet.addRecord(e,c);if(d){var f,g=this.get("paginator");if(g){f=g.get("totalRecords");
+f!==b.Paginator.VALUE_UNLIMITED&&g.set("totalRecords",f+1);f=this.getRecordIndex(d);g=g.getPageRecords()[1];f<=g&&this.render();this.fireEvent("rowAddEvent",{record:d})}else{f=this.getRecordIndex(d);if(a.isNumber(f)){this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){var b=a.record,a=a.recIndex,e=this._addTrEl(b);if(e){var c=this._elTbody.rows[a]?this._elTbody.rows[a]:null;this._elTbody.insertBefore(e,c);a===0&&this._setFirstRow();c===null&&this._setLastRow();this._setRowStripes();
+this.hideTableMessage();this.fireEvent("rowAddEvent",{record:b})}}},argument:{record:d,recIndex:f},scope:this,timeout:this.get("renderLoopSize")>0?0:-1});this._runRenderChain()}}}}},addRows:function(e,c){if((!a.isNumber(c)||!(c<0||c>this._oRecordSet.getLength()))&&a.isArray(e)){var d=this._oRecordSet.addRecords(e,c);if(d){var f=this.getRecordIndex(d[0]),g=this.get("paginator");if(g){var j=g.get("totalRecords");j!==b.Paginator.VALUE_UNLIMITED&&g.set("totalRecords",j+d.length);g=g.getPageRecords()[1];
+f<=g&&this.render();this.fireEvent("rowsAddEvent",{records:d})}else{var n=this.get("renderLoopSize"),o=f+e.length,g=f>=this._elTbody.rows.length;this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){for(var b=a.aRecords,e=a.nCurrentRow,c=a.nCurrentRecord,d=n>0?Math.min(e+n,o):o,i=document.createDocumentFragment(),f=this._elTbody.rows[e]?this._elTbody.rows[e]:null;e<d;e++,c++)i.appendChild(this._addTrEl(b[c]));this._elTbody.insertBefore(i,f);a.nCurrentRow=e;a.nCurrentRecord=c}},
+iterations:n>0?Math.ceil(o/n):1,argument:{nCurrentRow:f,nCurrentRecord:0,aRecords:d},scope:this,timeout:n>0?0:-1});this._oChainRender.add({method:function(a){a.recIndex===0&&this._setFirstRow();a.isLast&&this._setLastRow();this._setRowStripes();this.fireEvent("rowsAddEvent",{records:d})},argument:{recIndex:f,isLast:g},scope:this,timeout:-1});this._runRenderChain();this.hideTableMessage()}}}},updateRow:function(b,c){var d=b;a.isNumber(d)||(d=this.getRecordIndex(b));if(a.isNumber(d)&&d>=0){var f=this._oRecordSet.getRecord(d);
+if(f){var g=this._oRecordSet.setRecord(c,d),j=this.getTrEl(f),n=f?f.getData():null;if(g){for(var o=this._aSelections||[],m=0,f=f.getId(),r=g.getId();m<o.length;m++)if(o[m]===f)o[m]=r;else if(o[m].recordId===f)o[m].recordId=r;if(this._oAnchorRecord&&this._oAnchorRecord.getId()===f)this._oAnchorRecord=g;if(this._oAnchorCell&&this._oAnchorCell.record.getId()===f)this._oAnchorCell.record=g;this._oChainRender.add({method:function(){if(this instanceof h&&this._sId){var a=this.get("paginator");if(a){var b=
+a.getPageRecords()[0],a=a.getPageRecords()[1];(d>=b||d<=a)&&this.render()}else j?this._updateTrEl(j,g):this.getTbodyEl().appendChild(this._addTrEl(g));this.fireEvent("rowUpdateEvent",{record:g,oldData:n})}},scope:this,timeout:this.get("renderLoopSize")>0?0:-1});this._runRenderChain()}}}},updateRows:function(b,c){if(a.isArray(c)){var d=b,f=this._oRecordSet,g=f.getLength();a.isNumber(b)||(d=this.getRecordIndex(b));if(a.isNumber(d)&&d>=0&&d<f.getLength()){var j=d+c.length,n=f.getRecords(d,c.length),
+o=f.setRecords(c,d);if(o){for(var f=this._aSelections||[],m=0,r,s,t,u,w=this._oAnchorRecord?this._oAnchorRecord.getId():null,x=this._oAnchorCell?this._oAnchorCell.record.getId():null;m<n.length;m++){u=n[m].getId();s=o[m];t=s.getId();for(r=0;r<f.length;r++)if(f[r]===u)f[r]=t;else if(f[r].recordId===u)f[r].recordId=t;if(w&&w===u)this._oAnchorRecord=s;if(x&&x===u)this._oAnchorCell.record=s}if(m=this.get("paginator")){f=m.getPageRecords()[0];m=m.getPageRecords()[1];(d>=f||j<=m)&&this.render();this.fireEvent("rowsAddEvent",
+{newRecords:o,oldRecords:n})}else{var v=this.get("renderLoopSize"),f=c.length,m=j>=g,y=j>g;this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){for(var b=a.aRecords,e=a.nCurrentRow,c=a.nDataPointer,i=v>0?Math.min(e+v,d+b.length):d+b.length;e<i;e++,c++)y&&e>=g?this._elTbody.appendChild(this._addTrEl(b[c])):this._updateTrEl(this._elTbody.rows[e],b[c]);a.nCurrentRow=e;a.nDataPointer=c}},iterations:v>0?Math.ceil(f/v):1,argument:{nCurrentRow:d,aRecords:o,nDataPointer:0,isAdding:y},
+scope:this,timeout:v>0?0:-1});this._oChainRender.add({method:function(a){a.recIndex===0&&this._setFirstRow();a.isLast&&this._setLastRow();this._setRowStripes();this.fireEvent("rowsAddEvent",{newRecords:o,oldRecords:n})},argument:{recIndex:d,isLast:m},scope:this,timeout:-1});this._runRenderChain();this.hideTableMessage()}}}}},deleteRow:function(e){var c=a.isNumber(e)?e:this.getRecordIndex(e);if(a.isNumber(c))if(e=this.getRecord(c)){for(var d=this.getTrIndex(c),e=e.getId(),f=this._aSelections||[],g=
+f.length-1;g>-1;g--)(a.isString(f[g])&&f[g]===e||a.isObject(f[g])&&f[g].recordId===e)&&f.splice(g,1);var j=this._oRecordSet.deleteRecord(c);if(j)if(e=this.get("paginator")){f=e.get("totalRecords");g=e.getPageRecords();f!==b.Paginator.VALUE_UNLIMITED&&e.set("totalRecords",f-1);(!g||c<=g[1])&&this.render();this._oChainRender.add({method:function(){this instanceof h&&this._sId&&this.fireEvent("rowDeleteEvent",{recordIndex:c,oldData:j,trElIndex:d})},scope:this,timeout:this.get("renderLoopSize")>0?0:-1});
+this._runRenderChain()}else if(a.isNumber(d)){this._oChainRender.add({method:function(){if(this instanceof h&&this._sId){var a=c===this._oRecordSet.getLength();this._deleteTrEl(d);if(this._elTbody.rows.length>0){d===0&&this._setFirstRow();a&&this._setLastRow();d!=this._elTbody.rows.length&&this._setRowStripes(d)}this.fireEvent("rowDeleteEvent",{recordIndex:c,oldData:j,trElIndex:d})}},scope:this,timeout:this.get("renderLoopSize")>0?0:-1});this._runRenderChain();return}}return null},deleteRows:function(e,
+c){var d=a.isNumber(e)?e:this.getRecordIndex(e);if(a.isNumber(d)){var f=this.getRecord(d);if(f){for(var g=this.getTrIndex(d),f=f.getId(),j=this._aSelections||[],n=j.length-1;n>-1;n--)(a.isString(j[n])&&j[n]===f||a.isObject(j[n])&&j[n].recordId===f)&&j.splice(n,1);var o=f=d;if(c&&a.isNumber(c)){f=c>0?d+c-1:d;o=c>0?d:d+c+1;c=c>0?c:c*-1;if(o<0){o=0;c=f-o+1}}else c=1;var m=this._oRecordSet.deleteRecords(o,c);if(m){var d=this.get("paginator"),r=this.get("renderLoopSize");if(d){g=d.get("totalRecords");
+f=d.getPageRecords();g!==b.Paginator.VALUE_UNLIMITED&&d.set("totalRecords",g-m.length);(!f||o<=f[1])&&this.render();this._oChainRender.add({method:function(){this instanceof h&&this._sId&&this.fireEvent("rowsDeleteEvent",{recordIndex:o,oldData:m,count:c})},scope:this,timeout:r>0?0:-1});this._runRenderChain();return}if(a.isNumber(g)){var s=o;this._oChainRender.add({method:function(a){if(this instanceof h&&this._sId){for(var b=a.nCurrentRow,e=r>0?Math.max(b-r,s)-1:s-1;b>e;--b)this._deleteTrEl(b);a.nCurrentRow=
+b}},iterations:r>0?Math.ceil(c/r):1,argument:{nCurrentRow:f},scope:this,timeout:r>0?0:-1});this._oChainRender.add({method:function(){if(this._elTbody.rows.length>0){this._setFirstRow();this._setLastRow();this._setRowStripes()}this.fireEvent("rowsDeleteEvent",{recordIndex:o,oldData:m,count:c})},scope:this,timeout:-1});this._runRenderChain();return}}}}return null},formatCell:function(a,b,c){b||(b=this.getRecord(a));c||(c=this.getColumn(this.getCellIndex(a.parentNode)));if(b&&c){var d=b.getData(c.field),
+f=typeof c.formatter==="function"?c.formatter:h.Formatter[c.formatter+""]||h.Formatter.defaultFormatter;f?f.call(this,a,b,c,d):a.innerHTML=d;this.fireEvent("cellFormatEvent",{record:b,column:c,key:c.key,el:a})}},updateCell:function(a,b,c,d){if((b=b instanceof YAHOO.widget.Column?b:this.getColumn(b))&&b.getField()&&a instanceof YAHOO.widget.Record){var f=b.getField(),g=a.getData(f);this._oRecordSet.updateRecordValue(a,f,c);var j=this.getTdEl({record:a,column:b});if(j){this._oChainRender.add({method:function(){if(this instanceof
+h&&this._sId){this.formatCell(j.firstChild,a,b);this.fireEvent("cellUpdateEvent",{record:a,column:b,oldData:g})}},scope:this,timeout:this.get("renderLoopSize")>0?0:-1});d||this._runRenderChain()}else this.fireEvent("cellUpdateEvent",{record:a,column:b,oldData:g})}},_updatePaginator:function(a){var b=this.get("paginator");b&&a!==b&&b.unsubscribe("changeRequest",this.onPaginatorChangeRequest,this,true);a&&a.subscribe("changeRequest",this.onPaginatorChangeRequest,this,true)},_handlePaginatorChange:function(a){if(a.prevValue!==
+a.newValue){var b=a.newValue,c=a.prevValue,a=this._defaultPaginatorContainers();if(c){c.getContainerNodes()[0]==a[0]&&c.set("containers",[]);c.destroy();if(a[0])if(b&&!b.getContainerNodes().length)b.set("containers",a);else for(c=a.length-1;c>=0;--c)a[c]&&a[c].parentNode.removeChild(a[c])}this._bInit||this.render();b&&this.renderPaginator()}},_defaultPaginatorContainers:function(a){var b=this._sId+"-paginator0",c=this._sId+"-paginator1",d=f.get(b),g=f.get(c);if(a&&(!d||!g)){if(!d){d=document.createElement("div");
+d.id=b;f.addClass(d,h.CLASS_PAGINATOR);this._elContainer.insertBefore(d,this._elContainer.firstChild)}if(!g){g=document.createElement("div");g.id=c;f.addClass(g,h.CLASS_PAGINATOR);this._elContainer.appendChild(g)}}return[d,g]},_destroyPaginator:function(){var a=this.get("paginator");a&&a.destroy()},renderPaginator:function(){var a=this.get("paginator");if(a){a.getContainerNodes().length||a.set("containers",this._defaultPaginatorContainers(true));a.render()}},doBeforePaginatorChange:function(){this.showTableMessage(this.get("MSG_LOADING"),
+h.CLASS_LOADING);return true},onPaginatorChangeRequest:function(a){if(this.doBeforePaginatorChange(a))if(this.get("dynamicData")){var b=this.getState();b.pagination=a;a=this.get("generateRequest")(b,this);this.unselectAllRows();this.unselectAllCells();this._oDataSource.sendRequest(a,{success:this.onDataReturnSetRows,failure:this.onDataReturnSetRows,argument:b,scope:this})}else{a.paginator.setStartIndex(a.recordOffset,true);a.paginator.setRowsPerPage(a.rowsPerPage,true);this.render()}},_elLastHighlightedTd:null,
+_aSelections:null,_oAnchorRecord:null,_oAnchorCell:null,_unselectAllTrEls:function(){var a=f.getElementsByClassName(h.CLASS_SELECTED,"tr",this._elTbody);f.removeClass(a,h.CLASS_SELECTED)},_getSelectionTrigger:function(){var a=this.get("selectionMode"),b={},c,d,f,g;if(a=="cellblock"||a=="cellrange"||a=="singlecell"){if(a=this.getLastSelectedCell()){c=this.getRecord(a.recordId);d=this.getRecordIndex(c);f=this.getTrEl(c);g=this.getTrIndex(f);if(g===null)return null;b.record=c;b.recordIndex=d;b.el=this.getTdEl(a);
+b.trIndex=g;b.column=this.getColumn(a.columnKey);b.colKeyIndex=b.column.getKeyIndex();b.cell=a;return b}}else if(c=this.getLastSelectedRecord()){c=this.getRecord(c);d=this.getRecordIndex(c);f=this.getTrEl(c);g=this.getTrIndex(f);if(g===null)return null;b.record=c;b.recordIndex=d;b.el=f;b.trIndex=g;return b}return null},_getSelectionAnchor:function(a){var b=this.get("selectionMode"),c={},d;if(b=="cellblock"||b=="cellrange"||b=="singlecell"){var f=this._oAnchorCell;if(!f)if(a)f=this._oAnchorCell=a.cell;
+else return null;b=this._oAnchorCell.record;a=this._oRecordSet.getRecordIndex(b);d=this.getTrIndex(b);d===null&&(d=a<this.getRecordIndex(this.getFirstTrEl())?0:this.getRecordIndex(this.getLastTrEl()));c.record=b;c.recordIndex=a;c.trIndex=d;c.column=this._oAnchorCell.column;c.colKeyIndex=c.column.getKeyIndex();c.cell=f}else{b=this._oAnchorRecord;if(!b)if(a)b=this._oAnchorRecord=a.record;else return null;a=this.getRecordIndex(b);d=this.getTrIndex(b);d===null&&(d=a<this.getRecordIndex(this.getFirstTrEl())?
+0:this.getRecordIndex(this.getLastTrEl()));c.record=b;c.recordIndex=a;c.trIndex=d}return c},_handleStandardSelectionByMouse:function(a){var b=this.getTrEl(a.target);if(b){var c=a.event,d=c.shiftKey,c=c.ctrlKey||navigator.userAgent.toLowerCase().indexOf("mac")!=-1&&c.metaKey,b=this.getRecord(b),f=this._oRecordSet.getRecordIndex(b),g=this._getSelectionAnchor();if(d&&c)if(g)if(this.isSelected(g.record))if(g.recordIndex<f)for(a=g.recordIndex+1;a<=f;a++)this.isSelected(a)||this.selectRow(a);else for(a=
+g.recordIndex-1;a>=f;a--)this.isSelected(a)||this.selectRow(a);else{if(g.recordIndex<f)for(a=g.recordIndex+1;a<=f-1;a++)this.isSelected(a)&&this.unselectRow(a);else for(a=f+1;a<=g.recordIndex-1;a++)this.isSelected(a)&&this.unselectRow(a);this.selectRow(b)}else{this._oAnchorRecord=b;this.isSelected(b)?this.unselectRow(b):this.selectRow(b)}else if(d){this.unselectAllRows();if(g)if(g.recordIndex<f)for(a=g.recordIndex;a<=f;a++)this.selectRow(a);else for(a=g.recordIndex;a>=f;a--)this.selectRow(a);else{this._oAnchorRecord=
+b;this.selectRow(b)}}else if(c){this._oAnchorRecord=b;this.isSelected(b)?this.unselectRow(b):this.selectRow(b)}else this._handleSingleSelectionByMouse(a)}},_handleStandardSelectionByKey:function(a){var b=g.getCharCode(a);if(b==38||b==40){var c=a.shiftKey,d=this._getSelectionTrigger();if(!d)return null;g.stopEvent(a);var f=this._getSelectionAnchor(d);c?b==40&&f.recordIndex<=d.trIndex?this.selectRow(this.getNextTrEl(d.el)):b==38&&f.recordIndex>=d.trIndex?this.selectRow(this.getPreviousTrEl(d.el)):this.unselectRow(d.el):
+this._handleSingleSelectionByKey(a)}},_handleSingleSelectionByMouse:function(a){if(a=this.getTrEl(a.target)){this._oAnchorRecord=a=this.getRecord(a);this.unselectAllRows();this.selectRow(a)}},_handleSingleSelectionByKey:function(a){var b=g.getCharCode(a);if(b==38||b==40){var c=this._getSelectionTrigger();if(!c)return null;g.stopEvent(a);var d;if(b==38){d=this.getPreviousTrEl(c.el);d===null&&(d=this.getFirstTrEl())}else if(b==40){d=this.getNextTrEl(c.el);d===null&&(d=this.getLastTrEl())}this.unselectAllRows();
+this.selectRow(d);this._oAnchorRecord=this.getRecord(d)}},_handleCellBlockSelectionByMouse:function(a){var b=this.getTdEl(a.target);if(b){var c=a.event,d=c.shiftKey,f=c.ctrlKey||navigator.userAgent.toLowerCase().indexOf("mac")!=-1&&c.metaKey,g=this.getTrEl(b),c=this.getTrIndex(g),h=this.getColumn(b),j=h.getKeyIndex(),m=this.getRecord(g),r=this._oRecordSet.getRecordIndex(m),s={record:m,column:h},h=this._getSelectionAnchor(),m=this.getTbodyEl().rows;if(d&&f)if(h)if(this.isSelected(h.cell))if(h.recordIndex===
+r)if(h.colKeyIndex<j)for(a=h.colKeyIndex+1;a<=j;a++)this.selectCell(g.cells[a]);else{if(j<h.colKeyIndex)for(a=j;a<h.colKeyIndex;a++)this.selectCell(g.cells[a])}else if(h.recordIndex<r){b=Math.min(h.colKeyIndex,j);j=Math.max(h.colKeyIndex,j);for(a=h.trIndex;a<=c;a++)for(d=b;d<=j;d++)this.selectCell(m[a].cells[d])}else{b=Math.min(h.trIndex,j);j=Math.max(h.trIndex,j);for(a=h.trIndex;a>=c;a--)for(d=j;d>=b;d--)this.selectCell(m[a].cells[d])}else{if(h.recordIndex===r)if(h.colKeyIndex<j)for(a=h.colKeyIndex+
+1;a<j;a++)this.unselectCell(g.cells[a]);else if(j<h.colKeyIndex)for(a=j+1;a<h.colKeyIndex;a++)this.unselectCell(g.cells[a]);if(h.recordIndex<r)for(a=h.trIndex;a<=c;a++){g=m[a];for(d=0;d<g.cells.length;d++)g.sectionRowIndex===h.trIndex?d>h.colKeyIndex&&this.unselectCell(g.cells[d]):g.sectionRowIndex===c?d<j&&this.unselectCell(g.cells[d]):this.unselectCell(g.cells[d])}else for(a=c;a<=h.trIndex;a++){g=m[a];for(d=0;d<g.cells.length;d++)g.sectionRowIndex==c?d>j&&this.unselectCell(g.cells[d]):g.sectionRowIndex==
+h.trIndex?d<h.colKeyIndex&&this.unselectCell(g.cells[d]):this.unselectCell(g.cells[d])}this.selectCell(b)}else{this._oAnchorCell=s;this.isSelected(s)?this.unselectCell(s):this.selectCell(s)}else if(d){this.unselectAllCells();if(h)if(h.recordIndex===r)if(h.colKeyIndex<j)for(a=h.colKeyIndex;a<=j;a++)this.selectCell(g.cells[a]);else{if(j<h.colKeyIndex)for(a=j;a<=h.colKeyIndex;a++)this.selectCell(g.cells[a])}else if(h.recordIndex<r){b=Math.min(h.colKeyIndex,j);j=Math.max(h.colKeyIndex,j);for(a=h.trIndex;a<=
+c;a++)for(d=b;d<=j;d++)this.selectCell(m[a].cells[d])}else{b=Math.min(h.colKeyIndex,j);j=Math.max(h.colKeyIndex,j);for(a=c;a<=h.trIndex;a++)for(d=b;d<=j;d++)this.selectCell(m[a].cells[d])}else{this._oAnchorCell=s;this.selectCell(s)}}else if(f){this._oAnchorCell=s;this.isSelected(s)?this.unselectCell(s):this.selectCell(s)}else this._handleSingleCellSelectionByMouse(a)}},_handleCellBlockSelectionByKey:function(a){var b=g.getCharCode(a),c=a.shiftKey;if(b==9||!c)this._handleSingleCellSelectionByKey(a);
+else if(b>36&&b<41){c=this._getSelectionTrigger();if(!c)return null;g.stopEvent(a);var d=this._getSelectionAnchor(c),f,h,a=this.getTbodyEl().rows;h=c.el.parentNode;if(b==40)if(d.recordIndex<=c.recordIndex){if(a=this.getNextTrEl(c.el)){f=d.colKeyIndex;b=c.colKeyIndex;if(f>b)for(d=f;d>=b;d--){h=a.cells[d];this.selectCell(h)}else for(d=f;d<=b;d++){h=a.cells[d];this.selectCell(h)}}}else{f=Math.min(d.colKeyIndex,c.colKeyIndex);b=Math.max(d.colKeyIndex,c.colKeyIndex);for(d=f;d<=b;d++)this.unselectCell(h.cells[d])}else if(b==
+38)if(d.recordIndex>=c.recordIndex){if(a=this.getPreviousTrEl(c.el)){f=d.colKeyIndex;b=c.colKeyIndex;if(f>b)for(d=f;d>=b;d--){h=a.cells[d];this.selectCell(h)}else for(d=f;d<=b;d++){h=a.cells[d];this.selectCell(h)}}}else{f=Math.min(d.colKeyIndex,c.colKeyIndex);b=Math.max(d.colKeyIndex,c.colKeyIndex);for(d=f;d<=b;d++)this.unselectCell(h.cells[d])}else if(b==39)if(d.colKeyIndex<=c.colKeyIndex){if(c.colKeyIndex<h.cells.length-1){f=d.trIndex;b=c.trIndex;if(f>b)for(d=f;d>=b;d--){h=a[d].cells[c.colKeyIndex+
+1];this.selectCell(h)}else for(d=f;d<=b;d++){h=a[d].cells[c.colKeyIndex+1];this.selectCell(h)}}}else{f=Math.min(d.trIndex,c.trIndex);b=Math.max(d.trIndex,c.trIndex);for(d=f;d<=b;d++)this.unselectCell(a[d].cells[c.colKeyIndex])}else if(b==37)if(d.colKeyIndex>=c.colKeyIndex){if(c.colKeyIndex>0){f=d.trIndex;b=c.trIndex;if(f>b)for(d=f;d>=b;d--){h=a[d].cells[c.colKeyIndex-1];this.selectCell(h)}else for(d=f;d<=b;d++){h=a[d].cells[c.colKeyIndex-1];this.selectCell(h)}}}else{f=Math.min(d.trIndex,c.trIndex);
+b=Math.max(d.trIndex,c.trIndex);for(d=f;d<=b;d++)this.unselectCell(a[d].cells[c.colKeyIndex])}}},_handleCellRangeSelectionByMouse:function(a){var b=this.getTdEl(a.target);if(b){var c=a.event,d=c.shiftKey,f=c.ctrlKey||navigator.userAgent.toLowerCase().indexOf("mac")!=-1&&c.metaKey,g=this.getTrEl(b),c=this.getTrIndex(g),h=this.getColumn(b),j=h.getKeyIndex(),m=this.getRecord(g),r=this._oRecordSet.getRecordIndex(m),s={record:m,column:h},h=this._getSelectionAnchor(),m=this.getTbodyEl().rows;if(d&&f)if(h)if(this.isSelected(h.cell))if(h.recordIndex===
+r)if(h.colKeyIndex<j)for(a=h.colKeyIndex+1;a<=j;a++)this.selectCell(g.cells[a]);else{if(j<h.colKeyIndex)for(a=j;a<h.colKeyIndex;a++)this.selectCell(g.cells[a])}else if(h.recordIndex<r){for(a=h.colKeyIndex+1;a<g.cells.length;a++)this.selectCell(g.cells[a]);for(a=h.trIndex+1;a<c;a++)for(d=0;d<m[a].cells.length;d++)this.selectCell(m[a].cells[d]);for(a=0;a<=j;a++)this.selectCell(g.cells[a])}else{for(a=j;a<g.cells.length;a++)this.selectCell(g.cells[a]);for(a=c+1;a<h.trIndex;a++)for(d=0;d<m[a].cells.length;d++)this.selectCell(m[a].cells[d]);
+for(a=0;a<h.colKeyIndex;a++)this.selectCell(g.cells[a])}else{if(h.recordIndex===r)if(h.colKeyIndex<j)for(a=h.colKeyIndex+1;a<j;a++)this.unselectCell(g.cells[a]);else if(j<h.colKeyIndex)for(a=j+1;a<h.colKeyIndex;a++)this.unselectCell(g.cells[a]);if(h.recordIndex<r)for(a=h.trIndex;a<=c;a++){g=m[a];for(d=0;d<g.cells.length;d++)g.sectionRowIndex===h.trIndex?d>h.colKeyIndex&&this.unselectCell(g.cells[d]):g.sectionRowIndex===c?d<j&&this.unselectCell(g.cells[d]):this.unselectCell(g.cells[d])}else for(a=
+c;a<=h.trIndex;a++){g=m[a];for(d=0;d<g.cells.length;d++)g.sectionRowIndex==c?d>j&&this.unselectCell(g.cells[d]):g.sectionRowIndex==h.trIndex?d<h.colKeyIndex&&this.unselectCell(g.cells[d]):this.unselectCell(g.cells[d])}this.selectCell(b)}else{this._oAnchorCell=s;this.isSelected(s)?this.unselectCell(s):this.selectCell(s)}else if(d){this.unselectAllCells();if(h)if(h.recordIndex===r)if(h.colKeyIndex<j)for(a=h.colKeyIndex;a<=j;a++)this.selectCell(g.cells[a]);else{if(j<h.colKeyIndex)for(a=j;a<=h.colKeyIndex;a++)this.selectCell(g.cells[a])}else if(h.recordIndex<
+r)for(a=h.trIndex;a<=c;a++){g=m[a];for(d=0;d<g.cells.length;d++)g.sectionRowIndex==h.trIndex?d>=h.colKeyIndex&&this.selectCell(g.cells[d]):g.sectionRowIndex==c?d<=j&&this.selectCell(g.cells[d]):this.selectCell(g.cells[d])}else for(a=c;a<=h.trIndex;a++){g=m[a];for(d=0;d<g.cells.length;d++)g.sectionRowIndex==c?d>=j&&this.selectCell(g.cells[d]):g.sectionRowIndex==h.trIndex?d<=h.colKeyIndex&&this.selectCell(g.cells[d]):this.selectCell(g.cells[d])}else{this._oAnchorCell=s;this.selectCell(s)}}else if(f){this._oAnchorCell=
+s;this.isSelected(s)?this.unselectCell(s):this.selectCell(s)}else this._handleSingleCellSelectionByMouse(a)}},_handleCellRangeSelectionByKey:function(a){var b=g.getCharCode(a),c=a.shiftKey;if(b==9||!c)this._handleSingleCellSelectionByKey(a);else if(b>36&&b<41){c=this._getSelectionTrigger();if(!c)return null;g.stopEvent(a);var d=this._getSelectionAnchor(c),f;f=this.getTbodyEl().rows;a=c.el.parentNode;if(b==40){b=this.getNextTrEl(c.el);if(d.recordIndex<=c.recordIndex){for(d=c.colKeyIndex+1;d<a.cells.length;d++){f=
+a.cells[d];this.selectCell(f)}if(b)for(d=0;d<=c.colKeyIndex;d++){f=b.cells[d];this.selectCell(f)}}else{for(d=c.colKeyIndex;d<a.cells.length;d++)this.unselectCell(a.cells[d]);if(b)for(d=0;d<c.colKeyIndex;d++)this.unselectCell(b.cells[d])}}else if(b==38){b=this.getPreviousTrEl(c.el);if(d.recordIndex>=c.recordIndex){for(d=c.colKeyIndex-1;d>-1;d--){f=a.cells[d];this.selectCell(f)}if(b)for(d=a.cells.length-1;d>=c.colKeyIndex;d--){f=b.cells[d];this.selectCell(f)}}else{for(d=c.colKeyIndex;d>-1;d--)this.unselectCell(a.cells[d]);
+if(b)for(d=a.cells.length-1;d>c.colKeyIndex;d--)this.unselectCell(b.cells[d])}}else if(b==39){b=this.getNextTrEl(c.el);if(d.recordIndex<c.recordIndex)if(c.colKeyIndex<a.cells.length-1){f=a.cells[c.colKeyIndex+1];this.selectCell(f)}else{if(b){f=b.cells[0];this.selectCell(f)}}else if(d.recordIndex>c.recordIndex)this.unselectCell(a.cells[c.colKeyIndex]);else if(d.colKeyIndex<=c.colKeyIndex)if(c.colKeyIndex<a.cells.length-1){f=a.cells[c.colKeyIndex+1];this.selectCell(f)}else{if(c.trIndex<f.length-1){f=
+b.cells[0];this.selectCell(f)}}else this.unselectCell(a.cells[c.colKeyIndex])}else if(b==37){b=this.getPreviousTrEl(c.el);if(d.recordIndex<c.recordIndex)this.unselectCell(a.cells[c.colKeyIndex]);else if(d.recordIndex>c.recordIndex)if(c.colKeyIndex>0){f=a.cells[c.colKeyIndex-1];this.selectCell(f)}else{if(c.trIndex>0){f=b.cells[b.cells.length-1];this.selectCell(f)}}else if(d.colKeyIndex>=c.colKeyIndex)if(c.colKeyIndex>0){f=a.cells[c.colKeyIndex-1];this.selectCell(f)}else{if(c.trIndex>0){f=b.cells[b.cells.length-
+1];this.selectCell(f)}}else this.unselectCell(a.cells[c.colKeyIndex])}}},_handleSingleCellSelectionByMouse:function(a){var b=this.getTdEl(a.target);if(b){a=this.getRecord(this.getTrEl(b));b=this.getColumn(b);this._oAnchorCell=a={record:a,column:b};this.unselectAllCells();this.selectCell(a)}},_handleSingleCellSelectionByKey:function(a){var b=g.getCharCode(a);if(b==9||b>36&&b<41){var c=a.shiftKey,d=this._getSelectionTrigger();if(!d)return null;var f;if(b==40){f=this.getBelowTdEl(d.el);if(f===null)f=
+d.el}else if(b==38){f=this.getAboveTdEl(d.el);if(f===null)f=d.el}else if(b==39||!c&&b==9){f=this.getNextTdEl(d.el);if(f===null)return}else if(b==37||c&&b==9){f=this.getPreviousTdEl(d.el);if(f===null)return}g.stopEvent(a);this.unselectAllCells();this.selectCell(f);this._oAnchorCell={record:this.getRecord(f),column:this.getColumn(f)}}},getSelectedTrEls:function(){return f.getElementsByClassName(h.CLASS_SELECTED,"tr",this._elTbody)},selectRow:function(b){var c;if(b instanceof YAHOO.widget.Record){b=
+this._oRecordSet.getRecord(b);c=this.getTrEl(b)}else if(a.isNumber(b)){b=this.getRecord(b);c=this.getTrEl(b)}else{c=this.getTrEl(b);b=this.getRecord(c)}if(b){var d=this._aSelections||[],g=b.getId(),j=-1;if(d.indexOf)j=d.indexOf(g);else for(var p=d.length-1;p>-1;p--)if(d[p]===g){j=p;break}j>-1&&d.splice(j,1);d.push(g);this._aSelections=d;if(!this._oAnchorRecord)this._oAnchorRecord=b;c&&f.addClass(c,h.CLASS_SELECTED);this.fireEvent("rowSelectEvent",{record:b,el:c})}},unselectRow:function(b){var c=this.getTrEl(b);
+if(b=b instanceof YAHOO.widget.Record?this._oRecordSet.getRecord(b):a.isNumber(b)?this.getRecord(b):this.getRecord(c)){var d=this._aSelections||[],g=b.getId(),j=-1;if(d.indexOf)j=d.indexOf(g);else for(var p=d.length-1;p>-1;p--)if(d[p]===g){j=p;break}if(j>-1){d.splice(j,1);this._aSelections=d;f.removeClass(c,h.CLASS_SELECTED);this.fireEvent("rowUnselectEvent",{record:b,el:c})}}},unselectAllRows:function(){for(var b=this._aSelections||[],c,d=[],f=b.length-1;f>-1;f--)if(a.isString(b[f])){c=b.splice(f,
+1);d[d.length]=this.getRecord(a.isArray(c)?c[0]:c)}this._aSelections=b;this._unselectAllTrEls();this.fireEvent("unselectAllRowsEvent",{records:d})},_unselectAllTdEls:function(){var a=f.getElementsByClassName(h.CLASS_SELECTED,"td",this._elTbody);f.removeClass(a,h.CLASS_SELECTED)},getSelectedTdEls:function(){return f.getElementsByClassName(h.CLASS_SELECTED,"td",this._elTbody)},selectCell:function(a){if(a=this.getTdEl(a)){var b=this.getRecord(a),c=this.getColumn(this.getCellIndex(a)),d=c.getKey();if(b&&
+d){for(var g=this._aSelections||[],j=b.getId(),n=g.length-1;n>-1;n--)if(g[n].recordId===j&&g[n].columnKey===d){g.splice(n,1);break}g.push({recordId:j,columnKey:d});this._aSelections=g;if(!this._oAnchorCell)this._oAnchorCell={record:b,column:c};f.addClass(a,h.CLASS_SELECTED);this.fireEvent("cellSelectEvent",{record:b,column:c,key:d,el:a})}}},unselectCell:function(a){if(a=this.getTdEl(a)){var b=this.getRecord(a),c=this.getColumn(this.getCellIndex(a)),d=c.getKey();if(b&&d)for(var g=this._aSelections||
+[],j=b.getId(),n=g.length-1;n>-1;n--)if(g[n].recordId===j&&g[n].columnKey===d){g.splice(n,1);this._aSelections=g;f.removeClass(a,h.CLASS_SELECTED);this.fireEvent("cellUnselectEvent",{record:b,column:c,key:d,el:a});break}}},unselectAllCells:function(){for(var b=this._aSelections||[],c=b.length-1;c>-1;c--)a.isObject(b[c])&&b.splice(c,1);this._aSelections=b;this._unselectAllTdEls();this.fireEvent("unselectAllCellsEvent")},isSelected:function(b){if(b&&b.ownerDocument==document)return f.hasClass(this.getTdEl(b),
+h.CLASS_SELECTED)||f.hasClass(this.getTrEl(b),h.CLASS_SELECTED);var c,d=this._aSelections;if(d&&d.length>0){b instanceof YAHOO.widget.Record?c=b:a.isNumber(b)&&(c=this.getRecord(b));if(c){c=c.getId();if(d.indexOf){if(d.indexOf(c)>-1)return true}else for(b=d.length-1;b>-1;b--)if(d[b]===c)return true}else if(b.record&&b.column){c=b.record.getId();for(var g=b.column.getKey(),b=d.length-1;b>-1;b--)if(d[b].recordId===c&&d[b].columnKey===g)return true}}return false},getSelectedRows:function(){for(var b=
+[],c=this._aSelections||[],d=0;d<c.length;d++)a.isString(c[d])&&b.push(c[d]);return b},getSelectedCells:function(){for(var b=[],c=this._aSelections||[],d=0;d<c.length;d++)c[d]&&a.isObject(c[d])&&b.push(c[d]);return b},getLastSelectedRecord:function(){var b=this._aSelections;if(b&&b.length>0)for(var c=b.length-1;c>-1;c--)if(a.isString(b[c]))return b[c]},getLastSelectedCell:function(){var a=this._aSelections;if(a&&a.length>0)for(var b=a.length-1;b>-1;b--)if(a[b].recordId&&a[b].columnKey)return a[b]},
+highlightRow:function(a){if(a=this.getTrEl(a)){var b=this.getRecord(a);f.addClass(a,h.CLASS_HIGHLIGHTED);this.fireEvent("rowHighlightEvent",{record:b,el:a})}},unhighlightRow:function(a){if(a=this.getTrEl(a)){var b=this.getRecord(a);f.removeClass(a,h.CLASS_HIGHLIGHTED);this.fireEvent("rowUnhighlightEvent",{record:b,el:a})}},highlightCell:function(a){if(a=this.getTdEl(a)){this._elLastHighlightedTd&&this.unhighlightCell(this._elLastHighlightedTd);var b=this.getRecord(a),c=this.getColumn(this.getCellIndex(a)),
+d=c.getKey();f.addClass(a,h.CLASS_HIGHLIGHTED);this._elLastHighlightedTd=a;this.fireEvent("cellHighlightEvent",{record:b,column:c,key:d,el:a})}},unhighlightCell:function(a){if(a=this.getTdEl(a)){var b=this.getRecord(a);f.removeClass(a,h.CLASS_HIGHLIGHTED);this._elLastHighlightedTd=null;this.fireEvent("cellUnhighlightEvent",{record:b,column:this.getColumn(this.getCellIndex(a)),key:this.getColumn(this.getCellIndex(a)).getKey(),el:a})}},addCellEditor:function(a,b){a.editor=b;a.editor.subscribe("showEvent",
+this._onEditorShowEvent,this,true);a.editor.subscribe("keydownEvent",this._onEditorKeydownEvent,this,true);a.editor.subscribe("revertEvent",this._onEditorRevertEvent,this,true);a.editor.subscribe("saveEvent",this._onEditorSaveEvent,this,true);a.editor.subscribe("cancelEvent",this._onEditorCancelEvent,this,true);a.editor.subscribe("blurEvent",this._onEditorBlurEvent,this,true);a.editor.subscribe("blockEvent",this._onEditorBlockEvent,this,true);a.editor.subscribe("unblockEvent",this._onEditorUnblockEvent,
+this,true)},getCellEditor:function(){return this._oCellEditor},showCellEditor:function(b,c,d){if(b=this.getTdEl(b))if((d=this.getColumn(b))&&d.editor){var j=this._oCellEditor;j&&(this._oCellEditor.cancel?this._oCellEditor.cancel():j.isActive&&this.cancelCellEditor());if(d.editor instanceof YAHOO.widget.BaseCellEditor){j=d.editor;if(b=j.attach(this,b)){j.render();j.move();if(b=this.doBeforeShowCellEditor(j)){j.show();this._oCellEditor=j}}}else{if(!c||!(c instanceof YAHOO.widget.Record))c=this.getRecord(b);
+if(!d||!(d instanceof YAHOO.widget.Column))d=this.getColumn(b);if(c&&d){(!this._oCellEditor||this._oCellEditor.container)&&this._initCellEditorEl();j=this._oCellEditor;j.cell=b;j.record=c;j.column=d;j.validator=d.editorOptions&&a.isFunction(d.editorOptions.validator)?d.editorOptions.validator:null;j.value=c.getData(d.key);j.defaultValue=null;var c=j.container,q=f.getX(b),p=f.getY(b);if(isNaN(q)||isNaN(p)){q=b.offsetLeft+f.getX(this._elTbody.parentNode)-this._elTbody.scrollLeft;p=b.offsetTop+f.getY(this._elTbody.parentNode)-
+this._elTbody.scrollTop+this._elThead.offsetHeight}c.style.left=q+"px";c.style.top=p+"px";this.doBeforeShowCellEditor(this._oCellEditor);c.style.display="";g.addListener(c,"keydown",function(a,b){if(a.keyCode==27){b.cancelCellEditor();b.focusTbodyEl()}else b.fireEvent("editorKeydownEvent",{editor:b._oCellEditor,event:a})},this);var n;if(a.isString(d.editor))switch(d.editor){case "checkbox":n=h.editCheckbox;break;case "date":n=h.editDate;break;case "dropdown":n=h.editDropdown;break;case "radio":n=
+h.editRadio;break;case "textarea":n=h.editTextarea;break;case "textbox":n=h.editTextbox;break;default:n=null}else if(a.isFunction(d.editor))n=d.editor;if(n){n(this._oCellEditor,this);(!d.editorOptions||!d.editorOptions.disableBtns)&&this.showCellEditorBtns(c);j.isActive=true;this.fireEvent("editorShowEvent",{editor:j})}}}}},_initCellEditorEl:function(){var a=document.createElement("div");a.id=this._sId+"-celleditor";a.style.display="none";a.tabIndex=0;f.addClass(a,h.CLASS_EDITOR);var b=f.getFirstChild(document.body),
+a=b?f.insertBefore(a,b):document.body.appendChild(a),b={};b.container=a;b.value=null;b.isActive=false;this._oCellEditor=b},doBeforeShowCellEditor:function(){return true},saveCellEditor:function(){if(this._oCellEditor)if(this._oCellEditor.save)this._oCellEditor.save();else if(this._oCellEditor.isActive){var a=this._oCellEditor.value,b=this._oCellEditor.record.getData(this._oCellEditor.column.key);if(this._oCellEditor.validator){a=this._oCellEditor.value=this._oCellEditor.validator.call(this,a,b,this._oCellEditor);
+if(a===null){this.resetCellEditor();this.fireEvent("editorRevertEvent",{editor:this._oCellEditor,oldData:b,newData:a});return}}this._oRecordSet.updateRecordValue(this._oCellEditor.record,this._oCellEditor.column.key,this._oCellEditor.value);this.formatCell(this._oCellEditor.cell.firstChild,this._oCellEditor.record,this._oCellEditor.column);this._oChainRender.add({method:function(){this.validateColumnWidths()},scope:this});this._oChainRender.run();this.resetCellEditor();this.fireEvent("editorSaveEvent",
+{editor:this._oCellEditor,oldData:b,newData:a})}},cancelCellEditor:function(){if(this._oCellEditor)if(this._oCellEditor.cancel)this._oCellEditor.cancel();else if(this._oCellEditor.isActive){this.resetCellEditor();this.fireEvent("editorCancelEvent",{editor:this._oCellEditor})}},destroyCellEditor:function(){if(this._oCellEditor){this._oCellEditor.destroy();this._oCellEditor=null}},_onEditorShowEvent:function(a){this.fireEvent("editorShowEvent",a)},_onEditorKeydownEvent:function(a){this.fireEvent("editorKeydownEvent",
+a)},_onEditorRevertEvent:function(a){this.fireEvent("editorRevertEvent",a)},_onEditorSaveEvent:function(a){this.fireEvent("editorSaveEvent",a)},_onEditorCancelEvent:function(a){this.fireEvent("editorCancelEvent",a)},_onEditorBlurEvent:function(a){this.fireEvent("editorBlurEvent",a)},_onEditorBlockEvent:function(a){this.fireEvent("editorBlockEvent",a)},_onEditorUnblockEvent:function(a){this.fireEvent("editorUnblockEvent",a)},onEditorBlurEvent:function(a){a.editor.disableBtns?a.editor.save&&a.editor.save():
+a.editor.cancel&&a.editor.cancel()},onEditorBlockEvent:function(){this.disable()},onEditorUnblockEvent:function(){this.undisable()},doBeforeLoadData:function(){return true},onEventSortColumn:function(a){var b=a.event,a=a.target;if(a=this.getThEl(a)||this.getTdEl(a)){a=this.getColumn(a);if(a.sortable){g.stopEvent(b);this.sortColumn(a)}}},onEventSelectColumn:function(a){this.selectColumn(a.target)},onEventHighlightColumn:function(a){this.highlightColumn(a.target)},onEventUnhighlightColumn:function(a){this.unhighlightColumn(a.target)},
+onEventSelectRow:function(a){this.get("selectionMode")=="single"?this._handleSingleSelectionByMouse(a):this._handleStandardSelectionByMouse(a)},onEventSelectCell:function(a){var b=this.get("selectionMode");b=="cellblock"?this._handleCellBlockSelectionByMouse(a):b=="cellrange"?this._handleCellRangeSelectionByMouse(a):this._handleSingleCellSelectionByMouse(a)},onEventHighlightRow:function(a){this.highlightRow(a.target)},onEventUnhighlightRow:function(a){this.unhighlightRow(a.target)},onEventHighlightCell:function(a){this.highlightCell(a.target)},
+onEventUnhighlightCell:function(a){this.unhighlightCell(a.target)},onEventFormatCell:function(a){if(a=this.getTdEl(a.target)){var b=this.getColumn(this.getCellIndex(a));this.formatCell(a.firstChild,this.getRecord(a),b)}},onEventShowCellEditor:function(a){this.isDisabled()||this.showCellEditor(a.target)},onEventSaveCellEditor:function(){this._oCellEditor&&(this._oCellEditor.save?this._oCellEditor.save():this.saveCellEditor())},onEventCancelCellEditor:function(){this._oCellEditor&&(this._oCellEditor.cancel?
+this._oCellEditor.cancel():this.cancelCellEditor())},onDataReturnInitializeTable:function(a,b,c){if(this instanceof h&&this._sId){this.initializeTable();this.onDataReturnSetRows(a,b,c)}},onDataReturnReplaceRows:function(b,c,d){if(this instanceof h&&this._sId){this.fireEvent("dataReturnEvent",{request:b,response:c,payload:d});var f=this.doBeforeLoadData(b,c,d),g=this.get("paginator"),j=0;if(f&&c&&!c.error&&a.isArray(c.results)){this._oRecordSet.reset();if(this.get("dynamicData"))d&&d.pagination&&a.isNumber(d.pagination.recordOffset)?
+j=d.pagination.recordOffset:g&&(j=g.getStartIndex());this._oRecordSet.setRecords(c.results,j|0);this._handleDataReturnPayload(b,c,d);this.render()}else f&&c.error&&this.showTableMessage(this.get("MSG_ERROR"),h.CLASS_ERROR)}},onDataReturnAppendRows:function(b,c,d){if(this instanceof h&&this._sId){this.fireEvent("dataReturnEvent",{request:b,response:c,payload:d});var f=this.doBeforeLoadData(b,c,d);if(f&&c&&!c.error&&a.isArray(c.results)){this.addRows(c.results);this._handleDataReturnPayload(b,c,d)}else f&&
+c.error&&this.showTableMessage(this.get("MSG_ERROR"),h.CLASS_ERROR)}},onDataReturnInsertRows:function(b,c,d){if(this instanceof h&&this._sId){this.fireEvent("dataReturnEvent",{request:b,response:c,payload:d});var f=this.doBeforeLoadData(b,c,d);if(f&&c&&!c.error&&a.isArray(c.results)){this.addRows(c.results,d?d.insertIndex:0);this._handleDataReturnPayload(b,c,d)}else f&&c.error&&this.showTableMessage(this.get("MSG_ERROR"),h.CLASS_ERROR)}},onDataReturnUpdateRows:function(b,c,d){if(this instanceof h&&
+this._sId){this.fireEvent("dataReturnEvent",{request:b,response:c,payload:d});var f=this.doBeforeLoadData(b,c,d);if(f&&c&&!c.error&&a.isArray(c.results)){this.updateRows(d?d.updateIndex:0,c.results);this._handleDataReturnPayload(b,c,d)}else f&&c.error&&this.showTableMessage(this.get("MSG_ERROR"),h.CLASS_ERROR)}},onDataReturnSetRows:function(b,c,d){if(this instanceof h&&this._sId){this.fireEvent("dataReturnEvent",{request:b,response:c,payload:d});var f=this.doBeforeLoadData(b,c,d),g=this.get("paginator"),
+j=0;if(f&&c&&!c.error&&a.isArray(c.results)){if(this.get("dynamicData")){d&&d.pagination&&a.isNumber(d.pagination.recordOffset)?j=d.pagination.recordOffset:g&&(j=g.getStartIndex());this._oRecordSet.reset()}this._oRecordSet.setRecords(c.results,j|0);this._handleDataReturnPayload(b,c,d);this.render()}else f&&c.error&&this.showTableMessage(this.get("MSG_ERROR"),h.CLASS_ERROR)}},handleDataReturnPayload:function(a,b,c){return c||{}},_handleDataReturnPayload:function(c,d,f){if(f=this.handleDataReturnPayload(c,
+d,f)){if(c=this.get("paginator")){this.get("dynamicData")?b.Paginator.isNumeric(f.totalRecords)&&c.set("totalRecords",f.totalRecords):c.set("totalRecords",this._oRecordSet.getLength());if(a.isObject(f.pagination)){c.set("rowsPerPage",f.pagination.rowsPerPage);c.set("recordOffset",f.pagination.recordOffset)}}f.sortedBy?this.set("sortedBy",f.sortedBy):f.sorting&&this.set("sortedBy",f.sorting)}},showCellEditorBtns:function(a){a=a.appendChild(document.createElement("div"));f.addClass(a,h.CLASS_BUTTON);
+var b=a.appendChild(document.createElement("button"));f.addClass(b,h.CLASS_DEFAULT);b.innerHTML="OK";g.addListener(b,"click",function(a,b){b.onEventSaveCellEditor(a,b);b.focusTbodyEl()},this,true);a=a.appendChild(document.createElement("button"));a.innerHTML="Cancel";g.addListener(a,"click",function(a,b){b.onEventCancelCellEditor(a,b);b.focusTbodyEl()},this,true)},resetCellEditor:function(){var a=this._oCellEditor.container;a.style.display="none";g.purgeElement(a,true);a.innerHTML="";this._oCellEditor.value=
+null;this._oCellEditor.isActive=false},getBody:function(){return this.getTbodyEl()},getCell:function(a){return this.getTdEl(a)},getRow:function(a){return this.getTrEl(a)},refreshView:function(){this.render()},select:function(b){a.isArray(b)||(b=[b]);for(var c=0;c<b.length;c++)this.selectRow(b[c])},onEventEditCell:function(a){this.onEventShowCellEditor(a)},_syncColWidths:function(){this.validateColumnWidths()}});h.prototype.onDataReturnSetRecords=h.prototype.onDataReturnSetRows;h.prototype.onPaginatorChange=
+h.prototype.onPaginatorChangeRequest;h.editCheckbox=function(){};h.editDate=function(){};h.editDropdown=function(){};h.editRadio=function(){};h.editTextarea=function(){};h.editTextbox=function(){}})();
+(function(){var a=YAHOO.lang,c=YAHOO.util,b=YAHOO.widget,d=YAHOO.env.ua,f=c.Dom,g=c.Event,j=b.DataTable;b.ScrollingDataTable=function(a,c,d,f){f=f||{};if(f.scrollable)f.scrollable=false;this._init();b.ScrollingDataTable.superclass.constructor.call(this,a,c,d,f);this.subscribe("columnShowEvent",this._onColumnChange)};var h=b.ScrollingDataTable;a.augmentObject(h,{CLASS_HEADER:"yui-dt-hd",CLASS_BODY:"yui-dt-bd"});a.extend(h,j,{_elHdContainer:null,_elHdTable:null,_elBdContainer:null,_elBdThead:null,_elTmpContainer:null,
+_elTmpTable:null,_bScrollbarX:null,initAttributes:function(b){b=b||{};h.superclass.initAttributes.call(this,b);this.setAttributeConfig("width",{value:null,validator:a.isString,method:function(a){if(this._elHdContainer&&this._elBdContainer){this._elHdContainer.style.width=a;this._elBdContainer.style.width=a;this._syncScrollX();this._syncScrollOverhang()}}});this.setAttributeConfig("height",{value:null,validator:a.isString,method:function(a){if(this._elHdContainer&&this._elBdContainer){this._elBdContainer.style.height=
+a;this._syncScrollX();this._syncScrollY();this._syncScrollOverhang()}}});this.setAttributeConfig("COLOR_COLUMNFILLER",{value:"#F2F2F2",validator:a.isString,method:function(a){if(this._elHdContainer)this._elHdContainer.style.backgroundColor=a}})},_init:function(){this._elTmpTable=this._elTmpContainer=this._elBdThead=this._elBdContainer=this._elHdTable=this._elHdContainer=null},_initDomElements:function(a){this._initContainerEl(a);if(this._elContainer&&this._elHdContainer&&this._elBdContainer){this._initTableEl();
+if(this._elHdTable&&this._elTable){this._initColgroupEl(this._elHdTable);this._initTheadEl(this._elHdTable,this._elTable);this._initTbodyEl(this._elTable);this._initMsgTbodyEl(this._elTable)}}return!this._elContainer||!this._elTable||!this._elColgroup||!this._elThead||!this._elTbody||!this._elMsgTbody||!this._elHdTable||!this._elBdThead?false:true},_destroyContainerEl:function(a){f.removeClass(a,j.CLASS_SCROLLABLE);h.superclass._destroyContainerEl.call(this,a);this._elBdContainer=this._elHdContainer=
+null},_initContainerEl:function(a){h.superclass._initContainerEl.call(this,a);if(this._elContainer){a=this._elContainer;f.addClass(a,j.CLASS_SCROLLABLE);var b=document.createElement("div");b.style.width=this.get("width")||"";b.style.backgroundColor=this.get("COLOR_COLUMNFILLER");f.addClass(b,h.CLASS_HEADER);this._elHdContainer=b;a.appendChild(b);b=document.createElement("div");b.style.width=this.get("width")||"";b.style.height=this.get("height")||"";f.addClass(b,h.CLASS_BODY);g.addListener(b,"scroll",
+this._onScroll,this);this._elBdContainer=b;a.appendChild(b)}},_initCaptionEl:function(){},_destroyHdTableEl:function(){var a=this._elHdTable;if(a){g.purgeElement(a,true);a.parentNode.removeChild(a);this._elBdThead=null}},_initTableEl:function(){if(this._elHdContainer){this._destroyHdTableEl();this._elHdTable=this._elHdContainer.appendChild(document.createElement("table"));g.delegate(this._elHdTable,"mouseenter",this._onTableMouseover,"thead ."+j.CLASS_LABEL,this);g.delegate(this._elHdTable,"mouseleave",
+this._onTableMouseout,"thead ."+j.CLASS_LABEL,this)}h.superclass._initTableEl.call(this,this._elBdContainer)},_initTheadEl:function(a,b){a=a||this._elHdTable;b=b||this._elTable;this._initBdTheadEl(b);h.superclass._initTheadEl.call(this,a)},_initThEl:function(a,b){h.superclass._initThEl.call(this,a,b);a.id=this.getId()+"-fixedth-"+b.getSanitizedKey()},_destroyBdTheadEl:function(){var a=this._elBdThead;if(a){var b=a.parentNode;g.purgeElement(a,true);b.removeChild(a);this._elBdThead=null;this._destroyColumnHelpers()}},
+_initBdTheadEl:function(a){if(a){this._destroyBdTheadEl();var a=a.insertBefore(document.createElement("thead"),a.firstChild),b=this._oColumnSet.tree,c,d,f,g,h,j,m;g=0;for(j=b.length;g<j;g++){d=a.appendChild(document.createElement("tr"));h=0;for(m=b[g].length;h<m;h++){f=b[g][h];c=d.appendChild(document.createElement("th"));this._initBdThEl(c,f,g,h)}}this._elBdThead=a}},_initBdThEl:function(b,c){b.id=this.getId()+"-th-"+c.getSanitizedKey();b.rowSpan=c.getRowspan();b.colSpan=c.getColspan();if(c.abbr)b.abbr=
+c.abbr;var d=c.getKey(),d=a.isValue(c.label)?c.label:d;b.innerHTML=d},_initTbodyEl:function(a){h.superclass._initTbodyEl.call(this,a);a.style.marginTop=this._elTbody.offsetTop>0?"-"+this._elTbody.offsetTop+"px":0},_focusEl:function(a){var a=a||this._elTbody,b=this;this._storeScrollPositions();setTimeout(function(){setTimeout(function(){try{a.focus();b._restoreScrollPositions()}catch(c){}},0)},0)},_runRenderChain:function(){this._storeScrollPositions();this._oChainRender.run()},_storeScrollPositions:function(){this._nScrollTop=
+this._elBdContainer.scrollTop;this._nScrollLeft=this._elBdContainer.scrollLeft},clearScrollPositions:function(){this._nScrollLeft=this._nScrollTop=0},_restoreScrollPositions:function(){if(this._nScrollTop){this._elBdContainer.scrollTop=this._nScrollTop;this._nScrollTop=null}if(this._nScrollLeft){this._elBdContainer.scrollLeft=this._nScrollLeft;this._elHdContainer.scrollLeft=this._nScrollLeft;this._nScrollLeft=null}},_validateColumnWidth:function(a,b){if(!a.width&&!a.hidden){var c=a.getThEl();a._calculatedWidth&&
+this._setColumnWidth(a,"auto","visible");if(c.offsetWidth!==b.offsetWidth){var c=c.offsetWidth>b.offsetWidth?a.getThLinerEl():b.firstChild,c=Math.max(0,c.offsetWidth-(parseInt(f.getStyle(c,"paddingLeft"),10)|0)-(parseInt(f.getStyle(c,"paddingRight"),10)|0),a.minWidth),d="visible";if(a.maxAutoWidth>0&&c>a.maxAutoWidth){c=a.maxAutoWidth;d="hidden"}this._elTbody.style.display="none";this._setColumnWidth(a,c+"px",d);a._calculatedWidth=c;this._elTbody.style.display=""}}},validateColumnWidths:function(b){var c=
+this._oColumnSet.keys,g=c.length,h=this.getFirstTrEl();d.ie&&this._setOverhangValue(1);if(c&&h&&h.childNodes.length===g){var j=this.get("width");if(j){this._elHdContainer.style.width="";this._elBdContainer.style.width=""}this._elContainer.style.width="";if(b&&a.isNumber(b.getKeyIndex()))this._validateColumnWidth(b,h.childNodes[b.getKeyIndex()]);else{var p,n=[],o;for(o=0;o<g;o++){b=c[o];!b.width&&(!b.hidden&&b._calculatedWidth)&&(n[n.length]=b)}this._elTbody.style.display="none";o=0;for(b=n.length;o<
+b;o++)this._setColumnWidth(n[o],"auto","visible");this._elTbody.style.display="";n=[];for(o=0;o<g;o++){b=c[o];p=h.childNodes[o];if(!b.width&&!b.hidden){var m=b.getThEl();if(m.offsetWidth!==p.offsetWidth){p=m.offsetWidth>p.offsetWidth?b.getThLinerEl():p.firstChild;p=Math.max(0,p.offsetWidth-(parseInt(f.getStyle(p,"paddingLeft"),10)|0)-(parseInt(f.getStyle(p,"paddingRight"),10)|0),b.minWidth);m="visible";if(b.maxAutoWidth>0&&p>b.maxAutoWidth){p=b.maxAutoWidth;m="hidden"}n[n.length]=[b,p,m]}}}this._elTbody.style.display=
+"none";o=0;for(b=n.length;o<b;o++){c=n[o];this._setColumnWidth(c[0],c[1]+"px",c[2]);c[0]._calculatedWidth=c[1]}this._elTbody.style.display=""}if(j){this._elHdContainer.style.width=j;this._elBdContainer.style.width=j}}this._syncScroll();this._restoreScrollPositions()},_syncScroll:function(){this._syncScrollX();this._syncScrollY();this._syncScrollOverhang();if(d.opera){this._elHdContainer.scrollLeft=this._elBdContainer.scrollLeft;if(!this.get("width"))document.body.style=document.body.style+""}},_syncScrollY:function(){var a=
+this._elTbody,b=this._elBdContainer;if(!this.get("width"))this._elContainer.style.width=b.scrollHeight>b.clientHeight?a.parentNode.clientWidth+19+"px":a.parentNode.clientWidth+2+"px"},_syncScrollX:function(){var a=this._elTbody,b=this._elBdContainer;if(!this.get("height")&&d.ie)b.style.height=b.scrollWidth>b.offsetWidth?a.parentNode.offsetHeight+18+"px":a.parentNode.offsetHeight+"px";this._elMsgTbody.parentNode.style.width=this._elTbody.rows.length===0?this.getTheadEl().parentNode.offsetWidth+"px":
+""},_syncScrollOverhang:function(){var a=this._elBdContainer,b=1;a.scrollHeight>a.clientHeight&&a.scrollWidth>a.clientWidth&&(b=18);this._setOverhangValue(b)},_setOverhangValue:function(a){var b=this._oColumnSet.headers[this._oColumnSet.headers.length-1]||[],c=b.length,d=this._sId+"-fixedth-",a=a+"px solid "+this.get("COLOR_COLUMNFILLER");this._elThead.style.display="none";for(var g=0;g<c;g++)f.get(d+b[g]).style.borderRight=a;this._elThead.style.display=""},getHdContainerEl:function(){return this._elHdContainer},
+getBdContainerEl:function(){return this._elBdContainer},getHdTableEl:function(){return this._elHdTable},getBdTableEl:function(){return this._elTable},disable:function(){var a=this._elMask;a.style.width=this._elBdContainer.offsetWidth+"px";a.style.height=this._elHdContainer.offsetHeight+this._elBdContainer.offsetHeight+"px";a.style.display="";this.fireEvent("disableEvent")},removeColumn:function(a){var b=this._elHdContainer.scrollLeft,c=this._elBdContainer.scrollLeft,a=h.superclass.removeColumn.call(this,
+a);this._elHdContainer.scrollLeft=b;this._elBdContainer.scrollLeft=c;return a},insertColumn:function(a,b){var c=this._elHdContainer.scrollLeft,d=this._elBdContainer.scrollLeft,f=h.superclass.insertColumn.call(this,a,b);this._elHdContainer.scrollLeft=c;this._elBdContainer.scrollLeft=d;return f},reorderColumn:function(a,b){var c=this._elHdContainer.scrollLeft,d=this._elBdContainer.scrollLeft,f=h.superclass.reorderColumn.call(this,a,b);this._elHdContainer.scrollLeft=c;this._elBdContainer.scrollLeft=
+d;return f},setColumnWidth:function(b,c){if(b=this.getColumn(b)){this._storeScrollPositions();if(a.isNumber(c)){c=c>b.minWidth?c:b.minWidth;b.width=c;this._setColumnWidth(b,c+"px");this._syncScroll();this.fireEvent("columnSetWidthEvent",{column:b,width:c})}else if(c===null){b.width=c;this._setColumnWidth(b,"auto");this.validateColumnWidths(b);this.fireEvent("columnUnsetWidthEvent",{column:b})}this._clearTrTemplateEl()}},scrollTo:function(a){var b=this.getTdEl(a);if(b){this.clearScrollPositions();
+this.getBdContainerEl().scrollLeft=b.offsetLeft;this.getBdContainerEl().scrollTop=b.parentNode.offsetTop}else if(a=this.getTrEl(a)){this.clearScrollPositions();this.getBdContainerEl().scrollTop=a.offsetTop}},showTableMessage:function(b,c){var d=this._elMsgTd;if(a.isString(b))d.firstChild.innerHTML=b;a.isString(c)&&f.addClass(d.firstChild,c);this.getTheadEl();this._elMsgTbody.parentNode.style.width=this.getTheadEl().parentNode.offsetWidth+"px";this._elMsgTbody.style.display="";this.fireEvent("tableMsgShowEvent",
+{html:b,className:c})},_onColumnChange:function(a){a=a.column?a.column:a.editor?a.editor.column:null;this._storeScrollPositions();this.validateColumnWidths(a)},_onScroll:function(a,b){b._elHdContainer.scrollLeft=b._elBdContainer.scrollLeft;if(b._oCellEditor&&b._oCellEditor.isActive){b.fireEvent("editorBlurEvent",{editor:b._oCellEditor});b.cancelCellEditor()}var c=g.getTarget(a);c.nodeName.toLowerCase();b.fireEvent("tableScrollEvent",{event:a,target:c})},_onTheadKeydown:function(a,b){g.getCharCode(a)===
+9&&setTimeout(function(){if(b instanceof h&&b._sId)b._elBdContainer.scrollLeft=b._elHdContainer.scrollLeft},0);for(var c=g.getTarget(a),d=c.nodeName.toLowerCase(),f=true;c&&d!="table";){switch(d){case "body":return;case "thead":f=b.fireEvent("theadKeyEvent",{target:c,event:a})}if(f===false)return;(c=c.parentNode)&&(d=c.nodeName.toLowerCase())}b.fireEvent("tableKeyEvent",{target:c||b._elContainer,event:a})}})})();
+(function(){var a=YAHOO.lang,c=YAHOO.util,b=YAHOO.widget,d=YAHOO.env.ua,f=c.Dom,g=c.Event,j=b.DataTable;b.BaseCellEditor=function(a,b){this._sId=this._sId||f.generateId(null,"yui-ceditor");YAHOO.widget.BaseCellEditor._nCount++;this._sType=a;this._initConfigs(b);this._initEvents();this._needsRender=true};var h=b.BaseCellEditor;a.augmentObject(h,{_nCount:0,CLASS_CELLEDITOR:"yui-ceditor"});h.prototype={_sId:null,_sType:null,_oDataTable:null,_oColumn:null,_oRecord:null,_elTd:null,_elContainer:null,_elCancelBtn:null,
+_elSaveBtn:null,_initConfigs:function(a){if(a&&YAHOO.lang.isObject(a))for(var b in a)b&&(this[b]=a[b])},_initEvents:function(){this.createEvent("showEvent");this.createEvent("keydownEvent");this.createEvent("invalidDataEvent");this.createEvent("revertEvent");this.createEvent("saveEvent");this.createEvent("cancelEvent");this.createEvent("blurEvent");this.createEvent("blockEvent");this.createEvent("unblockEvent")},_initContainerEl:function(){if(this._elContainer){YAHOO.util.Event.purgeElement(this._elContainer,
+true);this._elContainer.innerHTML=""}var b=document.createElement("div");b.id=this.getId()+"-container";b.style.display="none";b.tabIndex=0;this.className=a.isArray(this.className)?this.className:this.className?[this.className]:[];this.className[this.className.length]=j.CLASS_EDITOR;b.className=this.className.join(" ");document.body.insertBefore(b,document.body.firstChild);this._elContainer=b},_initShimEl:function(){if(this.useIFrame&&!this._elIFrame){var a=document.createElement("iframe");a.src=
+"javascript:false";a.frameBorder=0;a.scrolling="no";a.style.display="none";a.className=j.CLASS_EDITOR_SHIM;a.tabIndex=-1;a.role="presentation";a.title="Presentational iframe shim";document.body.insertBefore(a,document.body.firstChild);this._elIFrame=a}},_hide:function(){this.getContainerEl().style.display="none";if(this._elIFrame)this._elIFrame.style.display="none";this.isActive=false;this.getDataTable()._oCellEditor=null},asyncSubmitter:null,value:null,defaultValue:null,validator:null,resetInvalidData:true,
+isActive:false,LABEL_SAVE:"Save",LABEL_CANCEL:"Cancel",disableBtns:false,useIFrame:false,className:null,toString:function(){return"CellEditor instance "+this._sId},getId:function(){return this._sId},getDataTable:function(){return this._oDataTable},getColumn:function(){return this._oColumn},getRecord:function(){return this._oRecord},getTdEl:function(){return this._elTd},getContainerEl:function(){return this._elContainer},destroy:function(){this.unsubscribeAll();var a=this.getColumn();if(a)a.editor=
+null;if(a=this.getContainerEl()){g.purgeElement(a,true);a.parentNode.removeChild(a)}},render:function(){if(this._needsRender){this._initContainerEl();this._initShimEl();g.addListener(this.getContainerEl(),"keydown",function(a,b){if(a.keyCode==27){var c=g.getTarget(a);c.nodeName&&c.nodeName.toLowerCase()==="select"&&c.blur();b.cancel()}b.fireEvent("keydownEvent",{editor:b,event:a})},this);this.renderForm();this.disableBtns||this.renderBtns();this.doAfterRender();this._needsRender=false}},renderBtns:function(){var a=
+this.getContainerEl().appendChild(document.createElement("div"));a.className=j.CLASS_BUTTON;var b=a.appendChild(document.createElement("button"));b.className=j.CLASS_DEFAULT;b.innerHTML=this.LABEL_SAVE;g.addListener(b,"click",function(){this.save()},this,true);this._elSaveBtn=b;a=a.appendChild(document.createElement("button"));a.innerHTML=this.LABEL_CANCEL;g.addListener(a,"click",function(){this.cancel()},this,true);this._elCancelBtn=a},attach:function(a,b){if(a instanceof YAHOO.widget.DataTable){this._oDataTable=
+a;if(b=a.getTdEl(b)){this._elTd=b;var c=a.getColumn(b);if(c){this._oColumn=c;if(c=a.getRecord(b)){this._oRecord=c;c=c.getData(this.getColumn().getField());this.value=c!==void 0?c:this.defaultValue;return true}}}}return false},move:function(){var a=this.getContainerEl(),b=this.getTdEl(),c=f.getX(b),d=f.getY(b);if(isNaN(c)||isNaN(d)){d=this.getDataTable().getTbodyEl();c=b.offsetLeft+f.getX(d.parentNode)-d.scrollLeft;d=b.offsetTop+f.getY(d.parentNode)-d.scrollTop+this.getDataTable().getTheadEl().offsetHeight}a.style.left=
+c+"px";a.style.top=d+"px";if(this._elIFrame){this._elIFrame.style.left=c+"px";this._elIFrame.style.top=d+"px"}},show:function(){var a=this.getContainerEl(),b=this._elIFrame;this.resetForm();this.isActive=true;a.style.display="";if(b){b.style.width=a.offsetWidth+"px";b.style.height=a.offsetHeight+"px";b.style.display=""}this.focus();this.fireEvent("showEvent",{editor:this})},block:function(){this.fireEvent("blockEvent",{editor:this})},unblock:function(){this.fireEvent("unblockEvent",{editor:this})},
+save:function(){var b=this.getInputValue(),c=b;if(this.validator){c=this.validator.call(this.getDataTable(),b,this.value,this);if(c===void 0){this.resetInvalidData&&this.resetForm();this.fireEvent("invalidDataEvent",{editor:this,oldData:this.value,newData:b});return}}var d=this,b=function(a,b){var c=d.value;if(a){d.value=b;d.getDataTable().updateCell(d.getRecord(),d.getColumn(),b);d._hide();d.fireEvent("saveEvent",{editor:d,oldData:c,newData:d.value})}else{d.resetForm();d.fireEvent("revertEvent",
+{editor:d,oldData:c,newData:b})}d.unblock()};this.block();a.isFunction(this.asyncSubmitter)?this.asyncSubmitter.call(this,b,c):b(true,c)},cancel:function(){if(this.isActive){this._hide();this.fireEvent("cancelEvent",{editor:this})}},renderForm:function(){},doAfterRender:function(){},handleDisabledBtns:function(){},resetForm:function(){},focus:function(){},getInputValue:function(){}};a.augmentProto(h,c.EventProvider);b.CheckboxCellEditor=function(a){a=a||{};this._sId=this._sId||f.generateId(null,"yui-checkboxceditor");
+YAHOO.widget.BaseCellEditor._nCount++;b.CheckboxCellEditor.superclass.constructor.call(this,a.type||"checkbox",a)};a.extend(b.CheckboxCellEditor,h,{checkboxOptions:null,checkboxes:null,value:null,renderForm:function(){if(a.isArray(this.checkboxOptions)){var b,c,d,f,g;f=0;for(g=this.checkboxOptions.length;f<g;f++){b=this.checkboxOptions[f];c=a.isValue(b.value)?b.value:b;d=this.getId()+"-chk"+f;this.getContainerEl().innerHTML+='<input type="checkbox" id="'+d+'" value="'+c+'" />';c=this.getContainerEl().appendChild(document.createElement("label"));
+c.htmlFor=d;c.innerHTML=a.isValue(b.label)?b.label:b}b=[];for(f=0;f<g;f++)b[b.length]=this.getContainerEl().childNodes[f*2];this.checkboxes=b;this.disableBtns&&this.handleDisabledBtns()}},handleDisabledBtns:function(){g.addListener(this.getContainerEl(),"click",function(a){g.getTarget(a).tagName.toLowerCase()==="input"&&this.save()},this,true)},resetForm:function(){for(var b=a.isArray(this.value)?this.value:[this.value],c=0,d=this.checkboxes.length;c<d;c++){this.checkboxes[c].checked=false;for(var f=
+0,g=b.length;f<g;f++)if(this.checkboxes[c].value==b[f])this.checkboxes[c].checked=true}},focus:function(){this.checkboxes[0].focus()},getInputValue:function(){for(var a=[],b=0,c=this.checkboxes.length;b<c;b++)if(this.checkboxes[b].checked)a[a.length]=this.checkboxes[b].value;return a}});a.augmentObject(b.CheckboxCellEditor,h);b.DateCellEditor=function(a){a=a||{};this._sId=this._sId||f.generateId(null,"yui-dateceditor");YAHOO.widget.BaseCellEditor._nCount++;b.DateCellEditor.superclass.constructor.call(this,
+a.type||"date",a)};a.extend(b.DateCellEditor,h,{calendar:null,calendarOptions:null,defaultValue:new Date,renderForm:function(){if(YAHOO.widget.Calendar){var a=this.getContainerEl().appendChild(document.createElement("div"));a.id=this.getId()+"-dateContainer";var b=new YAHOO.widget.Calendar(this.getId()+"-date",a.id,this.calendarOptions);b.render();a.style.cssFloat="none";b.hideEvent.subscribe(function(){this.cancel()},this,true);if(d.ie)this.getContainerEl().appendChild(document.createElement("div")).style.clear=
+"both";this.calendar=b;this.disableBtns&&this.handleDisabledBtns()}},handleDisabledBtns:function(){this.calendar.selectEvent.subscribe(function(){this.save()},this,true)},resetForm:function(){var a=this.value||new Date;this.calendar.select(a);this.calendar.cfg.setProperty("pagedate",a,false);this.calendar.render();this.calendar.show()},focus:function(){},getInputValue:function(){return this.calendar.getSelectedDates()[0]}});a.augmentObject(b.DateCellEditor,h);b.DropdownCellEditor=function(a){a=a||
+{};this._sId=this._sId||f.generateId(null,"yui-dropdownceditor");YAHOO.widget.BaseCellEditor._nCount++;b.DropdownCellEditor.superclass.constructor.call(this,a.type||"dropdown",a)};a.extend(b.DropdownCellEditor,h,{dropdownOptions:null,dropdown:null,multiple:false,size:null,renderForm:function(){var b=this.getContainerEl().appendChild(document.createElement("select"));b.style.zoom=1;if(this.multiple)b.multiple="multiple";if(a.isNumber(this.size))b.size=this.size;this.dropdown=b;if(a.isArray(this.dropdownOptions)){for(var c,
+d,f=0,g=this.dropdownOptions.length;f<g;f++){c=this.dropdownOptions[f];d=document.createElement("option");d.value=a.isValue(c.value)?c.value:c;d.innerHTML=a.isValue(c.label)?c.label:c;b.appendChild(d)}this.disableBtns&&this.handleDisabledBtns()}},handleDisabledBtns:function(){if(this.multiple)g.addListener(this.dropdown,"blur",function(){this.save()},this,true);else if(d.ie){g.addListener(this.dropdown,"blur",function(){this.save()},this,true);g.addListener(this.dropdown,"click",function(){this.save()},
+this,true)}else g.addListener(this.dropdown,"change",function(){this.save()},this,true)},resetForm:function(){var b=this.dropdown.options,c=0,d=b.length;if(a.isArray(this.value)){for(var f=this.value,g=0,h=f.length,j={};c<d;c++){b[c].selected=false;j[b[c].value]=b[c]}for(;g<h;g++)if(j[f[g]])j[f[g]].selected=true}else for(;c<d;c++)if(this.value==b[c].value)b[c].selected=true},focus:function(){this.getDataTable()._focusEl(this.dropdown)},getInputValue:function(){var a=this.dropdown.options;if(this.multiple){for(var b=
+[],c=0,d=a.length;c<d;c++)a[c].selected&&b.push(a[c].value);return b}return a[a.selectedIndex].value}});a.augmentObject(b.DropdownCellEditor,h);b.RadioCellEditor=function(a){a=a||{};this._sId=this._sId||f.generateId(null,"yui-radioceditor");YAHOO.widget.BaseCellEditor._nCount++;b.RadioCellEditor.superclass.constructor.call(this,a.type||"radio",a)};a.extend(b.RadioCellEditor,h,{radios:null,radioOptions:null,renderForm:function(){if(a.isArray(this.radioOptions)){for(var b,c,d,f=0,g=this.radioOptions.length;f<
+g;f++){b=this.radioOptions[f];c=a.isValue(b.value)?b.value:b;d=this.getId()+"-radio"+f;this.getContainerEl().innerHTML+='<input type="radio" name="'+this.getId()+'" value="'+c+'" id="'+d+'" />';c=this.getContainerEl().appendChild(document.createElement("label"));c.htmlFor=d;c.innerHTML=a.isValue(b.label)?b.label:b}b=[];for(f=0;f<g;f++){d=this.getContainerEl().childNodes[f*2];b[b.length]=d}this.radios=b;this.disableBtns&&this.handleDisabledBtns()}},handleDisabledBtns:function(){g.addListener(this.getContainerEl(),
+"click",function(a){g.getTarget(a).tagName.toLowerCase()==="input"&&this.save()},this,true)},resetForm:function(){for(var a=0,b=this.radios.length;a<b;a++){var c=this.radios[a];if(this.value==c.value){c.checked=true;break}}},focus:function(){for(var a=0,b=this.radios.length;a<b;a++)if(this.radios[a].checked){this.radios[a].focus();break}},getInputValue:function(){for(var a=0,b=this.radios.length;a<b;a++)if(this.radios[a].checked)return this.radios[a].value}});a.augmentObject(b.RadioCellEditor,h);
+b.TextareaCellEditor=function(a){a=a||{};this._sId=this._sId||f.generateId(null,"yui-textareaceditor");YAHOO.widget.BaseCellEditor._nCount++;b.TextareaCellEditor.superclass.constructor.call(this,a.type||"textarea",a)};a.extend(b.TextareaCellEditor,h,{textarea:null,renderForm:function(){this.textarea=this.getContainerEl().appendChild(document.createElement("textarea"));this.disableBtns&&this.handleDisabledBtns()},handleDisabledBtns:function(){g.addListener(this.textarea,"blur",function(){this.save()},
+this,true)},move:function(){this.textarea.style.width=this.getTdEl().offsetWidth+"px";this.textarea.style.height="3em";YAHOO.widget.TextareaCellEditor.superclass.move.call(this)},resetForm:function(){this.textarea.value=this.value},focus:function(){this.getDataTable()._focusEl(this.textarea);this.textarea.select()},getInputValue:function(){return this.textarea.value}});a.augmentObject(b.TextareaCellEditor,h);b.TextboxCellEditor=function(a){a=a||{};this._sId=this._sId||f.generateId(null,"yui-textboxceditor");
+YAHOO.widget.BaseCellEditor._nCount++;b.TextboxCellEditor.superclass.constructor.call(this,a.type||"textbox",a)};a.extend(b.TextboxCellEditor,h,{textbox:null,renderForm:function(){var a;a=d.webkit>420?this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input")):this.getContainerEl().appendChild(document.createElement("input"));a.type="text";this.textbox=a;g.addListener(a,"keypress",function(a){if(a.keyCode===13){YAHOO.util.Event.preventDefault(a);
+this.save()}},this,true);this.disableBtns&&this.handleDisabledBtns()},move:function(){this.textbox.style.width=this.getTdEl().offsetWidth+"px";b.TextboxCellEditor.superclass.move.call(this)},resetForm:function(){this.textbox.value=a.isValue(this.value)?this.value.toString():""},focus:function(){this.getDataTable()._focusEl(this.textbox);this.textbox.select()},getInputValue:function(){return this.textbox.value}});a.augmentObject(b.TextboxCellEditor,h);j.Editors={checkbox:b.CheckboxCellEditor,date:b.DateCellEditor,
+dropdown:b.DropdownCellEditor,radio:b.RadioCellEditor,textarea:b.TextareaCellEditor,textbox:b.TextboxCellEditor};b.CellEditor=function(b,c){if(b&&j.Editors[b]){a.augmentObject(h,j.Editors[b]);return new j.Editors[b](c)}return new h(null,c)};a.augmentObject(b.CellEditor,h)})();YAHOO.register("datatable",YAHOO.widget.DataTable,{version:"2.9.0",build:"2800"});
/*
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.9.0
*/
-if(typeof YAHOO=="undefined"||!YAHOO){var YAHOO={};}YAHOO.namespace=function(){var b=arguments,g=null,e,c,f;for(e=0;e<b.length;e=e+1){f=(""+b[e]).split(".");g=YAHOO;for(c=(f[0]=="YAHOO")?1:0;c<f.length;c=c+1){g[f[c]]=g[f[c]]||{};g=g[f[c]];}}return g;};YAHOO.log=function(d,a,c){var b=YAHOO.widget.Logger;if(b&&b.log){return b.log(d,a,c);}else{return false;}};YAHOO.register=function(a,f,e){var k=YAHOO.env.modules,c,j,h,g,d;if(!k[a]){k[a]={versions:[],builds:[]};}c=k[a];j=e.version;h=e.build;g=YAHOO.env.listeners;c.name=a;c.version=j;c.build=h;c.versions.push(j);c.builds.push(h);c.mainClass=f;for(d=0;d<g.length;d=d+1){g[d](c);}if(f){f.VERSION=j;f.BUILD=h;}else{YAHOO.log("mainClass is undefined for module "+a,"warn");}};YAHOO.env=YAHOO.env||{modules:[],listeners:[]};YAHOO.env.getVersion=function(a){return YAHOO.env.modules[a]||null;};YAHOO.env.parseUA=function(d){var e=function(i){var j=0;return parseFloat(i.replace(/\./g,function(){return(j++==1)?"":".";}));},h=navigator,g={ie:0,opera:0,gecko:0,webkit:0,chrome:0,mobile:null,air:0,ipad:0,iphone:0,ipod:0,ios:null,android:0,webos:0,caja:h&&h.cajaVersion,secure:false,os:null},c=d||(navigator&&navigator.userAgent),f=window&&window.location,b=f&&f.href,a;g.secure=b&&(b.toLowerCase().indexOf("https")===0);if(c){if((/windows|win32/i).test(c)){g.os="windows";}else{if((/macintosh/i).test(c)){g.os="macintosh";}else{if((/rhino/i).test(c)){g.os="rhino";}}}if((/KHTML/).test(c)){g.webkit=1;}a=c.match(/AppleWebKit\/([^\s]*)/);if(a&&a[1]){g.webkit=e(a[1]);if(/ Mobile\//.test(c)){g.mobile="Apple";a=c.match(/OS ([^\s]*)/);if(a&&a[1]){a=e(a[1].replace("_","."));}g.ios=a;g.ipad=g.ipod=g.iphone=0;a=c.match(/iPad|iPod|iPhone/);if(a&&a[0]){g[a[0].toLowerCase()]=g.ios;}}else{a=c.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);if(a){g.mobile=a[0];}if(/webOS/.test(c)){g.mobile="WebOS";a=c.match(/webOS\/([^\s]*);/);if(a&&a[1]){g.webos=e(a[1]);}}if(/ Android/.test(c)){g.mobile="Android";a=c.match(/Android ([^\s]*);/);if(a&&a[1]){g.android=e(a[1]);}}}a=c.match(/Chrome\/([^\s]*)/);if(a&&a[1]){g.chrome=e(a[1]);}else{a=c.match(/AdobeAIR\/([^\s]*)/);if(a){g.air=a[0];}}}if(!g.webkit){a=c.match(/Opera[\s\/]([^\s]*)/);if(a&&a[1]){g.opera=e(a[1]);a=c.match(/Version\/([^\s]*)/);if(a&&a[1]){g.opera=e(a[1]);}a=c.match(/Opera Mini[^;]*/);if(a){g.mobile=a[0];}}else{a=c.match(/MSIE\s([^;]*)/);if(a&&a[1]){g.ie=e(a[1]);}else{a=c.match(/Gecko\/([^\s]*)/);if(a){g.gecko=1;a=c.match(/rv:([^\s\)]*)/);if(a&&a[1]){g.gecko=e(a[1]);}}}}}}return g;};YAHOO.env.ua=YAHOO.env.parseUA();(function(){YAHOO.namespace("util","widget","example");if("undefined"!==typeof YAHOO_config){var b=YAHOO_config.listener,a=YAHOO.env.listeners,d=true,c;if(b){for(c=0;c<a.length;c++){if(a[c]==b){d=false;break;}}if(d){a.push(b);}}}})();YAHOO.lang=YAHOO.lang||{};(function(){var f=YAHOO.lang,a=Object.prototype,c="[object Array]",h="[object Function]",i="[object Object]",b=[],g={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;","`":"&#x60;"},d=["toString","valueOf"],e={isArray:function(j){return a.toString.apply(j)===c;},isBoolean:function(j){return typeof j==="boolean";},isFunction:function(j){return(typeof j==="function")||a.toString.apply(j)===h;},isNull:function(j){return j===null;},isNumber:function(j){return typeof j==="number"&&isFinite(j);},isObject:function(j){return(j&&(typeof j==="object"||f.isFunction(j)))||false;},isString:function(j){return typeof j==="string";},isUndefined:function(j){return typeof j==="undefined";},_IEEnumFix:(YAHOO.env.ua.ie)?function(l,k){var j,n,m;for(j=0;j<d.length;j=j+1){n=d[j];m=k[n];if(f.isFunction(m)&&m!=a[n]){l[n]=m;}}}:function(){},escapeHTML:function(j){return j.replace(/[&<>"'\/`]/g,function(k){return g[k];});},extend:function(m,n,l){if(!n||!m){throw new Error("extend failed, please check that "+"all dependencies are included.");}var k=function(){},j;k.prototype=n.prototype;m.prototype=new k();m.prototype.constructor=m;m.superclass=n.prototype;if(n.prototype.constructor==a.constructor){n.prototype.constructor=n;}if(l){for(j in l){if(f.hasOwnProperty(l,j)){m.prototype[j]=l[j];}}f._IEEnumFix(m.prototype,l);}},augmentObject:function(n,m){if(!m||!n){throw new Error("Absorb failed, verify dependencies.");}var j=arguments,l,o,k=j[2];if(k&&k!==true){for(l=2;l<j.length;l=l+1){n[j[l]]=m[j[l]];}}else{for(o in m){if(k||!(o in n)){n[o]=m[o];}}f._IEEnumFix(n,m);}return n;},augmentProto:function(m,l){if(!l||!m){throw new Error("Augment failed, verify dependencies.");}var j=[m.prototype,l.prototype],k;for(k=2;k<arguments.length;k=k+1){j.push(arguments[k]);}f.augmentObject.apply(this,j);return m;},dump:function(j,p){var l,n,r=[],t="{...}",k="f(){...}",q=", ",m=" => ";if(!f.isObject(j)){return j+"";}else{if(j instanceof Date||("nodeType" in j&&"tagName" in j)){return j;}else{if(f.isFunction(j)){return k;}}}p=(f.isNumber(p))?p:3;if(f.isArray(j)){r.push("[");for(l=0,n=j.length;l<n;l=l+1){if(f.isObject(j[l])){r.push((p>0)?f.dump(j[l],p-1):t);}else{r.push(j[l]);}r.push(q);}if(r.length>1){r.pop();}r.push("]");}else{r.push("{");for(l in j){if(f.hasOwnProperty(j,l)){r.push(l+m);if(f.isObject(j[l])){r.push((p>0)?f.dump(j[l],p-1):t);}else{r.push(j[l]);}r.push(q);}}if(r.length>1){r.pop();}r.push("}");}return r.join("");},substitute:function(x,y,E,l){var D,C,B,G,t,u,F=[],p,z=x.length,A="dump",r=" ",q="{",m="}",n,w;for(;;){D=x.lastIndexOf(q,z);if(D<0){break;}C=x.indexOf(m,D);if(D+1>C){break;}p=x.substring(D+1,C);G=p;u=null;B=G.indexOf(r);if(B>-1){u=G.substring(B+1);G=G.substring(0,B);}t=y[G];if(E){t=E(G,t,u);}if(f.isObject(t)){if(f.isArray(t)){t=f.dump(t,parseInt(u,10));}else{u=u||"";n=u.indexOf(A);if(n>-1){u=u.substring(4);}w=t.toString();if(w===i||n>-1){t=f.dump(t,parseInt(u,10));}else{t=w;}}}else{if(!f.isString(t)&&!f.isNumber(t)){t="~-"+F.length+"-~";F[F.length]=p;}}x=x.substring(0,D)+t+x.substring(C+1);if(l===false){z=D-1;}}for(D=F.length-1;D>=0;D=D-1){x=x.replace(new RegExp("~-"+D+"-~"),"{"+F[D]+"}","g");}return x;},trim:function(j){try{return j.replace(/^\s+|\s+$/g,"");}catch(k){return j;
-}},merge:function(){var n={},k=arguments,j=k.length,m;for(m=0;m<j;m=m+1){f.augmentObject(n,k[m],true);}return n;},later:function(t,k,u,n,p){t=t||0;k=k||{};var l=u,s=n,q,j;if(f.isString(u)){l=k[u];}if(!l){throw new TypeError("method undefined");}if(!f.isUndefined(n)&&!f.isArray(s)){s=[n];}q=function(){l.apply(k,s||b);};j=(p)?setInterval(q,t):setTimeout(q,t);return{interval:p,cancel:function(){if(this.interval){clearInterval(j);}else{clearTimeout(j);}}};},isValue:function(j){return(f.isObject(j)||f.isString(j)||f.isNumber(j)||f.isBoolean(j));}};f.hasOwnProperty=(a.hasOwnProperty)?function(j,k){return j&&j.hasOwnProperty&&j.hasOwnProperty(k);}:function(j,k){return !f.isUndefined(j[k])&&j.constructor.prototype[k]!==j[k];};e.augmentObject(f,e,true);YAHOO.util.Lang=f;f.augment=f.augmentProto;YAHOO.augment=f.augmentProto;YAHOO.extend=f.extend;})();YAHOO.register("yahoo",YAHOO,{version:"2.9.0",build:"2800"});YAHOO.util.Get=function(){var m={},k=0,r=0,l=false,n=YAHOO.env.ua,s=YAHOO.lang,q,d,e,i=function(x,t,y){var u=y||window,z=u.document,A=z.createElement(x),v;for(v in t){if(t.hasOwnProperty(v)){A.setAttribute(v,t[v]);}}return A;},h=function(u,v,t){var w={id:"yui__dyn_"+(r++),type:"text/css",rel:"stylesheet",href:u};if(t){s.augmentObject(w,t);}return i("link",w,v);},p=function(u,v,t){var w={id:"yui__dyn_"+(r++),type:"text/javascript",src:u};if(t){s.augmentObject(w,t);}return i("script",w,v);},a=function(t,u){return{tId:t.tId,win:t.win,data:t.data,nodes:t.nodes,msg:u,purge:function(){d(this.tId);}};},b=function(t,w){var u=m[w],v=(s.isString(t))?u.win.document.getElementById(t):t;if(!v){q(w,"target node not found: "+t);}return v;},c=function(w){YAHOO.log("Finishing transaction "+w);var u=m[w],v,t;u.finished=true;if(u.aborted){v="transaction "+w+" was aborted";q(w,v);return;}if(u.onSuccess){t=u.scope||u.win;u.onSuccess.call(t,a(u));}},o=function(v){YAHOO.log("Timeout "+v,"info","get");var u=m[v],t;if(u.onTimeout){t=u.scope||u;u.onTimeout.call(t,a(u));}},f=function(v,A){YAHOO.log("_next: "+v+", loaded: "+A,"info","Get");var u=m[v],D=u.win,C=D.document,B=C.getElementsByTagName("head")[0],x,y,t,E,z;if(u.timer){u.timer.cancel();}if(u.aborted){y="transaction "+v+" was aborted";q(v,y);return;}if(A){u.url.shift();if(u.varName){u.varName.shift();}}else{u.url=(s.isString(u.url))?[u.url]:u.url;if(u.varName){u.varName=(s.isString(u.varName))?[u.varName]:u.varName;}}if(u.url.length===0){if(u.type==="script"&&n.webkit&&n.webkit<420&&!u.finalpass&&!u.varName){z=p(null,u.win,u.attributes);z.innerHTML='YAHOO.util.Get._finalize("'+v+'");';u.nodes.push(z);B.appendChild(z);}else{c(v);}return;}t=u.url[0];if(!t){u.url.shift();YAHOO.log("skipping empty url");return f(v);}YAHOO.log("attempting to load "+t,"info","Get");if(u.timeout){u.timer=s.later(u.timeout,u,o,v);}if(u.type==="script"){x=p(t,D,u.attributes);}else{x=h(t,D,u.attributes);}e(u.type,x,v,t,D,u.url.length);u.nodes.push(x);if(u.insertBefore){E=b(u.insertBefore,v);if(E){E.parentNode.insertBefore(x,E);}}else{B.appendChild(x);}YAHOO.log("Appending node: "+t,"info","Get");if((n.webkit||n.gecko)&&u.type==="css"){f(v,t);}},j=function(){if(l){return;}l=true;var t,u;for(t in m){if(m.hasOwnProperty(t)){u=m[t];if(u.autopurge&&u.finished){d(u.tId);delete m[t];}}}l=false;},g=function(u,t,v){var x="q"+(k++),w;v=v||{};if(k%YAHOO.util.Get.PURGE_THRESH===0){j();}m[x]=s.merge(v,{tId:x,type:u,url:t,finished:false,aborted:false,nodes:[]});w=m[x];w.win=w.win||window;w.scope=w.scope||w.win;w.autopurge=("autopurge" in w)?w.autopurge:(u==="script")?true:false;w.attributes=w.attributes||{};w.attributes.charset=v.charset||w.attributes.charset||"utf-8";s.later(0,w,f,x);return{tId:x};};e=function(H,z,x,u,D,E,G){var F=G||f,B,t,I,v,J,A,C,y;if(n.ie){z.onreadystatechange=function(){B=this.readyState;if("loaded"===B||"complete"===B){YAHOO.log(x+" onload "+u,"info","Get");z.onreadystatechange=null;F(x,u);}};}else{if(n.webkit){if(H==="script"){if(n.webkit>=420){z.addEventListener("load",function(){YAHOO.log(x+" DOM2 onload "+u,"info","Get");F(x,u);});}else{t=m[x];if(t.varName){v=YAHOO.util.Get.POLL_FREQ;YAHOO.log("Polling for "+t.varName[0]);t.maxattempts=YAHOO.util.Get.TIMEOUT/v;t.attempts=0;t._cache=t.varName[0].split(".");t.timer=s.later(v,t,function(w){I=this._cache;A=I.length;J=this.win;for(C=0;C<A;C=C+1){J=J[I[C]];if(!J){this.attempts++;if(this.attempts++>this.maxattempts){y="Over retry limit, giving up";t.timer.cancel();q(x,y);}else{YAHOO.log(I[C]+" failed, retrying");}return;}}YAHOO.log("Safari poll complete");t.timer.cancel();F(x,u);},null,true);}else{s.later(YAHOO.util.Get.POLL_FREQ,null,F,[x,u]);}}}}else{z.onload=function(){YAHOO.log(x+" onload "+u,"info","Get");F(x,u);};}}};q=function(w,v){YAHOO.log("get failure: "+v,"warn","Get");var u=m[w],t;if(u.onFailure){t=u.scope||u.win;u.onFailure.call(t,a(u,v));}};d=function(z){if(m[z]){var t=m[z],u=t.nodes,x=u.length,C=t.win.document,A=C.getElementsByTagName("head")[0],v,y,w,B;if(t.insertBefore){v=b(t.insertBefore,z);if(v){A=v.parentNode;}}for(y=0;y<x;y=y+1){w=u[y];if(w.clearAttributes){w.clearAttributes();}else{for(B in w){if(w.hasOwnProperty(B)){delete w[B];}}}A.removeChild(w);}t.nodes=[];}};return{POLL_FREQ:10,PURGE_THRESH:20,TIMEOUT:2000,_finalize:function(t){YAHOO.log(t+" finalized ","info","Get");s.later(0,null,c,t);},abort:function(u){var v=(s.isString(u))?u:u.tId,t=m[v];if(t){YAHOO.log("Aborting "+v,"info","Get");t.aborted=true;}},script:function(t,u){return g("script",t,u);},css:function(t,u){return g("css",t,u);}};}();YAHOO.register("get",YAHOO.util.Get,{version:"2.9.0",build:"2800"});(function(){var Y=YAHOO,util=Y.util,lang=Y.lang,env=Y.env,PROV="_provides",SUPER="_supersedes",REQ="expanded",AFTER="_after",VERSION="2.9.0";var YUI={dupsAllowed:{"yahoo":true,"get":true},info:{"root":VERSION+"/build/","base":"http://yui.yahooapis.com/"+VERSION+"/build/","comboBase":"http://yui.yahooapis.com/combo?","skin":{"defaultSkin":"sam","base":"assets/skins/","path":"skin.css","after":["reset","fonts","grids","base"],"rollup":3},dupsAllowed:["yahoo","get"],"moduleInfo":{"animation":{"type":"js","path":"animation/animation-min.js","requires":["dom","event"]},"autocomplete":{"type":"js","path":"autocomplete/autocomplete-min.js","requires":["dom","event","datasource"],"optional":["connection","animation"],"skinnable":true},"base":{"type":"css","path":"base/base-min.css","after":["reset","fonts","grids"]},"button":{"type":"js","path":"button/button-min.js","requires":["element"],"optional":["menu"],"skinnable":true},"calendar":{"type":"js","path":"calendar/calendar-min.js","requires":["event","dom"],supersedes:["datemath"],"skinnable":true},"carousel":{"type":"js","path":"carousel/carousel-min.js","requires":["element"],"optional":["animation"],"skinnable":true},"charts":{"type":"js","path":"charts/charts-min.js","requires":["element","json","datasource","swf"]},"colorpicker":{"type":"js","path":"colorpicker/colorpicker-min.js","requires":["slider","element"],"optional":["animation"],"skinnable":true},"connection":{"type":"js","path":"connection/connection-min.js","requires":["event"],"supersedes":["connectioncore"]},"connectioncore":{"type":"js","path":"connection/connection_core-min.js","requires":["event"],"pkg":"connection"},"container":{"type":"js","path":"container/container-min.js","requires":["dom","event"],"optional":["dragdrop","animation","connection"],"supersedes":["containercore"],"skinnable":true},"containercore":{"type":"js","path":"container/container_core-min.js","requires":["dom","event"],"pkg":"container"},"cookie":{"type":"js","path":"cookie/cookie-min.js","requires":["yahoo"]},"datasource":{"type":"js","path":"datasource/datasource-min.js","requires":["event"],"optional":["connection"]},"datatable":{"type":"js","path":"datatable/datatable-min.js","requires":["element","datasource"],"optional":["calendar","dragdrop","paginator"],"skinnable":true},datemath:{"type":"js","path":"datemath/datemath-min.js","requires":["yahoo"]},"dom":{"type":"js","path":"dom/dom-min.js","requires":["yahoo"]},"dragdrop":{"type":"js","path":"dragdrop/dragdrop-min.js","requires":["dom","event"]},"editor":{"type":"js","path":"editor/editor-min.js","requires":["menu","element","button"],"optional":["animation","dragdrop"],"supersedes":["simpleeditor"],"skinnable":true},"element":{"type":"js","path":"element/element-min.js","requires":["dom","event"],"optional":["event-mouseenter","event-delegate"]},"element-delegate":{"type":"js","path":"element-delegate/element-delegate-min.js","requires":["element"]},"event":{"type":"js","path":"event/event-min.js","requires":["yahoo"]},"event-simulate":{"type":"js","path":"event-simulate/event-simulate-min.js","requires":["event"]},"event-delegate":{"type":"js","path":"event-delegate/event-delegate-min.js","requires":["event"],"optional":["selector"]},"event-mouseenter":{"type":"js","path":"event-mouseenter/event-mouseenter-min.js","requires":["dom","event"]},"fonts":{"type":"css","path":"fonts/fonts-min.css"},"get":{"type":"js","path":"get/get-min.js","requires":["yahoo"]},"grids":{"type":"css","path":"grids/grids-min.css","requires":["fonts"],"optional":["reset"]},"history":{"type":"js","path":"history/history-min.js","requires":["event"]},"imagecropper":{"type":"js","path":"imagecropper/imagecropper-min.js","requires":["dragdrop","element","resize"],"skinnable":true},"imageloader":{"type":"js","path":"imageloader/imageloader-min.js","requires":["event","dom"]},"json":{"type":"js","path":"json/json-min.js","requires":["yahoo"]},"layout":{"type":"js","path":"layout/layout-min.js","requires":["element"],"optional":["animation","dragdrop","resize","selector"],"skinnable":true},"logger":{"type":"js","path":"logger/logger-min.js","requires":["event","dom"],"optional":["dragdrop"],"skinnable":true},"menu":{"type":"js","path":"menu/menu-min.js","requires":["containercore"],"skinnable":true},"paginator":{"type":"js","path":"paginator/paginator-min.js","requires":["element"],"skinnable":true},"profiler":{"type":"js","path":"profiler/profiler-min.js","requires":["yahoo"]},"profilerviewer":{"type":"js","path":"profilerviewer/profilerviewer-min.js","requires":["profiler","yuiloader","element"],"skinnable":true},"progressbar":{"type":"js","path":"progressbar/progressbar-min.js","requires":["element"],"optional":["animation"],"skinnable":true},"reset":{"type":"css","path":"reset/reset-min.css"},"reset-fonts-grids":{"type":"css","path":"reset-fonts-grids/reset-fonts-grids.css","supersedes":["reset","fonts","grids","reset-fonts"],"rollup":4},"reset-fonts":{"type":"css","path":"reset-fonts/reset-fonts.css","supersedes":["reset","fonts"],"rollup":2},"resize":{"type":"js","path":"resize/resize-min.js","requires":["dragdrop","element"],"optional":["animation"],"skinnable":true},"selector":{"type":"js","path":"selector/selector-min.js","requires":["yahoo","dom"]},"simpleeditor":{"type":"js","path":"editor/simpleeditor-min.js","requires":["element"],"optional":["containercore","menu","button","animation","dragdrop"],"skinnable":true,"pkg":"editor"},"slider":{"type":"js","path":"slider/slider-min.js","requires":["dragdrop"],"optional":["animation"],"skinnable":true},"storage":{"type":"js","path":"storage/storage-min.js","requires":["yahoo","event","cookie"],"optional":["swfstore"]},"stylesheet":{"type":"js","path":"stylesheet/stylesheet-min.js","requires":["yahoo"]},"swf":{"type":"js","path":"swf/swf-min.js","requires":["element"],"supersedes":["swfdetect"]},"swfdetect":{"type":"js","path":"swfdetect/swfdetect-min.js","requires":["yahoo"]},"swfstore":{"type":"js","path":"swfstore/swfstore-min.js","requires":["element","cookie","swf"]},"tabview":{"type":"js","path":"tabview/tabview-min.js","requires":["element"],"optional":["connection"],"skinnable":true},"treeview":{"type":"js","path":"treeview/treeview-min.js","requires":["event","dom"],"optional":["json","animation","calendar"],"skinnable":true},"uploader":{"type":"js","path":"uploader/uploader-min.js","requires":["element"]},"utilities":{"type":"js","path":"utilities/utilities.js","supersedes":["yahoo","event","dragdrop","animation","dom","connection","element","yahoo-dom-event","get","yuiloader","yuiloader-dom-event"],"rollup":8},"yahoo":{"type":"js","path":"yahoo/yahoo-min.js"},"yahoo-dom-event":{"type":"js","path":"yahoo-dom-event/yahoo-dom-event.js","supersedes":["yahoo","event","dom"],"rollup":3},"yuiloader":{"type":"js","path":"yuiloader/yuiloader-min.js","supersedes":["yahoo","get"]},"yuiloader-dom-event":{"type":"js","path":"yuiloader-dom-event/yuiloader-dom-event.js","supersedes":["yahoo","dom","event","get","yuiloader","yahoo-dom-event"],"rollup":5},"yuitest":{"type":"js","path":"yuitest/yuitest-min.js","requires":["logger"],"optional":["event-simulate"],"skinnable":true}}},ObjectUtil:{appendArray:function(o,a){if(a){for(var i=0;
-i<a.length;i=i+1){o[a[i]]=true;}}},keys:function(o,ordered){var a=[],i;for(i in o){if(lang.hasOwnProperty(o,i)){a.push(i);}}return a;}},ArrayUtil:{appendArray:function(a1,a2){Array.prototype.push.apply(a1,a2);},indexOf:function(a,val){for(var i=0;i<a.length;i=i+1){if(a[i]===val){return i;}}return -1;},toObject:function(a){var o={};for(var i=0;i<a.length;i=i+1){o[a[i]]=true;}return o;},uniq:function(a){return YUI.ObjectUtil.keys(YUI.ArrayUtil.toObject(a));}}};YAHOO.util.YUILoader=function(o){this._internalCallback=null;this._useYahooListener=false;this.onSuccess=null;this.onFailure=Y.log;this.onProgress=null;this.onTimeout=null;this.scope=this;this.data=null;this.insertBefore=null;this.charset=null;this.varName=null;this.base=YUI.info.base;this.comboBase=YUI.info.comboBase;this.combine=false;this.root=YUI.info.root;this.timeout=0;this.ignore=null;this.force=null;this.allowRollup=true;this.filter=null;this.required={};this.moduleInfo=lang.merge(YUI.info.moduleInfo);this.rollups=null;this.loadOptional=false;this.sorted=[];this.loaded={};this.dirty=true;this.inserted={};var self=this;env.listeners.push(function(m){if(self._useYahooListener){self.loadNext(m.name);}});this.skin=lang.merge(YUI.info.skin);this._config(o);};Y.util.YUILoader.prototype={FILTERS:{RAW:{"searchExp":"-min\\.js","replaceStr":".js"},DEBUG:{"searchExp":"-min\\.js","replaceStr":"-debug.js"}},SKIN_PREFIX:"skin-",_config:function(o){if(o){for(var i in o){if(lang.hasOwnProperty(o,i)){if(i=="require"){this.require(o[i]);}else{this[i]=o[i];}}}}var f=this.filter;if(lang.isString(f)){f=f.toUpperCase();if(f==="DEBUG"){this.require("logger");}if(!Y.widget.LogWriter){Y.widget.LogWriter=function(){return Y;};}this.filter=this.FILTERS[f];}},addModule:function(o){if(!o||!o.name||!o.type||(!o.path&&!o.fullpath)){return false;}o.ext=("ext" in o)?o.ext:true;o.requires=o.requires||[];this.moduleInfo[o.name]=o;this.dirty=true;return true;},require:function(what){var a=(typeof what==="string")?arguments:what;this.dirty=true;YUI.ObjectUtil.appendArray(this.required,a);},_addSkin:function(skin,mod){var name=this.formatSkin(skin),info=this.moduleInfo,sinf=this.skin,ext=info[mod]&&info[mod].ext;if(!info[name]){this.addModule({"name":name,"type":"css","path":sinf.base+skin+"/"+sinf.path,"after":sinf.after,"rollup":sinf.rollup,"ext":ext});}if(mod){name=this.formatSkin(skin,mod);if(!info[name]){var mdef=info[mod],pkg=mdef.pkg||mod;this.addModule({"name":name,"type":"css","after":sinf.after,"path":pkg+"/"+sinf.base+skin+"/"+mod+".css","ext":ext});}}return name;},getRequires:function(mod){if(!mod){return[];}if(!this.dirty&&mod.expanded){return mod.expanded;}mod.requires=mod.requires||[];var i,d=[],r=mod.requires,o=mod.optional,info=this.moduleInfo,m;for(i=0;i<r.length;i=i+1){d.push(r[i]);m=info[r[i]];YUI.ArrayUtil.appendArray(d,this.getRequires(m));}if(o&&this.loadOptional){for(i=0;i<o.length;i=i+1){d.push(o[i]);YUI.ArrayUtil.appendArray(d,this.getRequires(info[o[i]]));}}mod.expanded=YUI.ArrayUtil.uniq(d);return mod.expanded;},getProvides:function(name,notMe){var addMe=!(notMe),ckey=(addMe)?PROV:SUPER,m=this.moduleInfo[name],o={};if(!m){return o;}if(m[ckey]){return m[ckey];}var s=m.supersedes,done={},me=this;var add=function(mm){if(!done[mm]){done[mm]=true;lang.augmentObject(o,me.getProvides(mm));}};if(s){for(var i=0;i<s.length;i=i+1){add(s[i]);}}m[SUPER]=o;m[PROV]=lang.merge(o);m[PROV][name]=true;return m[ckey];},calculate:function(o){if(o||this.dirty){this._config(o);this._setup();this._explode();if(this.allowRollup){this._rollup();}this._reduce();this._sort();this.dirty=false;}},_setup:function(){var info=this.moduleInfo,name,i,j;for(name in info){if(lang.hasOwnProperty(info,name)){var m=info[name];if(m&&m.skinnable){var o=this.skin.overrides,smod;if(o&&o[name]){for(i=0;i<o[name].length;i=i+1){smod=this._addSkin(o[name][i],name);}}else{smod=this._addSkin(this.skin.defaultSkin,name);}if(YUI.ArrayUtil.indexOf(m.requires,smod)==-1){m.requires.push(smod);}}}}var l=lang.merge(this.inserted);if(!this._sandbox){l=lang.merge(l,env.modules);}if(this.ignore){YUI.ObjectUtil.appendArray(l,this.ignore);}if(this.force){for(i=0;i<this.force.length;i=i+1){if(this.force[i] in l){delete l[this.force[i]];}}}for(j in l){if(lang.hasOwnProperty(l,j)){lang.augmentObject(l,this.getProvides(j));}}this.loaded=l;},_explode:function(){var r=this.required,i,mod;for(i in r){if(lang.hasOwnProperty(r,i)){mod=this.moduleInfo[i];if(mod){var req=this.getRequires(mod);if(req){YUI.ObjectUtil.appendArray(r,req);}}}}},_skin:function(){},formatSkin:function(skin,mod){var s=this.SKIN_PREFIX+skin;if(mod){s=s+"-"+mod;}return s;},parseSkin:function(mod){if(mod.indexOf(this.SKIN_PREFIX)===0){var a=mod.split("-");return{skin:a[1],module:a[2]};}return null;},_rollup:function(){var i,j,m,s,rollups={},r=this.required,roll,info=this.moduleInfo;if(this.dirty||!this.rollups){for(i in info){if(lang.hasOwnProperty(info,i)){m=info[i];if(m&&m.rollup){rollups[i]=m;}}}this.rollups=rollups;}for(;;){var rolled=false;for(i in rollups){if(!r[i]&&!this.loaded[i]){m=info[i];s=m.supersedes;roll=false;if(!m.rollup){continue;}var skin=(m.ext)?false:this.parseSkin(i),c=0;if(skin){for(j in r){if(lang.hasOwnProperty(r,j)){if(i!==j&&this.parseSkin(j)){c++;roll=(c>=m.rollup);if(roll){break;}}}}}else{for(j=0;j<s.length;j=j+1){if(this.loaded[s[j]]&&(!YUI.dupsAllowed[s[j]])){roll=false;break;}else{if(r[s[j]]){c++;roll=(c>=m.rollup);if(roll){break;}}}}}if(roll){r[i]=true;rolled=true;this.getRequires(m);}}}if(!rolled){break;}}},_reduce:function(){var i,j,s,m,r=this.required;for(i in r){if(i in this.loaded){delete r[i];}else{var skinDef=this.parseSkin(i);if(skinDef){if(!skinDef.module){var skin_pre=this.SKIN_PREFIX+skinDef.skin;for(j in r){if(lang.hasOwnProperty(r,j)){m=this.moduleInfo[j];var ext=m&&m.ext;if(!ext&&j!==i&&j.indexOf(skin_pre)>-1){delete r[j];}}}}}else{m=this.moduleInfo[i];s=m&&m.supersedes;if(s){for(j=0;j<s.length;j=j+1){if(s[j] in r){delete r[s[j]];}}}}}}},_onFailure:function(msg){YAHOO.log("Failure","info","loader");
-var f=this.onFailure;if(f){f.call(this.scope,{msg:"failure: "+msg,data:this.data,success:false});}},_onTimeout:function(){YAHOO.log("Timeout","info","loader");var f=this.onTimeout;if(f){f.call(this.scope,{msg:"timeout",data:this.data,success:false});}},_sort:function(){var s=[],info=this.moduleInfo,loaded=this.loaded,checkOptional=!this.loadOptional,me=this;var requires=function(aa,bb){var mm=info[aa];if(loaded[bb]||!mm){return false;}var ii,rr=mm.expanded,after=mm.after,other=info[bb],optional=mm.optional;if(rr&&YUI.ArrayUtil.indexOf(rr,bb)>-1){return true;}if(after&&YUI.ArrayUtil.indexOf(after,bb)>-1){return true;}if(checkOptional&&optional&&YUI.ArrayUtil.indexOf(optional,bb)>-1){return true;}var ss=info[bb]&&info[bb].supersedes;if(ss){for(ii=0;ii<ss.length;ii=ii+1){if(requires(aa,ss[ii])){return true;}}}if(mm.ext&&mm.type=="css"&&!other.ext&&other.type=="css"){return true;}return false;};for(var i in this.required){if(lang.hasOwnProperty(this.required,i)){s.push(i);}}var p=0;for(;;){var l=s.length,a,b,j,k,moved=false;for(j=p;j<l;j=j+1){a=s[j];for(k=j+1;k<l;k=k+1){if(requires(a,s[k])){b=s.splice(k,1);s.splice(j,0,b[0]);moved=true;break;}}if(moved){break;}else{p=p+1;}}if(!moved){break;}}this.sorted=s;},toString:function(){var o={type:"YUILoader",base:this.base,filter:this.filter,required:this.required,loaded:this.loaded,inserted:this.inserted};lang.dump(o,1);},_combine:function(){this._combining=[];var self=this,s=this.sorted,len=s.length,js=this.comboBase,css=this.comboBase,target,startLen=js.length,i,m,type=this.loadType;YAHOO.log("type "+type);for(i=0;i<len;i=i+1){m=this.moduleInfo[s[i]];if(m&&!m.ext&&(!type||type===m.type)){target=this.root+m.path;target+="&";if(m.type=="js"){js+=target;}else{css+=target;}this._combining.push(s[i]);}}if(this._combining.length){YAHOO.log("Attempting to combine: "+this._combining,"info","loader");var callback=function(o){var c=this._combining,len=c.length,i,m;for(i=0;i<len;i=i+1){this.inserted[c[i]]=true;}this.loadNext(o.data);},loadScript=function(){if(js.length>startLen){YAHOO.util.Get.script(self._filter(js),{data:self._loading,onSuccess:callback,onFailure:self._onFailure,onTimeout:self._onTimeout,insertBefore:self.insertBefore,charset:self.charset,timeout:self.timeout,scope:self});}else{this.loadNext();}};if(css.length>startLen){YAHOO.util.Get.css(this._filter(css),{data:this._loading,onSuccess:loadScript,onFailure:this._onFailure,onTimeout:this._onTimeout,insertBefore:this.insertBefore,charset:this.charset,timeout:this.timeout,scope:self});}else{loadScript();}return;}else{this.loadNext(this._loading);}},insert:function(o,type){this.calculate(o);this._loading=true;this.loadType=type;if(this.combine){return this._combine();}if(!type){var self=this;this._internalCallback=function(){self._internalCallback=null;self.insert(null,"js");};this.insert(null,"css");return;}this.loadNext();},sandbox:function(o,type){var self=this,success=function(o){var idx=o.argument[0],name=o.argument[2];self._scriptText[idx]=o.responseText;if(self.onProgress){self.onProgress.call(self.scope,{name:name,scriptText:o.responseText,xhrResponse:o,data:self.data});}self._loadCount++;if(self._loadCount>=self._stopCount){var v=self.varName||"YAHOO";var t="(function() {\n";var b="\nreturn "+v+";\n})();";var ref=eval(t+self._scriptText.join("\n")+b);self._pushEvents(ref);if(ref){self.onSuccess.call(self.scope,{reference:ref,data:self.data});}else{self._onFailure.call(self.varName+" reference failure");}}},failure=function(o){self.onFailure.call(self.scope,{msg:"XHR failure",xhrResponse:o,data:self.data});};self._config(o);if(!self.onSuccess){throw new Error("You must supply an onSuccess handler for your sandbox");}self._sandbox=true;if(!type||type!=="js"){self._internalCallback=function(){self._internalCallback=null;self.sandbox(null,"js");};self.insert(null,"css");return;}if(!util.Connect){var ld=new YAHOO.util.YUILoader();ld.insert({base:self.base,filter:self.filter,require:"connection",insertBefore:self.insertBefore,charset:self.charset,onSuccess:function(){self.sandbox(null,"js");},scope:self},"js");return;}self._scriptText=[];self._loadCount=0;self._stopCount=self.sorted.length;self._xhr=[];self.calculate();var s=self.sorted,l=s.length,i,m,url;for(i=0;i<l;i=i+1){m=self.moduleInfo[s[i]];if(!m){self._onFailure("undefined module "+m);for(var j=0;j<self._xhr.length;j=j+1){self._xhr[j].abort();}return;}if(m.type!=="js"){self._loadCount++;continue;}url=m.fullpath;url=(url)?self._filter(url):self._url(m.path);var xhrData={success:success,failure:failure,scope:self,argument:[i,url,s[i]]};self._xhr.push(util.Connect.asyncRequest("GET",url,xhrData));}},loadNext:function(mname){if(!this._loading){return;}var self=this,donext=function(o){self.loadNext(o.data);},successfn,s=this.sorted,len=s.length,i,fn,m,url;if(mname){if(mname!==this._loading){return;}this.inserted[mname]=true;if(this.onProgress){this.onProgress.call(this.scope,{name:mname,data:this.data});}}for(i=0;i<len;i=i+1){if(s[i] in this.inserted){continue;}if(s[i]===this._loading){return;}m=this.moduleInfo[s[i]];if(!m){this.onFailure.call(this.scope,{msg:"undefined module "+m,data:this.data});return;}if(!this.loadType||this.loadType===m.type){successfn=donext;this._loading=s[i];fn=(m.type==="css")?util.Get.css:util.Get.script;url=m.fullpath;url=(url)?this._filter(url):this._url(m.path);if(env.ua.webkit&&env.ua.webkit<420&&m.type==="js"&&!m.varName){successfn=null;this._useYahooListener=true;}fn(url,{data:s[i],onSuccess:successfn,onFailure:this._onFailure,onTimeout:this._onTimeout,insertBefore:this.insertBefore,charset:this.charset,timeout:this.timeout,varName:m.varName,scope:self});return;}}this._loading=null;if(this._internalCallback){var f=this._internalCallback;this._internalCallback=null;f.call(this);}else{if(this.onSuccess){this._pushEvents();this.onSuccess.call(this.scope,{data:this.data});}}},_pushEvents:function(ref){var r=ref||YAHOO;if(r.util&&r.util.Event){r.util.Event._load();}},_filter:function(str){var f=this.filter;return(f)?str.replace(new RegExp(f.searchExp,"g"),f.replaceStr):str;
-},_url:function(path){return this._filter((this.base||"")+path);}};})();YAHOO.register("yuiloader",YAHOO.util.YUILoader,{version:"2.9.0",build:"2800"});(function(){YAHOO.env._id_counter=YAHOO.env._id_counter||0;var e=YAHOO.util,k=YAHOO.lang,L=YAHOO.env.ua,a=YAHOO.lang.trim,B={},F={},m=/^t(?:able|d|h)$/i,w=/color$/i,j=window.document,v=j.documentElement,C="ownerDocument",M="defaultView",U="documentElement",S="compatMode",z="offsetLeft",o="offsetTop",T="offsetParent",x="parentNode",K="nodeType",c="tagName",n="scrollLeft",H="scrollTop",p="getBoundingClientRect",V="getComputedStyle",y="currentStyle",l="CSS1Compat",A="BackCompat",E="class",f="className",i="",b=" ",R="(?:^|\\s)",J="(?= |$)",t="g",O="position",D="fixed",u="relative",I="left",N="top",Q="medium",P="borderLeftWidth",q="borderTopWidth",d=L.opera,h=L.webkit,g=L.gecko,s=L.ie;e.Dom={CUSTOM_ATTRIBUTES:(!v.hasAttribute)?{"for":"htmlFor","class":f}:{"htmlFor":"for","className":E},DOT_ATTRIBUTES:{checked:true},get:function(aa){var ac,X,ab,Z,W,G,Y=null;if(aa){if(typeof aa=="string"||typeof aa=="number"){ac=aa+"";aa=j.getElementById(aa);G=(aa)?aa.attributes:null;if(aa&&G&&G.id&&G.id.value===ac){return aa;}else{if(aa&&j.all){aa=null;X=j.all[ac];if(X&&X.length){for(Z=0,W=X.length;Z<W;++Z){if(X[Z].id===ac){return X[Z];}}}}}}else{if(e.Element&&aa instanceof e.Element){aa=aa.get("element");}else{if(!aa.nodeType&&"length" in aa){ab=[];for(Z=0,W=aa.length;Z<W;++Z){ab[ab.length]=e.Dom.get(aa[Z]);}aa=ab;}}}Y=aa;}return Y;},getComputedStyle:function(G,W){if(window[V]){return G[C][M][V](G,null)[W];}else{if(G[y]){return e.Dom.IE_ComputedStyle.get(G,W);}}},getStyle:function(G,W){return e.Dom.batch(G,e.Dom._getStyle,W);},_getStyle:function(){if(window[V]){return function(G,Y){Y=(Y==="float")?Y="cssFloat":e.Dom._toCamel(Y);var X=G.style[Y],W;if(!X){W=G[C][M][V](G,null);if(W){X=W[Y];}}return X;};}else{if(v[y]){return function(G,Y){var X;switch(Y){case"opacity":X=100;try{X=G.filters["DXImageTransform.Microsoft.Alpha"].opacity;}catch(Z){try{X=G.filters("alpha").opacity;}catch(W){}}return X/100;case"float":Y="styleFloat";default:Y=e.Dom._toCamel(Y);X=G[y]?G[y][Y]:null;return(G.style[Y]||X);}};}}}(),setStyle:function(G,W,X){e.Dom.batch(G,e.Dom._setStyle,{prop:W,val:X});},_setStyle:function(){if(!window.getComputedStyle&&j.documentElement.currentStyle){return function(W,G){var X=e.Dom._toCamel(G.prop),Y=G.val;if(W){switch(X){case"opacity":if(Y===""||Y===null||Y===1){W.style.removeAttribute("filter");}else{if(k.isString(W.style.filter)){W.style.filter="alpha(opacity="+Y*100+")";if(!W[y]||!W[y].hasLayout){W.style.zoom=1;}}}break;case"float":X="styleFloat";default:W.style[X]=Y;}}else{}};}else{return function(W,G){var X=e.Dom._toCamel(G.prop),Y=G.val;if(W){if(X=="float"){X="cssFloat";}W.style[X]=Y;}else{}};}}(),getXY:function(G){return e.Dom.batch(G,e.Dom._getXY);},_canPosition:function(G){return(e.Dom._getStyle(G,"display")!=="none"&&e.Dom._inDoc(G));},_getXY:function(W){var X,G,Z,ab,Y,aa,ac=Math.round,ad=false;if(e.Dom._canPosition(W)){Z=W[p]();ab=W[C];X=e.Dom.getDocumentScrollLeft(ab);G=e.Dom.getDocumentScrollTop(ab);ad=[Z[I],Z[N]];if(Y||aa){ad[0]-=aa;ad[1]-=Y;}if((G||X)){ad[0]+=X;ad[1]+=G;}ad[0]=ac(ad[0]);ad[1]=ac(ad[1]);}else{}return ad;},getX:function(G){var W=function(X){return e.Dom.getXY(X)[0];};return e.Dom.batch(G,W,e.Dom,true);},getY:function(G){var W=function(X){return e.Dom.getXY(X)[1];};return e.Dom.batch(G,W,e.Dom,true);},setXY:function(G,X,W){e.Dom.batch(G,e.Dom._setXY,{pos:X,noRetry:W});},_setXY:function(G,Z){var aa=e.Dom._getStyle(G,O),Y=e.Dom.setStyle,ad=Z.pos,W=Z.noRetry,ab=[parseInt(e.Dom.getComputedStyle(G,I),10),parseInt(e.Dom.getComputedStyle(G,N),10)],ac,X;ac=e.Dom._getXY(G);if(!ad||ac===false){return false;}if(aa=="static"){aa=u;Y(G,O,aa);}if(isNaN(ab[0])){ab[0]=(aa==u)?0:G[z];}if(isNaN(ab[1])){ab[1]=(aa==u)?0:G[o];}if(ad[0]!==null){Y(G,I,ad[0]-ac[0]+ab[0]+"px");}if(ad[1]!==null){Y(G,N,ad[1]-ac[1]+ab[1]+"px");}if(!W){X=e.Dom._getXY(G);if((ad[0]!==null&&X[0]!=ad[0])||(ad[1]!==null&&X[1]!=ad[1])){e.Dom._setXY(G,{pos:ad,noRetry:true});}}},setX:function(W,G){e.Dom.setXY(W,[G,null]);},setY:function(G,W){e.Dom.setXY(G,[null,W]);},getRegion:function(G){var W=function(X){var Y=false;if(e.Dom._canPosition(X)){Y=e.Region.getRegion(X);}else{}return Y;};return e.Dom.batch(G,W,e.Dom,true);},getClientWidth:function(){return e.Dom.getViewportWidth();},getClientHeight:function(){return e.Dom.getViewportHeight();},getElementsByClassName:function(ab,af,ac,ae,X,ad){af=af||"*";ac=(ac)?e.Dom.get(ac):null||j;if(!ac){return[];}var W=[],G=ac.getElementsByTagName(af),Z=e.Dom.hasClass;for(var Y=0,aa=G.length;Y<aa;++Y){if(Z(G[Y],ab)){W[W.length]=G[Y];}}if(ae){e.Dom.batch(W,ae,X,ad);}return W;},hasClass:function(W,G){return e.Dom.batch(W,e.Dom._hasClass,G);},_hasClass:function(X,W){var G=false,Y;if(X&&W){Y=e.Dom._getAttribute(X,f)||i;if(Y){Y=Y.replace(/\s+/g,b);}if(W.exec){G=W.test(Y);}else{G=W&&(b+Y+b).indexOf(b+W+b)>-1;}}else{}return G;},addClass:function(W,G){return e.Dom.batch(W,e.Dom._addClass,G);},_addClass:function(X,W){var G=false,Y;if(X&&W){Y=e.Dom._getAttribute(X,f)||i;if(!e.Dom._hasClass(X,W)){e.Dom.setAttribute(X,f,a(Y+b+W));G=true;}}else{}return G;},removeClass:function(W,G){return e.Dom.batch(W,e.Dom._removeClass,G);},_removeClass:function(Y,X){var W=false,aa,Z,G;if(Y&&X){aa=e.Dom._getAttribute(Y,f)||i;e.Dom.setAttribute(Y,f,aa.replace(e.Dom._getClassRegex(X),i));Z=e.Dom._getAttribute(Y,f);if(aa!==Z){e.Dom.setAttribute(Y,f,a(Z));W=true;if(e.Dom._getAttribute(Y,f)===""){G=(Y.hasAttribute&&Y.hasAttribute(E))?E:f;Y.removeAttribute(G);}}}else{}return W;},replaceClass:function(X,W,G){return e.Dom.batch(X,e.Dom._replaceClass,{from:W,to:G});},_replaceClass:function(Y,X){var W,ab,aa,G=false,Z;if(Y&&X){ab=X.from;aa=X.to;if(!aa){G=false;}else{if(!ab){G=e.Dom._addClass(Y,X.to);}else{if(ab!==aa){Z=e.Dom._getAttribute(Y,f)||i;W=(b+Z.replace(e.Dom._getClassRegex(ab),b+aa).replace(/\s+/g,b)).split(e.Dom._getClassRegex(aa));W.splice(1,0,b+aa);e.Dom.setAttribute(Y,f,a(W.join(i)));G=true;}}}}else{}return G;},generateId:function(G,X){X=X||"yui-gen";var W=function(Y){if(Y&&Y.id){return Y.id;}var Z=X+YAHOO.env._id_counter++;
-if(Y){if(Y[C]&&Y[C].getElementById(Z)){return e.Dom.generateId(Y,Z+X);}Y.id=Z;}return Z;};return e.Dom.batch(G,W,e.Dom,true)||W.apply(e.Dom,arguments);},isAncestor:function(W,X){W=e.Dom.get(W);X=e.Dom.get(X);var G=false;if((W&&X)&&(W[K]&&X[K])){if(W.contains&&W!==X){G=W.contains(X);}else{if(W.compareDocumentPosition){G=!!(W.compareDocumentPosition(X)&16);}}}else{}return G;},inDocument:function(G,W){return e.Dom._inDoc(e.Dom.get(G),W);},_inDoc:function(W,X){var G=false;if(W&&W[c]){X=X||W[C];G=e.Dom.isAncestor(X[U],W);}else{}return G;},getElementsBy:function(W,af,ab,ad,X,ac,ae){af=af||"*";ab=(ab)?e.Dom.get(ab):null||j;var aa=(ae)?null:[],G;if(ab){G=ab.getElementsByTagName(af);for(var Y=0,Z=G.length;Y<Z;++Y){if(W(G[Y])){if(ae){aa=G[Y];break;}else{aa[aa.length]=G[Y];}}}if(ad){e.Dom.batch(aa,ad,X,ac);}}return aa;},getElementBy:function(X,G,W){return e.Dom.getElementsBy(X,G,W,null,null,null,true);},batch:function(X,ab,aa,Z){var Y=[],W=(Z)?aa:null;X=(X&&(X[c]||X.item))?X:e.Dom.get(X);if(X&&ab){if(X[c]||X.length===undefined){return ab.call(W,X,aa);}for(var G=0;G<X.length;++G){Y[Y.length]=ab.call(W||X[G],X[G],aa);}}else{return false;}return Y;},getDocumentHeight:function(){var W=(j[S]!=l||h)?j.body.scrollHeight:v.scrollHeight,G=Math.max(W,e.Dom.getViewportHeight());return G;},getDocumentWidth:function(){var W=(j[S]!=l||h)?j.body.scrollWidth:v.scrollWidth,G=Math.max(W,e.Dom.getViewportWidth());return G;},getViewportHeight:function(){var G=self.innerHeight,W=j[S];if((W||s)&&!d){G=(W==l)?v.clientHeight:j.body.clientHeight;}return G;},getViewportWidth:function(){var G=self.innerWidth,W=j[S];if(W||s){G=(W==l)?v.clientWidth:j.body.clientWidth;}return G;},getAncestorBy:function(G,W){while((G=G[x])){if(e.Dom._testElement(G,W)){return G;}}return null;},getAncestorByClassName:function(W,G){W=e.Dom.get(W);if(!W){return null;}var X=function(Y){return e.Dom.hasClass(Y,G);};return e.Dom.getAncestorBy(W,X);},getAncestorByTagName:function(W,G){W=e.Dom.get(W);if(!W){return null;}var X=function(Y){return Y[c]&&Y[c].toUpperCase()==G.toUpperCase();};return e.Dom.getAncestorBy(W,X);},getPreviousSiblingBy:function(G,W){while(G){G=G.previousSibling;if(e.Dom._testElement(G,W)){return G;}}return null;},getPreviousSibling:function(G){G=e.Dom.get(G);if(!G){return null;}return e.Dom.getPreviousSiblingBy(G);},getNextSiblingBy:function(G,W){while(G){G=G.nextSibling;if(e.Dom._testElement(G,W)){return G;}}return null;},getNextSibling:function(G){G=e.Dom.get(G);if(!G){return null;}return e.Dom.getNextSiblingBy(G);},getFirstChildBy:function(G,X){var W=(e.Dom._testElement(G.firstChild,X))?G.firstChild:null;return W||e.Dom.getNextSiblingBy(G.firstChild,X);},getFirstChild:function(G,W){G=e.Dom.get(G);if(!G){return null;}return e.Dom.getFirstChildBy(G);},getLastChildBy:function(G,X){if(!G){return null;}var W=(e.Dom._testElement(G.lastChild,X))?G.lastChild:null;return W||e.Dom.getPreviousSiblingBy(G.lastChild,X);},getLastChild:function(G){G=e.Dom.get(G);return e.Dom.getLastChildBy(G);},getChildrenBy:function(W,Y){var X=e.Dom.getFirstChildBy(W,Y),G=X?[X]:[];e.Dom.getNextSiblingBy(X,function(Z){if(!Y||Y(Z)){G[G.length]=Z;}return false;});return G;},getChildren:function(G){G=e.Dom.get(G);if(!G){}return e.Dom.getChildrenBy(G);},getDocumentScrollLeft:function(G){G=G||j;return Math.max(G[U].scrollLeft,G.body.scrollLeft);},getDocumentScrollTop:function(G){G=G||j;return Math.max(G[U].scrollTop,G.body.scrollTop);},insertBefore:function(W,G){W=e.Dom.get(W);G=e.Dom.get(G);if(!W||!G||!G[x]){return null;}return G[x].insertBefore(W,G);},insertAfter:function(W,G){W=e.Dom.get(W);G=e.Dom.get(G);if(!W||!G||!G[x]){return null;}if(G.nextSibling){return G[x].insertBefore(W,G.nextSibling);}else{return G[x].appendChild(W);}},getClientRegion:function(){var X=e.Dom.getDocumentScrollTop(),W=e.Dom.getDocumentScrollLeft(),Y=e.Dom.getViewportWidth()+W,G=e.Dom.getViewportHeight()+X;return new e.Region(X,Y,G,W);},setAttribute:function(W,G,X){e.Dom.batch(W,e.Dom._setAttribute,{attr:G,val:X});},_setAttribute:function(X,W){var G=e.Dom._toCamel(W.attr),Y=W.val;if(X&&X.setAttribute){if(e.Dom.DOT_ATTRIBUTES[G]&&X.tagName&&X.tagName!="BUTTON"){X[G]=Y;}else{G=e.Dom.CUSTOM_ATTRIBUTES[G]||G;X.setAttribute(G,Y);}}else{}},getAttribute:function(W,G){return e.Dom.batch(W,e.Dom._getAttribute,G);},_getAttribute:function(W,G){var X;G=e.Dom.CUSTOM_ATTRIBUTES[G]||G;if(e.Dom.DOT_ATTRIBUTES[G]){X=W[G];}else{if(W&&"getAttribute" in W){if(/^(?:href|src)$/.test(G)){X=W.getAttribute(G,2);}else{X=W.getAttribute(G);}}else{}}return X;},_toCamel:function(W){var X=B;function G(Y,Z){return Z.toUpperCase();}return X[W]||(X[W]=W.indexOf("-")===-1?W:W.replace(/-([a-z])/gi,G));},_getClassRegex:function(W){var G;if(W!==undefined){if(W.exec){G=W;}else{G=F[W];if(!G){W=W.replace(e.Dom._patterns.CLASS_RE_TOKENS,"\\$1");W=W.replace(/\s+/g,b);G=F[W]=new RegExp(R+W+J,t);}}}return G;},_patterns:{ROOT_TAG:/^body|html$/i,CLASS_RE_TOKENS:/([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g},_testElement:function(G,W){return G&&G[K]==1&&(!W||W(G));},_calcBorders:function(X,Y){var W=parseInt(e.Dom[V](X,q),10)||0,G=parseInt(e.Dom[V](X,P),10)||0;if(g){if(m.test(X[c])){W=0;G=0;}}Y[0]+=G;Y[1]+=W;return Y;}};var r=e.Dom[V];if(L.opera){e.Dom[V]=function(W,G){var X=r(W,G);if(w.test(G)){X=e.Dom.Color.toRGB(X);}return X;};}if(L.webkit){e.Dom[V]=function(W,G){var X=r(W,G);if(X==="rgba(0, 0, 0, 0)"){X="transparent";}return X;};}if(L.ie&&L.ie>=8){e.Dom.DOT_ATTRIBUTES.type=true;}})();YAHOO.util.Region=function(d,e,a,c){this.top=d;this.y=d;this[1]=d;this.right=e;this.bottom=a;this.left=c;this.x=c;this[0]=c;this.width=this.right-this.left;this.height=this.bottom-this.top;};YAHOO.util.Region.prototype.contains=function(a){return(a.left>=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(f){var d=Math.max(this.top,f.top),e=Math.min(this.right,f.right),a=Math.min(this.bottom,f.bottom),c=Math.max(this.left,f.left);
-if(a>=d&&e>=c){return new YAHOO.util.Region(d,e,a,c);}else{return null;}};YAHOO.util.Region.prototype.union=function(f){var d=Math.min(this.top,f.top),e=Math.max(this.right,f.right),a=Math.max(this.bottom,f.bottom),c=Math.min(this.left,f.left);return new YAHOO.util.Region(d,e,a,c);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+", height: "+this.height+", width: "+this.width+"}");};YAHOO.util.Region.getRegion=function(e){var g=YAHOO.util.Dom.getXY(e),d=g[1],f=g[0]+e.offsetWidth,a=g[1]+e.offsetHeight,c=g[0];return new YAHOO.util.Region(d,f,a,c);};YAHOO.util.Point=function(a,b){if(YAHOO.lang.isArray(a)){b=a[1];a=a[0];}YAHOO.util.Point.superclass.constructor.call(this,b,a,b,a);};YAHOO.extend(YAHOO.util.Point,YAHOO.util.Region);(function(){var b=YAHOO.util,a="clientTop",f="clientLeft",j="parentNode",k="right",w="hasLayout",i="px",u="opacity",l="auto",d="borderLeftWidth",g="borderTopWidth",p="borderRightWidth",v="borderBottomWidth",s="visible",q="transparent",n="height",e="width",h="style",t="currentStyle",r=/^width|height$/,o=/^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,m={get:function(x,z){var y="",A=x[t][z];if(z===u){y=b.Dom.getStyle(x,u);}else{if(!A||(A.indexOf&&A.indexOf(i)>-1)){y=A;}else{if(b.Dom.IE_COMPUTED[z]){y=b.Dom.IE_COMPUTED[z](x,z);}else{if(o.test(A)){y=b.Dom.IE.ComputedStyle.getPixel(x,z);}else{y=A;}}}}return y;},getOffset:function(z,E){var B=z[t][E],x=E.charAt(0).toUpperCase()+E.substr(1),C="offset"+x,y="pixel"+x,A="",D;if(B==l){D=z[C];if(D===undefined){A=0;}A=D;if(r.test(E)){z[h][E]=D;if(z[C]>D){A=D-(z[C]-D);}z[h][E]=l;}}else{if(!z[h][y]&&!z[h][E]){z[h][E]=B;}A=z[h][y];}return A+i;},getBorderWidth:function(x,z){var y=null;if(!x[t][w]){x[h].zoom=1;}switch(z){case g:y=x[a];break;case v:y=x.offsetHeight-x.clientHeight-x[a];break;case d:y=x[f];break;case p:y=x.offsetWidth-x.clientWidth-x[f];break;}return y+i;},getPixel:function(y,x){var A=null,B=y[t][k],z=y[t][x];y[h][k]=z;A=y[h].pixelRight;y[h][k]=B;return A+i;},getMargin:function(y,x){var z;if(y[t][x]==l){z=0+i;}else{z=b.Dom.IE.ComputedStyle.getPixel(y,x);}return z;},getVisibility:function(y,x){var z;while((z=y[t])&&z[x]=="inherit"){y=y[j];}return(z)?z[x]:s;},getColor:function(y,x){return b.Dom.Color.toRGB(y[t][x])||q;},getBorderColor:function(y,x){var z=y[t],A=z[x]||z.color;return b.Dom.Color.toRGB(b.Dom.Color.toHex(A));}},c={};c.top=c.right=c.bottom=c.left=c[e]=c[n]=m.getOffset;c.color=m.getColor;c[g]=c[p]=c[v]=c[d]=m.getBorderWidth;c.marginTop=c.marginRight=c.marginBottom=c.marginLeft=m.getMargin;c.visibility=m.getVisibility;c.borderColor=c.borderTopColor=c.borderRightColor=c.borderBottomColor=c.borderLeftColor=m.getBorderColor;b.Dom.IE_COMPUTED=c;b.Dom.IE_ComputedStyle=m;})();(function(){var c="toString",a=parseInt,b=RegExp,d=YAHOO.util;d.Dom.Color={KEYWORDS:{black:"000",silver:"c0c0c0",gray:"808080",white:"fff",maroon:"800000",red:"f00",purple:"800080",fuchsia:"f0f",green:"008000",lime:"0f0",olive:"808000",yellow:"ff0",navy:"000080",blue:"00f",teal:"008080",aqua:"0ff"},re_RGB:/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(e){if(!d.Dom.Color.re_RGB.test(e)){e=d.Dom.Color.toHex(e);}if(d.Dom.Color.re_hex.exec(e)){e="rgb("+[a(b.$1,16),a(b.$2,16),a(b.$3,16)].join(", ")+")";}return e;},toHex:function(f){f=d.Dom.Color.KEYWORDS[f]||f;if(d.Dom.Color.re_RGB.exec(f)){f=[Number(b.$1).toString(16),Number(b.$2).toString(16),Number(b.$3).toString(16)];for(var e=0;e<f.length;e++){if(f[e].length<2){f[e]="0"+f[e];}}f=f.join("");}if(f.length<6){f=f.replace(d.Dom.Color.re_hex3,"$1$1");}if(f!=="transparent"&&f.indexOf("#")<0){f="#"+f;}return f.toUpperCase();}};}());YAHOO.register("dom",YAHOO.util.Dom,{version:"2.9.0",build:"2800"});YAHOO.util.CustomEvent=function(d,c,b,a,e){this.type=d;this.scope=c||window;this.silent=b;this.fireOnce=e;this.fired=false;this.firedWith=null;this.signature=a||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var f="_YUICEOnSubscribe";if(d!==f){this.subscribeEvent=new YAHOO.util.CustomEvent(f,this,true);}this.lastError=null;};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(b,c,d){if(!b){throw new Error("Invalid callback for subscriber to '"+this.type+"'");}if(this.subscribeEvent){this.subscribeEvent.fire(b,c,d);}var a=new YAHOO.util.Subscriber(b,c,d);if(this.fireOnce&&this.fired){this.notify(a,this.firedWith);}else{this.subscribers.push(a);}},unsubscribe:function(d,f){if(!d){return this.unsubscribeAll();}var e=false;for(var b=0,a=this.subscribers.length;b<a;++b){var c=this.subscribers[b];if(c&&c.contains(d,f)){this._delete(b);e=true;}}return e;},fire:function(){this.lastError=null;var h=[],a=this.subscribers.length;var d=[].slice.call(arguments,0),c=true,f,b=false;if(this.fireOnce){if(this.fired){return true;}else{this.firedWith=d;}}this.fired=true;if(!a&&this.silent){return true;}if(!this.silent){}var e=this.subscribers.slice();for(f=0;f<a;++f){var g=e[f];if(!g||!g.fn){b=true;}else{c=this.notify(g,d);if(false===c){if(!this.silent){}break;}}}return(c!==false);},notify:function(g,c){var b,i=null,f=g.getScope(this.scope),a=YAHOO.util.Event.throwErrors;if(!this.silent){}if(this.signature==YAHOO.util.CustomEvent.FLAT){if(c.length>0){i=c[0];}try{b=g.fn.call(f,i,g.obj);}catch(h){this.lastError=h;if(a){throw h;}}}else{try{b=g.fn.call(f,this.type,c,g.obj);}catch(d){this.lastError=d;if(a){throw d;}}}return b;},unsubscribeAll:function(){var a=this.subscribers.length,b;for(b=a-1;b>-1;b--){this._delete(b);}this.subscribers=[];return a;},_delete:function(a){var b=this.subscribers[a];if(b){delete b.fn;delete b.obj;}this.subscribers.splice(a,1);},toString:function(){return"CustomEvent: "+"'"+this.type+"', "+"context: "+this.scope;}};YAHOO.util.Subscriber=function(a,b,c){this.fn=a;this.obj=YAHOO.lang.isUndefined(b)?null:b;this.overrideContext=c;};YAHOO.util.Subscriber.prototype.getScope=function(a){if(this.overrideContext){if(this.overrideContext===true){return this.obj;}else{return this.overrideContext;}}return a;};YAHOO.util.Subscriber.prototype.contains=function(a,b){if(b){return(this.fn==a&&this.obj==b);}else{return(this.fn==a);}};YAHOO.util.Subscriber.prototype.toString=function(){return"Subscriber { obj: "+this.obj+", overrideContext: "+(this.overrideContext||"no")+" }";};if(!YAHOO.util.Event){YAHOO.util.Event=function(){var g=false,h=[],j=[],a=0,e=[],b=0,c={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9},d=YAHOO.env.ua.ie,f="focusin",i="focusout";return{POLL_RETRYS:500,POLL_INTERVAL:40,EL:0,TYPE:1,FN:2,WFN:3,UNLOAD_OBJ:3,ADJ_SCOPE:4,OBJ:5,OVERRIDE:6,CAPTURE:7,lastError:null,isSafari:YAHOO.env.ua.webkit,webkit:YAHOO.env.ua.webkit,isIE:d,_interval:null,_dri:null,_specialTypes:{focusin:(d?"focusin":"focus"),focusout:(d?"focusout":"blur")},DOMReady:false,throwErrors:false,startInterval:function(){if(!this._interval){this._interval=YAHOO.lang.later(this.POLL_INTERVAL,this,this._tryPreloadAttach,null,true);}},onAvailable:function(q,m,o,p,n){var k=(YAHOO.lang.isString(q))?[q]:q;for(var l=0;l<k.length;l=l+1){e.push({id:k[l],fn:m,obj:o,overrideContext:p,checkReady:n});}a=this.POLL_RETRYS;this.startInterval();},onContentReady:function(n,k,l,m){this.onAvailable(n,k,l,m,true);},onDOMReady:function(){this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent,arguments);},_addListener:function(m,k,v,p,t,y){if(!v||!v.call){return false;}if(this._isValidCollection(m)){var w=true;for(var q=0,s=m.length;q<s;++q){w=this.on(m[q],k,v,p,t)&&w;}return w;}else{if(YAHOO.lang.isString(m)){var o=this.getEl(m);if(o){m=o;}else{this.onAvailable(m,function(){YAHOO.util.Event._addListener(m,k,v,p,t,y);});return true;}}}if(!m){return false;}if("unload"==k&&p!==this){j[j.length]=[m,k,v,p,t];return true;}var l=m;if(t){if(t===true){l=p;}else{l=t;}}var n=function(z){return v.call(l,YAHOO.util.Event.getEvent(z,m),p);};var x=[m,k,v,n,l,p,t,y];var r=h.length;h[r]=x;try{this._simpleAdd(m,k,n,y);}catch(u){this.lastError=u;this.removeListener(m,k,v);return false;}return true;},_getType:function(k){return this._specialTypes[k]||k;},addListener:function(m,p,l,n,o){var k=((p==f||p==i)&&!YAHOO.env.ua.ie)?true:false;return this._addListener(m,this._getType(p),l,n,o,k);},addFocusListener:function(l,k,m,n){return this.on(l,f,k,m,n);},removeFocusListener:function(l,k){return this.removeListener(l,f,k);},addBlurListener:function(l,k,m,n){return this.on(l,i,k,m,n);},removeBlurListener:function(l,k){return this.removeListener(l,i,k);},removeListener:function(l,k,r){var m,p,u;k=this._getType(k);if(typeof l=="string"){l=this.getEl(l);}else{if(this._isValidCollection(l)){var s=true;for(m=l.length-1;m>-1;m--){s=(this.removeListener(l[m],k,r)&&s);}return s;}}if(!r||!r.call){return this.purgeElement(l,false,k);}if("unload"==k){for(m=j.length-1;m>-1;m--){u=j[m];if(u&&u[0]==l&&u[1]==k&&u[2]==r){j.splice(m,1);return true;}}return false;}var n=null;var o=arguments[3];if("undefined"===typeof o){o=this._getCacheIndex(h,l,k,r);}if(o>=0){n=h[o];}if(!l||!n){return false;}var t=n[this.CAPTURE]===true?true:false;try{this._simpleRemove(l,k,n[this.WFN],t);}catch(q){this.lastError=q;return false;}delete h[o][this.WFN];delete h[o][this.FN];h.splice(o,1);return true;},getTarget:function(m,l){var k=m.target||m.srcElement;return this.resolveTextNode(k);},resolveTextNode:function(l){try{if(l&&3==l.nodeType){return l.parentNode;}}catch(k){return null;}return l;},getPageX:function(l){var k=l.pageX;if(!k&&0!==k){k=l.clientX||0;if(this.isIE){k+=this._getScrollLeft();}}return k;},getPageY:function(k){var l=k.pageY;if(!l&&0!==l){l=k.clientY||0;if(this.isIE){l+=this._getScrollTop();}}return l;},getXY:function(k){return[this.getPageX(k),this.getPageY(k)];},getRelatedTarget:function(l){var k=l.relatedTarget;
-if(!k){if(l.type=="mouseout"){k=l.toElement;}else{if(l.type=="mouseover"){k=l.fromElement;}}}return this.resolveTextNode(k);},getTime:function(m){if(!m.time){var l=new Date().getTime();try{m.time=l;}catch(k){this.lastError=k;return l;}}return m.time;},stopEvent:function(k){this.stopPropagation(k);this.preventDefault(k);},stopPropagation:function(k){if(k.stopPropagation){k.stopPropagation();}else{k.cancelBubble=true;}},preventDefault:function(k){if(k.preventDefault){k.preventDefault();}else{k.returnValue=false;}},getEvent:function(m,k){var l=m||window.event;if(!l){var n=this.getEvent.caller;while(n){l=n.arguments[0];if(l&&Event==l.constructor){break;}n=n.caller;}}return l;},getCharCode:function(l){var k=l.keyCode||l.charCode||0;if(YAHOO.env.ua.webkit&&(k in c)){k=c[k];}return k;},_getCacheIndex:function(n,q,r,p){for(var o=0,m=n.length;o<m;o=o+1){var k=n[o];if(k&&k[this.FN]==p&&k[this.EL]==q&&k[this.TYPE]==r){return o;}}return -1;},generateId:function(k){var l=k.id;if(!l){l="yuievtautoid-"+b;++b;k.id=l;}return l;},_isValidCollection:function(l){try{return(l&&typeof l!=="string"&&l.length&&!l.tagName&&!l.alert&&typeof l[0]!=="undefined");}catch(k){return false;}},elCache:{},getEl:function(k){return(typeof k==="string")?document.getElementById(k):k;},clearCache:function(){},DOMReadyEvent:new YAHOO.util.CustomEvent("DOMReady",YAHOO,0,0,1),_load:function(l){if(!g){g=true;var k=YAHOO.util.Event;k._ready();k._tryPreloadAttach();}},_ready:function(l){var k=YAHOO.util.Event;if(!k.DOMReady){k.DOMReady=true;k.DOMReadyEvent.fire();k._simpleRemove(document,"DOMContentLoaded",k._ready);}},_tryPreloadAttach:function(){if(e.length===0){a=0;if(this._interval){this._interval.cancel();this._interval=null;}return;}if(this.locked){return;}if(this.isIE){if(!this.DOMReady){this.startInterval();return;}}this.locked=true;var q=!g;if(!q){q=(a>0&&e.length>0);}var p=[];var r=function(t,u){var s=t;if(u.overrideContext){if(u.overrideContext===true){s=u.obj;}else{s=u.overrideContext;}}u.fn.call(s,u.obj);};var l,k,o,n,m=[];for(l=0,k=e.length;l<k;l=l+1){o=e[l];if(o){n=this.getEl(o.id);if(n){if(o.checkReady){if(g||n.nextSibling||!q){m.push(o);e[l]=null;}}else{r(n,o);e[l]=null;}}else{p.push(o);}}}for(l=0,k=m.length;l<k;l=l+1){o=m[l];r(this.getEl(o.id),o);}a--;if(q){for(l=e.length-1;l>-1;l--){o=e[l];if(!o||!o.id){e.splice(l,1);}}this.startInterval();}else{if(this._interval){this._interval.cancel();this._interval=null;}}this.locked=false;},purgeElement:function(p,q,s){var n=(YAHOO.lang.isString(p))?this.getEl(p):p;var r=this.getListeners(n,s),o,k;if(r){for(o=r.length-1;o>-1;o--){var m=r[o];this.removeListener(n,m.type,m.fn);}}if(q&&n&&n.childNodes){for(o=0,k=n.childNodes.length;o<k;++o){this.purgeElement(n.childNodes[o],q,s);}}},getListeners:function(n,k){var q=[],m;if(!k){m=[h,j];}else{if(k==="unload"){m=[j];}else{k=this._getType(k);m=[h];}}var s=(YAHOO.lang.isString(n))?this.getEl(n):n;for(var p=0;p<m.length;p=p+1){var u=m[p];if(u){for(var r=0,t=u.length;r<t;++r){var o=u[r];if(o&&o[this.EL]===s&&(!k||k===o[this.TYPE])){q.push({type:o[this.TYPE],fn:o[this.FN],obj:o[this.OBJ],adjust:o[this.OVERRIDE],scope:o[this.ADJ_SCOPE],index:r});}}}}return(q.length)?q:null;},_unload:function(s){var m=YAHOO.util.Event,p,o,n,r,q,t=j.slice(),k;for(p=0,r=j.length;p<r;++p){n=t[p];if(n){try{k=window;if(n[m.ADJ_SCOPE]){if(n[m.ADJ_SCOPE]===true){k=n[m.UNLOAD_OBJ];}else{k=n[m.ADJ_SCOPE];}}n[m.FN].call(k,m.getEvent(s,n[m.EL]),n[m.UNLOAD_OBJ]);}catch(w){}t[p]=null;}}n=null;k=null;j=null;if(h){for(o=h.length-1;o>-1;o--){n=h[o];if(n){try{m.removeListener(n[m.EL],n[m.TYPE],n[m.FN],o);}catch(v){}}}n=null;}try{m._simpleRemove(window,"unload",m._unload);m._simpleRemove(window,"load",m._load);}catch(u){}},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var k=document.documentElement,l=document.body;if(k&&(k.scrollTop||k.scrollLeft)){return[k.scrollTop,k.scrollLeft];}else{if(l){return[l.scrollTop,l.scrollLeft];}else{return[0,0];}}},regCE:function(){},_simpleAdd:function(){if(window.addEventListener){return function(m,n,l,k){m.addEventListener(n,l,(k));};}else{if(window.attachEvent){return function(m,n,l,k){m.attachEvent("on"+n,l);};}else{return function(){};}}}(),_simpleRemove:function(){if(window.removeEventListener){return function(m,n,l,k){m.removeEventListener(n,l,(k));};}else{if(window.detachEvent){return function(l,m,k){l.detachEvent("on"+m,k);};}else{return function(){};}}}()};}();(function(){var a=YAHOO.util.Event;a.on=a.addListener;a.onFocus=a.addFocusListener;a.onBlur=a.addBlurListener;
-/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
-if(a.isIE){if(self!==self.top){document.onreadystatechange=function(){if(document.readyState=="complete"){document.onreadystatechange=null;a._ready();}};}else{YAHOO.util.Event.onDOMReady(YAHOO.util.Event._tryPreloadAttach,YAHOO.util.Event,true);var b=document.createElement("p");a._dri=setInterval(function(){try{b.doScroll("left");clearInterval(a._dri);a._dri=null;a._ready();b=null;}catch(c){}},a.POLL_INTERVAL);}}else{if(a.webkit&&a.webkit<525){a._dri=setInterval(function(){var c=document.readyState;if("loaded"==c||"complete"==c){clearInterval(a._dri);a._dri=null;a._ready();}},a.POLL_INTERVAL);}else{a._simpleAdd(document,"DOMContentLoaded",a._ready);}}a._simpleAdd(window,"load",a._load);a._simpleAdd(window,"unload",a._unload);a._tryPreloadAttach();})();}YAHOO.util.EventProvider=function(){};YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(a,c,f,e){this.__yui_events=this.__yui_events||{};var d=this.__yui_events[a];if(d){d.subscribe(c,f,e);}else{this.__yui_subscribers=this.__yui_subscribers||{};var b=this.__yui_subscribers;if(!b[a]){b[a]=[];}b[a].push({fn:c,obj:f,overrideContext:e});}},unsubscribe:function(c,e,g){this.__yui_events=this.__yui_events||{};var a=this.__yui_events;if(c){var f=a[c];if(f){return f.unsubscribe(e,g);}}else{var b=true;for(var d in a){if(YAHOO.lang.hasOwnProperty(a,d)){b=b&&a[d].unsubscribe(e,g);
-}}return b;}return false;},unsubscribeAll:function(a){return this.unsubscribe(a);},createEvent:function(b,g){this.__yui_events=this.__yui_events||{};var e=g||{},d=this.__yui_events,f;if(d[b]){}else{f=new YAHOO.util.CustomEvent(b,e.scope||this,e.silent,YAHOO.util.CustomEvent.FLAT,e.fireOnce);d[b]=f;if(e.onSubscribeCallback){f.subscribeEvent.subscribe(e.onSubscribeCallback);}this.__yui_subscribers=this.__yui_subscribers||{};var a=this.__yui_subscribers[b];if(a){for(var c=0;c<a.length;++c){f.subscribe(a[c].fn,a[c].obj,a[c].overrideContext);}}}return d[b];},fireEvent:function(b){this.__yui_events=this.__yui_events||{};var d=this.__yui_events[b];if(!d){return null;}var a=[];for(var c=1;c<arguments.length;++c){a.push(arguments[c]);}return d.fire.apply(d,a);},hasEvent:function(a){if(this.__yui_events){if(this.__yui_events[a]){return true;}}return false;}};(function(){var a=YAHOO.util.Event,c=YAHOO.lang;YAHOO.util.KeyListener=function(d,i,e,f){if(!d){}else{if(!i){}else{if(!e){}}}if(!f){f=YAHOO.util.KeyListener.KEYDOWN;}var g=new YAHOO.util.CustomEvent("keyPressed");this.enabledEvent=new YAHOO.util.CustomEvent("enabled");this.disabledEvent=new YAHOO.util.CustomEvent("disabled");if(c.isString(d)){d=document.getElementById(d);}if(c.isFunction(e)){g.subscribe(e);}else{g.subscribe(e.fn,e.scope,e.correctScope);}function h(o,n){if(!i.shift){i.shift=false;}if(!i.alt){i.alt=false;}if(!i.ctrl){i.ctrl=false;}if(o.shiftKey==i.shift&&o.altKey==i.alt&&o.ctrlKey==i.ctrl){var j,m=i.keys,l;if(YAHOO.lang.isArray(m)){for(var k=0;k<m.length;k++){j=m[k];l=a.getCharCode(o);if(j==l){g.fire(l,o);break;}}}else{l=a.getCharCode(o);if(m==l){g.fire(l,o);}}}}this.enable=function(){if(!this.enabled){a.on(d,f,h);this.enabledEvent.fire(i);}this.enabled=true;};this.disable=function(){if(this.enabled){a.removeListener(d,f,h);this.disabledEvent.fire(i);}this.enabled=false;};this.toString=function(){return"KeyListener ["+i.keys+"] "+d.tagName+(d.id?"["+d.id+"]":"");};};var b=YAHOO.util.KeyListener;b.KEYDOWN="keydown";b.KEYUP="keyup";b.KEY={ALT:18,BACK_SPACE:8,CAPS_LOCK:20,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,META:224,NUM_LOCK:144,PAGE_DOWN:34,PAGE_UP:33,PAUSE:19,PRINTSCREEN:44,RIGHT:39,SCROLL_LOCK:145,SHIFT:16,SPACE:32,TAB:9,UP:38};})();YAHOO.register("event",YAHOO.util.Event,{version:"2.9.0",build:"2800"});YAHOO.util.Connect={_msxml_progid:["Microsoft.XMLHTTP","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP"],_http_headers:{},_has_http_headers:false,_use_default_post_header:true,_default_post_header:"application/x-www-form-urlencoded; charset=UTF-8",_default_form_header:"application/x-www-form-urlencoded",_use_default_xhr_header:true,_default_xhr_header:"XMLHttpRequest",_has_default_headers:true,_isFormSubmit:false,_default_headers:{},_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,startEvent:new YAHOO.util.CustomEvent("start"),completeEvent:new YAHOO.util.CustomEvent("complete"),successEvent:new YAHOO.util.CustomEvent("success"),failureEvent:new YAHOO.util.CustomEvent("failure"),abortEvent:new YAHOO.util.CustomEvent("abort"),_customEvents:{onStart:["startEvent","start"],onComplete:["completeEvent","complete"],onSuccess:["successEvent","success"],onFailure:["failureEvent","failure"],onUpload:["uploadEvent","upload"],onAbort:["abortEvent","abort"]},setProgId:function(a){this._msxml_progid.unshift(a);},setDefaultPostHeader:function(a){if(typeof a=="string"){this._default_post_header=a;this._use_default_post_header=true;}else{if(typeof a=="boolean"){this._use_default_post_header=a;}}},setDefaultXhrHeader:function(a){if(typeof a=="string"){this._default_xhr_header=a;}else{this._use_default_xhr_header=a;}},setPollingInterval:function(a){if(typeof a=="number"&&isFinite(a)){this._polling_interval=a;}},createXhrObject:function(g){var d,a,b;try{a=new XMLHttpRequest();d={conn:a,tId:g,xhr:true};}catch(c){for(b=0;b<this._msxml_progid.length;++b){try{a=new ActiveXObject(this._msxml_progid[b]);d={conn:a,tId:g,xhr:true};break;}catch(f){}}}finally{return d;}},getConnectionObject:function(a){var c,d=this._transaction_id;try{if(!a){c=this.createXhrObject(d);}else{c={tId:d};if(a==="xdr"){c.conn=this._transport;c.xdr=true;}else{if(a==="upload"){c.upload=true;}}}if(c){this._transaction_id++;}}catch(b){}return c;},asyncRequest:function(h,d,g,a){var b=g&&g.argument?g.argument:null,e=this,f,c;if(this._isFileUpload){c="upload";}else{if(g&&g.xdr){c="xdr";}}f=this.getConnectionObject(c);if(!f){return null;}else{if(g&&g.customevents){this.initCustomEvents(f,g);}if(this._isFormSubmit){if(this._isFileUpload){window.setTimeout(function(){e.uploadFile(f,g,d,a);},10);return f;}if(h.toUpperCase()=="GET"){if(this._sFormData.length!==0){d+=((d.indexOf("?")==-1)?"?":"&")+this._sFormData;}}else{if(h.toUpperCase()=="POST"){a=a?this._sFormData+"&"+a:this._sFormData;}}}if(h.toUpperCase()=="GET"&&(g&&g.cache===false)){d+=((d.indexOf("?")==-1)?"?":"&")+"rnd="+new Date().valueOf().toString();}if(this._use_default_xhr_header){if(!this._default_headers["X-Requested-With"]){this.initHeader("X-Requested-With",this._default_xhr_header,true);}}if((h.toUpperCase()==="POST"&&this._use_default_post_header)&&this._isFormSubmit===false){this.initHeader("Content-Type",this._default_post_header);}if(f.xdr){this.xdr(f,h,d,g,a);return f;}f.conn.open(h,d,true);if(this._has_default_headers||this._has_http_headers){this.setHeader(f);}this.handleReadyState(f,g);f.conn.send(a||"");if(this._isFormSubmit===true){this.resetFormState();}this.startEvent.fire(f,b);if(f.startEvent){f.startEvent.fire(f,b);}return f;}},initCustomEvents:function(a,c){var b;for(b in c.customevents){if(this._customEvents[b][0]){a[this._customEvents[b][0]]=new YAHOO.util.CustomEvent(this._customEvents[b][1],(c.scope)?c.scope:null);a[this._customEvents[b][0]].subscribe(c.customevents[b]);}}},handleReadyState:function(c,d){var b=this,a=(d&&d.argument)?d.argument:null;if(d&&d.timeout){this._timeOut[c.tId]=window.setTimeout(function(){b.abort(c,d,true);},d.timeout);}this._poll[c.tId]=window.setInterval(function(){if(c.conn&&c.conn.readyState===4){window.clearInterval(b._poll[c.tId]);delete b._poll[c.tId];if(d&&d.timeout){window.clearTimeout(b._timeOut[c.tId]);delete b._timeOut[c.tId];}b.completeEvent.fire(c,a);if(c.completeEvent){c.completeEvent.fire(c,a);}b.handleTransactionResponse(c,d);}},this._polling_interval);},handleTransactionResponse:function(b,j,d){var f,a,h=(j&&j.argument)?j.argument:null,c=(b.r&&b.r.statusText==="xdr:success")?true:false,i=(b.r&&b.r.statusText==="xdr:failure")?true:false,k=d;try{if((b.conn.status!==undefined&&b.conn.status!==0)||c){f=b.conn.status;}else{if(i&&!k){f=0;}else{f=13030;}}}catch(g){f=13030;}if((f>=200&&f<300)||f===1223||c){a=b.xdr?b.r:this.createResponseObject(b,h);if(j&&j.success){if(!j.scope){j.success(a);}else{j.success.apply(j.scope,[a]);}}this.successEvent.fire(a);if(b.successEvent){b.successEvent.fire(a);}}else{switch(f){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:a=this.createExceptionObject(b.tId,h,(d?d:false));if(j&&j.failure){if(!j.scope){j.failure(a);}else{j.failure.apply(j.scope,[a]);}}break;default:a=(b.xdr)?b.response:this.createResponseObject(b,h);if(j&&j.failure){if(!j.scope){j.failure(a);}else{j.failure.apply(j.scope,[a]);}}}this.failureEvent.fire(a);if(b.failureEvent){b.failureEvent.fire(a);}}this.releaseObject(b);a=null;},createResponseObject:function(a,h){var d={},k={},f,c,g,b;try{c=a.conn.getAllResponseHeaders();g=c.split("\n");for(f=0;f<g.length;f++){b=g[f].indexOf(":");if(b!=-1){k[g[f].substring(0,b)]=YAHOO.lang.trim(g[f].substring(b+2));}}}catch(j){}d.tId=a.tId;d.status=(a.conn.status==1223)?204:a.conn.status;d.statusText=(a.conn.status==1223)?"No Content":a.conn.statusText;d.getResponseHeader=k;d.getAllResponseHeaders=c;d.responseText=a.conn.responseText;d.responseXML=a.conn.responseXML;if(h){d.argument=h;}return d;},createExceptionObject:function(h,d,a){var f=0,g="communication failure",c=-1,b="transaction aborted",e={};e.tId=h;if(a){e.status=c;e.statusText=b;}else{e.status=f;e.statusText=g;}if(d){e.argument=d;}return e;},initHeader:function(a,d,c){var b=(c)?this._default_headers:this._http_headers;b[a]=d;if(c){this._has_default_headers=true;}else{this._has_http_headers=true;}},setHeader:function(a){var b;if(this._has_default_headers){for(b in this._default_headers){if(YAHOO.lang.hasOwnProperty(this._default_headers,b)){a.conn.setRequestHeader(b,this._default_headers[b]);
-}}}if(this._has_http_headers){for(b in this._http_headers){if(YAHOO.lang.hasOwnProperty(this._http_headers,b)){a.conn.setRequestHeader(b,this._http_headers[b]);}}this._http_headers={};this._has_http_headers=false;}},resetDefaultHeaders:function(){this._default_headers={};this._has_default_headers=false;},abort:function(e,g,a){var d,b=(g&&g.argument)?g.argument:null;e=e||{};if(e.conn){if(e.xhr){if(this.isCallInProgress(e)){e.conn.abort();window.clearInterval(this._poll[e.tId]);delete this._poll[e.tId];if(a){window.clearTimeout(this._timeOut[e.tId]);delete this._timeOut[e.tId];}d=true;}}else{if(e.xdr){e.conn.abort(e.tId);d=true;}}}else{if(e.upload){var c="yuiIO"+e.tId;var f=document.getElementById(c);if(f){YAHOO.util.Event.removeListener(f,"load");document.body.removeChild(f);if(a){window.clearTimeout(this._timeOut[e.tId]);delete this._timeOut[e.tId];}d=true;}}else{d=false;}}if(d===true){this.abortEvent.fire(e,b);if(e.abortEvent){e.abortEvent.fire(e,b);}this.handleTransactionResponse(e,g,true);}return d;},isCallInProgress:function(a){a=a||{};if(a.xhr&&a.conn){return a.conn.readyState!==4&&a.conn.readyState!==0;}else{if(a.xdr&&a.conn){return a.conn.isCallInProgress(a.tId);}else{if(a.upload===true){return document.getElementById("yuiIO"+a.tId)?true:false;}else{return false;}}}},releaseObject:function(a){if(a&&a.conn){a.conn=null;a=null;}}};(function(){var g=YAHOO.util.Connect,h={};function d(i){var j='<object id="YUIConnectionSwf" type="application/x-shockwave-flash" data="'+i+'" width="0" height="0">'+'<param name="movie" value="'+i+'">'+'<param name="allowScriptAccess" value="always">'+"</object>",k=document.createElement("div");document.body.appendChild(k);k.innerHTML=j;}function b(l,i,j,n,k){h[parseInt(l.tId)]={"o":l,"c":n};if(k){n.method=i;n.data=k;}l.conn.send(j,n,l.tId);}function e(i){d(i);g._transport=document.getElementById("YUIConnectionSwf");}function c(){g.xdrReadyEvent.fire();}function a(j,i){if(j){g.startEvent.fire(j,i.argument);if(j.startEvent){j.startEvent.fire(j,i.argument);}}}function f(j){var k=h[j.tId].o,i=h[j.tId].c;if(j.statusText==="xdr:start"){a(k,i);return;}j.responseText=decodeURI(j.responseText);k.r=j;if(i.argument){k.r.argument=i.argument;}this.handleTransactionResponse(k,i,j.statusText==="xdr:abort"?true:false);delete h[j.tId];}g.xdr=b;g.swf=d;g.transport=e;g.xdrReadyEvent=new YAHOO.util.CustomEvent("xdrReady");g.xdrReady=c;g.handleXdrResponse=f;})();(function(){var e=YAHOO.util.Connect,g=YAHOO.util.Event,a=document.documentMode?document.documentMode:false;e._isFileUpload=false;e._formNode=null;e._sFormData=null;e._submitElementValue=null;e.uploadEvent=new YAHOO.util.CustomEvent("upload");e._hasSubmitListener=function(){if(g){g.addListener(document,"click",function(k){var j=g.getTarget(k),i=j.nodeName.toLowerCase();if((i==="input"||i==="button")&&(j.type&&j.type.toLowerCase()=="submit")){e._submitElementValue=encodeURIComponent(j.name)+"="+encodeURIComponent(j.value);}});return true;}return false;}();function h(w,r,m){var v,l,u,s,z,t=false,p=[],y=0,o,q,n,x,k;this.resetFormState();if(typeof w=="string"){v=(document.getElementById(w)||document.forms[w]);}else{if(typeof w=="object"){v=w;}else{return;}}if(r){this.createFrame(m?m:null);this._isFormSubmit=true;this._isFileUpload=true;this._formNode=v;return;}for(o=0,q=v.elements.length;o<q;++o){l=v.elements[o];z=l.disabled;u=l.name;if(!z&&u){u=encodeURIComponent(u)+"=";s=encodeURIComponent(l.value);switch(l.type){case"select-one":if(l.selectedIndex>-1){k=l.options[l.selectedIndex];p[y++]=u+encodeURIComponent((k.attributes.value&&k.attributes.value.specified)?k.value:k.text);}break;case"select-multiple":if(l.selectedIndex>-1){for(n=l.selectedIndex,x=l.options.length;n<x;++n){k=l.options[n];if(k.selected){p[y++]=u+encodeURIComponent((k.attributes.value&&k.attributes.value.specified)?k.value:k.text);}}}break;case"radio":case"checkbox":if(l.checked){p[y++]=u+s;}break;case"file":case undefined:case"reset":case"button":break;case"submit":if(t===false){if(this._hasSubmitListener&&this._submitElementValue){p[y++]=this._submitElementValue;}t=true;}break;default:p[y++]=u+s;}}}this._isFormSubmit=true;this._sFormData=p.join("&");this.initHeader("Content-Type",this._default_form_header);return this._sFormData;}function d(){this._isFormSubmit=false;this._isFileUpload=false;this._formNode=null;this._sFormData="";}function c(i){var j="yuiIO"+this._transaction_id,l=(a===9)?true:false,k;if(YAHOO.env.ua.ie&&!l){k=document.createElement('<iframe id="'+j+'" name="'+j+'" />');if(typeof i=="boolean"){k.src="javascript:false";}}else{k=document.createElement("iframe");k.id=j;k.name=j;}k.style.position="absolute";k.style.top="-1000px";k.style.left="-1000px";document.body.appendChild(k);}function f(j){var m=[],k=j.split("&"),l,n;for(l=0;l<k.length;l++){n=k[l].indexOf("=");if(n!=-1){m[l]=document.createElement("input");m[l].type="hidden";m[l].name=decodeURIComponent(k[l].substring(0,n));m[l].value=decodeURIComponent(k[l].substring(n+1));this._formNode.appendChild(m[l]);}}return m;}function b(m,y,n,l){var t="yuiIO"+m.tId,u="multipart/form-data",w=document.getElementById(t),p=(a>=8)?true:false,z=this,v=(y&&y.argument)?y.argument:null,x,s,k,r,j,q;j={action:this._formNode.getAttribute("action"),method:this._formNode.getAttribute("method"),target:this._formNode.getAttribute("target")};this._formNode.setAttribute("action",n);this._formNode.setAttribute("method","POST");this._formNode.setAttribute("target",t);if(YAHOO.env.ua.ie&&!p){this._formNode.setAttribute("encoding",u);}else{this._formNode.setAttribute("enctype",u);}if(l){x=this.appendPostData(l);}this._formNode.submit();this.startEvent.fire(m,v);if(m.startEvent){m.startEvent.fire(m,v);}if(y&&y.timeout){this._timeOut[m.tId]=window.setTimeout(function(){z.abort(m,y,true);},y.timeout);}if(x&&x.length>0){for(s=0;s<x.length;s++){this._formNode.removeChild(x[s]);}}for(k in j){if(YAHOO.lang.hasOwnProperty(j,k)){if(j[k]){this._formNode.setAttribute(k,j[k]);}else{this._formNode.removeAttribute(k);}}}this.resetFormState();
-q=function(){var i,A,B;if(y&&y.timeout){window.clearTimeout(z._timeOut[m.tId]);delete z._timeOut[m.tId];}z.completeEvent.fire(m,v);if(m.completeEvent){m.completeEvent.fire(m,v);}r={tId:m.tId,argument:v};try{i=w.contentWindow.document.getElementsByTagName("body")[0];A=w.contentWindow.document.getElementsByTagName("pre")[0];if(i){if(A){B=A.textContent?A.textContent:A.innerText;}else{B=i.textContent?i.textContent:i.innerText;}}r.responseText=B;r.responseXML=w.contentWindow.document.XMLDocument?w.contentWindow.document.XMLDocument:w.contentWindow.document;}catch(o){}if(y&&y.upload){if(!y.scope){y.upload(r);}else{y.upload.apply(y.scope,[r]);}}z.uploadEvent.fire(r);if(m.uploadEvent){m.uploadEvent.fire(r);}g.removeListener(w,"load",q);setTimeout(function(){document.body.removeChild(w);z.releaseObject(m);},100);};g.addListener(w,"load",q);}e.setForm=h;e.resetFormState=d;e.createFrame=c;e.appendPostData=f;e.uploadFile=b;})();YAHOO.register("connection",YAHOO.util.Connect,{version:"2.9.0",build:"2800"});(function(){var b=YAHOO.util;var a=function(d,c,e,f){if(!d){}this.init(d,c,e,f);};a.NAME="Anim";a.prototype={toString:function(){var c=this.getEl()||{};var d=c.id||c.tagName;return(this.constructor.NAME+": "+d);},patterns:{noNegatives:/width|height|opacity|padding/i,offsetAttribute:/^((width|height)|(top|left))$/,defaultUnit:/width|height|top$|bottom$|left$|right$/i,offsetUnit:/\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i},doMethod:function(c,e,d){return this.method(this.currentFrame,e,d-e,this.totalFrames);},setAttribute:function(c,f,e){var d=this.getEl();if(this.patterns.noNegatives.test(c)){f=(f>0)?f:0;}if(c in d&&!("style" in d&&c in d.style)){d[c]=f;}else{b.Dom.setStyle(d,c,f+e);}},getAttribute:function(c){var e=this.getEl();var g=b.Dom.getStyle(e,c);if(g!=="auto"&&!this.patterns.offsetUnit.test(g)){return parseFloat(g);}var d=this.patterns.offsetAttribute.exec(c)||[];var h=!!(d[3]);var f=!!(d[2]);if("style" in e){if(f||(b.Dom.getStyle(e,"position")=="absolute"&&h)){g=e["offset"+d[0].charAt(0).toUpperCase()+d[0].substr(1)];}else{g=0;}}else{if(c in e){g=e[c];}}return g;},getDefaultUnit:function(c){if(this.patterns.defaultUnit.test(c)){return"px";}return"";},setRuntimeAttribute:function(d){var j;var e;var f=this.attributes;this.runtimeAttributes[d]={};var h=function(i){return(typeof i!=="undefined");};if(!h(f[d]["to"])&&!h(f[d]["by"])){return false;}j=(h(f[d]["from"]))?f[d]["from"]:this.getAttribute(d);if(h(f[d]["to"])){e=f[d]["to"];}else{if(h(f[d]["by"])){if(j.constructor==Array){e=[];for(var g=0,c=j.length;g<c;++g){e[g]=j[g]+f[d]["by"][g]*1;}}else{e=j+f[d]["by"]*1;}}}this.runtimeAttributes[d].start=j;this.runtimeAttributes[d].end=e;this.runtimeAttributes[d].unit=(h(f[d].unit))?f[d]["unit"]:this.getDefaultUnit(d);return true;},init:function(f,c,h,i){var d=false;var e=null;var g=0;f=b.Dom.get(f);this.attributes=c||{};this.duration=!YAHOO.lang.isUndefined(h)?h:1;this.method=i||b.Easing.easeNone;this.useSeconds=true;this.currentFrame=0;this.totalFrames=b.AnimMgr.fps;this.setEl=function(j){f=b.Dom.get(j);};this.getEl=function(){return f;};this.isAnimated=function(){return d;};this.getStartTime=function(){return e;};this.runtimeAttributes={};this.animate=function(){if(this.isAnimated()){return false;}this.currentFrame=0;this.totalFrames=(this.useSeconds)?Math.ceil(b.AnimMgr.fps*this.duration):this.duration;if(this.duration===0&&this.useSeconds){this.totalFrames=1;}b.AnimMgr.registerElement(this);return true;};this.stop=function(j){if(!this.isAnimated()){return false;}if(j){this.currentFrame=this.totalFrames;this._onTween.fire();}b.AnimMgr.stop(this);};this._handleStart=function(){this.onStart.fire();this.runtimeAttributes={};for(var j in this.attributes){if(this.attributes.hasOwnProperty(j)){this.setRuntimeAttribute(j);}}d=true;g=0;e=new Date();};this._handleTween=function(){var l={duration:new Date()-this.getStartTime(),currentFrame:this.currentFrame};l.toString=function(){return("duration: "+l.duration+", currentFrame: "+l.currentFrame);};this.onTween.fire(l);var k=this.runtimeAttributes;for(var j in k){if(k.hasOwnProperty(j)){this.setAttribute(j,this.doMethod(j,k[j].start,k[j].end),k[j].unit);}}this.afterTween.fire(l);g+=1;};this._handleComplete=function(){var j=(new Date()-e)/1000;var k={duration:j,frames:g,fps:g/j};k.toString=function(){return("duration: "+k.duration+", frames: "+k.frames+", fps: "+k.fps);};d=false;g=0;this.onComplete.fire(k);};this._onStart=new b.CustomEvent("_start",this,true);this.onStart=new b.CustomEvent("start",this);this.onTween=new b.CustomEvent("tween",this);this.afterTween=new b.CustomEvent("afterTween",this);this._onTween=new b.CustomEvent("_tween",this,true);this.onComplete=new b.CustomEvent("complete",this);this._onComplete=new b.CustomEvent("_complete",this,true);this._onStart.subscribe(this._handleStart);this._onTween.subscribe(this._handleTween);this._onComplete.subscribe(this._handleComplete);}};b.Anim=a;})();YAHOO.util.AnimMgr=new function(){var e=null;var c=[];var g=0;this.fps=1000;this.delay=20;this.registerElement=function(j){c[c.length]=j;g+=1;j._onStart.fire();this.start();};var f=[];var d=false;var h=function(){var j=f.shift();b.apply(YAHOO.util.AnimMgr,j);if(f.length){arguments.callee();}};var b=function(k,j){j=j||a(k);if(!k.isAnimated()||j===-1){return false;}k._onComplete.fire();c.splice(j,1);g-=1;if(g<=0){this.stop();}return true;};this.unRegister=function(){f.push(arguments);if(!d){d=true;h();d=false;}};this.start=function(){if(e===null){e=setInterval(this.run,this.delay);}};this.stop=function(l){if(!l){clearInterval(e);for(var k=0,j=c.length;k<j;++k){this.unRegister(c[0],0);}c=[];e=null;g=0;}else{this.unRegister(l);}};this.run=function(){for(var l=0,j=c.length;l<j;++l){var k=c[l];if(!k||!k.isAnimated()){continue;}if(k.currentFrame<k.totalFrames||k.totalFrames===null){k.currentFrame+=1;if(k.useSeconds){i(k);}k._onTween.fire();}else{YAHOO.util.AnimMgr.stop(k,l);}}};var a=function(l){for(var k=0,j=c.length;k<j;++k){if(c[k]===l){return k;}}return -1;};var i=function(k){var n=k.totalFrames;var m=k.currentFrame;var l=(k.currentFrame*k.duration*1000/k.totalFrames);var j=(new Date()-k.getStartTime());var o=0;if(j<k.duration*1000){o=Math.round((j/l-1)*k.currentFrame);}else{o=n-(m+1);}if(o>0&&isFinite(o)){if(k.currentFrame+o>=n){o=n-(m+1);}k.currentFrame+=o;}};this._queue=c;this._getIndex=a;};YAHOO.util.Bezier=new function(){this.getPosition=function(e,d){var f=e.length;var c=[];for(var b=0;b<f;++b){c[b]=[e[b][0],e[b][1]];}for(var a=1;a<f;++a){for(b=0;b<f-a;++b){c[b][0]=(1-d)*c[b][0]+d*c[parseInt(b+1,10)][0];c[b][1]=(1-d)*c[b][1]+d*c[parseInt(b+1,10)][1];}}return[c[0][0],c[0][1]];};};(function(){var a=function(f,e,g,h){a.superclass.constructor.call(this,f,e,g,h);};a.NAME="ColorAnim";a.DEFAULT_BGCOLOR="#fff";var c=YAHOO.util;YAHOO.extend(a,c.Anim);var d=a.superclass;var b=a.prototype;b.patterns.color=/color$/i;b.patterns.rgb=/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;b.patterns.hex=/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;b.patterns.hex3=/^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
-b.patterns.transparent=/^transparent|rgba\(0, 0, 0, 0\)$/;b.parseColor=function(e){if(e.length==3){return e;}var f=this.patterns.hex.exec(e);if(f&&f.length==4){return[parseInt(f[1],16),parseInt(f[2],16),parseInt(f[3],16)];}f=this.patterns.rgb.exec(e);if(f&&f.length==4){return[parseInt(f[1],10),parseInt(f[2],10),parseInt(f[3],10)];}f=this.patterns.hex3.exec(e);if(f&&f.length==4){return[parseInt(f[1]+f[1],16),parseInt(f[2]+f[2],16),parseInt(f[3]+f[3],16)];}return null;};b.getAttribute=function(e){var g=this.getEl();if(this.patterns.color.test(e)){var i=YAHOO.util.Dom.getStyle(g,e);var h=this;if(this.patterns.transparent.test(i)){var f=YAHOO.util.Dom.getAncestorBy(g,function(j){return !h.patterns.transparent.test(i);});if(f){i=c.Dom.getStyle(f,e);}else{i=a.DEFAULT_BGCOLOR;}}}else{i=d.getAttribute.call(this,e);}return i;};b.doMethod=function(f,k,g){var j;if(this.patterns.color.test(f)){j=[];for(var h=0,e=k.length;h<e;++h){j[h]=d.doMethod.call(this,f,k[h],g[h]);}j="rgb("+Math.floor(j[0])+","+Math.floor(j[1])+","+Math.floor(j[2])+")";}else{j=d.doMethod.call(this,f,k,g);}return j;};b.setRuntimeAttribute=function(f){d.setRuntimeAttribute.call(this,f);if(this.patterns.color.test(f)){var h=this.attributes;var k=this.parseColor(this.runtimeAttributes[f].start);var g=this.parseColor(this.runtimeAttributes[f].end);if(typeof h[f]["to"]==="undefined"&&typeof h[f]["by"]!=="undefined"){g=this.parseColor(h[f].by);for(var j=0,e=k.length;j<e;++j){g[j]=k[j]+g[j];}}this.runtimeAttributes[f].start=k;this.runtimeAttributes[f].end=g;}};c.ColorAnim=a;})();
-/*!
-TERMS OF USE - EASING EQUATIONS
-Open source under the BSD License.
-Copyright 2001 Robert Penner 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 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.
-*/
-YAHOO.util.Easing={easeNone:function(e,a,g,f){return g*e/f+a;},easeIn:function(e,a,g,f){return g*(e/=f)*e+a;},easeOut:function(e,a,g,f){return -g*(e/=f)*(e-2)+a;},easeBoth:function(e,a,g,f){if((e/=f/2)<1){return g/2*e*e+a;}return -g/2*((--e)*(e-2)-1)+a;},easeInStrong:function(e,a,g,f){return g*(e/=f)*e*e*e+a;},easeOutStrong:function(e,a,g,f){return -g*((e=e/f-1)*e*e*e-1)+a;},easeBothStrong:function(e,a,g,f){if((e/=f/2)<1){return g/2*e*e*e*e+a;}return -g/2*((e-=2)*e*e*e-2)+a;},elasticIn:function(g,e,k,j,f,i){if(g==0){return e;}if((g/=j)==1){return e+k;}if(!i){i=j*0.3;}if(!f||f<Math.abs(k)){f=k;var h=i/4;}else{var h=i/(2*Math.PI)*Math.asin(k/f);}return -(f*Math.pow(2,10*(g-=1))*Math.sin((g*j-h)*(2*Math.PI)/i))+e;},elasticOut:function(g,e,k,j,f,i){if(g==0){return e;}if((g/=j)==1){return e+k;}if(!i){i=j*0.3;}if(!f||f<Math.abs(k)){f=k;var h=i/4;}else{var h=i/(2*Math.PI)*Math.asin(k/f);}return f*Math.pow(2,-10*g)*Math.sin((g*j-h)*(2*Math.PI)/i)+k+e;},elasticBoth:function(g,e,k,j,f,i){if(g==0){return e;}if((g/=j/2)==2){return e+k;}if(!i){i=j*(0.3*1.5);}if(!f||f<Math.abs(k)){f=k;var h=i/4;}else{var h=i/(2*Math.PI)*Math.asin(k/f);}if(g<1){return -0.5*(f*Math.pow(2,10*(g-=1))*Math.sin((g*j-h)*(2*Math.PI)/i))+e;}return f*Math.pow(2,-10*(g-=1))*Math.sin((g*j-h)*(2*Math.PI)/i)*0.5+k+e;},backIn:function(e,a,h,g,f){if(typeof f=="undefined"){f=1.70158;}return h*(e/=g)*e*((f+1)*e-f)+a;},backOut:function(e,a,h,g,f){if(typeof f=="undefined"){f=1.70158;}return h*((e=e/g-1)*e*((f+1)*e+f)+1)+a;},backBoth:function(e,a,h,g,f){if(typeof f=="undefined"){f=1.70158;}if((e/=g/2)<1){return h/2*(e*e*(((f*=(1.525))+1)*e-f))+a;}return h/2*((e-=2)*e*(((f*=(1.525))+1)*e+f)+2)+a;},bounceIn:function(e,a,g,f){return g-YAHOO.util.Easing.bounceOut(f-e,0,g,f)+a;},bounceOut:function(e,a,g,f){if((e/=f)<(1/2.75)){return g*(7.5625*e*e)+a;}else{if(e<(2/2.75)){return g*(7.5625*(e-=(1.5/2.75))*e+0.75)+a;}else{if(e<(2.5/2.75)){return g*(7.5625*(e-=(2.25/2.75))*e+0.9375)+a;}}}return g*(7.5625*(e-=(2.625/2.75))*e+0.984375)+a;},bounceBoth:function(e,a,g,f){if(e<f/2){return YAHOO.util.Easing.bounceIn(e*2,0,g,f)*0.5+a;}return YAHOO.util.Easing.bounceOut(e*2-f,0,g,f)*0.5+g*0.5+a;}};(function(){var a=function(h,g,i,j){if(h){a.superclass.constructor.call(this,h,g,i,j);}};a.NAME="Motion";var e=YAHOO.util;YAHOO.extend(a,e.ColorAnim);var f=a.superclass;var c=a.prototype;c.patterns.points=/^points$/i;c.setAttribute=function(g,i,h){if(this.patterns.points.test(g)){h=h||"px";f.setAttribute.call(this,"left",i[0],h);f.setAttribute.call(this,"top",i[1],h);}else{f.setAttribute.call(this,g,i,h);}};c.getAttribute=function(g){if(this.patterns.points.test(g)){var h=[f.getAttribute.call(this,"left"),f.getAttribute.call(this,"top")];}else{h=f.getAttribute.call(this,g);}return h;};c.doMethod=function(g,k,h){var j=null;if(this.patterns.points.test(g)){var i=this.method(this.currentFrame,0,100,this.totalFrames)/100;j=e.Bezier.getPosition(this.runtimeAttributes[g],i);
-}else{j=f.doMethod.call(this,g,k,h);}return j;};c.setRuntimeAttribute=function(q){if(this.patterns.points.test(q)){var h=this.getEl();var k=this.attributes;var g;var m=k["points"]["control"]||[];var j;var n,p;if(m.length>0&&!(m[0] instanceof Array)){m=[m];}else{var l=[];for(n=0,p=m.length;n<p;++n){l[n]=m[n];}m=l;}if(e.Dom.getStyle(h,"position")=="static"){e.Dom.setStyle(h,"position","relative");}if(d(k["points"]["from"])){e.Dom.setXY(h,k["points"]["from"]);}else{e.Dom.setXY(h,e.Dom.getXY(h));}g=this.getAttribute("points");if(d(k["points"]["to"])){j=b.call(this,k["points"]["to"],g);var o=e.Dom.getXY(this.getEl());for(n=0,p=m.length;n<p;++n){m[n]=b.call(this,m[n],g);}}else{if(d(k["points"]["by"])){j=[g[0]+k["points"]["by"][0],g[1]+k["points"]["by"][1]];for(n=0,p=m.length;n<p;++n){m[n]=[g[0]+m[n][0],g[1]+m[n][1]];}}}this.runtimeAttributes[q]=[g];if(m.length>0){this.runtimeAttributes[q]=this.runtimeAttributes[q].concat(m);}this.runtimeAttributes[q][this.runtimeAttributes[q].length]=j;}else{f.setRuntimeAttribute.call(this,q);}};var b=function(g,i){var h=e.Dom.getXY(this.getEl());g=[g[0]-h[0]+i[0],g[1]-h[1]+i[1]];return g;};var d=function(g){return(typeof g!=="undefined");};e.Motion=a;})();(function(){var d=function(f,e,g,h){if(f){d.superclass.constructor.call(this,f,e,g,h);}};d.NAME="Scroll";var b=YAHOO.util;YAHOO.extend(d,b.ColorAnim);var c=d.superclass;var a=d.prototype;a.doMethod=function(e,h,f){var g=null;if(e=="scroll"){g=[this.method(this.currentFrame,h[0],f[0]-h[0],this.totalFrames),this.method(this.currentFrame,h[1],f[1]-h[1],this.totalFrames)];}else{g=c.doMethod.call(this,e,h,f);}return g;};a.getAttribute=function(e){var g=null;var f=this.getEl();if(e=="scroll"){g=[f.scrollLeft,f.scrollTop];}else{g=c.getAttribute.call(this,e);}return g;};a.setAttribute=function(e,h,g){var f=this.getEl();if(e=="scroll"){f.scrollLeft=h[0];f.scrollTop=h[1];}else{c.setAttribute.call(this,e,h,g);}};b.Scroll=d;})();YAHOO.register("animation",YAHOO.util.Anim,{version:"2.9.0",build:"2800"});if(!YAHOO.util.DragDropMgr){YAHOO.util.DragDropMgr=function(){var A=YAHOO.util.Event,B=YAHOO.util.Dom;return{useShim:false,_shimActive:false,_shimState:false,_debugShim:false,_createShim:function(){var C=document.createElement("div");C.id="yui-ddm-shim";if(document.body.firstChild){document.body.insertBefore(C,document.body.firstChild);}else{document.body.appendChild(C);}C.style.display="none";C.style.backgroundColor="red";C.style.position="absolute";C.style.zIndex="99999";B.setStyle(C,"opacity","0");this._shim=C;A.on(C,"mouseup",this.handleMouseUp,this,true);A.on(C,"mousemove",this.handleMouseMove,this,true);A.on(window,"scroll",this._sizeShim,this,true);},_sizeShim:function(){if(this._shimActive){var C=this._shim;C.style.height=B.getDocumentHeight()+"px";C.style.width=B.getDocumentWidth()+"px";C.style.top="0";C.style.left="0";}},_activateShim:function(){if(this.useShim){if(!this._shim){this._createShim();}this._shimActive=true;var C=this._shim,D="0";if(this._debugShim){D=".5";}B.setStyle(C,"opacity",D);this._sizeShim();C.style.display="block";}},_deactivateShim:function(){this._shim.style.display="none";this._shimActive=false;},_shim:null,ids:{},handleIds:{},dragCurrent:null,dragOvers:{},deltaX:0,deltaY:0,preventDefault:true,stopPropagation:true,initialized:false,locked:false,interactionInfo:null,init:function(){this.initialized=true;},POINT:0,INTERSECT:1,STRICT_INTERSECT:2,mode:0,_execOnAll:function(E,D){for(var F in this.ids){for(var C in this.ids[F]){var G=this.ids[F][C];if(!this.isTypeOfDD(G)){continue;}G[E].apply(G,D);}}},_onLoad:function(){this.init();A.on(document,"mouseup",this.handleMouseUp,this,true);A.on(document,"mousemove",this.handleMouseMove,this,true);A.on(window,"unload",this._onUnload,this,true);A.on(window,"resize",this._onResize,this,true);},_onResize:function(C){this._execOnAll("resetConstraints",[]);},lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isLocked:function(){return this.locked;},locationCache:{},useCache:true,clickPixelThresh:3,clickTimeThresh:1000,dragThreshMet:false,clickTimeout:null,startX:0,startY:0,fromTimeout:false,regDragDrop:function(D,C){if(!this.initialized){this.init();}if(!this.ids[C]){this.ids[C]={};}this.ids[C][D.id]=D;},removeDDFromGroup:function(E,C){if(!this.ids[C]){this.ids[C]={};}var D=this.ids[C];if(D&&D[E.id]){delete D[E.id];}},_remove:function(E){for(var D in E.groups){if(D){var C=this.ids[D];if(C&&C[E.id]){delete C[E.id];}}}delete this.handleIds[E.id];},regHandle:function(D,C){if(!this.handleIds[D]){this.handleIds[D]={};}this.handleIds[D][C]=C;},isDragDrop:function(C){return(this.getDDById(C))?true:false;},getRelated:function(H,D){var G=[];for(var F in H.groups){for(var E in this.ids[F]){var C=this.ids[F][E];if(!this.isTypeOfDD(C)){continue;}if(!D||C.isTarget){G[G.length]=C;}}}return G;},isLegalTarget:function(G,F){var D=this.getRelated(G,true);for(var E=0,C=D.length;E<C;++E){if(D[E].id==F.id){return true;}}return false;},isTypeOfDD:function(C){return(C&&C.__ygDragDrop);},isHandle:function(D,C){return(this.handleIds[D]&&this.handleIds[D][C]);},getDDById:function(D){for(var C in this.ids){if(this.ids[C][D]){return this.ids[C][D];}}return null;},handleMouseDown:function(E,D){this.currentTarget=YAHOO.util.Event.getTarget(E);this.dragCurrent=D;var C=D.getEl();this.startX=YAHOO.util.Event.getPageX(E);this.startY=YAHOO.util.Event.getPageY(E);this.deltaX=this.startX-C.offsetLeft;this.deltaY=this.startY-C.offsetTop;this.dragThreshMet=false;this.clickTimeout=setTimeout(function(){var F=YAHOO.util.DDM;F.startDrag(F.startX,F.startY);F.fromTimeout=true;},this.clickTimeThresh);},startDrag:function(C,E){if(this.dragCurrent&&this.dragCurrent.useShim){this._shimState=this.useShim;this.useShim=true;}this._activateShim();clearTimeout(this.clickTimeout);var D=this.dragCurrent;if(D&&D.events.b4StartDrag){D.b4StartDrag(C,E);D.fireEvent("b4StartDragEvent",{x:C,y:E});}if(D&&D.events.startDrag){D.startDrag(C,E);D.fireEvent("startDragEvent",{x:C,y:E});}this.dragThreshMet=true;},handleMouseUp:function(C){if(this.dragCurrent){clearTimeout(this.clickTimeout);if(this.dragThreshMet){if(this.fromTimeout){this.fromTimeout=false;this.handleMouseMove(C);}this.fromTimeout=false;this.fireEvents(C,true);}else{}this.stopDrag(C);this.stopEvent(C);}},stopEvent:function(C){if(this.stopPropagation){YAHOO.util.Event.stopPropagation(C);}if(this.preventDefault){YAHOO.util.Event.preventDefault(C);}},stopDrag:function(E,D){var C=this.dragCurrent;if(C&&!D){if(this.dragThreshMet){if(C.events.b4EndDrag){C.b4EndDrag(E);C.fireEvent("b4EndDragEvent",{e:E});}if(C.events.endDrag){C.endDrag(E);C.fireEvent("endDragEvent",{e:E});}}if(C.events.mouseUp){C.onMouseUp(E);C.fireEvent("mouseUpEvent",{e:E});}}if(this._shimActive){this._deactivateShim();if(this.dragCurrent&&this.dragCurrent.useShim){this.useShim=this._shimState;this._shimState=false;}}this.dragCurrent=null;this.dragOvers={};},handleMouseMove:function(F){var C=this.dragCurrent;if(C){if(YAHOO.env.ua.ie&&(YAHOO.env.ua.ie<9)&&!F.button){this.stopEvent(F);return this.handleMouseUp(F);}else{if(F.clientX<0||F.clientY<0){}}if(!this.dragThreshMet){var E=Math.abs(this.startX-YAHOO.util.Event.getPageX(F));var D=Math.abs(this.startY-YAHOO.util.Event.getPageY(F));if(E>this.clickPixelThresh||D>this.clickPixelThresh){this.startDrag(this.startX,this.startY);}}if(this.dragThreshMet){if(C&&C.events.b4Drag){C.b4Drag(F);C.fireEvent("b4DragEvent",{e:F});}if(C&&C.events.drag){C.onDrag(F);C.fireEvent("dragEvent",{e:F});}if(C){this.fireEvents(F,false);}}this.stopEvent(F);}},fireEvents:function(W,M){var c=this.dragCurrent;if(!c||c.isLocked()||c.dragOnly){return;}var O=YAHOO.util.Event.getPageX(W),N=YAHOO.util.Event.getPageY(W),Q=new YAHOO.util.Point(O,N),K=c.getTargetCoord(Q.x,Q.y),F=c.getDragEl(),E=["out","over","drop","enter"],V=new YAHOO.util.Region(K.y,K.x+F.offsetWidth,K.y+F.offsetHeight,K.x),I=[],D={},L={},R=[],d={outEvts:[],overEvts:[],dropEvts:[],enterEvts:[]};for(var T in this.dragOvers){var f=this.dragOvers[T];if(!this.isTypeOfDD(f)){continue;
-}if(!this.isOverTarget(Q,f,this.mode,V)){d.outEvts.push(f);}I[T]=true;delete this.dragOvers[T];}for(var S in c.groups){if("string"!=typeof S){continue;}for(T in this.ids[S]){var G=this.ids[S][T];if(!this.isTypeOfDD(G)){continue;}if(G.isTarget&&!G.isLocked()&&G!=c){if(this.isOverTarget(Q,G,this.mode,V)){D[S]=true;if(M){d.dropEvts.push(G);}else{if(!I[G.id]){d.enterEvts.push(G);}else{d.overEvts.push(G);}this.dragOvers[G.id]=G;}}}}}this.interactionInfo={out:d.outEvts,enter:d.enterEvts,over:d.overEvts,drop:d.dropEvts,point:Q,draggedRegion:V,sourceRegion:this.locationCache[c.id],validDrop:M};for(var C in D){R.push(C);}if(M&&!d.dropEvts.length){this.interactionInfo.validDrop=false;if(c.events.invalidDrop){c.onInvalidDrop(W);c.fireEvent("invalidDropEvent",{e:W});}}for(T=0;T<E.length;T++){var Z=null;if(d[E[T]+"Evts"]){Z=d[E[T]+"Evts"];}if(Z&&Z.length){var H=E[T].charAt(0).toUpperCase()+E[T].substr(1),Y="onDrag"+H,J="b4Drag"+H,P="drag"+H+"Event",X="drag"+H;if(this.mode){if(c.events[J]){c[J](W,Z,R);L[Y]=c.fireEvent(J+"Event",{event:W,info:Z,group:R});}if(c.events[X]&&(L[Y]!==false)){c[Y](W,Z,R);c.fireEvent(P,{event:W,info:Z,group:R});}}else{for(var a=0,U=Z.length;a<U;++a){if(c.events[J]){c[J](W,Z[a].id,R[0]);L[Y]=c.fireEvent(J+"Event",{event:W,info:Z[a].id,group:R[0]});}if(c.events[X]&&(L[Y]!==false)){c[Y](W,Z[a].id,R[0]);c.fireEvent(P,{event:W,info:Z[a].id,group:R[0]});}}}}}},getBestMatch:function(E){var G=null;var D=E.length;if(D==1){G=E[0];}else{for(var F=0;F<D;++F){var C=E[F];if(this.mode==this.INTERSECT&&C.cursorIsOver){G=C;break;}else{if(!G||!G.overlap||(C.overlap&&G.overlap.getArea()<C.overlap.getArea())){G=C;}}}}return G;},refreshCache:function(D){var F=D||this.ids;for(var C in F){if("string"!=typeof C){continue;}for(var E in this.ids[C]){var G=this.ids[C][E];if(this.isTypeOfDD(G)){var H=this.getLocation(G);if(H){this.locationCache[G.id]=H;}else{delete this.locationCache[G.id];}}}}},verifyEl:function(D){try{if(D){var C=D.offsetParent;if(C){return true;}}}catch(E){}return false;},getLocation:function(H){if(!this.isTypeOfDD(H)){return null;}var F=H.getEl(),K,E,D,M,L,N,C,J,G;try{K=YAHOO.util.Dom.getXY(F);}catch(I){}if(!K){return null;}E=K[0];D=E+F.offsetWidth;M=K[1];L=M+F.offsetHeight;N=M-H.padding[0];C=D+H.padding[1];J=L+H.padding[2];G=E-H.padding[3];return new YAHOO.util.Region(N,C,J,G);},isOverTarget:function(K,C,E,F){var G=this.locationCache[C.id];if(!G||!this.useCache){G=this.getLocation(C);this.locationCache[C.id]=G;}if(!G){return false;}C.cursorIsOver=G.contains(K);var J=this.dragCurrent;if(!J||(!E&&!J.constrainX&&!J.constrainY)){return C.cursorIsOver;}C.overlap=null;if(!F){var H=J.getTargetCoord(K.x,K.y);var D=J.getDragEl();F=new YAHOO.util.Region(H.y,H.x+D.offsetWidth,H.y+D.offsetHeight,H.x);}var I=F.intersect(G);if(I){C.overlap=I;return(E)?true:C.cursorIsOver;}else{return false;}},_onUnload:function(D,C){this.unregAll();},unregAll:function(){if(this.dragCurrent){this.stopDrag();this.dragCurrent=null;}this._execOnAll("unreg",[]);this.ids={};},elementCache:{},getElWrapper:function(D){var C=this.elementCache[D];if(!C||!C.el){C=this.elementCache[D]=new this.ElementWrapper(YAHOO.util.Dom.get(D));}return C;},getElement:function(C){return YAHOO.util.Dom.get(C);},getCss:function(D){var C=YAHOO.util.Dom.get(D);return(C)?C.style:null;},ElementWrapper:function(C){this.el=C||null;this.id=this.el&&C.id;this.css=this.el&&C.style;},getPosX:function(C){return YAHOO.util.Dom.getX(C);},getPosY:function(C){return YAHOO.util.Dom.getY(C);},swapNode:function(E,C){if(E.swapNode){E.swapNode(C);}else{var F=C.parentNode;var D=C.nextSibling;if(D==E){F.insertBefore(E,C);}else{if(C==E.nextSibling){F.insertBefore(C,E);}else{E.parentNode.replaceChild(C,E);F.insertBefore(E,D);}}}},getScroll:function(){var E,C,F=document.documentElement,D=document.body;if(F&&(F.scrollTop||F.scrollLeft)){E=F.scrollTop;C=F.scrollLeft;}else{if(D){E=D.scrollTop;C=D.scrollLeft;}else{}}return{top:E,left:C};},getStyle:function(D,C){return YAHOO.util.Dom.getStyle(D,C);},getScrollTop:function(){return this.getScroll().top;},getScrollLeft:function(){return this.getScroll().left;},moveToEl:function(C,E){var D=YAHOO.util.Dom.getXY(E);YAHOO.util.Dom.setXY(C,D);},getClientHeight:function(){return YAHOO.util.Dom.getViewportHeight();},getClientWidth:function(){return YAHOO.util.Dom.getViewportWidth();},numericSort:function(D,C){return(D-C);},_timeoutCount:0,_addListeners:function(){var C=YAHOO.util.DDM;if(YAHOO.util.Event&&document){C._onLoad();}else{if(C._timeoutCount>2000){}else{setTimeout(C._addListeners,10);if(document&&document.body){C._timeoutCount+=1;}}}},handleWasClicked:function(C,E){if(this.isHandle(E,C.id)){return true;}else{var D=C.parentNode;while(D){if(this.isHandle(E,D.id)){return true;}else{D=D.parentNode;}}}return false;}};}();YAHOO.util.DDM=YAHOO.util.DragDropMgr;YAHOO.util.DDM._addListeners();}(function(){var A=YAHOO.util.Event;var B=YAHOO.util.Dom;YAHOO.util.DragDrop=function(E,C,D){if(E){this.init(E,C,D);}};YAHOO.util.DragDrop.prototype={events:null,on:function(){this.subscribe.apply(this,arguments);},id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isTarget:true,padding:null,dragOnly:false,useShim:false,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,deltaX:0,deltaY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,cursorIsOver:false,overlap:null,b4StartDrag:function(C,D){},startDrag:function(C,D){},b4Drag:function(C){},onDrag:function(C){},onDragEnter:function(C,D){},b4DragOver:function(C){},onDragOver:function(C,D){},b4DragOut:function(C){},onDragOut:function(C,D){},b4DragDrop:function(C){},onDragDrop:function(C,D){},onInvalidDrop:function(C){},b4EndDrag:function(C){},endDrag:function(C){},b4MouseDown:function(C){},onMouseDown:function(C){},onMouseUp:function(C){},onAvailable:function(){},getEl:function(){if(!this._domRef){this._domRef=B.get(this.id);
-}return this._domRef;},getDragEl:function(){return B.get(this.dragElId);},init:function(F,C,D){this.initTarget(F,C,D);A.on(this._domRef||this.id,"mousedown",this.handleMouseDown,this,true);for(var E in this.events){this.createEvent(E+"Event");}},initTarget:function(E,C,D){this.config=D||{};this.events={};this.DDM=YAHOO.util.DDM;this.groups={};if(typeof E!=="string"){this._domRef=E;E=B.generateId(E);}this.id=E;this.addToGroup((C)?C:"default");this.handleElId=E;A.onAvailable(E,this.handleOnAvailable,this,true);this.setDragElId(E);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig();},applyConfig:function(){this.events={mouseDown:true,b4MouseDown:true,mouseUp:true,b4StartDrag:true,startDrag:true,b4EndDrag:true,endDrag:true,drag:true,b4Drag:true,invalidDrop:true,b4DragOut:true,dragOut:true,dragEnter:true,b4DragOver:true,dragOver:true,b4DragDrop:true,dragDrop:true};if(this.config.events){for(var C in this.config.events){if(this.config.events[C]===false){this.events[C]=false;}}}this.padding=this.config.padding||[0,0,0,0];this.isTarget=(this.config.isTarget!==false);this.maintainOffset=(this.config.maintainOffset);this.primaryButtonOnly=(this.config.primaryButtonOnly!==false);this.dragOnly=((this.config.dragOnly===true)?true:false);this.useShim=((this.config.useShim===true)?true:false);},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable();},setPadding:function(E,C,F,D){if(!C&&0!==C){this.padding=[E,E,E,E];}else{if(!F&&0!==F){this.padding=[E,C,E,C];}else{this.padding=[E,C,F,D];}}},setInitPosition:function(F,E){var G=this.getEl();if(!this.DDM.verifyEl(G)){if(G&&G.style&&(G.style.display=="none")){}else{}return;}var D=F||0;var C=E||0;var H=B.getXY(G);this.initPageX=H[0]-D;this.initPageY=H[1]-C;this.lastPageX=H[0];this.lastPageY=H[1];this.setStartPosition(H);},setStartPosition:function(D){var C=D||B.getXY(this.getEl());this.deltaSetXY=null;this.startPageX=C[0];this.startPageY=C[1];},addToGroup:function(C){this.groups[C]=true;this.DDM.regDragDrop(this,C);},removeFromGroup:function(C){if(this.groups[C]){delete this.groups[C];}this.DDM.removeDDFromGroup(this,C);},setDragElId:function(C){this.dragElId=C;},setHandleElId:function(C){if(typeof C!=="string"){C=B.generateId(C);}this.handleElId=C;this.DDM.regHandle(this.id,C);},setOuterHandleElId:function(C){if(typeof C!=="string"){C=B.generateId(C);}A.on(C,"mousedown",this.handleMouseDown,this,true);this.setHandleElId(C);this.hasOuterHandles=true;},unreg:function(){A.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this);},isLocked:function(){return(this.DDM.isLocked()||this.locked);},handleMouseDown:function(J,I){var D=J.which||J.button;if(this.primaryButtonOnly&&D>1){return;}if(this.isLocked()){return;}var C=this.b4MouseDown(J),F=true;if(this.events.b4MouseDown){F=this.fireEvent("b4MouseDownEvent",J);}var E=this.onMouseDown(J),H=true;if(this.events.mouseDown){if(E===false){H=false;}else{H=this.fireEvent("mouseDownEvent",J);}}if((C===false)||(E===false)||(F===false)||(H===false)){return;}this.DDM.refreshCache(this.groups);var G=new YAHOO.util.Point(A.getPageX(J),A.getPageY(J));if(!this.hasOuterHandles&&!this.DDM.isOverTarget(G,this)){}else{if(this.clickValidator(J)){this.setStartPosition();this.DDM.handleMouseDown(J,this);this.DDM.stopEvent(J);}else{}}},clickValidator:function(D){var C=YAHOO.util.Event.getTarget(D);return(this.isValidHandleChild(C)&&(this.id==this.handleElId||this.DDM.handleWasClicked(C,this.id)));},getTargetCoord:function(E,D){var C=E-this.deltaX;var F=D-this.deltaY;if(this.constrainX){if(C<this.minX){C=this.minX;}if(C>this.maxX){C=this.maxX;}}if(this.constrainY){if(F<this.minY){F=this.minY;}if(F>this.maxY){F=this.maxY;}}C=this.getTick(C,this.xTicks);F=this.getTick(F,this.yTicks);return{x:C,y:F};},addInvalidHandleType:function(C){var D=C.toUpperCase();this.invalidHandleTypes[D]=D;},addInvalidHandleId:function(C){if(typeof C!=="string"){C=B.generateId(C);}this.invalidHandleIds[C]=C;},addInvalidHandleClass:function(C){this.invalidHandleClasses.push(C);},removeInvalidHandleType:function(C){var D=C.toUpperCase();delete this.invalidHandleTypes[D];},removeInvalidHandleId:function(C){if(typeof C!=="string"){C=B.generateId(C);}delete this.invalidHandleIds[C];},removeInvalidHandleClass:function(D){for(var E=0,C=this.invalidHandleClasses.length;E<C;++E){if(this.invalidHandleClasses[E]==D){delete this.invalidHandleClasses[E];}}},isValidHandleChild:function(F){var E=true;var H;try{H=F.nodeName.toUpperCase();}catch(G){H=F.nodeName;}E=E&&!this.invalidHandleTypes[H];E=E&&!this.invalidHandleIds[F.id];for(var D=0,C=this.invalidHandleClasses.length;E&&D<C;++D){E=!B.hasClass(F,this.invalidHandleClasses[D]);}return E;},setXTicks:function(F,C){this.xTicks=[];this.xTickSize=C;var E={};for(var D=this.initPageX;D>=this.minX;D=D-C){if(!E[D]){this.xTicks[this.xTicks.length]=D;E[D]=true;}}for(D=this.initPageX;D<=this.maxX;D=D+C){if(!E[D]){this.xTicks[this.xTicks.length]=D;E[D]=true;}}this.xTicks.sort(this.DDM.numericSort);},setYTicks:function(F,C){this.yTicks=[];this.yTickSize=C;var E={};for(var D=this.initPageY;D>=this.minY;D=D-C){if(!E[D]){this.yTicks[this.yTicks.length]=D;E[D]=true;}}for(D=this.initPageY;D<=this.maxY;D=D+C){if(!E[D]){this.yTicks[this.yTicks.length]=D;E[D]=true;}}this.yTicks.sort(this.DDM.numericSort);},setXConstraint:function(E,D,C){this.leftConstraint=parseInt(E,10);this.rightConstraint=parseInt(D,10);this.minX=this.initPageX-this.leftConstraint;this.maxX=this.initPageX+this.rightConstraint;if(C){this.setXTicks(this.initPageX,C);}this.constrainX=true;},clearConstraints:function(){this.constrainX=false;this.constrainY=false;this.clearTicks();},clearTicks:function(){this.xTicks=null;this.yTicks=null;this.xTickSize=0;this.yTickSize=0;},setYConstraint:function(C,E,D){this.topConstraint=parseInt(C,10);this.bottomConstraint=parseInt(E,10);this.minY=this.initPageY-this.topConstraint;this.maxY=this.initPageY+this.bottomConstraint;
-if(D){this.setYTicks(this.initPageY,D);}this.constrainY=true;},resetConstraints:function(){if(this.initPageX||this.initPageX===0){var D=(this.maintainOffset)?this.lastPageX-this.initPageX:0;var C=(this.maintainOffset)?this.lastPageY-this.initPageY:0;this.setInitPosition(D,C);}else{this.setInitPosition();}if(this.constrainX){this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize);}if(this.constrainY){this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize);}},getTick:function(I,F){if(!F){return I;}else{if(F[0]>=I){return F[0];}else{for(var D=0,C=F.length;D<C;++D){var E=D+1;if(F[E]&&F[E]>=I){var H=I-F[D];var G=F[E]-I;return(G>H)?F[D]:F[E];}}return F[F.length-1];}}},toString:function(){return("DragDrop "+this.id);}};YAHOO.augment(YAHOO.util.DragDrop,YAHOO.util.EventProvider);})();YAHOO.util.DD=function(C,A,B){if(C){this.init(C,A,B);}};YAHOO.extend(YAHOO.util.DD,YAHOO.util.DragDrop,{scroll:true,autoOffset:function(C,B){var A=C-this.startPageX;var D=B-this.startPageY;this.setDelta(A,D);},setDelta:function(B,A){this.deltaX=B;this.deltaY=A;},setDragElPos:function(C,B){var A=this.getDragEl();this.alignElWithMouse(A,C,B);},alignElWithMouse:function(C,G,F){var E=this.getTargetCoord(G,F);if(!this.deltaSetXY){var H=[E.x,E.y];YAHOO.util.Dom.setXY(C,H);var D=parseInt(YAHOO.util.Dom.getStyle(C,"left"),10);var B=parseInt(YAHOO.util.Dom.getStyle(C,"top"),10);this.deltaSetXY=[D-E.x,B-E.y];}else{YAHOO.util.Dom.setStyle(C,"left",(E.x+this.deltaSetXY[0])+"px");YAHOO.util.Dom.setStyle(C,"top",(E.y+this.deltaSetXY[1])+"px");}this.cachePosition(E.x,E.y);var A=this;setTimeout(function(){A.autoScroll.call(A,E.x,E.y,C.offsetHeight,C.offsetWidth);},0);},cachePosition:function(B,A){if(B){this.lastPageX=B;this.lastPageY=A;}else{var C=YAHOO.util.Dom.getXY(this.getEl());this.lastPageX=C[0];this.lastPageY=C[1];}},autoScroll:function(J,I,E,K){if(this.scroll){var L=this.DDM.getClientHeight();var B=this.DDM.getClientWidth();var N=this.DDM.getScrollTop();var D=this.DDM.getScrollLeft();var H=E+I;var M=K+J;var G=(L+N-I-this.deltaY);var F=(B+D-J-this.deltaX);var C=40;var A=(document.all)?80:30;if(H>L&&G<C){window.scrollTo(D,N+A);}if(I<N&&N>0&&I-N<C){window.scrollTo(D,N-A);}if(M>B&&F<C){window.scrollTo(D+A,N);}if(J<D&&D>0&&J-D<C){window.scrollTo(D-A,N);}}},applyConfig:function(){YAHOO.util.DD.superclass.applyConfig.call(this);this.scroll=(this.config.scroll!==false);},b4MouseDown:function(A){this.setStartPosition();this.autoOffset(YAHOO.util.Event.getPageX(A),YAHOO.util.Event.getPageY(A));},b4Drag:function(A){this.setDragElPos(YAHOO.util.Event.getPageX(A),YAHOO.util.Event.getPageY(A));},toString:function(){return("DD "+this.id);}});YAHOO.util.DDProxy=function(C,A,B){if(C){this.init(C,A,B);this.initFrame();}};YAHOO.util.DDProxy.dragElId="ygddfdiv";YAHOO.extend(YAHOO.util.DDProxy,YAHOO.util.DD,{resizeFrame:true,centerFrame:false,createFrame:function(){var B=this,A=document.body;if(!A||!A.firstChild){setTimeout(function(){B.createFrame();},50);return;}var F=this.getDragEl(),E=YAHOO.util.Dom;if(!F){F=document.createElement("div");F.id=this.dragElId;var D=F.style;D.position="absolute";D.visibility="hidden";D.cursor="move";D.border="2px solid #aaa";D.zIndex=999;D.height="25px";D.width="25px";var C=document.createElement("div");E.setStyle(C,"height","100%");E.setStyle(C,"width","100%");E.setStyle(C,"background-color","#ccc");E.setStyle(C,"opacity","0");F.appendChild(C);A.insertBefore(F,A.firstChild);}},initFrame:function(){this.createFrame();},applyConfig:function(){YAHOO.util.DDProxy.superclass.applyConfig.call(this);this.resizeFrame=(this.config.resizeFrame!==false);this.centerFrame=(this.config.centerFrame);this.setDragElId(this.config.dragElId||YAHOO.util.DDProxy.dragElId);},showFrame:function(E,D){var C=this.getEl();var A=this.getDragEl();var B=A.style;this._resizeProxy();if(this.centerFrame){this.setDelta(Math.round(parseInt(B.width,10)/2),Math.round(parseInt(B.height,10)/2));}this.setDragElPos(E,D);YAHOO.util.Dom.setStyle(A,"visibility","visible");},_resizeProxy:function(){if(this.resizeFrame){var H=YAHOO.util.Dom;var B=this.getEl();var C=this.getDragEl();var G=parseInt(H.getStyle(C,"borderTopWidth"),10);var I=parseInt(H.getStyle(C,"borderRightWidth"),10);var F=parseInt(H.getStyle(C,"borderBottomWidth"),10);var D=parseInt(H.getStyle(C,"borderLeftWidth"),10);if(isNaN(G)){G=0;}if(isNaN(I)){I=0;}if(isNaN(F)){F=0;}if(isNaN(D)){D=0;}var E=Math.max(0,B.offsetWidth-I-D);var A=Math.max(0,B.offsetHeight-G-F);H.setStyle(C,"width",E+"px");H.setStyle(C,"height",A+"px");}},b4MouseDown:function(B){this.setStartPosition();var A=YAHOO.util.Event.getPageX(B);var C=YAHOO.util.Event.getPageY(B);this.autoOffset(A,C);},b4StartDrag:function(A,B){this.showFrame(A,B);},b4EndDrag:function(A){YAHOO.util.Dom.setStyle(this.getDragEl(),"visibility","hidden");},endDrag:function(D){var C=YAHOO.util.Dom;var B=this.getEl();var A=this.getDragEl();C.setStyle(A,"visibility","");C.setStyle(B,"visibility","hidden");YAHOO.util.DDM.moveToEl(B,A);C.setStyle(A,"visibility","hidden");C.setStyle(B,"visibility","");},toString:function(){return("DDProxy "+this.id);}});YAHOO.util.DDTarget=function(C,A,B){if(C){this.initTarget(C,A,B);}};YAHOO.extend(YAHOO.util.DDTarget,YAHOO.util.DragDrop,{toString:function(){return("DDTarget "+this.id);}});YAHOO.register("dragdrop",YAHOO.util.DragDropMgr,{version:"2.9.0",build:"2800"});YAHOO.util.Attribute=function(b,a){if(a){this.owner=a;this.configure(b,true);}};YAHOO.util.Attribute.INVALID_VALUE={};YAHOO.util.Attribute.prototype={name:undefined,value:null,owner:null,readOnly:false,writeOnce:false,_initialConfig:null,_written:false,method:null,setter:null,getter:null,validator:null,getValue:function(){var a=this.value;if(this.getter){a=this.getter.call(this.owner,this.name,a);}return a;},setValue:function(f,b){var e,a=this.owner,c=this.name,g=YAHOO.util.Attribute.INVALID_VALUE,d={type:c,prevValue:this.getValue(),newValue:f};if(this.readOnly||(this.writeOnce&&this._written)){return false;}if(this.validator&&!this.validator.call(a,f)){return false;}if(!b){e=a.fireBeforeChangeEvent(d);if(e===false){return false;}}if(this.setter){f=this.setter.call(a,f,this.name);if(f===undefined){}if(f===g){return false;}}if(this.method){if(this.method.call(a,f,this.name)===g){return false;}}this.value=f;this._written=true;d.type=c;if(!b){this.owner.fireChangeEvent(d);}return true;},configure:function(b,c){b=b||{};if(c){this._written=false;}this._initialConfig=this._initialConfig||{};for(var a in b){if(b.hasOwnProperty(a)){this[a]=b[a];if(c){this._initialConfig[a]=b[a];}}}},resetValue:function(){return this.setValue(this._initialConfig.value);},resetConfig:function(){this.configure(this._initialConfig,true);},refresh:function(a){this.setValue(this.value,a);}};(function(){var a=YAHOO.util.Lang;YAHOO.util.AttributeProvider=function(){};YAHOO.util.AttributeProvider.prototype={_configs:null,get:function(c){this._configs=this._configs||{};var b=this._configs[c];if(!b||!this._configs.hasOwnProperty(c)){return null;}return b.getValue();},set:function(d,e,b){this._configs=this._configs||{};var c=this._configs[d];if(!c){return false;}return c.setValue(e,b);},getAttributeKeys:function(){this._configs=this._configs;var c=[],b;for(b in this._configs){if(a.hasOwnProperty(this._configs,b)&&!a.isUndefined(this._configs[b])){c[c.length]=b;}}return c;},setAttributes:function(d,b){for(var c in d){if(a.hasOwnProperty(d,c)){this.set(c,d[c],b);}}},resetValue:function(c,b){this._configs=this._configs||{};if(this._configs[c]){this.set(c,this._configs[c]._initialConfig.value,b);return true;}return false;},refresh:function(e,c){this._configs=this._configs||{};var f=this._configs;e=((a.isString(e))?[e]:e)||this.getAttributeKeys();for(var d=0,b=e.length;d<b;++d){if(f.hasOwnProperty(e[d])){this._configs[e[d]].refresh(c);}}},register:function(b,c){this.setAttributeConfig(b,c);},getAttributeConfig:function(c){this._configs=this._configs||{};var b=this._configs[c]||{};var d={};for(c in b){if(a.hasOwnProperty(b,c)){d[c]=b[c];}}return d;},setAttributeConfig:function(b,c,d){this._configs=this._configs||{};c=c||{};if(!this._configs[b]){c.name=b;this._configs[b]=this.createAttribute(c);}else{this._configs[b].configure(c,d);}},configureAttribute:function(b,c,d){this.setAttributeConfig(b,c,d);},resetAttributeConfig:function(b){this._configs=this._configs||{};this._configs[b].resetConfig();},subscribe:function(b,c){this._events=this._events||{};if(!(b in this._events)){this._events[b]=this.createEvent(b);}YAHOO.util.EventProvider.prototype.subscribe.apply(this,arguments);},on:function(){this.subscribe.apply(this,arguments);},addListener:function(){this.subscribe.apply(this,arguments);},fireBeforeChangeEvent:function(c){var b="before";b+=c.type.charAt(0).toUpperCase()+c.type.substr(1)+"Change";c.type=b;return this.fireEvent(c.type,c);},fireChangeEvent:function(b){b.type+="Change";return this.fireEvent(b.type,b);},createAttribute:function(b){return new YAHOO.util.Attribute(b,this);}};YAHOO.augment(YAHOO.util.AttributeProvider,YAHOO.util.EventProvider);})();(function(){var b=YAHOO.util.Dom,d=YAHOO.util.AttributeProvider,c={mouseenter:true,mouseleave:true};var a=function(e,f){this.init.apply(this,arguments);};a.DOM_EVENTS={"click":true,"dblclick":true,"keydown":true,"keypress":true,"keyup":true,"mousedown":true,"mousemove":true,"mouseout":true,"mouseover":true,"mouseup":true,"mouseenter":true,"mouseleave":true,"focus":true,"blur":true,"submit":true,"change":true};a.prototype={DOM_EVENTS:null,DEFAULT_HTML_SETTER:function(g,e){var f=this.get("element");if(f){f[e]=g;}return g;},DEFAULT_HTML_GETTER:function(e){var f=this.get("element"),g;if(f){g=f[e];}return g;},appendChild:function(e){e=e.get?e.get("element"):e;return this.get("element").appendChild(e);},getElementsByTagName:function(e){return this.get("element").getElementsByTagName(e);},hasChildNodes:function(){return this.get("element").hasChildNodes();},insertBefore:function(e,f){e=e.get?e.get("element"):e;f=(f&&f.get)?f.get("element"):f;return this.get("element").insertBefore(e,f);},removeChild:function(e){e=e.get?e.get("element"):e;return this.get("element").removeChild(e);},replaceChild:function(e,f){e=e.get?e.get("element"):e;f=f.get?f.get("element"):f;return this.get("element").replaceChild(e,f);},initAttributes:function(e){},addListener:function(j,i,k,h){h=h||this;var e=YAHOO.util.Event,g=this.get("element")||this.get("id"),f=this;if(c[j]&&!e._createMouseDelegate){return false;}if(!this._events[j]){if(g&&this.DOM_EVENTS[j]){e.on(g,j,function(m,l){if(m.srcElement&&!m.target){m.target=m.srcElement;}if((m.toElement&&!m.relatedTarget)||(m.fromElement&&!m.relatedTarget)){m.relatedTarget=e.getRelatedTarget(m);}if(!m.currentTarget){m.currentTarget=g;}f.fireEvent(j,m,l);},k,h);}this.createEvent(j,{scope:this});}return YAHOO.util.EventProvider.prototype.subscribe.apply(this,arguments);},on:function(){return this.addListener.apply(this,arguments);},subscribe:function(){return this.addListener.apply(this,arguments);},removeListener:function(f,e){return this.unsubscribe.apply(this,arguments);},addClass:function(e){b.addClass(this.get("element"),e);},getElementsByClassName:function(f,e){return b.getElementsByClassName(f,e,this.get("element"));},hasClass:function(e){return b.hasClass(this.get("element"),e);},removeClass:function(e){return b.removeClass(this.get("element"),e);},replaceClass:function(f,e){return b.replaceClass(this.get("element"),f,e);
-},setStyle:function(f,e){return b.setStyle(this.get("element"),f,e);},getStyle:function(e){return b.getStyle(this.get("element"),e);},fireQueue:function(){var f=this._queue;for(var g=0,e=f.length;g<e;++g){this[f[g][0]].apply(this,f[g][1]);}},appendTo:function(f,g){f=(f.get)?f.get("element"):b.get(f);this.fireEvent("beforeAppendTo",{type:"beforeAppendTo",target:f});g=(g&&g.get)?g.get("element"):b.get(g);var e=this.get("element");if(!e){return false;}if(!f){return false;}if(e.parent!=f){if(g){f.insertBefore(e,g);}else{f.appendChild(e);}}this.fireEvent("appendTo",{type:"appendTo",target:f});return e;},get:function(e){var g=this._configs||{},f=g.element;if(f&&!g[e]&&!YAHOO.lang.isUndefined(f.value[e])){this._setHTMLAttrConfig(e);}return d.prototype.get.call(this,e);},setAttributes:function(l,h){var f={},j=this._configOrder;for(var k=0,e=j.length;k<e;++k){if(l[j[k]]!==undefined){f[j[k]]=true;this.set(j[k],l[j[k]],h);}}for(var g in l){if(l.hasOwnProperty(g)&&!f[g]){this.set(g,l[g],h);}}},set:function(f,h,e){var g=this.get("element");if(!g){this._queue[this._queue.length]=["set",arguments];if(this._configs[f]){this._configs[f].value=h;}return;}if(!this._configs[f]&&!YAHOO.lang.isUndefined(g[f])){this._setHTMLAttrConfig(f);}return d.prototype.set.apply(this,arguments);},setAttributeConfig:function(e,f,g){this._configOrder.push(e);d.prototype.setAttributeConfig.apply(this,arguments);},createEvent:function(f,e){this._events[f]=true;return d.prototype.createEvent.apply(this,arguments);},init:function(f,e){this._initElement(f,e);},destroy:function(){var e=this.get("element");YAHOO.util.Event.purgeElement(e,true);this.unsubscribeAll();if(e&&e.parentNode){e.parentNode.removeChild(e);}this._queue=[];this._events={};this._configs={};this._configOrder=[];},_initElement:function(g,f){this._queue=this._queue||[];this._events=this._events||{};this._configs=this._configs||{};this._configOrder=[];f=f||{};f.element=f.element||g||null;var i=false;var e=a.DOM_EVENTS;this.DOM_EVENTS=this.DOM_EVENTS||{};for(var h in e){if(e.hasOwnProperty(h)){this.DOM_EVENTS[h]=e[h];}}if(typeof f.element==="string"){this._setHTMLAttrConfig("id",{value:f.element});}if(b.get(f.element)){i=true;this._initHTMLElement(f);this._initContent(f);}YAHOO.util.Event.onAvailable(f.element,function(){if(!i){this._initHTMLElement(f);}this.fireEvent("available",{type:"available",target:b.get(f.element)});},this,true);YAHOO.util.Event.onContentReady(f.element,function(){if(!i){this._initContent(f);}this.fireEvent("contentReady",{type:"contentReady",target:b.get(f.element)});},this,true);},_initHTMLElement:function(e){this.setAttributeConfig("element",{value:b.get(e.element),readOnly:true});},_initContent:function(e){this.initAttributes(e);this.setAttributes(e,true);this.fireQueue();},_setHTMLAttrConfig:function(e,g){var f=this.get("element");g=g||{};g.name=e;g.setter=g.setter||this.DEFAULT_HTML_SETTER;g.getter=g.getter||this.DEFAULT_HTML_GETTER;g.value=g.value||f[e];this._configs[e]=new YAHOO.util.Attribute(g,this);}};YAHOO.augment(a,d);YAHOO.util.Element=a;})();YAHOO.register("element",YAHOO.util.Element,{version:"2.9.0",build:"2800"});YAHOO.register("utilities", YAHOO, {version: "2.9.0", build: "2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-(function(){var lang=YAHOO.lang,util=YAHOO.util,Ev=util.Event;util.DataSourceBase=function(oLiveData,oConfigs){if(oLiveData===null||oLiveData===undefined){return;}this.liveData=oLiveData;this._oQueue={interval:null,conn:null,requests:[]};this.responseSchema={};if(oConfigs&&(oConfigs.constructor==Object)){for(var sConfig in oConfigs){if(sConfig){this[sConfig]=oConfigs[sConfig];}}}var maxCacheEntries=this.maxCacheEntries;if(!lang.isNumber(maxCacheEntries)||(maxCacheEntries<0)){maxCacheEntries=0;}this._aIntervals=[];this.createEvent("cacheRequestEvent");this.createEvent("cacheResponseEvent");this.createEvent("requestEvent");this.createEvent("responseEvent");this.createEvent("responseParseEvent");this.createEvent("responseCacheEvent");this.createEvent("dataErrorEvent");this.createEvent("cacheFlushEvent");var DS=util.DataSourceBase;this._sName="DataSource instance"+DS._nIndex;DS._nIndex++;};var DS=util.DataSourceBase;lang.augmentObject(DS,{TYPE_UNKNOWN:-1,TYPE_JSARRAY:0,TYPE_JSFUNCTION:1,TYPE_XHR:2,TYPE_JSON:3,TYPE_XML:4,TYPE_TEXT:5,TYPE_HTMLTABLE:6,TYPE_SCRIPTNODE:7,TYPE_LOCAL:8,ERROR_DATAINVALID:"Invalid data",ERROR_DATANULL:"Null data",_nIndex:0,_nTransactionId:0,_cloneObject:function(o){if(!lang.isValue(o)){return o;}var copy={};if(Object.prototype.toString.apply(o)==="[object RegExp]"){copy=o;}else{if(lang.isFunction(o)){copy=o;}else{if(lang.isArray(o)){var array=[];for(var i=0,len=o.length;i<len;i++){array[i]=DS._cloneObject(o[i]);}copy=array;}else{if(lang.isObject(o)){for(var x in o){if(lang.hasOwnProperty(o,x)){if(lang.isValue(o[x])&&lang.isObject(o[x])||lang.isArray(o[x])){copy[x]=DS._cloneObject(o[x]);}else{copy[x]=o[x];}}}}else{copy=o;}}}}return copy;},_getLocationValue:function(field,context){var locator=field.locator||field.key||field,xmldoc=context.ownerDocument||context,result,res,value=null;try{if(!lang.isUndefined(xmldoc.evaluate)){result=xmldoc.evaluate(locator,context,xmldoc.createNSResolver(!context.ownerDocument?context.documentElement:context.ownerDocument.documentElement),0,null);while(res=result.iterateNext()){value=res.textContent;}}else{xmldoc.setProperty("SelectionLanguage","XPath");result=context.selectNodes(locator)[0];value=result.value||result.text||null;}return value;}catch(e){}},issueCallback:function(callback,params,error,scope){if(lang.isFunction(callback)){callback.apply(scope,params);}else{if(lang.isObject(callback)){scope=callback.scope||scope||window;var callbackFunc=callback.success;if(error){callbackFunc=callback.failure;}if(callbackFunc){callbackFunc.apply(scope,params.concat([callback.argument]));}}}},parseString:function(oData){if(!lang.isValue(oData)){return null;}var string=oData+"";if(lang.isString(string)){return string;}else{return null;}},parseNumber:function(oData){if(!lang.isValue(oData)||(oData==="")){return null;}var number=oData*1;if(lang.isNumber(number)){return number;}else{return null;}},convertNumber:function(oData){return DS.parseNumber(oData);},parseDate:function(oData){var date=null;if(lang.isValue(oData)&&!(oData instanceof Date)){date=new Date(oData);}else{return oData;}if(date instanceof Date){return date;}else{return null;}},convertDate:function(oData){return DS.parseDate(oData);}});DS.Parser={string:DS.parseString,number:DS.parseNumber,date:DS.parseDate};DS.prototype={_sName:null,_aCache:null,_oQueue:null,_aIntervals:null,maxCacheEntries:0,liveData:null,dataType:DS.TYPE_UNKNOWN,responseType:DS.TYPE_UNKNOWN,responseSchema:null,useXPath:false,cloneBeforeCaching:false,toString:function(){return this._sName;},getCachedResponse:function(oRequest,oCallback,oCaller){var aCache=this._aCache;if(this.maxCacheEntries>0){if(!aCache){this._aCache=[];}else{var nCacheLength=aCache.length;if(nCacheLength>0){var oResponse=null;this.fireEvent("cacheRequestEvent",{request:oRequest,callback:oCallback,caller:oCaller});for(var i=nCacheLength-1;i>=0;i--){var oCacheElem=aCache[i];if(this.isCacheHit(oRequest,oCacheElem.request)){oResponse=oCacheElem.response;this.fireEvent("cacheResponseEvent",{request:oRequest,response:oResponse,callback:oCallback,caller:oCaller});if(i<nCacheLength-1){aCache.splice(i,1);this.addToCache(oRequest,oResponse);}oResponse.cached=true;break;}}return oResponse;}}}else{if(aCache){this._aCache=null;}}return null;},isCacheHit:function(oRequest,oCachedRequest){return(oRequest===oCachedRequest);},addToCache:function(oRequest,oResponse){var aCache=this._aCache;if(!aCache){return;}while(aCache.length>=this.maxCacheEntries){aCache.shift();}oResponse=(this.cloneBeforeCaching)?DS._cloneObject(oResponse):oResponse;var oCacheElem={request:oRequest,response:oResponse};aCache[aCache.length]=oCacheElem;this.fireEvent("responseCacheEvent",{request:oRequest,response:oResponse});},flushCache:function(){if(this._aCache){this._aCache=[];this.fireEvent("cacheFlushEvent");}},setInterval:function(nMsec,oRequest,oCallback,oCaller){if(lang.isNumber(nMsec)&&(nMsec>=0)){var oSelf=this;var nId=setInterval(function(){oSelf.makeConnection(oRequest,oCallback,oCaller);},nMsec);this._aIntervals.push(nId);return nId;}else{}},clearInterval:function(nId){var tracker=this._aIntervals||[];for(var i=tracker.length-1;i>-1;i--){if(tracker[i]===nId){tracker.splice(i,1);clearInterval(nId);}}},clearAllIntervals:function(){var tracker=this._aIntervals||[];for(var i=tracker.length-1;i>-1;i--){clearInterval(tracker[i]);}tracker=[];},sendRequest:function(oRequest,oCallback,oCaller){var oCachedResponse=this.getCachedResponse(oRequest,oCallback,oCaller);if(oCachedResponse){DS.issueCallback(oCallback,[oRequest,oCachedResponse],false,oCaller);return null;}return this.makeConnection(oRequest,oCallback,oCaller);},makeConnection:function(oRequest,oCallback,oCaller){var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});var oRawResponse=this.liveData;this.handleResponse(oRequest,oRawResponse,oCallback,oCaller,tId);return tId;},handleResponse:function(oRequest,oRawResponse,oCallback,oCaller,tId){this.fireEvent("responseEvent",{tId:tId,request:oRequest,response:oRawResponse,callback:oCallback,caller:oCaller});
-var xhr=(this.dataType==DS.TYPE_XHR)?true:false;var oParsedResponse=null;var oFullResponse=oRawResponse;if(this.responseType===DS.TYPE_UNKNOWN){var ctype=(oRawResponse&&oRawResponse.getResponseHeader)?oRawResponse.getResponseHeader["Content-Type"]:null;if(ctype){if(ctype.indexOf("text/xml")>-1){this.responseType=DS.TYPE_XML;}else{if(ctype.indexOf("application/json")>-1){this.responseType=DS.TYPE_JSON;}else{if(ctype.indexOf("text/plain")>-1){this.responseType=DS.TYPE_TEXT;}}}}else{if(YAHOO.lang.isArray(oRawResponse)){this.responseType=DS.TYPE_JSARRAY;}else{if(oRawResponse&&oRawResponse.nodeType&&(oRawResponse.nodeType===9||oRawResponse.nodeType===1||oRawResponse.nodeType===11)){this.responseType=DS.TYPE_XML;}else{if(oRawResponse&&oRawResponse.nodeName&&(oRawResponse.nodeName.toLowerCase()=="table")){this.responseType=DS.TYPE_HTMLTABLE;}else{if(YAHOO.lang.isObject(oRawResponse)){this.responseType=DS.TYPE_JSON;}else{if(YAHOO.lang.isString(oRawResponse)){this.responseType=DS.TYPE_TEXT;}}}}}}}switch(this.responseType){case DS.TYPE_JSARRAY:if(xhr&&oRawResponse&&oRawResponse.responseText){oFullResponse=oRawResponse.responseText;}try{if(lang.isString(oFullResponse)){var parseArgs=[oFullResponse].concat(this.parseJSONArgs);if(lang.JSON){oFullResponse=lang.JSON.parse.apply(lang.JSON,parseArgs);}else{if(window.JSON&&JSON.parse){oFullResponse=JSON.parse.apply(JSON,parseArgs);}else{if(oFullResponse.parseJSON){oFullResponse=oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));}else{while(oFullResponse.length>0&&(oFullResponse.charAt(0)!="{")&&(oFullResponse.charAt(0)!="[")){oFullResponse=oFullResponse.substring(1,oFullResponse.length);}if(oFullResponse.length>0){var arrayEnd=Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));oFullResponse=oFullResponse.substring(0,arrayEnd+1);oFullResponse=eval("("+oFullResponse+")");}}}}}}catch(e1){}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseArrayData(oRequest,oFullResponse);break;case DS.TYPE_JSON:if(xhr&&oRawResponse&&oRawResponse.responseText){oFullResponse=oRawResponse.responseText;}try{if(lang.isString(oFullResponse)){var parseArgs=[oFullResponse].concat(this.parseJSONArgs);if(lang.JSON){oFullResponse=lang.JSON.parse.apply(lang.JSON,parseArgs);}else{if(window.JSON&&JSON.parse){oFullResponse=JSON.parse.apply(JSON,parseArgs);}else{if(oFullResponse.parseJSON){oFullResponse=oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));}else{while(oFullResponse.length>0&&(oFullResponse.charAt(0)!="{")&&(oFullResponse.charAt(0)!="[")){oFullResponse=oFullResponse.substring(1,oFullResponse.length);}if(oFullResponse.length>0){var objEnd=Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));oFullResponse=oFullResponse.substring(0,objEnd+1);oFullResponse=eval("("+oFullResponse+")");}}}}}}catch(e){}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseJSONData(oRequest,oFullResponse);break;case DS.TYPE_HTMLTABLE:if(xhr&&oRawResponse.responseText){var el=document.createElement("div");el.innerHTML=oRawResponse.responseText;oFullResponse=el.getElementsByTagName("table")[0];}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseHTMLTableData(oRequest,oFullResponse);break;case DS.TYPE_XML:if(xhr&&oRawResponse.responseXML){oFullResponse=oRawResponse.responseXML;}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseXMLData(oRequest,oFullResponse);break;case DS.TYPE_TEXT:if(xhr&&lang.isString(oRawResponse.responseText)){oFullResponse=oRawResponse.responseText;}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseTextData(oRequest,oFullResponse);break;default:oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseData(oRequest,oFullResponse);break;}oParsedResponse=oParsedResponse||{};if(!oParsedResponse.results){oParsedResponse.results=[];}if(!oParsedResponse.meta){oParsedResponse.meta={};}if(!oParsedResponse.error){oParsedResponse=this.doBeforeCallback(oRequest,oFullResponse,oParsedResponse,oCallback);this.fireEvent("responseParseEvent",{request:oRequest,response:oParsedResponse,callback:oCallback,caller:oCaller});this.addToCache(oRequest,oParsedResponse);}else{oParsedResponse.error=true;this.fireEvent("dataErrorEvent",{request:oRequest,response:oRawResponse,callback:oCallback,caller:oCaller,message:DS.ERROR_DATANULL});}oParsedResponse.tId=tId;DS.issueCallback(oCallback,[oRequest,oParsedResponse],oParsedResponse.error,oCaller);},doBeforeParseData:function(oRequest,oFullResponse,oCallback){return oFullResponse;},doBeforeCallback:function(oRequest,oFullResponse,oParsedResponse,oCallback){return oParsedResponse;},parseData:function(oRequest,oFullResponse){if(lang.isValue(oFullResponse)){var oParsedResponse={results:oFullResponse,meta:{}};return oParsedResponse;}return null;},parseArrayData:function(oRequest,oFullResponse){if(lang.isArray(oFullResponse)){var results=[],i,j,rec,field,data;if(lang.isArray(this.responseSchema.fields)){var fields=this.responseSchema.fields;for(i=fields.length-1;i>=0;--i){if(typeof fields[i]!=="object"){fields[i]={key:fields[i]};}}var parsers={},p;for(i=fields.length-1;i>=0;--i){p=(typeof fields[i].parser==="function"?fields[i].parser:DS.Parser[fields[i].parser+""])||fields[i].converter;if(p){parsers[fields[i].key]=p;}}var arrType=lang.isArray(oFullResponse[0]);for(i=oFullResponse.length-1;i>-1;i--){var oResult={};rec=oFullResponse[i];if(typeof rec==="object"){for(j=fields.length-1;j>-1;j--){field=fields[j];data=arrType?rec[j]:rec[field.key];if(parsers[field.key]){data=parsers[field.key].call(this,data);}if(data===undefined){data=null;}oResult[field.key]=data;}}else{if(lang.isString(rec)){for(j=fields.length-1;j>-1;j--){field=fields[j];data=rec;if(parsers[field.key]){data=parsers[field.key].call(this,data);}if(data===undefined){data=null;}oResult[field.key]=data;
-}}}results[i]=oResult;}}else{results=oFullResponse;}var oParsedResponse={results:results};return oParsedResponse;}return null;},parseTextData:function(oRequest,oFullResponse){if(lang.isString(oFullResponse)){if(lang.isString(this.responseSchema.recordDelim)&&lang.isString(this.responseSchema.fieldDelim)){var oParsedResponse={results:[]};var recDelim=this.responseSchema.recordDelim;var fieldDelim=this.responseSchema.fieldDelim;if(oFullResponse.length>0){var newLength=oFullResponse.length-recDelim.length;if(oFullResponse.substr(newLength)==recDelim){oFullResponse=oFullResponse.substr(0,newLength);}if(oFullResponse.length>0){var recordsarray=oFullResponse.split(recDelim);for(var i=0,len=recordsarray.length,recIdx=0;i<len;++i){var bError=false,sRecord=recordsarray[i];if(lang.isString(sRecord)&&(sRecord.length>0)){var fielddataarray=recordsarray[i].split(fieldDelim);var oResult={};if(lang.isArray(this.responseSchema.fields)){var fields=this.responseSchema.fields;for(var j=fields.length-1;j>-1;j--){try{var data=fielddataarray[j];if(lang.isString(data)){if(data.charAt(0)=='"'){data=data.substr(1);}if(data.charAt(data.length-1)=='"'){data=data.substr(0,data.length-1);}var field=fields[j];var key=(lang.isValue(field.key))?field.key:field;if(!field.parser&&field.converter){field.parser=field.converter;}var parser=(typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""];if(parser){data=parser.call(this,data);}if(data===undefined){data=null;}oResult[key]=data;}else{bError=true;}}catch(e){bError=true;}}}else{oResult=fielddataarray;}if(!bError){oParsedResponse.results[recIdx++]=oResult;}}}}}return oParsedResponse;}}return null;},parseXMLResult:function(result){var oResult={},schema=this.responseSchema;try{for(var m=schema.fields.length-1;m>=0;m--){var field=schema.fields[m];var key=(lang.isValue(field.key))?field.key:field;var data=null;if(this.useXPath){data=YAHOO.util.DataSource._getLocationValue(field,result);}else{var xmlAttr=result.attributes.getNamedItem(key);if(xmlAttr){data=xmlAttr.value;}else{var xmlNode=result.getElementsByTagName(key);if(xmlNode&&xmlNode.item(0)){var item=xmlNode.item(0);data=(item)?((item.text)?item.text:(item.textContent)?item.textContent:null):null;if(!data){var datapieces=[];for(var j=0,len=item.childNodes.length;j<len;j++){if(item.childNodes[j].nodeValue){datapieces[datapieces.length]=item.childNodes[j].nodeValue;}}if(datapieces.length>0){data=datapieces.join("");}}}}}if(data===null){data="";}if(!field.parser&&field.converter){field.parser=field.converter;}var parser=(typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""];if(parser){data=parser.call(this,data);}if(data===undefined){data=null;}oResult[key]=data;}}catch(e){}return oResult;},parseXMLData:function(oRequest,oFullResponse){var bError=false,schema=this.responseSchema,oParsedResponse={meta:{}},xmlList=null,metaNode=schema.metaNode,metaLocators=schema.metaFields||{},i,k,loc,v;try{if(this.useXPath){for(k in metaLocators){oParsedResponse.meta[k]=YAHOO.util.DataSource._getLocationValue(metaLocators[k],oFullResponse);}}else{metaNode=metaNode?oFullResponse.getElementsByTagName(metaNode)[0]:oFullResponse;if(metaNode){for(k in metaLocators){if(lang.hasOwnProperty(metaLocators,k)){loc=metaLocators[k];v=metaNode.getElementsByTagName(loc)[0];if(v){v=v.firstChild.nodeValue;}else{v=metaNode.attributes.getNamedItem(loc);if(v){v=v.value;}}if(lang.isValue(v)){oParsedResponse.meta[k]=v;}}}}}xmlList=(schema.resultNode)?oFullResponse.getElementsByTagName(schema.resultNode):null;}catch(e){}if(!xmlList||!lang.isArray(schema.fields)){bError=true;}else{oParsedResponse.results=[];for(i=xmlList.length-1;i>=0;--i){var oResult=this.parseXMLResult(xmlList.item(i));oParsedResponse.results[i]=oResult;}}if(bError){oParsedResponse.error=true;}else{}return oParsedResponse;},parseJSONData:function(oRequest,oFullResponse){var oParsedResponse={results:[],meta:{}};if(lang.isObject(oFullResponse)&&this.responseSchema.resultsList){var schema=this.responseSchema,fields=schema.fields,resultsList=oFullResponse,results=[],metaFields=schema.metaFields||{},fieldParsers=[],fieldPaths=[],simpleFields=[],bError=false,i,len,j,v,key,parser,path;var buildPath=function(needle){var path=null,keys=[],i=0;if(needle){needle=needle.replace(/\[(['"])(.*?)\1\]/g,function(x,$1,$2){keys[i]=$2;return".@"+(i++);}).replace(/\[(\d+)\]/g,function(x,$1){keys[i]=parseInt($1,10)|0;return".@"+(i++);}).replace(/^\./,"");if(!/[^\w\.\$@]/.test(needle)){path=needle.split(".");for(i=path.length-1;i>=0;--i){if(path[i].charAt(0)==="@"){path[i]=keys[parseInt(path[i].substr(1),10)];}}}else{}}return path;};var walkPath=function(path,origin){var v=origin,i=0,len=path.length;for(;i<len&&v;++i){v=v[path[i]];}return v;};path=buildPath(schema.resultsList);if(path){resultsList=walkPath(path,oFullResponse);if(resultsList===undefined){bError=true;}}else{bError=true;}if(!resultsList){resultsList=[];}if(!lang.isArray(resultsList)){resultsList=[resultsList];}if(!bError){if(schema.fields){var field;for(i=0,len=fields.length;i<len;i++){field=fields[i];key=field.key||field;parser=((typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""])||field.converter;path=buildPath(key);if(parser){fieldParsers[fieldParsers.length]={key:key,parser:parser};}if(path){if(path.length>1){fieldPaths[fieldPaths.length]={key:key,path:path};}else{simpleFields[simpleFields.length]={key:key,path:path[0]};}}else{}}for(i=resultsList.length-1;i>=0;--i){var r=resultsList[i],rec={};if(r){for(j=simpleFields.length-1;j>=0;--j){rec[simpleFields[j].key]=(r[simpleFields[j].path]!==undefined)?r[simpleFields[j].path]:r[j];}for(j=fieldPaths.length-1;j>=0;--j){rec[fieldPaths[j].key]=walkPath(fieldPaths[j].path,r);}for(j=fieldParsers.length-1;j>=0;--j){var p=fieldParsers[j].key;rec[p]=fieldParsers[j].parser.call(this,rec[p]);if(rec[p]===undefined){rec[p]=null;}}}results[i]=rec;}}else{results=resultsList;}for(key in metaFields){if(lang.hasOwnProperty(metaFields,key)){path=buildPath(metaFields[key]);
-if(path){v=walkPath(path,oFullResponse);oParsedResponse.meta[key]=v;}}}}else{oParsedResponse.error=true;}oParsedResponse.results=results;}else{oParsedResponse.error=true;}return oParsedResponse;},parseHTMLTableData:function(oRequest,oFullResponse){var bError=false;var elTable=oFullResponse;var fields=this.responseSchema.fields;var oParsedResponse={results:[]};if(lang.isArray(fields)){for(var i=0;i<elTable.tBodies.length;i++){var elTbody=elTable.tBodies[i];for(var j=elTbody.rows.length-1;j>-1;j--){var elRow=elTbody.rows[j];var oResult={};for(var k=fields.length-1;k>-1;k--){var field=fields[k];var key=(lang.isValue(field.key))?field.key:field;var data=elRow.cells[k].innerHTML;if(!field.parser&&field.converter){field.parser=field.converter;}var parser=(typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""];if(parser){data=parser.call(this,data);}if(data===undefined){data=null;}oResult[key]=data;}oParsedResponse.results[j]=oResult;}}}else{bError=true;}if(bError){oParsedResponse.error=true;}else{}return oParsedResponse;}};lang.augmentProto(DS,util.EventProvider);util.LocalDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_LOCAL;if(oLiveData){if(YAHOO.lang.isArray(oLiveData)){this.responseType=DS.TYPE_JSARRAY;}else{if(oLiveData.nodeType&&oLiveData.nodeType==9){this.responseType=DS.TYPE_XML;}else{if(oLiveData.nodeName&&(oLiveData.nodeName.toLowerCase()=="table")){this.responseType=DS.TYPE_HTMLTABLE;oLiveData=oLiveData.cloneNode(true);}else{if(YAHOO.lang.isString(oLiveData)){this.responseType=DS.TYPE_TEXT;}else{if(YAHOO.lang.isObject(oLiveData)){this.responseType=DS.TYPE_JSON;}}}}}}else{oLiveData=[];this.responseType=DS.TYPE_JSARRAY;}util.LocalDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.LocalDataSource,DS);lang.augmentObject(util.LocalDataSource,DS);util.FunctionDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_JSFUNCTION;oLiveData=oLiveData||function(){};util.FunctionDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.FunctionDataSource,DS,{scope:null,makeConnection:function(oRequest,oCallback,oCaller){var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});var oRawResponse=(this.scope)?this.liveData.call(this.scope,oRequest,this,oCallback):this.liveData(oRequest,oCallback);if(this.responseType===DS.TYPE_UNKNOWN){if(YAHOO.lang.isArray(oRawResponse)){this.responseType=DS.TYPE_JSARRAY;}else{if(oRawResponse&&oRawResponse.nodeType&&oRawResponse.nodeType==9){this.responseType=DS.TYPE_XML;}else{if(oRawResponse&&oRawResponse.nodeName&&(oRawResponse.nodeName.toLowerCase()=="table")){this.responseType=DS.TYPE_HTMLTABLE;}else{if(YAHOO.lang.isObject(oRawResponse)){this.responseType=DS.TYPE_JSON;}else{if(YAHOO.lang.isString(oRawResponse)){this.responseType=DS.TYPE_TEXT;}}}}}}this.handleResponse(oRequest,oRawResponse,oCallback,oCaller,tId);return tId;}});lang.augmentObject(util.FunctionDataSource,DS);util.ScriptNodeDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_SCRIPTNODE;oLiveData=oLiveData||"";util.ScriptNodeDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.ScriptNodeDataSource,DS,{getUtility:util.Get,asyncMode:"allowAll",scriptCallbackParam:"callback",generateRequestCallback:function(id){return"&"+this.scriptCallbackParam+"=YAHOO.util.ScriptNodeDataSource.callbacks["+id+"]";},doBeforeGetScriptNode:function(sUri){return sUri;},makeConnection:function(oRequest,oCallback,oCaller){var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});if(util.ScriptNodeDataSource._nPending===0){util.ScriptNodeDataSource.callbacks=[];util.ScriptNodeDataSource._nId=0;}var id=util.ScriptNodeDataSource._nId;util.ScriptNodeDataSource._nId++;var oSelf=this;util.ScriptNodeDataSource.callbacks[id]=function(oRawResponse){if((oSelf.asyncMode!=="ignoreStaleResponses")||(id===util.ScriptNodeDataSource.callbacks.length-1)){if(oSelf.responseType===DS.TYPE_UNKNOWN){if(YAHOO.lang.isArray(oRawResponse)){oSelf.responseType=DS.TYPE_JSARRAY;}else{if(oRawResponse.nodeType&&oRawResponse.nodeType==9){oSelf.responseType=DS.TYPE_XML;}else{if(oRawResponse.nodeName&&(oRawResponse.nodeName.toLowerCase()=="table")){oSelf.responseType=DS.TYPE_HTMLTABLE;}else{if(YAHOO.lang.isObject(oRawResponse)){oSelf.responseType=DS.TYPE_JSON;}else{if(YAHOO.lang.isString(oRawResponse)){oSelf.responseType=DS.TYPE_TEXT;}}}}}}oSelf.handleResponse(oRequest,oRawResponse,oCallback,oCaller,tId);}else{}delete util.ScriptNodeDataSource.callbacks[id];};util.ScriptNodeDataSource._nPending++;var sUri=this.liveData+oRequest+this.generateRequestCallback(id);sUri=this.doBeforeGetScriptNode(sUri);this.getUtility.script(sUri,{autopurge:true,onsuccess:util.ScriptNodeDataSource._bumpPendingDown,onfail:util.ScriptNodeDataSource._bumpPendingDown});return tId;}});lang.augmentObject(util.ScriptNodeDataSource,DS);lang.augmentObject(util.ScriptNodeDataSource,{_nId:0,_nPending:0,callbacks:[]});util.XHRDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_XHR;this.connMgr=this.connMgr||util.Connect;oLiveData=oLiveData||"";util.XHRDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.XHRDataSource,DS,{connMgr:null,connXhrMode:"allowAll",connMethodPost:false,connTimeout:0,makeConnection:function(oRequest,oCallback,oCaller){var oRawResponse=null;var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});var oSelf=this;var oConnMgr=this.connMgr;var oQueue=this._oQueue;var _xhrSuccess=function(oResponse){if(oResponse&&(this.connXhrMode=="ignoreStaleResponses")&&(oResponse.tId!=oQueue.conn.tId)){return null;}else{if(!oResponse){this.fireEvent("dataErrorEvent",{request:oRequest,response:null,callback:oCallback,caller:oCaller,message:DS.ERROR_DATANULL});DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller);return null;
-}else{if(this.responseType===DS.TYPE_UNKNOWN){var ctype=(oResponse.getResponseHeader)?oResponse.getResponseHeader["Content-Type"]:null;if(ctype){if(ctype.indexOf("text/xml")>-1){this.responseType=DS.TYPE_XML;}else{if(ctype.indexOf("application/json")>-1){this.responseType=DS.TYPE_JSON;}else{if(ctype.indexOf("text/plain")>-1){this.responseType=DS.TYPE_TEXT;}}}}}this.handleResponse(oRequest,oResponse,oCallback,oCaller,tId);}}};var _xhrFailure=function(oResponse){this.fireEvent("dataErrorEvent",{request:oRequest,response:oResponse,callback:oCallback,caller:oCaller,message:DS.ERROR_DATAINVALID});if(lang.isString(this.liveData)&&lang.isString(oRequest)&&(this.liveData.lastIndexOf("?")!==this.liveData.length-1)&&(oRequest.indexOf("?")!==0)){}oResponse=oResponse||{};oResponse.error=true;DS.issueCallback(oCallback,[oRequest,oResponse],true,oCaller);return null;};var _xhrCallback={success:_xhrSuccess,failure:_xhrFailure,scope:this};if(lang.isNumber(this.connTimeout)){_xhrCallback.timeout=this.connTimeout;}if(this.connXhrMode=="cancelStaleRequests"){if(oQueue.conn){if(oConnMgr.abort){oConnMgr.abort(oQueue.conn);oQueue.conn=null;}else{}}}if(oConnMgr&&oConnMgr.asyncRequest){var sLiveData=this.liveData;var isPost=this.connMethodPost;var sMethod=(isPost)?"POST":"GET";var sUri=(isPost||!lang.isValue(oRequest))?sLiveData:sLiveData+oRequest;var sRequest=(isPost)?oRequest:null;if(this.connXhrMode!="queueRequests"){oQueue.conn=oConnMgr.asyncRequest(sMethod,sUri,_xhrCallback,sRequest);}else{if(oQueue.conn){var allRequests=oQueue.requests;allRequests.push({request:oRequest,callback:_xhrCallback});if(!oQueue.interval){oQueue.interval=setInterval(function(){if(oConnMgr.isCallInProgress(oQueue.conn)){return;}else{if(allRequests.length>0){sUri=(isPost||!lang.isValue(allRequests[0].request))?sLiveData:sLiveData+allRequests[0].request;sRequest=(isPost)?allRequests[0].request:null;oQueue.conn=oConnMgr.asyncRequest(sMethod,sUri,allRequests[0].callback,sRequest);allRequests.shift();}else{clearInterval(oQueue.interval);oQueue.interval=null;}}},50);}}else{oQueue.conn=oConnMgr.asyncRequest(sMethod,sUri,_xhrCallback,sRequest);}}}else{DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller);}return tId;}});lang.augmentObject(util.XHRDataSource,DS);util.DataSource=function(oLiveData,oConfigs){oConfigs=oConfigs||{};var dataType=oConfigs.dataType;if(dataType){if(dataType==DS.TYPE_LOCAL){return new util.LocalDataSource(oLiveData,oConfigs);}else{if(dataType==DS.TYPE_XHR){return new util.XHRDataSource(oLiveData,oConfigs);}else{if(dataType==DS.TYPE_SCRIPTNODE){return new util.ScriptNodeDataSource(oLiveData,oConfigs);}else{if(dataType==DS.TYPE_JSFUNCTION){return new util.FunctionDataSource(oLiveData,oConfigs);}}}}}if(YAHOO.lang.isString(oLiveData)){return new util.XHRDataSource(oLiveData,oConfigs);}else{if(YAHOO.lang.isFunction(oLiveData)){return new util.FunctionDataSource(oLiveData,oConfigs);}else{return new util.LocalDataSource(oLiveData,oConfigs);}}};lang.augmentObject(util.DataSource,DS);})();YAHOO.util.Number={format:function(e,k){if(e===""||e===null||!isFinite(e)){return"";}e=+e;k=YAHOO.lang.merge(YAHOO.util.Number.format.defaults,(k||{}));var j=e+"",l=Math.abs(e),b=k.decimalPlaces||0,r=k.thousandsSeparator,f=k.negativeFormat||("-"+k.format),q,p,g,h;if(f.indexOf("#")>-1){f=f.replace(/#/,k.format);}if(b<0){q=l-(l%1)+"";g=q.length+b;if(g>0){q=Number("."+q).toFixed(g).slice(2)+new Array(q.length-g+1).join("0");}else{q="0";}}else{var a=l+"";if(b>0||a.indexOf(".")>0){var d=Math.pow(10,b);q=Math.round(l*d)/d+"";var c=q.indexOf("."),m,o;if(c<0){m=b;o=(Math.pow(10,m)+"").substring(1);if(b>0){q=q+"."+o;}}else{m=b-(q.length-c-1);o=(Math.pow(10,m)+"").substring(1);q=q+o;}}else{q=l.toFixed(b)+"";}}p=q.split(/\D/);if(l>=1000){g=p[0].length%3||3;p[0]=p[0].slice(0,g)+p[0].slice(g).replace(/(\d{3})/g,r+"$1");}return YAHOO.util.Number.format._applyFormat((e<0?f:k.format),p.join(k.decimalSeparator),k);}};YAHOO.util.Number.format.defaults={format:"{prefix}{number}{suffix}",negativeFormat:null,decimalSeparator:".",decimalPlaces:null,thousandsSeparator:""};YAHOO.util.Number.format._applyFormat=function(a,b,c){return a.replace(/\{(\w+)\}/g,function(d,e){return e==="number"?b:e in c?c[e]:"";});};(function(){var a=function(c,e,d){if(typeof d==="undefined"){d=10;}for(;parseInt(c,10)<d&&d>1;d/=10){c=e.toString()+c;}return c.toString();};var b={formats:{a:function(e,c){return c.a[e.getDay()];},A:function(e,c){return c.A[e.getDay()];},b:function(e,c){return c.b[e.getMonth()];},B:function(e,c){return c.B[e.getMonth()];},C:function(c){return a(parseInt(c.getFullYear()/100,10),0);},d:["getDate","0"],e:["getDate"," "],g:function(c){return a(parseInt(b.formats.G(c)%100,10),0);},G:function(f){var g=f.getFullYear();var e=parseInt(b.formats.V(f),10);var c=parseInt(b.formats.W(f),10);if(c>e){g++;}else{if(c===0&&e>=52){g--;}}return g;},H:["getHours","0"],I:function(e){var c=e.getHours()%12;return a(c===0?12:c,0);},j:function(h){var g=new Date(""+h.getFullYear()+"/1/1 GMT");var e=new Date(""+h.getFullYear()+"/"+(h.getMonth()+1)+"/"+h.getDate()+" GMT");var c=e-g;var f=parseInt(c/60000/60/24,10)+1;return a(f,0,100);},k:["getHours"," "],l:function(e){var c=e.getHours()%12;return a(c===0?12:c," ");},m:function(c){return a(c.getMonth()+1,0);},M:["getMinutes","0"],p:function(e,c){return c.p[e.getHours()>=12?1:0];},P:function(e,c){return c.P[e.getHours()>=12?1:0];},s:function(e,c){return parseInt(e.getTime()/1000,10);},S:["getSeconds","0"],u:function(c){var e=c.getDay();return e===0?7:e;},U:function(g){var c=parseInt(b.formats.j(g),10);var f=6-g.getDay();var e=parseInt((c+f)/7,10);return a(e,0);},V:function(g){var f=parseInt(b.formats.W(g),10);var c=(new Date(""+g.getFullYear()+"/1/1")).getDay();var e=f+(c>4||c<=1?0:1);if(e===53&&(new Date(""+g.getFullYear()+"/12/31")).getDay()<4){e=1;}else{if(e===0){e=b.formats.V(new Date(""+(g.getFullYear()-1)+"/12/31"));}}return a(e,0);},w:"getDay",W:function(g){var c=parseInt(b.formats.j(g),10);var f=7-b.formats.u(g);var e=parseInt((c+f)/7,10);
-return a(e,0,10);},y:function(c){return a(c.getFullYear()%100,0);},Y:"getFullYear",z:function(f){var e=f.getTimezoneOffset();var c=a(parseInt(Math.abs(e/60),10),0);var g=a(Math.abs(e%60),0);return(e>0?"-":"+")+c+g;},Z:function(c){var e=c.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");if(e.length>4){e=b.formats.z(c);}return e;},"%":function(c){return"%";}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(g,f,d){f=f||{};if(!(g instanceof Date)){return YAHOO.lang.isValue(g)?g:"";}var h=f.format||"%m/%d/%Y";if(h==="YYYY/MM/DD"){h="%Y/%m/%d";}else{if(h==="DD/MM/YYYY"){h="%d/%m/%Y";}else{if(h==="MM/DD/YYYY"){h="%m/%d/%Y";}}}d=d||"en";if(!(d in YAHOO.util.DateLocale)){if(d.replace(/-[a-zA-Z]+$/,"") in YAHOO.util.DateLocale){d=d.replace(/-[a-zA-Z]+$/,"");}else{d="en";}}var j=YAHOO.util.DateLocale[d];var c=function(l,k){var m=b.aggregates[k];return(m==="locale"?j[k]:m);};var e=function(l,k){var m=b.formats[k];if(typeof m==="string"){return g[m]();}else{if(typeof m==="function"){return m.call(g,g,j);}else{if(typeof m==="object"&&typeof m[0]==="string"){return a(g[m[0]](),m[1]);}else{return k;}}}};while(h.match(/%[cDFhnrRtTxX]/)){h=h.replace(/%([cDFhnrRtTxX])/g,c);}var i=h.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,e);c=e=undefined;return i;}};YAHOO.namespace("YAHOO.util");YAHOO.util.Date=b;YAHOO.util.DateLocale={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};YAHOO.util.DateLocale["en"]=YAHOO.lang.merge(YAHOO.util.DateLocale,{});YAHOO.util.DateLocale["en-US"]=YAHOO.lang.merge(YAHOO.util.DateLocale["en"],{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});YAHOO.util.DateLocale["en-GB"]=YAHOO.lang.merge(YAHOO.util.DateLocale["en"],{r:"%l:%M:%S %P %Z"});YAHOO.util.DateLocale["en-AU"]=YAHOO.lang.merge(YAHOO.util.DateLocale["en"]);})();YAHOO.register("datasource",YAHOO.util.DataSource,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-YAHOO.widget.DS_JSArray=YAHOO.util.LocalDataSource;YAHOO.widget.DS_JSFunction=YAHOO.util.FunctionDataSource;YAHOO.widget.DS_XHR=function(b,a,d){var c=new YAHOO.util.XHRDataSource(b,d);c._aDeprecatedSchema=a;return c;};YAHOO.widget.DS_ScriptNode=function(b,a,d){var c=new YAHOO.util.ScriptNodeDataSource(b,d);c._aDeprecatedSchema=a;return c;};YAHOO.widget.DS_XHR.TYPE_JSON=YAHOO.util.DataSourceBase.TYPE_JSON;YAHOO.widget.DS_XHR.TYPE_XML=YAHOO.util.DataSourceBase.TYPE_XML;YAHOO.widget.DS_XHR.TYPE_FLAT=YAHOO.util.DataSourceBase.TYPE_TEXT;YAHOO.widget.AutoComplete=function(g,b,j,c){if(g&&b&&j){if(j&&YAHOO.lang.isFunction(j.sendRequest)){this.dataSource=j;}else{return;}this.key=0;var d=j.responseSchema;if(j._aDeprecatedSchema){var k=j._aDeprecatedSchema;if(YAHOO.lang.isArray(k)){if((j.responseType===YAHOO.util.DataSourceBase.TYPE_JSON)||(j.responseType===YAHOO.util.DataSourceBase.TYPE_UNKNOWN)){d.resultsList=k[0];this.key=k[1];d.fields=(k.length<3)?null:k.slice(1);}else{if(j.responseType===YAHOO.util.DataSourceBase.TYPE_XML){d.resultNode=k[0];this.key=k[1];d.fields=k.slice(1);}else{if(j.responseType===YAHOO.util.DataSourceBase.TYPE_TEXT){d.recordDelim=k[0];d.fieldDelim=k[1];}}}j.responseSchema=d;}}if(YAHOO.util.Dom.inDocument(g)){if(YAHOO.lang.isString(g)){this._sName="instance"+YAHOO.widget.AutoComplete._nIndex+" "+g;this._elTextbox=document.getElementById(g);}else{this._sName=(g.id)?"instance"+YAHOO.widget.AutoComplete._nIndex+" "+g.id:"instance"+YAHOO.widget.AutoComplete._nIndex;this._elTextbox=g;}YAHOO.util.Dom.addClass(this._elTextbox,"yui-ac-input");}else{return;}if(YAHOO.util.Dom.inDocument(b)){if(YAHOO.lang.isString(b)){this._elContainer=document.getElementById(b);}else{this._elContainer=b;}if(this._elContainer.style.display=="none"){}var e=this._elContainer.parentNode;var a=e.tagName.toLowerCase();if(a=="div"){YAHOO.util.Dom.addClass(e,"yui-ac");}else{}}else{return;}if(this.dataSource.dataType===YAHOO.util.DataSourceBase.TYPE_LOCAL){this.applyLocalFilter=true;}if(c&&(c.constructor==Object)){for(var i in c){if(i){this[i]=c[i];}}}this._initContainerEl();this._initProps();this._initListEl();this._initContainerHelperEls();var h=this;var f=this._elTextbox;YAHOO.util.Event.addListener(f,"keyup",h._onTextboxKeyUp,h);YAHOO.util.Event.addListener(f,"keydown",h._onTextboxKeyDown,h);YAHOO.util.Event.addListener(f,"focus",h._onTextboxFocus,h);YAHOO.util.Event.addListener(f,"blur",h._onTextboxBlur,h);YAHOO.util.Event.addListener(b,"mouseover",h._onContainerMouseover,h);YAHOO.util.Event.addListener(b,"mouseout",h._onContainerMouseout,h);YAHOO.util.Event.addListener(b,"click",h._onContainerClick,h);YAHOO.util.Event.addListener(b,"scroll",h._onContainerScroll,h);YAHOO.util.Event.addListener(b,"resize",h._onContainerResize,h);YAHOO.util.Event.addListener(f,"keypress",h._onTextboxKeyPress,h);YAHOO.util.Event.addListener(window,"unload",h._onWindowUnload,h);this.textboxFocusEvent=new YAHOO.util.CustomEvent("textboxFocus",this);this.textboxKeyEvent=new YAHOO.util.CustomEvent("textboxKey",this);this.dataRequestEvent=new YAHOO.util.CustomEvent("dataRequest",this);this.dataRequestCancelEvent=new YAHOO.util.CustomEvent("dataRequestCancel",this);this.dataReturnEvent=new YAHOO.util.CustomEvent("dataReturn",this);this.dataErrorEvent=new YAHOO.util.CustomEvent("dataError",this);this.containerPopulateEvent=new YAHOO.util.CustomEvent("containerPopulate",this);this.containerExpandEvent=new YAHOO.util.CustomEvent("containerExpand",this);this.typeAheadEvent=new YAHOO.util.CustomEvent("typeAhead",this);this.itemMouseOverEvent=new YAHOO.util.CustomEvent("itemMouseOver",this);this.itemMouseOutEvent=new YAHOO.util.CustomEvent("itemMouseOut",this);this.itemArrowToEvent=new YAHOO.util.CustomEvent("itemArrowTo",this);this.itemArrowFromEvent=new YAHOO.util.CustomEvent("itemArrowFrom",this);this.itemSelectEvent=new YAHOO.util.CustomEvent("itemSelect",this);this.unmatchedItemSelectEvent=new YAHOO.util.CustomEvent("unmatchedItemSelect",this);this.selectionEnforceEvent=new YAHOO.util.CustomEvent("selectionEnforce",this);this.containerCollapseEvent=new YAHOO.util.CustomEvent("containerCollapse",this);this.textboxBlurEvent=new YAHOO.util.CustomEvent("textboxBlur",this);this.textboxChangeEvent=new YAHOO.util.CustomEvent("textboxChange",this);f.setAttribute("autocomplete","off");YAHOO.widget.AutoComplete._nIndex++;}else{}};YAHOO.widget.AutoComplete.prototype.dataSource=null;YAHOO.widget.AutoComplete.prototype.applyLocalFilter=null;YAHOO.widget.AutoComplete.prototype.queryMatchCase=false;YAHOO.widget.AutoComplete.prototype.queryMatchContains=false;YAHOO.widget.AutoComplete.prototype.queryMatchSubset=false;YAHOO.widget.AutoComplete.prototype.minQueryLength=1;YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed=10;YAHOO.widget.AutoComplete.prototype.queryDelay=0.2;YAHOO.widget.AutoComplete.prototype.typeAheadDelay=0.5;YAHOO.widget.AutoComplete.prototype.queryInterval=500;YAHOO.widget.AutoComplete.prototype.highlightClassName="yui-ac-highlight";YAHOO.widget.AutoComplete.prototype.prehighlightClassName=null;YAHOO.widget.AutoComplete.prototype.delimChar=null;YAHOO.widget.AutoComplete.prototype.autoHighlight=true;YAHOO.widget.AutoComplete.prototype.typeAhead=false;YAHOO.widget.AutoComplete.prototype.animHoriz=false;YAHOO.widget.AutoComplete.prototype.animVert=true;YAHOO.widget.AutoComplete.prototype.animSpeed=0.3;YAHOO.widget.AutoComplete.prototype.forceSelection=false;YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete=true;YAHOO.widget.AutoComplete.prototype.alwaysShowContainer=false;YAHOO.widget.AutoComplete.prototype.useIFrame=false;YAHOO.widget.AutoComplete.prototype.useShadow=false;YAHOO.widget.AutoComplete.prototype.suppressInputUpdate=false;YAHOO.widget.AutoComplete.prototype.resultTypeList=true;YAHOO.widget.AutoComplete.prototype.queryQuestionMark=true;YAHOO.widget.AutoComplete.prototype.autoSnapContainer=true;YAHOO.widget.AutoComplete.prototype.toString=function(){return"AutoComplete "+this._sName;};YAHOO.widget.AutoComplete.prototype.getInputEl=function(){return this._elTextbox;
-};YAHOO.widget.AutoComplete.prototype.getContainerEl=function(){return this._elContainer;};YAHOO.widget.AutoComplete.prototype.isFocused=function(){return this._bFocused;};YAHOO.widget.AutoComplete.prototype.isContainerOpen=function(){return this._bContainerOpen;};YAHOO.widget.AutoComplete.prototype.getListEl=function(){return this._elList;};YAHOO.widget.AutoComplete.prototype.getListItemMatch=function(a){if(a._sResultMatch){return a._sResultMatch;}else{return null;}};YAHOO.widget.AutoComplete.prototype.getListItemData=function(a){if(a._oResultData){return a._oResultData;}else{return null;}};YAHOO.widget.AutoComplete.prototype.getListItemIndex=function(a){if(YAHOO.lang.isNumber(a._nItemIndex)){return a._nItemIndex;}else{return null;}};YAHOO.widget.AutoComplete.prototype.setHeader=function(b){if(this._elHeader){var a=this._elHeader;if(b){a.innerHTML=b;a.style.display="";}else{a.innerHTML="";a.style.display="none";}}};YAHOO.widget.AutoComplete.prototype.setFooter=function(b){if(this._elFooter){var a=this._elFooter;if(b){a.innerHTML=b;a.style.display="";}else{a.innerHTML="";a.style.display="none";}}};YAHOO.widget.AutoComplete.prototype.setBody=function(a){if(this._elBody){var b=this._elBody;YAHOO.util.Event.purgeElement(b,true);if(a){b.innerHTML=a;b.style.display="";}else{b.innerHTML="";b.style.display="none";}this._elList=null;}};YAHOO.widget.AutoComplete.prototype.generateRequest=function(b){var a=this.dataSource.dataType;if(a===YAHOO.util.DataSourceBase.TYPE_XHR){if(!this.dataSource.connMethodPost){b=(this.queryQuestionMark?"?":"")+(this.dataSource.scriptQueryParam||"query")+"="+b+(this.dataSource.scriptQueryAppend?("&"+this.dataSource.scriptQueryAppend):"");}else{b=(this.dataSource.scriptQueryParam||"query")+"="+b+(this.dataSource.scriptQueryAppend?("&"+this.dataSource.scriptQueryAppend):"");}}else{if(a===YAHOO.util.DataSourceBase.TYPE_SCRIPTNODE){b="&"+(this.dataSource.scriptQueryParam||"query")+"="+b+(this.dataSource.scriptQueryAppend?("&"+this.dataSource.scriptQueryAppend):"");}}return b;};YAHOO.widget.AutoComplete.prototype.sendQuery=function(b){this._bFocused=true;var a=(this.delimChar)?this._elTextbox.value+b:b;this._sendQuery(a);};YAHOO.widget.AutoComplete.prototype.snapContainer=function(){var a=this._elTextbox,b=YAHOO.util.Dom.getXY(a);b[1]+=YAHOO.util.Dom.get(a).offsetHeight+2;YAHOO.util.Dom.setXY(this._elContainer,b);};YAHOO.widget.AutoComplete.prototype.expandContainer=function(){this._toggleContainer(true);};YAHOO.widget.AutoComplete.prototype.collapseContainer=function(){this._toggleContainer(false);};YAHOO.widget.AutoComplete.prototype.clearList=function(){var b=this._elList.childNodes,a=b.length-1;for(;a>-1;a--){b[a].style.display="none";}};YAHOO.widget.AutoComplete.prototype.getSubsetMatches=function(e){var d,c,a;for(var b=e.length;b>=this.minQueryLength;b--){a=this.generateRequest(e.substr(0,b));this.dataRequestEvent.fire(this,d,a);c=this.dataSource.getCachedResponse(a);if(c){return this.filterResults.apply(this.dataSource,[e,c,c,{scope:this}]);}}return null;};YAHOO.widget.AutoComplete.prototype.preparseRawResponse=function(c,b,a){var d=((this.responseStripAfter!=="")&&(b.indexOf))?b.indexOf(this.responseStripAfter):-1;if(d!=-1){b=b.substring(0,d);}return b;};YAHOO.widget.AutoComplete.prototype.filterResults=function(l,n,r,m){if(m&&m.argument&&YAHOO.lang.isValue(m.argument.query)){l=m.argument.query;}if(l&&l!==""){r=YAHOO.widget.AutoComplete._cloneObject(r);var j=m.scope,q=this,c=r.results,o=[],b=j.maxResultsDisplayed,k=(q.queryMatchCase||j.queryMatchCase),a=(q.queryMatchContains||j.queryMatchContains);for(var d=0,h=c.length;d<h;d++){var f=c[d];var e=null;if(YAHOO.lang.isString(f)){e=f;}else{if(YAHOO.lang.isArray(f)){e=f[0];}else{if(this.responseSchema.fields){var p=this.responseSchema.fields[0].key||this.responseSchema.fields[0];e=f[p];}else{if(this.key){e=f[this.key];}}}}if(YAHOO.lang.isString(e)){var g=(k)?e.indexOf(decodeURIComponent(l)):e.toLowerCase().indexOf(decodeURIComponent(l).toLowerCase());if((!a&&(g===0))||(a&&(g>-1))){o.push(f);}}if(h>b&&o.length===b){break;}}r.results=o;}else{}return r;};YAHOO.widget.AutoComplete.prototype.handleResponse=function(c,a,b){if((this instanceof YAHOO.widget.AutoComplete)&&this._sName){this._populateList(c,a,b);}};YAHOO.widget.AutoComplete.prototype.doBeforeLoadData=function(c,a,b){return true;};YAHOO.widget.AutoComplete.prototype.formatResult=function(b,d,a){var c=(a)?a:"";return c;};YAHOO.widget.AutoComplete.prototype.formatEscapedResult=function(c,d,b){var a=(b)?b:"";return YAHOO.lang.escapeHTML(a);};YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer=function(d,a,c,b){return true;};YAHOO.widget.AutoComplete.prototype.destroy=function(){var b=this.toString();var a=this._elTextbox;var d=this._elContainer;this.textboxFocusEvent.unsubscribeAll();this.textboxKeyEvent.unsubscribeAll();this.dataRequestEvent.unsubscribeAll();this.dataReturnEvent.unsubscribeAll();this.dataErrorEvent.unsubscribeAll();this.containerPopulateEvent.unsubscribeAll();this.containerExpandEvent.unsubscribeAll();this.typeAheadEvent.unsubscribeAll();this.itemMouseOverEvent.unsubscribeAll();this.itemMouseOutEvent.unsubscribeAll();this.itemArrowToEvent.unsubscribeAll();this.itemArrowFromEvent.unsubscribeAll();this.itemSelectEvent.unsubscribeAll();this.unmatchedItemSelectEvent.unsubscribeAll();this.selectionEnforceEvent.unsubscribeAll();this.containerCollapseEvent.unsubscribeAll();this.textboxBlurEvent.unsubscribeAll();this.textboxChangeEvent.unsubscribeAll();YAHOO.util.Event.purgeElement(a,true);YAHOO.util.Event.purgeElement(d,true);d.innerHTML="";for(var c in this){if(YAHOO.lang.hasOwnProperty(this,c)){this[c]=null;}}};YAHOO.widget.AutoComplete.prototype.textboxFocusEvent=null;YAHOO.widget.AutoComplete.prototype.textboxKeyEvent=null;YAHOO.widget.AutoComplete.prototype.dataRequestEvent=null;YAHOO.widget.AutoComplete.prototype.dataRequestCancelEvent=null;YAHOO.widget.AutoComplete.prototype.dataReturnEvent=null;YAHOO.widget.AutoComplete.prototype.dataErrorEvent=null;
-YAHOO.widget.AutoComplete.prototype.containerPopulateEvent=null;YAHOO.widget.AutoComplete.prototype.containerExpandEvent=null;YAHOO.widget.AutoComplete.prototype.typeAheadEvent=null;YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent=null;YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent=null;YAHOO.widget.AutoComplete.prototype.itemArrowToEvent=null;YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent=null;YAHOO.widget.AutoComplete.prototype.itemSelectEvent=null;YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent=null;YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent=null;YAHOO.widget.AutoComplete.prototype.containerCollapseEvent=null;YAHOO.widget.AutoComplete.prototype.textboxBlurEvent=null;YAHOO.widget.AutoComplete.prototype.textboxChangeEvent=null;YAHOO.widget.AutoComplete._nIndex=0;YAHOO.widget.AutoComplete.prototype._sName=null;YAHOO.widget.AutoComplete.prototype._elTextbox=null;YAHOO.widget.AutoComplete.prototype._elContainer=null;YAHOO.widget.AutoComplete.prototype._elContent=null;YAHOO.widget.AutoComplete.prototype._elHeader=null;YAHOO.widget.AutoComplete.prototype._elBody=null;YAHOO.widget.AutoComplete.prototype._elFooter=null;YAHOO.widget.AutoComplete.prototype._elShadow=null;YAHOO.widget.AutoComplete.prototype._elIFrame=null;YAHOO.widget.AutoComplete.prototype._bFocused=false;YAHOO.widget.AutoComplete.prototype._oAnim=null;YAHOO.widget.AutoComplete.prototype._bContainerOpen=false;YAHOO.widget.AutoComplete.prototype._bOverContainer=false;YAHOO.widget.AutoComplete.prototype._elList=null;YAHOO.widget.AutoComplete.prototype._nDisplayedItems=0;YAHOO.widget.AutoComplete.prototype._sCurQuery=null;YAHOO.widget.AutoComplete.prototype._sPastSelections="";YAHOO.widget.AutoComplete.prototype._sInitInputValue=null;YAHOO.widget.AutoComplete.prototype._elCurListItem=null;YAHOO.widget.AutoComplete.prototype._elCurPrehighlightItem=null;YAHOO.widget.AutoComplete.prototype._bItemSelected=false;YAHOO.widget.AutoComplete.prototype._nKeyCode=null;YAHOO.widget.AutoComplete.prototype._nDelayID=-1;YAHOO.widget.AutoComplete.prototype._nTypeAheadDelayID=-1;YAHOO.widget.AutoComplete.prototype._iFrameSrc="javascript:false;";YAHOO.widget.AutoComplete.prototype._queryInterval=null;YAHOO.widget.AutoComplete.prototype._sLastTextboxValue=null;YAHOO.widget.AutoComplete.prototype._initProps=function(){var b=this.minQueryLength;if(!YAHOO.lang.isNumber(b)){this.minQueryLength=1;}var e=this.maxResultsDisplayed;if(!YAHOO.lang.isNumber(e)||(e<1)){this.maxResultsDisplayed=10;}var f=this.queryDelay;if(!YAHOO.lang.isNumber(f)||(f<0)){this.queryDelay=0.2;}var c=this.typeAheadDelay;if(!YAHOO.lang.isNumber(c)||(c<0)){this.typeAheadDelay=0.2;}var a=this.delimChar;if(YAHOO.lang.isString(a)&&(a.length>0)){this.delimChar=[a];}else{if(!YAHOO.lang.isArray(a)){this.delimChar=null;}}var d=this.animSpeed;if((this.animHoriz||this.animVert)&&YAHOO.util.Anim){if(!YAHOO.lang.isNumber(d)||(d<0)){this.animSpeed=0.3;}if(!this._oAnim){this._oAnim=new YAHOO.util.Anim(this._elContent,{},this.animSpeed);}else{this._oAnim.duration=this.animSpeed;}}if(this.forceSelection&&a){}};YAHOO.widget.AutoComplete.prototype._initContainerHelperEls=function(){if(this.useShadow&&!this._elShadow){var a=document.createElement("div");a.className="yui-ac-shadow";a.style.width=0;a.style.height=0;this._elShadow=this._elContainer.appendChild(a);}if(this.useIFrame&&!this._elIFrame){var b=document.createElement("iframe");b.src=this._iFrameSrc;b.frameBorder=0;b.scrolling="no";b.style.position="absolute";b.style.width=0;b.style.height=0;b.style.padding=0;b.tabIndex=-1;b.role="presentation";b.title="Presentational iframe shim";this._elIFrame=this._elContainer.appendChild(b);}};YAHOO.widget.AutoComplete.prototype._initContainerEl=function(){YAHOO.util.Dom.addClass(this._elContainer,"yui-ac-container");if(!this._elContent){var c=document.createElement("div");c.className="yui-ac-content";c.style.display="none";this._elContent=this._elContainer.appendChild(c);var b=document.createElement("div");b.className="yui-ac-hd";b.style.display="none";this._elHeader=this._elContent.appendChild(b);var d=document.createElement("div");d.className="yui-ac-bd";this._elBody=this._elContent.appendChild(d);var a=document.createElement("div");a.className="yui-ac-ft";a.style.display="none";this._elFooter=this._elContent.appendChild(a);}else{}};YAHOO.widget.AutoComplete.prototype._initListEl=function(){var c=this.maxResultsDisplayed,a=this._elList||document.createElement("ul"),b;while(a.childNodes.length<c){b=document.createElement("li");b.style.display="none";b._nItemIndex=a.childNodes.length;a.appendChild(b);}if(!this._elList){var d=this._elBody;YAHOO.util.Event.purgeElement(d,true);d.innerHTML="";this._elList=d.appendChild(a);}this._elBody.style.display="";};YAHOO.widget.AutoComplete.prototype._focus=function(){var a=this;setTimeout(function(){try{a._elTextbox.focus();}catch(b){}},0);};YAHOO.widget.AutoComplete.prototype._enableIntervalDetection=function(){var a=this;if(!a._queryInterval&&a.queryInterval){a._queryInterval=setInterval(function(){a._onInterval();},a.queryInterval);}};YAHOO.widget.AutoComplete.prototype.enableIntervalDetection=YAHOO.widget.AutoComplete.prototype._enableIntervalDetection;YAHOO.widget.AutoComplete.prototype._onInterval=function(){var a=this._elTextbox.value;var b=this._sLastTextboxValue;if(a!=b){this._sLastTextboxValue=a;this._sendQuery(a);}};YAHOO.widget.AutoComplete.prototype._clearInterval=function(){if(this._queryInterval){clearInterval(this._queryInterval);this._queryInterval=null;}};YAHOO.widget.AutoComplete.prototype._isIgnoreKey=function(a){if((a==9)||(a==13)||(a==16)||(a==17)||(a>=18&&a<=20)||(a==27)||(a>=33&&a<=35)||(a>=36&&a<=40)||(a>=44&&a<=45)||(a==229)){return true;}return false;};YAHOO.widget.AutoComplete.prototype._sendQuery=function(d){if(this.minQueryLength<0){this._toggleContainer(false);return;}if(this.delimChar){var a=this._extractQuery(d);d=a.query;this._sPastSelections=a.previous;}if((d&&(d.length<this.minQueryLength))||(!d&&this.minQueryLength>0)){if(this._nDelayID!=-1){clearTimeout(this._nDelayID);
-}this._toggleContainer(false);return;}d=encodeURIComponent(d);this._nDelayID=-1;if(this.dataSource.queryMatchSubset||this.queryMatchSubset){var c=this.getSubsetMatches(d);if(c){this.handleResponse(d,c,{query:d});return;}}if(this.dataSource.responseStripAfter){this.dataSource.doBeforeParseData=this.preparseRawResponse;}if(this.applyLocalFilter){this.dataSource.doBeforeCallback=this.filterResults;}var b=this.generateRequest(d);if(b!==undefined){this.dataRequestEvent.fire(this,d,b);this.dataSource.sendRequest(b,{success:this.handleResponse,failure:this.handleResponse,scope:this,argument:{query:d}});}else{this.dataRequestCancelEvent.fire(this,d);}};YAHOO.widget.AutoComplete.prototype._populateListItem=function(b,a,c){b.innerHTML=this.formatResult(a,c,b._sResultMatch);};YAHOO.widget.AutoComplete.prototype._populateList=function(n,f,c){if(this._nTypeAheadDelayID!=-1){clearTimeout(this._nTypeAheadDelayID);}n=(c&&c.query)?c.query:n;var h=this.doBeforeLoadData(n,f,c);if(h&&!f.error){this.dataReturnEvent.fire(this,n,f.results);if(this._bFocused){var p=decodeURIComponent(n);this._sCurQuery=p;this._bItemSelected=false;var u=f.results,a=Math.min(u.length,this.maxResultsDisplayed),m=(this.dataSource.responseSchema.fields)?(this.dataSource.responseSchema.fields[0].key||this.dataSource.responseSchema.fields[0]):0;if(a>0){if(!this._elList||(this._elList.childNodes.length<a)){this._initListEl();}this._initContainerHelperEls();var l=this._elList.childNodes;for(var t=a-1;t>=0;t--){var s=l[t],e=u[t];if(this.resultTypeList){var b=[];b[0]=(YAHOO.lang.isString(e))?e:e[m]||e[this.key];var o=this.dataSource.responseSchema.fields;if(YAHOO.lang.isArray(o)&&(o.length>1)){for(var q=1,v=o.length;q<v;q++){b[b.length]=e[o[q].key||o[q]];}}else{if(YAHOO.lang.isArray(e)){b=e;}else{if(YAHOO.lang.isString(e)){b=[e];}else{b[1]=e;}}}e=b;}s._sResultMatch=(YAHOO.lang.isString(e))?e:(YAHOO.lang.isArray(e))?e[0]:(e[m]||"");s._oResultData=e;this._populateListItem(s,e,p);s.style.display="";}if(a<l.length){var g;for(var r=l.length-1;r>=a;r--){g=l[r];g.style.display="none";}}this._nDisplayedItems=a;this.containerPopulateEvent.fire(this,n,u);if(this.autoHighlight){var d=this._elList.firstChild;this._toggleHighlight(d,"to");this.itemArrowToEvent.fire(this,d);this._typeAhead(d,n);}else{this._toggleHighlight(this._elCurListItem,"from");}h=this._doBeforeExpandContainer(this._elTextbox,this._elContainer,n,u);this._toggleContainer(h);}else{this._toggleContainer(false);}return;}}else{this.dataErrorEvent.fire(this,n,f);}};YAHOO.widget.AutoComplete.prototype._doBeforeExpandContainer=function(d,a,c,b){if(this.autoSnapContainer){this.snapContainer();}return this.doBeforeExpandContainer(d,a,c,b);};YAHOO.widget.AutoComplete.prototype._clearSelection=function(){var a=(this.delimChar)?this._extractQuery(this._elTextbox.value):{previous:"",query:this._elTextbox.value};this._elTextbox.value=a.previous;this.selectionEnforceEvent.fire(this,a.query);};YAHOO.widget.AutoComplete.prototype._textMatchesOption=function(){var a=null;for(var b=0;b<this._nDisplayedItems;b++){var c=this._elList.childNodes[b];var d=(""+c._sResultMatch).toLowerCase();if(d==this._sCurQuery.toLowerCase()){a=c;break;}}return(a);};YAHOO.widget.AutoComplete.prototype._typeAhead=function(b,d){if(!this.typeAhead||(this._nKeyCode==8)){return;}var a=this,c=this._elTextbox;if(c.setSelectionRange||c.createTextRange){this._nTypeAheadDelayID=setTimeout(function(){var f=c.value.length;a._updateValue(b);var g=c.value.length;a._selectText(c,f,g);var e=c.value.substr(f,g);a._sCurQuery=b._sResultMatch;a.typeAheadEvent.fire(a,d,e);},(this.typeAheadDelay*1000));}};YAHOO.widget.AutoComplete.prototype._selectText=function(d,a,b){if(d.setSelectionRange){d.setSelectionRange(a,b);}else{if(d.createTextRange){var c=d.createTextRange();c.moveStart("character",a);c.moveEnd("character",b-d.value.length);c.select();}else{d.select();}}};YAHOO.widget.AutoComplete.prototype._extractQuery=function(h){var c=this.delimChar,f=-1,g,e,b=c.length-1,d;for(;b>=0;b--){g=h.lastIndexOf(c[b]);if(g>f){f=g;}}if(c[b]==" "){for(var a=c.length-1;a>=0;a--){if(h[f-1]==c[a]){f--;break;}}}if(f>-1){e=f+1;while(h.charAt(e)==" "){e+=1;}d=h.substring(0,e);h=h.substr(e);}else{d="";}return{previous:d,query:h};};YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers=function(d){var e=this._elContent.offsetWidth+"px";var b=this._elContent.offsetHeight+"px";if(this.useIFrame&&this._elIFrame){var c=this._elIFrame;if(d){c.style.width=e;c.style.height=b;c.style.padding="";}else{c.style.width=0;c.style.height=0;c.style.padding=0;}}if(this.useShadow&&this._elShadow){var a=this._elShadow;if(d){a.style.width=e;a.style.height=b;}else{a.style.width=0;a.style.height=0;}}};YAHOO.widget.AutoComplete.prototype._toggleContainer=function(i){var d=this._elContainer;if(this.alwaysShowContainer&&this._bContainerOpen){return;}if(!i){this._toggleHighlight(this._elCurListItem,"from");this._nDisplayedItems=0;this._sCurQuery=null;if(this._elContent.style.display=="none"){return;}}var a=this._oAnim;if(a&&a.getEl()&&(this.animHoriz||this.animVert)){if(a.isAnimated()){a.stop(true);}var g=this._elContent.cloneNode(true);d.appendChild(g);g.style.top="-9000px";g.style.width="";g.style.height="";g.style.display="";var f=g.offsetWidth;var c=g.offsetHeight;var b=(this.animHoriz)?0:f;var e=(this.animVert)?0:c;a.attributes=(i)?{width:{to:f},height:{to:c}}:{width:{to:b},height:{to:e}};if(i&&!this._bContainerOpen){this._elContent.style.width=b+"px";this._elContent.style.height=e+"px";}else{this._elContent.style.width=f+"px";this._elContent.style.height=c+"px";}d.removeChild(g);g=null;var h=this;var j=function(){a.onComplete.unsubscribeAll();if(i){h._toggleContainerHelpers(true);h._bContainerOpen=i;h.containerExpandEvent.fire(h);}else{h._elContent.style.display="none";h._bContainerOpen=i;h.containerCollapseEvent.fire(h);}};this._toggleContainerHelpers(false);this._elContent.style.display="";a.onComplete.subscribe(j);a.animate();}else{if(i){this._elContent.style.display="";this._toggleContainerHelpers(true);
-this._bContainerOpen=i;this.containerExpandEvent.fire(this);}else{this._toggleContainerHelpers(false);this._elContent.style.display="none";this._bContainerOpen=i;this.containerCollapseEvent.fire(this);}}};YAHOO.widget.AutoComplete.prototype._toggleHighlight=function(a,c){if(a){var b=this.highlightClassName;if(this._elCurListItem){YAHOO.util.Dom.removeClass(this._elCurListItem,b);this._elCurListItem=null;}if((c=="to")&&b){YAHOO.util.Dom.addClass(a,b);this._elCurListItem=a;}}};YAHOO.widget.AutoComplete.prototype._togglePrehighlight=function(b,c){var a=this.prehighlightClassName;if(this._elCurPrehighlightItem){YAHOO.util.Dom.removeClass(this._elCurPrehighlightItem,a);}if(b==this._elCurListItem){return;}if((c=="mouseover")&&a){YAHOO.util.Dom.addClass(b,a);this._elCurPrehighlightItem=b;}else{YAHOO.util.Dom.removeClass(b,a);}};YAHOO.widget.AutoComplete.prototype._updateValue=function(c){if(!this.suppressInputUpdate){var f=this._elTextbox;var e=(this.delimChar)?(this.delimChar[0]||this.delimChar):null;var b=c._sResultMatch;var d="";if(e){d=this._sPastSelections;d+=b+e;if(e!=" "){d+=" ";}}else{d=b;}f.value=d;if(f.type=="textarea"){f.scrollTop=f.scrollHeight;}var a=f.value.length;this._selectText(f,a,a);this._elCurListItem=c;}};YAHOO.widget.AutoComplete.prototype._selectItem=function(a){this._bItemSelected=true;this._updateValue(a);this._sPastSelections=this._elTextbox.value;this._clearInterval();this.itemSelectEvent.fire(this,a,a._oResultData);this._toggleContainer(false);};YAHOO.widget.AutoComplete.prototype._jumpSelection=function(){if(this._elCurListItem){this._selectItem(this._elCurListItem);}else{this._toggleContainer(false);}};YAHOO.widget.AutoComplete.prototype._moveSelection=function(g){if(this._bContainerOpen){var h=this._elCurListItem,d=-1;if(h){d=h._nItemIndex;}var e=(g==40)?(d+1):(d-1);if(e<-2||e>=this._nDisplayedItems){return;}if(h){this._toggleHighlight(h,"from");this.itemArrowFromEvent.fire(this,h);}if(e==-1){if(this.delimChar){this._elTextbox.value=this._sPastSelections+this._sCurQuery;}else{this._elTextbox.value=this._sCurQuery;}return;}if(e==-2){this._toggleContainer(false);return;}var f=this._elList.childNodes[e],b=this._elContent,c=YAHOO.util.Dom.getStyle(b,"overflow"),i=YAHOO.util.Dom.getStyle(b,"overflowY"),a=((c=="auto")||(c=="scroll")||(i=="auto")||(i=="scroll"));if(a&&(e>-1)&&(e<this._nDisplayedItems)){if(g==40){if((f.offsetTop+f.offsetHeight)>(b.scrollTop+b.offsetHeight)){b.scrollTop=(f.offsetTop+f.offsetHeight)-b.offsetHeight;}else{if((f.offsetTop+f.offsetHeight)<b.scrollTop){b.scrollTop=f.offsetTop;}}}else{if(f.offsetTop<b.scrollTop){this._elContent.scrollTop=f.offsetTop;}else{if(f.offsetTop>(b.scrollTop+b.offsetHeight)){this._elContent.scrollTop=(f.offsetTop+f.offsetHeight)-b.offsetHeight;}}}}this._toggleHighlight(f,"to");this.itemArrowToEvent.fire(this,f);if(this.typeAhead){this._updateValue(f);this._sCurQuery=f._sResultMatch;}}};YAHOO.widget.AutoComplete.prototype._onContainerMouseover=function(a,c){var d=YAHOO.util.Event.getTarget(a);var b=d.nodeName.toLowerCase();while(d&&(b!="table")){switch(b){case"body":return;case"li":if(c.prehighlightClassName){c._togglePrehighlight(d,"mouseover");}else{c._toggleHighlight(d,"to");}c.itemMouseOverEvent.fire(c,d);break;case"div":if(YAHOO.util.Dom.hasClass(d,"yui-ac-container")){c._bOverContainer=true;return;}break;default:break;}d=d.parentNode;if(d){b=d.nodeName.toLowerCase();}}};YAHOO.widget.AutoComplete.prototype._onContainerMouseout=function(a,c){var d=YAHOO.util.Event.getTarget(a);var b=d.nodeName.toLowerCase();while(d&&(b!="table")){switch(b){case"body":return;case"li":if(c.prehighlightClassName){c._togglePrehighlight(d,"mouseout");}else{c._toggleHighlight(d,"from");}c.itemMouseOutEvent.fire(c,d);break;case"ul":c._toggleHighlight(c._elCurListItem,"to");break;case"div":if(YAHOO.util.Dom.hasClass(d,"yui-ac-container")){c._bOverContainer=false;return;}break;default:break;}d=d.parentNode;if(d){b=d.nodeName.toLowerCase();}}};YAHOO.widget.AutoComplete.prototype._onContainerClick=function(a,c){var d=YAHOO.util.Event.getTarget(a);var b=d.nodeName.toLowerCase();while(d&&(b!="table")){switch(b){case"body":return;case"li":c._toggleHighlight(d,"to");c._selectItem(d);return;default:break;}d=d.parentNode;if(d){b=d.nodeName.toLowerCase();}}};YAHOO.widget.AutoComplete.prototype._onContainerScroll=function(a,b){b._focus();};YAHOO.widget.AutoComplete.prototype._onContainerResize=function(a,b){b._toggleContainerHelpers(b._bContainerOpen);};YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown=function(a,b){var c=a.keyCode;if(b._nTypeAheadDelayID!=-1){clearTimeout(b._nTypeAheadDelayID);}switch(c){case 9:if(!YAHOO.env.ua.opera&&(navigator.userAgent.toLowerCase().indexOf("mac")==-1)||(YAHOO.env.ua.webkit>420)){if(b._elCurListItem){if(b.delimChar&&(b._nKeyCode!=c)){if(b._bContainerOpen){YAHOO.util.Event.stopEvent(a);}}b._selectItem(b._elCurListItem);}else{b._toggleContainer(false);}}break;case 13:if(!YAHOO.env.ua.opera&&(navigator.userAgent.toLowerCase().indexOf("mac")==-1)||(YAHOO.env.ua.webkit>420)){if(b._elCurListItem){if(b._nKeyCode!=c){if(b._bContainerOpen){YAHOO.util.Event.stopEvent(a);}}b._selectItem(b._elCurListItem);}else{b._toggleContainer(false);}}break;case 27:b._toggleContainer(false);return;case 39:b._jumpSelection();break;case 38:if(b._bContainerOpen){YAHOO.util.Event.stopEvent(a);b._moveSelection(c);}break;case 40:if(b._bContainerOpen){YAHOO.util.Event.stopEvent(a);b._moveSelection(c);}break;default:b._bItemSelected=false;b._toggleHighlight(b._elCurListItem,"from");b.textboxKeyEvent.fire(b,c);break;}if(c===18){b._enableIntervalDetection();}b._nKeyCode=c;};YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress=function(a,b){var c=a.keyCode;if(YAHOO.env.ua.opera||(navigator.userAgent.toLowerCase().indexOf("mac")!=-1)&&(YAHOO.env.ua.webkit<420)){switch(c){case 9:if(b._bContainerOpen){if(b.delimChar){YAHOO.util.Event.stopEvent(a);}if(b._elCurListItem){b._selectItem(b._elCurListItem);}else{b._toggleContainer(false);}}break;case 13:if(b._bContainerOpen){YAHOO.util.Event.stopEvent(a);
-if(b._elCurListItem){b._selectItem(b._elCurListItem);}else{b._toggleContainer(false);}}break;default:break;}}else{if(c==229){b._enableIntervalDetection();}}};YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp=function(a,c){var b=this.value;c._initProps();var d=a.keyCode;if(c._isIgnoreKey(d)){return;}if(c._nDelayID!=-1){clearTimeout(c._nDelayID);}c._nDelayID=setTimeout(function(){c._sendQuery(b);},(c.queryDelay*1000));};YAHOO.widget.AutoComplete.prototype._onTextboxFocus=function(a,b){if(!b._bFocused){b._elTextbox.setAttribute("autocomplete","off");b._bFocused=true;b._sInitInputValue=b._elTextbox.value;b.textboxFocusEvent.fire(b);}};YAHOO.widget.AutoComplete.prototype._onTextboxBlur=function(a,c){if(!c._bOverContainer||(c._nKeyCode==9)){if(!c._bItemSelected){var b=c._textMatchesOption();if(!c._bContainerOpen||(c._bContainerOpen&&(b===null))){if(c.forceSelection){c._clearSelection();}else{c.unmatchedItemSelectEvent.fire(c,c._sCurQuery);}}else{if(c.forceSelection){c._selectItem(b);}}}c._clearInterval();c._bFocused=false;if(c._sInitInputValue!==c._elTextbox.value){c.textboxChangeEvent.fire(c);}c.textboxBlurEvent.fire(c);c._toggleContainer(false);}else{c._focus();}};YAHOO.widget.AutoComplete.prototype._onWindowUnload=function(a,b){if(b&&b._elTextbox&&b.allowBrowserAutocomplete){b._elTextbox.setAttribute("autocomplete","on");}};YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery=function(a){return this.generateRequest(a);};YAHOO.widget.AutoComplete.prototype.getListItems=function(){var c=[],b=this._elList.childNodes;for(var a=b.length-1;a>=0;a--){c[a]=b[a];}return c;};YAHOO.widget.AutoComplete._cloneObject=function(d){if(!YAHOO.lang.isValue(d)){return d;}var f={};if(YAHOO.lang.isFunction(d)){f=d;}else{if(YAHOO.lang.isArray(d)){var e=[];for(var c=0,b=d.length;c<b;c++){e[c]=YAHOO.widget.AutoComplete._cloneObject(d[c]);}f=e;}else{if(YAHOO.lang.isObject(d)){for(var a in d){if(YAHOO.lang.hasOwnProperty(d,a)){if(YAHOO.lang.isValue(d[a])&&YAHOO.lang.isObject(d[a])||YAHOO.lang.isArray(d[a])){f[a]=YAHOO.widget.AutoComplete._cloneObject(d[a]);}else{f[a]=d[a];}}}}else{f=d;}}}return f;};YAHOO.register("autocomplete",YAHOO.widget.AutoComplete,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-(function(){YAHOO.util.Config=function(d){if(d){this.init(d);}};var b=YAHOO.lang,c=YAHOO.util.CustomEvent,a=YAHOO.util.Config;a.CONFIG_CHANGED_EVENT="configChanged";a.BOOLEAN_TYPE="boolean";a.prototype={owner:null,queueInProgress:false,config:null,initialConfig:null,eventQueue:null,configChangedEvent:null,init:function(d){this.owner=d;this.configChangedEvent=this.createEvent(a.CONFIG_CHANGED_EVENT);this.configChangedEvent.signature=c.LIST;this.queueInProgress=false;this.config={};this.initialConfig={};this.eventQueue=[];},checkBoolean:function(d){return(typeof d==a.BOOLEAN_TYPE);},checkNumber:function(d){return(!isNaN(d));},fireEvent:function(d,f){var e=this.config[d];if(e&&e.event){e.event.fire(f);}},addProperty:function(e,d){e=e.toLowerCase();this.config[e]=d;d.event=this.createEvent(e,{scope:this.owner});d.event.signature=c.LIST;d.key=e;if(d.handler){d.event.subscribe(d.handler,this.owner);}this.setProperty(e,d.value,true);if(!d.suppressEvent){this.queueProperty(e,d.value);}},getConfig:function(){var d={},f=this.config,g,e;for(g in f){if(b.hasOwnProperty(f,g)){e=f[g];if(e&&e.event){d[g]=e.value;}}}return d;},getProperty:function(d){var e=this.config[d.toLowerCase()];if(e&&e.event){return e.value;}else{return undefined;}},resetProperty:function(d){d=d.toLowerCase();var e=this.config[d];if(e&&e.event){if(d in this.initialConfig){this.setProperty(d,this.initialConfig[d]);return true;}}else{return false;}},setProperty:function(e,g,d){var f;e=e.toLowerCase();if(this.queueInProgress&&!d){this.queueProperty(e,g);return true;}else{f=this.config[e];if(f&&f.event){if(f.validator&&!f.validator(g)){return false;}else{f.value=g;if(!d){this.fireEvent(e,g);this.configChangedEvent.fire([e,g]);}return true;}}else{return false;}}},queueProperty:function(v,r){v=v.toLowerCase();var u=this.config[v],l=false,k,g,h,j,p,t,f,n,o,d,m,w,e;if(u&&u.event){if(!b.isUndefined(r)&&u.validator&&!u.validator(r)){return false;}else{if(!b.isUndefined(r)){u.value=r;}else{r=u.value;}l=false;k=this.eventQueue.length;for(m=0;m<k;m++){g=this.eventQueue[m];if(g){h=g[0];j=g[1];if(h==v){this.eventQueue[m]=null;this.eventQueue.push([v,(!b.isUndefined(r)?r:j)]);l=true;break;}}}if(!l&&!b.isUndefined(r)){this.eventQueue.push([v,r]);}}if(u.supercedes){p=u.supercedes.length;for(w=0;w<p;w++){t=u.supercedes[w];f=this.eventQueue.length;for(e=0;e<f;e++){n=this.eventQueue[e];if(n){o=n[0];d=n[1];if(o==t.toLowerCase()){this.eventQueue.push([o,d]);this.eventQueue[e]=null;break;}}}}}return true;}else{return false;}},refireEvent:function(d){d=d.toLowerCase();var e=this.config[d];if(e&&e.event&&!b.isUndefined(e.value)){if(this.queueInProgress){this.queueProperty(d);}else{this.fireEvent(d,e.value);}}},applyConfig:function(d,g){var f,e;if(g){e={};for(f in d){if(b.hasOwnProperty(d,f)){e[f.toLowerCase()]=d[f];}}this.initialConfig=e;}for(f in d){if(b.hasOwnProperty(d,f)){this.queueProperty(f,d[f]);}}},refresh:function(){var d;for(d in this.config){if(b.hasOwnProperty(this.config,d)){this.refireEvent(d);}}},fireQueue:function(){var e,h,d,g,f;this.queueInProgress=true;for(e=0;e<this.eventQueue.length;e++){h=this.eventQueue[e];if(h){d=h[0];g=h[1];f=this.config[d];f.value=g;this.eventQueue[e]=null;this.fireEvent(d,g);}}this.queueInProgress=false;this.eventQueue=[];},subscribeToConfigEvent:function(d,e,g,h){var f=this.config[d.toLowerCase()];if(f&&f.event){if(!a.alreadySubscribed(f.event,e,g)){f.event.subscribe(e,g,h);}return true;}else{return false;}},unsubscribeFromConfigEvent:function(d,e,g){var f=this.config[d.toLowerCase()];if(f&&f.event){return f.event.unsubscribe(e,g);}else{return false;}},toString:function(){var d="Config";if(this.owner){d+=" ["+this.owner.toString()+"]";}return d;},outputEventQueue:function(){var d="",g,e,f=this.eventQueue.length;for(e=0;e<f;e++){g=this.eventQueue[e];if(g){d+=g[0]+"="+g[1]+", ";}}return d;},destroy:function(){var e=this.config,d,f;for(d in e){if(b.hasOwnProperty(e,d)){f=e[d];f.event.unsubscribeAll();f.event=null;}}this.configChangedEvent.unsubscribeAll();this.configChangedEvent=null;this.owner=null;this.config=null;this.initialConfig=null;this.eventQueue=null;}};a.alreadySubscribed=function(e,h,j){var f=e.subscribers.length,d,g;if(f>0){g=f-1;do{d=e.subscribers[g];if(d&&d.obj==j&&d.fn==h){return true;}}while(g--);}return false;};YAHOO.lang.augmentProto(a,YAHOO.util.EventProvider);}());(function(){YAHOO.widget.Module=function(r,q){if(r){this.init(r,q);}else{}};var f=YAHOO.util.Dom,d=YAHOO.util.Config,n=YAHOO.util.Event,m=YAHOO.util.CustomEvent,g=YAHOO.widget.Module,i=YAHOO.env.ua,h,p,o,e,a={"BEFORE_INIT":"beforeInit","INIT":"init","APPEND":"append","BEFORE_RENDER":"beforeRender","RENDER":"render","CHANGE_HEADER":"changeHeader","CHANGE_BODY":"changeBody","CHANGE_FOOTER":"changeFooter","CHANGE_CONTENT":"changeContent","DESTROY":"destroy","BEFORE_SHOW":"beforeShow","SHOW":"show","BEFORE_HIDE":"beforeHide","HIDE":"hide"},j={"VISIBLE":{key:"visible",value:true,validator:YAHOO.lang.isBoolean},"EFFECT":{key:"effect",suppressEvent:true,supercedes:["visible"]},"MONITOR_RESIZE":{key:"monitorresize",value:true},"APPEND_TO_DOCUMENT_BODY":{key:"appendtodocumentbody",value:false}};g.IMG_ROOT=null;g.IMG_ROOT_SSL=null;g.CSS_MODULE="yui-module";g.CSS_HEADER="hd";g.CSS_BODY="bd";g.CSS_FOOTER="ft";g.RESIZE_MONITOR_SECURE_URL="javascript:false;";g.RESIZE_MONITOR_BUFFER=1;g.textResizeEvent=new m("textResize");g.forceDocumentRedraw=function(){var q=document.documentElement;if(q){q.className+=" ";q.className=YAHOO.lang.trim(q.className);}};function l(){if(!h){h=document.createElement("div");h.innerHTML=('<div class="'+g.CSS_HEADER+'"></div>'+'<div class="'+g.CSS_BODY+'"></div><div class="'+g.CSS_FOOTER+'"></div>');p=h.firstChild;o=p.nextSibling;e=o.nextSibling;}return h;}function k(){if(!p){l();}return(p.cloneNode(false));}function b(){if(!o){l();}return(o.cloneNode(false));}function c(){if(!e){l();}return(e.cloneNode(false));}g.prototype={constructor:g,element:null,header:null,body:null,footer:null,id:null,imageRoot:g.IMG_ROOT,initEvents:function(){var q=m.LIST;
-this.beforeInitEvent=this.createEvent(a.BEFORE_INIT);this.beforeInitEvent.signature=q;this.initEvent=this.createEvent(a.INIT);this.initEvent.signature=q;this.appendEvent=this.createEvent(a.APPEND);this.appendEvent.signature=q;this.beforeRenderEvent=this.createEvent(a.BEFORE_RENDER);this.beforeRenderEvent.signature=q;this.renderEvent=this.createEvent(a.RENDER);this.renderEvent.signature=q;this.changeHeaderEvent=this.createEvent(a.CHANGE_HEADER);this.changeHeaderEvent.signature=q;this.changeBodyEvent=this.createEvent(a.CHANGE_BODY);this.changeBodyEvent.signature=q;this.changeFooterEvent=this.createEvent(a.CHANGE_FOOTER);this.changeFooterEvent.signature=q;this.changeContentEvent=this.createEvent(a.CHANGE_CONTENT);this.changeContentEvent.signature=q;this.destroyEvent=this.createEvent(a.DESTROY);this.destroyEvent.signature=q;this.beforeShowEvent=this.createEvent(a.BEFORE_SHOW);this.beforeShowEvent.signature=q;this.showEvent=this.createEvent(a.SHOW);this.showEvent.signature=q;this.beforeHideEvent=this.createEvent(a.BEFORE_HIDE);this.beforeHideEvent.signature=q;this.hideEvent=this.createEvent(a.HIDE);this.hideEvent.signature=q;},platform:function(){var q=navigator.userAgent.toLowerCase();if(q.indexOf("windows")!=-1||q.indexOf("win32")!=-1){return"windows";}else{if(q.indexOf("macintosh")!=-1){return"mac";}else{return false;}}}(),browser:function(){var q=navigator.userAgent.toLowerCase();if(q.indexOf("opera")!=-1){return"opera";}else{if(q.indexOf("msie 7")!=-1){return"ie7";}else{if(q.indexOf("msie")!=-1){return"ie";}else{if(q.indexOf("safari")!=-1){return"safari";}else{if(q.indexOf("gecko")!=-1){return"gecko";}else{return false;}}}}}}(),isSecure:function(){if(window.location.href.toLowerCase().indexOf("https")===0){return true;}else{return false;}}(),initDefaultConfig:function(){this.cfg.addProperty(j.VISIBLE.key,{handler:this.configVisible,value:j.VISIBLE.value,validator:j.VISIBLE.validator});this.cfg.addProperty(j.EFFECT.key,{handler:this.configEffect,suppressEvent:j.EFFECT.suppressEvent,supercedes:j.EFFECT.supercedes});this.cfg.addProperty(j.MONITOR_RESIZE.key,{handler:this.configMonitorResize,value:j.MONITOR_RESIZE.value});this.cfg.addProperty(j.APPEND_TO_DOCUMENT_BODY.key,{value:j.APPEND_TO_DOCUMENT_BODY.value});},init:function(v,u){var s,w;this.initEvents();this.beforeInitEvent.fire(g);this.cfg=new d(this);if(this.isSecure){this.imageRoot=g.IMG_ROOT_SSL;}if(typeof v=="string"){s=v;v=document.getElementById(v);if(!v){v=(l()).cloneNode(false);v.id=s;}}this.id=f.generateId(v);this.element=v;w=this.element.firstChild;if(w){var r=false,q=false,t=false;do{if(1==w.nodeType){if(!r&&f.hasClass(w,g.CSS_HEADER)){this.header=w;r=true;}else{if(!q&&f.hasClass(w,g.CSS_BODY)){this.body=w;q=true;}else{if(!t&&f.hasClass(w,g.CSS_FOOTER)){this.footer=w;t=true;}}}}}while((w=w.nextSibling));}this.initDefaultConfig();f.addClass(this.element,g.CSS_MODULE);if(u){this.cfg.applyConfig(u,true);}if(!d.alreadySubscribed(this.renderEvent,this.cfg.fireQueue,this.cfg)){this.renderEvent.subscribe(this.cfg.fireQueue,this.cfg,true);}this.initEvent.fire(g);},initResizeMonitor:function(){var r=(i.gecko&&this.platform=="windows");if(r){var q=this;setTimeout(function(){q._initResizeMonitor();},0);}else{this._initResizeMonitor();}},_initResizeMonitor:function(){var q,s,u;function w(){g.textResizeEvent.fire();}if(!i.opera){s=f.get("_yuiResizeMonitor");var v=this._supportsCWResize();if(!s){s=document.createElement("iframe");if(this.isSecure&&g.RESIZE_MONITOR_SECURE_URL&&i.ie){s.src=g.RESIZE_MONITOR_SECURE_URL;}if(!v){u=["<html><head><script ",'type="text/javascript">',"window.onresize=function(){window.parent.","YAHOO.widget.Module.textResizeEvent.","fire();};<","/script></head>","<body></body></html>"].join("");s.src="data:text/html;charset=utf-8,"+encodeURIComponent(u);}s.id="_yuiResizeMonitor";s.title="Text Resize Monitor";s.tabIndex=-1;s.setAttribute("role","presentation");s.style.position="absolute";s.style.visibility="hidden";var r=document.body,t=r.firstChild;if(t){r.insertBefore(s,t);}else{r.appendChild(s);}s.style.backgroundColor="transparent";s.style.borderWidth="0";s.style.width="2em";s.style.height="2em";s.style.left="0";s.style.top=(-1*(s.offsetHeight+g.RESIZE_MONITOR_BUFFER))+"px";s.style.visibility="visible";if(i.webkit){q=s.contentWindow.document;q.open();q.close();}}if(s&&s.contentWindow){g.textResizeEvent.subscribe(this.onDomResize,this,true);if(!g.textResizeInitialized){if(v){if(!n.on(s.contentWindow,"resize",w)){n.on(s,"resize",w);}}g.textResizeInitialized=true;}this.resizeMonitor=s;}}},_supportsCWResize:function(){var q=true;if(i.gecko&&i.gecko<=1.8){q=false;}return q;},onDomResize:function(s,r){var q=-1*(this.resizeMonitor.offsetHeight+g.RESIZE_MONITOR_BUFFER);this.resizeMonitor.style.top=q+"px";this.resizeMonitor.style.left="0";},setHeader:function(r){var q=this.header||(this.header=k());if(r.nodeName){q.innerHTML="";q.appendChild(r);}else{q.innerHTML=r;}if(this._rendered){this._renderHeader();}this.changeHeaderEvent.fire(r);this.changeContentEvent.fire();},appendToHeader:function(r){var q=this.header||(this.header=k());q.appendChild(r);this.changeHeaderEvent.fire(r);this.changeContentEvent.fire();},setBody:function(r){var q=this.body||(this.body=b());if(r.nodeName){q.innerHTML="";q.appendChild(r);}else{q.innerHTML=r;}if(this._rendered){this._renderBody();}this.changeBodyEvent.fire(r);this.changeContentEvent.fire();},appendToBody:function(r){var q=this.body||(this.body=b());q.appendChild(r);this.changeBodyEvent.fire(r);this.changeContentEvent.fire();},setFooter:function(r){var q=this.footer||(this.footer=c());if(r.nodeName){q.innerHTML="";q.appendChild(r);}else{q.innerHTML=r;}if(this._rendered){this._renderFooter();}this.changeFooterEvent.fire(r);this.changeContentEvent.fire();},appendToFooter:function(r){var q=this.footer||(this.footer=c());q.appendChild(r);this.changeFooterEvent.fire(r);this.changeContentEvent.fire();},render:function(s,q){var t=this;function r(u){if(typeof u=="string"){u=document.getElementById(u);
-}if(u){t._addToParent(u,t.element);t.appendEvent.fire();}}this.beforeRenderEvent.fire();if(!q){q=this.element;}if(s){r(s);}else{if(!f.inDocument(this.element)){return false;}}this._renderHeader(q);this._renderBody(q);this._renderFooter(q);this._rendered=true;this.renderEvent.fire();return true;},_renderHeader:function(q){q=q||this.element;if(this.header&&!f.inDocument(this.header)){var r=q.firstChild;if(r){q.insertBefore(this.header,r);}else{q.appendChild(this.header);}}},_renderBody:function(q){q=q||this.element;if(this.body&&!f.inDocument(this.body)){if(this.footer&&f.isAncestor(q,this.footer)){q.insertBefore(this.body,this.footer);}else{q.appendChild(this.body);}}},_renderFooter:function(q){q=q||this.element;if(this.footer&&!f.inDocument(this.footer)){q.appendChild(this.footer);}},destroy:function(q){var r,s=!(q);if(this.element){n.purgeElement(this.element,s);r=this.element.parentNode;}if(r){r.removeChild(this.element);}this.element=null;this.header=null;this.body=null;this.footer=null;g.textResizeEvent.unsubscribe(this.onDomResize,this);this.cfg.destroy();this.cfg=null;this.destroyEvent.fire();},show:function(){this.cfg.setProperty("visible",true);},hide:function(){this.cfg.setProperty("visible",false);},configVisible:function(r,q,s){var t=q[0];if(t){if(this.beforeShowEvent.fire()){f.setStyle(this.element,"display","block");this.showEvent.fire();}}else{if(this.beforeHideEvent.fire()){f.setStyle(this.element,"display","none");this.hideEvent.fire();}}},configEffect:function(r,q,s){this._cachedEffects=(this.cacheEffects)?this._createEffects(q[0]):null;},cacheEffects:true,_createEffects:function(t){var q=null,u,r,s;if(t){if(t instanceof Array){q=[];u=t.length;for(r=0;r<u;r++){s=t[r];if(s.effect){q[q.length]=s.effect(this,s.duration);}}}else{if(t.effect){q=[t.effect(this,t.duration)];}}}return q;},configMonitorResize:function(s,r,t){var q=r[0];if(q){this.initResizeMonitor();}else{g.textResizeEvent.unsubscribe(this.onDomResize,this,true);this.resizeMonitor=null;}},_addToParent:function(q,r){if(!this.cfg.getProperty("appendtodocumentbody")&&q===document.body&&q.firstChild){q.insertBefore(r,q.firstChild);}else{q.appendChild(r);}},toString:function(){return"Module "+this.id;}};YAHOO.lang.augmentProto(g,YAHOO.util.EventProvider);}());(function(){YAHOO.widget.Overlay=function(p,o){YAHOO.widget.Overlay.superclass.constructor.call(this,p,o);};var i=YAHOO.lang,m=YAHOO.util.CustomEvent,g=YAHOO.widget.Module,n=YAHOO.util.Event,f=YAHOO.util.Dom,d=YAHOO.util.Config,k=YAHOO.env.ua,b=YAHOO.widget.Overlay,h="subscribe",e="unsubscribe",c="contained",j,a={"BEFORE_MOVE":"beforeMove","MOVE":"move"},l={"X":{key:"x",validator:i.isNumber,suppressEvent:true,supercedes:["iframe"]},"Y":{key:"y",validator:i.isNumber,suppressEvent:true,supercedes:["iframe"]},"XY":{key:"xy",suppressEvent:true,supercedes:["iframe"]},"CONTEXT":{key:"context",suppressEvent:true,supercedes:["iframe"]},"FIXED_CENTER":{key:"fixedcenter",value:false,supercedes:["iframe","visible"]},"WIDTH":{key:"width",suppressEvent:true,supercedes:["context","fixedcenter","iframe"]},"HEIGHT":{key:"height",suppressEvent:true,supercedes:["context","fixedcenter","iframe"]},"AUTO_FILL_HEIGHT":{key:"autofillheight",supercedes:["height"],value:"body"},"ZINDEX":{key:"zindex",value:null},"CONSTRAIN_TO_VIEWPORT":{key:"constraintoviewport",value:false,validator:i.isBoolean,supercedes:["iframe","x","y","xy"]},"IFRAME":{key:"iframe",value:(k.ie==6?true:false),validator:i.isBoolean,supercedes:["zindex"]},"PREVENT_CONTEXT_OVERLAP":{key:"preventcontextoverlap",value:false,validator:i.isBoolean,supercedes:["constraintoviewport"]}};b.IFRAME_SRC="javascript:false;";b.IFRAME_OFFSET=3;b.VIEWPORT_OFFSET=10;b.TOP_LEFT="tl";b.TOP_RIGHT="tr";b.BOTTOM_LEFT="bl";b.BOTTOM_RIGHT="br";b.PREVENT_OVERLAP_X={"tltr":true,"blbr":true,"brbl":true,"trtl":true};b.PREVENT_OVERLAP_Y={"trbr":true,"tlbl":true,"bltl":true,"brtr":true};b.CSS_OVERLAY="yui-overlay";b.CSS_HIDDEN="yui-overlay-hidden";b.CSS_IFRAME="yui-overlay-iframe";b.STD_MOD_RE=/^\s*?(body|footer|header)\s*?$/i;b.windowScrollEvent=new m("windowScroll");b.windowResizeEvent=new m("windowResize");b.windowScrollHandler=function(p){var o=n.getTarget(p);if(!o||o===window||o===window.document){if(k.ie){if(!window.scrollEnd){window.scrollEnd=-1;}clearTimeout(window.scrollEnd);window.scrollEnd=setTimeout(function(){b.windowScrollEvent.fire();},1);}else{b.windowScrollEvent.fire();}}};b.windowResizeHandler=function(o){if(k.ie){if(!window.resizeEnd){window.resizeEnd=-1;}clearTimeout(window.resizeEnd);window.resizeEnd=setTimeout(function(){b.windowResizeEvent.fire();},100);}else{b.windowResizeEvent.fire();}};b._initialized=null;if(b._initialized===null){n.on(window,"scroll",b.windowScrollHandler);n.on(window,"resize",b.windowResizeHandler);b._initialized=true;}b._TRIGGER_MAP={"windowScroll":b.windowScrollEvent,"windowResize":b.windowResizeEvent,"textResize":g.textResizeEvent};YAHOO.extend(b,g,{CONTEXT_TRIGGERS:[],init:function(p,o){b.superclass.init.call(this,p);this.beforeInitEvent.fire(b);f.addClass(this.element,b.CSS_OVERLAY);if(o){this.cfg.applyConfig(o,true);}if(this.platform=="mac"&&k.gecko){if(!d.alreadySubscribed(this.showEvent,this.showMacGeckoScrollbars,this)){this.showEvent.subscribe(this.showMacGeckoScrollbars,this,true);}if(!d.alreadySubscribed(this.hideEvent,this.hideMacGeckoScrollbars,this)){this.hideEvent.subscribe(this.hideMacGeckoScrollbars,this,true);}}this.initEvent.fire(b);},initEvents:function(){b.superclass.initEvents.call(this);var o=m.LIST;this.beforeMoveEvent=this.createEvent(a.BEFORE_MOVE);this.beforeMoveEvent.signature=o;this.moveEvent=this.createEvent(a.MOVE);this.moveEvent.signature=o;},initDefaultConfig:function(){b.superclass.initDefaultConfig.call(this);var o=this.cfg;o.addProperty(l.X.key,{handler:this.configX,validator:l.X.validator,suppressEvent:l.X.suppressEvent,supercedes:l.X.supercedes});o.addProperty(l.Y.key,{handler:this.configY,validator:l.Y.validator,suppressEvent:l.Y.suppressEvent,supercedes:l.Y.supercedes});
-o.addProperty(l.XY.key,{handler:this.configXY,suppressEvent:l.XY.suppressEvent,supercedes:l.XY.supercedes});o.addProperty(l.CONTEXT.key,{handler:this.configContext,suppressEvent:l.CONTEXT.suppressEvent,supercedes:l.CONTEXT.supercedes});o.addProperty(l.FIXED_CENTER.key,{handler:this.configFixedCenter,value:l.FIXED_CENTER.value,validator:l.FIXED_CENTER.validator,supercedes:l.FIXED_CENTER.supercedes});o.addProperty(l.WIDTH.key,{handler:this.configWidth,suppressEvent:l.WIDTH.suppressEvent,supercedes:l.WIDTH.supercedes});o.addProperty(l.HEIGHT.key,{handler:this.configHeight,suppressEvent:l.HEIGHT.suppressEvent,supercedes:l.HEIGHT.supercedes});o.addProperty(l.AUTO_FILL_HEIGHT.key,{handler:this.configAutoFillHeight,value:l.AUTO_FILL_HEIGHT.value,validator:this._validateAutoFill,supercedes:l.AUTO_FILL_HEIGHT.supercedes});o.addProperty(l.ZINDEX.key,{handler:this.configzIndex,value:l.ZINDEX.value});o.addProperty(l.CONSTRAIN_TO_VIEWPORT.key,{handler:this.configConstrainToViewport,value:l.CONSTRAIN_TO_VIEWPORT.value,validator:l.CONSTRAIN_TO_VIEWPORT.validator,supercedes:l.CONSTRAIN_TO_VIEWPORT.supercedes});o.addProperty(l.IFRAME.key,{handler:this.configIframe,value:l.IFRAME.value,validator:l.IFRAME.validator,supercedes:l.IFRAME.supercedes});o.addProperty(l.PREVENT_CONTEXT_OVERLAP.key,{value:l.PREVENT_CONTEXT_OVERLAP.value,validator:l.PREVENT_CONTEXT_OVERLAP.validator,supercedes:l.PREVENT_CONTEXT_OVERLAP.supercedes});},moveTo:function(o,p){this.cfg.setProperty("xy",[o,p]);},hideMacGeckoScrollbars:function(){f.replaceClass(this.element,"show-scrollbars","hide-scrollbars");},showMacGeckoScrollbars:function(){f.replaceClass(this.element,"hide-scrollbars","show-scrollbars");},_setDomVisibility:function(o){f.setStyle(this.element,"visibility",(o)?"visible":"hidden");var p=b.CSS_HIDDEN;if(o){f.removeClass(this.element,p);}else{f.addClass(this.element,p);}},configVisible:function(x,w,t){var p=w[0],B=f.getStyle(this.element,"visibility"),o=this._cachedEffects||this._createEffects(this.cfg.getProperty("effect")),A=(this.platform=="mac"&&k.gecko),y=d.alreadySubscribed,q,v,s,r,u,z;if(B=="inherit"){v=this.element.parentNode;while(v.nodeType!=9&&v.nodeType!=11){B=f.getStyle(v,"visibility");if(B!="inherit"){break;}v=v.parentNode;}if(B=="inherit"){B="visible";}}if(p){if(A){this.showMacGeckoScrollbars();}if(o){if(p){if(B!="visible"||B===""||this._fadingOut){if(this.beforeShowEvent.fire()){z=o.length;for(s=0;s<z;s++){q=o[s];if(s===0&&!y(q.animateInCompleteEvent,this.showEvent.fire,this.showEvent)){q.animateInCompleteEvent.subscribe(this.showEvent.fire,this.showEvent,true);}q.animateIn();}}}}}else{if(B!="visible"||B===""){if(this.beforeShowEvent.fire()){this._setDomVisibility(true);this.cfg.refireEvent("iframe");this.showEvent.fire();}}else{this._setDomVisibility(true);}}}else{if(A){this.hideMacGeckoScrollbars();}if(o){if(B=="visible"||this._fadingIn){if(this.beforeHideEvent.fire()){z=o.length;for(r=0;r<z;r++){u=o[r];if(r===0&&!y(u.animateOutCompleteEvent,this.hideEvent.fire,this.hideEvent)){u.animateOutCompleteEvent.subscribe(this.hideEvent.fire,this.hideEvent,true);}u.animateOut();}}}else{if(B===""){this._setDomVisibility(false);}}}else{if(B=="visible"||B===""){if(this.beforeHideEvent.fire()){this._setDomVisibility(false);this.hideEvent.fire();}}else{this._setDomVisibility(false);}}}},doCenterOnDOMEvent:function(){var o=this.cfg,p=o.getProperty("fixedcenter");if(o.getProperty("visible")){if(p&&(p!==c||this.fitsInViewport())){this.center();}}},fitsInViewport:function(){var s=b.VIEWPORT_OFFSET,q=this.element,t=q.offsetWidth,r=q.offsetHeight,o=f.getViewportWidth(),p=f.getViewportHeight();return((t+s<o)&&(r+s<p));},configFixedCenter:function(s,q,t){var u=q[0],p=d.alreadySubscribed,r=b.windowResizeEvent,o=b.windowScrollEvent;if(u){this.center();if(!p(this.beforeShowEvent,this.center)){this.beforeShowEvent.subscribe(this.center);}if(!p(r,this.doCenterOnDOMEvent,this)){r.subscribe(this.doCenterOnDOMEvent,this,true);}if(!p(o,this.doCenterOnDOMEvent,this)){o.subscribe(this.doCenterOnDOMEvent,this,true);}}else{this.beforeShowEvent.unsubscribe(this.center);r.unsubscribe(this.doCenterOnDOMEvent,this);o.unsubscribe(this.doCenterOnDOMEvent,this);}},configHeight:function(r,p,s){var o=p[0],q=this.element;f.setStyle(q,"height",o);this.cfg.refireEvent("iframe");},configAutoFillHeight:function(t,s,p){var v=s[0],q=this.cfg,u="autofillheight",w="height",r=q.getProperty(u),o=this._autoFillOnHeightChange;q.unsubscribeFromConfigEvent(w,o);g.textResizeEvent.unsubscribe(o);this.changeContentEvent.unsubscribe(o);if(r&&v!==r&&this[r]){f.setStyle(this[r],w,"");}if(v){v=i.trim(v.toLowerCase());q.subscribeToConfigEvent(w,o,this[v],this);g.textResizeEvent.subscribe(o,this[v],this);this.changeContentEvent.subscribe(o,this[v],this);q.setProperty(u,v,true);}},configWidth:function(r,o,s){var q=o[0],p=this.element;f.setStyle(p,"width",q);this.cfg.refireEvent("iframe");},configzIndex:function(q,o,r){var s=o[0],p=this.element;if(!s){s=f.getStyle(p,"zIndex");if(!s||isNaN(s)){s=0;}}if(this.iframe||this.cfg.getProperty("iframe")===true){if(s<=0){s=1;}}f.setStyle(p,"zIndex",s);this.cfg.setProperty("zIndex",s,true);if(this.iframe){this.stackIframe();}},configXY:function(q,p,r){var t=p[0],o=t[0],s=t[1];this.cfg.setProperty("x",o);this.cfg.setProperty("y",s);this.beforeMoveEvent.fire([o,s]);o=this.cfg.getProperty("x");s=this.cfg.getProperty("y");this.cfg.refireEvent("iframe");this.moveEvent.fire([o,s]);},configX:function(q,p,r){var o=p[0],s=this.cfg.getProperty("y");this.cfg.setProperty("x",o,true);this.cfg.setProperty("y",s,true);this.beforeMoveEvent.fire([o,s]);o=this.cfg.getProperty("x");s=this.cfg.getProperty("y");f.setX(this.element,o,true);this.cfg.setProperty("xy",[o,s],true);this.cfg.refireEvent("iframe");this.moveEvent.fire([o,s]);},configY:function(q,p,r){var o=this.cfg.getProperty("x"),s=p[0];this.cfg.setProperty("x",o,true);this.cfg.setProperty("y",s,true);this.beforeMoveEvent.fire([o,s]);o=this.cfg.getProperty("x");s=this.cfg.getProperty("y");f.setY(this.element,s,true);
-this.cfg.setProperty("xy",[o,s],true);this.cfg.refireEvent("iframe");this.moveEvent.fire([o,s]);},showIframe:function(){var p=this.iframe,o;if(p){o=this.element.parentNode;if(o!=p.parentNode){this._addToParent(o,p);}p.style.display="block";}},hideIframe:function(){if(this.iframe){this.iframe.style.display="none";}},syncIframe:function(){var o=this.iframe,q=this.element,s=b.IFRAME_OFFSET,p=(s*2),r;if(o){o.style.width=(q.offsetWidth+p+"px");o.style.height=(q.offsetHeight+p+"px");r=this.cfg.getProperty("xy");if(!i.isArray(r)||(isNaN(r[0])||isNaN(r[1]))){this.syncPosition();r=this.cfg.getProperty("xy");}f.setXY(o,[(r[0]-s),(r[1]-s)]);}},stackIframe:function(){if(this.iframe){var o=f.getStyle(this.element,"zIndex");if(!YAHOO.lang.isUndefined(o)&&!isNaN(o)){f.setStyle(this.iframe,"zIndex",(o-1));}}},configIframe:function(r,q,s){var o=q[0];function t(){var v=this.iframe,w=this.element,x;if(!v){if(!j){j=document.createElement("iframe");if(this.isSecure){j.src=b.IFRAME_SRC;}if(k.ie){j.style.filter="alpha(opacity=0)";j.frameBorder=0;}else{j.style.opacity="0";}j.style.position="absolute";j.style.border="none";j.style.margin="0";j.style.padding="0";j.style.display="none";j.tabIndex=-1;j.className=b.CSS_IFRAME;}v=j.cloneNode(false);v.id=this.id+"_f";x=w.parentNode;var u=x||document.body;this._addToParent(u,v);this.iframe=v;}this.showIframe();this.syncIframe();this.stackIframe();if(!this._hasIframeEventListeners){this.showEvent.subscribe(this.showIframe);this.hideEvent.subscribe(this.hideIframe);this.changeContentEvent.subscribe(this.syncIframe);this._hasIframeEventListeners=true;}}function p(){t.call(this);this.beforeShowEvent.unsubscribe(p);this._iframeDeferred=false;}if(o){if(this.cfg.getProperty("visible")){t.call(this);}else{if(!this._iframeDeferred){this.beforeShowEvent.subscribe(p);this._iframeDeferred=true;}}}else{this.hideIframe();if(this._hasIframeEventListeners){this.showEvent.unsubscribe(this.showIframe);this.hideEvent.unsubscribe(this.hideIframe);this.changeContentEvent.unsubscribe(this.syncIframe);this._hasIframeEventListeners=false;}}},_primeXYFromDOM:function(){if(YAHOO.lang.isUndefined(this.cfg.getProperty("xy"))){this.syncPosition();this.cfg.refireEvent("xy");this.beforeShowEvent.unsubscribe(this._primeXYFromDOM);}},configConstrainToViewport:function(p,o,q){var r=o[0];if(r){if(!d.alreadySubscribed(this.beforeMoveEvent,this.enforceConstraints,this)){this.beforeMoveEvent.subscribe(this.enforceConstraints,this,true);}if(!d.alreadySubscribed(this.beforeShowEvent,this._primeXYFromDOM)){this.beforeShowEvent.subscribe(this._primeXYFromDOM);}}else{this.beforeShowEvent.unsubscribe(this._primeXYFromDOM);this.beforeMoveEvent.unsubscribe(this.enforceConstraints,this);}},configContext:function(u,t,q){var x=t[0],r,o,v,s,p,w=this.CONTEXT_TRIGGERS;if(x){r=x[0];o=x[1];v=x[2];s=x[3];p=x[4];if(w&&w.length>0){s=(s||[]).concat(w);}if(r){if(typeof r=="string"){this.cfg.setProperty("context",[document.getElementById(r),o,v,s,p],true);}if(o&&v){this.align(o,v,p);}if(this._contextTriggers){this._processTriggers(this._contextTriggers,e,this._alignOnTrigger);}if(s){this._processTriggers(s,h,this._alignOnTrigger);this._contextTriggers=s;}}}},_alignOnTrigger:function(p,o){this.align();},_findTriggerCE:function(o){var p=null;if(o instanceof m){p=o;}else{if(b._TRIGGER_MAP[o]){p=b._TRIGGER_MAP[o];}}return p;},_processTriggers:function(s,v,r){var q,u;for(var p=0,o=s.length;p<o;++p){q=s[p];u=this._findTriggerCE(q);if(u){u[v](r,this,true);}else{this[v](q,r);}}},align:function(p,w,s){var v=this.cfg.getProperty("context"),t=this,o,q,u;function r(z,A){var y=null,x=null;switch(p){case b.TOP_LEFT:y=A;x=z;break;case b.TOP_RIGHT:y=A-q.offsetWidth;x=z;break;case b.BOTTOM_LEFT:y=A;x=z-q.offsetHeight;break;case b.BOTTOM_RIGHT:y=A-q.offsetWidth;x=z-q.offsetHeight;break;}if(y!==null&&x!==null){if(s){y+=s[0];x+=s[1];}t.moveTo(y,x);}}if(v){o=v[0];q=this.element;t=this;if(!p){p=v[1];}if(!w){w=v[2];}if(!s&&v[4]){s=v[4];}if(q&&o){u=f.getRegion(o);switch(w){case b.TOP_LEFT:r(u.top,u.left);break;case b.TOP_RIGHT:r(u.top,u.right);break;case b.BOTTOM_LEFT:r(u.bottom,u.left);break;case b.BOTTOM_RIGHT:r(u.bottom,u.right);break;}}}},enforceConstraints:function(p,o,q){var s=o[0];var r=this.getConstrainedXY(s[0],s[1]);this.cfg.setProperty("x",r[0],true);this.cfg.setProperty("y",r[1],true);this.cfg.setProperty("xy",r,true);},_getConstrainedPos:function(y,p){var t=this.element,r=b.VIEWPORT_OFFSET,A=(y=="x"),z=(A)?t.offsetWidth:t.offsetHeight,s=(A)?f.getViewportWidth():f.getViewportHeight(),D=(A)?f.getDocumentScrollLeft():f.getDocumentScrollTop(),C=(A)?b.PREVENT_OVERLAP_X:b.PREVENT_OVERLAP_Y,o=this.cfg.getProperty("context"),u=(z+r<s),w=this.cfg.getProperty("preventcontextoverlap")&&o&&C[(o[1]+o[2])],v=D+r,B=D+s-z-r,q=p;if(p<v||p>B){if(w){q=this._preventOverlap(y,o[0],z,s,D);}else{if(u){if(p<v){q=v;}else{if(p>B){q=B;}}}else{q=v;}}}return q;},_preventOverlap:function(y,w,z,u,C){var A=(y=="x"),t=b.VIEWPORT_OFFSET,s=this,q=((A)?f.getX(w):f.getY(w))-C,o=(A)?w.offsetWidth:w.offsetHeight,p=q-t,r=(u-(q+o))-t,D=false,v=function(){var x;if((s.cfg.getProperty(y)-C)>q){x=(q-z);}else{x=(q+o);}s.cfg.setProperty(y,(x+C),true);return x;},B=function(){var E=((s.cfg.getProperty(y)-C)>q)?r:p,x;if(z>E){if(D){v();}else{v();D=true;x=B();}}return x;};B();return this.cfg.getProperty(y);},getConstrainedX:function(o){return this._getConstrainedPos("x",o);},getConstrainedY:function(o){return this._getConstrainedPos("y",o);},getConstrainedXY:function(o,p){return[this.getConstrainedX(o),this.getConstrainedY(p)];},center:function(){var r=b.VIEWPORT_OFFSET,s=this.element.offsetWidth,q=this.element.offsetHeight,p=f.getViewportWidth(),t=f.getViewportHeight(),o,u;if(s<p){o=(p/2)-(s/2)+f.getDocumentScrollLeft();}else{o=r+f.getDocumentScrollLeft();}if(q<t){u=(t/2)-(q/2)+f.getDocumentScrollTop();}else{u=r+f.getDocumentScrollTop();}this.cfg.setProperty("xy",[parseInt(o,10),parseInt(u,10)]);this.cfg.refireEvent("iframe");if(k.webkit){this.forceContainerRedraw();}},syncPosition:function(){var o=f.getXY(this.element);
-this.cfg.setProperty("x",o[0],true);this.cfg.setProperty("y",o[1],true);this.cfg.setProperty("xy",o,true);},onDomResize:function(q,p){var o=this;b.superclass.onDomResize.call(this,q,p);setTimeout(function(){o.syncPosition();o.cfg.refireEvent("iframe");o.cfg.refireEvent("context");},0);},_getComputedHeight:(function(){if(document.defaultView&&document.defaultView.getComputedStyle){return function(p){var o=null;if(p.ownerDocument&&p.ownerDocument.defaultView){var q=p.ownerDocument.defaultView.getComputedStyle(p,"");if(q){o=parseInt(q.height,10);}}return(i.isNumber(o))?o:null;};}else{return function(p){var o=null;if(p.style.pixelHeight){o=p.style.pixelHeight;}return(i.isNumber(o))?o:null;};}})(),_validateAutoFillHeight:function(o){return(!o)||(i.isString(o)&&b.STD_MOD_RE.test(o));},_autoFillOnHeightChange:function(r,p,q){var o=this.cfg.getProperty("height");if((o&&o!=="auto")||(o===0)){this.fillHeight(q);}},_getPreciseHeight:function(p){var o=p.offsetHeight;if(p.getBoundingClientRect){var q=p.getBoundingClientRect();o=q.bottom-q.top;}return o;},fillHeight:function(r){if(r){var p=this.innerElement||this.element,o=[this.header,this.body,this.footer],v,w=0,x=0,t=0,q=false;for(var u=0,s=o.length;u<s;u++){v=o[u];if(v){if(r!==v){x+=this._getPreciseHeight(v);}else{q=true;}}}if(q){if(k.ie||k.opera){f.setStyle(r,"height",0+"px");}w=this._getComputedHeight(p);if(w===null){f.addClass(p,"yui-override-padding");w=p.clientHeight;f.removeClass(p,"yui-override-padding");}t=Math.max(w-x,0);f.setStyle(r,"height",t+"px");if(r.offsetHeight!=t){t=Math.max(t-(r.offsetHeight-t),0);}f.setStyle(r,"height",t+"px");}}},bringToTop:function(){var s=[],r=this.element;function v(z,y){var B=f.getStyle(z,"zIndex"),A=f.getStyle(y,"zIndex"),x=(!B||isNaN(B))?0:parseInt(B,10),w=(!A||isNaN(A))?0:parseInt(A,10);if(x>w){return -1;}else{if(x<w){return 1;}else{return 0;}}}function q(y){var x=f.hasClass(y,b.CSS_OVERLAY),w=YAHOO.widget.Panel;if(x&&!f.isAncestor(r,y)){if(w&&f.hasClass(y,w.CSS_PANEL)){s[s.length]=y.parentNode;}else{s[s.length]=y;}}}f.getElementsBy(q,"div",document.body);s.sort(v);var o=s[0],u;if(o){u=f.getStyle(o,"zIndex");if(!isNaN(u)){var t=false;if(o!=r){t=true;}else{if(s.length>1){var p=f.getStyle(s[1],"zIndex");if(!isNaN(p)&&(u==p)){t=true;}}}if(t){this.cfg.setProperty("zindex",(parseInt(u,10)+2));}}}},destroy:function(o){if(this.iframe){this.iframe.parentNode.removeChild(this.iframe);}this.iframe=null;b.windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent,this);b.windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent,this);g.textResizeEvent.unsubscribe(this._autoFillOnHeightChange);if(this._contextTriggers){this._processTriggers(this._contextTriggers,e,this._alignOnTrigger);}b.superclass.destroy.call(this,o);},forceContainerRedraw:function(){var o=this;f.addClass(o.element,"yui-force-redraw");setTimeout(function(){f.removeClass(o.element,"yui-force-redraw");},0);},toString:function(){return"Overlay "+this.id;}});}());(function(){YAHOO.widget.OverlayManager=function(g){this.init(g);};var d=YAHOO.widget.Overlay,c=YAHOO.util.Event,e=YAHOO.util.Dom,b=YAHOO.util.Config,f=YAHOO.util.CustomEvent,a=YAHOO.widget.OverlayManager;a.CSS_FOCUSED="focused";a.prototype={constructor:a,overlays:null,initDefaultConfig:function(){this.cfg.addProperty("overlays",{suppressEvent:true});this.cfg.addProperty("focusevent",{value:"mousedown"});},init:function(i){this.cfg=new b(this);this.initDefaultConfig();if(i){this.cfg.applyConfig(i,true);}this.cfg.fireQueue();var h=null;this.getActive=function(){return h;};this.focus=function(j){var k=this.find(j);if(k){k.focus();}};this.remove=function(k){var m=this.find(k),j;if(m){if(h==m){h=null;}var l=(m.element===null&&m.cfg===null)?true:false;if(!l){j=e.getStyle(m.element,"zIndex");m.cfg.setProperty("zIndex",-1000,true);}this.overlays.sort(this.compareZIndexDesc);this.overlays=this.overlays.slice(0,(this.overlays.length-1));m.hideEvent.unsubscribe(m.blur);m.destroyEvent.unsubscribe(this._onOverlayDestroy,m);m.focusEvent.unsubscribe(this._onOverlayFocusHandler,m);m.blurEvent.unsubscribe(this._onOverlayBlurHandler,m);if(!l){c.removeListener(m.element,this.cfg.getProperty("focusevent"),this._onOverlayElementFocus);m.cfg.setProperty("zIndex",j,true);m.cfg.setProperty("manager",null);}if(m.focusEvent._managed){m.focusEvent=null;}if(m.blurEvent._managed){m.blurEvent=null;}if(m.focus._managed){m.focus=null;}if(m.blur._managed){m.blur=null;}}};this.blurAll=function(){var k=this.overlays.length,j;if(k>0){j=k-1;do{this.overlays[j].blur();}while(j--);}};this._manageBlur=function(j){var k=false;if(h==j){e.removeClass(h.element,a.CSS_FOCUSED);h=null;k=true;}return k;};this._manageFocus=function(j){var k=false;if(h!=j){if(h){h.blur();}h=j;this.bringToTop(h);e.addClass(h.element,a.CSS_FOCUSED);k=true;}return k;};var g=this.cfg.getProperty("overlays");if(!this.overlays){this.overlays=[];}if(g){this.register(g);this.overlays.sort(this.compareZIndexDesc);}},_onOverlayElementFocus:function(i){var g=c.getTarget(i),h=this.close;if(h&&(g==h||e.isAncestor(h,g))){this.blur();}else{this.focus();}},_onOverlayDestroy:function(h,g,i){this.remove(i);},_onOverlayFocusHandler:function(h,g,i){this._manageFocus(i);},_onOverlayBlurHandler:function(h,g,i){this._manageBlur(i);},_bindFocus:function(g){var h=this;if(!g.focusEvent){g.focusEvent=g.createEvent("focus");g.focusEvent.signature=f.LIST;g.focusEvent._managed=true;}else{g.focusEvent.subscribe(h._onOverlayFocusHandler,g,h);}if(!g.focus){c.on(g.element,h.cfg.getProperty("focusevent"),h._onOverlayElementFocus,null,g);g.focus=function(){if(h._manageFocus(this)){if(this.cfg.getProperty("visible")&&this.focusFirst){this.focusFirst();}this.focusEvent.fire();}};g.focus._managed=true;}},_bindBlur:function(g){var h=this;if(!g.blurEvent){g.blurEvent=g.createEvent("blur");g.blurEvent.signature=f.LIST;g.focusEvent._managed=true;}else{g.blurEvent.subscribe(h._onOverlayBlurHandler,g,h);}if(!g.blur){g.blur=function(){if(h._manageBlur(this)){this.blurEvent.fire();}};g.blur._managed=true;}g.hideEvent.subscribe(g.blur);
-},_bindDestroy:function(g){var h=this;g.destroyEvent.subscribe(h._onOverlayDestroy,g,h);},_syncZIndex:function(g){var h=e.getStyle(g.element,"zIndex");if(!isNaN(h)){g.cfg.setProperty("zIndex",parseInt(h,10));}else{g.cfg.setProperty("zIndex",0);}},register:function(g){var k=false,h,j;if(g instanceof d){g.cfg.addProperty("manager",{value:this});this._bindFocus(g);this._bindBlur(g);this._bindDestroy(g);this._syncZIndex(g);this.overlays.push(g);this.bringToTop(g);k=true;}else{if(g instanceof Array){for(h=0,j=g.length;h<j;h++){k=this.register(g[h])||k;}}}return k;},bringToTop:function(m){var i=this.find(m),l,g,j;if(i){j=this.overlays;j.sort(this.compareZIndexDesc);g=j[0];if(g){l=e.getStyle(g.element,"zIndex");if(!isNaN(l)){var k=false;if(g!==i){k=true;}else{if(j.length>1){var h=e.getStyle(j[1].element,"zIndex");if(!isNaN(h)&&(l==h)){k=true;}}}if(k){i.cfg.setProperty("zindex",(parseInt(l,10)+2));}}j.sort(this.compareZIndexDesc);}}},find:function(g){var l=g instanceof d,j=this.overlays,p=j.length,k=null,m,h;if(l||typeof g=="string"){for(h=p-1;h>=0;h--){m=j[h];if((l&&(m===g))||(m.id==g)){k=m;break;}}}return k;},compareZIndexDesc:function(j,i){var h=(j.cfg)?j.cfg.getProperty("zIndex"):null,g=(i.cfg)?i.cfg.getProperty("zIndex"):null;if(h===null&&g===null){return 0;}else{if(h===null){return 1;}else{if(g===null){return -1;}else{if(h>g){return -1;}else{if(h<g){return 1;}else{return 0;}}}}}},showAll:function(){var h=this.overlays,j=h.length,g;for(g=j-1;g>=0;g--){h[g].show();}},hideAll:function(){var h=this.overlays,j=h.length,g;for(g=j-1;g>=0;g--){h[g].hide();}},toString:function(){return"OverlayManager";}};}());(function(){YAHOO.widget.Tooltip=function(p,o){YAHOO.widget.Tooltip.superclass.constructor.call(this,p,o);};var e=YAHOO.lang,n=YAHOO.util.Event,m=YAHOO.util.CustomEvent,c=YAHOO.util.Dom,j=YAHOO.widget.Tooltip,h=YAHOO.env.ua,g=(h.ie&&(h.ie<=6||document.compatMode=="BackCompat")),f,i={"PREVENT_OVERLAP":{key:"preventoverlap",value:true,validator:e.isBoolean,supercedes:["x","y","xy"]},"SHOW_DELAY":{key:"showdelay",value:200,validator:e.isNumber},"AUTO_DISMISS_DELAY":{key:"autodismissdelay",value:5000,validator:e.isNumber},"HIDE_DELAY":{key:"hidedelay",value:250,validator:e.isNumber},"TEXT":{key:"text",suppressEvent:true},"CONTAINER":{key:"container"},"DISABLED":{key:"disabled",value:false,suppressEvent:true},"XY_OFFSET":{key:"xyoffset",value:[0,25],suppressEvent:true}},a={"CONTEXT_MOUSE_OVER":"contextMouseOver","CONTEXT_MOUSE_OUT":"contextMouseOut","CONTEXT_TRIGGER":"contextTrigger"};j.CSS_TOOLTIP="yui-tt";function k(q,o){var p=this.cfg,r=p.getProperty("width");if(r==o){p.setProperty("width",q);}}function d(p,o){if("_originalWidth" in this){k.call(this,this._originalWidth,this._forcedWidth);}var q=document.body,u=this.cfg,t=u.getProperty("width"),r,s;if((!t||t=="auto")&&(u.getProperty("container")!=q||u.getProperty("x")>=c.getViewportWidth()||u.getProperty("y")>=c.getViewportHeight())){s=this.element.cloneNode(true);s.style.visibility="hidden";s.style.top="0px";s.style.left="0px";q.appendChild(s);r=(s.offsetWidth+"px");q.removeChild(s);s=null;u.setProperty("width",r);u.refireEvent("xy");this._originalWidth=t||"";this._forcedWidth=r;}}function b(p,o,q){this.render(q);}function l(){n.onDOMReady(b,this.cfg.getProperty("container"),this);}YAHOO.extend(j,YAHOO.widget.Overlay,{init:function(p,o){j.superclass.init.call(this,p);this.beforeInitEvent.fire(j);c.addClass(this.element,j.CSS_TOOLTIP);if(o){this.cfg.applyConfig(o,true);}this.cfg.queueProperty("visible",false);this.cfg.queueProperty("constraintoviewport",true);this.setBody("");this.subscribe("changeContent",d);this.subscribe("init",l);this.subscribe("render",this.onRender);this.initEvent.fire(j);},initEvents:function(){j.superclass.initEvents.call(this);var o=m.LIST;this.contextMouseOverEvent=this.createEvent(a.CONTEXT_MOUSE_OVER);this.contextMouseOverEvent.signature=o;this.contextMouseOutEvent=this.createEvent(a.CONTEXT_MOUSE_OUT);this.contextMouseOutEvent.signature=o;this.contextTriggerEvent=this.createEvent(a.CONTEXT_TRIGGER);this.contextTriggerEvent.signature=o;},initDefaultConfig:function(){j.superclass.initDefaultConfig.call(this);this.cfg.addProperty(i.PREVENT_OVERLAP.key,{value:i.PREVENT_OVERLAP.value,validator:i.PREVENT_OVERLAP.validator,supercedes:i.PREVENT_OVERLAP.supercedes});this.cfg.addProperty(i.SHOW_DELAY.key,{handler:this.configShowDelay,value:200,validator:i.SHOW_DELAY.validator});this.cfg.addProperty(i.AUTO_DISMISS_DELAY.key,{handler:this.configAutoDismissDelay,value:i.AUTO_DISMISS_DELAY.value,validator:i.AUTO_DISMISS_DELAY.validator});this.cfg.addProperty(i.HIDE_DELAY.key,{handler:this.configHideDelay,value:i.HIDE_DELAY.value,validator:i.HIDE_DELAY.validator});this.cfg.addProperty(i.TEXT.key,{handler:this.configText,suppressEvent:i.TEXT.suppressEvent});this.cfg.addProperty(i.CONTAINER.key,{handler:this.configContainer,value:document.body});this.cfg.addProperty(i.DISABLED.key,{handler:this.configContainer,value:i.DISABLED.value,supressEvent:i.DISABLED.suppressEvent});this.cfg.addProperty(i.XY_OFFSET.key,{value:i.XY_OFFSET.value.concat(),supressEvent:i.XY_OFFSET.suppressEvent});},configText:function(p,o,q){var r=o[0];if(r){this.setBody(r);}},configContainer:function(q,p,r){var o=p[0];if(typeof o=="string"){this.cfg.setProperty("container",document.getElementById(o),true);}},_removeEventListeners:function(){var r=this._context,o,q,p;if(r){o=r.length;if(o>0){p=o-1;do{q=r[p];n.removeListener(q,"mouseover",this.onContextMouseOver);n.removeListener(q,"mousemove",this.onContextMouseMove);n.removeListener(q,"mouseout",this.onContextMouseOut);}while(p--);}}},configContext:function(t,p,u){var s=p[0],v,o,r,q;if(s){if(!(s instanceof Array)){if(typeof s=="string"){this.cfg.setProperty("context",[document.getElementById(s)],true);}else{this.cfg.setProperty("context",[s],true);}s=this.cfg.getProperty("context");}this._removeEventListeners();this._context=s;v=this._context;if(v){o=v.length;if(o>0){q=o-1;do{r=v[q];n.on(r,"mouseover",this.onContextMouseOver,this);
-n.on(r,"mousemove",this.onContextMouseMove,this);n.on(r,"mouseout",this.onContextMouseOut,this);}while(q--);}}}},onContextMouseMove:function(p,o){o.pageX=n.getPageX(p);o.pageY=n.getPageY(p);},onContextMouseOver:function(q,p){var o=this;if(o.title){p._tempTitle=o.title;o.title="";}if(p.fireEvent("contextMouseOver",o,q)!==false&&!p.cfg.getProperty("disabled")){if(p.hideProcId){clearTimeout(p.hideProcId);p.hideProcId=null;}n.on(o,"mousemove",p.onContextMouseMove,p);p.showProcId=p.doShow(q,o);}},onContextMouseOut:function(q,p){var o=this;if(p._tempTitle){o.title=p._tempTitle;p._tempTitle=null;}if(p.showProcId){clearTimeout(p.showProcId);p.showProcId=null;}if(p.hideProcId){clearTimeout(p.hideProcId);p.hideProcId=null;}p.fireEvent("contextMouseOut",o,q);p.hideProcId=setTimeout(function(){p.hide();},p.cfg.getProperty("hidedelay"));},doShow:function(r,o){var t=this.cfg.getProperty("xyoffset"),p=t[0],s=t[1],q=this;if(h.opera&&o.tagName&&o.tagName.toUpperCase()=="A"){s+=12;}return setTimeout(function(){var u=q.cfg.getProperty("text");if(q._tempTitle&&(u===""||YAHOO.lang.isUndefined(u)||YAHOO.lang.isNull(u))){q.setBody(q._tempTitle);}else{q.cfg.refireEvent("text");}q.moveTo(q.pageX+p,q.pageY+s);if(q.cfg.getProperty("preventoverlap")){q.preventOverlap(q.pageX,q.pageY);}n.removeListener(o,"mousemove",q.onContextMouseMove);q.contextTriggerEvent.fire(o);q.show();q.hideProcId=q.doHide();},this.cfg.getProperty("showdelay"));},doHide:function(){var o=this;return setTimeout(function(){o.hide();},this.cfg.getProperty("autodismissdelay"));},preventOverlap:function(s,r){var o=this.element.offsetHeight,q=new YAHOO.util.Point(s,r),p=c.getRegion(this.element);p.top-=5;p.left-=5;p.right+=5;p.bottom+=5;if(p.contains(q)){this.cfg.setProperty("y",(r-o-5));}},onRender:function(s,r){function t(){var w=this.element,v=this.underlay;if(v){v.style.width=(w.offsetWidth+6)+"px";v.style.height=(w.offsetHeight+1)+"px";}}function p(){c.addClass(this.underlay,"yui-tt-shadow-visible");if(h.ie){this.forceUnderlayRedraw();}}function o(){c.removeClass(this.underlay,"yui-tt-shadow-visible");}function u(){var x=this.underlay,w,v,z,y;if(!x){w=this.element;v=YAHOO.widget.Module;z=h.ie;y=this;if(!f){f=document.createElement("div");f.className="yui-tt-shadow";}x=f.cloneNode(false);w.appendChild(x);this.underlay=x;this._shadow=this.underlay;p.call(this);this.subscribe("beforeShow",p);this.subscribe("hide",o);if(g){window.setTimeout(function(){t.call(y);},0);this.cfg.subscribeToConfigEvent("width",t);this.cfg.subscribeToConfigEvent("height",t);this.subscribe("changeContent",t);v.textResizeEvent.subscribe(t,this,true);this.subscribe("destroy",function(){v.textResizeEvent.unsubscribe(t,this);});}}}function q(){u.call(this);this.unsubscribe("beforeShow",q);}if(this.cfg.getProperty("visible")){u.call(this);}else{this.subscribe("beforeShow",q);}},forceUnderlayRedraw:function(){var o=this;c.addClass(o.underlay,"yui-force-redraw");setTimeout(function(){c.removeClass(o.underlay,"yui-force-redraw");},0);},destroy:function(){this._removeEventListeners();j.superclass.destroy.call(this);},toString:function(){return"Tooltip "+this.id;}});}());(function(){YAHOO.widget.Panel=function(v,u){YAHOO.widget.Panel.superclass.constructor.call(this,v,u);};var s=null;var e=YAHOO.lang,f=YAHOO.util,a=f.Dom,t=f.Event,m=f.CustomEvent,k=YAHOO.util.KeyListener,i=f.Config,h=YAHOO.widget.Overlay,o=YAHOO.widget.Panel,l=YAHOO.env.ua,p=(l.ie&&(l.ie<=6||document.compatMode=="BackCompat")),g,q,c,d={"BEFORE_SHOW_MASK":"beforeShowMask","BEFORE_HIDE_MASK":"beforeHideMask","SHOW_MASK":"showMask","HIDE_MASK":"hideMask","DRAG":"drag"},n={"CLOSE":{key:"close",value:true,validator:e.isBoolean,supercedes:["visible"]},"DRAGGABLE":{key:"draggable",value:(f.DD?true:false),validator:e.isBoolean,supercedes:["visible"]},"DRAG_ONLY":{key:"dragonly",value:false,validator:e.isBoolean,supercedes:["draggable"]},"UNDERLAY":{key:"underlay",value:"shadow",supercedes:["visible"]},"MODAL":{key:"modal",value:false,validator:e.isBoolean,supercedes:["visible","zindex"]},"KEY_LISTENERS":{key:"keylisteners",suppressEvent:true,supercedes:["visible"]},"STRINGS":{key:"strings",supercedes:["close"],validator:e.isObject,value:{close:"Close"}}};o.CSS_PANEL="yui-panel";o.CSS_PANEL_CONTAINER="yui-panel-container";o.FOCUSABLE=["a","button","select","textarea","input","iframe"];function j(v,u){if(!this.header&&this.cfg.getProperty("draggable")){this.setHeader("&#160;");}}function r(v,u,w){var z=w[0],x=w[1],y=this.cfg,A=y.getProperty("width");if(A==x){y.setProperty("width",z);}this.unsubscribe("hide",r,w);}function b(v,u){var y,x,w;if(p){y=this.cfg;x=y.getProperty("width");if(!x||x=="auto"){w=(this.element.offsetWidth+"px");y.setProperty("width",w);this.subscribe("hide",r,[(x||""),w]);}}}YAHOO.extend(o,h,{init:function(v,u){o.superclass.init.call(this,v);this.beforeInitEvent.fire(o);a.addClass(this.element,o.CSS_PANEL);this.buildWrapper();if(u){this.cfg.applyConfig(u,true);}this.subscribe("showMask",this._addFocusHandlers);this.subscribe("hideMask",this._removeFocusHandlers);this.subscribe("beforeRender",j);this.subscribe("render",function(){this.setFirstLastFocusable();this.subscribe("changeContent",this.setFirstLastFocusable);});this.subscribe("show",this._focusOnShow);this.initEvent.fire(o);},_onElementFocus:function(z){if(s===this){var y=t.getTarget(z),x=document.documentElement,v=(y!==x&&y!==window);if(v&&y!==this.element&&y!==this.mask&&!a.isAncestor(this.element,y)){try{this._focusFirstModal();}catch(w){try{if(v&&y!==document.body){y.blur();}}catch(u){}}}}},_focusFirstModal:function(){var u=this.firstElement;if(u){u.focus();}else{if(this._modalFocus){this._modalFocus.focus();}else{this.innerElement.focus();}}},_addFocusHandlers:function(v,u){if(!this.firstElement){if(l.webkit||l.opera){if(!this._modalFocus){this._createHiddenFocusElement();}}else{this.innerElement.tabIndex=0;}}this._setTabLoop(this.firstElement,this.lastElement);t.onFocus(document.documentElement,this._onElementFocus,this,true);s=this;},_createHiddenFocusElement:function(){var u=document.createElement("button");
-u.style.height="1px";u.style.width="1px";u.style.position="absolute";u.style.left="-10000em";u.style.opacity=0;u.tabIndex=-1;this.innerElement.appendChild(u);this._modalFocus=u;},_removeFocusHandlers:function(v,u){t.removeFocusListener(document.documentElement,this._onElementFocus,this);if(s==this){s=null;}},_focusOnShow:function(v,u,w){if(u&&u[1]){t.stopEvent(u[1]);}if(!this.focusFirst(v,u,w)){if(this.cfg.getProperty("modal")){this._focusFirstModal();}}},focusFirst:function(w,u,z){var v=this.firstElement,y=false;if(u&&u[1]){t.stopEvent(u[1]);}if(v){try{v.focus();y=true;}catch(x){}}return y;},focusLast:function(w,u,z){var v=this.lastElement,y=false;if(u&&u[1]){t.stopEvent(u[1]);}if(v){try{v.focus();y=true;}catch(x){}}return y;},_setTabLoop:function(u,v){this.setTabLoop(u,v);},setTabLoop:function(x,z){var v=this.preventBackTab,w=this.preventTabOut,u=this.showEvent,y=this.hideEvent;if(v){v.disable();u.unsubscribe(v.enable,v);y.unsubscribe(v.disable,v);v=this.preventBackTab=null;}if(w){w.disable();u.unsubscribe(w.enable,w);y.unsubscribe(w.disable,w);w=this.preventTabOut=null;}if(x){this.preventBackTab=new k(x,{shift:true,keys:9},{fn:this.focusLast,scope:this,correctScope:true});v=this.preventBackTab;u.subscribe(v.enable,v,true);y.subscribe(v.disable,v,true);}if(z){this.preventTabOut=new k(z,{shift:false,keys:9},{fn:this.focusFirst,scope:this,correctScope:true});w=this.preventTabOut;u.subscribe(w.enable,w,true);y.subscribe(w.disable,w,true);}},getFocusableElements:function(v){v=v||this.innerElement;var x={},u=this;for(var w=0;w<o.FOCUSABLE.length;w++){x[o.FOCUSABLE[w]]=true;}return a.getElementsBy(function(y){return u._testIfFocusable(y,x);},null,v);},_testIfFocusable:function(u,v){if(u.focus&&u.type!=="hidden"&&!u.disabled&&v[u.tagName.toLowerCase()]){return true;}return false;},setFirstLastFocusable:function(){this.firstElement=null;this.lastElement=null;var u=this.getFocusableElements();this.focusableElements=u;if(u.length>0){this.firstElement=u[0];this.lastElement=u[u.length-1];}if(this.cfg.getProperty("modal")){this._setTabLoop(this.firstElement,this.lastElement);}},initEvents:function(){o.superclass.initEvents.call(this);var u=m.LIST;this.showMaskEvent=this.createEvent(d.SHOW_MASK);this.showMaskEvent.signature=u;this.beforeShowMaskEvent=this.createEvent(d.BEFORE_SHOW_MASK);this.beforeShowMaskEvent.signature=u;this.hideMaskEvent=this.createEvent(d.HIDE_MASK);this.hideMaskEvent.signature=u;this.beforeHideMaskEvent=this.createEvent(d.BEFORE_HIDE_MASK);this.beforeHideMaskEvent.signature=u;this.dragEvent=this.createEvent(d.DRAG);this.dragEvent.signature=u;},initDefaultConfig:function(){o.superclass.initDefaultConfig.call(this);this.cfg.addProperty(n.CLOSE.key,{handler:this.configClose,value:n.CLOSE.value,validator:n.CLOSE.validator,supercedes:n.CLOSE.supercedes});this.cfg.addProperty(n.DRAGGABLE.key,{handler:this.configDraggable,value:(f.DD)?true:false,validator:n.DRAGGABLE.validator,supercedes:n.DRAGGABLE.supercedes});this.cfg.addProperty(n.DRAG_ONLY.key,{value:n.DRAG_ONLY.value,validator:n.DRAG_ONLY.validator,supercedes:n.DRAG_ONLY.supercedes});this.cfg.addProperty(n.UNDERLAY.key,{handler:this.configUnderlay,value:n.UNDERLAY.value,supercedes:n.UNDERLAY.supercedes});this.cfg.addProperty(n.MODAL.key,{handler:this.configModal,value:n.MODAL.value,validator:n.MODAL.validator,supercedes:n.MODAL.supercedes});this.cfg.addProperty(n.KEY_LISTENERS.key,{handler:this.configKeyListeners,suppressEvent:n.KEY_LISTENERS.suppressEvent,supercedes:n.KEY_LISTENERS.supercedes});this.cfg.addProperty(n.STRINGS.key,{value:n.STRINGS.value,handler:this.configStrings,validator:n.STRINGS.validator,supercedes:n.STRINGS.supercedes});},configClose:function(y,v,z){var A=v[0],x=this.close,u=this.cfg.getProperty("strings"),w;if(A){if(!x){if(!c){c=document.createElement("a");c.className="container-close";c.href="#";}x=c.cloneNode(true);w=this.innerElement.firstChild;if(w){this.innerElement.insertBefore(x,w);}else{this.innerElement.appendChild(x);}x.innerHTML=(u&&u.close)?u.close:"&#160;";t.on(x,"click",this._doClose,this,true);this.close=x;}else{x.style.display="block";}}else{if(x){x.style.display="none";}}},_doClose:function(u){t.preventDefault(u);this.hide();},configDraggable:function(v,u,w){var x=u[0];if(x){if(!f.DD){this.cfg.setProperty("draggable",false);return;}if(this.header){a.setStyle(this.header,"cursor","move");this.registerDragDrop();}this.subscribe("beforeShow",b);}else{if(this.dd){this.dd.unreg();}if(this.header){a.setStyle(this.header,"cursor","auto");}this.unsubscribe("beforeShow",b);}},configUnderlay:function(D,C,z){var B=(this.platform=="mac"&&l.gecko),E=C[0].toLowerCase(),v=this.underlay,w=this.element;function x(){var F=false;if(!v){if(!q){q=document.createElement("div");q.className="underlay";}v=q.cloneNode(false);this.element.appendChild(v);this.underlay=v;if(p){this.sizeUnderlay();this.cfg.subscribeToConfigEvent("width",this.sizeUnderlay);this.cfg.subscribeToConfigEvent("height",this.sizeUnderlay);this.changeContentEvent.subscribe(this.sizeUnderlay);YAHOO.widget.Module.textResizeEvent.subscribe(this.sizeUnderlay,this,true);}if(l.webkit&&l.webkit<420){this.changeContentEvent.subscribe(this.forceUnderlayRedraw);}F=true;}}function A(){var F=x.call(this);if(!F&&p){this.sizeUnderlay();}this._underlayDeferred=false;this.beforeShowEvent.unsubscribe(A);}function y(){if(this._underlayDeferred){this.beforeShowEvent.unsubscribe(A);this._underlayDeferred=false;}if(v){this.cfg.unsubscribeFromConfigEvent("width",this.sizeUnderlay);this.cfg.unsubscribeFromConfigEvent("height",this.sizeUnderlay);this.changeContentEvent.unsubscribe(this.sizeUnderlay);this.changeContentEvent.unsubscribe(this.forceUnderlayRedraw);YAHOO.widget.Module.textResizeEvent.unsubscribe(this.sizeUnderlay,this,true);this.element.removeChild(v);this.underlay=null;}}switch(E){case"shadow":a.removeClass(w,"matte");a.addClass(w,"shadow");break;case"matte":if(!B){y.call(this);}a.removeClass(w,"shadow");a.addClass(w,"matte");break;default:if(!B){y.call(this);
-}a.removeClass(w,"shadow");a.removeClass(w,"matte");break;}if((E=="shadow")||(B&&!v)){if(this.cfg.getProperty("visible")){var u=x.call(this);if(!u&&p){this.sizeUnderlay();}}else{if(!this._underlayDeferred){this.beforeShowEvent.subscribe(A);this._underlayDeferred=true;}}}},configModal:function(v,u,x){var w=u[0];if(w){if(!this._hasModalityEventListeners){this.subscribe("beforeShow",this.buildMask);this.subscribe("beforeShow",this.bringToTop);this.subscribe("beforeShow",this.showMask);this.subscribe("hide",this.hideMask);h.windowResizeEvent.subscribe(this.sizeMask,this,true);this._hasModalityEventListeners=true;}}else{if(this._hasModalityEventListeners){if(this.cfg.getProperty("visible")){this.hideMask();this.removeMask();}this.unsubscribe("beforeShow",this.buildMask);this.unsubscribe("beforeShow",this.bringToTop);this.unsubscribe("beforeShow",this.showMask);this.unsubscribe("hide",this.hideMask);h.windowResizeEvent.unsubscribe(this.sizeMask,this);this._hasModalityEventListeners=false;}}},removeMask:function(){var v=this.mask,u;if(v){this.hideMask();u=v.parentNode;if(u){u.removeChild(v);}this.mask=null;}},configKeyListeners:function(x,u,A){var w=u[0],z,y,v;if(w){if(w instanceof Array){y=w.length;for(v=0;v<y;v++){z=w[v];if(!i.alreadySubscribed(this.showEvent,z.enable,z)){this.showEvent.subscribe(z.enable,z,true);}if(!i.alreadySubscribed(this.hideEvent,z.disable,z)){this.hideEvent.subscribe(z.disable,z,true);this.destroyEvent.subscribe(z.disable,z,true);}}}else{if(!i.alreadySubscribed(this.showEvent,w.enable,w)){this.showEvent.subscribe(w.enable,w,true);}if(!i.alreadySubscribed(this.hideEvent,w.disable,w)){this.hideEvent.subscribe(w.disable,w,true);this.destroyEvent.subscribe(w.disable,w,true);}}}},configStrings:function(v,u,w){var x=e.merge(n.STRINGS.value,u[0]);this.cfg.setProperty(n.STRINGS.key,x,true);},configHeight:function(x,v,y){var u=v[0],w=this.innerElement;a.setStyle(w,"height",u);this.cfg.refireEvent("iframe");},_autoFillOnHeightChange:function(x,v,w){o.superclass._autoFillOnHeightChange.apply(this,arguments);if(p){var u=this;setTimeout(function(){u.sizeUnderlay();},0);}},configWidth:function(x,u,y){var w=u[0],v=this.innerElement;a.setStyle(v,"width",w);this.cfg.refireEvent("iframe");},configzIndex:function(v,u,x){o.superclass.configzIndex.call(this,v,u,x);if(this.mask||this.cfg.getProperty("modal")===true){var w=a.getStyle(this.element,"zIndex");if(!w||isNaN(w)){w=0;}if(w===0){this.cfg.setProperty("zIndex",1);}else{this.stackMask();}}},buildWrapper:function(){var w=this.element.parentNode,u=this.element,v=document.createElement("div");v.className=o.CSS_PANEL_CONTAINER;v.id=u.id+"_c";if(w){w.insertBefore(v,u);}v.appendChild(u);this.element=v;this.innerElement=u;a.setStyle(this.innerElement,"visibility","inherit");},sizeUnderlay:function(){var v=this.underlay,u;if(v){u=this.element;v.style.width=u.offsetWidth+"px";v.style.height=u.offsetHeight+"px";}},registerDragDrop:function(){var v=this;if(this.header){if(!f.DD){return;}var u=(this.cfg.getProperty("dragonly")===true);this.dd=new f.DD(this.element.id,this.id,{dragOnly:u});if(!this.header.id){this.header.id=this.id+"_h";}this.dd.startDrag=function(){var x,z,w,C,B,A;if(YAHOO.env.ua.ie==6){a.addClass(v.element,"drag");}if(v.cfg.getProperty("constraintoviewport")){var y=h.VIEWPORT_OFFSET;x=v.element.offsetHeight;z=v.element.offsetWidth;w=a.getViewportWidth();C=a.getViewportHeight();B=a.getDocumentScrollLeft();A=a.getDocumentScrollTop();if(x+y<C){this.minY=A+y;this.maxY=A+C-x-y;}else{this.minY=A+y;this.maxY=A+y;}if(z+y<w){this.minX=B+y;this.maxX=B+w-z-y;}else{this.minX=B+y;this.maxX=B+y;}this.constrainX=true;this.constrainY=true;}else{this.constrainX=false;this.constrainY=false;}v.dragEvent.fire("startDrag",arguments);};this.dd.onDrag=function(){v.syncPosition();v.cfg.refireEvent("iframe");if(this.platform=="mac"&&YAHOO.env.ua.gecko){this.showMacGeckoScrollbars();}v.dragEvent.fire("onDrag",arguments);};this.dd.endDrag=function(){if(YAHOO.env.ua.ie==6){a.removeClass(v.element,"drag");}v.dragEvent.fire("endDrag",arguments);v.moveEvent.fire(v.cfg.getProperty("xy"));};this.dd.setHandleElId(this.header.id);this.dd.addInvalidHandleType("INPUT");this.dd.addInvalidHandleType("SELECT");this.dd.addInvalidHandleType("TEXTAREA");}},buildMask:function(){var u=this.mask;if(!u){if(!g){g=document.createElement("div");g.className="mask";g.innerHTML="&#160;";}u=g.cloneNode(true);u.id=this.id+"_mask";document.body.insertBefore(u,document.body.firstChild);this.mask=u;if(YAHOO.env.ua.gecko&&this.platform=="mac"){a.addClass(this.mask,"block-scrollbars");}this.stackMask();}},hideMask:function(){if(this.cfg.getProperty("modal")&&this.mask&&this.beforeHideMaskEvent.fire()){this.mask.style.display="none";a.removeClass(document.body,"masked");this.hideMaskEvent.fire();}},showMask:function(){if(this.cfg.getProperty("modal")&&this.mask&&this.beforeShowMaskEvent.fire()){a.addClass(document.body,"masked");this.sizeMask();this.mask.style.display="block";this.showMaskEvent.fire();}},sizeMask:function(){if(this.mask){var v=this.mask,w=a.getViewportWidth(),u=a.getViewportHeight();if(v.offsetHeight>u){v.style.height=u+"px";}if(v.offsetWidth>w){v.style.width=w+"px";}v.style.height=a.getDocumentHeight()+"px";v.style.width=a.getDocumentWidth()+"px";}},stackMask:function(){if(this.mask){var u=a.getStyle(this.element,"zIndex");if(!YAHOO.lang.isUndefined(u)&&!isNaN(u)){a.setStyle(this.mask,"zIndex",u-1);}}},render:function(u){return o.superclass.render.call(this,u,this.innerElement);},_renderHeader:function(u){u=u||this.innerElement;o.superclass._renderHeader.call(this,u);},_renderBody:function(u){u=u||this.innerElement;o.superclass._renderBody.call(this,u);},_renderFooter:function(u){u=u||this.innerElement;o.superclass._renderFooter.call(this,u);},destroy:function(u){h.windowResizeEvent.unsubscribe(this.sizeMask,this);this.removeMask();if(this.close){t.purgeElement(this.close);}o.superclass.destroy.call(this,u);},forceUnderlayRedraw:function(){var v=this.underlay;a.addClass(v,"yui-force-redraw");
-setTimeout(function(){a.removeClass(v,"yui-force-redraw");},0);},toString:function(){return"Panel "+this.id;}});}());(function(){YAHOO.widget.Dialog=function(j,i){YAHOO.widget.Dialog.superclass.constructor.call(this,j,i);};var b=YAHOO.util.Event,g=YAHOO.util.CustomEvent,e=YAHOO.util.Dom,a=YAHOO.widget.Dialog,f=YAHOO.lang,h={"BEFORE_SUBMIT":"beforeSubmit","SUBMIT":"submit","MANUAL_SUBMIT":"manualSubmit","ASYNC_SUBMIT":"asyncSubmit","FORM_SUBMIT":"formSubmit","CANCEL":"cancel"},c={"POST_METHOD":{key:"postmethod",value:"async"},"POST_DATA":{key:"postdata",value:null},"BUTTONS":{key:"buttons",value:"none",supercedes:["visible"]},"HIDEAFTERSUBMIT":{key:"hideaftersubmit",value:true}};a.CSS_DIALOG="yui-dialog";function d(){var m=this._aButtons,k,l,j;if(f.isArray(m)){k=m.length;if(k>0){j=k-1;do{l=m[j];if(YAHOO.widget.Button&&l instanceof YAHOO.widget.Button){l.destroy();}else{if(l.tagName.toUpperCase()=="BUTTON"){b.purgeElement(l);b.purgeElement(l,false);}}}while(j--);}}}YAHOO.extend(a,YAHOO.widget.Panel,{form:null,initDefaultConfig:function(){a.superclass.initDefaultConfig.call(this);this.callback={success:null,failure:null,argument:null};this.cfg.addProperty(c.POST_METHOD.key,{handler:this.configPostMethod,value:c.POST_METHOD.value,validator:function(i){if(i!="form"&&i!="async"&&i!="none"&&i!="manual"){return false;}else{return true;}}});this.cfg.addProperty(c.POST_DATA.key,{value:c.POST_DATA.value});this.cfg.addProperty(c.HIDEAFTERSUBMIT.key,{value:c.HIDEAFTERSUBMIT.value});this.cfg.addProperty(c.BUTTONS.key,{handler:this.configButtons,value:c.BUTTONS.value,supercedes:c.BUTTONS.supercedes});},initEvents:function(){a.superclass.initEvents.call(this);var i=g.LIST;this.beforeSubmitEvent=this.createEvent(h.BEFORE_SUBMIT);this.beforeSubmitEvent.signature=i;this.submitEvent=this.createEvent(h.SUBMIT);this.submitEvent.signature=i;this.manualSubmitEvent=this.createEvent(h.MANUAL_SUBMIT);this.manualSubmitEvent.signature=i;this.asyncSubmitEvent=this.createEvent(h.ASYNC_SUBMIT);this.asyncSubmitEvent.signature=i;this.formSubmitEvent=this.createEvent(h.FORM_SUBMIT);this.formSubmitEvent.signature=i;this.cancelEvent=this.createEvent(h.CANCEL);this.cancelEvent.signature=i;},init:function(j,i){a.superclass.init.call(this,j);this.beforeInitEvent.fire(a);e.addClass(this.element,a.CSS_DIALOG);this.cfg.setProperty("visible",false);if(i){this.cfg.applyConfig(i,true);}this.beforeHideEvent.subscribe(this.blurButtons,this,true);this.subscribe("changeBody",this.registerForm);this.initEvent.fire(a);},doSubmit:function(){var q=YAHOO.util.Connect,r=this.form,l=false,o=false,s,n,m,j;switch(this.cfg.getProperty("postmethod")){case"async":s=r.elements;n=s.length;if(n>0){m=n-1;do{if(s[m].type=="file"){l=true;break;}}while(m--);}if(l&&YAHOO.env.ua.ie&&this.isSecure){o=true;}j=this._getFormAttributes(r);q.setForm(r,l,o);var k=this.cfg.getProperty("postdata");var p=q.asyncRequest(j.method,j.action,this.callback,k);this.asyncSubmitEvent.fire(p);break;case"form":r.submit();this.formSubmitEvent.fire();break;case"none":case"manual":this.manualSubmitEvent.fire();break;}},_getFormAttributes:function(k){var i={method:null,action:null};if(k){if(k.getAttributeNode){var j=k.getAttributeNode("action");var l=k.getAttributeNode("method");if(j){i.action=j.value;}if(l){i.method=l.value;}}else{i.action=k.getAttribute("action");i.method=k.getAttribute("method");}}i.method=(f.isString(i.method)?i.method:"POST").toUpperCase();i.action=f.isString(i.action)?i.action:"";return i;},registerForm:function(){var i=this.element.getElementsByTagName("form")[0];if(this.form){if(this.form==i&&e.isAncestor(this.element,this.form)){return;}else{b.purgeElement(this.form);this.form=null;}}if(!i){i=document.createElement("form");i.name="frm_"+this.id;this.body.appendChild(i);}if(i){this.form=i;b.on(i,"submit",this._submitHandler,this,true);}},_submitHandler:function(i){b.stopEvent(i);this.submit();this.form.blur();},setTabLoop:function(i,j){i=i||this.firstButton;j=j||this.lastButton;a.superclass.setTabLoop.call(this,i,j);},_setTabLoop:function(i,j){i=i||this.firstButton;j=this.lastButton||j;this.setTabLoop(i,j);},setFirstLastFocusable:function(){a.superclass.setFirstLastFocusable.call(this);var k,j,m,n=this.focusableElements;this.firstFormElement=null;this.lastFormElement=null;if(this.form&&n&&n.length>0){j=n.length;for(k=0;k<j;++k){m=n[k];if(this.form===m.form){this.firstFormElement=m;break;}}for(k=j-1;k>=0;--k){m=n[k];if(this.form===m.form){this.lastFormElement=m;break;}}}},configClose:function(j,i,k){a.superclass.configClose.apply(this,arguments);},_doClose:function(i){b.preventDefault(i);this.cancel();},configButtons:function(t,s,n){var o=YAHOO.widget.Button,v=s[0],l=this.innerElement,u,q,k,r,p,j,m;d.call(this);this._aButtons=null;if(f.isArray(v)){p=document.createElement("span");p.className="button-group";r=v.length;this._aButtons=[];this.defaultHtmlButton=null;for(m=0;m<r;m++){u=v[m];if(o){k=new o({label:u.text,type:u.type});k.appendTo(p);q=k.get("element");if(u.isDefault){k.addClass("default");this.defaultHtmlButton=q;}if(f.isFunction(u.handler)){k.set("onclick",{fn:u.handler,obj:this,scope:this});}else{if(f.isObject(u.handler)&&f.isFunction(u.handler.fn)){k.set("onclick",{fn:u.handler.fn,obj:((!f.isUndefined(u.handler.obj))?u.handler.obj:this),scope:(u.handler.scope||this)});}}this._aButtons[this._aButtons.length]=k;}else{q=document.createElement("button");q.setAttribute("type","button");if(u.isDefault){q.className="default";this.defaultHtmlButton=q;}q.innerHTML=u.text;if(f.isFunction(u.handler)){b.on(q,"click",u.handler,this,true);}else{if(f.isObject(u.handler)&&f.isFunction(u.handler.fn)){b.on(q,"click",u.handler.fn,((!f.isUndefined(u.handler.obj))?u.handler.obj:this),(u.handler.scope||this));}}p.appendChild(q);this._aButtons[this._aButtons.length]=q;}u.htmlButton=q;if(m===0){this.firstButton=q;}if(m==(r-1)){this.lastButton=q;}}this.setFooter(p);j=this.footer;if(e.inDocument(this.element)&&!e.isAncestor(l,j)){l.appendChild(j);}this.buttonSpan=p;}else{p=this.buttonSpan;
-j=this.footer;if(p&&j){j.removeChild(p);this.buttonSpan=null;this.firstButton=null;this.lastButton=null;this.defaultHtmlButton=null;}}this.changeContentEvent.fire();},getButtons:function(){return this._aButtons||null;},focusFirst:function(k,i,n){var j=this.firstFormElement,m=false;if(i&&i[1]){b.stopEvent(i[1]);if(i[0]===9&&this.firstElement){j=this.firstElement;}}if(j){try{j.focus();m=true;}catch(l){}}else{if(this.defaultHtmlButton){m=this.focusDefaultButton();}else{m=this.focusFirstButton();}}return m;},focusLast:function(k,i,n){var o=this.cfg.getProperty("buttons"),j=this.lastFormElement,m=false;if(i&&i[1]){b.stopEvent(i[1]);if(i[0]===9&&this.lastElement){j=this.lastElement;}}if(o&&f.isArray(o)){m=this.focusLastButton();}else{if(j){try{j.focus();m=true;}catch(l){}}}return m;},_getButton:function(j){var i=YAHOO.widget.Button;if(i&&j&&j.nodeName&&j.id){j=i.getButton(j.id)||j;}return j;},focusDefaultButton:function(){var i=this._getButton(this.defaultHtmlButton),k=false;if(i){try{i.focus();k=true;}catch(j){}}return k;},blurButtons:function(){var o=this.cfg.getProperty("buttons"),l,n,k,j;if(o&&f.isArray(o)){l=o.length;if(l>0){j=(l-1);do{n=o[j];if(n){k=this._getButton(n.htmlButton);if(k){try{k.blur();}catch(m){}}}}while(j--);}}},focusFirstButton:function(){var m=this.cfg.getProperty("buttons"),k,i,l=false;if(m&&f.isArray(m)){k=m[0];if(k){i=this._getButton(k.htmlButton);if(i){try{i.focus();l=true;}catch(j){}}}}return l;},focusLastButton:function(){var n=this.cfg.getProperty("buttons"),j,l,i,m=false;if(n&&f.isArray(n)){j=n.length;if(j>0){l=n[(j-1)];if(l){i=this._getButton(l.htmlButton);if(i){try{i.focus();m=true;}catch(k){}}}}}return m;},configPostMethod:function(j,i,k){this.registerForm();},validate:function(){return true;},submit:function(){if(this.validate()){if(this.beforeSubmitEvent.fire()){this.doSubmit();this.submitEvent.fire();if(this.cfg.getProperty("hideaftersubmit")){this.hide();}return true;}else{return false;}}else{return false;}},cancel:function(){this.cancelEvent.fire();this.hide();},getData:function(){var A=this.form,k,t,w,m,u,r,q,j,x,l,y,B,p,C,o,z,v;function s(n){var i=n.tagName.toUpperCase();return((i=="INPUT"||i=="TEXTAREA"||i=="SELECT")&&n.name==m);}if(A){k=A.elements;t=k.length;w={};for(z=0;z<t;z++){m=k[z].name;u=e.getElementsBy(s,"*",A);r=u.length;if(r>0){if(r==1){u=u[0];q=u.type;j=u.tagName.toUpperCase();switch(j){case"INPUT":if(q=="checkbox"){w[m]=u.checked;}else{if(q!="radio"){w[m]=u.value;}}break;case"TEXTAREA":w[m]=u.value;break;case"SELECT":x=u.options;l=x.length;y=[];for(v=0;v<l;v++){B=x[v];if(B.selected){o=B.attributes.value;y[y.length]=(o&&o.specified)?B.value:B.text;}}w[m]=y;break;}}else{q=u[0].type;switch(q){case"radio":for(v=0;v<r;v++){p=u[v];if(p.checked){w[m]=p.value;break;}}break;case"checkbox":y=[];for(v=0;v<r;v++){C=u[v];if(C.checked){y[y.length]=C.value;}}w[m]=y;break;}}}}}return w;},destroy:function(i){d.call(this);this._aButtons=null;var j=this.element.getElementsByTagName("form"),k;if(j.length>0){k=j[0];if(k){b.purgeElement(k);if(k.parentNode){k.parentNode.removeChild(k);}this.form=null;}}a.superclass.destroy.call(this,i);},toString:function(){return"Dialog "+this.id;}});}());(function(){YAHOO.widget.SimpleDialog=function(e,d){YAHOO.widget.SimpleDialog.superclass.constructor.call(this,e,d);};var c=YAHOO.util.Dom,b=YAHOO.widget.SimpleDialog,a={"ICON":{key:"icon",value:"none",suppressEvent:true},"TEXT":{key:"text",value:"",suppressEvent:true,supercedes:["icon"]}};b.ICON_BLOCK="blckicon";b.ICON_ALARM="alrticon";b.ICON_HELP="hlpicon";b.ICON_INFO="infoicon";b.ICON_WARN="warnicon";b.ICON_TIP="tipicon";b.ICON_CSS_CLASSNAME="yui-icon";b.CSS_SIMPLEDIALOG="yui-simple-dialog";YAHOO.extend(b,YAHOO.widget.Dialog,{initDefaultConfig:function(){b.superclass.initDefaultConfig.call(this);this.cfg.addProperty(a.ICON.key,{handler:this.configIcon,value:a.ICON.value,suppressEvent:a.ICON.suppressEvent});this.cfg.addProperty(a.TEXT.key,{handler:this.configText,value:a.TEXT.value,suppressEvent:a.TEXT.suppressEvent,supercedes:a.TEXT.supercedes});},init:function(e,d){b.superclass.init.call(this,e);this.beforeInitEvent.fire(b);c.addClass(this.element,b.CSS_SIMPLEDIALOG);this.cfg.queueProperty("postmethod","manual");if(d){this.cfg.applyConfig(d,true);}this.beforeRenderEvent.subscribe(function(){if(!this.body){this.setBody("");}},this,true);this.initEvent.fire(b);},registerForm:function(){b.superclass.registerForm.call(this);var e=this.form.ownerDocument,d=e.createElement("input");d.type="hidden";d.name=this.id;d.value="";this.form.appendChild(d);},configIcon:function(k,j,h){var d=j[0],e=this.body,f=b.ICON_CSS_CLASSNAME,l,i,g;if(d&&d!="none"){l=c.getElementsByClassName(f,"*",e);if(l.length===1){i=l[0];g=i.parentNode;if(g){g.removeChild(i);i=null;}}if(d.indexOf(".")==-1){i=document.createElement("span");i.className=(f+" "+d);i.innerHTML="&#160;";}else{i=document.createElement("img");i.src=(this.imageRoot+d);i.className=f;}if(i){e.insertBefore(i,e.firstChild);}}},configText:function(e,d,f){var g=d[0];if(g){this.setBody(g);this.cfg.refireEvent("icon");}},toString:function(){return"SimpleDialog "+this.id;}});}());(function(){YAHOO.widget.ContainerEffect=function(e,h,g,d,f){if(!f){f=YAHOO.util.Anim;}this.overlay=e;this.attrIn=h;this.attrOut=g;this.targetElement=d||e.element;this.animClass=f;};var b=YAHOO.util.Dom,c=YAHOO.util.CustomEvent,a=YAHOO.widget.ContainerEffect;a.FADE=function(d,f){var g=YAHOO.util.Easing,i={attributes:{opacity:{from:0,to:1}},duration:f,method:g.easeIn},e={attributes:{opacity:{to:0}},duration:f,method:g.easeOut},h=new a(d,i,e,d.element);h.handleUnderlayStart=function(){var k=this.overlay.underlay;if(k&&YAHOO.env.ua.ie){var j=(k.filters&&k.filters.length>0);if(j){b.addClass(d.element,"yui-effect-fade");}}};h.handleUnderlayComplete=function(){var j=this.overlay.underlay;if(j&&YAHOO.env.ua.ie){b.removeClass(d.element,"yui-effect-fade");}};h.handleStartAnimateIn=function(k,j,l){l.overlay._fadingIn=true;b.addClass(l.overlay.element,"hide-select");if(!l.overlay.underlay){l.overlay.cfg.refireEvent("underlay");
-}l.handleUnderlayStart();l.overlay._setDomVisibility(true);b.setStyle(l.overlay.element,"opacity",0);};h.handleCompleteAnimateIn=function(k,j,l){l.overlay._fadingIn=false;b.removeClass(l.overlay.element,"hide-select");if(l.overlay.element.style.filter){l.overlay.element.style.filter=null;}l.handleUnderlayComplete();l.overlay.cfg.refireEvent("iframe");l.animateInCompleteEvent.fire();};h.handleStartAnimateOut=function(k,j,l){l.overlay._fadingOut=true;b.addClass(l.overlay.element,"hide-select");l.handleUnderlayStart();};h.handleCompleteAnimateOut=function(k,j,l){l.overlay._fadingOut=false;b.removeClass(l.overlay.element,"hide-select");if(l.overlay.element.style.filter){l.overlay.element.style.filter=null;}l.overlay._setDomVisibility(false);b.setStyle(l.overlay.element,"opacity",1);l.handleUnderlayComplete();l.overlay.cfg.refireEvent("iframe");l.animateOutCompleteEvent.fire();};h.init();return h;};a.SLIDE=function(f,d){var i=YAHOO.util.Easing,l=f.cfg.getProperty("x")||b.getX(f.element),k=f.cfg.getProperty("y")||b.getY(f.element),m=b.getClientWidth(),h=f.element.offsetWidth,j={attributes:{points:{to:[l,k]}},duration:d,method:i.easeIn},e={attributes:{points:{to:[(m+25),k]}},duration:d,method:i.easeOut},g=new a(f,j,e,f.element,YAHOO.util.Motion);g.handleStartAnimateIn=function(o,n,p){p.overlay.element.style.left=((-25)-h)+"px";p.overlay.element.style.top=k+"px";};g.handleTweenAnimateIn=function(q,p,r){var s=b.getXY(r.overlay.element),o=s[0],n=s[1];if(b.getStyle(r.overlay.element,"visibility")=="hidden"&&o<l){r.overlay._setDomVisibility(true);}r.overlay.cfg.setProperty("xy",[o,n],true);r.overlay.cfg.refireEvent("iframe");};g.handleCompleteAnimateIn=function(o,n,p){p.overlay.cfg.setProperty("xy",[l,k],true);p.startX=l;p.startY=k;p.overlay.cfg.refireEvent("iframe");p.animateInCompleteEvent.fire();};g.handleStartAnimateOut=function(o,n,r){var p=b.getViewportWidth(),s=b.getXY(r.overlay.element),q=s[1];r.animOut.attributes.points.to=[(p+25),q];};g.handleTweenAnimateOut=function(p,o,q){var s=b.getXY(q.overlay.element),n=s[0],r=s[1];q.overlay.cfg.setProperty("xy",[n,r],true);q.overlay.cfg.refireEvent("iframe");};g.handleCompleteAnimateOut=function(o,n,p){p.overlay._setDomVisibility(false);p.overlay.cfg.setProperty("xy",[l,k]);p.animateOutCompleteEvent.fire();};g.init();return g;};a.prototype={init:function(){this.beforeAnimateInEvent=this.createEvent("beforeAnimateIn");this.beforeAnimateInEvent.signature=c.LIST;this.beforeAnimateOutEvent=this.createEvent("beforeAnimateOut");this.beforeAnimateOutEvent.signature=c.LIST;this.animateInCompleteEvent=this.createEvent("animateInComplete");this.animateInCompleteEvent.signature=c.LIST;this.animateOutCompleteEvent=this.createEvent("animateOutComplete");this.animateOutCompleteEvent.signature=c.LIST;this.animIn=new this.animClass(this.targetElement,this.attrIn.attributes,this.attrIn.duration,this.attrIn.method);this.animIn.onStart.subscribe(this.handleStartAnimateIn,this);this.animIn.onTween.subscribe(this.handleTweenAnimateIn,this);this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn,this);this.animOut=new this.animClass(this.targetElement,this.attrOut.attributes,this.attrOut.duration,this.attrOut.method);this.animOut.onStart.subscribe(this.handleStartAnimateOut,this);this.animOut.onTween.subscribe(this.handleTweenAnimateOut,this);this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut,this);},animateIn:function(){this._stopAnims(this.lastFrameOnStop);this.beforeAnimateInEvent.fire();this.animIn.animate();},animateOut:function(){this._stopAnims(this.lastFrameOnStop);this.beforeAnimateOutEvent.fire();this.animOut.animate();},lastFrameOnStop:true,_stopAnims:function(d){if(this.animOut&&this.animOut.isAnimated()){this.animOut.stop(d);}if(this.animIn&&this.animIn.isAnimated()){this.animIn.stop(d);}},handleStartAnimateIn:function(e,d,f){},handleTweenAnimateIn:function(e,d,f){},handleCompleteAnimateIn:function(e,d,f){},handleStartAnimateOut:function(e,d,f){},handleTweenAnimateOut:function(e,d,f){},handleCompleteAnimateOut:function(e,d,f){},toString:function(){var d="ContainerEffect";if(this.overlay){d+=" ["+this.overlay.toString()+"]";}return d;}};YAHOO.lang.augmentProto(a,YAHOO.util.EventProvider);})();YAHOO.register("container",YAHOO.widget.Module,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-var Y=YAHOO,Y_DOM=YAHOO.util.Dom,EMPTY_ARRAY=[],Y_UA=Y.env.ua,Y_Lang=Y.lang,Y_DOC=document,Y_DOCUMENT_ELEMENT=Y_DOC.documentElement,Y_DOM_inDoc=Y_DOM.inDocument,Y_mix=Y_Lang.augmentObject,Y_guid=Y_DOM.generateId,Y_getDoc=function(a){var b=Y_DOC;if(a){b=(a.nodeType===9)?a:a.ownerDocument||a.document||Y_DOC;}return b;},Y_Array=function(g,d){var c,b,h=d||0;try{return Array.prototype.slice.call(g,h);}catch(f){b=[];c=g.length;for(;h<c;h++){b.push(g[h]);}return b;}},Y_DOM_allById=function(f,a){a=a||Y_DOC;var b=[],c=[],d,e;if(a.querySelectorAll){c=a.querySelectorAll('[id="'+f+'"]');}else{if(a.all){b=a.all(f);if(b){if(b.nodeName){if(b.id===f){c.push(b);b=EMPTY_ARRAY;}else{b=[b];}}if(b.length){for(d=0;e=b[d++];){if(e.id===f||(e.attributes&&e.attributes.id&&e.attributes.id.value===f)){c.push(e);}}}}}else{c=[Y_getDoc(a).getElementById(f)];}}return c;};var COMPARE_DOCUMENT_POSITION="compareDocumentPosition",OWNER_DOCUMENT="ownerDocument",Selector={_foundCache:[],useNative:true,_compare:("sourceIndex" in Y_DOCUMENT_ELEMENT)?function(f,e){var d=f.sourceIndex,c=e.sourceIndex;if(d===c){return 0;}else{if(d>c){return 1;}}return -1;}:(Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION]?function(b,a){if(b[COMPARE_DOCUMENT_POSITION](a)&4){return -1;}else{return 1;}}:function(e,d){var c,a,b;if(e&&d){c=e[OWNER_DOCUMENT].createRange();c.setStart(e,0);a=d[OWNER_DOCUMENT].createRange();a.setStart(d,0);b=c.compareBoundaryPoints(1,a);}return b;}),_sort:function(a){if(a){a=Y_Array(a,0,true);if(a.sort){a.sort(Selector._compare);}}return a;},_deDupe:function(a){var b=[],c,d;for(c=0;(d=a[c++]);){if(!d._found){b[b.length]=d;d._found=true;}}for(c=0;(d=b[c++]);){d._found=null;d.removeAttribute("_found");}return b;},query:function(b,j,k,a){if(j&&typeof j=="string"){j=Y_DOM.get(j);if(!j){return(k)?null:[];}}else{j=j||Y_DOC;}var f=[],c=(Selector.useNative&&Y_DOC.querySelector&&!a),e=[[b,j]],g,l,d,h=(c)?Selector._nativeQuery:Selector._bruteQuery;if(b&&h){if(!a&&(!c||j.tagName)){e=Selector._splitQueries(b,j);}for(d=0;(g=e[d++]);){l=h(g[0],g[1],k);if(!k){l=Y_Array(l,0,true);}if(l){f=f.concat(l);}}if(e.length>1){f=Selector._sort(Selector._deDupe(f));}}return(k)?(f[0]||null):f;},_splitQueries:function(c,f){var b=c.split(","),d=[],g="",e,a;if(f){if(f.tagName){f.id=f.id||Y_guid();g='[id="'+f.id+'"] ';}for(e=0,a=b.length;e<a;++e){c=g+b[e];d.push([c,f]);}}return d;},_nativeQuery:function(a,b,c){if(Y_UA.webkit&&a.indexOf(":checked")>-1&&(Selector.pseudos&&Selector.pseudos.checked)){return Selector.query(a,b,c,true);}try{return b["querySelector"+(c?"":"All")](a);}catch(d){return Selector.query(a,b,c,true);}},filter:function(b,a){var c=[],d,e;if(b&&a){for(d=0;(e=b[d++]);){if(Selector.test(e,a)){c[c.length]=e;}}}else{}return c;},test:function(c,d,k){var g=false,b=d.split(","),a=false,l,o,h,n,f,e,m;if(c&&c.tagName){if(!k&&!Y_DOM_inDoc(c)){l=c.parentNode;if(l){k=l;}else{n=c[OWNER_DOCUMENT].createDocumentFragment();n.appendChild(c);k=n;a=true;}}k=k||c[OWNER_DOCUMENT];if(!c.id){c.id=Y_guid();}for(f=0;(m=b[f++]);){m+='[id="'+c.id+'"]';h=Selector.query(m,k);for(e=0;o=h[e++];){if(o===c){g=true;break;}}if(g){break;}}if(a){n.removeChild(c);}}return g;}};YAHOO.util.Selector=Selector;var PARENT_NODE="parentNode",TAG_NAME="tagName",ATTRIBUTES="attributes",COMBINATOR="combinator",PSEUDOS="pseudos",SelectorCSS2={_reRegExpTokens:/([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,SORT_RESULTS:true,_children:function(e,a){var b=e.children,d,c=[],f,g;if(e.children&&a&&e.children.tags){c=e.children.tags(a);}else{if((!b&&e[TAG_NAME])||(b&&a)){f=b||e.childNodes;b=[];for(d=0;(g=f[d++]);){if(g.tagName){if(!a||a===g.tagName){b.push(g);}}}}}return b||[];},_re:{attr:/(\[[^\]]*\])/g,esc:/\\[:\[\]\(\)#\.\'\>+~"]/gi,pseudos:/(\([^\)]*\))/g},shorthand:{"\\#(-?[_a-z]+[-\\w\\uE000]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w\\uE000]*)":"[className~=$1]"},operators:{"":function(b,a){return !!b.getAttribute(a);},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}(?:-|$)"},pseudos:{"first-child":function(a){return Selector._children(a[PARENT_NODE])[0]===a;}},_bruteQuery:function(f,j,l){var g=[],a=[],i=Selector._tokenize(f),e=i[i.length-1],k=Y_getDoc(j),c,b,h,d;if(e){b=e.id;h=e.className;d=e.tagName||"*";if(j.getElementsByTagName){if(b&&(j.all||(j.nodeType===9||Y_DOM_inDoc(j)))){a=Y_DOM_allById(b,j);}else{if(h){a=j.getElementsByClassName(h);}else{a=j.getElementsByTagName(d);}}}else{c=j.firstChild;while(c){if(c.tagName){a.push(c);}c=c.nextSilbing||c.firstChild;}}if(a.length){g=Selector._filterNodes(a,i,l);}}return g;},_filterNodes:function(l,f,h){var r=0,q,s=f.length,k=s-1,e=[],o=l[0],v=o,t=Selector.getters,d,p,c,g,a,m,b,u;for(r=0;(v=o=l[r++]);){k=s-1;g=null;testLoop:while(v&&v.tagName){c=f[k];b=c.tests;q=b.length;if(q&&!a){while((u=b[--q])){d=u[1];if(t[u[0]]){m=t[u[0]](v,u[0]);}else{m=v[u[0]];if(m===undefined&&v.getAttribute){m=v.getAttribute(u[0]);}}if((d==="="&&m!==u[2])||(typeof d!=="string"&&d.test&&!d.test(m))||(!d.test&&typeof d==="function"&&!d(v,u[0],u[2]))){if((v=v[g])){while(v&&(!v.tagName||(c.tagName&&c.tagName!==v.tagName))){v=v[g];}}continue testLoop;}}}k--;if(!a&&(p=c.combinator)){g=p.axis;v=v[g];while(v&&!v.tagName){v=v[g];}if(p.direct){g=null;}}else{e.push(o);if(h){return e;}break;}}}o=v=null;return e;},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:ATTRIBUTES,re:/^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,fn:function(d,e){var c=d[2]||"",a=Selector.operators,b=(d[3])?d[3].replace(/\\/g,""):"",f;if((d[1]==="id"&&c==="=")||(d[1]==="className"&&Y_DOCUMENT_ELEMENT.getElementsByClassName&&(c==="~="||c==="="))){e.prefilter=d[1];d[3]=b;e[d[1]]=(d[1]==="id")?d[3]:b;}if(c in a){f=a[c];if(typeof f==="string"){d[3]=b.replace(Selector._reRegExpTokens,"\\$1");f=new RegExp(f.replace("{val}",d[3]));}d[2]=f;}if(!e.last||e.prefilter!==d[1]){return d.slice(1);}}},{name:TAG_NAME,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(b,c){var a=b[1].toUpperCase();c.tagName=a;if(a!=="*"&&(!c.last||c.prefilter)){return[TAG_NAME,"=",a];
-}if(!c.prefilter){c.prefilter="tagName";}}},{name:COMBINATOR,re:/^\s*([>+~]|\s)\s*/,fn:function(a,b){}},{name:PSEUDOS,re:/^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,fn:function(a,b){var c=Selector[PSEUDOS][a[1]];if(c){if(a[2]){a[2]=a[2].replace(/\\/g,"");}return[a[2],c];}else{return false;}}}],_getToken:function(a){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(c){c=c||"";c=Selector._replaceShorthand(Y_Lang.trim(c));var b=Selector._getToken(),h=c,g=[],j=false,e,f,d,a;outer:do{j=false;for(d=0;(a=Selector._parsers[d++]);){if((e=a.re.exec(c))){if(a.name!==COMBINATOR){b.selector=c;}c=c.replace(e[0],"");if(!c.length){b.last=true;}if(Selector._attrFilters[e[1]]){e[1]=Selector._attrFilters[e[1]];}f=a.fn(e,b);if(f===false){j=false;break outer;}else{if(f){b.tests.push(f);}}if(!c.length||a.name===COMBINATOR){g.push(b);b=Selector._getToken(b);if(a.name===COMBINATOR){b.combinator=Selector.combinators[e[1]];}}j=true;}}}while(j&&c.length);if(!j||c.length){g=[];}return g;},_replaceShorthand:function(b){var d=Selector.shorthand,c=b.match(Selector._re.esc),e,h,g,f,a;if(c){b=b.replace(Selector._re.esc,"\uE000");}e=b.match(Selector._re.attr);h=b.match(Selector._re.pseudos);if(e){b=b.replace(Selector._re.attr,"\uE001");}if(h){b=b.replace(Selector._re.pseudos,"\uE002");}for(g in d){if(d.hasOwnProperty(g)){b=b.replace(new RegExp(g,"gi"),d[g]);}}if(e){for(f=0,a=e.length;f<a;++f){b=b.replace(/\uE001/,e[f]);}}if(h){for(f=0,a=h.length;f<a;++f){b=b.replace(/\uE002/,h[f]);}}b=b.replace(/\[/g,"\uE003");b=b.replace(/\]/g,"\uE004");b=b.replace(/\(/g,"\uE005");b=b.replace(/\)/g,"\uE006");if(c){for(f=0,a=c.length;f<a;++f){b=b.replace("\uE000",c[f]);}}return b;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(b,a){return Y_DOM.getAttribute(b,a);}}};Y_mix(Selector,SelectorCSS2,true);Selector.getters.src=Selector.getters.rel=Selector.getters.href;if(Selector.useNative&&Y_DOC.querySelector){Selector.shorthand["\\.([^\\s\\\\(\\[:]*)"]="[class~=$1]";}Selector._reNth=/^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;Selector._getNth=function(d,o,q,h){Selector._reNth.test(o);var m=parseInt(RegExp.$1,10),c=RegExp.$2,j=RegExp.$3,k=parseInt(RegExp.$4,10)||0,p=[],l=Selector._children(d.parentNode,q),f;if(j){m=2;f="+";c="n";k=(j==="odd")?1:0;}else{if(isNaN(m)){m=(c)?1:0;}}if(m===0){if(h){k=l.length-k+1;}if(l[k-1]===d){return true;}else{return false;}}else{if(m<0){h=!!h;m=Math.abs(m);}}if(!h){for(var e=k-1,g=l.length;e<g;e+=m){if(e>=0&&l[e]===d){return true;}}}else{for(var e=l.length-k,g=l.length;e>=0;e-=m){if(e<g&&l[e]===d){return true;}}}return false;};Y_mix(Selector.pseudos,{"root":function(a){return a===a.ownerDocument.documentElement;},"nth-child":function(a,b){return Selector._getNth(a,b);},"nth-last-child":function(a,b){return Selector._getNth(a,b,null,true);},"nth-of-type":function(a,b){return Selector._getNth(a,b,a.tagName);},"nth-last-of-type":function(a,b){return Selector._getNth(a,b,a.tagName,true);},"last-child":function(b){var a=Selector._children(b.parentNode);return a[a.length-1]===b;},"first-of-type":function(a){return Selector._children(a.parentNode,a.tagName)[0]===a;},"last-of-type":function(b){var a=Selector._children(b.parentNode,b.tagName);return a[a.length-1]===b;},"only-child":function(b){var a=Selector._children(b.parentNode);return a.length===1&&a[0]===b;},"only-of-type":function(b){var a=Selector._children(b.parentNode,b.tagName);return a.length===1&&a[0]===b;},"empty":function(a){return a.childNodes.length===0;},"not":function(a,b){return !Selector.test(a,b);},"contains":function(a,b){var c=a.innerText||a.textContent||"";return c.indexOf(b)>-1;},"checked":function(a){return(a.checked===true||a.selected===true);},enabled:function(a){return(a.disabled!==undefined&&!a.disabled);},disabled:function(a){return(a.disabled);}});Y_mix(Selector.operators,{"^=":"^{val}","!=":function(b,a,c){return b[a]!==c;},"$=":"{val}$","*=":"{val}"});Selector.combinators["~"]={axis:"previousSibling"};YAHOO.register("selector",YAHOO.util.Selector,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-(function(){var A=YAHOO.util.Event,C=YAHOO.lang,B=[],D=function(H,E,F){var G;if(!H||H===F){G=false;}else{G=YAHOO.util.Selector.test(H,E)?H:D(H.parentNode,E,F);}return G;};C.augmentObject(A,{_createDelegate:function(F,E,G,H){return function(I){var J=this,N=A.getTarget(I),L=E,P=(J.nodeType===9),Q,K,O,M;if(C.isFunction(E)){Q=E(N);}else{if(C.isString(E)){if(!P){O=J.id;if(!O){O=A.generateId(J);}M=("#"+O+" ");L=(M+E).replace(/,/gi,(","+M));}if(YAHOO.util.Selector.test(N,L)){Q=N;}else{if(YAHOO.util.Selector.test(N,((L.replace(/,/gi," *,"))+" *"))){Q=D(N,L,J);}}}}if(Q){K=Q;if(H){if(H===true){K=G;}else{K=H;}}return F.call(K,I,Q,J,G);}};},delegate:function(F,J,L,G,H,I){var E=J,K,M;if(C.isString(G)&&!YAHOO.util.Selector){return false;}if(J=="mouseenter"||J=="mouseleave"){if(!A._createMouseDelegate){return false;}E=A._getType(J);K=A._createMouseDelegate(L,H,I);M=A._createDelegate(function(P,O,N){return K.call(O,P,N);},G,H,I);}else{M=A._createDelegate(L,G,H,I);}B.push([F,E,L,M]);return A.on(F,E,M);},removeDelegate:function(F,J,I){var K=J,H=false,G,E;if(J=="mouseenter"||J=="mouseleave"){K=A._getType(J);}G=A._getCacheIndex(B,F,K,I);if(G>=0){E=B[G];}if(F&&E){H=A.removeListener(E[0],E[1],E[3]);if(H){delete B[G][2];delete B[G][3];B.splice(G,1);}}return H;}});}());YAHOO.register("event-delegate",YAHOO.util.Event,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-(function(){var l=YAHOO.lang,isFunction=l.isFunction,isObject=l.isObject,isArray=l.isArray,_toStr=Object.prototype.toString,Native=(YAHOO.env.ua.caja?window:this).JSON,_UNICODE_EXCEPTIONS=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,_ESCAPES=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,_VALUES=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,_BRACKETS=/(?:^|:|,)(?:\s*\[)+/g,_UNSAFE=/[^\],:{}\s]/,_SPECIAL_CHARS=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,_CHARS={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},UNDEFINED="undefined",OBJECT="object",NULL="null",STRING="string",NUMBER="number",BOOLEAN="boolean",DATE="date",_allowable={"undefined":UNDEFINED,"string":STRING,"[object String]":STRING,"number":NUMBER,"[object Number]":NUMBER,"boolean":BOOLEAN,"[object Boolean]":BOOLEAN,"[object Date]":DATE,"[object RegExp]":OBJECT},EMPTY="",OPEN_O="{",CLOSE_O="}",OPEN_A="[",CLOSE_A="]",COMMA=",",COMMA_CR=",\n",CR="\n",COLON=":",COLON_SP=": ",QUOTE='"';Native=_toStr.call(Native)==="[object JSON]"&&Native;function _char(c){if(!_CHARS[c]){_CHARS[c]="\\u"+("0000"+(+(c.charCodeAt(0))).toString(16)).slice(-4);}return _CHARS[c];}function _revive(data,reviver){var walk=function(o,key){var k,v,value=o[key];if(value&&typeof value==="object"){for(k in value){if(l.hasOwnProperty(value,k)){v=walk(value,k);if(v===undefined){delete value[k];}else{value[k]=v;}}}}return reviver.call(o,key,value);};return typeof reviver==="function"?walk({"":data},""):data;}function _prepare(s){return s.replace(_UNICODE_EXCEPTIONS,_char);}function _isSafe(str){return l.isString(str)&&!_UNSAFE.test(str.replace(_ESCAPES,"@").replace(_VALUES,"]").replace(_BRACKETS,""));}function _parse(s,reviver){s=_prepare(s);if(_isSafe(s)){return _revive(eval("("+s+")"),reviver);}throw new SyntaxError("JSON.parse");}function _type(o){var t=typeof o;return _allowable[t]||_allowable[_toStr.call(o)]||(t===OBJECT?(o?OBJECT:NULL):UNDEFINED);}function _string(s){return QUOTE+s.replace(_SPECIAL_CHARS,_char)+QUOTE;}function _indent(s,space){return s.replace(/^/gm,space);}function _stringify(o,w,space){if(o===undefined){return undefined;}var replacer=isFunction(w)?w:null,format=_toStr.call(space).match(/String|Number/)||[],_date=YAHOO.lang.JSON.dateToString,stack=[],tmp,i,len;if(replacer||!isArray(w)){w=undefined;}if(w){tmp={};for(i=0,len=w.length;i<len;++i){tmp[w[i]]=true;}w=tmp;}space=format[0]==="Number"?new Array(Math.min(Math.max(0,space),10)+1).join(" "):(space||EMPTY).slice(0,10);function _serialize(h,key){var value=h[key],t=_type(value),a=[],colon=space?COLON_SP:COLON,arr,i,keys,k,v;if(isObject(value)&&isFunction(value.toJSON)){value=value.toJSON(key);}else{if(t===DATE){value=_date(value);}}if(isFunction(replacer)){value=replacer.call(h,key,value);}if(value!==h[key]){t=_type(value);}switch(t){case DATE:case OBJECT:break;case STRING:return _string(value);case NUMBER:return isFinite(value)?value+EMPTY:NULL;case BOOLEAN:return value+EMPTY;case NULL:return NULL;default:return undefined;}for(i=stack.length-1;i>=0;--i){if(stack[i]===value){throw new Error("JSON.stringify. Cyclical reference");}}arr=isArray(value);stack.push(value);if(arr){for(i=value.length-1;i>=0;--i){a[i]=_serialize(value,i)||NULL;}}else{keys=w||value;i=0;for(k in keys){if(l.hasOwnProperty(keys,k)){v=_serialize(value,k);if(v){a[i++]=_string(k)+colon+v;}}}}stack.pop();if(space&&a.length){return arr?OPEN_A+CR+_indent(a.join(COMMA_CR),space)+CR+CLOSE_A:OPEN_O+CR+_indent(a.join(COMMA_CR),space)+CR+CLOSE_O;}else{return arr?OPEN_A+a.join(COMMA)+CLOSE_A:OPEN_O+a.join(COMMA)+CLOSE_O;}}return _serialize({"":o},"");}YAHOO.lang.JSON={useNativeParse:!!Native,useNativeStringify:!!Native,isSafe:function(s){return _isSafe(_prepare(s));},parse:function(s,reviver){if(typeof s!=="string"){s+="";}return Native&&YAHOO.lang.JSON.useNativeParse?Native.parse(s,reviver):_parse(s,reviver);},stringify:function(o,w,space){return Native&&YAHOO.lang.JSON.useNativeStringify?Native.stringify(o,w,space):_stringify(o,w,space);},dateToString:function(d){function _zeroPad(v){return v<10?"0"+v:v;}return d.getUTCFullYear()+"-"+_zeroPad(d.getUTCMonth()+1)+"-"+_zeroPad(d.getUTCDate())+"T"+_zeroPad(d.getUTCHours())+COLON+_zeroPad(d.getUTCMinutes())+COLON+_zeroPad(d.getUTCSeconds())+"Z";},stringToDate:function(str){var m=str.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?Z$/);if(m){var d=new Date();d.setUTCFullYear(m[1],m[2]-1,m[3]);d.setUTCHours(m[4],m[5],m[6],(m[7]||0));return d;}return str;}};YAHOO.lang.JSON.isValid=YAHOO.lang.JSON.isSafe;})();YAHOO.register("json",YAHOO.lang.JSON,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-(function(){var b=YAHOO.util.Event,g=YAHOO.lang,e=b.addListener,f=b.removeListener,c=b.getListeners,d=[],h={mouseenter:"mouseover",mouseleave:"mouseout"},a=function(n,m,l){var j=b._getCacheIndex(d,n,m,l),i,k;if(j>=0){i=d[j];}if(n&&i){k=f.call(b,i[0],m,i[3]);if(k){delete d[j][2];delete d[j][3];d.splice(j,1);}}return k;};g.augmentObject(b._specialTypes,h);g.augmentObject(b,{_createMouseDelegate:function(i,j,k){return function(q,m){var p=this,l=b.getRelatedTarget(q),o,n;if(p!=l&&!YAHOO.util.Dom.isAncestor(p,l)){o=p;if(k){if(k===true){o=j;}else{o=k;}}n=[q,j];if(m){n.splice(1,0,p,m);}return i.apply(o,n);}};},addListener:function(m,l,k,n,o){var i,j;if(h[l]){i=b._createMouseDelegate(k,n,o);i.mouseDelegate=true;d.push([m,l,k,i]);j=e.call(b,m,l,i);}else{j=e.apply(b,arguments);}return j;},removeListener:function(l,k,j){var i;if(h[k]){i=a.apply(b,arguments);}else{i=f.apply(b,arguments);}return i;},getListeners:function(p,o){var n=[],r,m=(o==="mouseover"||o==="mouseout"),q,k,j;if(o&&(m||h[o])){r=c.call(b,p,this._getType(o));if(r){for(k=r.length-1;k>-1;k--){j=r[k];q=j.fn.mouseDelegate;if((h[o]&&q)||(m&&!q)){n.push(j);}}}}else{n=c.apply(b,arguments);}return(n&&n.length)?n:null;}},true);b.on=b.addListener;}());YAHOO.register("event-mouseenter",YAHOO.util.Event,{version:"2.9.0",build:"2800"});
-/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-(function(){var lang=YAHOO.lang,util=YAHOO.util,Ev=util.Event;util.DataSourceBase=function(oLiveData,oConfigs){if(oLiveData===null||oLiveData===undefined){return;}this.liveData=oLiveData;this._oQueue={interval:null,conn:null,requests:[]};this.responseSchema={};if(oConfigs&&(oConfigs.constructor==Object)){for(var sConfig in oConfigs){if(sConfig){this[sConfig]=oConfigs[sConfig];}}}var maxCacheEntries=this.maxCacheEntries;if(!lang.isNumber(maxCacheEntries)||(maxCacheEntries<0)){maxCacheEntries=0;}this._aIntervals=[];this.createEvent("cacheRequestEvent");this.createEvent("cacheResponseEvent");this.createEvent("requestEvent");this.createEvent("responseEvent");this.createEvent("responseParseEvent");this.createEvent("responseCacheEvent");this.createEvent("dataErrorEvent");this.createEvent("cacheFlushEvent");var DS=util.DataSourceBase;this._sName="DataSource instance"+DS._nIndex;DS._nIndex++;};var DS=util.DataSourceBase;lang.augmentObject(DS,{TYPE_UNKNOWN:-1,TYPE_JSARRAY:0,TYPE_JSFUNCTION:1,TYPE_XHR:2,TYPE_JSON:3,TYPE_XML:4,TYPE_TEXT:5,TYPE_HTMLTABLE:6,TYPE_SCRIPTNODE:7,TYPE_LOCAL:8,ERROR_DATAINVALID:"Invalid data",ERROR_DATANULL:"Null data",_nIndex:0,_nTransactionId:0,_cloneObject:function(o){if(!lang.isValue(o)){return o;}var copy={};if(Object.prototype.toString.apply(o)==="[object RegExp]"){copy=o;}else{if(lang.isFunction(o)){copy=o;}else{if(lang.isArray(o)){var array=[];for(var i=0,len=o.length;i<len;i++){array[i]=DS._cloneObject(o[i]);}copy=array;}else{if(lang.isObject(o)){for(var x in o){if(lang.hasOwnProperty(o,x)){if(lang.isValue(o[x])&&lang.isObject(o[x])||lang.isArray(o[x])){copy[x]=DS._cloneObject(o[x]);}else{copy[x]=o[x];}}}}else{copy=o;}}}}return copy;},_getLocationValue:function(field,context){var locator=field.locator||field.key||field,xmldoc=context.ownerDocument||context,result,res,value=null;try{if(!lang.isUndefined(xmldoc.evaluate)){result=xmldoc.evaluate(locator,context,xmldoc.createNSResolver(!context.ownerDocument?context.documentElement:context.ownerDocument.documentElement),0,null);while(res=result.iterateNext()){value=res.textContent;}}else{xmldoc.setProperty("SelectionLanguage","XPath");result=context.selectNodes(locator)[0];value=result.value||result.text||null;}return value;}catch(e){}},issueCallback:function(callback,params,error,scope){if(lang.isFunction(callback)){callback.apply(scope,params);}else{if(lang.isObject(callback)){scope=callback.scope||scope||window;var callbackFunc=callback.success;if(error){callbackFunc=callback.failure;}if(callbackFunc){callbackFunc.apply(scope,params.concat([callback.argument]));}}}},parseString:function(oData){if(!lang.isValue(oData)){return null;}var string=oData+"";if(lang.isString(string)){return string;}else{return null;}},parseNumber:function(oData){if(!lang.isValue(oData)||(oData==="")){return null;}var number=oData*1;if(lang.isNumber(number)){return number;}else{return null;}},convertNumber:function(oData){return DS.parseNumber(oData);},parseDate:function(oData){var date=null;if(lang.isValue(oData)&&!(oData instanceof Date)){date=new Date(oData);}else{return oData;}if(date instanceof Date){return date;}else{return null;}},convertDate:function(oData){return DS.parseDate(oData);}});DS.Parser={string:DS.parseString,number:DS.parseNumber,date:DS.parseDate};DS.prototype={_sName:null,_aCache:null,_oQueue:null,_aIntervals:null,maxCacheEntries:0,liveData:null,dataType:DS.TYPE_UNKNOWN,responseType:DS.TYPE_UNKNOWN,responseSchema:null,useXPath:false,cloneBeforeCaching:false,toString:function(){return this._sName;},getCachedResponse:function(oRequest,oCallback,oCaller){var aCache=this._aCache;if(this.maxCacheEntries>0){if(!aCache){this._aCache=[];}else{var nCacheLength=aCache.length;if(nCacheLength>0){var oResponse=null;this.fireEvent("cacheRequestEvent",{request:oRequest,callback:oCallback,caller:oCaller});for(var i=nCacheLength-1;i>=0;i--){var oCacheElem=aCache[i];if(this.isCacheHit(oRequest,oCacheElem.request)){oResponse=oCacheElem.response;this.fireEvent("cacheResponseEvent",{request:oRequest,response:oResponse,callback:oCallback,caller:oCaller});if(i<nCacheLength-1){aCache.splice(i,1);this.addToCache(oRequest,oResponse);}oResponse.cached=true;break;}}return oResponse;}}}else{if(aCache){this._aCache=null;}}return null;},isCacheHit:function(oRequest,oCachedRequest){return(oRequest===oCachedRequest);},addToCache:function(oRequest,oResponse){var aCache=this._aCache;if(!aCache){return;}while(aCache.length>=this.maxCacheEntries){aCache.shift();}oResponse=(this.cloneBeforeCaching)?DS._cloneObject(oResponse):oResponse;var oCacheElem={request:oRequest,response:oResponse};aCache[aCache.length]=oCacheElem;this.fireEvent("responseCacheEvent",{request:oRequest,response:oResponse});},flushCache:function(){if(this._aCache){this._aCache=[];this.fireEvent("cacheFlushEvent");}},setInterval:function(nMsec,oRequest,oCallback,oCaller){if(lang.isNumber(nMsec)&&(nMsec>=0)){var oSelf=this;var nId=setInterval(function(){oSelf.makeConnection(oRequest,oCallback,oCaller);},nMsec);this._aIntervals.push(nId);return nId;}else{}},clearInterval:function(nId){var tracker=this._aIntervals||[];for(var i=tracker.length-1;i>-1;i--){if(tracker[i]===nId){tracker.splice(i,1);clearInterval(nId);}}},clearAllIntervals:function(){var tracker=this._aIntervals||[];for(var i=tracker.length-1;i>-1;i--){clearInterval(tracker[i]);}tracker=[];},sendRequest:function(oRequest,oCallback,oCaller){var oCachedResponse=this.getCachedResponse(oRequest,oCallback,oCaller);if(oCachedResponse){DS.issueCallback(oCallback,[oRequest,oCachedResponse],false,oCaller);return null;}return this.makeConnection(oRequest,oCallback,oCaller);},makeConnection:function(oRequest,oCallback,oCaller){var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});var oRawResponse=this.liveData;this.handleResponse(oRequest,oRawResponse,oCallback,oCaller,tId);return tId;},handleResponse:function(oRequest,oRawResponse,oCallback,oCaller,tId){this.fireEvent("responseEvent",{tId:tId,request:oRequest,response:oRawResponse,callback:oCallback,caller:oCaller});
-var xhr=(this.dataType==DS.TYPE_XHR)?true:false;var oParsedResponse=null;var oFullResponse=oRawResponse;if(this.responseType===DS.TYPE_UNKNOWN){var ctype=(oRawResponse&&oRawResponse.getResponseHeader)?oRawResponse.getResponseHeader["Content-Type"]:null;if(ctype){if(ctype.indexOf("text/xml")>-1){this.responseType=DS.TYPE_XML;}else{if(ctype.indexOf("application/json")>-1){this.responseType=DS.TYPE_JSON;}else{if(ctype.indexOf("text/plain")>-1){this.responseType=DS.TYPE_TEXT;}}}}else{if(YAHOO.lang.isArray(oRawResponse)){this.responseType=DS.TYPE_JSARRAY;}else{if(oRawResponse&&oRawResponse.nodeType&&(oRawResponse.nodeType===9||oRawResponse.nodeType===1||oRawResponse.nodeType===11)){this.responseType=DS.TYPE_XML;}else{if(oRawResponse&&oRawResponse.nodeName&&(oRawResponse.nodeName.toLowerCase()=="table")){this.responseType=DS.TYPE_HTMLTABLE;}else{if(YAHOO.lang.isObject(oRawResponse)){this.responseType=DS.TYPE_JSON;}else{if(YAHOO.lang.isString(oRawResponse)){this.responseType=DS.TYPE_TEXT;}}}}}}}switch(this.responseType){case DS.TYPE_JSARRAY:if(xhr&&oRawResponse&&oRawResponse.responseText){oFullResponse=oRawResponse.responseText;}try{if(lang.isString(oFullResponse)){var parseArgs=[oFullResponse].concat(this.parseJSONArgs);if(lang.JSON){oFullResponse=lang.JSON.parse.apply(lang.JSON,parseArgs);}else{if(window.JSON&&JSON.parse){oFullResponse=JSON.parse.apply(JSON,parseArgs);}else{if(oFullResponse.parseJSON){oFullResponse=oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));}else{while(oFullResponse.length>0&&(oFullResponse.charAt(0)!="{")&&(oFullResponse.charAt(0)!="[")){oFullResponse=oFullResponse.substring(1,oFullResponse.length);}if(oFullResponse.length>0){var arrayEnd=Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));oFullResponse=oFullResponse.substring(0,arrayEnd+1);oFullResponse=eval("("+oFullResponse+")");}}}}}}catch(e1){}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseArrayData(oRequest,oFullResponse);break;case DS.TYPE_JSON:if(xhr&&oRawResponse&&oRawResponse.responseText){oFullResponse=oRawResponse.responseText;}try{if(lang.isString(oFullResponse)){var parseArgs=[oFullResponse].concat(this.parseJSONArgs);if(lang.JSON){oFullResponse=lang.JSON.parse.apply(lang.JSON,parseArgs);}else{if(window.JSON&&JSON.parse){oFullResponse=JSON.parse.apply(JSON,parseArgs);}else{if(oFullResponse.parseJSON){oFullResponse=oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));}else{while(oFullResponse.length>0&&(oFullResponse.charAt(0)!="{")&&(oFullResponse.charAt(0)!="[")){oFullResponse=oFullResponse.substring(1,oFullResponse.length);}if(oFullResponse.length>0){var objEnd=Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));oFullResponse=oFullResponse.substring(0,objEnd+1);oFullResponse=eval("("+oFullResponse+")");}}}}}}catch(e){}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseJSONData(oRequest,oFullResponse);break;case DS.TYPE_HTMLTABLE:if(xhr&&oRawResponse.responseText){var el=document.createElement("div");el.innerHTML=oRawResponse.responseText;oFullResponse=el.getElementsByTagName("table")[0];}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseHTMLTableData(oRequest,oFullResponse);break;case DS.TYPE_XML:if(xhr&&oRawResponse.responseXML){oFullResponse=oRawResponse.responseXML;}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseXMLData(oRequest,oFullResponse);break;case DS.TYPE_TEXT:if(xhr&&lang.isString(oRawResponse.responseText)){oFullResponse=oRawResponse.responseText;}oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseTextData(oRequest,oFullResponse);break;default:oFullResponse=this.doBeforeParseData(oRequest,oFullResponse,oCallback);oParsedResponse=this.parseData(oRequest,oFullResponse);break;}oParsedResponse=oParsedResponse||{};if(!oParsedResponse.results){oParsedResponse.results=[];}if(!oParsedResponse.meta){oParsedResponse.meta={};}if(!oParsedResponse.error){oParsedResponse=this.doBeforeCallback(oRequest,oFullResponse,oParsedResponse,oCallback);this.fireEvent("responseParseEvent",{request:oRequest,response:oParsedResponse,callback:oCallback,caller:oCaller});this.addToCache(oRequest,oParsedResponse);}else{oParsedResponse.error=true;this.fireEvent("dataErrorEvent",{request:oRequest,response:oRawResponse,callback:oCallback,caller:oCaller,message:DS.ERROR_DATANULL});}oParsedResponse.tId=tId;DS.issueCallback(oCallback,[oRequest,oParsedResponse],oParsedResponse.error,oCaller);},doBeforeParseData:function(oRequest,oFullResponse,oCallback){return oFullResponse;},doBeforeCallback:function(oRequest,oFullResponse,oParsedResponse,oCallback){return oParsedResponse;},parseData:function(oRequest,oFullResponse){if(lang.isValue(oFullResponse)){var oParsedResponse={results:oFullResponse,meta:{}};return oParsedResponse;}return null;},parseArrayData:function(oRequest,oFullResponse){if(lang.isArray(oFullResponse)){var results=[],i,j,rec,field,data;if(lang.isArray(this.responseSchema.fields)){var fields=this.responseSchema.fields;for(i=fields.length-1;i>=0;--i){if(typeof fields[i]!=="object"){fields[i]={key:fields[i]};}}var parsers={},p;for(i=fields.length-1;i>=0;--i){p=(typeof fields[i].parser==="function"?fields[i].parser:DS.Parser[fields[i].parser+""])||fields[i].converter;if(p){parsers[fields[i].key]=p;}}var arrType=lang.isArray(oFullResponse[0]);for(i=oFullResponse.length-1;i>-1;i--){var oResult={};rec=oFullResponse[i];if(typeof rec==="object"){for(j=fields.length-1;j>-1;j--){field=fields[j];data=arrType?rec[j]:rec[field.key];if(parsers[field.key]){data=parsers[field.key].call(this,data);}if(data===undefined){data=null;}oResult[field.key]=data;}}else{if(lang.isString(rec)){for(j=fields.length-1;j>-1;j--){field=fields[j];data=rec;if(parsers[field.key]){data=parsers[field.key].call(this,data);}if(data===undefined){data=null;}oResult[field.key]=data;
-}}}results[i]=oResult;}}else{results=oFullResponse;}var oParsedResponse={results:results};return oParsedResponse;}return null;},parseTextData:function(oRequest,oFullResponse){if(lang.isString(oFullResponse)){if(lang.isString(this.responseSchema.recordDelim)&&lang.isString(this.responseSchema.fieldDelim)){var oParsedResponse={results:[]};var recDelim=this.responseSchema.recordDelim;var fieldDelim=this.responseSchema.fieldDelim;if(oFullResponse.length>0){var newLength=oFullResponse.length-recDelim.length;if(oFullResponse.substr(newLength)==recDelim){oFullResponse=oFullResponse.substr(0,newLength);}if(oFullResponse.length>0){var recordsarray=oFullResponse.split(recDelim);for(var i=0,len=recordsarray.length,recIdx=0;i<len;++i){var bError=false,sRecord=recordsarray[i];if(lang.isString(sRecord)&&(sRecord.length>0)){var fielddataarray=recordsarray[i].split(fieldDelim);var oResult={};if(lang.isArray(this.responseSchema.fields)){var fields=this.responseSchema.fields;for(var j=fields.length-1;j>-1;j--){try{var data=fielddataarray[j];if(lang.isString(data)){if(data.charAt(0)=='"'){data=data.substr(1);}if(data.charAt(data.length-1)=='"'){data=data.substr(0,data.length-1);}var field=fields[j];var key=(lang.isValue(field.key))?field.key:field;if(!field.parser&&field.converter){field.parser=field.converter;}var parser=(typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""];if(parser){data=parser.call(this,data);}if(data===undefined){data=null;}oResult[key]=data;}else{bError=true;}}catch(e){bError=true;}}}else{oResult=fielddataarray;}if(!bError){oParsedResponse.results[recIdx++]=oResult;}}}}}return oParsedResponse;}}return null;},parseXMLResult:function(result){var oResult={},schema=this.responseSchema;try{for(var m=schema.fields.length-1;m>=0;m--){var field=schema.fields[m];var key=(lang.isValue(field.key))?field.key:field;var data=null;if(this.useXPath){data=YAHOO.util.DataSource._getLocationValue(field,result);}else{var xmlAttr=result.attributes.getNamedItem(key);if(xmlAttr){data=xmlAttr.value;}else{var xmlNode=result.getElementsByTagName(key);if(xmlNode&&xmlNode.item(0)){var item=xmlNode.item(0);data=(item)?((item.text)?item.text:(item.textContent)?item.textContent:null):null;if(!data){var datapieces=[];for(var j=0,len=item.childNodes.length;j<len;j++){if(item.childNodes[j].nodeValue){datapieces[datapieces.length]=item.childNodes[j].nodeValue;}}if(datapieces.length>0){data=datapieces.join("");}}}}}if(data===null){data="";}if(!field.parser&&field.converter){field.parser=field.converter;}var parser=(typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""];if(parser){data=parser.call(this,data);}if(data===undefined){data=null;}oResult[key]=data;}}catch(e){}return oResult;},parseXMLData:function(oRequest,oFullResponse){var bError=false,schema=this.responseSchema,oParsedResponse={meta:{}},xmlList=null,metaNode=schema.metaNode,metaLocators=schema.metaFields||{},i,k,loc,v;try{if(this.useXPath){for(k in metaLocators){oParsedResponse.meta[k]=YAHOO.util.DataSource._getLocationValue(metaLocators[k],oFullResponse);}}else{metaNode=metaNode?oFullResponse.getElementsByTagName(metaNode)[0]:oFullResponse;if(metaNode){for(k in metaLocators){if(lang.hasOwnProperty(metaLocators,k)){loc=metaLocators[k];v=metaNode.getElementsByTagName(loc)[0];if(v){v=v.firstChild.nodeValue;}else{v=metaNode.attributes.getNamedItem(loc);if(v){v=v.value;}}if(lang.isValue(v)){oParsedResponse.meta[k]=v;}}}}}xmlList=(schema.resultNode)?oFullResponse.getElementsByTagName(schema.resultNode):null;}catch(e){}if(!xmlList||!lang.isArray(schema.fields)){bError=true;}else{oParsedResponse.results=[];for(i=xmlList.length-1;i>=0;--i){var oResult=this.parseXMLResult(xmlList.item(i));oParsedResponse.results[i]=oResult;}}if(bError){oParsedResponse.error=true;}else{}return oParsedResponse;},parseJSONData:function(oRequest,oFullResponse){var oParsedResponse={results:[],meta:{}};if(lang.isObject(oFullResponse)&&this.responseSchema.resultsList){var schema=this.responseSchema,fields=schema.fields,resultsList=oFullResponse,results=[],metaFields=schema.metaFields||{},fieldParsers=[],fieldPaths=[],simpleFields=[],bError=false,i,len,j,v,key,parser,path;var buildPath=function(needle){var path=null,keys=[],i=0;if(needle){needle=needle.replace(/\[(['"])(.*?)\1\]/g,function(x,$1,$2){keys[i]=$2;return".@"+(i++);}).replace(/\[(\d+)\]/g,function(x,$1){keys[i]=parseInt($1,10)|0;return".@"+(i++);}).replace(/^\./,"");if(!/[^\w\.\$@]/.test(needle)){path=needle.split(".");for(i=path.length-1;i>=0;--i){if(path[i].charAt(0)==="@"){path[i]=keys[parseInt(path[i].substr(1),10)];}}}else{}}return path;};var walkPath=function(path,origin){var v=origin,i=0,len=path.length;for(;i<len&&v;++i){v=v[path[i]];}return v;};path=buildPath(schema.resultsList);if(path){resultsList=walkPath(path,oFullResponse);if(resultsList===undefined){bError=true;}}else{bError=true;}if(!resultsList){resultsList=[];}if(!lang.isArray(resultsList)){resultsList=[resultsList];}if(!bError){if(schema.fields){var field;for(i=0,len=fields.length;i<len;i++){field=fields[i];key=field.key||field;parser=((typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""])||field.converter;path=buildPath(key);if(parser){fieldParsers[fieldParsers.length]={key:key,parser:parser};}if(path){if(path.length>1){fieldPaths[fieldPaths.length]={key:key,path:path};}else{simpleFields[simpleFields.length]={key:key,path:path[0]};}}else{}}for(i=resultsList.length-1;i>=0;--i){var r=resultsList[i],rec={};if(r){for(j=simpleFields.length-1;j>=0;--j){rec[simpleFields[j].key]=(r[simpleFields[j].path]!==undefined)?r[simpleFields[j].path]:r[j];}for(j=fieldPaths.length-1;j>=0;--j){rec[fieldPaths[j].key]=walkPath(fieldPaths[j].path,r);}for(j=fieldParsers.length-1;j>=0;--j){var p=fieldParsers[j].key;rec[p]=fieldParsers[j].parser.call(this,rec[p]);if(rec[p]===undefined){rec[p]=null;}}}results[i]=rec;}}else{results=resultsList;}for(key in metaFields){if(lang.hasOwnProperty(metaFields,key)){path=buildPath(metaFields[key]);
-if(path){v=walkPath(path,oFullResponse);oParsedResponse.meta[key]=v;}}}}else{oParsedResponse.error=true;}oParsedResponse.results=results;}else{oParsedResponse.error=true;}return oParsedResponse;},parseHTMLTableData:function(oRequest,oFullResponse){var bError=false;var elTable=oFullResponse;var fields=this.responseSchema.fields;var oParsedResponse={results:[]};if(lang.isArray(fields)){for(var i=0;i<elTable.tBodies.length;i++){var elTbody=elTable.tBodies[i];for(var j=elTbody.rows.length-1;j>-1;j--){var elRow=elTbody.rows[j];var oResult={};for(var k=fields.length-1;k>-1;k--){var field=fields[k];var key=(lang.isValue(field.key))?field.key:field;var data=elRow.cells[k].innerHTML;if(!field.parser&&field.converter){field.parser=field.converter;}var parser=(typeof field.parser==="function")?field.parser:DS.Parser[field.parser+""];if(parser){data=parser.call(this,data);}if(data===undefined){data=null;}oResult[key]=data;}oParsedResponse.results[j]=oResult;}}}else{bError=true;}if(bError){oParsedResponse.error=true;}else{}return oParsedResponse;}};lang.augmentProto(DS,util.EventProvider);util.LocalDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_LOCAL;if(oLiveData){if(YAHOO.lang.isArray(oLiveData)){this.responseType=DS.TYPE_JSARRAY;}else{if(oLiveData.nodeType&&oLiveData.nodeType==9){this.responseType=DS.TYPE_XML;}else{if(oLiveData.nodeName&&(oLiveData.nodeName.toLowerCase()=="table")){this.responseType=DS.TYPE_HTMLTABLE;oLiveData=oLiveData.cloneNode(true);}else{if(YAHOO.lang.isString(oLiveData)){this.responseType=DS.TYPE_TEXT;}else{if(YAHOO.lang.isObject(oLiveData)){this.responseType=DS.TYPE_JSON;}}}}}}else{oLiveData=[];this.responseType=DS.TYPE_JSARRAY;}util.LocalDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.LocalDataSource,DS);lang.augmentObject(util.LocalDataSource,DS);util.FunctionDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_JSFUNCTION;oLiveData=oLiveData||function(){};util.FunctionDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.FunctionDataSource,DS,{scope:null,makeConnection:function(oRequest,oCallback,oCaller){var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});var oRawResponse=(this.scope)?this.liveData.call(this.scope,oRequest,this,oCallback):this.liveData(oRequest,oCallback);if(this.responseType===DS.TYPE_UNKNOWN){if(YAHOO.lang.isArray(oRawResponse)){this.responseType=DS.TYPE_JSARRAY;}else{if(oRawResponse&&oRawResponse.nodeType&&oRawResponse.nodeType==9){this.responseType=DS.TYPE_XML;}else{if(oRawResponse&&oRawResponse.nodeName&&(oRawResponse.nodeName.toLowerCase()=="table")){this.responseType=DS.TYPE_HTMLTABLE;}else{if(YAHOO.lang.isObject(oRawResponse)){this.responseType=DS.TYPE_JSON;}else{if(YAHOO.lang.isString(oRawResponse)){this.responseType=DS.TYPE_TEXT;}}}}}}this.handleResponse(oRequest,oRawResponse,oCallback,oCaller,tId);return tId;}});lang.augmentObject(util.FunctionDataSource,DS);util.ScriptNodeDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_SCRIPTNODE;oLiveData=oLiveData||"";util.ScriptNodeDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.ScriptNodeDataSource,DS,{getUtility:util.Get,asyncMode:"allowAll",scriptCallbackParam:"callback",generateRequestCallback:function(id){return"&"+this.scriptCallbackParam+"=YAHOO.util.ScriptNodeDataSource.callbacks["+id+"]";},doBeforeGetScriptNode:function(sUri){return sUri;},makeConnection:function(oRequest,oCallback,oCaller){var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});if(util.ScriptNodeDataSource._nPending===0){util.ScriptNodeDataSource.callbacks=[];util.ScriptNodeDataSource._nId=0;}var id=util.ScriptNodeDataSource._nId;util.ScriptNodeDataSource._nId++;var oSelf=this;util.ScriptNodeDataSource.callbacks[id]=function(oRawResponse){if((oSelf.asyncMode!=="ignoreStaleResponses")||(id===util.ScriptNodeDataSource.callbacks.length-1)){if(oSelf.responseType===DS.TYPE_UNKNOWN){if(YAHOO.lang.isArray(oRawResponse)){oSelf.responseType=DS.TYPE_JSARRAY;}else{if(oRawResponse.nodeType&&oRawResponse.nodeType==9){oSelf.responseType=DS.TYPE_XML;}else{if(oRawResponse.nodeName&&(oRawResponse.nodeName.toLowerCase()=="table")){oSelf.responseType=DS.TYPE_HTMLTABLE;}else{if(YAHOO.lang.isObject(oRawResponse)){oSelf.responseType=DS.TYPE_JSON;}else{if(YAHOO.lang.isString(oRawResponse)){oSelf.responseType=DS.TYPE_TEXT;}}}}}}oSelf.handleResponse(oRequest,oRawResponse,oCallback,oCaller,tId);}else{}delete util.ScriptNodeDataSource.callbacks[id];};util.ScriptNodeDataSource._nPending++;var sUri=this.liveData+oRequest+this.generateRequestCallback(id);sUri=this.doBeforeGetScriptNode(sUri);this.getUtility.script(sUri,{autopurge:true,onsuccess:util.ScriptNodeDataSource._bumpPendingDown,onfail:util.ScriptNodeDataSource._bumpPendingDown});return tId;}});lang.augmentObject(util.ScriptNodeDataSource,DS);lang.augmentObject(util.ScriptNodeDataSource,{_nId:0,_nPending:0,callbacks:[]});util.XHRDataSource=function(oLiveData,oConfigs){this.dataType=DS.TYPE_XHR;this.connMgr=this.connMgr||util.Connect;oLiveData=oLiveData||"";util.XHRDataSource.superclass.constructor.call(this,oLiveData,oConfigs);};lang.extend(util.XHRDataSource,DS,{connMgr:null,connXhrMode:"allowAll",connMethodPost:false,connTimeout:0,makeConnection:function(oRequest,oCallback,oCaller){var oRawResponse=null;var tId=DS._nTransactionId++;this.fireEvent("requestEvent",{tId:tId,request:oRequest,callback:oCallback,caller:oCaller});var oSelf=this;var oConnMgr=this.connMgr;var oQueue=this._oQueue;var _xhrSuccess=function(oResponse){if(oResponse&&(this.connXhrMode=="ignoreStaleResponses")&&(oResponse.tId!=oQueue.conn.tId)){return null;}else{if(!oResponse){this.fireEvent("dataErrorEvent",{request:oRequest,response:null,callback:oCallback,caller:oCaller,message:DS.ERROR_DATANULL});DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller);return null;
-}else{if(this.responseType===DS.TYPE_UNKNOWN){var ctype=(oResponse.getResponseHeader)?oResponse.getResponseHeader["Content-Type"]:null;if(ctype){if(ctype.indexOf("text/xml")>-1){this.responseType=DS.TYPE_XML;}else{if(ctype.indexOf("application/json")>-1){this.responseType=DS.TYPE_JSON;}else{if(ctype.indexOf("text/plain")>-1){this.responseType=DS.TYPE_TEXT;}}}}}this.handleResponse(oRequest,oResponse,oCallback,oCaller,tId);}}};var _xhrFailure=function(oResponse){this.fireEvent("dataErrorEvent",{request:oRequest,response:oResponse,callback:oCallback,caller:oCaller,message:DS.ERROR_DATAINVALID});if(lang.isString(this.liveData)&&lang.isString(oRequest)&&(this.liveData.lastIndexOf("?")!==this.liveData.length-1)&&(oRequest.indexOf("?")!==0)){}oResponse=oResponse||{};oResponse.error=true;DS.issueCallback(oCallback,[oRequest,oResponse],true,oCaller);return null;};var _xhrCallback={success:_xhrSuccess,failure:_xhrFailure,scope:this};if(lang.isNumber(this.connTimeout)){_xhrCallback.timeout=this.connTimeout;}if(this.connXhrMode=="cancelStaleRequests"){if(oQueue.conn){if(oConnMgr.abort){oConnMgr.abort(oQueue.conn);oQueue.conn=null;}else{}}}if(oConnMgr&&oConnMgr.asyncRequest){var sLiveData=this.liveData;var isPost=this.connMethodPost;var sMethod=(isPost)?"POST":"GET";var sUri=(isPost||!lang.isValue(oRequest))?sLiveData:sLiveData+oRequest;var sRequest=(isPost)?oRequest:null;if(this.connXhrMode!="queueRequests"){oQueue.conn=oConnMgr.asyncRequest(sMethod,sUri,_xhrCallback,sRequest);}else{if(oQueue.conn){var allRequests=oQueue.requests;allRequests.push({request:oRequest,callback:_xhrCallback});if(!oQueue.interval){oQueue.interval=setInterval(function(){if(oConnMgr.isCallInProgress(oQueue.conn)){return;}else{if(allRequests.length>0){sUri=(isPost||!lang.isValue(allRequests[0].request))?sLiveData:sLiveData+allRequests[0].request;sRequest=(isPost)?allRequests[0].request:null;oQueue.conn=oConnMgr.asyncRequest(sMethod,sUri,allRequests[0].callback,sRequest);allRequests.shift();}else{clearInterval(oQueue.interval);oQueue.interval=null;}}},50);}}else{oQueue.conn=oConnMgr.asyncRequest(sMethod,sUri,_xhrCallback,sRequest);}}}else{DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller);}return tId;}});lang.augmentObject(util.XHRDataSource,DS);util.DataSource=function(oLiveData,oConfigs){oConfigs=oConfigs||{};var dataType=oConfigs.dataType;if(dataType){if(dataType==DS.TYPE_LOCAL){return new util.LocalDataSource(oLiveData,oConfigs);}else{if(dataType==DS.TYPE_XHR){return new util.XHRDataSource(oLiveData,oConfigs);}else{if(dataType==DS.TYPE_SCRIPTNODE){return new util.ScriptNodeDataSource(oLiveData,oConfigs);}else{if(dataType==DS.TYPE_JSFUNCTION){return new util.FunctionDataSource(oLiveData,oConfigs);}}}}}if(YAHOO.lang.isString(oLiveData)){return new util.XHRDataSource(oLiveData,oConfigs);}else{if(YAHOO.lang.isFunction(oLiveData)){return new util.FunctionDataSource(oLiveData,oConfigs);}else{return new util.LocalDataSource(oLiveData,oConfigs);}}};lang.augmentObject(util.DataSource,DS);})();YAHOO.util.Number={format:function(e,k){if(e===""||e===null||!isFinite(e)){return"";}e=+e;k=YAHOO.lang.merge(YAHOO.util.Number.format.defaults,(k||{}));var j=e+"",l=Math.abs(e),b=k.decimalPlaces||0,r=k.thousandsSeparator,f=k.negativeFormat||("-"+k.format),q,p,g,h;if(f.indexOf("#")>-1){f=f.replace(/#/,k.format);}if(b<0){q=l-(l%1)+"";g=q.length+b;if(g>0){q=Number("."+q).toFixed(g).slice(2)+new Array(q.length-g+1).join("0");}else{q="0";}}else{var a=l+"";if(b>0||a.indexOf(".")>0){var d=Math.pow(10,b);q=Math.round(l*d)/d+"";var c=q.indexOf("."),m,o;if(c<0){m=b;o=(Math.pow(10,m)+"").substring(1);if(b>0){q=q+"."+o;}}else{m=b-(q.length-c-1);o=(Math.pow(10,m)+"").substring(1);q=q+o;}}else{q=l.toFixed(b)+"";}}p=q.split(/\D/);if(l>=1000){g=p[0].length%3||3;p[0]=p[0].slice(0,g)+p[0].slice(g).replace(/(\d{3})/g,r+"$1");}return YAHOO.util.Number.format._applyFormat((e<0?f:k.format),p.join(k.decimalSeparator),k);}};YAHOO.util.Number.format.defaults={format:"{prefix}{number}{suffix}",negativeFormat:null,decimalSeparator:".",decimalPlaces:null,thousandsSeparator:""};YAHOO.util.Number.format._applyFormat=function(a,b,c){return a.replace(/\{(\w+)\}/g,function(d,e){return e==="number"?b:e in c?c[e]:"";});};(function(){var a=function(c,e,d){if(typeof d==="undefined"){d=10;}for(;parseInt(c,10)<d&&d>1;d/=10){c=e.toString()+c;}return c.toString();};var b={formats:{a:function(e,c){return c.a[e.getDay()];},A:function(e,c){return c.A[e.getDay()];},b:function(e,c){return c.b[e.getMonth()];},B:function(e,c){return c.B[e.getMonth()];},C:function(c){return a(parseInt(c.getFullYear()/100,10),0);},d:["getDate","0"],e:["getDate"," "],g:function(c){return a(parseInt(b.formats.G(c)%100,10),0);},G:function(f){var g=f.getFullYear();var e=parseInt(b.formats.V(f),10);var c=parseInt(b.formats.W(f),10);if(c>e){g++;}else{if(c===0&&e>=52){g--;}}return g;},H:["getHours","0"],I:function(e){var c=e.getHours()%12;return a(c===0?12:c,0);},j:function(h){var g=new Date(""+h.getFullYear()+"/1/1 GMT");var e=new Date(""+h.getFullYear()+"/"+(h.getMonth()+1)+"/"+h.getDate()+" GMT");var c=e-g;var f=parseInt(c/60000/60/24,10)+1;return a(f,0,100);},k:["getHours"," "],l:function(e){var c=e.getHours()%12;return a(c===0?12:c," ");},m:function(c){return a(c.getMonth()+1,0);},M:["getMinutes","0"],p:function(e,c){return c.p[e.getHours()>=12?1:0];},P:function(e,c){return c.P[e.getHours()>=12?1:0];},s:function(e,c){return parseInt(e.getTime()/1000,10);},S:["getSeconds","0"],u:function(c){var e=c.getDay();return e===0?7:e;},U:function(g){var c=parseInt(b.formats.j(g),10);var f=6-g.getDay();var e=parseInt((c+f)/7,10);return a(e,0);},V:function(g){var f=parseInt(b.formats.W(g),10);var c=(new Date(""+g.getFullYear()+"/1/1")).getDay();var e=f+(c>4||c<=1?0:1);if(e===53&&(new Date(""+g.getFullYear()+"/12/31")).getDay()<4){e=1;}else{if(e===0){e=b.formats.V(new Date(""+(g.getFullYear()-1)+"/12/31"));}}return a(e,0);},w:"getDay",W:function(g){var c=parseInt(b.formats.j(g),10);var f=7-b.formats.u(g);var e=parseInt((c+f)/7,10);
-return a(e,0,10);},y:function(c){return a(c.getFullYear()%100,0);},Y:"getFullYear",z:function(f){var e=f.getTimezoneOffset();var c=a(parseInt(Math.abs(e/60),10),0);var g=a(Math.abs(e%60),0);return(e>0?"-":"+")+c+g;},Z:function(c){var e=c.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");if(e.length>4){e=b.formats.z(c);}return e;},"%":function(c){return"%";}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(g,f,d){f=f||{};if(!(g instanceof Date)){return YAHOO.lang.isValue(g)?g:"";}var h=f.format||"%m/%d/%Y";if(h==="YYYY/MM/DD"){h="%Y/%m/%d";}else{if(h==="DD/MM/YYYY"){h="%d/%m/%Y";}else{if(h==="MM/DD/YYYY"){h="%m/%d/%Y";}}}d=d||"en";if(!(d in YAHOO.util.DateLocale)){if(d.replace(/-[a-zA-Z]+$/,"") in YAHOO.util.DateLocale){d=d.replace(/-[a-zA-Z]+$/,"");}else{d="en";}}var j=YAHOO.util.DateLocale[d];var c=function(l,k){var m=b.aggregates[k];return(m==="locale"?j[k]:m);};var e=function(l,k){var m=b.formats[k];if(typeof m==="string"){return g[m]();}else{if(typeof m==="function"){return m.call(g,g,j);}else{if(typeof m==="object"&&typeof m[0]==="string"){return a(g[m[0]](),m[1]);}else{return k;}}}};while(h.match(/%[cDFhnrRtTxX]/)){h=h.replace(/%([cDFhnrRtTxX])/g,c);}var i=h.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,e);c=e=undefined;return i;}};YAHOO.namespace("YAHOO.util");YAHOO.util.Date=b;YAHOO.util.DateLocale={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};YAHOO.util.DateLocale["en"]=YAHOO.lang.merge(YAHOO.util.DateLocale,{});YAHOO.util.DateLocale["en-US"]=YAHOO.lang.merge(YAHOO.util.DateLocale["en"],{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});YAHOO.util.DateLocale["en-GB"]=YAHOO.lang.merge(YAHOO.util.DateLocale["en"],{r:"%l:%M:%S %P %Z"});YAHOO.util.DateLocale["en-AU"]=YAHOO.lang.merge(YAHOO.util.DateLocale["en"]);})();YAHOO.register("datasource",YAHOO.util.DataSource,{version:"2.9.0",build:"2800"});/*
-Copyright (c) 2011, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.com/yui/license.html
-version: 2.9.0
-*/
-YAHOO.util.Chain=function(){this.q=[].slice.call(arguments);this.createEvent("end");};YAHOO.util.Chain.prototype={id:0,run:function(){var g=this.q[0],d;if(!g){this.fireEvent("end");return this;}else{if(this.id){return this;}}d=g.method||g;if(typeof d==="function"){var f=g.scope||{},b=g.argument||[],a=g.timeout||0,e=this;if(!(b instanceof Array)){b=[b];}if(a<0){this.id=a;if(g.until){for(;!g.until();){d.apply(f,b);}}else{if(g.iterations){for(;g.iterations-->0;){d.apply(f,b);}}else{d.apply(f,b);}}this.q.shift();this.id=0;return this.run();}else{if(g.until){if(g.until()){this.q.shift();return this.run();}}else{if(!g.iterations||!--g.iterations){this.q.shift();}}this.id=setTimeout(function(){d.apply(f,b);if(e.id){e.id=0;e.run();}},a);}}return this;},add:function(a){this.q.push(a);return this;},pause:function(){if(this.id>0){clearTimeout(this.id);}this.id=0;return this;},stop:function(){this.pause();this.q=[];return this;}};YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);(function(){var a=YAHOO.util.Event,c=YAHOO.lang,b=[],d=function(h,e,f){var g;if(!h||h===f){g=false;}else{g=YAHOO.util.Selector.test(h,e)?h:d(h.parentNode,e,f);}return g;};c.augmentObject(a,{_createDelegate:function(f,e,g,h){return function(i){var j=this,n=a.getTarget(i),l=e,p=(j.nodeType===9),q,k,o,m;if(c.isFunction(e)){q=e(n);}else{if(c.isString(e)){if(!p){o=j.id;if(!o){o=a.generateId(j);}m=("#"+o+" ");l=(m+e).replace(/,/gi,(","+m));}if(YAHOO.util.Selector.test(n,l)){q=n;}else{if(YAHOO.util.Selector.test(n,((l.replace(/,/gi," *,"))+" *"))){q=d(n,l,j);}}}}if(q){k=q;if(h){if(h===true){k=g;}else{k=h;}}return f.call(k,i,q,j,g);}};},delegate:function(f,j,l,g,h,i){var e=j,k,m;if(c.isString(g)&&!YAHOO.util.Selector){return false;}if(j=="mouseenter"||j=="mouseleave"){if(!a._createMouseDelegate){return false;}e=a._getType(j);k=a._createMouseDelegate(l,h,i);m=a._createDelegate(function(p,o,n){return k.call(o,p,n);},g,h,i);}else{m=a._createDelegate(l,g,h,i);}b.push([f,e,l,m]);return a.on(f,e,m);},removeDelegate:function(f,j,i){var k=j,h=false,g,e;if(j=="mouseenter"||j=="mouseleave"){k=a._getType(j);}g=a._getCacheIndex(b,f,k,i);if(g>=0){e=b[g];}if(f&&e){h=a.removeListener(e[0],e[1],e[3]);if(h){delete b[g][2];delete b[g][3];b.splice(g,1);}}return h;}});}());(function(){var b=YAHOO.util.Event,g=YAHOO.lang,e=b.addListener,f=b.removeListener,c=b.getListeners,d=[],h={mouseenter:"mouseover",mouseleave:"mouseout"},a=function(n,m,l){var j=b._getCacheIndex(d,n,m,l),i,k;if(j>=0){i=d[j];}if(n&&i){k=f.call(b,i[0],m,i[3]);if(k){delete d[j][2];delete d[j][3];d.splice(j,1);}}return k;};g.augmentObject(b._specialTypes,h);g.augmentObject(b,{_createMouseDelegate:function(i,j,k){return function(q,m){var p=this,l=b.getRelatedTarget(q),o,n;if(p!=l&&!YAHOO.util.Dom.isAncestor(p,l)){o=p;if(k){if(k===true){o=j;}else{o=k;}}n=[q,j];if(m){n.splice(1,0,p,m);}return i.apply(o,n);}};},addListener:function(m,l,k,n,o){var i,j;if(h[l]){i=b._createMouseDelegate(k,n,o);i.mouseDelegate=true;d.push([m,l,k,i]);j=e.call(b,m,l,i);}else{j=e.apply(b,arguments);}return j;},removeListener:function(l,k,j){var i;if(h[k]){i=a.apply(b,arguments);}else{i=f.apply(b,arguments);}return i;},getListeners:function(p,o){var n=[],r,m=(o==="mouseover"||o==="mouseout"),q,k,j;if(o&&(m||h[o])){r=c.call(b,p,this._getType(o));if(r){for(k=r.length-1;k>-1;k--){j=r[k];q=j.fn.mouseDelegate;if((h[o]&&q)||(m&&!q)){n.push(j);}}}}else{n=c.apply(b,arguments);}return(n&&n.length)?n:null;}},true);b.on=b.addListener;}());YAHOO.register("event-mouseenter",YAHOO.util.Event,{version:"2.9.0",build:"2800"});var Y=YAHOO,Y_DOM=YAHOO.util.Dom,EMPTY_ARRAY=[],Y_UA=Y.env.ua,Y_Lang=Y.lang,Y_DOC=document,Y_DOCUMENT_ELEMENT=Y_DOC.documentElement,Y_DOM_inDoc=Y_DOM.inDocument,Y_mix=Y_Lang.augmentObject,Y_guid=Y_DOM.generateId,Y_getDoc=function(a){var b=Y_DOC;if(a){b=(a.nodeType===9)?a:a.ownerDocument||a.document||Y_DOC;}return b;},Y_Array=function(g,d){var c,b,h=d||0;try{return Array.prototype.slice.call(g,h);}catch(f){b=[];c=g.length;for(;h<c;h++){b.push(g[h]);}return b;}},Y_DOM_allById=function(f,a){a=a||Y_DOC;var b=[],c=[],d,e;if(a.querySelectorAll){c=a.querySelectorAll('[id="'+f+'"]');}else{if(a.all){b=a.all(f);if(b){if(b.nodeName){if(b.id===f){c.push(b);b=EMPTY_ARRAY;}else{b=[b];}}if(b.length){for(d=0;e=b[d++];){if(e.id===f||(e.attributes&&e.attributes.id&&e.attributes.id.value===f)){c.push(e);}}}}}else{c=[Y_getDoc(a).getElementById(f)];}}return c;};var COMPARE_DOCUMENT_POSITION="compareDocumentPosition",OWNER_DOCUMENT="ownerDocument",Selector={_foundCache:[],useNative:true,_compare:("sourceIndex" in Y_DOCUMENT_ELEMENT)?function(f,e){var d=f.sourceIndex,c=e.sourceIndex;if(d===c){return 0;}else{if(d>c){return 1;}}return -1;}:(Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION]?function(b,a){if(b[COMPARE_DOCUMENT_POSITION](a)&4){return -1;}else{return 1;}}:function(e,d){var c,a,b;if(e&&d){c=e[OWNER_DOCUMENT].createRange();c.setStart(e,0);a=d[OWNER_DOCUMENT].createRange();a.setStart(d,0);b=c.compareBoundaryPoints(1,a);}return b;}),_sort:function(a){if(a){a=Y_Array(a,0,true);if(a.sort){a.sort(Selector._compare);}}return a;},_deDupe:function(a){var b=[],c,d;for(c=0;(d=a[c++]);){if(!d._found){b[b.length]=d;d._found=true;}}for(c=0;(d=b[c++]);){d._found=null;d.removeAttribute("_found");}return b;},query:function(b,j,k,a){if(typeof j=="string"){j=Y_DOM.get(j);if(!j){return(k)?null:[];}}else{j=j||Y_DOC;}var f=[],c=(Selector.useNative&&Y_DOC.querySelector&&!a),e=[[b,j]],g,l,d,h=(c)?Selector._nativeQuery:Selector._bruteQuery;if(b&&h){if(!a&&(!c||j.tagName)){e=Selector._splitQueries(b,j);}for(d=0;(g=e[d++]);){l=h(g[0],g[1],k);if(!k){l=Y_Array(l,0,true);}if(l){f=f.concat(l);}}if(e.length>1){f=Selector._sort(Selector._deDupe(f));}}Y.log("query: "+b+" returning: "+f.length,"info","Selector");return(k)?(f[0]||null):f;},_splitQueries:function(c,f){var b=c.split(","),d=[],g="",e,a;if(f){if(f.tagName){f.id=f.id||Y_guid();g='[id="'+f.id+'"] ';}for(e=0,a=b.length;e<a;++e){c=g+b[e];d.push([c,f]);}}return d;},_nativeQuery:function(a,b,c){if(Y_UA.webkit&&a.indexOf(":checked")>-1&&(Selector.pseudos&&Selector.pseudos.checked)){return Selector.query(a,b,c,true);
-}try{return b["querySelector"+(c?"":"All")](a);}catch(d){return Selector.query(a,b,c,true);}},filter:function(b,a){var c=[],d,e;if(b&&a){for(d=0;(e=b[d++]);){if(Selector.test(e,a)){c[c.length]=e;}}}else{Y.log("invalid filter input (nodes: "+b+", selector: "+a+")","warn","Selector");}return c;},test:function(c,d,k){var g=false,b=d.split(","),a=false,l,o,h,n,f,e,m;if(c&&c.tagName){if(!k&&!Y_DOM_inDoc(c)){l=c.parentNode;if(l){k=l;}else{n=c[OWNER_DOCUMENT].createDocumentFragment();n.appendChild(c);k=n;a=true;}}k=k||c[OWNER_DOCUMENT];if(!c.id){c.id=Y_guid();}for(f=0;(m=b[f++]);){m+='[id="'+c.id+'"]';h=Selector.query(m,k);for(e=0;o=h[e++];){if(o===c){g=true;break;}}if(g){break;}}if(a){n.removeChild(c);}}return g;}};YAHOO.util.Selector=Selector;var PARENT_NODE="parentNode",TAG_NAME="tagName",ATTRIBUTES="attributes",COMBINATOR="combinator",PSEUDOS="pseudos",SelectorCSS2={_reRegExpTokens:/([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,SORT_RESULTS:true,_children:function(e,a){var b=e.children,d,c=[],f,g;if(e.children&&a&&e.children.tags){c=e.children.tags(a);}else{if((!b&&e[TAG_NAME])||(b&&a)){f=b||e.childNodes;b=[];for(d=0;(g=f[d++]);){if(g.tagName){if(!a||a===g.tagName){b.push(g);}}}}}return b||[];},_re:{attr:/(\[[^\]]*\])/g,esc:/\\[:\[\]\(\)#\.\'\>+~"]/gi,pseudos:/(\([^\)]*\))/g},shorthand:{"\\#(-?[_a-z]+[-\\w\\uE000]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w\\uE000]*)":"[className~=$1]"},operators:{"":function(b,a){return !!b.getAttribute(a);},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}(?:-|$)"},pseudos:{"first-child":function(a){return Selector._children(a[PARENT_NODE])[0]===a;}},_bruteQuery:function(f,j,l){var g=[],a=[],i=Selector._tokenize(f),e=i[i.length-1],k=Y_getDoc(j),c,b,h,d;if(e){b=e.id;h=e.className;d=e.tagName||"*";if(j.getElementsByTagName){if(b&&(j.all||(j.nodeType===9||Y_DOM_inDoc(j)))){a=Y_DOM_allById(b,j);}else{if(h){a=j.getElementsByClassName(h);}else{a=j.getElementsByTagName(d);}}}else{c=j.firstChild;while(c){if(c.tagName){a.push(c);}c=c.nextSilbing||c.firstChild;}}if(a.length){g=Selector._filterNodes(a,i,l);}}return g;},_filterNodes:function(l,f,h){var r=0,q,s=f.length,k=s-1,e=[],o=l[0],v=o,t=Selector.getters,d,p,c,g,a,m,b,u;for(r=0;(v=o=l[r++]);){k=s-1;g=null;testLoop:while(v&&v.tagName){c=f[k];b=c.tests;q=b.length;if(q&&!a){while((u=b[--q])){d=u[1];if(t[u[0]]){m=t[u[0]](v,u[0]);}else{m=v[u[0]];if(m===undefined&&v.getAttribute){m=v.getAttribute(u[0]);}}if((d==="="&&m!==u[2])||(typeof d!=="string"&&d.test&&!d.test(m))||(!d.test&&typeof d==="function"&&!d(v,u[0],u[2]))){if((v=v[g])){while(v&&(!v.tagName||(c.tagName&&c.tagName!==v.tagName))){v=v[g];}}continue testLoop;}}}k--;if(!a&&(p=c.combinator)){g=p.axis;v=v[g];while(v&&!v.tagName){v=v[g];}if(p.direct){g=null;}}else{e.push(o);if(h){return e;}break;}}}o=v=null;return e;},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:ATTRIBUTES,re:/^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,fn:function(d,e){var c=d[2]||"",a=Selector.operators,b=(d[3])?d[3].replace(/\\/g,""):"",f;if((d[1]==="id"&&c==="=")||(d[1]==="className"&&Y_DOCUMENT_ELEMENT.getElementsByClassName&&(c==="~="||c==="="))){e.prefilter=d[1];d[3]=b;e[d[1]]=(d[1]==="id")?d[3]:b;}if(c in a){f=a[c];if(typeof f==="string"){d[3]=b.replace(Selector._reRegExpTokens,"\\$1");f=new RegExp(f.replace("{val}",d[3]));}d[2]=f;}if(!e.last||e.prefilter!==d[1]){return d.slice(1);}}},{name:TAG_NAME,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(b,c){var a=b[1].toUpperCase();c.tagName=a;if(a!=="*"&&(!c.last||c.prefilter)){return[TAG_NAME,"=",a];}if(!c.prefilter){c.prefilter="tagName";}}},{name:COMBINATOR,re:/^\s*([>+~]|\s)\s*/,fn:function(a,b){}},{name:PSEUDOS,re:/^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,fn:function(a,b){var c=Selector[PSEUDOS][a[1]];if(c){if(a[2]){a[2]=a[2].replace(/\\/g,"");}return[a[2],c];}else{return false;}}}],_getToken:function(a){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(c){c=c||"";c=Selector._replaceShorthand(Y_Lang.trim(c));var b=Selector._getToken(),h=c,g=[],j=false,e,f,d,a;outer:do{j=false;for(d=0;(a=Selector._parsers[d++]);){if((e=a.re.exec(c))){if(a.name!==COMBINATOR){b.selector=c;}c=c.replace(e[0],"");if(!c.length){b.last=true;}if(Selector._attrFilters[e[1]]){e[1]=Selector._attrFilters[e[1]];}f=a.fn(e,b);if(f===false){j=false;break outer;}else{if(f){b.tests.push(f);}}if(!c.length||a.name===COMBINATOR){g.push(b);b=Selector._getToken(b);if(a.name===COMBINATOR){b.combinator=Selector.combinators[e[1]];}}j=true;}}}while(j&&c.length);if(!j||c.length){Y.log("query: "+h+" contains unsupported token in: "+c,"warn","Selector");g=[];}return g;},_replaceShorthand:function(b){var d=Selector.shorthand,c=b.match(Selector._re.esc),e,h,g,f,a;if(c){b=b.replace(Selector._re.esc,"\uE000");}e=b.match(Selector._re.attr);h=b.match(Selector._re.pseudos);if(e){b=b.replace(Selector._re.attr,"\uE001");}if(h){b=b.replace(Selector._re.pseudos,"\uE002");}for(g in d){if(d.hasOwnProperty(g)){b=b.replace(new RegExp(g,"gi"),d[g]);}}if(e){for(f=0,a=e.length;f<a;++f){b=b.replace(/\uE001/,e[f]);}}if(h){for(f=0,a=h.length;f<a;++f){b=b.replace(/\uE002/,h[f]);}}b=b.replace(/\[/g,"\uE003");b=b.replace(/\]/g,"\uE004");b=b.replace(/\(/g,"\uE005");b=b.replace(/\)/g,"\uE006");if(c){for(f=0,a=c.length;f<a;++f){b=b.replace("\uE000",c[f]);}}return b;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(b,a){return Y_DOM.getAttribute(b,a);}}};Y_mix(Selector,SelectorCSS2,true);Selector.getters.src=Selector.getters.rel=Selector.getters.href;if(Selector.useNative&&Y_DOC.querySelector){Selector.shorthand["\\.([^\\s\\\\(\\[:]*)"]="[class~=$1]";}Selector._reNth=/^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;Selector._getNth=function(d,o,q,h){Selector._reNth.test(o);var m=parseInt(RegExp.$1,10),c=RegExp.$2,j=RegExp.$3,k=parseInt(RegExp.$4,10)||0,p=[],l=Selector._children(d.parentNode,q),f;if(j){m=2;f="+";c="n";k=(j==="odd")?1:0;}else{if(isNaN(m)){m=(c)?1:0;
-}}if(m===0){if(h){k=l.length-k+1;}if(l[k-1]===d){return true;}else{return false;}}else{if(m<0){h=!!h;m=Math.abs(m);}}if(!h){for(var e=k-1,g=l.length;e<g;e+=m){if(e>=0&&l[e]===d){return true;}}}else{for(var e=l.length-k,g=l.length;e>=0;e-=m){if(e<g&&l[e]===d){return true;}}}return false;};Y_mix(Selector.pseudos,{"root":function(a){return a===a.ownerDocument.documentElement;},"nth-child":function(a,b){return Selector._getNth(a,b);},"nth-last-child":function(a,b){return Selector._getNth(a,b,null,true);},"nth-of-type":function(a,b){return Selector._getNth(a,b,a.tagName);},"nth-last-of-type":function(a,b){return Selector._getNth(a,b,a.tagName,true);},"last-child":function(b){var a=Selector._children(b.parentNode);return a[a.length-1]===b;},"first-of-type":function(a){return Selector._children(a.parentNode,a.tagName)[0]===a;},"last-of-type":function(b){var a=Selector._children(b.parentNode,b.tagName);return a[a.length-1]===b;},"only-child":function(b){var a=Selector._children(b.parentNode);return a.length===1&&a[0]===b;},"only-of-type":function(b){var a=Selector._children(b.parentNode,b.tagName);return a.length===1&&a[0]===b;},"empty":function(a){return a.childNodes.length===0;},"not":function(a,b){return !Selector.test(a,b);},"contains":function(a,b){var c=a.innerText||a.textContent||"";return c.indexOf(b)>-1;},"checked":function(a){return(a.checked===true||a.selected===true);},enabled:function(a){return(a.disabled!==undefined&&!a.disabled);},disabled:function(a){return(a.disabled);}});Y_mix(Selector.operators,{"^=":"^{val}","!=":function(b,a,c){return b[a]!==c;},"$=":"{val}$","*=":"{val}"});Selector.combinators["~"]={axis:"previousSibling"};YAHOO.register("selector",YAHOO.util.Selector,{version:"2.9.0",build:"2800"});var Dom=YAHOO.util.Dom;YAHOO.widget.ColumnSet=function(a){this._sId=Dom.generateId(null,"yui-cs");a=YAHOO.widget.DataTable._cloneObject(a);this._init(a);YAHOO.widget.ColumnSet._nCount++;};YAHOO.widget.ColumnSet._nCount=0;YAHOO.widget.ColumnSet.prototype={_sId:null,_aDefinitions:null,tree:null,flat:null,keys:null,headers:null,_init:function(j){var k=[];var a=[];var g=[];var e=[];var c=-1;var b=function(m,s){c++;if(!k[c]){k[c]=[];}for(var o=0;o<m.length;o++){var i=m[o];var q=new YAHOO.widget.Column(i);i.yuiColumnId=q._sId;a.push(q);if(s){q._oParent=s;}if(YAHOO.lang.isArray(i.children)){q.children=i.children;var r=0;var p=function(v){var w=v.children;for(var u=0;u<w.length;u++){if(YAHOO.lang.isArray(w[u].children)){p(w[u]);}else{r++;}}};p(i);q._nColspan=r;var t=i.children;for(var n=0;n<t.length;n++){var l=t[n];if(q.className&&(l.className===undefined)){l.className=q.className;}if(q.editor&&(l.editor===undefined)){l.editor=q.editor;}if(q.editorOptions&&(l.editorOptions===undefined)){l.editorOptions=q.editorOptions;}if(q.formatter&&(l.formatter===undefined)){l.formatter=q.formatter;}if(q.resizeable&&(l.resizeable===undefined)){l.resizeable=q.resizeable;}if(q.sortable&&(l.sortable===undefined)){l.sortable=q.sortable;}if(q.hidden){l.hidden=true;}if(q.width&&(l.width===undefined)){l.width=q.width;}if(q.minWidth&&(l.minWidth===undefined)){l.minWidth=q.minWidth;}if(q.maxAutoWidth&&(l.maxAutoWidth===undefined)){l.maxAutoWidth=q.maxAutoWidth;}if(q.type&&(l.type===undefined)){l.type=q.type;}if(q.type&&!q.formatter){q.formatter=q.type;}if(q.text&&!YAHOO.lang.isValue(q.label)){q.label=q.text;}if(q.parser){}if(q.sortOptions&&((q.sortOptions.ascFunction)||(q.sortOptions.descFunction))){}}if(!k[c+1]){k[c+1]=[];}b(t,q);}else{q._nKeyIndex=g.length;q._nColspan=1;g.push(q);}k[c].push(q);}c--;};if(YAHOO.lang.isArray(j)){b(j);this._aDefinitions=j;}else{return null;}var f;var d=function(l){var n=1;var q;var o;var r=function(t,p){p=p||1;for(var u=0;u<t.length;u++){var m=t[u];if(YAHOO.lang.isArray(m.children)){p++;r(m.children,p);p--;}else{if(p>n){n=p;}}}};for(var i=0;i<l.length;i++){q=l[i];r(q);for(var s=0;s<q.length;s++){o=q[s];if(!YAHOO.lang.isArray(o.children)){o._nRowspan=n;}else{o._nRowspan=1;}}n=1;}};d(k);for(f=0;f<k[0].length;f++){k[0][f]._nTreeIndex=f;}var h=function(l,m){e[l].push(m.getSanitizedKey());if(m._oParent){h(l,m._oParent);}};for(f=0;f<g.length;f++){e[f]=[];h(f,g[f]);e[f]=e[f].reverse();}this.tree=k;this.flat=a;this.keys=g;this.headers=e;},getId:function(){return this._sId;},toString:function(){return"ColumnSet instance "+this._sId;},getDefinitions:function(){var a=this._aDefinitions;var b=function(e,g){for(var d=0;d<e.length;d++){var f=e[d];var i=g.getColumnById(f.yuiColumnId);if(i){var h=i.getDefinition();for(var c in h){if(YAHOO.lang.hasOwnProperty(h,c)){f[c]=h[c];}}}if(YAHOO.lang.isArray(f.children)){b(f.children,g);}}};b(a,this);this._aDefinitions=a;return a;},getColumnById:function(c){if(YAHOO.lang.isString(c)){var a=this.flat;for(var b=a.length-1;b>-1;b--){if(a[b]._sId===c){return a[b];}}}return null;},getColumn:function(c){if(YAHOO.lang.isNumber(c)&&this.keys[c]){return this.keys[c];}else{if(YAHOO.lang.isString(c)){var a=this.flat;var d=[];for(var b=0;b<a.length;b++){if(a[b].key===c){d.push(a[b]);}}if(d.length===1){return d[0];}else{if(d.length>1){return d;}}}}return null;},getDescendants:function(d){var b=this;var c=[];var a;var e=function(f){c.push(f);if(f.children){for(a=0;a<f.children.length;a++){e(b.getColumn(f.children[a].key));}}};e(d);return c;}};YAHOO.widget.Column=function(b){this._sId=Dom.generateId(null,"yui-col");if(b&&YAHOO.lang.isObject(b)){for(var a in b){if(a){this[a]=b[a];}}}if(!YAHOO.lang.isValue(this.key)){this.key=Dom.generateId(null,"yui-dt-col");}if(!YAHOO.lang.isValue(this.field)){this.field=this.key;}YAHOO.widget.Column._nCount++;if(this.width&&!YAHOO.lang.isNumber(this.width)){this.width=null;}if(this.editor&&YAHOO.lang.isString(this.editor)){this.editor=new YAHOO.widget.CellEditor(this.editor,this.editorOptions);}};YAHOO.lang.augmentObject(YAHOO.widget.Column,{_nCount:0,formatCheckbox:function(b,a,c,d){YAHOO.widget.DataTable.formatCheckbox(b,a,c,d);},formatCurrency:function(b,a,c,d){YAHOO.widget.DataTable.formatCurrency(b,a,c,d);},formatDate:function(b,a,c,d){YAHOO.widget.DataTable.formatDate(b,a,c,d);
-},formatEmail:function(b,a,c,d){YAHOO.widget.DataTable.formatEmail(b,a,c,d);},formatLink:function(b,a,c,d){YAHOO.widget.DataTable.formatLink(b,a,c,d);},formatNumber:function(b,a,c,d){YAHOO.widget.DataTable.formatNumber(b,a,c,d);},formatSelect:function(b,a,c,d){YAHOO.widget.DataTable.formatDropdown(b,a,c,d);}});YAHOO.widget.Column.prototype={_sId:null,_nKeyIndex:null,_nTreeIndex:null,_nColspan:1,_nRowspan:1,_oParent:null,_elTh:null,_elThLiner:null,_elThLabel:null,_elResizer:null,_nWidth:null,_dd:null,_ddResizer:null,key:null,field:null,label:null,abbr:null,children:null,width:null,minWidth:null,maxAutoWidth:null,hidden:false,selected:false,className:null,formatter:null,currencyOptions:null,dateOptions:null,dropdownOptions:null,editor:null,resizeable:false,sortable:false,sortOptions:null,getId:function(){return this._sId;},toString:function(){return"Column instance "+this._sId;},getDefinition:function(){var a={};a.abbr=this.abbr;a.className=this.className;a.editor=this.editor;a.editorOptions=this.editorOptions;a.field=this.field;a.formatter=this.formatter;a.hidden=this.hidden;a.key=this.key;a.label=this.label;a.minWidth=this.minWidth;a.maxAutoWidth=this.maxAutoWidth;a.resizeable=this.resizeable;a.selected=this.selected;a.sortable=this.sortable;a.sortOptions=this.sortOptions;a.width=this.width;a._calculatedWidth=this._calculatedWidth;return a;},getKey:function(){return this.key;},getField:function(){return this.field;},getSanitizedKey:function(){return this.getKey().replace(/[^\w\-]/g,"");},getKeyIndex:function(){return this._nKeyIndex;},getTreeIndex:function(){return this._nTreeIndex;},getParent:function(){return this._oParent;},getColspan:function(){return this._nColspan;},getColSpan:function(){return this.getColspan();},getRowspan:function(){return this._nRowspan;},getThEl:function(){return this._elTh;},getThLinerEl:function(){return this._elThLiner;},getResizerEl:function(){return this._elResizer;},getColEl:function(){return this.getThEl();},getIndex:function(){return this.getKeyIndex();},format:function(){}};YAHOO.util.Sort={compare:function(d,c,e){if((d===null)||(typeof d=="undefined")){if((c===null)||(typeof c=="undefined")){return 0;}else{return 1;}}else{if((c===null)||(typeof c=="undefined")){return -1;}}if(d.constructor==String){d=d.toLowerCase();}if(c.constructor==String){c=c.toLowerCase();}if(d<c){return(e)?1:-1;}else{if(d>c){return(e)?-1:1;}else{return 0;}}}};YAHOO.widget.ColumnDD=function(d,a,c,b){if(d&&a&&c&&b){this.datatable=d;this.table=d.getTableEl();this.column=a;this.headCell=c;this.pointer=b;this.newIndex=null;this.init(c);this.initFrame();this.invalidHandleTypes={};this.setPadding(10,0,(this.datatable.getTheadEl().offsetHeight+10),0);YAHOO.util.Event.on(window,"resize",function(){this.initConstraints();},this,true);}else{}};if(YAHOO.util.DDProxy){YAHOO.extend(YAHOO.widget.ColumnDD,YAHOO.util.DDProxy,{initConstraints:function(){var g=YAHOO.util.Dom.getRegion(this.table),d=this.getEl(),f=YAHOO.util.Dom.getXY(d),c=parseInt(YAHOO.util.Dom.getStyle(d,"width"),10),a=parseInt(YAHOO.util.Dom.getStyle(d,"height"),10),e=((f[0]-g.left)+15),b=((g.right-f[0]-c)+15);this.setXConstraint(e,b);this.setYConstraint(10,10);},_resizeProxy:function(){YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this,arguments);var a=this.getDragEl(),b=this.getEl();YAHOO.util.Dom.setStyle(this.pointer,"height",(this.table.parentNode.offsetHeight+10)+"px");YAHOO.util.Dom.setStyle(this.pointer,"display","block");var c=YAHOO.util.Dom.getXY(b);YAHOO.util.Dom.setXY(this.pointer,[c[0],(c[1]-5)]);YAHOO.util.Dom.setStyle(a,"height",this.datatable.getContainerEl().offsetHeight+"px");YAHOO.util.Dom.setStyle(a,"width",(parseInt(YAHOO.util.Dom.getStyle(a,"width"),10)+4)+"px");YAHOO.util.Dom.setXY(this.dragEl,c);},onMouseDown:function(){this.initConstraints();this.resetConstraints();},clickValidator:function(b){if(!this.column.hidden){var a=YAHOO.util.Event.getTarget(b);return(this.isValidHandleChild(a)&&(this.id==this.handleElId||this.DDM.handleWasClicked(a,this.id)));}},onDragOver:function(h,a){var f=this.datatable.getColumn(a);if(f){var c=f.getTreeIndex();while((c===null)&&f.getParent()){f=f.getParent();c=f.getTreeIndex();}if(c!==null){var b=f.getThEl();var k=c;var d=YAHOO.util.Event.getPageX(h),i=YAHOO.util.Dom.getX(b),j=i+((YAHOO.util.Dom.get(b).offsetWidth)/2),e=this.column.getTreeIndex();if(d<j){YAHOO.util.Dom.setX(this.pointer,i);}else{var g=parseInt(b.offsetWidth,10);YAHOO.util.Dom.setX(this.pointer,(i+g));k++;}if(c>e){k--;}if(k<0){k=0;}else{if(k>this.datatable.getColumnSet().tree[0].length){k=this.datatable.getColumnSet().tree[0].length;}}this.newIndex=k;}}},onDragDrop:function(){this.datatable.reorderColumn(this.column,this.newIndex);},endDrag:function(){this.newIndex=null;YAHOO.util.Dom.setStyle(this.pointer,"display","none");}});}YAHOO.util.ColumnResizer=function(e,c,d,a,b){if(e&&c&&d&&a){this.datatable=e;this.column=c;this.headCell=d;this.headCellLiner=c.getThLinerEl();this.resizerLiner=d.firstChild;this.init(a,a,{dragOnly:true,dragElId:b.id});this.initFrame();this.resetResizerEl();this.setPadding(0,1,0,0);}else{}};if(YAHOO.util.DD){YAHOO.extend(YAHOO.util.ColumnResizer,YAHOO.util.DDProxy,{resetResizerEl:function(){var a=YAHOO.util.Dom.get(this.handleElId).style;a.left="auto";a.right=0;a.top="auto";a.bottom=0;a.height=this.headCell.offsetHeight+"px";},onMouseUp:function(h){var f=this.datatable.getColumnSet().keys,b;for(var c=0,a=f.length;c<a;c++){b=f[c];if(b._ddResizer){b._ddResizer.resetResizerEl();}}this.resetResizerEl();var d=this.headCellLiner;var g=d.offsetWidth-(parseInt(YAHOO.util.Dom.getStyle(d,"paddingLeft"),10)|0)-(parseInt(YAHOO.util.Dom.getStyle(d,"paddingRight"),10)|0);this.datatable.fireEvent("columnResizeEvent",{column:this.column,target:this.headCell,width:g});},onMouseDown:function(a){this.startWidth=this.headCellLiner.offsetWidth;this.startX=YAHOO.util.Event.getXY(a)[0];this.nLinerPadding=(parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0)+(parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
-},clickValidator:function(b){if(!this.column.hidden){var a=YAHOO.util.Event.getTarget(b);return(this.isValidHandleChild(a)&&(this.id==this.handleElId||this.DDM.handleWasClicked(a,this.id)));}},startDrag:function(){var e=this.datatable.getColumnSet().keys,d=this.column.getKeyIndex(),b;for(var c=0,a=e.length;c<a;c++){b=e[c];if(b._ddResizer){YAHOO.util.Dom.get(b._ddResizer.handleElId).style.height="1em";}}},onDrag:function(c){var d=YAHOO.util.Event.getXY(c)[0];if(d>YAHOO.util.Dom.getX(this.headCellLiner)){var a=d-this.startX;var b=this.startWidth+a-this.nLinerPadding;if(b>0){this.datatable.setColumnWidth(this.column,b);}}}});}(function(){var g=YAHOO.lang,a=YAHOO.util,e=YAHOO.widget,c=a.Dom,f=a.Event,d=e.DataTable;YAHOO.widget.RecordSet=function(h){this._init(h);};var b=e.RecordSet;b._nCount=0;b.prototype={_sId:null,_init:function(h){this._sId=c.generateId(null,"yui-rs");e.RecordSet._nCount++;this._records=[];this._initEvents();if(h){if(g.isArray(h)){this.addRecords(h);}else{if(g.isObject(h)){this.addRecord(h);}}}},_initEvents:function(){this.createEvent("recordAddEvent");this.createEvent("recordsAddEvent");this.createEvent("recordSetEvent");this.createEvent("recordsSetEvent");this.createEvent("recordUpdateEvent");this.createEvent("recordDeleteEvent");this.createEvent("recordsDeleteEvent");this.createEvent("resetEvent");this.createEvent("recordValueUpdateEvent");},_addRecord:function(j,h){var i=new YAHOO.widget.Record(j);if(YAHOO.lang.isNumber(h)&&(h>-1)){this._records.splice(h,0,i);}else{this._records[this._records.length]=i;}return i;},_setRecord:function(i,h){if(!g.isNumber(h)||h<0){h=this._records.length;}return(this._records[h]=new e.Record(i));},_deleteRecord:function(i,h){if(!g.isNumber(h)||(h<0)){h=1;}this._records.splice(i,h);},getId:function(){return this._sId;},toString:function(){return"RecordSet instance "+this._sId;},getLength:function(){return this._records.length;},getRecord:function(h){var j;if(h instanceof e.Record){for(j=0;j<this._records.length;j++){if(this._records[j]&&(this._records[j]._sId===h._sId)){return h;}}}else{if(g.isNumber(h)){if((h>-1)&&(h<this.getLength())){return this._records[h];}}else{if(g.isString(h)){for(j=0;j<this._records.length;j++){if(this._records[j]&&(this._records[j]._sId===h)){return this._records[j];}}}}}return null;},getRecords:function(i,h){if(!g.isNumber(i)){return this._records;}if(!g.isNumber(h)){return this._records.slice(i);}return this._records.slice(i,i+h);},hasRecords:function(j,h){var l=this.getRecords(j,h);for(var k=0;k<h;++k){if(typeof l[k]==="undefined"){return false;}}return true;},getRecordIndex:function(j){if(j){for(var h=this._records.length-1;h>-1;h--){if(this._records[h]&&j.getId()===this._records[h].getId()){return h;}}}return null;},addRecord:function(j,h){if(g.isObject(j)){var i=this._addRecord(j,h);this.fireEvent("recordAddEvent",{record:i,data:j});return i;}else{return null;}},addRecords:function(m,l){if(g.isArray(m)){var p=[],j,n,h;l=g.isNumber(l)?l:this._records.length;j=l;for(n=0,h=m.length;n<h;++n){if(g.isObject(m[n])){var k=this._addRecord(m[n],j++);p.push(k);}}this.fireEvent("recordsAddEvent",{records:p,data:m});return p;}else{if(g.isObject(m)){var o=this._addRecord(m);this.fireEvent("recordsAddEvent",{records:[o],data:m});return o;}else{return null;}}},setRecord:function(j,h){if(g.isObject(j)){var i=this._setRecord(j,h);this.fireEvent("recordSetEvent",{record:i,data:j});return i;}else{return null;}},setRecords:function(o,n){var r=e.Record,k=g.isArray(o)?o:[o],q=[],p=0,h=k.length,m=0;n=parseInt(n,10)|0;for(;p<h;++p){if(typeof k[p]==="object"&&k[p]){q[m++]=this._records[n+p]=new r(k[p]);}}this.fireEvent("recordsSetEvent",{records:q,data:o});this.fireEvent("recordsSet",{records:q,data:o});if(k.length&&!q.length){}return q;},updateRecord:function(h,l){var j=this.getRecord(h);if(j&&g.isObject(l)){var k={};for(var i in j._oData){if(g.hasOwnProperty(j._oData,i)){k[i]=j._oData[i];}}j._oData=l;this.fireEvent("recordUpdateEvent",{record:j,newData:l,oldData:k});return j;}else{return null;}},updateKey:function(h,i,j){this.updateRecordValue(h,i,j);},updateRecordValue:function(h,k,n){var j=this.getRecord(h);if(j){var m=null;var l=j._oData[k];if(l&&g.isObject(l)){m={};for(var i in l){if(g.hasOwnProperty(l,i)){m[i]=l[i];}}}else{m=l;}j._oData[k]=n;this.fireEvent("keyUpdateEvent",{record:j,key:k,newData:n,oldData:m});this.fireEvent("recordValueUpdateEvent",{record:j,key:k,newData:n,oldData:m});}else{}},replaceRecords:function(h){this.reset();return this.addRecords(h);},sortRecords:function(h,j,i){return this._records.sort(function(l,k){return h(l,k,j,i);});},reverseRecords:function(){return this._records.reverse();},deleteRecord:function(h){if(g.isNumber(h)&&(h>-1)&&(h<this.getLength())){var i=this.getRecord(h).getData();this._deleteRecord(h);this.fireEvent("recordDeleteEvent",{data:i,index:h});return i;}else{return null;}},deleteRecords:function(k,h){if(!g.isNumber(h)){h=1;}if(g.isNumber(k)&&(k>-1)&&(k<this.getLength())){var m=this.getRecords(k,h);var j=[],n=[];for(var l=0;l<m.length;l++){j[j.length]=m[l];n[n.length]=m[l].getData();}this._deleteRecord(k,h);this.fireEvent("recordsDeleteEvent",{data:j,deletedData:n,index:k});return j;}else{return null;}},reset:function(){this._records=[];this.fireEvent("resetEvent");}};g.augmentProto(b,a.EventProvider);YAHOO.widget.Record=function(h){this._nCount=e.Record._nCount;this._sId=c.generateId(null,"yui-rec");e.Record._nCount++;this._oData={};if(g.isObject(h)){for(var i in h){if(g.hasOwnProperty(h,i)){this._oData[i]=h[i];}}}};YAHOO.widget.Record._nCount=0;YAHOO.widget.Record.prototype={_nCount:null,_sId:null,_oData:null,getCount:function(){return this._nCount;},getId:function(){return this._sId;},getData:function(h){if(g.isString(h)){return this._oData[h];}else{return this._oData;}},setData:function(h,i){this._oData[h]=i;}};})();(function(){var h=YAHOO.lang,a=YAHOO.util,e=YAHOO.widget,b=YAHOO.env.ua,c=a.Dom,g=a.Event,f=a.DataSourceBase;YAHOO.widget.DataTable=function(i,m,o,k){var l=e.DataTable;
-if(k&&k.scrollable){return new YAHOO.widget.ScrollingDataTable(i,m,o,k);}this._nIndex=l._nCount;this._sId=c.generateId(null,"yui-dt");this._oChainRender=new YAHOO.util.Chain();this._oChainRender.subscribe("end",this._onRenderChainEnd,this,true);this._initConfigs(k);this._initDataSource(o);if(!this._oDataSource){return;}this._initColumnSet(m);if(!this._oColumnSet){return;}this._initRecordSet();if(!this._oRecordSet){}l.superclass.constructor.call(this,i,this.configs);var q=this._initDomElements(i);if(!q){return;}this.showTableMessage(this.get("MSG_LOADING"),l.CLASS_LOADING);this._initEvents();l._nCount++;l._nCurrentCount++;var n={success:this.onDataReturnSetRows,failure:this.onDataReturnSetRows,scope:this,argument:this.getState()};var p=this.get("initialLoad");if(p===true){this._oDataSource.sendRequest(this.get("initialRequest"),n);}else{if(p===false){this.showTableMessage(this.get("MSG_EMPTY"),l.CLASS_EMPTY);}else{var j=p||{};n.argument=j.argument||{};this._oDataSource.sendRequest(j.request,n);}}};var d=e.DataTable;h.augmentObject(d,{CLASS_DATATABLE:"yui-dt",CLASS_LINER:"yui-dt-liner",CLASS_LABEL:"yui-dt-label",CLASS_MESSAGE:"yui-dt-message",CLASS_MASK:"yui-dt-mask",CLASS_DATA:"yui-dt-data",CLASS_COLTARGET:"yui-dt-coltarget",CLASS_RESIZER:"yui-dt-resizer",CLASS_RESIZERLINER:"yui-dt-resizerliner",CLASS_RESIZERPROXY:"yui-dt-resizerproxy",CLASS_EDITOR:"yui-dt-editor",CLASS_EDITOR_SHIM:"yui-dt-editor-shim",CLASS_PAGINATOR:"yui-dt-paginator",CLASS_PAGE:"yui-dt-page",CLASS_DEFAULT:"yui-dt-default",CLASS_PREVIOUS:"yui-dt-previous",CLASS_NEXT:"yui-dt-next",CLASS_FIRST:"yui-dt-first",CLASS_LAST:"yui-dt-last",CLASS_REC:"yui-dt-rec",CLASS_EVEN:"yui-dt-even",CLASS_ODD:"yui-dt-odd",CLASS_SELECTED:"yui-dt-selected",CLASS_HIGHLIGHTED:"yui-dt-highlighted",CLASS_HIDDEN:"yui-dt-hidden",CLASS_DISABLED:"yui-dt-disabled",CLASS_EMPTY:"yui-dt-empty",CLASS_LOADING:"yui-dt-loading",CLASS_ERROR:"yui-dt-error",CLASS_EDITABLE:"yui-dt-editable",CLASS_DRAGGABLE:"yui-dt-draggable",CLASS_RESIZEABLE:"yui-dt-resizeable",CLASS_SCROLLABLE:"yui-dt-scrollable",CLASS_SORTABLE:"yui-dt-sortable",CLASS_ASC:"yui-dt-asc",CLASS_DESC:"yui-dt-desc",CLASS_BUTTON:"yui-dt-button",CLASS_CHECKBOX:"yui-dt-checkbox",CLASS_DROPDOWN:"yui-dt-dropdown",CLASS_RADIO:"yui-dt-radio",_nCount:0,_nCurrentCount:0,_elDynStyleNode:null,_bDynStylesFallback:(b.ie)?true:false,_oDynStyles:{},_cloneObject:function(m){if(!h.isValue(m)){return m;}var p={};if(m instanceof YAHOO.widget.BaseCellEditor){p=m;}else{if(Object.prototype.toString.apply(m)==="[object RegExp]"){p=m;}else{if(h.isFunction(m)){p=m;}else{if(h.isArray(m)){var n=[];for(var l=0,k=m.length;l<k;l++){n[l]=d._cloneObject(m[l]);}p=n;}else{if(h.isObject(m)){for(var j in m){if(h.hasOwnProperty(m,j)){if(h.isValue(m[j])&&h.isObject(m[j])||h.isArray(m[j])){p[j]=d._cloneObject(m[j]);}else{p[j]=m[j];}}}}else{p=m;}}}}}return p;},formatButton:function(i,j,k,n,m){var l=h.isValue(n)?n:"Click";i.innerHTML='<button type="button" class="'+d.CLASS_BUTTON+'">'+l+"</button>";},formatCheckbox:function(i,j,k,n,m){var l=n;l=(l)?' checked="checked"':"";i.innerHTML='<input type="checkbox"'+l+' class="'+d.CLASS_CHECKBOX+'" />';},formatCurrency:function(j,k,l,n,m){var i=m||this;j.innerHTML=a.Number.format(n,l.currencyOptions||i.get("currencyOptions"));},formatDate:function(j,l,m,o,n){var i=n||this,k=m.dateOptions||i.get("dateOptions");j.innerHTML=a.Date.format(o,k,k.locale);},formatDropdown:function(l,u,q,j,t){var s=t||this,r=(h.isValue(j))?j:u.getData(q.field),v=(h.isArray(q.dropdownOptions))?q.dropdownOptions:null,k,p=l.getElementsByTagName("select");if(p.length===0){k=document.createElement("select");k.className=d.CLASS_DROPDOWN;k=l.appendChild(k);g.addListener(k,"change",s._onDropdownChange,s);}k=p[0];if(k){k.innerHTML="";if(v){for(var n=0;n<v.length;n++){var o=v[n];var m=document.createElement("option");m.value=(h.isValue(o.value))?o.value:o;m.innerHTML=(h.isValue(o.text))?o.text:(h.isValue(o.label))?o.label:o;m=k.appendChild(m);if(m.value==r){m.selected=true;}}}else{k.innerHTML='<option selected value="'+r+'">'+r+"</option>";}}else{l.innerHTML=h.isValue(j)?j:"";}},formatEmail:function(i,j,k,m,l){if(h.isString(m)){m=h.escapeHTML(m);i.innerHTML='<a href="mailto:'+m+'">'+m+"</a>";}else{i.innerHTML=h.isValue(m)?h.escapeHTML(m.toString()):"";}},formatLink:function(i,j,k,m,l){if(h.isString(m)){m=h.escapeHTML(m);i.innerHTML='<a href="'+m+'">'+m+"</a>";}else{i.innerHTML=h.isValue(m)?h.escapeHTML(m.toString()):"";}},formatNumber:function(j,k,l,n,m){var i=m||this;j.innerHTML=a.Number.format(n,l.numberOptions||i.get("numberOptions"));},formatRadio:function(j,k,l,o,n){var i=n||this,m=o;m=(m)?' checked="checked"':"";j.innerHTML='<input type="radio"'+m+' name="'+i.getId()+"-col-"+l.getSanitizedKey()+'"'+' class="'+d.CLASS_RADIO+'" />';},formatText:function(i,j,l,n,m){var k=(h.isValue(n))?n:"";i.innerHTML=h.escapeHTML(k.toString());},formatTextarea:function(j,k,m,o,n){var l=(h.isValue(o))?h.escapeHTML(o.toString()):"",i="<textarea>"+l+"</textarea>";j.innerHTML=i;},formatTextbox:function(j,k,m,o,n){var l=(h.isValue(o))?h.escapeHTML(o.toString()):"",i='<input type="text" value="'+l+'" />';j.innerHTML=i;},formatDefault:function(i,j,k,m,l){i.innerHTML=(h.isValue(m)&&m!=="")?m.toString():"&#160;";},validateNumber:function(j){var i=j*1;if(h.isNumber(i)){return i;}else{return undefined;}}});d.Formatter={button:d.formatButton,checkbox:d.formatCheckbox,currency:d.formatCurrency,"date":d.formatDate,dropdown:d.formatDropdown,email:d.formatEmail,link:d.formatLink,"number":d.formatNumber,radio:d.formatRadio,text:d.formatText,textarea:d.formatTextarea,textbox:d.formatTextbox,defaultFormatter:d.formatDefault};h.extend(d,a.Element,{initAttributes:function(i){i=i||{};d.superclass.initAttributes.call(this,i);this.setAttributeConfig("summary",{value:"",validator:h.isString,method:function(j){if(this._elTable){this._elTable.summary=j;}}});this.setAttributeConfig("selectionMode",{value:"standard",validator:h.isString});this.setAttributeConfig("sortedBy",{value:null,validator:function(j){if(j){return(h.isObject(j)&&j.key);
-}else{return(j===null);}},method:function(k){var r=this.get("sortedBy");this._configs.sortedBy.value=k;var j,o,m,q;if(this._elThead){if(r&&r.key&&r.dir){j=this._oColumnSet.getColumn(r.key);o=j.getKeyIndex();var u=j.getThEl();c.removeClass(u,r.dir);this.formatTheadCell(j.getThLinerEl().firstChild,j,k);}if(k){m=(k.column)?k.column:this._oColumnSet.getColumn(k.key);q=m.getKeyIndex();var v=m.getThEl();if(k.dir&&((k.dir=="asc")||(k.dir=="desc"))){var p=(k.dir=="desc")?d.CLASS_DESC:d.CLASS_ASC;c.addClass(v,p);}else{var l=k.dir||d.CLASS_ASC;c.addClass(v,l);}this.formatTheadCell(m.getThLinerEl().firstChild,m,k);}}if(this._elTbody){this._elTbody.style.display="none";var s=this._elTbody.rows,t;for(var n=s.length-1;n>-1;n--){t=s[n].childNodes;if(t[o]){c.removeClass(t[o],r.dir);}if(t[q]){c.addClass(t[q],k.dir);}}this._elTbody.style.display="";}this._clearTrTemplateEl();}});this.setAttributeConfig("paginator",{value:null,validator:function(j){return j===null||j instanceof e.Paginator;},method:function(){this._updatePaginator.apply(this,arguments);}});this.setAttributeConfig("caption",{value:null,validator:h.isString,method:function(j){this._initCaptionEl(j);}});this.setAttributeConfig("draggableColumns",{value:false,validator:h.isBoolean,method:function(j){if(this._elThead){if(j){this._initDraggableColumns();}else{this._destroyDraggableColumns();}}}});this.setAttributeConfig("renderLoopSize",{value:0,validator:h.isNumber});this.setAttributeConfig("sortFunction",{value:function(k,j,o,n){var m=YAHOO.util.Sort.compare,l=m(k.getData(n),j.getData(n),o);if(l===0){return m(k.getCount(),j.getCount(),o);}else{return l;}}});this.setAttributeConfig("formatRow",{value:null,validator:h.isFunction});this.setAttributeConfig("generateRequest",{value:function(k,n){k=k||{pagination:null,sortedBy:null};var m=encodeURIComponent((k.sortedBy)?k.sortedBy.key:n.getColumnSet().keys[0].getKey());var j=(k.sortedBy&&k.sortedBy.dir===YAHOO.widget.DataTable.CLASS_DESC)?"desc":"asc";var o=(k.pagination)?k.pagination.recordOffset:0;var l=(k.pagination)?k.pagination.rowsPerPage:null;return"sort="+m+"&dir="+j+"&startIndex="+o+((l!==null)?"&results="+l:"");},validator:h.isFunction});this.setAttributeConfig("initialRequest",{value:null});this.setAttributeConfig("initialLoad",{value:true});this.setAttributeConfig("dynamicData",{value:false,validator:h.isBoolean});this.setAttributeConfig("MSG_EMPTY",{value:"No records found.",validator:h.isString});this.setAttributeConfig("MSG_LOADING",{value:"Loading...",validator:h.isString});this.setAttributeConfig("MSG_ERROR",{value:"Data error.",validator:h.isString});this.setAttributeConfig("MSG_SORTASC",{value:"Click to sort ascending",validator:h.isString,method:function(k){if(this._elThead){for(var l=0,m=this.getColumnSet().keys,j=m.length;l<j;l++){if(m[l].sortable&&this.getColumnSortDir(m[l])===d.CLASS_ASC){m[l]._elThLabel.firstChild.title=k;}}}}});this.setAttributeConfig("MSG_SORTDESC",{value:"Click to sort descending",validator:h.isString,method:function(k){if(this._elThead){for(var l=0,m=this.getColumnSet().keys,j=m.length;l<j;l++){if(m[l].sortable&&this.getColumnSortDir(m[l])===d.CLASS_DESC){m[l]._elThLabel.firstChild.title=k;}}}}});this.setAttributeConfig("currencySymbol",{value:"$",validator:h.isString});this.setAttributeConfig("currencyOptions",{value:{prefix:this.get("currencySymbol"),decimalPlaces:2,decimalSeparator:".",thousandsSeparator:","}});this.setAttributeConfig("dateOptions",{value:{format:"%m/%d/%Y",locale:"en"}});this.setAttributeConfig("numberOptions",{value:{decimalPlaces:0,thousandsSeparator:","}});},_bInit:true,_nIndex:null,_nTrCount:0,_nTdCount:0,_sId:null,_oChainRender:null,_elContainer:null,_elMask:null,_elTable:null,_elCaption:null,_elColgroup:null,_elThead:null,_elTbody:null,_elMsgTbody:null,_elMsgTr:null,_elMsgTd:null,_elColumnDragTarget:null,_elColumnResizerProxy:null,_oDataSource:null,_oColumnSet:null,_oRecordSet:null,_oCellEditor:null,_sFirstTrId:null,_sLastTrId:null,_elTrTemplate:null,_aDynFunctions:[],_disabled:false,clearTextSelection:function(){var i;if(window.getSelection){i=window.getSelection();}else{if(document.getSelection){i=document.getSelection();}else{if(document.selection){i=document.selection;}}}if(i){if(i.empty){i.empty();}else{if(i.removeAllRanges){i.removeAllRanges();}else{if(i.collapse){i.collapse();}}}}},_focusEl:function(i){i=i||this._elTbody;setTimeout(function(){try{i.focus();}catch(j){}},0);},_repaintGecko:(b.gecko)?function(j){j=j||this._elContainer;var i=j.parentNode;var k=j.nextSibling;i.insertBefore(i.removeChild(j),k);}:function(){},_repaintOpera:(b.opera)?function(){if(b.opera){document.documentElement.className+=" ";document.documentElement.className=YAHOO.lang.trim(document.documentElement.className);}}:function(){},_repaintWebkit:(b.webkit)?function(j){j=j||this._elContainer;var i=j.parentNode;var k=j.nextSibling;i.insertBefore(i.removeChild(j),k);}:function(){},_initConfigs:function(i){if(!i||!h.isObject(i)){i={};}this.configs=i;},_initColumnSet:function(n){var m,k,j;if(this._oColumnSet){for(k=0,j=this._oColumnSet.keys.length;k<j;k++){m=this._oColumnSet.keys[k];d._oDynStyles["."+this.getId()+"-col-"+m.getSanitizedKey()+" ."+d.CLASS_LINER]=undefined;if(m.editor&&m.editor.unsubscribeAll){m.editor.unsubscribeAll();}}this._oColumnSet=null;this._clearTrTemplateEl();}if(h.isArray(n)){this._oColumnSet=new YAHOO.widget.ColumnSet(n);}else{if(n instanceof YAHOO.widget.ColumnSet){this._oColumnSet=n;}}var l=this._oColumnSet.keys;for(k=0,j=l.length;k<j;k++){m=l[k];if(m.editor&&m.editor.subscribe){m.editor.subscribe("showEvent",this._onEditorShowEvent,this,true);m.editor.subscribe("keydownEvent",this._onEditorKeydownEvent,this,true);m.editor.subscribe("revertEvent",this._onEditorRevertEvent,this,true);m.editor.subscribe("saveEvent",this._onEditorSaveEvent,this,true);m.editor.subscribe("cancelEvent",this._onEditorCancelEvent,this,true);m.editor.subscribe("blurEvent",this._onEditorBlurEvent,this,true);m.editor.subscribe("blockEvent",this._onEditorBlockEvent,this,true);
-m.editor.subscribe("unblockEvent",this._onEditorUnblockEvent,this,true);}}},_initDataSource:function(j){this._oDataSource=null;if(j&&(h.isFunction(j.sendRequest))){this._oDataSource=j;}else{var k=null;var o=this._elContainer;var l=0;if(o.hasChildNodes()){var n=o.childNodes;for(l=0;l<n.length;l++){if(n[l].nodeName&&n[l].nodeName.toLowerCase()=="table"){k=n[l];break;}}if(k){var m=[];for(;l<this._oColumnSet.keys.length;l++){m.push({key:this._oColumnSet.keys[l].key});}this._oDataSource=new f(k);this._oDataSource.responseType=f.TYPE_HTMLTABLE;this._oDataSource.responseSchema={fields:m};}}}},_initRecordSet:function(){if(this._oRecordSet){this._oRecordSet.reset();}else{this._oRecordSet=new YAHOO.widget.RecordSet();}},_initDomElements:function(i){this._initContainerEl(i);this._initTableEl(this._elContainer);this._initColgroupEl(this._elTable);this._initTheadEl(this._elTable);this._initMsgTbodyEl(this._elTable);this._initTbodyEl(this._elTable);if(!this._elContainer||!this._elTable||!this._elColgroup||!this._elThead||!this._elTbody||!this._elMsgTbody){return false;}else{return true;}},_destroyContainerEl:function(m){var k=this._oColumnSet.keys,l,j;c.removeClass(m,d.CLASS_DATATABLE);g.purgeElement(m);g.purgeElement(this._elThead,true);g.purgeElement(this._elTbody);g.purgeElement(this._elMsgTbody);l=m.getElementsByTagName("select");if(l.length){g.detachListener(l,"change");}for(j=k.length-1;j>=0;--j){if(k[j].editor){g.purgeElement(k[j].editor._elContainer);}}m.innerHTML="";this._elContainer=null;this._elColgroup=null;this._elThead=null;this._elTbody=null;},_initContainerEl:function(j){j=c.get(j);if(j&&j.nodeName&&(j.nodeName.toLowerCase()=="div")){this._destroyContainerEl(j);c.addClass(j,d.CLASS_DATATABLE);g.addListener(j,"focus",this._onTableFocus,this);g.addListener(j,"dblclick",this._onTableDblclick,this);this._elContainer=j;var i=document.createElement("div");i.className=d.CLASS_MASK;i.style.display="none";this._elMask=j.appendChild(i);}},_destroyTableEl:function(){var i=this._elTable;if(i){g.purgeElement(i,true);i.parentNode.removeChild(i);this._elCaption=null;this._elColgroup=null;this._elThead=null;this._elTbody=null;}},_initCaptionEl:function(i){if(this._elTable&&i){if(!this._elCaption){this._elCaption=this._elTable.createCaption();}this._elCaption.innerHTML=i;}else{if(this._elCaption){this._elCaption.parentNode.removeChild(this._elCaption);}}},_initTableEl:function(i){if(i){this._destroyTableEl();this._elTable=i.appendChild(document.createElement("table"));this._elTable.summary=this.get("summary");if(this.get("caption")){this._initCaptionEl(this.get("caption"));}g.delegate(this._elTable,"mouseenter",this._onTableMouseover,"thead ."+d.CLASS_LABEL,this);g.delegate(this._elTable,"mouseleave",this._onTableMouseout,"thead ."+d.CLASS_LABEL,this);g.delegate(this._elTable,"mouseenter",this._onTableMouseover,"tbody.yui-dt-data>tr>td",this);g.delegate(this._elTable,"mouseleave",this._onTableMouseout,"tbody.yui-dt-data>tr>td",this);g.delegate(this._elTable,"mouseenter",this._onTableMouseover,"tbody.yui-dt-message>tr>td",this);g.delegate(this._elTable,"mouseleave",this._onTableMouseout,"tbody.yui-dt-message>tr>td",this);}},_destroyColgroupEl:function(){var i=this._elColgroup;if(i){var j=i.parentNode;g.purgeElement(i,true);j.removeChild(i);this._elColgroup=null;}},_initColgroupEl:function(s){if(s){this._destroyColgroupEl();var l=this._aColIds||[],r=this._oColumnSet.keys,m=0,p=l.length,j,o,q=document.createDocumentFragment(),n=document.createElement("col");for(m=0,p=r.length;m<p;m++){o=r[m];j=q.appendChild(n.cloneNode(false));}var k=s.insertBefore(document.createElement("colgroup"),s.firstChild);k.appendChild(q);this._elColgroup=k;}},_insertColgroupColEl:function(i){if(h.isNumber(i)&&this._elColgroup){var j=this._elColgroup.childNodes[i]||null;this._elColgroup.insertBefore(document.createElement("col"),j);}},_removeColgroupColEl:function(i){if(h.isNumber(i)&&this._elColgroup&&this._elColgroup.childNodes[i]){this._elColgroup.removeChild(this._elColgroup.childNodes[i]);}},_reorderColgroupColEl:function(l,k){if(h.isArray(l)&&h.isNumber(k)&&this._elColgroup&&(this._elColgroup.childNodes.length>l[l.length-1])){var j,n=[];for(j=l.length-1;j>-1;j--){n.push(this._elColgroup.removeChild(this._elColgroup.childNodes[l[j]]));}var m=this._elColgroup.childNodes[k]||null;for(j=n.length-1;j>-1;j--){this._elColgroup.insertBefore(n[j],m);}}},_destroyTheadEl:function(){var j=this._elThead;if(j){var i=j.parentNode;g.purgeElement(j,true);this._destroyColumnHelpers();i.removeChild(j);this._elThead=null;}},_initTheadEl:function(v){v=v||this._elTable;if(v){this._destroyTheadEl();var q=(this._elColgroup)?v.insertBefore(document.createElement("thead"),this._elColgroup.nextSibling):v.appendChild(document.createElement("thead"));g.addListener(q,"focus",this._onTheadFocus,this);g.addListener(q,"keydown",this._onTheadKeydown,this);g.addListener(q,"mousedown",this._onTableMousedown,this);g.addListener(q,"mouseup",this._onTableMouseup,this);g.addListener(q,"click",this._onTheadClick,this);var x=this._oColumnSet,t,r,p,n;var w=x.tree;var o;for(r=0;r<w.length;r++){var m=q.appendChild(document.createElement("tr"));for(p=0;p<w[r].length;p++){t=w[r][p];o=m.appendChild(document.createElement("th"));this._initThEl(o,t);}if(r===0){c.addClass(m,d.CLASS_FIRST);}if(r===(w.length-1)){c.addClass(m,d.CLASS_LAST);}}var k=x.headers[0]||[];for(r=0;r<k.length;r++){c.addClass(c.get(this.getId()+"-th-"+k[r]),d.CLASS_FIRST);}var s=x.headers[x.headers.length-1]||[];for(r=0;r<s.length;r++){c.addClass(c.get(this.getId()+"-th-"+s[r]),d.CLASS_LAST);}if(b.webkit&&b.webkit<420){var u=this;setTimeout(function(){q.style.display="";},0);q.style.display="none";}this._elThead=q;this._initColumnHelpers();}},_initThEl:function(m,l){m.id=this.getId()+"-th-"+l.getSanitizedKey();m.innerHTML="";m.rowSpan=l.getRowspan();m.colSpan=l.getColspan();l._elTh=m;var i=m.appendChild(document.createElement("div"));i.id=m.id+"-liner";i.className=d.CLASS_LINER;l._elThLiner=i;var j=i.appendChild(document.createElement("span"));
-j.className=d.CLASS_LABEL;if(l.abbr){m.abbr=l.abbr;}if(l.hidden){this._clearMinWidth(l);}m.className=this._getColumnClassNames(l);if(l.width){var k=(l.minWidth&&(l.width<l.minWidth))?l.minWidth:l.width;if(d._bDynStylesFallback){m.firstChild.style.overflow="hidden";m.firstChild.style.width=k+"px";}else{this._setColumnWidthDynStyles(l,k+"px","hidden");}}this.formatTheadCell(j,l,this.get("sortedBy"));l._elThLabel=j;},formatTheadCell:function(i,m,k){var q=m.getKey();var p=h.isValue(m.label)?m.label:q;if(m.sortable){var n=this.getColumnSortDir(m,k);var j=(n===d.CLASS_DESC);if(k&&(m.key===k.key)){j=!(k.dir===d.CLASS_DESC);}var l=this.getId()+"-href-"+m.getSanitizedKey();var o=(j)?this.get("MSG_SORTDESC"):this.get("MSG_SORTASC");i.innerHTML='<a href="'+l+'" title="'+o+'" class="'+d.CLASS_SORTABLE+'">'+p+"</a>";}else{i.innerHTML=p;}},_destroyDraggableColumns:function(){var l,m;for(var k=0,j=this._oColumnSet.tree[0].length;k<j;k++){l=this._oColumnSet.tree[0][k];if(l._dd){l._dd=l._dd.unreg();c.removeClass(l.getThEl(),d.CLASS_DRAGGABLE);}}this._destroyColumnDragTargetEl();},_initDraggableColumns:function(){this._destroyDraggableColumns();if(a.DD){var m,n,k;for(var l=0,j=this._oColumnSet.tree[0].length;l<j;l++){m=this._oColumnSet.tree[0][l];n=m.getThEl();c.addClass(n,d.CLASS_DRAGGABLE);k=this._initColumnDragTargetEl();m._dd=new YAHOO.widget.ColumnDD(this,m,n,k);}}else{}},_destroyColumnDragTargetEl:function(){if(this._elColumnDragTarget){var i=this._elColumnDragTarget;YAHOO.util.Event.purgeElement(i);i.parentNode.removeChild(i);this._elColumnDragTarget=null;}},_initColumnDragTargetEl:function(){if(!this._elColumnDragTarget){var i=document.createElement("div");i.id=this.getId()+"-coltarget";i.className=d.CLASS_COLTARGET;i.style.display="none";document.body.insertBefore(i,document.body.firstChild);this._elColumnDragTarget=i;}return this._elColumnDragTarget;},_destroyResizeableColumns:function(){var k=this._oColumnSet.keys;for(var l=0,j=k.length;l<j;l++){if(k[l]._ddResizer){k[l]._ddResizer=k[l]._ddResizer.unreg();c.removeClass(k[l].getThEl(),d.CLASS_RESIZEABLE);}}this._destroyColumnResizerProxyEl();},_initResizeableColumns:function(){this._destroyResizeableColumns();if(a.DD){var p,k,n,q,j,r,m;for(var l=0,o=this._oColumnSet.keys.length;l<o;l++){p=this._oColumnSet.keys[l];if(p.resizeable){k=p.getThEl();c.addClass(k,d.CLASS_RESIZEABLE);n=p.getThLinerEl();q=k.appendChild(document.createElement("div"));q.className=d.CLASS_RESIZERLINER;q.appendChild(n);j=q.appendChild(document.createElement("div"));j.id=k.id+"-resizer";j.className=d.CLASS_RESIZER;p._elResizer=j;r=this._initColumnResizerProxyEl();p._ddResizer=new YAHOO.util.ColumnResizer(this,p,k,j,r);m=function(i){g.stopPropagation(i);};g.addListener(j,"click",m);}}}else{}},_destroyColumnResizerProxyEl:function(){if(this._elColumnResizerProxy){var i=this._elColumnResizerProxy;YAHOO.util.Event.purgeElement(i);i.parentNode.removeChild(i);this._elColumnResizerProxy=null;}},_initColumnResizerProxyEl:function(){if(!this._elColumnResizerProxy){var i=document.createElement("div");i.id=this.getId()+"-colresizerproxy";i.className=d.CLASS_RESIZERPROXY;document.body.insertBefore(i,document.body.firstChild);this._elColumnResizerProxy=i;}return this._elColumnResizerProxy;},_destroyColumnHelpers:function(){this._destroyDraggableColumns();this._destroyResizeableColumns();},_initColumnHelpers:function(){if(this.get("draggableColumns")){this._initDraggableColumns();}this._initResizeableColumns();},_destroyTbodyEl:function(){var i=this._elTbody;if(i){var j=i.parentNode;g.purgeElement(i,true);j.removeChild(i);this._elTbody=null;}},_initTbodyEl:function(j){if(j){this._destroyTbodyEl();var i=j.appendChild(document.createElement("tbody"));i.tabIndex=0;i.className=d.CLASS_DATA;g.addListener(i,"focus",this._onTbodyFocus,this);g.addListener(i,"mousedown",this._onTableMousedown,this);g.addListener(i,"mouseup",this._onTableMouseup,this);g.addListener(i,"keydown",this._onTbodyKeydown,this);g.addListener(i,"click",this._onTbodyClick,this);if(b.ie){i.hideFocus=true;}this._elTbody=i;}},_destroyMsgTbodyEl:function(){var i=this._elMsgTbody;if(i){var j=i.parentNode;g.purgeElement(i,true);j.removeChild(i);this._elTbody=null;}},_initMsgTbodyEl:function(l){if(l){var k=document.createElement("tbody");k.className=d.CLASS_MESSAGE;var j=k.appendChild(document.createElement("tr"));j.className=d.CLASS_FIRST+" "+d.CLASS_LAST;this._elMsgTr=j;var m=j.appendChild(document.createElement("td"));m.colSpan=this._oColumnSet.keys.length||1;m.className=d.CLASS_FIRST+" "+d.CLASS_LAST;this._elMsgTd=m;k=l.insertBefore(k,this._elTbody);var i=m.appendChild(document.createElement("div"));i.className=d.CLASS_LINER;this._elMsgTbody=k;g.addListener(k,"focus",this._onTbodyFocus,this);g.addListener(k,"mousedown",this._onTableMousedown,this);g.addListener(k,"mouseup",this._onTableMouseup,this);g.addListener(k,"keydown",this._onTbodyKeydown,this);g.addListener(k,"click",this._onTbodyClick,this);}},_initEvents:function(){this._initColumnSort();YAHOO.util.Event.addListener(document,"click",this._onDocumentClick,this);this.subscribe("paginatorChange",function(){this._handlePaginatorChange.apply(this,arguments);});this.subscribe("initEvent",function(){this.renderPaginator();});this._initCellEditing();},_initColumnSort:function(){this.subscribe("theadCellClickEvent",this.onEventSortColumn);var i=this.get("sortedBy");if(i){if(i.dir=="desc"){this._configs.sortedBy.value.dir=d.CLASS_DESC;}else{if(i.dir=="asc"){this._configs.sortedBy.value.dir=d.CLASS_ASC;}}}},_initCellEditing:function(){this.subscribe("editorBlurEvent",function(){this.onEditorBlurEvent.apply(this,arguments);});this.subscribe("editorBlockEvent",function(){this.onEditorBlockEvent.apply(this,arguments);});this.subscribe("editorUnblockEvent",function(){this.onEditorUnblockEvent.apply(this,arguments);});},_getColumnClassNames:function(l,k){var i;if(h.isString(l.className)){i=[l.className];}else{if(h.isArray(l.className)){i=l.className;}else{i=[];}}i[i.length]=this.getId()+"-col-"+l.getSanitizedKey();
-i[i.length]="yui-dt-col-"+l.getSanitizedKey();var j=this.get("sortedBy")||{};if(l.key===j.key){i[i.length]=j.dir||"";}if(l.hidden){i[i.length]=d.CLASS_HIDDEN;}if(l.selected){i[i.length]=d.CLASS_SELECTED;}if(l.sortable){i[i.length]=d.CLASS_SORTABLE;}if(l.resizeable){i[i.length]=d.CLASS_RESIZEABLE;}if(l.editor){i[i.length]=d.CLASS_EDITABLE;}if(k){i=i.concat(k);}return i.join(" ");},_clearTrTemplateEl:function(){this._elTrTemplate=null;},_getTrTemplateEl:function(u,o){if(this._elTrTemplate){return this._elTrTemplate;}else{var q=document,s=q.createElement("tr"),l=q.createElement("td"),k=q.createElement("div");l.appendChild(k);var t=document.createDocumentFragment(),r=this._oColumnSet.keys,n;var p;for(var m=0,j=r.length;m<j;m++){n=l.cloneNode(true);n=this._formatTdEl(r[m],n,m,(m===j-1));t.appendChild(n);}s.appendChild(t);s.className=d.CLASS_REC;this._elTrTemplate=s;return s;}},_formatTdEl:function(n,p,q,m){var t=this._oColumnSet;var i=t.headers,k=i[q],o="",v;for(var l=0,u=k.length;l<u;l++){v=this._sId+"-th-"+k[l]+" ";o+=v;}p.headers=o;var s=[];if(q===0){s[s.length]=d.CLASS_FIRST;}if(m){s[s.length]=d.CLASS_LAST;}p.className=this._getColumnClassNames(n,s);p.firstChild.className=d.CLASS_LINER;if(n.width&&d._bDynStylesFallback){var r=(n.minWidth&&(n.width<n.minWidth))?n.minWidth:n.width;p.firstChild.style.overflow="hidden";p.firstChild.style.width=r+"px";}return p;},_addTrEl:function(k){var j=this._getTrTemplateEl();var i=j.cloneNode(true);return this._updateTrEl(i,k);},_updateTrEl:function(q,r){var p=this.get("formatRow")?this.get("formatRow").call(this,q,r):true;if(p){q.style.display="none";var o=q.childNodes,m;for(var l=0,n=o.length;l<n;++l){m=o[l];this.formatCell(o[l].firstChild,r,this._oColumnSet.keys[l]);}q.style.display="";}var j=q.id,k=r.getId();if(this._sFirstTrId===j){this._sFirstTrId=k;}if(this._sLastTrId===j){this._sLastTrId=k;}q.id=k;return q;},_deleteTrEl:function(i){var j;if(!h.isNumber(i)){j=c.get(i).sectionRowIndex;}else{j=i;}if(h.isNumber(j)&&(j>-2)&&(j<this._elTbody.rows.length)){return this._elTbody.removeChild(this._elTbody.rows[i]);}else{return null;}},_unsetFirstRow:function(){if(this._sFirstTrId){c.removeClass(this._sFirstTrId,d.CLASS_FIRST);this._sFirstTrId=null;}},_setFirstRow:function(){this._unsetFirstRow();var i=this.getFirstTrEl();if(i){c.addClass(i,d.CLASS_FIRST);this._sFirstTrId=i.id;}},_unsetLastRow:function(){if(this._sLastTrId){c.removeClass(this._sLastTrId,d.CLASS_LAST);this._sLastTrId=null;}},_setLastRow:function(){this._unsetLastRow();var i=this.getLastTrEl();if(i){c.addClass(i,d.CLASS_LAST);this._sLastTrId=i.id;}},_setRowStripes:function(t,l){var m=this._elTbody.rows,q=0,s=m.length,p=[],r=0,n=[],j=0;if((t!==null)&&(t!==undefined)){var o=this.getTrEl(t);if(o){q=o.sectionRowIndex;if(h.isNumber(l)&&(l>1)){s=q+l;}}}for(var k=q;k<s;k++){if(k%2){p[r++]=m[k];}else{n[j++]=m[k];}}if(p.length){c.replaceClass(p,d.CLASS_EVEN,d.CLASS_ODD);}if(n.length){c.replaceClass(n,d.CLASS_ODD,d.CLASS_EVEN);}},_setSelections:function(){var l=this.getSelectedRows();var n=this.getSelectedCells();if((l.length>0)||(n.length>0)){var m=this._oColumnSet,k;for(var j=0;j<l.length;j++){k=c.get(l[j]);if(k){c.addClass(k,d.CLASS_SELECTED);}}for(j=0;j<n.length;j++){k=c.get(n[j].recordId);if(k){c.addClass(k.childNodes[m.getColumn(n[j].columnKey).getKeyIndex()],d.CLASS_SELECTED);}}}},_onRenderChainEnd:function(){this.hideTableMessage();if(this._elTbody.rows.length===0){this.showTableMessage(this.get("MSG_EMPTY"),d.CLASS_EMPTY);}var i=this;setTimeout(function(){if((i instanceof d)&&i._sId){if(i._bInit){i._bInit=false;i.fireEvent("initEvent");}i.fireEvent("renderEvent");i.fireEvent("refreshEvent");i.validateColumnWidths();i.fireEvent("postRenderEvent");}},0);},_onDocumentClick:function(l,j){var m=g.getTarget(l);var i=m.nodeName.toLowerCase();if(!c.isAncestor(j._elContainer,m)){j.fireEvent("tableBlurEvent");if(j._oCellEditor){if(j._oCellEditor.getContainerEl){var k=j._oCellEditor.getContainerEl();if(!c.isAncestor(k,m)&&(k.id!==m.id)){j._oCellEditor.fireEvent("blurEvent",{editor:j._oCellEditor});}}else{if(j._oCellEditor.isActive){if(!c.isAncestor(j._oCellEditor.container,m)&&(j._oCellEditor.container.id!==m.id)){j.fireEvent("editorBlurEvent",{editor:j._oCellEditor});}}}}}},_onTableFocus:function(j,i){i.fireEvent("tableFocusEvent");},_onTheadFocus:function(j,i){i.fireEvent("theadFocusEvent");i.fireEvent("tableFocusEvent");},_onTbodyFocus:function(j,i){i.fireEvent("tbodyFocusEvent");i.fireEvent("tableFocusEvent");},_onTableMouseover:function(n,m,i,k){var o=m;var j=o.nodeName&&o.nodeName.toLowerCase();var l=true;while(o&&(j!="table")){switch(j){case"body":return;case"a":break;case"td":l=k.fireEvent("cellMouseoverEvent",{target:o,event:n});break;case"span":if(c.hasClass(o,d.CLASS_LABEL)){l=k.fireEvent("theadLabelMouseoverEvent",{target:o,event:n});l=k.fireEvent("headerLabelMouseoverEvent",{target:o,event:n});}break;case"th":l=k.fireEvent("theadCellMouseoverEvent",{target:o,event:n});l=k.fireEvent("headerCellMouseoverEvent",{target:o,event:n});break;case"tr":if(o.parentNode.nodeName.toLowerCase()=="thead"){l=k.fireEvent("theadRowMouseoverEvent",{target:o,event:n});l=k.fireEvent("headerRowMouseoverEvent",{target:o,event:n});}else{l=k.fireEvent("rowMouseoverEvent",{target:o,event:n});}break;default:break;}if(l===false){return;}else{o=o.parentNode;if(o){j=o.nodeName.toLowerCase();}}}k.fireEvent("tableMouseoverEvent",{target:(o||k._elContainer),event:n});},_onTableMouseout:function(n,m,i,k){var o=m;var j=o.nodeName&&o.nodeName.toLowerCase();var l=true;while(o&&(j!="table")){switch(j){case"body":return;case"a":break;case"td":l=k.fireEvent("cellMouseoutEvent",{target:o,event:n});break;case"span":if(c.hasClass(o,d.CLASS_LABEL)){l=k.fireEvent("theadLabelMouseoutEvent",{target:o,event:n});l=k.fireEvent("headerLabelMouseoutEvent",{target:o,event:n});}break;case"th":l=k.fireEvent("theadCellMouseoutEvent",{target:o,event:n});l=k.fireEvent("headerCellMouseoutEvent",{target:o,event:n});break;case"tr":if(o.parentNode.nodeName.toLowerCase()=="thead"){l=k.fireEvent("theadRowMouseoutEvent",{target:o,event:n});
-l=k.fireEvent("headerRowMouseoutEvent",{target:o,event:n});}else{l=k.fireEvent("rowMouseoutEvent",{target:o,event:n});}break;default:break;}if(l===false){return;}else{o=o.parentNode;if(o){j=o.nodeName.toLowerCase();}}}k.fireEvent("tableMouseoutEvent",{target:(o||k._elContainer),event:n});},_onTableMousedown:function(l,j){var m=g.getTarget(l);var i=m.nodeName&&m.nodeName.toLowerCase();var k=true;while(m&&(i!="table")){switch(i){case"body":return;case"a":break;case"td":k=j.fireEvent("cellMousedownEvent",{target:m,event:l});break;case"span":if(c.hasClass(m,d.CLASS_LABEL)){k=j.fireEvent("theadLabelMousedownEvent",{target:m,event:l});k=j.fireEvent("headerLabelMousedownEvent",{target:m,event:l});}break;case"th":k=j.fireEvent("theadCellMousedownEvent",{target:m,event:l});k=j.fireEvent("headerCellMousedownEvent",{target:m,event:l});break;case"tr":if(m.parentNode.nodeName.toLowerCase()=="thead"){k=j.fireEvent("theadRowMousedownEvent",{target:m,event:l});k=j.fireEvent("headerRowMousedownEvent",{target:m,event:l});}else{k=j.fireEvent("rowMousedownEvent",{target:m,event:l});}break;default:break;}if(k===false){return;}else{m=m.parentNode;if(m){i=m.nodeName.toLowerCase();}}}j.fireEvent("tableMousedownEvent",{target:(m||j._elContainer),event:l});},_onTableMouseup:function(l,j){var m=g.getTarget(l);var i=m.nodeName&&m.nodeName.toLowerCase();var k=true;while(m&&(i!="table")){switch(i){case"body":return;case"a":break;case"td":k=j.fireEvent("cellMouseupEvent",{target:m,event:l});break;case"span":if(c.hasClass(m,d.CLASS_LABEL)){k=j.fireEvent("theadLabelMouseupEvent",{target:m,event:l});k=j.fireEvent("headerLabelMouseupEvent",{target:m,event:l});}break;case"th":k=j.fireEvent("theadCellMouseupEvent",{target:m,event:l});k=j.fireEvent("headerCellMouseupEvent",{target:m,event:l});break;case"tr":if(m.parentNode.nodeName.toLowerCase()=="thead"){k=j.fireEvent("theadRowMouseupEvent",{target:m,event:l});k=j.fireEvent("headerRowMouseupEvent",{target:m,event:l});}else{k=j.fireEvent("rowMouseupEvent",{target:m,event:l});}break;default:break;}if(k===false){return;}else{m=m.parentNode;if(m){i=m.nodeName.toLowerCase();}}}j.fireEvent("tableMouseupEvent",{target:(m||j._elContainer),event:l});},_onTableDblclick:function(l,j){var m=g.getTarget(l);var i=m.nodeName&&m.nodeName.toLowerCase();var k=true;while(m&&(i!="table")){switch(i){case"body":return;case"td":k=j.fireEvent("cellDblclickEvent",{target:m,event:l});break;case"span":if(c.hasClass(m,d.CLASS_LABEL)){k=j.fireEvent("theadLabelDblclickEvent",{target:m,event:l});k=j.fireEvent("headerLabelDblclickEvent",{target:m,event:l});}break;case"th":k=j.fireEvent("theadCellDblclickEvent",{target:m,event:l});k=j.fireEvent("headerCellDblclickEvent",{target:m,event:l});break;case"tr":if(m.parentNode.nodeName.toLowerCase()=="thead"){k=j.fireEvent("theadRowDblclickEvent",{target:m,event:l});k=j.fireEvent("headerRowDblclickEvent",{target:m,event:l});}else{k=j.fireEvent("rowDblclickEvent",{target:m,event:l});}break;default:break;}if(k===false){return;}else{m=m.parentNode;if(m){i=m.nodeName.toLowerCase();}}}j.fireEvent("tableDblclickEvent",{target:(m||j._elContainer),event:l});},_onTheadKeydown:function(l,j){var m=g.getTarget(l);var i=m.nodeName&&m.nodeName.toLowerCase();var k=true;while(m&&(i!="table")){switch(i){case"body":return;case"input":case"textarea":break;case"thead":k=j.fireEvent("theadKeyEvent",{target:m,event:l});break;default:break;}if(k===false){return;}else{m=m.parentNode;if(m){i=m.nodeName.toLowerCase();}}}j.fireEvent("tableKeyEvent",{target:(m||j._elContainer),event:l});},_onTbodyKeydown:function(m,k){var j=k.get("selectionMode");if(j=="standard"){k._handleStandardSelectionByKey(m);}else{if(j=="single"){k._handleSingleSelectionByKey(m);}else{if(j=="cellblock"){k._handleCellBlockSelectionByKey(m);}else{if(j=="cellrange"){k._handleCellRangeSelectionByKey(m);}else{if(j=="singlecell"){k._handleSingleCellSelectionByKey(m);}}}}}if(k._oCellEditor){if(k._oCellEditor.fireEvent){k._oCellEditor.fireEvent("blurEvent",{editor:k._oCellEditor});}else{if(k._oCellEditor.isActive){k.fireEvent("editorBlurEvent",{editor:k._oCellEditor});}}}var n=g.getTarget(m);var i=n.nodeName&&n.nodeName.toLowerCase();var l=true;while(n&&(i!="table")){switch(i){case"body":return;case"tbody":l=k.fireEvent("tbodyKeyEvent",{target:n,event:m});break;default:break;}if(l===false){return;}else{n=n.parentNode;if(n){i=n.nodeName.toLowerCase();}}}k.fireEvent("tableKeyEvent",{target:(n||k._elContainer),event:m});},_onTheadClick:function(l,j){if(j._oCellEditor){if(j._oCellEditor.fireEvent){j._oCellEditor.fireEvent("blurEvent",{editor:j._oCellEditor});}else{if(j._oCellEditor.isActive){j.fireEvent("editorBlurEvent",{editor:j._oCellEditor});}}}var m=g.getTarget(l),i=m.nodeName&&m.nodeName.toLowerCase(),k=true;while(m&&(i!="table")){switch(i){case"body":return;case"input":var n=m.type.toLowerCase();if(n=="checkbox"){k=j.fireEvent("theadCheckboxClickEvent",{target:m,event:l});}else{if(n=="radio"){k=j.fireEvent("theadRadioClickEvent",{target:m,event:l});}else{if((n=="button")||(n=="image")||(n=="submit")||(n=="reset")){if(!m.disabled){k=j.fireEvent("theadButtonClickEvent",{target:m,event:l});}else{k=false;}}else{if(m.disabled){k=false;}}}}break;case"a":k=j.fireEvent("theadLinkClickEvent",{target:m,event:l});break;case"button":if(!m.disabled){k=j.fireEvent("theadButtonClickEvent",{target:m,event:l});}else{k=false;}break;case"span":if(c.hasClass(m,d.CLASS_LABEL)){k=j.fireEvent("theadLabelClickEvent",{target:m,event:l});k=j.fireEvent("headerLabelClickEvent",{target:m,event:l});}break;case"th":k=j.fireEvent("theadCellClickEvent",{target:m,event:l});k=j.fireEvent("headerCellClickEvent",{target:m,event:l});break;case"tr":k=j.fireEvent("theadRowClickEvent",{target:m,event:l});k=j.fireEvent("headerRowClickEvent",{target:m,event:l});break;default:break;}if(k===false){return;}else{m=m.parentNode;if(m){i=m.nodeName.toLowerCase();}}}j.fireEvent("tableClickEvent",{target:(m||j._elContainer),event:l});},_onTbodyClick:function(l,j){if(j._oCellEditor){if(j._oCellEditor.fireEvent){j._oCellEditor.fireEvent("blurEvent",{editor:j._oCellEditor});
-}else{if(j._oCellEditor.isActive){j.fireEvent("editorBlurEvent",{editor:j._oCellEditor});}}}var m=g.getTarget(l),i=m.nodeName&&m.nodeName.toLowerCase(),k=true;while(m&&(i!="table")){switch(i){case"body":return;case"input":var n=m.type.toLowerCase();if(n=="checkbox"){k=j.fireEvent("checkboxClickEvent",{target:m,event:l});}else{if(n=="radio"){k=j.fireEvent("radioClickEvent",{target:m,event:l});}else{if((n=="button")||(n=="image")||(n=="submit")||(n=="reset")){if(!m.disabled){k=j.fireEvent("buttonClickEvent",{target:m,event:l});}else{k=false;}}else{if(m.disabled){k=false;}}}}break;case"a":k=j.fireEvent("linkClickEvent",{target:m,event:l});break;case"button":if(!m.disabled){k=j.fireEvent("buttonClickEvent",{target:m,event:l});}else{k=false;}break;case"td":k=j.fireEvent("cellClickEvent",{target:m,event:l});break;case"tr":k=j.fireEvent("rowClickEvent",{target:m,event:l});break;default:break;}if(k===false){return;}else{m=m.parentNode;if(m){i=m.nodeName.toLowerCase();}}}j.fireEvent("tableClickEvent",{target:(m||j._elContainer),event:l});},_onDropdownChange:function(j,i){var k=g.getTarget(j);i.fireEvent("dropdownChangeEvent",{event:j,target:k});},configs:null,getId:function(){return this._sId;},toString:function(){return"DataTable instance "+this._sId;},getDataSource:function(){return this._oDataSource;},getColumnSet:function(){return this._oColumnSet;},getRecordSet:function(){return this._oRecordSet;},getState:function(){return{totalRecords:this.get("paginator")?this.get("paginator").get("totalRecords"):this._oRecordSet.getLength(),pagination:this.get("paginator")?this.get("paginator").getState():null,sortedBy:this.get("sortedBy"),selectedRows:this.getSelectedRows(),selectedCells:this.getSelectedCells()};},getContainerEl:function(){return this._elContainer;},getTableEl:function(){return this._elTable;},getTheadEl:function(){return this._elThead;},getTbodyEl:function(){return this._elTbody;},getMsgTbodyEl:function(){return this._elMsgTbody;},getMsgTdEl:function(){return this._elMsgTd;},getTrEl:function(k){if(k instanceof YAHOO.widget.Record){return document.getElementById(k.getId());}else{if(h.isNumber(k)){var j=c.getElementsByClassName(d.CLASS_REC,"tr",this._elTbody);return j&&j[k]?j[k]:null;}else{if(k){var i=(h.isString(k))?document.getElementById(k):k;if(i&&i.ownerDocument==document){if(i.nodeName.toLowerCase()!="tr"){i=c.getAncestorByTagName(i,"tr");}return i;}}}}return null;},getFirstTrEl:function(){var k=this._elTbody.rows,j=0;while(k[j]){if(this.getRecord(k[j])){return k[j];}j++;}return null;},getLastTrEl:function(){var k=this._elTbody.rows,j=k.length-1;while(j>-1){if(this.getRecord(k[j])){return k[j];}j--;}return null;},getNextTrEl:function(l,i){var j=this.getTrIndex(l);if(j!==null){var k=this._elTbody.rows;if(i){while(j<k.length-1){l=k[j+1];if(this.getRecord(l)){return l;}j++;}}else{if(j<k.length-1){return k[j+1];}}}return null;},getPreviousTrEl:function(l,i){var j=this.getTrIndex(l);if(j!==null){var k=this._elTbody.rows;if(i){while(j>0){l=k[j-1];if(this.getRecord(l)){return l;}j--;}}else{if(j>0){return k[j-1];}}}return null;},getCellIndex:function(k){k=this.getTdEl(k);if(k){if(b.ie>0){var l=0,n=k.parentNode,m=n.childNodes,j=m.length;for(;l<j;l++){if(m[l]==k){return l;}}}else{return k.cellIndex;}}},getTdLinerEl:function(i){var j=this.getTdEl(i);return j.firstChild||null;},getTdEl:function(i){var n;var l=c.get(i);if(l&&(l.ownerDocument==document)){if(l.nodeName.toLowerCase()!="td"){n=c.getAncestorByTagName(l,"td");}else{n=l;}if(n&&((n.parentNode.parentNode==this._elTbody)||(n.parentNode.parentNode===null)||(n.parentNode.parentNode.nodeType===11))){return n;}}else{if(i){var m,k;if(h.isString(i.columnKey)&&h.isString(i.recordId)){m=this.getRecord(i.recordId);var o=this.getColumn(i.columnKey);if(o){k=o.getKeyIndex();}}if(i.record&&i.column&&i.column.getKeyIndex){m=i.record;k=i.column.getKeyIndex();}var j=this.getTrEl(m);if((k!==null)&&j&&j.cells&&j.cells.length>0){return j.cells[k]||null;}}}return null;},getFirstTdEl:function(j){var i=h.isValue(j)?this.getTrEl(j):this.getFirstTrEl();if(i){if(i.cells&&i.cells.length>0){return i.cells[0];}else{if(i.childNodes&&i.childNodes.length>0){return i.childNodes[0];}}}return null;},getLastTdEl:function(j){var i=h.isValue(j)?this.getTrEl(j):this.getLastTrEl();if(i){if(i.cells&&i.cells.length>0){return i.cells[i.cells.length-1];}else{if(i.childNodes&&i.childNodes.length>0){return i.childNodes[i.childNodes.length-1];}}}return null;},getNextTdEl:function(i){var m=this.getTdEl(i);if(m){var k=this.getCellIndex(m);var j=this.getTrEl(m);if(j.cells&&(j.cells.length)>0&&(k<j.cells.length-1)){return j.cells[k+1];}else{if(j.childNodes&&(j.childNodes.length)>0&&(k<j.childNodes.length-1)){return j.childNodes[k+1];}else{var l=this.getNextTrEl(j);if(l){return l.cells[0];}}}}return null;},getPreviousTdEl:function(i){var m=this.getTdEl(i);if(m){var k=this.getCellIndex(m);var j=this.getTrEl(m);if(k>0){if(j.cells&&j.cells.length>0){return j.cells[k-1];}else{if(j.childNodes&&j.childNodes.length>0){return j.childNodes[k-1];}}}else{var l=this.getPreviousTrEl(j);if(l){return this.getLastTdEl(l);}}}return null;},getAboveTdEl:function(j,i){var m=this.getTdEl(j);if(m){var l=this.getPreviousTrEl(m,i);if(l){var k=this.getCellIndex(m);if(l.cells&&l.cells.length>0){return l.cells[k]?l.cells[k]:null;}else{if(l.childNodes&&l.childNodes.length>0){return l.childNodes[k]?l.childNodes[k]:null;}}}}return null;},getBelowTdEl:function(j,i){var m=this.getTdEl(j);if(m){var l=this.getNextTrEl(m,i);if(l){var k=this.getCellIndex(m);if(l.cells&&l.cells.length>0){return l.cells[k]?l.cells[k]:null;}else{if(l.childNodes&&l.childNodes.length>0){return l.childNodes[k]?l.childNodes[k]:null;}}}}return null;},getThLinerEl:function(j){var i=this.getColumn(j);return(i)?i.getThLinerEl():null;},getThEl:function(k){var l;if(k instanceof YAHOO.widget.Column){var j=k;l=j.getThEl();if(l){return l;}}else{var i=c.get(k);if(i&&(i.ownerDocument==document)){if(i.nodeName.toLowerCase()!="th"){l=c.getAncestorByTagName(i,"th");
-}else{l=i;}return l;}}return null;},getTrIndex:function(m){var i=this.getRecord(m),k=this.getRecordIndex(i),l;if(i){l=this.getTrEl(i);if(l){return l.sectionRowIndex;}else{var j=this.get("paginator");if(j){return j.get("recordOffset")+k;}else{return k;}}}return null;},load:function(i){i=i||{};(i.datasource||this._oDataSource).sendRequest(i.request||this.get("initialRequest"),i.callback||{success:this.onDataReturnInitializeTable,failure:this.onDataReturnInitializeTable,scope:this,argument:this.getState()});},initializeTable:function(){this._bInit=true;this._oRecordSet.reset();var i=this.get("paginator");if(i){i.set("totalRecords",0);}this._unselectAllTrEls();this._unselectAllTdEls();this._aSelections=null;this._oAnchorRecord=null;this._oAnchorCell=null;this.set("sortedBy",null);},_runRenderChain:function(){this._oChainRender.run();},_getViewRecords:function(){var i=this.get("paginator");if(i){return this._oRecordSet.getRecords(i.getStartIndex(),i.getRowsPerPage());}else{return this._oRecordSet.getRecords();}},render:function(){this._oChainRender.stop();this.fireEvent("beforeRenderEvent");var r,p,o,s,l=this._getViewRecords();var m=this._elTbody,q=this.get("renderLoopSize"),t=l.length;if(t>0){m.style.display="none";while(m.lastChild){m.removeChild(m.lastChild);}m.style.display="";this._oChainRender.add({method:function(u){if((this instanceof d)&&this._sId){var k=u.nCurrentRecord,w=((u.nCurrentRecord+u.nLoopLength)>t)?t:(u.nCurrentRecord+u.nLoopLength),j,v;m.style.display="none";for(;k<w;k++){j=c.get(l[k].getId());j=j||this._addTrEl(l[k]);v=m.childNodes[k]||null;m.insertBefore(j,v);}m.style.display="";u.nCurrentRecord=k;}},scope:this,iterations:(q>0)?Math.ceil(t/q):1,argument:{nCurrentRecord:0,nLoopLength:(q>0)?q:t},timeout:(q>0)?0:-1});this._oChainRender.add({method:function(i){if((this instanceof d)&&this._sId){while(m.rows.length>t){m.removeChild(m.lastChild);}this._setFirstRow();this._setLastRow();this._setRowStripes();this._setSelections();}},scope:this,timeout:(q>0)?0:-1});}else{var n=m.rows.length;if(n>0){this._oChainRender.add({method:function(k){if((this instanceof d)&&this._sId){var j=k.nCurrent,v=k.nLoopLength,u=(j-v<0)?0:j-v;m.style.display="none";for(;j>u;j--){m.deleteRow(-1);}m.style.display="";k.nCurrent=j;}},scope:this,iterations:(q>0)?Math.ceil(n/q):1,argument:{nCurrent:n,nLoopLength:(q>0)?q:n},timeout:(q>0)?0:-1});}}this._runRenderChain();},disable:function(){this._disabled=true;var i=this._elTable;var j=this._elMask;j.style.width=i.offsetWidth+"px";j.style.height=i.offsetHeight+"px";j.style.left=i.offsetLeft+"px";j.style.display="";this.fireEvent("disableEvent");},undisable:function(){this._disabled=false;this._elMask.style.display="none";this.fireEvent("undisableEvent");},isDisabled:function(){return this._disabled;},destroy:function(){var k=this.toString();this._oChainRender.stop();this._destroyColumnHelpers();var m;for(var l=0,j=this._oColumnSet.flat.length;l<j;l++){m=this._oColumnSet.flat[l].editor;if(m&&m.destroy){m.destroy();this._oColumnSet.flat[l].editor=null;}}this._destroyPaginator();this._oRecordSet.unsubscribeAll();this.unsubscribeAll();g.removeListener(document,"click",this._onDocumentClick);this._destroyContainerEl(this._elContainer);for(var n in this){if(h.hasOwnProperty(this,n)){this[n]=null;}}d._nCurrentCount--;if(d._nCurrentCount<1){if(d._elDynStyleNode){document.getElementsByTagName("head")[0].removeChild(d._elDynStyleNode);d._elDynStyleNode=null;}}},showTableMessage:function(j,i){var k=this._elMsgTd;if(h.isString(j)){k.firstChild.innerHTML=j;}if(h.isString(i)){k.className=i;}this._elMsgTbody.style.display="";this.fireEvent("tableMsgShowEvent",{html:j,className:i});},hideTableMessage:function(){if(this._elMsgTbody.style.display!="none"){this._elMsgTbody.style.display="none";this._elMsgTbody.parentNode.style.width="";this.fireEvent("tableMsgHideEvent");}},focus:function(){this.focusTbodyEl();},focusTheadEl:function(){this._focusEl(this._elThead);},focusTbodyEl:function(){this._focusEl(this._elTbody);},onShow:function(){this.validateColumnWidths();for(var m=this._oColumnSet.keys,l=0,j=m.length,k;l<j;l++){k=m[l];if(k._ddResizer){k._ddResizer.resetResizerEl();}}},getRecordIndex:function(l){var k;if(!h.isNumber(l)){if(l instanceof YAHOO.widget.Record){return this._oRecordSet.getRecordIndex(l);}else{var j=this.getTrEl(l);if(j){k=j.sectionRowIndex;}}}else{k=l;}if(h.isNumber(k)){var i=this.get("paginator");if(i){return i.get("recordOffset")+k;}else{return k;}}return null;},getRecord:function(k){var j=this._oRecordSet.getRecord(k);if(!j){var i=this.getTrEl(k);if(i){j=this._oRecordSet.getRecord(i.id);}}if(j instanceof YAHOO.widget.Record){return this._oRecordSet.getRecord(j);}else{return null;}},getColumn:function(m){var o=this._oColumnSet.getColumn(m);if(!o){var n=this.getTdEl(m);if(n){o=this._oColumnSet.getColumn(this.getCellIndex(n));}else{n=this.getThEl(m);if(n){var k=this._oColumnSet.flat;for(var l=0,j=k.length;l<j;l++){if(k[l].getThEl().id===n.id){o=k[l];}}}}}if(!o){}return o;},getColumnById:function(i){return this._oColumnSet.getColumnById(i);},getColumnSortDir:function(k,l){if(k.sortOptions&&k.sortOptions.defaultDir){if(k.sortOptions.defaultDir=="asc"){k.sortOptions.defaultDir=d.CLASS_ASC;}else{if(k.sortOptions.defaultDir=="desc"){k.sortOptions.defaultDir=d.CLASS_DESC;}}}var j=(k.sortOptions&&k.sortOptions.defaultDir)?k.sortOptions.defaultDir:d.CLASS_ASC;var i=false;l=l||this.get("sortedBy");if(l&&(l.key===k.key)){i=true;if(l.dir){j=(l.dir===d.CLASS_ASC)?d.CLASS_DESC:d.CLASS_ASC;}else{j=(j===d.CLASS_ASC)?d.CLASS_DESC:d.CLASS_ASC;}}return j;},doBeforeSortColumn:function(j,i){this.showTableMessage(this.get("MSG_LOADING"),d.CLASS_LOADING);return true;},sortColumn:function(m,j){if(m&&(m instanceof YAHOO.widget.Column)){if(!m.sortable){c.addClass(this.getThEl(m),d.CLASS_SORTABLE);}if(j&&(j!==d.CLASS_ASC)&&(j!==d.CLASS_DESC)){j=null;}var n=j||this.getColumnSortDir(m);var l=this.get("sortedBy")||{};var t=(l.key===m.key)?true:false;var p=this.doBeforeSortColumn(m,n);
-if(p){if(this.get("dynamicData")){var s=this.getState();if(s.pagination){s.pagination.recordOffset=0;}s.sortedBy={key:m.key,dir:n};var k=this.get("generateRequest")(s,this);this.unselectAllRows();this.unselectAllCells();var r={success:this.onDataReturnSetRows,failure:this.onDataReturnSetRows,argument:s,scope:this};this._oDataSource.sendRequest(k,r);}else{var i=(m.sortOptions&&h.isFunction(m.sortOptions.sortFunction))?m.sortOptions.sortFunction:null;if(!t||j||i){i=i||this.get("sortFunction");var q=(m.sortOptions&&m.sortOptions.field)?m.sortOptions.field:m.field;this._oRecordSet.sortRecords(i,((n==d.CLASS_DESC)?true:false),q);}else{this._oRecordSet.reverseRecords();}var o=this.get("paginator");if(o){o.setPage(1,true);}this.render();this.set("sortedBy",{key:m.key,dir:n,column:m});}this.fireEvent("columnSortEvent",{column:m,dir:n});return;}}},setColumnWidth:function(j,i){if(!(j instanceof YAHOO.widget.Column)){j=this.getColumn(j);}if(j){if(h.isNumber(i)){i=(i>j.minWidth)?i:j.minWidth;j.width=i;this._setColumnWidth(j,i+"px");this.fireEvent("columnSetWidthEvent",{column:j,width:i});}else{if(i===null){j.width=i;this._setColumnWidth(j,"auto");this.validateColumnWidths(j);this.fireEvent("columnUnsetWidthEvent",{column:j});}}this._clearTrTemplateEl();}else{}},_setColumnWidth:function(j,i,k){if(j&&(j.getKeyIndex()!==null)){k=k||(((i==="")||(i==="auto"))?"visible":"hidden");if(!d._bDynStylesFallback){this._setColumnWidthDynStyles(j,i,k);}else{this._setColumnWidthDynFunction(j,i,k);}}else{}},_setColumnWidthDynStyles:function(m,l,n){var j=d._elDynStyleNode,k;if(!j){j=document.createElement("style");j.type="text/css";j=document.getElementsByTagName("head").item(0).appendChild(j);d._elDynStyleNode=j;}if(j){var i="."+this.getId()+"-col-"+m.getSanitizedKey()+" ."+d.CLASS_LINER;if(this._elTbody){this._elTbody.style.display="none";}k=d._oDynStyles[i];if(!k){if(j.styleSheet&&j.styleSheet.addRule){j.styleSheet.addRule(i,"overflow:"+n);j.styleSheet.addRule(i,"width:"+l);k=j.styleSheet.rules[j.styleSheet.rules.length-1];d._oDynStyles[i]=k;}else{if(j.sheet&&j.sheet.insertRule){j.sheet.insertRule(i+" {overflow:"+n+";width:"+l+";}",j.sheet.cssRules.length);k=j.sheet.cssRules[j.sheet.cssRules.length-1];d._oDynStyles[i]=k;}}}else{k.style.overflow=n;k.style.width=l;}if(this._elTbody){this._elTbody.style.display="";}}if(!k){d._bDynStylesFallback=true;this._setColumnWidthDynFunction(m,l);}},_setColumnWidthDynFunction:function(r,m,s){if(m=="auto"){m="";}var l=this._elTbody?this._elTbody.rows.length:0;if(!this._aDynFunctions[l]){var q,p,o;var t=["var colIdx=oColumn.getKeyIndex();","oColumn.getThLinerEl().style.overflow="];for(q=l-1,p=2;q>=0;--q){t[p++]="this._elTbody.rows[";t[p++]=q;t[p++]="].cells[colIdx].firstChild.style.overflow=";}t[p]="sOverflow;";t[p+1]="oColumn.getThLinerEl().style.width=";for(q=l-1,o=p+2;q>=0;--q){t[o++]="this._elTbody.rows[";t[o++]=q;t[o++]="].cells[colIdx].firstChild.style.width=";}t[o]="sWidth;";this._aDynFunctions[l]=new Function("oColumn","sWidth","sOverflow",t.join(""));}var n=this._aDynFunctions[l];if(n){n.call(this,r,m,s);}},validateColumnWidths:function(o){var l=this._elColgroup;var q=l.cloneNode(true);var p=false;var n=this._oColumnSet.keys;var k;if(o&&!o.hidden&&!o.width&&(o.getKeyIndex()!==null)){k=o.getThLinerEl();if((o.minWidth>0)&&(k.offsetWidth<o.minWidth)){q.childNodes[o.getKeyIndex()].style.width=o.minWidth+(parseInt(c.getStyle(k,"paddingLeft"),10)|0)+(parseInt(c.getStyle(k,"paddingRight"),10)|0)+"px";p=true;}else{if((o.maxAutoWidth>0)&&(k.offsetWidth>o.maxAutoWidth)){this._setColumnWidth(o,o.maxAutoWidth+"px","hidden");}}}else{for(var m=0,j=n.length;m<j;m++){o=n[m];if(!o.hidden&&!o.width){k=o.getThLinerEl();if((o.minWidth>0)&&(k.offsetWidth<o.minWidth)){q.childNodes[m].style.width=o.minWidth+(parseInt(c.getStyle(k,"paddingLeft"),10)|0)+(parseInt(c.getStyle(k,"paddingRight"),10)|0)+"px";p=true;}else{if((o.maxAutoWidth>0)&&(k.offsetWidth>o.maxAutoWidth)){this._setColumnWidth(o,o.maxAutoWidth+"px","hidden");}}}}}if(p){l.parentNode.replaceChild(q,l);this._elColgroup=q;}},_clearMinWidth:function(i){if(i.getKeyIndex()!==null){this._elColgroup.childNodes[i.getKeyIndex()].style.width="";}},_restoreMinWidth:function(i){if(i.minWidth&&(i.getKeyIndex()!==null)){this._elColgroup.childNodes[i.getKeyIndex()].style.width=i.minWidth+"px";}},hideColumn:function(r){if(!(r instanceof YAHOO.widget.Column)){r=this.getColumn(r);}if(r&&!r.hidden&&r.getTreeIndex()!==null){var o=this.getTbodyEl().rows;var n=o.length;var m=this._oColumnSet.getDescendants(r);for(var q=0,s=m.length;q<s;q++){var t=m[q];t.hidden=true;c.addClass(t.getThEl(),d.CLASS_HIDDEN);var k=t.getKeyIndex();if(k!==null){this._clearMinWidth(r);for(var p=0;p<n;p++){c.addClass(o[p].cells[k],d.CLASS_HIDDEN);}}this.fireEvent("columnHideEvent",{column:t});}this._repaintOpera();this._clearTrTemplateEl();}else{}},showColumn:function(r){if(!(r instanceof YAHOO.widget.Column)){r=this.getColumn(r);}if(r&&r.hidden&&(r.getTreeIndex()!==null)){var o=this.getTbodyEl().rows;var n=o.length;var m=this._oColumnSet.getDescendants(r);for(var q=0,s=m.length;q<s;q++){var t=m[q];t.hidden=false;c.removeClass(t.getThEl(),d.CLASS_HIDDEN);var k=t.getKeyIndex();if(k!==null){this._restoreMinWidth(r);for(var p=0;p<n;p++){c.removeClass(o[p].cells[k],d.CLASS_HIDDEN);}}this.fireEvent("columnShowEvent",{column:t});}this._clearTrTemplateEl();}else{}},removeColumn:function(p){if(!(p instanceof YAHOO.widget.Column)){p=this.getColumn(p);}if(p){var m=p.getTreeIndex();if(m!==null){var o,r,q=p.getKeyIndex();if(q===null){var u=[];var j=this._oColumnSet.getDescendants(p);for(o=0,r=j.length;o<r;o++){var s=j[o].getKeyIndex();if(s!==null){u[u.length]=s;}}if(u.length>0){q=u;}}else{q=[q];}if(q!==null){q.sort(function(v,i){return YAHOO.util.Sort.compare(v,i);});this._destroyTheadEl();var k=this._oColumnSet.getDefinitions();p=k.splice(m,1)[0];this._initColumnSet(k);this._initTheadEl();for(o=q.length-1;o>-1;o--){this._removeColgroupColEl(q[o]);}var t=this._elTbody.rows;if(t.length>0){var n=this.get("renderLoopSize"),l=t.length;
-this._oChainRender.add({method:function(y){if((this instanceof d)&&this._sId){var x=y.nCurrentRow,v=n>0?Math.min(x+n,t.length):t.length,z=y.aIndexes,w;for(;x<v;++x){for(w=z.length-1;w>-1;w--){t[x].removeChild(t[x].childNodes[z[w]]);}}y.nCurrentRow=x;}},iterations:(n>0)?Math.ceil(l/n):1,argument:{nCurrentRow:0,aIndexes:q},scope:this,timeout:(n>0)?0:-1});this._runRenderChain();}this.fireEvent("columnRemoveEvent",{column:p});return p;}}}},insertColumn:function(r,s){if(r instanceof YAHOO.widget.Column){r=r.getDefinition();}else{if(r.constructor!==Object){return;}}var x=this._oColumnSet;if(!h.isValue(s)||!h.isNumber(s)){s=x.tree[0].length;}this._destroyTheadEl();var z=this._oColumnSet.getDefinitions();z.splice(s,0,r);this._initColumnSet(z);this._initTheadEl();x=this._oColumnSet;var n=x.tree[0][s];var p,t,w=[];var l=x.getDescendants(n);for(p=0,t=l.length;p<t;p++){var u=l[p].getKeyIndex();if(u!==null){w[w.length]=u;}}if(w.length>0){var y=w.sort(function(A,i){return YAHOO.util.Sort.compare(A,i);})[0];for(p=w.length-1;p>-1;p--){this._insertColgroupColEl(w[p]);}var v=this._elTbody.rows;if(v.length>0){var o=this.get("renderLoopSize"),m=v.length;var k=[],q;for(p=0,t=w.length;p<t;p++){var j=w[p];q=this._getTrTemplateEl().childNodes[p].cloneNode(true);q=this._formatTdEl(this._oColumnSet.keys[j],q,j,(j===this._oColumnSet.keys.length-1));k[j]=q;}this._oChainRender.add({method:function(D){if((this instanceof d)&&this._sId){var C=D.nCurrentRow,B,F=D.descKeyIndexes,A=o>0?Math.min(C+o,v.length):v.length,E;for(;C<A;++C){E=v[C].childNodes[y]||null;for(B=F.length-1;B>-1;B--){v[C].insertBefore(D.aTdTemplates[F[B]].cloneNode(true),E);}}D.nCurrentRow=C;}},iterations:(o>0)?Math.ceil(m/o):1,argument:{nCurrentRow:0,aTdTemplates:k,descKeyIndexes:w},scope:this,timeout:(o>0)?0:-1});this._runRenderChain();}this.fireEvent("columnInsertEvent",{column:r,index:s});return n;}},reorderColumn:function(q,r){if(!(q instanceof YAHOO.widget.Column)){q=this.getColumn(q);}if(q&&YAHOO.lang.isNumber(r)){var z=q.getTreeIndex();if((z!==null)&&(z!==r)){var p,s,l=q.getKeyIndex(),k,v=[],t;if(l===null){k=this._oColumnSet.getDescendants(q);for(p=0,s=k.length;p<s;p++){t=k[p].getKeyIndex();if(t!==null){v[v.length]=t;}}if(v.length>0){l=v;}}else{l=[l];}if(l!==null){l.sort(function(A,i){return YAHOO.util.Sort.compare(A,i);});this._destroyTheadEl();var w=this._oColumnSet.getDefinitions();var j=w.splice(z,1)[0];w.splice(r,0,j);this._initColumnSet(w);this._initTheadEl();var n=this._oColumnSet.tree[0][r];var y=n.getKeyIndex();if(y===null){v=[];k=this._oColumnSet.getDescendants(n);for(p=0,s=k.length;p<s;p++){t=k[p].getKeyIndex();if(t!==null){v[v.length]=t;}}if(v.length>0){y=v;}}else{y=[y];}var x=y.sort(function(A,i){return YAHOO.util.Sort.compare(A,i);})[0];this._reorderColgroupColEl(l,x);var u=this._elTbody.rows;if(u.length>0){var o=this.get("renderLoopSize"),m=u.length;this._oChainRender.add({method:function(D){if((this instanceof d)&&this._sId){var C=D.nCurrentRow,B,F,E,A=o>0?Math.min(C+o,u.length):u.length,H=D.aIndexes,G;for(;C<A;++C){F=[];G=u[C];for(B=H.length-1;B>-1;B--){F.push(G.removeChild(G.childNodes[H[B]]));}E=G.childNodes[x]||null;for(B=F.length-1;B>-1;B--){G.insertBefore(F[B],E);}}D.nCurrentRow=C;}},iterations:(o>0)?Math.ceil(m/o):1,argument:{nCurrentRow:0,aIndexes:l},scope:this,timeout:(o>0)?0:-1});this._runRenderChain();}this.fireEvent("columnReorderEvent",{column:n,oldIndex:z});return n;}}}},selectColumn:function(k){k=this.getColumn(k);if(k&&!k.selected){if(k.getKeyIndex()!==null){k.selected=true;var l=k.getThEl();c.addClass(l,d.CLASS_SELECTED);var j=this.getTbodyEl().rows;var i=this._oChainRender;i.add({method:function(m){if((this instanceof d)&&this._sId&&j[m.rowIndex]&&j[m.rowIndex].cells[m.cellIndex]){c.addClass(j[m.rowIndex].cells[m.cellIndex],d.CLASS_SELECTED);}m.rowIndex++;},scope:this,iterations:j.length,argument:{rowIndex:0,cellIndex:k.getKeyIndex()}});this._clearTrTemplateEl();this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnSelectEvent",{column:k});}else{}}},unselectColumn:function(k){k=this.getColumn(k);if(k&&k.selected){if(k.getKeyIndex()!==null){k.selected=false;var l=k.getThEl();c.removeClass(l,d.CLASS_SELECTED);var j=this.getTbodyEl().rows;var i=this._oChainRender;i.add({method:function(m){if((this instanceof d)&&this._sId&&j[m.rowIndex]&&j[m.rowIndex].cells[m.cellIndex]){c.removeClass(j[m.rowIndex].cells[m.cellIndex],d.CLASS_SELECTED);}m.rowIndex++;},scope:this,iterations:j.length,argument:{rowIndex:0,cellIndex:k.getKeyIndex()}});this._clearTrTemplateEl();this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnUnselectEvent",{column:k});}else{}}},getSelectedColumns:function(n){var k=[];var l=this._oColumnSet.keys;for(var m=0,j=l.length;m<j;m++){if(l[m].selected){k[k.length]=l[m];}}return k;},highlightColumn:function(i){var l=this.getColumn(i);if(l&&(l.getKeyIndex()!==null)){var m=l.getThEl();c.addClass(m,d.CLASS_HIGHLIGHTED);var k=this.getTbodyEl().rows;var j=this._oChainRender;j.add({method:function(n){if((this instanceof d)&&this._sId&&k[n.rowIndex]&&k[n.rowIndex].cells[n.cellIndex]){c.addClass(k[n.rowIndex].cells[n.cellIndex],d.CLASS_HIGHLIGHTED);}n.rowIndex++;},scope:this,iterations:k.length,argument:{rowIndex:0,cellIndex:l.getKeyIndex()},timeout:-1});this._elTbody.style.display="none";this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnHighlightEvent",{column:l});}else{}},unhighlightColumn:function(i){var l=this.getColumn(i);if(l&&(l.getKeyIndex()!==null)){var m=l.getThEl();c.removeClass(m,d.CLASS_HIGHLIGHTED);var k=this.getTbodyEl().rows;var j=this._oChainRender;j.add({method:function(n){if((this instanceof d)&&this._sId&&k[n.rowIndex]&&k[n.rowIndex].cells[n.cellIndex]){c.removeClass(k[n.rowIndex].cells[n.cellIndex],d.CLASS_HIGHLIGHTED);}n.rowIndex++;},scope:this,iterations:k.length,argument:{rowIndex:0,cellIndex:l.getKeyIndex()},timeout:-1});this._elTbody.style.display="none";
-this._runRenderChain();this._elTbody.style.display="";this.fireEvent("columnUnhighlightEvent",{column:l});}else{}},addRow:function(o,k){if(h.isNumber(k)&&(k<0||k>this._oRecordSet.getLength())){return;}if(o&&h.isObject(o)){var m=this._oRecordSet.addRecord(o,k);if(m){var i;var j=this.get("paginator");if(j){var n=j.get("totalRecords");if(n!==e.Paginator.VALUE_UNLIMITED){j.set("totalRecords",n+1);}i=this.getRecordIndex(m);var l=(j.getPageRecords())[1];if(i<=l){this.render();}this.fireEvent("rowAddEvent",{record:m});return;}else{i=this.getRecordIndex(m);if(h.isNumber(i)){this._oChainRender.add({method:function(r){if((this instanceof d)&&this._sId){var s=r.record;var p=r.recIndex;var t=this._addTrEl(s);if(t){var q=(this._elTbody.rows[p])?this._elTbody.rows[p]:null;this._elTbody.insertBefore(t,q);if(p===0){this._setFirstRow();}if(q===null){this._setLastRow();}this._setRowStripes();this.hideTableMessage();this.fireEvent("rowAddEvent",{record:s});}}},argument:{record:m,recIndex:i},scope:this,timeout:(this.get("renderLoopSize")>0)?0:-1});this._runRenderChain();return;}}}}},addRows:function(k,n){if(h.isNumber(n)&&(n<0||n>this._oRecordSet.getLength())){return;}if(h.isArray(k)){var o=this._oRecordSet.addRecords(k,n);if(o){var s=this.getRecordIndex(o[0]);var r=this.get("paginator");if(r){var p=r.get("totalRecords");if(p!==e.Paginator.VALUE_UNLIMITED){r.set("totalRecords",p+o.length);}var q=(r.getPageRecords())[1];if(s<=q){this.render();}this.fireEvent("rowsAddEvent",{records:o});return;}else{var m=this.get("renderLoopSize");var j=s+k.length;var i=(j-s);var l=(s>=this._elTbody.rows.length);this._oChainRender.add({method:function(x){if((this instanceof d)&&this._sId){var y=x.aRecords,w=x.nCurrentRow,v=x.nCurrentRecord,t=m>0?Math.min(w+m,j):j,z=document.createDocumentFragment(),u=(this._elTbody.rows[w])?this._elTbody.rows[w]:null;for(;w<t;w++,v++){z.appendChild(this._addTrEl(y[v]));}this._elTbody.insertBefore(z,u);x.nCurrentRow=w;x.nCurrentRecord=v;}},iterations:(m>0)?Math.ceil(j/m):1,argument:{nCurrentRow:s,nCurrentRecord:0,aRecords:o},scope:this,timeout:(m>0)?0:-1});this._oChainRender.add({method:function(u){var t=u.recIndex;if(t===0){this._setFirstRow();}if(u.isLast){this._setLastRow();}this._setRowStripes();this.fireEvent("rowsAddEvent",{records:o});},argument:{recIndex:s,isLast:l},scope:this,timeout:-1});this._runRenderChain();this.hideTableMessage();return;}}}},updateRow:function(u,k){var r=u;if(!h.isNumber(r)){r=this.getRecordIndex(u);}if(h.isNumber(r)&&(r>=0)){var s=this._oRecordSet,q=s.getRecord(r);if(q){var o=this._oRecordSet.setRecord(k,r),j=this.getTrEl(q),p=q?q.getData():null;if(o){var t=this._aSelections||[],n=0,l=q.getId(),m=o.getId();for(;n<t.length;n++){if((t[n]===l)){t[n]=m;}else{if(t[n].recordId===l){t[n].recordId=m;}}}if(this._oAnchorRecord&&this._oAnchorRecord.getId()===l){this._oAnchorRecord=o;}if(this._oAnchorCell&&this._oAnchorCell.record.getId()===l){this._oAnchorCell.record=o;}this._oChainRender.add({method:function(){if((this instanceof d)&&this._sId){var v=this.get("paginator");if(v){var i=(v.getPageRecords())[0],w=(v.getPageRecords())[1];if((r>=i)||(r<=w)){this.render();}}else{if(j){this._updateTrEl(j,o);}else{this.getTbodyEl().appendChild(this._addTrEl(o));}}this.fireEvent("rowUpdateEvent",{record:o,oldData:p});}},scope:this,timeout:(this.get("renderLoopSize")>0)?0:-1});this._runRenderChain();return;}}}return;},updateRows:function(A,m){if(h.isArray(m)){var s=A,l=this._oRecordSet,o=l.getLength();if(!h.isNumber(A)){s=this.getRecordIndex(A);}if(h.isNumber(s)&&(s>=0)&&(s<l.getLength())){var E=s+m.length,B=l.getRecords(s,m.length),G=l.setRecords(m,s);if(G){var t=this._aSelections||[],D=0,C,u,x,z,y=this._oAnchorRecord?this._oAnchorRecord.getId():null,n=this._oAnchorCell?this._oAnchorCell.record.getId():null;for(;D<B.length;D++){z=B[D].getId();u=G[D];x=u.getId();for(C=0;C<t.length;C++){if((t[C]===z)){t[C]=x;}else{if(t[C].recordId===z){t[C].recordId=x;}}}if(y&&y===z){this._oAnchorRecord=u;}if(n&&n===z){this._oAnchorCell.record=u;}}var F=this.get("paginator");if(F){var r=(F.getPageRecords())[0],p=(F.getPageRecords())[1];if((s>=r)||(E<=p)){this.render();}this.fireEvent("rowsAddEvent",{newRecords:G,oldRecords:B});return;}else{var k=this.get("renderLoopSize"),v=m.length,w=(E>=o),q=(E>o);this._oChainRender.add({method:function(K){if((this instanceof d)&&this._sId){var L=K.aRecords,J=K.nCurrentRow,I=K.nDataPointer,H=k>0?Math.min(J+k,s+L.length):s+L.length;for(;J<H;J++,I++){if(q&&(J>=o)){this._elTbody.appendChild(this._addTrEl(L[I]));}else{this._updateTrEl(this._elTbody.rows[J],L[I]);}}K.nCurrentRow=J;K.nDataPointer=I;}},iterations:(k>0)?Math.ceil(v/k):1,argument:{nCurrentRow:s,aRecords:G,nDataPointer:0,isAdding:q},scope:this,timeout:(k>0)?0:-1});this._oChainRender.add({method:function(j){var i=j.recIndex;if(i===0){this._setFirstRow();}if(j.isLast){this._setLastRow();}this._setRowStripes();this.fireEvent("rowsAddEvent",{newRecords:G,oldRecords:B});},argument:{recIndex:s,isLast:w},scope:this,timeout:-1});this._runRenderChain();this.hideTableMessage();return;}}}}},deleteRow:function(s){var k=(h.isNumber(s))?s:this.getRecordIndex(s);if(h.isNumber(k)){var t=this.getRecord(k);if(t){var m=this.getTrIndex(k);var p=t.getId();var r=this._aSelections||[];for(var n=r.length-1;n>-1;n--){if((h.isString(r[n])&&(r[n]===p))||(h.isObject(r[n])&&(r[n].recordId===p))){r.splice(n,1);}}var l=this._oRecordSet.deleteRecord(k);if(l){var q=this.get("paginator");if(q){var o=q.get("totalRecords"),i=q.getPageRecords();if(o!==e.Paginator.VALUE_UNLIMITED){q.set("totalRecords",o-1);}if(!i||k<=i[1]){this.render();}this._oChainRender.add({method:function(){if((this instanceof d)&&this._sId){this.fireEvent("rowDeleteEvent",{recordIndex:k,oldData:l,trElIndex:m});}},scope:this,timeout:(this.get("renderLoopSize")>0)?0:-1});this._runRenderChain();}else{if(h.isNumber(m)){this._oChainRender.add({method:function(){if((this instanceof d)&&this._sId){var j=(k===this._oRecordSet.getLength());this._deleteTrEl(m);if(this._elTbody.rows.length>0){if(m===0){this._setFirstRow();
-}if(j){this._setLastRow();}if(m!=this._elTbody.rows.length){this._setRowStripes(m);}}this.fireEvent("rowDeleteEvent",{recordIndex:k,oldData:l,trElIndex:m});}},scope:this,timeout:(this.get("renderLoopSize")>0)?0:-1});this._runRenderChain();return;}}}}}return null;},deleteRows:function(y,s){var l=(h.isNumber(y))?y:this.getRecordIndex(y);if(h.isNumber(l)){var z=this.getRecord(l);if(z){var m=this.getTrIndex(l);var u=z.getId();var x=this._aSelections||[];for(var q=x.length-1;q>-1;q--){if((h.isString(x[q])&&(x[q]===u))||(h.isObject(x[q])&&(x[q].recordId===u))){x.splice(q,1);}}var n=l;var w=l;if(s&&h.isNumber(s)){n=(s>0)?l+s-1:l;w=(s>0)?l:l+s+1;s=(s>0)?s:s*-1;if(w<0){w=0;s=n-w+1;}}else{s=1;}var p=this._oRecordSet.deleteRecords(w,s);if(p){var v=this.get("paginator"),r=this.get("renderLoopSize");if(v){var t=v.get("totalRecords"),k=v.getPageRecords();if(t!==e.Paginator.VALUE_UNLIMITED){v.set("totalRecords",t-p.length);}if(!k||w<=k[1]){this.render();}this._oChainRender.add({method:function(j){if((this instanceof d)&&this._sId){this.fireEvent("rowsDeleteEvent",{recordIndex:w,oldData:p,count:s});}},scope:this,timeout:(r>0)?0:-1});this._runRenderChain();return;}else{if(h.isNumber(m)){var o=w;var i=s;this._oChainRender.add({method:function(B){if((this instanceof d)&&this._sId){var A=B.nCurrentRow,j=(r>0)?(Math.max(A-r,o)-1):o-1;for(;A>j;--A){this._deleteTrEl(A);}B.nCurrentRow=A;}},iterations:(r>0)?Math.ceil(s/r):1,argument:{nCurrentRow:n},scope:this,timeout:(r>0)?0:-1});this._oChainRender.add({method:function(){if(this._elTbody.rows.length>0){this._setFirstRow();this._setLastRow();this._setRowStripes();}this.fireEvent("rowsDeleteEvent",{recordIndex:w,oldData:p,count:s});},scope:this,timeout:-1});this._runRenderChain();return;}}}}}return null;},formatCell:function(j,l,m){if(!l){l=this.getRecord(j);}if(!m){m=this.getColumn(this.getCellIndex(j.parentNode));}if(l&&m){var i=m.field;var n=l.getData(i);var k=typeof m.formatter==="function"?m.formatter:d.Formatter[m.formatter+""]||d.Formatter.defaultFormatter;if(k){k.call(this,j,l,m,n);}else{j.innerHTML=n;}this.fireEvent("cellFormatEvent",{record:l,column:m,key:m.key,el:j});}else{}},updateCell:function(k,m,o,j){m=(m instanceof YAHOO.widget.Column)?m:this.getColumn(m);if(m&&m.getField()&&(k instanceof YAHOO.widget.Record)){var l=m.getField(),n=k.getData(l);this._oRecordSet.updateRecordValue(k,l,o);var i=this.getTdEl({record:k,column:m});if(i){this._oChainRender.add({method:function(){if((this instanceof d)&&this._sId){this.formatCell(i.firstChild,k,m);this.fireEvent("cellUpdateEvent",{record:k,column:m,oldData:n});}},scope:this,timeout:(this.get("renderLoopSize")>0)?0:-1});if(!j){this._runRenderChain();}}else{this.fireEvent("cellUpdateEvent",{record:k,column:m,oldData:n});}}},_updatePaginator:function(j){var i=this.get("paginator");if(i&&j!==i){i.unsubscribe("changeRequest",this.onPaginatorChangeRequest,this,true);}if(j){j.subscribe("changeRequest",this.onPaginatorChangeRequest,this,true);}},_handlePaginatorChange:function(l){if(l.prevValue===l.newValue){return;}var n=l.newValue,m=l.prevValue,k=this._defaultPaginatorContainers();if(m){if(m.getContainerNodes()[0]==k[0]){m.set("containers",[]);}m.destroy();if(k[0]){if(n&&!n.getContainerNodes().length){n.set("containers",k);}else{for(var j=k.length-1;j>=0;--j){if(k[j]){k[j].parentNode.removeChild(k[j]);}}}}}if(!this._bInit){this.render();}if(n){this.renderPaginator();}},_defaultPaginatorContainers:function(l){var j=this._sId+"-paginator0",k=this._sId+"-paginator1",i=c.get(j),m=c.get(k);if(l&&(!i||!m)){if(!i){i=document.createElement("div");i.id=j;c.addClass(i,d.CLASS_PAGINATOR);this._elContainer.insertBefore(i,this._elContainer.firstChild);}if(!m){m=document.createElement("div");m.id=k;c.addClass(m,d.CLASS_PAGINATOR);this._elContainer.appendChild(m);}}return[i,m];},_destroyPaginator:function(){var i=this.get("paginator");if(i){i.destroy();}},renderPaginator:function(){var i=this.get("paginator");if(!i){return;}if(!i.getContainerNodes().length){i.set("containers",this._defaultPaginatorContainers(true));}i.render();},doBeforePaginatorChange:function(i){this.showTableMessage(this.get("MSG_LOADING"),d.CLASS_LOADING);return true;},onPaginatorChangeRequest:function(l){var j=this.doBeforePaginatorChange(l);if(j){if(this.get("dynamicData")){var i=this.getState();i.pagination=l;var k=this.get("generateRequest")(i,this);this.unselectAllRows();this.unselectAllCells();var m={success:this.onDataReturnSetRows,failure:this.onDataReturnSetRows,argument:i,scope:this};this._oDataSource.sendRequest(k,m);}else{l.paginator.setStartIndex(l.recordOffset,true);l.paginator.setRowsPerPage(l.rowsPerPage,true);this.render();}}else{}},_elLastHighlightedTd:null,_aSelections:null,_oAnchorRecord:null,_oAnchorCell:null,_unselectAllTrEls:function(){var i=c.getElementsByClassName(d.CLASS_SELECTED,"tr",this._elTbody);c.removeClass(i,d.CLASS_SELECTED);},_getSelectionTrigger:function(){var l=this.get("selectionMode");var k={};var o,i,j,n,m;if((l=="cellblock")||(l=="cellrange")||(l=="singlecell")){o=this.getLastSelectedCell();if(!o){return null;}else{i=this.getRecord(o.recordId);j=this.getRecordIndex(i);n=this.getTrEl(i);m=this.getTrIndex(n);if(m===null){return null;}else{k.record=i;k.recordIndex=j;k.el=this.getTdEl(o);k.trIndex=m;k.column=this.getColumn(o.columnKey);k.colKeyIndex=k.column.getKeyIndex();k.cell=o;return k;}}}else{i=this.getLastSelectedRecord();if(!i){return null;}else{i=this.getRecord(i);j=this.getRecordIndex(i);n=this.getTrEl(i);m=this.getTrIndex(n);if(m===null){return null;}else{k.record=i;k.recordIndex=j;k.el=n;k.trIndex=m;return k;}}}},_getSelectionAnchor:function(k){var j=this.get("selectionMode");var l={};var m,o,i;if((j=="cellblock")||(j=="cellrange")||(j=="singlecell")){var n=this._oAnchorCell;if(!n){if(k){n=this._oAnchorCell=k.cell;}else{return null;}}m=this._oAnchorCell.record;o=this._oRecordSet.getRecordIndex(m);i=this.getTrIndex(m);if(i===null){if(o<this.getRecordIndex(this.getFirstTrEl())){i=0;}else{i=this.getRecordIndex(this.getLastTrEl());
-}}l.record=m;l.recordIndex=o;l.trIndex=i;l.column=this._oAnchorCell.column;l.colKeyIndex=l.column.getKeyIndex();l.cell=n;return l;}else{m=this._oAnchorRecord;if(!m){if(k){m=this._oAnchorRecord=k.record;}else{return null;}}o=this.getRecordIndex(m);i=this.getTrIndex(m);if(i===null){if(o<this.getRecordIndex(this.getFirstTrEl())){i=0;}else{i=this.getRecordIndex(this.getLastTrEl());}}l.record=m;l.recordIndex=o;l.trIndex=i;return l;}},_handleStandardSelectionByMouse:function(k){var j=k.target;var m=this.getTrEl(j);if(m){var p=k.event;var s=p.shiftKey;var o=p.ctrlKey||((navigator.userAgent.toLowerCase().indexOf("mac")!=-1)&&p.metaKey);var r=this.getRecord(m);var l=this._oRecordSet.getRecordIndex(r);var q=this._getSelectionAnchor();var n;if(s&&o){if(q){if(this.isSelected(q.record)){if(q.recordIndex<l){for(n=q.recordIndex+1;n<=l;n++){if(!this.isSelected(n)){this.selectRow(n);}}}else{for(n=q.recordIndex-1;n>=l;n--){if(!this.isSelected(n)){this.selectRow(n);}}}}else{if(q.recordIndex<l){for(n=q.recordIndex+1;n<=l-1;n++){if(this.isSelected(n)){this.unselectRow(n);}}}else{for(n=l+1;n<=q.recordIndex-1;n++){if(this.isSelected(n)){this.unselectRow(n);}}}this.selectRow(r);}}else{this._oAnchorRecord=r;if(this.isSelected(r)){this.unselectRow(r);}else{this.selectRow(r);}}}else{if(s){this.unselectAllRows();if(q){if(q.recordIndex<l){for(n=q.recordIndex;n<=l;n++){this.selectRow(n);}}else{for(n=q.recordIndex;n>=l;n--){this.selectRow(n);}}}else{this._oAnchorRecord=r;this.selectRow(r);}}else{if(o){this._oAnchorRecord=r;if(this.isSelected(r)){this.unselectRow(r);}else{this.selectRow(r);}}else{this._handleSingleSelectionByMouse(k);return;}}}}},_handleStandardSelectionByKey:function(m){var i=g.getCharCode(m);if((i==38)||(i==40)){var k=m.shiftKey;var j=this._getSelectionTrigger();if(!j){return null;}g.stopEvent(m);var l=this._getSelectionAnchor(j);if(k){if((i==40)&&(l.recordIndex<=j.trIndex)){this.selectRow(this.getNextTrEl(j.el));}else{if((i==38)&&(l.recordIndex>=j.trIndex)){this.selectRow(this.getPreviousTrEl(j.el));}else{this.unselectRow(j.el);}}}else{this._handleSingleSelectionByKey(m);}}},_handleSingleSelectionByMouse:function(k){var l=k.target;var j=this.getTrEl(l);if(j){var i=this.getRecord(j);this._oAnchorRecord=i;this.unselectAllRows();this.selectRow(i);}},_handleSingleSelectionByKey:function(l){var i=g.getCharCode(l);if((i==38)||(i==40)){var j=this._getSelectionTrigger();if(!j){return null;}g.stopEvent(l);var k;if(i==38){k=this.getPreviousTrEl(j.el);if(k===null){k=this.getFirstTrEl();}}else{if(i==40){k=this.getNextTrEl(j.el);if(k===null){k=this.getLastTrEl();}}}this.unselectAllRows();this.selectRow(k);this._oAnchorRecord=this.getRecord(k);}},_handleCellBlockSelectionByMouse:function(A){var B=A.target;var l=this.getTdEl(B);if(l){var z=A.event;var q=z.shiftKey;var m=z.ctrlKey||((navigator.userAgent.toLowerCase().indexOf("mac")!=-1)&&z.metaKey);var s=this.getTrEl(l);var r=this.getTrIndex(s);var v=this.getColumn(l);var w=v.getKeyIndex();var u=this.getRecord(s);var D=this._oRecordSet.getRecordIndex(u);var p={record:u,column:v};var t=this._getSelectionAnchor();var o=this.getTbodyEl().rows;var n,k,C,y,x;if(q&&m){if(t){if(this.isSelected(t.cell)){if(t.recordIndex===D){if(t.colKeyIndex<w){for(y=t.colKeyIndex+1;y<=w;y++){this.selectCell(s.cells[y]);}}else{if(w<t.colKeyIndex){for(y=w;y<t.colKeyIndex;y++){this.selectCell(s.cells[y]);}}}}else{if(t.recordIndex<D){n=Math.min(t.colKeyIndex,w);k=Math.max(t.colKeyIndex,w);for(y=t.trIndex;y<=r;y++){for(x=n;x<=k;x++){this.selectCell(o[y].cells[x]);}}}else{n=Math.min(t.trIndex,w);k=Math.max(t.trIndex,w);for(y=t.trIndex;y>=r;y--){for(x=k;x>=n;x--){this.selectCell(o[y].cells[x]);}}}}}else{if(t.recordIndex===D){if(t.colKeyIndex<w){for(y=t.colKeyIndex+1;y<w;y++){this.unselectCell(s.cells[y]);}}else{if(w<t.colKeyIndex){for(y=w+1;y<t.colKeyIndex;y++){this.unselectCell(s.cells[y]);}}}}if(t.recordIndex<D){for(y=t.trIndex;y<=r;y++){C=o[y];for(x=0;x<C.cells.length;x++){if(C.sectionRowIndex===t.trIndex){if(x>t.colKeyIndex){this.unselectCell(C.cells[x]);}}else{if(C.sectionRowIndex===r){if(x<w){this.unselectCell(C.cells[x]);}}else{this.unselectCell(C.cells[x]);}}}}}else{for(y=r;y<=t.trIndex;y++){C=o[y];for(x=0;x<C.cells.length;x++){if(C.sectionRowIndex==r){if(x>w){this.unselectCell(C.cells[x]);}}else{if(C.sectionRowIndex==t.trIndex){if(x<t.colKeyIndex){this.unselectCell(C.cells[x]);}}else{this.unselectCell(C.cells[x]);}}}}}this.selectCell(l);}}else{this._oAnchorCell=p;if(this.isSelected(p)){this.unselectCell(p);}else{this.selectCell(p);}}}else{if(q){this.unselectAllCells();if(t){if(t.recordIndex===D){if(t.colKeyIndex<w){for(y=t.colKeyIndex;y<=w;y++){this.selectCell(s.cells[y]);}}else{if(w<t.colKeyIndex){for(y=w;y<=t.colKeyIndex;y++){this.selectCell(s.cells[y]);}}}}else{if(t.recordIndex<D){n=Math.min(t.colKeyIndex,w);k=Math.max(t.colKeyIndex,w);for(y=t.trIndex;y<=r;y++){for(x=n;x<=k;x++){this.selectCell(o[y].cells[x]);}}}else{n=Math.min(t.colKeyIndex,w);k=Math.max(t.colKeyIndex,w);for(y=r;y<=t.trIndex;y++){for(x=n;x<=k;x++){this.selectCell(o[y].cells[x]);}}}}}else{this._oAnchorCell=p;this.selectCell(p);}}else{if(m){this._oAnchorCell=p;if(this.isSelected(p)){this.unselectCell(p);}else{this.selectCell(p);}}else{this._handleSingleCellSelectionByMouse(A);}}}}},_handleCellBlockSelectionByKey:function(o){var j=g.getCharCode(o);var t=o.shiftKey;if((j==9)||!t){this._handleSingleCellSelectionByKey(o);return;}if((j>36)&&(j<41)){var u=this._getSelectionTrigger();if(!u){return null;}g.stopEvent(o);var r=this._getSelectionAnchor(u);var k,s,l,q,m;var p=this.getTbodyEl().rows;var n=u.el.parentNode;if(j==40){if(r.recordIndex<=u.recordIndex){m=this.getNextTrEl(u.el);if(m){s=r.colKeyIndex;l=u.colKeyIndex;if(s>l){for(k=s;k>=l;k--){q=m.cells[k];this.selectCell(q);}}else{for(k=s;k<=l;k++){q=m.cells[k];this.selectCell(q);}}}}else{s=Math.min(r.colKeyIndex,u.colKeyIndex);l=Math.max(r.colKeyIndex,u.colKeyIndex);for(k=s;k<=l;k++){this.unselectCell(n.cells[k]);}}}else{if(j==38){if(r.recordIndex>=u.recordIndex){m=this.getPreviousTrEl(u.el);
-if(m){s=r.colKeyIndex;l=u.colKeyIndex;if(s>l){for(k=s;k>=l;k--){q=m.cells[k];this.selectCell(q);}}else{for(k=s;k<=l;k++){q=m.cells[k];this.selectCell(q);}}}}else{s=Math.min(r.colKeyIndex,u.colKeyIndex);l=Math.max(r.colKeyIndex,u.colKeyIndex);for(k=s;k<=l;k++){this.unselectCell(n.cells[k]);}}}else{if(j==39){if(r.colKeyIndex<=u.colKeyIndex){if(u.colKeyIndex<n.cells.length-1){s=r.trIndex;l=u.trIndex;if(s>l){for(k=s;k>=l;k--){q=p[k].cells[u.colKeyIndex+1];this.selectCell(q);}}else{for(k=s;k<=l;k++){q=p[k].cells[u.colKeyIndex+1];this.selectCell(q);}}}}else{s=Math.min(r.trIndex,u.trIndex);l=Math.max(r.trIndex,u.trIndex);for(k=s;k<=l;k++){this.unselectCell(p[k].cells[u.colKeyIndex]);}}}else{if(j==37){if(r.colKeyIndex>=u.colKeyIndex){if(u.colKeyIndex>0){s=r.trIndex;l=u.trIndex;if(s>l){for(k=s;k>=l;k--){q=p[k].cells[u.colKeyIndex-1];this.selectCell(q);}}else{for(k=s;k<=l;k++){q=p[k].cells[u.colKeyIndex-1];this.selectCell(q);}}}}else{s=Math.min(r.trIndex,u.trIndex);l=Math.max(r.trIndex,u.trIndex);for(k=s;k<=l;k++){this.unselectCell(p[k].cells[u.colKeyIndex]);}}}}}}}},_handleCellRangeSelectionByMouse:function(y){var z=y.target;var k=this.getTdEl(z);if(k){var x=y.event;var o=x.shiftKey;var l=x.ctrlKey||((navigator.userAgent.toLowerCase().indexOf("mac")!=-1)&&x.metaKey);var q=this.getTrEl(k);var p=this.getTrIndex(q);var t=this.getColumn(k);var u=t.getKeyIndex();var s=this.getRecord(q);var B=this._oRecordSet.getRecordIndex(s);var n={record:s,column:t};var r=this._getSelectionAnchor();var m=this.getTbodyEl().rows;var A,w,v;if(o&&l){if(r){if(this.isSelected(r.cell)){if(r.recordIndex===B){if(r.colKeyIndex<u){for(w=r.colKeyIndex+1;w<=u;w++){this.selectCell(q.cells[w]);}}else{if(u<r.colKeyIndex){for(w=u;w<r.colKeyIndex;w++){this.selectCell(q.cells[w]);}}}}else{if(r.recordIndex<B){for(w=r.colKeyIndex+1;w<q.cells.length;w++){this.selectCell(q.cells[w]);}for(w=r.trIndex+1;w<p;w++){for(v=0;v<m[w].cells.length;v++){this.selectCell(m[w].cells[v]);}}for(w=0;w<=u;w++){this.selectCell(q.cells[w]);}}else{for(w=u;w<q.cells.length;w++){this.selectCell(q.cells[w]);}for(w=p+1;w<r.trIndex;w++){for(v=0;v<m[w].cells.length;v++){this.selectCell(m[w].cells[v]);}}for(w=0;w<r.colKeyIndex;w++){this.selectCell(q.cells[w]);}}}}else{if(r.recordIndex===B){if(r.colKeyIndex<u){for(w=r.colKeyIndex+1;w<u;w++){this.unselectCell(q.cells[w]);}}else{if(u<r.colKeyIndex){for(w=u+1;w<r.colKeyIndex;w++){this.unselectCell(q.cells[w]);}}}}if(r.recordIndex<B){for(w=r.trIndex;w<=p;w++){A=m[w];for(v=0;v<A.cells.length;v++){if(A.sectionRowIndex===r.trIndex){if(v>r.colKeyIndex){this.unselectCell(A.cells[v]);}}else{if(A.sectionRowIndex===p){if(v<u){this.unselectCell(A.cells[v]);}}else{this.unselectCell(A.cells[v]);}}}}}else{for(w=p;w<=r.trIndex;w++){A=m[w];for(v=0;v<A.cells.length;v++){if(A.sectionRowIndex==p){if(v>u){this.unselectCell(A.cells[v]);}}else{if(A.sectionRowIndex==r.trIndex){if(v<r.colKeyIndex){this.unselectCell(A.cells[v]);}}else{this.unselectCell(A.cells[v]);}}}}}this.selectCell(k);}}else{this._oAnchorCell=n;if(this.isSelected(n)){this.unselectCell(n);}else{this.selectCell(n);}}}else{if(o){this.unselectAllCells();if(r){if(r.recordIndex===B){if(r.colKeyIndex<u){for(w=r.colKeyIndex;w<=u;w++){this.selectCell(q.cells[w]);}}else{if(u<r.colKeyIndex){for(w=u;w<=r.colKeyIndex;w++){this.selectCell(q.cells[w]);}}}}else{if(r.recordIndex<B){for(w=r.trIndex;w<=p;w++){A=m[w];for(v=0;v<A.cells.length;v++){if(A.sectionRowIndex==r.trIndex){if(v>=r.colKeyIndex){this.selectCell(A.cells[v]);}}else{if(A.sectionRowIndex==p){if(v<=u){this.selectCell(A.cells[v]);}}else{this.selectCell(A.cells[v]);}}}}}else{for(w=p;w<=r.trIndex;w++){A=m[w];for(v=0;v<A.cells.length;v++){if(A.sectionRowIndex==p){if(v>=u){this.selectCell(A.cells[v]);}}else{if(A.sectionRowIndex==r.trIndex){if(v<=r.colKeyIndex){this.selectCell(A.cells[v]);}}else{this.selectCell(A.cells[v]);}}}}}}}else{this._oAnchorCell=n;this.selectCell(n);}}else{if(l){this._oAnchorCell=n;if(this.isSelected(n)){this.unselectCell(n);}else{this.selectCell(n);}}else{this._handleSingleCellSelectionByMouse(y);}}}}},_handleCellRangeSelectionByKey:function(n){var j=g.getCharCode(n);var r=n.shiftKey;if((j==9)||!r){this._handleSingleCellSelectionByKey(n);return;}if((j>36)&&(j<41)){var s=this._getSelectionTrigger();if(!s){return null;}g.stopEvent(n);var q=this._getSelectionAnchor(s);var k,l,p;var o=this.getTbodyEl().rows;var m=s.el.parentNode;if(j==40){l=this.getNextTrEl(s.el);if(q.recordIndex<=s.recordIndex){for(k=s.colKeyIndex+1;k<m.cells.length;k++){p=m.cells[k];this.selectCell(p);}if(l){for(k=0;k<=s.colKeyIndex;k++){p=l.cells[k];this.selectCell(p);}}}else{for(k=s.colKeyIndex;k<m.cells.length;k++){this.unselectCell(m.cells[k]);}if(l){for(k=0;k<s.colKeyIndex;k++){this.unselectCell(l.cells[k]);}}}}else{if(j==38){l=this.getPreviousTrEl(s.el);if(q.recordIndex>=s.recordIndex){for(k=s.colKeyIndex-1;k>-1;k--){p=m.cells[k];this.selectCell(p);}if(l){for(k=m.cells.length-1;k>=s.colKeyIndex;k--){p=l.cells[k];this.selectCell(p);}}}else{for(k=s.colKeyIndex;k>-1;k--){this.unselectCell(m.cells[k]);}if(l){for(k=m.cells.length-1;k>s.colKeyIndex;k--){this.unselectCell(l.cells[k]);}}}}else{if(j==39){l=this.getNextTrEl(s.el);if(q.recordIndex<s.recordIndex){if(s.colKeyIndex<m.cells.length-1){p=m.cells[s.colKeyIndex+1];this.selectCell(p);}else{if(l){p=l.cells[0];this.selectCell(p);}}}else{if(q.recordIndex>s.recordIndex){this.unselectCell(m.cells[s.colKeyIndex]);if(s.colKeyIndex<m.cells.length-1){}else{}}else{if(q.colKeyIndex<=s.colKeyIndex){if(s.colKeyIndex<m.cells.length-1){p=m.cells[s.colKeyIndex+1];this.selectCell(p);}else{if(s.trIndex<o.length-1){p=l.cells[0];this.selectCell(p);}}}else{this.unselectCell(m.cells[s.colKeyIndex]);}}}}else{if(j==37){l=this.getPreviousTrEl(s.el);if(q.recordIndex<s.recordIndex){this.unselectCell(m.cells[s.colKeyIndex]);if(s.colKeyIndex>0){}else{}}else{if(q.recordIndex>s.recordIndex){if(s.colKeyIndex>0){p=m.cells[s.colKeyIndex-1];this.selectCell(p);}else{if(s.trIndex>0){p=l.cells[l.cells.length-1];this.selectCell(p);
-}}}else{if(q.colKeyIndex>=s.colKeyIndex){if(s.colKeyIndex>0){p=m.cells[s.colKeyIndex-1];this.selectCell(p);}else{if(s.trIndex>0){p=l.cells[l.cells.length-1];this.selectCell(p);}}}else{this.unselectCell(m.cells[s.colKeyIndex]);if(s.colKeyIndex>0){}else{}}}}}}}}}},_handleSingleCellSelectionByMouse:function(n){var o=n.target;var k=this.getTdEl(o);if(k){var j=this.getTrEl(k);var i=this.getRecord(j);var m=this.getColumn(k);var l={record:i,column:m};this._oAnchorCell=l;this.unselectAllCells();this.selectCell(l);}},_handleSingleCellSelectionByKey:function(m){var i=g.getCharCode(m);if((i==9)||((i>36)&&(i<41))){var k=m.shiftKey;var j=this._getSelectionTrigger();if(!j){return null;}var l;if(i==40){l=this.getBelowTdEl(j.el);if(l===null){l=j.el;}}else{if(i==38){l=this.getAboveTdEl(j.el);if(l===null){l=j.el;}}else{if((i==39)||(!k&&(i==9))){l=this.getNextTdEl(j.el);if(l===null){return;}}else{if((i==37)||(k&&(i==9))){l=this.getPreviousTdEl(j.el);if(l===null){return;}}}}}g.stopEvent(m);this.unselectAllCells();this.selectCell(l);this._oAnchorCell={record:this.getRecord(l),column:this.getColumn(l)};}},getSelectedTrEls:function(){return c.getElementsByClassName(d.CLASS_SELECTED,"tr",this._elTbody);},selectRow:function(p){var o,i;if(p instanceof YAHOO.widget.Record){o=this._oRecordSet.getRecord(p);i=this.getTrEl(o);}else{if(h.isNumber(p)){o=this.getRecord(p);i=this.getTrEl(o);}else{i=this.getTrEl(p);o=this.getRecord(i);}}if(o){var n=this._aSelections||[];var m=o.getId();var l=-1;if(n.indexOf){l=n.indexOf(m);}else{for(var k=n.length-1;k>-1;k--){if(n[k]===m){l=k;break;}}}if(l>-1){n.splice(l,1);}n.push(m);this._aSelections=n;if(!this._oAnchorRecord){this._oAnchorRecord=o;}if(i){c.addClass(i,d.CLASS_SELECTED);}this.fireEvent("rowSelectEvent",{record:o,el:i});}else{}},unselectRow:function(p){var i=this.getTrEl(p);var o;if(p instanceof YAHOO.widget.Record){o=this._oRecordSet.getRecord(p);}else{if(h.isNumber(p)){o=this.getRecord(p);}else{o=this.getRecord(i);}}if(o){var n=this._aSelections||[];var m=o.getId();var l=-1;if(n.indexOf){l=n.indexOf(m);}else{for(var k=n.length-1;k>-1;k--){if(n[k]===m){l=k;break;}}}if(l>-1){n.splice(l,1);this._aSelections=n;c.removeClass(i,d.CLASS_SELECTED);this.fireEvent("rowUnselectEvent",{record:o,el:i});return;}}},unselectAllRows:function(){var k=this._aSelections||[],m,l=[];for(var i=k.length-1;i>-1;i--){if(h.isString(k[i])){m=k.splice(i,1);l[l.length]=this.getRecord(h.isArray(m)?m[0]:m);}}this._aSelections=k;this._unselectAllTrEls();this.fireEvent("unselectAllRowsEvent",{records:l});},_unselectAllTdEls:function(){var i=c.getElementsByClassName(d.CLASS_SELECTED,"td",this._elTbody);c.removeClass(i,d.CLASS_SELECTED);},getSelectedTdEls:function(){return c.getElementsByClassName(d.CLASS_SELECTED,"td",this._elTbody);},selectCell:function(i){var p=this.getTdEl(i);if(p){var o=this.getRecord(p);var q=this.getColumn(this.getCellIndex(p));var m=q.getKey();if(o&&m){var n=this._aSelections||[];var l=o.getId();for(var k=n.length-1;k>-1;k--){if((n[k].recordId===l)&&(n[k].columnKey===m)){n.splice(k,1);break;}}n.push({recordId:l,columnKey:m});this._aSelections=n;if(!this._oAnchorCell){this._oAnchorCell={record:o,column:q};}c.addClass(p,d.CLASS_SELECTED);this.fireEvent("cellSelectEvent",{record:o,column:q,key:m,el:p});return;}}},unselectCell:function(i){var o=this.getTdEl(i);if(o){var n=this.getRecord(o);var p=this.getColumn(this.getCellIndex(o));var l=p.getKey();if(n&&l){var m=this._aSelections||[];var q=n.getId();for(var k=m.length-1;k>-1;k--){if((m[k].recordId===q)&&(m[k].columnKey===l)){m.splice(k,1);this._aSelections=m;c.removeClass(o,d.CLASS_SELECTED);this.fireEvent("cellUnselectEvent",{record:n,column:p,key:l,el:o});return;}}}}},unselectAllCells:function(){var k=this._aSelections||[];for(var i=k.length-1;i>-1;i--){if(h.isObject(k[i])){k.splice(i,1);}}this._aSelections=k;this._unselectAllTdEls();this.fireEvent("unselectAllCellsEvent");},isSelected:function(p){if(p&&(p.ownerDocument==document)){return(c.hasClass(this.getTdEl(p),d.CLASS_SELECTED)||c.hasClass(this.getTrEl(p),d.CLASS_SELECTED));}else{var n,k,i;var m=this._aSelections;if(m&&m.length>0){if(p instanceof YAHOO.widget.Record){n=p;}else{if(h.isNumber(p)){n=this.getRecord(p);}}if(n){k=n.getId();if(m.indexOf){if(m.indexOf(k)>-1){return true;}}else{for(i=m.length-1;i>-1;i--){if(m[i]===k){return true;}}}}else{if(p.record&&p.column){k=p.record.getId();var l=p.column.getKey();for(i=m.length-1;i>-1;i--){if((m[i].recordId===k)&&(m[i].columnKey===l)){return true;}}}}}}return false;},getSelectedRows:function(){var i=[];var l=this._aSelections||[];for(var k=0;k<l.length;k++){if(h.isString(l[k])){i.push(l[k]);}}return i;},getSelectedCells:function(){var k=[];var l=this._aSelections||[];for(var i=0;i<l.length;i++){if(l[i]&&h.isObject(l[i])){k.push(l[i]);}}return k;},getLastSelectedRecord:function(){var k=this._aSelections;if(k&&k.length>0){for(var j=k.length-1;j>-1;j--){if(h.isString(k[j])){return k[j];}}}},getLastSelectedCell:function(){var k=this._aSelections;if(k&&k.length>0){for(var j=k.length-1;j>-1;j--){if(k[j].recordId&&k[j].columnKey){return k[j];}}}},highlightRow:function(k){var i=this.getTrEl(k);if(i){var j=this.getRecord(i);c.addClass(i,d.CLASS_HIGHLIGHTED);this.fireEvent("rowHighlightEvent",{record:j,el:i});return;}},unhighlightRow:function(k){var i=this.getTrEl(k);if(i){var j=this.getRecord(i);c.removeClass(i,d.CLASS_HIGHLIGHTED);this.fireEvent("rowUnhighlightEvent",{record:j,el:i});return;}},highlightCell:function(i){var l=this.getTdEl(i);if(l){if(this._elLastHighlightedTd){this.unhighlightCell(this._elLastHighlightedTd);}var k=this.getRecord(l);var m=this.getColumn(this.getCellIndex(l));var j=m.getKey();c.addClass(l,d.CLASS_HIGHLIGHTED);this._elLastHighlightedTd=l;this.fireEvent("cellHighlightEvent",{record:k,column:m,key:j,el:l});return;}},unhighlightCell:function(i){var k=this.getTdEl(i);if(k){var j=this.getRecord(k);c.removeClass(k,d.CLASS_HIGHLIGHTED);this._elLastHighlightedTd=null;this.fireEvent("cellUnhighlightEvent",{record:j,column:this.getColumn(this.getCellIndex(k)),key:this.getColumn(this.getCellIndex(k)).getKey(),el:k});
-return;}},addCellEditor:function(j,i){j.editor=i;j.editor.subscribe("showEvent",this._onEditorShowEvent,this,true);j.editor.subscribe("keydownEvent",this._onEditorKeydownEvent,this,true);j.editor.subscribe("revertEvent",this._onEditorRevertEvent,this,true);j.editor.subscribe("saveEvent",this._onEditorSaveEvent,this,true);j.editor.subscribe("cancelEvent",this._onEditorCancelEvent,this,true);j.editor.subscribe("blurEvent",this._onEditorBlurEvent,this,true);j.editor.subscribe("blockEvent",this._onEditorBlockEvent,this,true);j.editor.subscribe("unblockEvent",this._onEditorUnblockEvent,this,true);},getCellEditor:function(){return this._oCellEditor;},showCellEditor:function(p,q,l){p=this.getTdEl(p);if(p){l=this.getColumn(p);if(l&&l.editor){var j=this._oCellEditor;if(j){if(this._oCellEditor.cancel){this._oCellEditor.cancel();}else{if(j.isActive){this.cancelCellEditor();}}}if(l.editor instanceof YAHOO.widget.BaseCellEditor){j=l.editor;var n=j.attach(this,p);if(n){j.render();j.move();n=this.doBeforeShowCellEditor(j);if(n){j.show();this._oCellEditor=j;}}}else{if(!q||!(q instanceof YAHOO.widget.Record)){q=this.getRecord(p);}if(!l||!(l instanceof YAHOO.widget.Column)){l=this.getColumn(p);}if(q&&l){if(!this._oCellEditor||this._oCellEditor.container){this._initCellEditorEl();}j=this._oCellEditor;j.cell=p;j.record=q;j.column=l;j.validator=(l.editorOptions&&h.isFunction(l.editorOptions.validator))?l.editorOptions.validator:null;j.value=q.getData(l.key);j.defaultValue=null;var k=j.container;var o=c.getX(p);var m=c.getY(p);if(isNaN(o)||isNaN(m)){o=p.offsetLeft+c.getX(this._elTbody.parentNode)-this._elTbody.scrollLeft;m=p.offsetTop+c.getY(this._elTbody.parentNode)-this._elTbody.scrollTop+this._elThead.offsetHeight;}k.style.left=o+"px";k.style.top=m+"px";this.doBeforeShowCellEditor(this._oCellEditor);k.style.display="";g.addListener(k,"keydown",function(s,r){if((s.keyCode==27)){r.cancelCellEditor();r.focusTbodyEl();}else{r.fireEvent("editorKeydownEvent",{editor:r._oCellEditor,event:s});}},this);var i;if(h.isString(l.editor)){switch(l.editor){case"checkbox":i=d.editCheckbox;break;case"date":i=d.editDate;break;case"dropdown":i=d.editDropdown;break;case"radio":i=d.editRadio;break;case"textarea":i=d.editTextarea;break;case"textbox":i=d.editTextbox;break;default:i=null;}}else{if(h.isFunction(l.editor)){i=l.editor;}}if(i){i(this._oCellEditor,this);if(!l.editorOptions||!l.editorOptions.disableBtns){this.showCellEditorBtns(k);}j.isActive=true;this.fireEvent("editorShowEvent",{editor:j});return;}}}}}},_initCellEditorEl:function(){var i=document.createElement("div");i.id=this._sId+"-celleditor";i.style.display="none";i.tabIndex=0;c.addClass(i,d.CLASS_EDITOR);var k=c.getFirstChild(document.body);if(k){i=c.insertBefore(i,k);}else{i=document.body.appendChild(i);}var j={};j.container=i;j.value=null;j.isActive=false;this._oCellEditor=j;},doBeforeShowCellEditor:function(i){return true;},saveCellEditor:function(){if(this._oCellEditor){if(this._oCellEditor.save){this._oCellEditor.save();}else{if(this._oCellEditor.isActive){var i=this._oCellEditor.value;var j=this._oCellEditor.record.getData(this._oCellEditor.column.key);if(this._oCellEditor.validator){i=this._oCellEditor.value=this._oCellEditor.validator.call(this,i,j,this._oCellEditor);if(i===null){this.resetCellEditor();this.fireEvent("editorRevertEvent",{editor:this._oCellEditor,oldData:j,newData:i});return;}}this._oRecordSet.updateRecordValue(this._oCellEditor.record,this._oCellEditor.column.key,this._oCellEditor.value);this.formatCell(this._oCellEditor.cell.firstChild,this._oCellEditor.record,this._oCellEditor.column);this._oChainRender.add({method:function(){this.validateColumnWidths();},scope:this});this._oChainRender.run();this.resetCellEditor();this.fireEvent("editorSaveEvent",{editor:this._oCellEditor,oldData:j,newData:i});}}}},cancelCellEditor:function(){if(this._oCellEditor){if(this._oCellEditor.cancel){this._oCellEditor.cancel();}else{if(this._oCellEditor.isActive){this.resetCellEditor();this.fireEvent("editorCancelEvent",{editor:this._oCellEditor});}}}},destroyCellEditor:function(){if(this._oCellEditor){this._oCellEditor.destroy();this._oCellEditor=null;}},_onEditorShowEvent:function(i){this.fireEvent("editorShowEvent",i);},_onEditorKeydownEvent:function(i){this.fireEvent("editorKeydownEvent",i);},_onEditorRevertEvent:function(i){this.fireEvent("editorRevertEvent",i);},_onEditorSaveEvent:function(i){this.fireEvent("editorSaveEvent",i);},_onEditorCancelEvent:function(i){this.fireEvent("editorCancelEvent",i);},_onEditorBlurEvent:function(i){this.fireEvent("editorBlurEvent",i);},_onEditorBlockEvent:function(i){this.fireEvent("editorBlockEvent",i);},_onEditorUnblockEvent:function(i){this.fireEvent("editorUnblockEvent",i);},onEditorBlurEvent:function(i){if(i.editor.disableBtns){if(i.editor.save){i.editor.save();}}else{if(i.editor.cancel){i.editor.cancel();}}},onEditorBlockEvent:function(i){this.disable();},onEditorUnblockEvent:function(i){this.undisable();},doBeforeLoadData:function(i,j,k){return true;},onEventSortColumn:function(k){var i=k.event;var m=k.target;var j=this.getThEl(m)||this.getTdEl(m);if(j){var l=this.getColumn(j);if(l.sortable){g.stopEvent(i);this.sortColumn(l);}}else{}},onEventSelectColumn:function(i){this.selectColumn(i.target);},onEventHighlightColumn:function(i){this.highlightColumn(i.target);},onEventUnhighlightColumn:function(i){this.unhighlightColumn(i.target);},onEventSelectRow:function(j){var i=this.get("selectionMode");if(i=="single"){this._handleSingleSelectionByMouse(j);}else{this._handleStandardSelectionByMouse(j);}},onEventSelectCell:function(j){var i=this.get("selectionMode");if(i=="cellblock"){this._handleCellBlockSelectionByMouse(j);}else{if(i=="cellrange"){this._handleCellRangeSelectionByMouse(j);}else{this._handleSingleCellSelectionByMouse(j);}}},onEventHighlightRow:function(i){this.highlightRow(i.target);},onEventUnhighlightRow:function(i){this.unhighlightRow(i.target);},onEventHighlightCell:function(i){this.highlightCell(i.target);
-},onEventUnhighlightCell:function(i){this.unhighlightCell(i.target);},onEventFormatCell:function(i){var l=i.target;var j=this.getTdEl(l);if(j){var k=this.getColumn(this.getCellIndex(j));this.formatCell(j.firstChild,this.getRecord(j),k);}else{}},onEventShowCellEditor:function(i){if(!this.isDisabled()){this.showCellEditor(i.target);}},onEventSaveCellEditor:function(i){if(this._oCellEditor){if(this._oCellEditor.save){this._oCellEditor.save();}else{this.saveCellEditor();}}},onEventCancelCellEditor:function(i){if(this._oCellEditor){if(this._oCellEditor.cancel){this._oCellEditor.cancel();}else{this.cancelCellEditor();}}},onDataReturnInitializeTable:function(i,j,k){if((this instanceof d)&&this._sId){this.initializeTable();this.onDataReturnSetRows(i,j,k);}},onDataReturnReplaceRows:function(m,l,n){if((this instanceof d)&&this._sId){this.fireEvent("dataReturnEvent",{request:m,response:l,payload:n});var j=this.doBeforeLoadData(m,l,n),k=this.get("paginator"),i=0;if(j&&l&&!l.error&&h.isArray(l.results)){this._oRecordSet.reset();if(this.get("dynamicData")){if(n&&n.pagination&&h.isNumber(n.pagination.recordOffset)){i=n.pagination.recordOffset;}else{if(k){i=k.getStartIndex();}}}this._oRecordSet.setRecords(l.results,i|0);this._handleDataReturnPayload(m,l,n);this.render();}else{if(j&&l.error){this.showTableMessage(this.get("MSG_ERROR"),d.CLASS_ERROR);}}}},onDataReturnAppendRows:function(j,k,l){if((this instanceof d)&&this._sId){this.fireEvent("dataReturnEvent",{request:j,response:k,payload:l});var i=this.doBeforeLoadData(j,k,l);if(i&&k&&!k.error&&h.isArray(k.results)){this.addRows(k.results);this._handleDataReturnPayload(j,k,l);}else{if(i&&k.error){this.showTableMessage(this.get("MSG_ERROR"),d.CLASS_ERROR);}}}},onDataReturnInsertRows:function(j,k,l){if((this instanceof d)&&this._sId){this.fireEvent("dataReturnEvent",{request:j,response:k,payload:l});var i=this.doBeforeLoadData(j,k,l);if(i&&k&&!k.error&&h.isArray(k.results)){this.addRows(k.results,(l?l.insertIndex:0));this._handleDataReturnPayload(j,k,l);}else{if(i&&k.error){this.showTableMessage(this.get("MSG_ERROR"),d.CLASS_ERROR);}}}},onDataReturnUpdateRows:function(j,k,l){if((this instanceof d)&&this._sId){this.fireEvent("dataReturnEvent",{request:j,response:k,payload:l});var i=this.doBeforeLoadData(j,k,l);if(i&&k&&!k.error&&h.isArray(k.results)){this.updateRows((l?l.updateIndex:0),k.results);this._handleDataReturnPayload(j,k,l);}else{if(i&&k.error){this.showTableMessage(this.get("MSG_ERROR"),d.CLASS_ERROR);}}}},onDataReturnSetRows:function(m,l,n){if((this instanceof d)&&this._sId){this.fireEvent("dataReturnEvent",{request:m,response:l,payload:n});var j=this.doBeforeLoadData(m,l,n),k=this.get("paginator"),i=0;if(j&&l&&!l.error&&h.isArray(l.results)){if(this.get("dynamicData")){if(n&&n.pagination&&h.isNumber(n.pagination.recordOffset)){i=n.pagination.recordOffset;}else{if(k){i=k.getStartIndex();}}this._oRecordSet.reset();}this._oRecordSet.setRecords(l.results,i|0);this._handleDataReturnPayload(m,l,n);this.render();}else{if(j&&l.error){this.showTableMessage(this.get("MSG_ERROR"),d.CLASS_ERROR);}}}else{}},handleDataReturnPayload:function(j,i,k){return k||{};},_handleDataReturnPayload:function(k,j,l){l=this.handleDataReturnPayload(k,j,l);if(l){var i=this.get("paginator");if(i){if(this.get("dynamicData")){if(e.Paginator.isNumeric(l.totalRecords)){i.set("totalRecords",l.totalRecords);}}else{i.set("totalRecords",this._oRecordSet.getLength());}if(h.isObject(l.pagination)){i.set("rowsPerPage",l.pagination.rowsPerPage);i.set("recordOffset",l.pagination.recordOffset);}}if(l.sortedBy){this.set("sortedBy",l.sortedBy);}else{if(l.sorting){this.set("sortedBy",l.sorting);}}}},showCellEditorBtns:function(k){var l=k.appendChild(document.createElement("div"));c.addClass(l,d.CLASS_BUTTON);var j=l.appendChild(document.createElement("button"));c.addClass(j,d.CLASS_DEFAULT);j.innerHTML="OK";g.addListener(j,"click",function(n,m){m.onEventSaveCellEditor(n,m);m.focusTbodyEl();},this,true);var i=l.appendChild(document.createElement("button"));i.innerHTML="Cancel";g.addListener(i,"click",function(n,m){m.onEventCancelCellEditor(n,m);m.focusTbodyEl();},this,true);},resetCellEditor:function(){var i=this._oCellEditor.container;i.style.display="none";g.purgeElement(i,true);i.innerHTML="";this._oCellEditor.value=null;this._oCellEditor.isActive=false;},getBody:function(){return this.getTbodyEl();},getCell:function(i){return this.getTdEl(i);},getRow:function(i){return this.getTrEl(i);},refreshView:function(){this.render();},select:function(k){if(!h.isArray(k)){k=[k];}for(var j=0;j<k.length;j++){this.selectRow(k[j]);}},onEventEditCell:function(i){this.onEventShowCellEditor(i);},_syncColWidths:function(){this.validateColumnWidths();}});d.prototype.onDataReturnSetRecords=d.prototype.onDataReturnSetRows;d.prototype.onPaginatorChange=d.prototype.onPaginatorChangeRequest;d.editCheckbox=function(){};d.editDate=function(){};d.editDropdown=function(){};d.editRadio=function(){};d.editTextarea=function(){};d.editTextbox=function(){};})();(function(){var c=YAHOO.lang,f=YAHOO.util,e=YAHOO.widget,a=YAHOO.env.ua,d=f.Dom,j=f.Event,i=f.DataSourceBase,g=e.DataTable,b=e.Paginator;e.ScrollingDataTable=function(n,m,k,l){l=l||{};if(l.scrollable){l.scrollable=false;}this._init();e.ScrollingDataTable.superclass.constructor.call(this,n,m,k,l);this.subscribe("columnShowEvent",this._onColumnChange);};var h=e.ScrollingDataTable;c.augmentObject(h,{CLASS_HEADER:"yui-dt-hd",CLASS_BODY:"yui-dt-bd"});c.extend(h,g,{_elHdContainer:null,_elHdTable:null,_elBdContainer:null,_elBdThead:null,_elTmpContainer:null,_elTmpTable:null,_bScrollbarX:null,initAttributes:function(k){k=k||{};h.superclass.initAttributes.call(this,k);this.setAttributeConfig("width",{value:null,validator:c.isString,method:function(l){if(this._elHdContainer&&this._elBdContainer){this._elHdContainer.style.width=l;this._elBdContainer.style.width=l;this._syncScrollX();this._syncScrollOverhang();}}});this.setAttributeConfig("height",{value:null,validator:c.isString,method:function(l){if(this._elHdContainer&&this._elBdContainer){this._elBdContainer.style.height=l;
-this._syncScrollX();this._syncScrollY();this._syncScrollOverhang();}}});this.setAttributeConfig("COLOR_COLUMNFILLER",{value:"#F2F2F2",validator:c.isString,method:function(l){if(this._elHdContainer){this._elHdContainer.style.backgroundColor=l;}}});},_init:function(){this._elHdContainer=null;this._elHdTable=null;this._elBdContainer=null;this._elBdThead=null;this._elTmpContainer=null;this._elTmpTable=null;},_initDomElements:function(k){this._initContainerEl(k);if(this._elContainer&&this._elHdContainer&&this._elBdContainer){this._initTableEl();if(this._elHdTable&&this._elTable){this._initColgroupEl(this._elHdTable);this._initTheadEl(this._elHdTable,this._elTable);this._initTbodyEl(this._elTable);this._initMsgTbodyEl(this._elTable);}}if(!this._elContainer||!this._elTable||!this._elColgroup||!this._elThead||!this._elTbody||!this._elMsgTbody||!this._elHdTable||!this._elBdThead){return false;}else{return true;}},_destroyContainerEl:function(k){d.removeClass(k,g.CLASS_SCROLLABLE);h.superclass._destroyContainerEl.call(this,k);this._elHdContainer=null;this._elBdContainer=null;},_initContainerEl:function(l){h.superclass._initContainerEl.call(this,l);if(this._elContainer){l=this._elContainer;d.addClass(l,g.CLASS_SCROLLABLE);var k=document.createElement("div");k.style.width=this.get("width")||"";k.style.backgroundColor=this.get("COLOR_COLUMNFILLER");d.addClass(k,h.CLASS_HEADER);this._elHdContainer=k;l.appendChild(k);var m=document.createElement("div");m.style.width=this.get("width")||"";m.style.height=this.get("height")||"";d.addClass(m,h.CLASS_BODY);j.addListener(m,"scroll",this._onScroll,this);this._elBdContainer=m;l.appendChild(m);}},_initCaptionEl:function(k){},_destroyHdTableEl:function(){var k=this._elHdTable;if(k){j.purgeElement(k,true);k.parentNode.removeChild(k);this._elBdThead=null;}},_initTableEl:function(){if(this._elHdContainer){this._destroyHdTableEl();this._elHdTable=this._elHdContainer.appendChild(document.createElement("table"));j.delegate(this._elHdTable,"mouseenter",this._onTableMouseover,"thead ."+g.CLASS_LABEL,this);j.delegate(this._elHdTable,"mouseleave",this._onTableMouseout,"thead ."+g.CLASS_LABEL,this);}h.superclass._initTableEl.call(this,this._elBdContainer);},_initTheadEl:function(l,k){l=l||this._elHdTable;k=k||this._elTable;this._initBdTheadEl(k);h.superclass._initTheadEl.call(this,l);},_initThEl:function(l,k){h.superclass._initThEl.call(this,l,k);l.id=this.getId()+"-fixedth-"+k.getSanitizedKey();},_destroyBdTheadEl:function(){var k=this._elBdThead;if(k){var l=k.parentNode;j.purgeElement(k,true);l.removeChild(k);this._elBdThead=null;this._destroyColumnHelpers();}},_initBdTheadEl:function(t){if(t){this._destroyBdTheadEl();var p=t.insertBefore(document.createElement("thead"),t.firstChild);var v=this._oColumnSet,u=v.tree,o,l,s,q,n,m,r;for(q=0,m=u.length;q<m;q++){l=p.appendChild(document.createElement("tr"));for(n=0,r=u[q].length;n<r;n++){s=u[q][n];o=l.appendChild(document.createElement("th"));this._initBdThEl(o,s,q,n);}}this._elBdThead=p;}},_initBdThEl:function(n,m){n.id=this.getId()+"-th-"+m.getSanitizedKey();n.rowSpan=m.getRowspan();n.colSpan=m.getColspan();if(m.abbr){n.abbr=m.abbr;}var l=m.getKey();var k=c.isValue(m.label)?m.label:l;n.innerHTML=k;},_initTbodyEl:function(k){h.superclass._initTbodyEl.call(this,k);k.style.marginTop=(this._elTbody.offsetTop>0)?"-"+this._elTbody.offsetTop+"px":0;},_focusEl:function(l){l=l||this._elTbody;var k=this;this._storeScrollPositions();setTimeout(function(){setTimeout(function(){try{l.focus();k._restoreScrollPositions();}catch(m){}},0);},0);},_runRenderChain:function(){this._storeScrollPositions();this._oChainRender.run();},_storeScrollPositions:function(){this._nScrollTop=this._elBdContainer.scrollTop;this._nScrollLeft=this._elBdContainer.scrollLeft;},clearScrollPositions:function(){this._nScrollTop=0;this._nScrollLeft=0;},_restoreScrollPositions:function(){if(this._nScrollTop){this._elBdContainer.scrollTop=this._nScrollTop;this._nScrollTop=null;}if(this._nScrollLeft){this._elBdContainer.scrollLeft=this._nScrollLeft;this._elHdContainer.scrollLeft=this._nScrollLeft;this._nScrollLeft=null;}},_validateColumnWidth:function(n,k){if(!n.width&&!n.hidden){var p=n.getThEl();if(n._calculatedWidth){this._setColumnWidth(n,"auto","visible");}if(p.offsetWidth!==k.offsetWidth){var m=(p.offsetWidth>k.offsetWidth)?n.getThLinerEl():k.firstChild;var l=Math.max(0,(m.offsetWidth-(parseInt(d.getStyle(m,"paddingLeft"),10)|0)-(parseInt(d.getStyle(m,"paddingRight"),10)|0)),n.minWidth);var o="visible";if((n.maxAutoWidth>0)&&(l>n.maxAutoWidth)){l=n.maxAutoWidth;o="hidden";}this._elTbody.style.display="none";this._setColumnWidth(n,l+"px",o);n._calculatedWidth=l;this._elTbody.style.display="";}}},validateColumnWidths:function(s){var u=this._oColumnSet.keys,w=u.length,l=this.getFirstTrEl();if(a.ie){this._setOverhangValue(1);}if(u&&l&&(l.childNodes.length===w)){var m=this.get("width");if(m){this._elHdContainer.style.width="";this._elBdContainer.style.width="";}this._elContainer.style.width="";if(s&&c.isNumber(s.getKeyIndex())){this._validateColumnWidth(s,l.childNodes[s.getKeyIndex()]);}else{var t,k=[],o,q,r;for(q=0;q<w;q++){s=u[q];if(!s.width&&!s.hidden&&s._calculatedWidth){k[k.length]=s;}}this._elTbody.style.display="none";for(q=0,r=k.length;q<r;q++){this._setColumnWidth(k[q],"auto","visible");}this._elTbody.style.display="";k=[];for(q=0;q<w;q++){s=u[q];t=l.childNodes[q];if(!s.width&&!s.hidden){var n=s.getThEl();if(n.offsetWidth!==t.offsetWidth){var v=(n.offsetWidth>t.offsetWidth)?s.getThLinerEl():t.firstChild;var p=Math.max(0,(v.offsetWidth-(parseInt(d.getStyle(v,"paddingLeft"),10)|0)-(parseInt(d.getStyle(v,"paddingRight"),10)|0)),s.minWidth);var x="visible";if((s.maxAutoWidth>0)&&(p>s.maxAutoWidth)){p=s.maxAutoWidth;x="hidden";}k[k.length]=[s,p,x];}}}this._elTbody.style.display="none";for(q=0,r=k.length;q<r;q++){o=k[q];this._setColumnWidth(o[0],o[1]+"px",o[2]);o[0]._calculatedWidth=o[1];}this._elTbody.style.display="";}if(m){this._elHdContainer.style.width=m;this._elBdContainer.style.width=m;
-}}this._syncScroll();this._restoreScrollPositions();},_syncScroll:function(){this._syncScrollX();this._syncScrollY();this._syncScrollOverhang();if(a.opera){this._elHdContainer.scrollLeft=this._elBdContainer.scrollLeft;if(!this.get("width")){document.body.style+="";}}},_syncScrollY:function(){var k=this._elTbody,l=this._elBdContainer;if(!this.get("width")){this._elContainer.style.width=(l.scrollHeight>l.clientHeight)?(k.parentNode.clientWidth+19)+"px":(k.parentNode.clientWidth+2)+"px";}},_syncScrollX:function(){var k=this._elTbody,l=this._elBdContainer;if(!this.get("height")&&(a.ie)){l.style.height=(l.scrollWidth>l.offsetWidth)?(k.parentNode.offsetHeight+18)+"px":k.parentNode.offsetHeight+"px";}if(this._elTbody.rows.length===0){this._elMsgTbody.parentNode.style.width=this.getTheadEl().parentNode.offsetWidth+"px";}else{this._elMsgTbody.parentNode.style.width="";}},_syncScrollOverhang:function(){var l=this._elBdContainer,k=1;if((l.scrollHeight>l.clientHeight)&&(l.scrollWidth>l.clientWidth)){k=18;}this._setOverhangValue(k);},_setOverhangValue:function(n){var p=this._oColumnSet.headers[this._oColumnSet.headers.length-1]||[],l=p.length,k=this._sId+"-fixedth-",o=n+"px solid "+this.get("COLOR_COLUMNFILLER");this._elThead.style.display="none";for(var m=0;m<l;m++){d.get(k+p[m]).style.borderRight=o;}this._elThead.style.display="";},getHdContainerEl:function(){return this._elHdContainer;},getBdContainerEl:function(){return this._elBdContainer;},getHdTableEl:function(){return this._elHdTable;},getBdTableEl:function(){return this._elTable;},disable:function(){var k=this._elMask;k.style.width=this._elBdContainer.offsetWidth+"px";k.style.height=this._elHdContainer.offsetHeight+this._elBdContainer.offsetHeight+"px";k.style.display="";this.fireEvent("disableEvent");},removeColumn:function(m){var k=this._elHdContainer.scrollLeft;var l=this._elBdContainer.scrollLeft;m=h.superclass.removeColumn.call(this,m);this._elHdContainer.scrollLeft=k;this._elBdContainer.scrollLeft=l;return m;},insertColumn:function(n,l){var k=this._elHdContainer.scrollLeft;var m=this._elBdContainer.scrollLeft;var o=h.superclass.insertColumn.call(this,n,l);this._elHdContainer.scrollLeft=k;this._elBdContainer.scrollLeft=m;return o;},reorderColumn:function(n,l){var k=this._elHdContainer.scrollLeft;var m=this._elBdContainer.scrollLeft;var o=h.superclass.reorderColumn.call(this,n,l);this._elHdContainer.scrollLeft=k;this._elBdContainer.scrollLeft=m;return o;},setColumnWidth:function(l,k){l=this.getColumn(l);if(l){this._storeScrollPositions();if(c.isNumber(k)){k=(k>l.minWidth)?k:l.minWidth;l.width=k;this._setColumnWidth(l,k+"px");this._syncScroll();this.fireEvent("columnSetWidthEvent",{column:l,width:k});}else{if(k===null){l.width=k;this._setColumnWidth(l,"auto");this.validateColumnWidths(l);this.fireEvent("columnUnsetWidthEvent",{column:l});}}this._clearTrTemplateEl();}else{}},scrollTo:function(m){var l=this.getTdEl(m);if(l){this.clearScrollPositions();this.getBdContainerEl().scrollLeft=l.offsetLeft;this.getBdContainerEl().scrollTop=l.parentNode.offsetTop;}else{var k=this.getTrEl(m);if(k){this.clearScrollPositions();this.getBdContainerEl().scrollTop=k.offsetTop;}}},showTableMessage:function(o,k){var p=this._elMsgTd;if(c.isString(o)){p.firstChild.innerHTML=o;}if(c.isString(k)){d.addClass(p.firstChild,k);}var n=this.getTheadEl();var l=n.parentNode;var m=l.offsetWidth;this._elMsgTbody.parentNode.style.width=this.getTheadEl().parentNode.offsetWidth+"px";this._elMsgTbody.style.display="";this.fireEvent("tableMsgShowEvent",{html:o,className:k});},_onColumnChange:function(k){var l=(k.column)?k.column:(k.editor)?k.editor.column:null;this._storeScrollPositions();this.validateColumnWidths(l);},_onScroll:function(m,l){l._elHdContainer.scrollLeft=l._elBdContainer.scrollLeft;if(l._oCellEditor&&l._oCellEditor.isActive){l.fireEvent("editorBlurEvent",{editor:l._oCellEditor});l.cancelCellEditor();}var n=j.getTarget(m);var k=n.nodeName.toLowerCase();l.fireEvent("tableScrollEvent",{event:m,target:n});},_onTheadKeydown:function(n,l){if(j.getCharCode(n)===9){setTimeout(function(){if((l instanceof h)&&l._sId){l._elBdContainer.scrollLeft=l._elHdContainer.scrollLeft;}},0);}var o=j.getTarget(n);var k=o.nodeName.toLowerCase();var m=true;while(o&&(k!="table")){switch(k){case"body":return;case"input":case"textarea":break;case"thead":m=l.fireEvent("theadKeyEvent",{target:o,event:n});break;default:break;}if(m===false){return;}else{o=o.parentNode;if(o){k=o.nodeName.toLowerCase();}}}l.fireEvent("tableKeyEvent",{target:(o||l._elContainer),event:n});}});})();(function(){var c=YAHOO.lang,f=YAHOO.util,e=YAHOO.widget,b=YAHOO.env.ua,d=f.Dom,i=f.Event,h=e.DataTable;e.BaseCellEditor=function(k,j){this._sId=this._sId||d.generateId(null,"yui-ceditor");YAHOO.widget.BaseCellEditor._nCount++;this._sType=k;this._initConfigs(j);this._initEvents();this._needsRender=true;};var a=e.BaseCellEditor;c.augmentObject(a,{_nCount:0,CLASS_CELLEDITOR:"yui-ceditor"});a.prototype={_sId:null,_sType:null,_oDataTable:null,_oColumn:null,_oRecord:null,_elTd:null,_elContainer:null,_elCancelBtn:null,_elSaveBtn:null,_initConfigs:function(k){if(k&&YAHOO.lang.isObject(k)){for(var j in k){if(j){this[j]=k[j];}}}},_initEvents:function(){this.createEvent("showEvent");this.createEvent("keydownEvent");this.createEvent("invalidDataEvent");this.createEvent("revertEvent");this.createEvent("saveEvent");this.createEvent("cancelEvent");this.createEvent("blurEvent");this.createEvent("blockEvent");this.createEvent("unblockEvent");},_initContainerEl:function(){if(this._elContainer){YAHOO.util.Event.purgeElement(this._elContainer,true);this._elContainer.innerHTML="";}var j=document.createElement("div");j.id=this.getId()+"-container";j.style.display="none";j.tabIndex=0;this.className=c.isArray(this.className)?this.className:this.className?[this.className]:[];this.className[this.className.length]=h.CLASS_EDITOR;j.className=this.className.join(" ");document.body.insertBefore(j,document.body.firstChild);this._elContainer=j;},_initShimEl:function(){if(this.useIFrame){if(!this._elIFrame){var j=document.createElement("iframe");
-j.src="javascript:false";j.frameBorder=0;j.scrolling="no";j.style.display="none";j.className=h.CLASS_EDITOR_SHIM;j.tabIndex=-1;j.role="presentation";j.title="Presentational iframe shim";document.body.insertBefore(j,document.body.firstChild);this._elIFrame=j;}}},_hide:function(){this.getContainerEl().style.display="none";if(this._elIFrame){this._elIFrame.style.display="none";}this.isActive=false;this.getDataTable()._oCellEditor=null;},asyncSubmitter:null,value:null,defaultValue:null,validator:null,resetInvalidData:true,isActive:false,LABEL_SAVE:"Save",LABEL_CANCEL:"Cancel",disableBtns:false,useIFrame:false,className:null,toString:function(){return"CellEditor instance "+this._sId;},getId:function(){return this._sId;},getDataTable:function(){return this._oDataTable;},getColumn:function(){return this._oColumn;},getRecord:function(){return this._oRecord;},getTdEl:function(){return this._elTd;},getContainerEl:function(){return this._elContainer;},destroy:function(){this.unsubscribeAll();var k=this.getColumn();if(k){k.editor=null;}var j=this.getContainerEl();if(j){i.purgeElement(j,true);j.parentNode.removeChild(j);}},render:function(){if(!this._needsRender){return;}this._initContainerEl();this._initShimEl();i.addListener(this.getContainerEl(),"keydown",function(l,j){if((l.keyCode==27)){var k=i.getTarget(l);if(k.nodeName&&k.nodeName.toLowerCase()==="select"){k.blur();}j.cancel();}j.fireEvent("keydownEvent",{editor:j,event:l});},this);this.renderForm();if(!this.disableBtns){this.renderBtns();}this.doAfterRender();this._needsRender=false;},renderBtns:function(){var l=this.getContainerEl().appendChild(document.createElement("div"));l.className=h.CLASS_BUTTON;var k=l.appendChild(document.createElement("button"));k.className=h.CLASS_DEFAULT;k.innerHTML=this.LABEL_SAVE;i.addListener(k,"click",function(m){this.save();},this,true);this._elSaveBtn=k;var j=l.appendChild(document.createElement("button"));j.innerHTML=this.LABEL_CANCEL;i.addListener(j,"click",function(m){this.cancel();},this,true);this._elCancelBtn=j;},attach:function(n,l){if(n instanceof YAHOO.widget.DataTable){this._oDataTable=n;l=n.getTdEl(l);if(l){this._elTd=l;var m=n.getColumn(l);if(m){this._oColumn=m;var j=n.getRecord(l);if(j){this._oRecord=j;var k=j.getData(this.getColumn().getField());this.value=(k!==undefined)?k:this.defaultValue;return true;}}}}return false;},move:function(){var m=this.getContainerEl(),l=this.getTdEl(),j=d.getX(l),n=d.getY(l);if(isNaN(j)||isNaN(n)){var k=this.getDataTable().getTbodyEl();j=l.offsetLeft+d.getX(k.parentNode)-k.scrollLeft;n=l.offsetTop+d.getY(k.parentNode)-k.scrollTop+this.getDataTable().getTheadEl().offsetHeight;}m.style.left=j+"px";m.style.top=n+"px";if(this._elIFrame){this._elIFrame.style.left=j+"px";this._elIFrame.style.top=n+"px";}},show:function(){var k=this.getContainerEl(),j=this._elIFrame;this.resetForm();this.isActive=true;k.style.display="";if(j){j.style.width=k.offsetWidth+"px";j.style.height=k.offsetHeight+"px";j.style.display="";}this.focus();this.fireEvent("showEvent",{editor:this});},block:function(){this.fireEvent("blockEvent",{editor:this});},unblock:function(){this.fireEvent("unblockEvent",{editor:this});},save:function(){var k=this.getInputValue();var l=k;if(this.validator){l=this.validator.call(this.getDataTable(),k,this.value,this);if(l===undefined){if(this.resetInvalidData){this.resetForm();}this.fireEvent("invalidDataEvent",{editor:this,oldData:this.value,newData:k});return;}}var m=this;var j=function(o,n){var p=m.value;if(o){m.value=n;m.getDataTable().updateCell(m.getRecord(),m.getColumn(),n);m._hide();m.fireEvent("saveEvent",{editor:m,oldData:p,newData:m.value});}else{m.resetForm();m.fireEvent("revertEvent",{editor:m,oldData:p,newData:n});}m.unblock();};this.block();if(c.isFunction(this.asyncSubmitter)){this.asyncSubmitter.call(this,j,l);}else{j(true,l);}},cancel:function(){if(this.isActive){this._hide();this.fireEvent("cancelEvent",{editor:this});}else{}},renderForm:function(){},doAfterRender:function(){},handleDisabledBtns:function(){},resetForm:function(){},focus:function(){},getInputValue:function(){}};c.augmentProto(a,f.EventProvider);e.CheckboxCellEditor=function(j){j=j||{};this._sId=this._sId||d.generateId(null,"yui-checkboxceditor");YAHOO.widget.BaseCellEditor._nCount++;e.CheckboxCellEditor.superclass.constructor.call(this,j.type||"checkbox",j);};c.extend(e.CheckboxCellEditor,a,{checkboxOptions:null,checkboxes:null,value:null,renderForm:function(){if(c.isArray(this.checkboxOptions)){var n,o,q,l,m,k;for(m=0,k=this.checkboxOptions.length;m<k;m++){n=this.checkboxOptions[m];o=c.isValue(n.value)?n.value:n;q=this.getId()+"-chk"+m;this.getContainerEl().innerHTML+='<input type="checkbox"'+' id="'+q+'"'+' value="'+o+'" />';l=this.getContainerEl().appendChild(document.createElement("label"));l.htmlFor=q;l.innerHTML=c.isValue(n.label)?n.label:n;}var p=[];for(m=0;m<k;m++){p[p.length]=this.getContainerEl().childNodes[m*2];}this.checkboxes=p;if(this.disableBtns){this.handleDisabledBtns();}}else{}},handleDisabledBtns:function(){i.addListener(this.getContainerEl(),"click",function(j){if(i.getTarget(j).tagName.toLowerCase()==="input"){this.save();}},this,true);},resetForm:function(){var p=c.isArray(this.value)?this.value:[this.value];for(var o=0,n=this.checkboxes.length;o<n;o++){this.checkboxes[o].checked=false;for(var m=0,l=p.length;m<l;m++){if(this.checkboxes[o].value==p[m]){this.checkboxes[o].checked=true;}}}},focus:function(){this.checkboxes[0].focus();},getInputValue:function(){var k=[];for(var m=0,l=this.checkboxes.length;m<l;m++){if(this.checkboxes[m].checked){k[k.length]=this.checkboxes[m].value;}}return k;}});c.augmentObject(e.CheckboxCellEditor,a);e.DateCellEditor=function(j){j=j||{};this._sId=this._sId||d.generateId(null,"yui-dateceditor");YAHOO.widget.BaseCellEditor._nCount++;e.DateCellEditor.superclass.constructor.call(this,j.type||"date",j);};c.extend(e.DateCellEditor,a,{calendar:null,calendarOptions:null,defaultValue:new Date(),renderForm:function(){if(YAHOO.widget.Calendar){var k=this.getContainerEl().appendChild(document.createElement("div"));
-k.id=this.getId()+"-dateContainer";var l=new YAHOO.widget.Calendar(this.getId()+"-date",k.id,this.calendarOptions);l.render();k.style.cssFloat="none";l.hideEvent.subscribe(function(){this.cancel();},this,true);if(b.ie){var j=this.getContainerEl().appendChild(document.createElement("div"));j.style.clear="both";}this.calendar=l;if(this.disableBtns){this.handleDisabledBtns();}}else{}},handleDisabledBtns:function(){this.calendar.selectEvent.subscribe(function(j){this.save();},this,true);},resetForm:function(){var j=this.value||(new Date());this.calendar.select(j);this.calendar.cfg.setProperty("pagedate",j,false);this.calendar.render();this.calendar.show();},focus:function(){},getInputValue:function(){return this.calendar.getSelectedDates()[0];}});c.augmentObject(e.DateCellEditor,a);e.DropdownCellEditor=function(j){j=j||{};this._sId=this._sId||d.generateId(null,"yui-dropdownceditor");YAHOO.widget.BaseCellEditor._nCount++;e.DropdownCellEditor.superclass.constructor.call(this,j.type||"dropdown",j);};c.extend(e.DropdownCellEditor,a,{dropdownOptions:null,dropdown:null,multiple:false,size:null,renderForm:function(){var n=this.getContainerEl().appendChild(document.createElement("select"));n.style.zoom=1;if(this.multiple){n.multiple="multiple";}if(c.isNumber(this.size)){n.size=this.size;}this.dropdown=n;if(c.isArray(this.dropdownOptions)){var o,m;for(var l=0,k=this.dropdownOptions.length;l<k;l++){o=this.dropdownOptions[l];m=document.createElement("option");m.value=(c.isValue(o.value))?o.value:o;m.innerHTML=(c.isValue(o.label))?o.label:o;m=n.appendChild(m);}if(this.disableBtns){this.handleDisabledBtns();}}},handleDisabledBtns:function(){if(this.multiple){i.addListener(this.dropdown,"blur",function(j){this.save();},this,true);}else{if(!b.ie){i.addListener(this.dropdown,"change",function(j){this.save();},this,true);}else{i.addListener(this.dropdown,"blur",function(j){this.save();},this,true);i.addListener(this.dropdown,"click",function(j){this.save();},this,true);}}},resetForm:function(){var s=this.dropdown.options,p=0,o=s.length;if(c.isArray(this.value)){var l=this.value,k=0,r=l.length,q={};for(;p<o;p++){s[p].selected=false;q[s[p].value]=s[p];}for(;k<r;k++){if(q[l[k]]){q[l[k]].selected=true;}}}else{for(;p<o;p++){if(this.value==s[p].value){s[p].selected=true;}}}},focus:function(){this.getDataTable()._focusEl(this.dropdown);},getInputValue:function(){var n=this.dropdown.options;if(this.multiple){var k=[],m=0,l=n.length;for(;m<l;m++){if(n[m].selected){k.push(n[m].value);}}return k;}else{return n[n.selectedIndex].value;}}});c.augmentObject(e.DropdownCellEditor,a);e.RadioCellEditor=function(j){j=j||{};this._sId=this._sId||d.generateId(null,"yui-radioceditor");YAHOO.widget.BaseCellEditor._nCount++;e.RadioCellEditor.superclass.constructor.call(this,j.type||"radio",j);};c.extend(e.RadioCellEditor,a,{radios:null,radioOptions:null,renderForm:function(){if(c.isArray(this.radioOptions)){var k,l,r,o;for(var n=0,p=this.radioOptions.length;n<p;n++){k=this.radioOptions[n];l=c.isValue(k.value)?k.value:k;r=this.getId()+"-radio"+n;this.getContainerEl().innerHTML+='<input type="radio"'+' name="'+this.getId()+'"'+' value="'+l+'"'+' id="'+r+'" />';o=this.getContainerEl().appendChild(document.createElement("label"));o.htmlFor=r;o.innerHTML=(c.isValue(k.label))?k.label:k;}var q=[],s;for(var m=0;m<p;m++){s=this.getContainerEl().childNodes[m*2];q[q.length]=s;}this.radios=q;if(this.disableBtns){this.handleDisabledBtns();}}else{}},handleDisabledBtns:function(){i.addListener(this.getContainerEl(),"click",function(j){if(i.getTarget(j).tagName.toLowerCase()==="input"){this.save();}},this,true);},resetForm:function(){for(var m=0,l=this.radios.length;m<l;m++){var k=this.radios[m];if(this.value==k.value){k.checked=true;return;}}},focus:function(){for(var l=0,k=this.radios.length;l<k;l++){if(this.radios[l].checked){this.radios[l].focus();return;}}},getInputValue:function(){for(var l=0,k=this.radios.length;l<k;l++){if(this.radios[l].checked){return this.radios[l].value;}}}});c.augmentObject(e.RadioCellEditor,a);e.TextareaCellEditor=function(j){j=j||{};this._sId=this._sId||d.generateId(null,"yui-textareaceditor");YAHOO.widget.BaseCellEditor._nCount++;e.TextareaCellEditor.superclass.constructor.call(this,j.type||"textarea",j);};c.extend(e.TextareaCellEditor,a,{textarea:null,renderForm:function(){var j=this.getContainerEl().appendChild(document.createElement("textarea"));this.textarea=j;if(this.disableBtns){this.handleDisabledBtns();}},handleDisabledBtns:function(){i.addListener(this.textarea,"blur",function(j){this.save();},this,true);},move:function(){this.textarea.style.width=this.getTdEl().offsetWidth+"px";this.textarea.style.height="3em";YAHOO.widget.TextareaCellEditor.superclass.move.call(this);},resetForm:function(){this.textarea.value=this.value;},focus:function(){this.getDataTable()._focusEl(this.textarea);this.textarea.select();},getInputValue:function(){return this.textarea.value;}});c.augmentObject(e.TextareaCellEditor,a);e.TextboxCellEditor=function(j){j=j||{};this._sId=this._sId||d.generateId(null,"yui-textboxceditor");YAHOO.widget.BaseCellEditor._nCount++;e.TextboxCellEditor.superclass.constructor.call(this,j.type||"textbox",j);};c.extend(e.TextboxCellEditor,a,{textbox:null,renderForm:function(){var j;if(b.webkit>420){j=this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));}else{j=this.getContainerEl().appendChild(document.createElement("input"));}j.type="text";this.textbox=j;i.addListener(j,"keypress",function(k){if((k.keyCode===13)){YAHOO.util.Event.preventDefault(k);this.save();}},this,true);if(this.disableBtns){this.handleDisabledBtns();}},move:function(){this.textbox.style.width=this.getTdEl().offsetWidth+"px";e.TextboxCellEditor.superclass.move.call(this);},resetForm:function(){this.textbox.value=c.isValue(this.value)?this.value.toString():"";},focus:function(){this.getDataTable()._focusEl(this.textbox);this.textbox.select();},getInputValue:function(){return this.textbox.value;
-}});c.augmentObject(e.TextboxCellEditor,a);h.Editors={checkbox:e.CheckboxCellEditor,"date":e.DateCellEditor,dropdown:e.DropdownCellEditor,radio:e.RadioCellEditor,textarea:e.TextareaCellEditor,textbox:e.TextboxCellEditor};e.CellEditor=function(k,j){if(k&&h.Editors[k]){c.augmentObject(a,h.Editors[k]);return new h.Editors[k](j);}else{return new a(null,j);}};var g=e.CellEditor;c.augmentObject(g,a);})();YAHOO.register("datatable",YAHOO.widget.DataTable,{version:"2.9.0",build:"2800"});
-
-
+(function(){var d=YAHOO.util.Dom,f=YAHOO.lang,b=f.isObject,e=f.isFunction,c=f.isArray,a=f.isString;function g(k){var n=g.VALUE_UNLIMITED,l,h,i,j,m;k=b(k)?k:{};this.initConfig();this.initEvents();this.set("rowsPerPage",k.rowsPerPage,true);if(g.isNumeric(k.totalRecords)){this.set("totalRecords",k.totalRecords,true);}this.initUIComponents();for(l in k){if(k.hasOwnProperty(l)){this.set(l,k[l],true);}}h=this.get("initialPage");i=this.get("totalRecords");j=this.get("rowsPerPage");if(h>1&&j!==n){m=(h-1)*j;if(i===n||m<i){this.set("recordOffset",m,true);}}}f.augmentObject(g,{id:0,ID_BASE:"yui-pg",VALUE_UNLIMITED:-1,TEMPLATE_DEFAULT:"{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}",TEMPLATE_ROWS_PER_PAGE:"{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}",ui:{},isNumeric:function(h){return isFinite(+h);},toNumber:function(h){return isFinite(+h)?+h:null;}},true);g.prototype={_containers:[],_batch:false,_pageChanged:false,_state:null,initConfig:function(){var h=g.VALUE_UNLIMITED;this.setAttributeConfig("rowsPerPage",{value:0,validator:g.isNumeric,setter:g.toNumber});this.setAttributeConfig("containers",{value:null,validator:function(l){if(!c(l)){l=[l];}for(var k=0,j=l.length;k<j;++k){if(a(l[k])||(b(l[k])&&l[k].nodeType===1)){continue;}return false;}return true;},method:function(i){i=d.get(i);if(!c(i)){i=[i];}this._containers=i;}});this.setAttributeConfig("totalRecords",{value:0,validator:g.isNumeric,setter:g.toNumber});this.setAttributeConfig("recordOffset",{value:0,validator:function(j){var i=this.get("totalRecords");if(g.isNumeric(j)){j=+j;return i===h||i>j||(i===0&&j===0);}return false;},setter:g.toNumber});this.setAttributeConfig("initialPage",{value:1,validator:g.isNumeric,setter:g.toNumber});this.setAttributeConfig("template",{value:g.TEMPLATE_DEFAULT,validator:a});this.setAttributeConfig("containerClass",{value:"yui-pg-container",validator:a});this.setAttributeConfig("alwaysVisible",{value:true,validator:f.isBoolean});this.setAttributeConfig("updateOnChange",{value:false,validator:f.isBoolean});this.setAttributeConfig("id",{value:g.id++,readOnly:true});this.setAttributeConfig("rendered",{value:false,readOnly:true});},initUIComponents:function(){var j=g.ui,i,h;for(i in j){if(j.hasOwnProperty(i)){h=j[i];if(b(h)&&e(h.init)){h.init(this);}}}},initEvents:function(){this.createEvent("render");this.createEvent("rendered");this.createEvent("changeRequest");this.createEvent("pageChange");this.createEvent("beforeDestroy");this.createEvent("destroy");this._selfSubscribe();},_selfSubscribe:function(){this.subscribe("totalRecordsChange",this.updateVisibility,this,true);this.subscribe("alwaysVisibleChange",this.updateVisibility,this,true);this.subscribe("totalRecordsChange",this._handleStateChange,this,true);this.subscribe("recordOffsetChange",this._handleStateChange,this,true);this.subscribe("rowsPerPageChange",this._handleStateChange,this,true);this.subscribe("totalRecordsChange",this._syncRecordOffset,this,true);},_syncRecordOffset:function(k){var h=k.newValue,j,i;if(k.prevValue!==h){if(h!==g.VALUE_UNLIMITED){j=this.get("rowsPerPage");if(j&&this.get("recordOffset")>=h){i=this.getState({totalRecords:k.prevValue,recordOffset:this.get("recordOffset")});this.set("recordOffset",i.before.recordOffset);this._firePageChange(i);}}}},_handleStateChange:function(i){if(i.prevValue!==i.newValue){var j=this._state||{},h;j[i.type.replace(/Change$/,"")]=i.prevValue;h=this.getState(j);if(h.page!==h.before.page){if(this._batch){this._pageChanged=true;}else{this._firePageChange(h);}}}},_firePageChange:function(h){if(b(h)){var i=h.before;delete h.before;this.fireEvent("pageChange",{type:"pageChange",prevValue:h.page,newValue:i.page,prevState:h,newState:i});}},render:function(){if(this.get("rendered")){return this;}var l=this.get("template"),m=this.getState(),k=g.ID_BASE+this.get("id")+"-",j,h;for(j=0,h=this._containers.length;j<h;++j){this._renderTemplate(this._containers[j],l,k+j,true);}this.updateVisibility();if(this._containers.length){this.setAttributeConfig("rendered",{value:true});this.fireEvent("render",m);this.fireEvent("rendered",m);}return this;},_renderTemplate:function(j,n,m,l){var p=this.get("containerClass"),o,k,h;if(!j){return;}d.setStyle(j,"display","none");d.addClass(j,p);j.innerHTML=n.replace(/\{([a-z0-9_ \-]+)\}/gi,'<span class="yui-pg-ui yui-pg-ui-$1"></span>');o=d.getElementsByClassName("yui-pg-ui","span",j);for(k=0,h=o.length;k<h;++k){this.renderUIComponent(o[k],m);}if(!l){d.setStyle(j,"display","");}},renderUIComponent:function(h,m){var l=h.parentNode,k=/yui-pg-ui-(\w+)/.exec(h.className),j=k&&g.ui[k[1]],i;if(e(j)){i=new j(this);if(e(i.render)){l.replaceChild(i.render(m),h);}}return this;},destroy:function(){this.fireEvent("beforeDestroy");this.fireEvent("destroy");this.setAttributeConfig("rendered",{value:false});this.unsubscribeAll();},updateVisibility:function(m){var p=this.get("alwaysVisible"),n,j,q,o,k,l,h;if(!m||m.type==="alwaysVisibleChange"||!p){n=this.get("totalRecords");j=true;q=this.get("rowsPerPage");o=this.get("rowsPerPageOptions");if(c(o)){for(k=0,l=o.length;k<l;++k){h=o[k];if(f.isNumber(h||h.value)){q=Math.min(q,(h.value||h));}}}if(n!==g.VALUE_UNLIMITED&&n<=q){j=false;}j=j||p;for(k=0,l=this._containers.length;k<l;++k){d.setStyle(this._containers[k],"display",j?"":"none");}}},getContainerNodes:function(){return this._containers;},getTotalPages:function(){var h=this.get("totalRecords"),i=this.get("rowsPerPage");if(!i){return null;}if(h===g.VALUE_UNLIMITED){return g.VALUE_UNLIMITED;}return Math.ceil(h/i);},hasPage:function(i){if(!f.isNumber(i)||i<1){return false;}var h=this.getTotalPages();return(h===g.VALUE_UNLIMITED||h>=i);},getCurrentPage:function(){var h=this.get("rowsPerPage");if(!h||!this.get("totalRecords")){return 0;}return Math.floor(this.get("recordOffset")/h)+1;},hasNextPage:function(){var h=this.getCurrentPage(),i=this.getTotalPages();return h&&(i===g.VALUE_UNLIMITED||h<i);},getNextPage:function(){return this.hasNextPage()?this.getCurrentPage()+1:null;
+},hasPreviousPage:function(){return(this.getCurrentPage()>1);},getPreviousPage:function(){return(this.hasPreviousPage()?this.getCurrentPage()-1:1);},getPageRecords:function(k){if(!f.isNumber(k)){k=this.getCurrentPage();}var j=this.get("rowsPerPage"),i=this.get("totalRecords"),l,h;if(!k||!j){return null;}l=(k-1)*j;if(i!==g.VALUE_UNLIMITED){if(l>=i){return null;}h=Math.min(l+j,i)-1;}else{h=l+j-1;}return[l,h];},setPage:function(i,h){if(this.hasPage(i)&&i!==this.getCurrentPage()){if(this.get("updateOnChange")||h){this.set("recordOffset",(i-1)*this.get("rowsPerPage"));}else{this.fireEvent("changeRequest",this.getState({"page":i}));}}},getRowsPerPage:function(){return this.get("rowsPerPage");},setRowsPerPage:function(i,h){if(g.isNumeric(i)&&+i>0&&+i!==this.get("rowsPerPage")){if(this.get("updateOnChange")||h){this.set("rowsPerPage",i);}else{this.fireEvent("changeRequest",this.getState({"rowsPerPage":+i}));}}},getTotalRecords:function(){return this.get("totalRecords");},setTotalRecords:function(i,h){if(g.isNumeric(i)&&+i>=0&&+i!==this.get("totalRecords")){if(this.get("updateOnChange")||h){this.set("totalRecords",i);}else{this.fireEvent("changeRequest",this.getState({"totalRecords":+i}));}}},getStartIndex:function(){return this.get("recordOffset");},setStartIndex:function(i,h){if(g.isNumeric(i)&&+i>=0&&+i!==this.get("recordOffset")){if(this.get("updateOnChange")||h){this.set("recordOffset",i);}else{this.fireEvent("changeRequest",this.getState({"recordOffset":+i}));}}},getState:function(n){var p=g.VALUE_UNLIMITED,l=Math,m=l.max,o=l.ceil,j,h,k;function i(s,q,r){if(s<=0||q===0){return 0;}if(q===p||q>s){return s-(s%r);}return q-(q%r||r);}j={paginator:this,totalRecords:this.get("totalRecords"),rowsPerPage:this.get("rowsPerPage"),records:this.getPageRecords()};j.recordOffset=i(this.get("recordOffset"),j.totalRecords,j.rowsPerPage);j.page=o(j.recordOffset/j.rowsPerPage)+1;if(!n){return j;}h={paginator:this,before:j,rowsPerPage:n.rowsPerPage||j.rowsPerPage,totalRecords:(g.isNumeric(n.totalRecords)?m(n.totalRecords,p):+j.totalRecords)};if(h.totalRecords===0){h.recordOffset=h.page=0;}else{k=g.isNumeric(n.page)?(n.page-1)*h.rowsPerPage:g.isNumeric(n.recordOffset)?+n.recordOffset:j.recordOffset;h.recordOffset=i(k,h.totalRecords,h.rowsPerPage);h.page=o(h.recordOffset/h.rowsPerPage)+1;}h.records=[h.recordOffset,h.recordOffset+h.rowsPerPage-1];if(h.totalRecords!==p&&h.recordOffset<h.totalRecords&&h.records&&h.records[1]>h.totalRecords-1){h.records[1]=h.totalRecords-1;}return h;},setState:function(i){if(b(i)){this._state=this.getState({});i={page:i.page,rowsPerPage:i.rowsPerPage,totalRecords:i.totalRecords,recordOffset:i.recordOffset};if(i.page&&i.recordOffset===undefined){i.recordOffset=(i.page-1)*(i.rowsPerPage||this.get("rowsPerPage"));}this._batch=true;this._pageChanged=false;for(var h in i){if(i.hasOwnProperty(h)&&this._configs.hasOwnProperty(h)){this.set(h,i[h]);}}this._batch=false;if(this._pageChanged){this._pageChanged=false;this._firePageChange(this.getState(this._state));}}}};f.augmentProto(g,YAHOO.util.AttributeProvider);YAHOO.widget.Paginator=g;})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.CurrentPageReport=function(d){this.paginator=d;d.subscribe("recordOffsetChange",this.update,this,true);d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.update,this,true);d.subscribe("pageReportTemplateChange",this.update,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("pageReportClassChange",this.update,this,true);};c.ui.CurrentPageReport.init=function(d){d.setAttributeConfig("pageReportClass",{value:"yui-pg-current",validator:b.isString});d.setAttributeConfig("pageReportTemplate",{value:"({currentPage} of {totalPages})",validator:b.isString});d.setAttributeConfig("pageReportValueGenerator",{value:function(g){var f=g.getCurrentPage(),e=g.getPageRecords();return{"currentPage":e?f:0,"totalPages":g.getTotalPages(),"startIndex":e?e[0]:0,"endIndex":e?e[1]:0,"startRecord":e?e[0]+1:0,"endRecord":e?e[1]+1:0,"totalRecords":g.get("totalRecords")};},validator:b.isFunction});};c.ui.CurrentPageReport.sprintf=function(e,d){return e.replace(/\{([\w\s\-]+)\}/g,function(f,g){return(g in d)?d[g]:"";});};c.ui.CurrentPageReport.prototype={span:null,render:function(d){this.span=document.createElement("span");this.span.className=this.paginator.get("pageReportClass");a(this.span,d+"-page-report");this.update();return this.span;},update:function(d){if(d&&d.prevValue===d.newValue){return;}this.span.innerHTML=c.ui.CurrentPageReport.sprintf(this.paginator.get("pageReportTemplate"),this.paginator.get("pageReportValueGenerator")(this.paginator));},destroy:function(){this.span.parentNode.removeChild(this.span);this.span=null;}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.PageLinks=function(d){this.paginator=d;d.subscribe("recordOffsetChange",this.update,this,true);d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.update,this,true);d.subscribe("pageLinksChange",this.rebuild,this,true);d.subscribe("pageLinkClassChange",this.rebuild,this,true);d.subscribe("currentPageClassChange",this.rebuild,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("pageLinksContainerClassChange",this.rebuild,this,true);};c.ui.PageLinks.init=function(d){d.setAttributeConfig("pageLinkClass",{value:"yui-pg-page",validator:b.isString});d.setAttributeConfig("currentPageClass",{value:"yui-pg-current-page",validator:b.isString});d.setAttributeConfig("pageLinksContainerClass",{value:"yui-pg-pages",validator:b.isString});d.setAttributeConfig("pageLinks",{value:10,validator:c.isNumeric});d.setAttributeConfig("pageLabelBuilder",{value:function(e,f){return e;},validator:b.isFunction});d.setAttributeConfig("pageTitleBuilder",{value:function(e,f){return"Page "+e;},validator:b.isFunction});};c.ui.PageLinks.calculateRange=function(f,g,e){var j=c.VALUE_UNLIMITED,i,d,h;if(!f||e===0||g===0||(g===j&&e===j)){return[0,-1];
+}if(g!==j){e=e===j?g:Math.min(e,g);}i=Math.max(1,Math.ceil(f-(e/2)));if(g===j){d=i+e-1;}else{d=Math.min(g,i+e-1);}h=e-(d-i+1);i=Math.max(1,i-h);return[i,d];};c.ui.PageLinks.prototype={current:0,container:null,render:function(d){var e=this.paginator;this.container=document.createElement("span");a(this.container,d+"-pages");this.container.className=e.get("pageLinksContainerClass");YAHOO.util.Event.on(this.container,"click",this.onClick,this,true);this.update({newValue:null,rebuild:true});return this.container;},update:function(q){if(q&&q.prevValue===q.newValue){return;}var g=this.paginator,m=g.getCurrentPage();if(this.current!==m||!m||q.rebuild){var r=g.get("pageLabelBuilder"),l=g.get("pageTitleBuilder"),k=c.ui.PageLinks.calculateRange(m,g.getTotalPages(),g.get("pageLinks")),f=k[0],h=k[1],o="",d,j,n;d='<a href="#" class="{class}" page="{page}" title="{title}">{label}</a>';n='<span class="{class}">{label}</span>';for(j=f;j<=h;++j){if(j===m){o+=b.substitute(n,{"class":g.get("currentPageClass")+" "+g.get("pageLinkClass"),"label":r(j,g)});}else{o+=b.substitute(d,{"class":g.get("pageLinkClass"),"page":j,"label":r(j,g),"title":l(j,g)});}}this.container.innerHTML=o;}},rebuild:function(d){d.rebuild=true;this.update(d);},destroy:function(){YAHOO.util.Event.purgeElement(this.container,true);this.container.parentNode.removeChild(this.container);this.container=null;},onClick:function(f){var d=YAHOO.util.Event.getTarget(f);if(d&&YAHOO.util.Dom.hasClass(d,this.paginator.get("pageLinkClass"))){YAHOO.util.Event.stopEvent(f);this.paginator.setPage(parseInt(d.getAttribute("page"),10));}}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.FirstPageLink=function(d){this.paginator=d;d.subscribe("recordOffsetChange",this.update,this,true);d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.update,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("firstPageLinkLabelChange",this.update,this,true);d.subscribe("firstPageLinkClassChange",this.update,this,true);};c.ui.FirstPageLink.init=function(d){d.setAttributeConfig("firstPageLinkLabel",{value:"&lt;&lt; first",validator:b.isString});d.setAttributeConfig("firstPageLinkClass",{value:"yui-pg-first",validator:b.isString});d.setAttributeConfig("firstPageLinkTitle",{value:"First Page",validator:b.isString});};c.ui.FirstPageLink.prototype={current:null,link:null,span:null,render:function(e){var f=this.paginator,h=f.get("firstPageLinkClass"),d=f.get("firstPageLinkLabel"),g=f.get("firstPageLinkTitle");this.link=document.createElement("a");this.span=document.createElement("span");a(this.link,e+"-first-link");this.link.href="#";this.link.className=h;this.link.innerHTML=d;this.link.title=g;YAHOO.util.Event.on(this.link,"click",this.onClick,this,true);a(this.span,e+"-first-span");this.span.className=h;this.span.innerHTML=d;this.current=f.getCurrentPage()>1?this.link:this.span;return this.current;},update:function(f){if(f&&f.prevValue===f.newValue){return;}var d=this.current?this.current.parentNode:null;if(this.paginator.getCurrentPage()>1){if(d&&this.current===this.span){d.replaceChild(this.link,this.current);this.current=this.link;}}else{if(d&&this.current===this.link){d.replaceChild(this.span,this.current);this.current=this.span;}}},destroy:function(){YAHOO.util.Event.purgeElement(this.link);this.current.parentNode.removeChild(this.current);this.link=this.span=null;},onClick:function(d){YAHOO.util.Event.stopEvent(d);this.paginator.setPage(1);}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.LastPageLink=function(d){this.paginator=d;d.subscribe("recordOffsetChange",this.update,this,true);d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.update,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("lastPageLinkLabelChange",this.update,this,true);d.subscribe("lastPageLinkClassChange",this.update,this,true);};c.ui.LastPageLink.init=function(d){d.setAttributeConfig("lastPageLinkLabel",{value:"last &gt;&gt;",validator:b.isString});d.setAttributeConfig("lastPageLinkClass",{value:"yui-pg-last",validator:b.isString});d.setAttributeConfig("lastPageLinkTitle",{value:"Last Page",validator:b.isString});};c.ui.LastPageLink.prototype={current:null,link:null,span:null,na:null,render:function(e){var g=this.paginator,i=g.get("lastPageLinkClass"),d=g.get("lastPageLinkLabel"),f=g.getTotalPages(),h=g.get("lastPageLinkTitle");this.link=document.createElement("a");this.span=document.createElement("span");this.na=this.span.cloneNode(false);a(this.link,e+"-last-link");this.link.href="#";this.link.className=i;this.link.innerHTML=d;this.link.title=h;YAHOO.util.Event.on(this.link,"click",this.onClick,this,true);a(this.span,e+"-last-span");this.span.className=i;this.span.innerHTML=d;a(this.na,e+"-last-na");switch(f){case c.VALUE_UNLIMITED:this.current=this.na;break;case g.getCurrentPage():this.current=this.span;break;default:this.current=this.link;}return this.current;},update:function(f){if(f&&f.prevValue===f.newValue){return;}var d=this.current?this.current.parentNode:null,g=this.link;if(d){switch(this.paginator.getTotalPages()){case c.VALUE_UNLIMITED:g=this.na;break;case this.paginator.getCurrentPage():g=this.span;break;}if(this.current!==g){d.replaceChild(g,this.current);this.current=g;}}},destroy:function(){YAHOO.util.Event.purgeElement(this.link);this.current.parentNode.removeChild(this.current);this.link=this.span=null;},onClick:function(d){YAHOO.util.Event.stopEvent(d);this.paginator.setPage(this.paginator.getTotalPages());}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.NextPageLink=function(d){this.paginator=d;d.subscribe("recordOffsetChange",this.update,this,true);d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.update,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("nextPageLinkLabelChange",this.update,this,true);
+d.subscribe("nextPageLinkClassChange",this.update,this,true);};c.ui.NextPageLink.init=function(d){d.setAttributeConfig("nextPageLinkLabel",{value:"next &gt;",validator:b.isString});d.setAttributeConfig("nextPageLinkClass",{value:"yui-pg-next",validator:b.isString});d.setAttributeConfig("nextPageLinkTitle",{value:"Next Page",validator:b.isString});};c.ui.NextPageLink.prototype={current:null,link:null,span:null,render:function(e){var g=this.paginator,i=g.get("nextPageLinkClass"),d=g.get("nextPageLinkLabel"),f=g.getTotalPages(),h=g.get("nextPageLinkTitle");this.link=document.createElement("a");this.span=document.createElement("span");a(this.link,e+"-next-link");this.link.href="#";this.link.className=i;this.link.innerHTML=d;this.link.title=h;YAHOO.util.Event.on(this.link,"click",this.onClick,this,true);a(this.span,e+"-next-span");this.span.className=i;this.span.innerHTML=d;this.current=g.getCurrentPage()===f?this.span:this.link;return this.current;},update:function(g){if(g&&g.prevValue===g.newValue){return;}var f=this.paginator.getTotalPages(),d=this.current?this.current.parentNode:null;if(this.paginator.getCurrentPage()!==f){if(d&&this.current===this.span){d.replaceChild(this.link,this.current);this.current=this.link;}}else{if(this.current===this.link){if(d){d.replaceChild(this.span,this.current);this.current=this.span;}}}},destroy:function(){YAHOO.util.Event.purgeElement(this.link);this.current.parentNode.removeChild(this.current);this.link=this.span=null;},onClick:function(d){YAHOO.util.Event.stopEvent(d);this.paginator.setPage(this.paginator.getNextPage());}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.PreviousPageLink=function(d){this.paginator=d;d.subscribe("recordOffsetChange",this.update,this,true);d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.update,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("previousPageLinkLabelChange",this.update,this,true);d.subscribe("previousPageLinkClassChange",this.update,this,true);};c.ui.PreviousPageLink.init=function(d){d.setAttributeConfig("previousPageLinkLabel",{value:"&lt; prev",validator:b.isString});d.setAttributeConfig("previousPageLinkClass",{value:"yui-pg-previous",validator:b.isString});d.setAttributeConfig("previousPageLinkTitle",{value:"Previous Page",validator:b.isString});};c.ui.PreviousPageLink.prototype={current:null,link:null,span:null,render:function(e){var f=this.paginator,h=f.get("previousPageLinkClass"),d=f.get("previousPageLinkLabel"),g=f.get("previousPageLinkTitle");this.link=document.createElement("a");this.span=document.createElement("span");a(this.link,e+"-prev-link");this.link.href="#";this.link.className=h;this.link.innerHTML=d;this.link.title=g;YAHOO.util.Event.on(this.link,"click",this.onClick,this,true);a(this.span,e+"-prev-span");this.span.className=h;this.span.innerHTML=d;this.current=f.getCurrentPage()>1?this.link:this.span;return this.current;},update:function(f){if(f&&f.prevValue===f.newValue){return;}var d=this.current?this.current.parentNode:null;if(this.paginator.getCurrentPage()>1){if(d&&this.current===this.span){d.replaceChild(this.link,this.current);this.current=this.link;}}else{if(d&&this.current===this.link){d.replaceChild(this.span,this.current);this.current=this.span;}}},destroy:function(){YAHOO.util.Event.purgeElement(this.link);this.current.parentNode.removeChild(this.current);this.link=this.span=null;},onClick:function(d){YAHOO.util.Event.stopEvent(d);this.paginator.setPage(this.paginator.getPreviousPage());}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.RowsPerPageDropdown=function(d){this.paginator=d;d.subscribe("rowsPerPageChange",this.update,this,true);d.subscribe("rowsPerPageOptionsChange",this.rebuild,this,true);d.subscribe("totalRecordsChange",this._handleTotalRecordsChange,this,true);d.subscribe("destroy",this.destroy,this,true);d.subscribe("rowsPerPageDropdownClassChange",this.rebuild,this,true);};c.ui.RowsPerPageDropdown.init=function(d){d.setAttributeConfig("rowsPerPageOptions",{value:[],validator:b.isArray});d.setAttributeConfig("rowsPerPageDropdownClass",{value:"yui-pg-rpp-options",validator:b.isString});};c.ui.RowsPerPageDropdown.prototype={select:null,all:null,render:function(d){this.select=document.createElement("select");a(this.select,d+"-rpp");this.select.className=this.paginator.get("rowsPerPageDropdownClass");this.select.title="Rows per page";YAHOO.util.Event.on(this.select,"change",this.onChange,this,true);this.rebuild();return this.select;},rebuild:function(m){var d=this.paginator,g=this.select,n=d.get("rowsPerPageOptions"),f,l,h,j,k;this.all=null;for(j=0,k=n.length;j<k;++j){l=n[j];f=g.options[j]||g.appendChild(document.createElement("option"));h=b.isValue(l.value)?l.value:l;f.text=b.isValue(l.text)?l.text:l;if(b.isString(h)&&h.toLowerCase()==="all"){this.all=f;f.value=d.get("totalRecords");}else{f.value=h;}}while(g.options.length>n.length){g.removeChild(g.firstChild);}this.update();},update:function(j){if(j&&j.prevValue===j.newValue){return;}var h=this.paginator.get("rowsPerPage")+"",f=this.select.options,g,d;for(g=0,d=f.length;g<d;++g){if(f[g].value===h){f[g].selected=true;break;}}},onChange:function(d){this.paginator.setRowsPerPage(parseInt(this.select.options[this.select.selectedIndex].value,10));},_handleTotalRecordsChange:function(d){if(!this.all||(d&&d.prevValue===d.newValue)){return;}this.all.value=d.newValue;if(this.all.selected){this.paginator.set("rowsPerPage",d.newValue);}},destroy:function(){YAHOO.util.Event.purgeElement(this.select);this.select.parentNode.removeChild(this.select);this.select=null;}};})();(function(){var c=YAHOO.widget.Paginator,b=YAHOO.lang,a=YAHOO.util.Dom.generateId;c.ui.JumpToPageDropdown=function(d){this.paginator=d;d.subscribe("rowsPerPageChange",this.rebuild,this,true);d.subscribe("rowsPerPageOptionsChange",this.rebuild,this,true);d.subscribe("pageChange",this.update,this,true);d.subscribe("totalRecordsChange",this.rebuild,this,true);
+d.subscribe("destroy",this.destroy,this,true);};c.ui.JumpToPageDropdown.init=function(d){d.setAttributeConfig("jumpToPageDropdownClass",{value:"yui-pg-jtp-options",validator:b.isString});};c.ui.JumpToPageDropdown.prototype={select:null,render:function(d){this.select=document.createElement("select");a(this.select,d+"-jtp");this.select.className=this.paginator.get("jumpToPageDropdownClass");this.select.title="Jump to page";YAHOO.util.Event.on(this.select,"change",this.onChange,this,true);this.rebuild();return this.select;},rebuild:function(l){var k=this.paginator,j=this.select,f=k.getTotalPages(),h,g,d;this.all=null;for(g=0,d=f;g<d;++g){h=j.options[g]||j.appendChild(document.createElement("option"));h.innerHTML=g+1;h.value=g+1;}for(g=f,d=j.options.length;g<d;g++){j.removeChild(j.lastChild);}this.update();},update:function(j){if(j&&j.prevValue===j.newValue){return;}var h=this.paginator.getCurrentPage()+"",f=this.select.options,g,d;for(g=0,d=f.length;g<d;++g){if(f[g].value===h){f[g].selected=true;break;}}},onChange:function(d){this.paginator.setPage(parseInt(this.select.options[this.select.selectedIndex].value,false));},destroy:function(){YAHOO.util.Event.purgeElement(this.select);this.select.parentNode.removeChild(this.select);this.select=null;}};})();YAHOO.register("paginator",YAHOO.widget.Paginator,{version:"2.9.0",build:"2800"});
diff --git a/rhodecode/templates/admin/admin_log.html b/rhodecode/templates/admin/admin_log.html
index cf1f1312..7bbcd9ed 100644
--- a/rhodecode/templates/admin/admin_log.html
+++ b/rhodecode/templates/admin/admin_log.html
@@ -12,19 +12,20 @@
%for cnt,l in enumerate(c.users_log):
<tr class="parity${cnt%2}">
<td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
- <td>${h.action_parser(l)[0]}
+ <td>${h.action_parser(l)[0]()}
<div class="journal_action_params">
- ${h.literal(h.action_parser(l)[1]())}</div>
+ ${h.literal(h.action_parser(l)[1]())}
+ </div>
</td>
<td>
- %if l.repository:
+ %if l.repository is not None:
${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
%else:
${l.repository_name}
%endif
</td>
- <td>${l.action_date}</td>
+ <td>${h.fmt_date(l.action_date)}</td>
<td>${l.user_ip}</td>
</tr>
%endfor
diff --git a/rhodecode/templates/admin/ldap/ldap.html b/rhodecode/templates/admin/ldap/ldap.html
index 2e9da4c6..4337c732 100644
--- a/rhodecode/templates/admin/ldap/ldap.html
+++ b/rhodecode/templates/admin/ldap/ldap.html
@@ -86,7 +86,7 @@
</div>
<div class="buttons">
- ${h.submit('save',_('Save'),class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/notifications/notifications.html b/rhodecode/templates/admin/notifications/notifications.html
index 91ee13ad..f849d893 100644
--- a/rhodecode/templates/admin/notifications/notifications.html
+++ b/rhodecode/templates/admin/notifications/notifications.html
@@ -24,33 +24,42 @@
## </li>
##</ul>
</div>
- %if c.notifications:
+
+ <div style="padding:14px 18px;text-align: right;float:left">
+ <span id='all' class="ui-btn"><a href="${h.url.current()}">${_('All')}</a></span>
+ <span id='comment' class="ui-btn"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
+ <span id='pull_request' class="ui-btn"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull requests')}</a></span>
+ </div>
+ %if c.notifications:
<div style="padding:14px 18px;text-align: right;float:right">
<span id='mark_all_read' class="ui-btn">${_('Mark all read')}</span>
</div>
- %endif
+ %endif
<div id='notification_data'>
<%include file='notifications_data.html'/>
</div>
</div>
<script type="text/javascript">
-var url_del = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
-YUE.on(YUQ('.delete-notification'),'click',function(e){
- var notification_id = e.currentTarget.id;
- deleteNotification(url_del,notification_id)
-})
+var url_action = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
+var run = function(){
+ YUE.on(YUQ('.delete-notification'),'click',function(e){
+ var notification_id = e.currentTarget.id;
+ deleteNotification(url_action,notification_id)
+ })
+ YUE.on(YUQ('.read-notification'),'click',function(e){
+ var notification_id = e.currentTarget.id;
+ readNotification(url_action,notification_id)
+ })
+}
+run()
YUE.on('mark_all_read','click',function(e){
- var url = "${h.url('notifications_mark_all_read')}";
- ypjax(url,'notification_data',function(){
- var notification_counter = YUD.get('notification_counter');
- if(notification_counter){
- notification_counter.innerHTML=0;
- }
- YUE.on(YUQ('.delete-notification'),'click',function(e){
- var notification_id = e.currentTarget.id;
- deleteNotification(url_del,notification_id)
- })
- });
+ var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
+ ypjax(url,'notification_data',function(){run()});
})
+
+var current_filter = "${c.current_filter}";
+if (YUD.get(current_filter)){
+ YUD.addClass(current_filter, 'active');
+}
</script>
</%def>
diff --git a/rhodecode/templates/admin/notifications/notifications_data.html b/rhodecode/templates/admin/notifications/notifications_data.html
index 6f12242a..ed670ed0 100644
--- a/rhodecode/templates/admin/notifications/notifications_data.html
+++ b/rhodecode/templates/admin/notifications/notifications_data.html
@@ -3,18 +3,14 @@
<%
unread = lambda n:{False:'unread'}.get(n)
%>
-<div class="notification-paginator">
- <div class="pagination-wh pagination-left">
- ${c.notifications.pager('$link_previous ~2~ $link_next')}
- </div>
-</div>
+
<div class="notification-list notification-table">
%for notification in c.notifications:
<div id="notification_${notification.notification.notification_id}" class="container ${unread(notification.read)}">
<div class="notification-header">
<div class="gravatar">
- <img alt="gravatar" src="${h.gravatar_url(h.email(notification.notification.created_by_user.email),24)}"/>
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(notification.notification.created_by_user.email),24)}"/>
</div>
<div class="desc ${unread(notification.read)}">
<a href="${url('notification', notification_id=notification.notification.notification_id)}">${notification.notification.description}</a>
@@ -22,6 +18,11 @@ unread = lambda n:{False:'unread'}.get(n)
<div class="delete-notifications">
<span id="${notification.notification.notification_id}" class="delete-notification delete_icon action"></span>
</div>
+ %if not notification.read:
+ <div class="read-notifications">
+ <span id="${notification.notification.notification_id}" class="read-notification accept_icon action"></span>
+ </div>
+ %endif
</div>
<div class="notification-subject">${h.literal(notification.notification.subject)}</div>
</div>
@@ -30,7 +31,7 @@ unread = lambda n:{False:'unread'}.get(n)
<div class="notification-paginator">
<div class="pagination-wh pagination-left">
- ${c.notifications.pager('$link_previous ~2~ $link_next')}
+ ${c.notifications.pager('$link_previous ~2~ $link_next',**request.GET.mixed())}
</div>
</div>
diff --git a/rhodecode/templates/admin/notifications/show_notification.html b/rhodecode/templates/admin/notifications/show_notification.html
index 940b421d..052e2e82 100644
--- a/rhodecode/templates/admin/notifications/show_notification.html
+++ b/rhodecode/templates/admin/notifications/show_notification.html
@@ -30,7 +30,7 @@
<div id="notification_${c.notification.notification_id}">
<div class="notification-header">
<div class="gravatar">
- <img alt="gravatar" src="${h.gravatar_url(h.email(c.notification.created_by_user.email),24)}"/>
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.notification.created_by_user.email),24)}"/>
</div>
<div class="desc">
${c.notification.description}
diff --git a/rhodecode/templates/admin/permissions/permissions.html b/rhodecode/templates/admin/permissions/permissions.html
index 93dbf441..b10e8feb 100644
--- a/rhodecode/templates/admin/permissions/permissions.html
+++ b/rhodecode/templates/admin/permissions/permissions.html
@@ -37,7 +37,7 @@
</div>
</div>
<div class="field">
- <div class="label label-select">
+ <div class="label">
<label for="default_perm">${_('Repository permission')}:</label>
</div>
<div class="select">
@@ -66,9 +66,16 @@
${h.select('default_create','',c.create_choices)}
</div>
</div>
-
+ <div class="field">
+ <div class="label">
+ <label for="default_fork">${_('Repository forking')}:</label>
+ </div>
+ <div class="select">
+ ${h.select('default_fork','',c.fork_choices)}
+ </div>
+ </div>
<div class="buttons">
- ${h.submit('set',_('set'),class_="ui-button")}
+ ${h.submit('set',_('set'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/repos/repo_add_base.html b/rhodecode/templates/admin/repos/repo_add_base.html
index 83ad3887..042ffa2f 100644
--- a/rhodecode/templates/admin/repos/repo_add_base.html
+++ b/rhodecode/templates/admin/repos/repo_add_base.html
@@ -30,7 +30,7 @@ ${h.form(url('repos'))}
</div>
<div class="input">
${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
- <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
+ <span class="help-block">${_('Optionaly select a group to put this repository into.')}</span>
</div>
</div>
<div class="field">
@@ -42,6 +42,15 @@ ${h.form(url('repos'))}
<span class="help-block">${_('Type of repository to create.')}</span>
</div>
</div>
+ <div class="field">
+ <div class="label">
+ <label for="landing_rev">${_('Landing revision')}:</label>
+ </div>
+ <div class="input">
+ ${h.select('landing_rev','',c.landing_revs,class_="medium")}
+ <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
+ </div>
+ </div>
<div class="field">
<div class="label label-textarea">
<label for="description">${_('Description')}:</label>
@@ -61,7 +70,7 @@ ${h.form(url('repos'))}
</div>
</div>
<div class="buttons">
- ${h.submit('add',_('add'),class_="ui-button")}
+ ${h.submit('add',_('add'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/repos/repo_edit.html b/rhodecode/templates/admin/repos/repo_edit.html
index cf6ca390..d7b3edf7 100644
--- a/rhodecode/templates/admin/repos/repo_edit.html
+++ b/rhodecode/templates/admin/repos/repo_edit.html
@@ -62,6 +62,15 @@
</div>
</div>
<div class="field">
+ <div class="label">
+ <label for="landing_rev">${_('Landing revision')}:</label>
+ </div>
+ <div class="input">
+ ${h.select('landing_rev','',c.landing_revs,class_="medium")}
+ <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
+ </div>
+ </div>
+ <div class="field">
<div class="label label-textarea">
<label for="description">${_('Description')}:</label>
</div>
@@ -99,6 +108,15 @@
</div>
</div>
<div class="field">
+ <div class="label label-checkbox">
+ <label for="enable_locking">${_('Enable locking')}:</label>
+ </div>
+ <div class="checkboxes">
+ ${h.checkbox('enable_locking',value="True")}
+ <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
+ </div>
+ </div>
+ <div class="field">
<div class="label">
<label for="user">${_('Owner')}:</label>
</div>
@@ -120,8 +138,8 @@
</div>
<div class="buttons">
- ${h.submit('save','Save',class_="ui-button")}
- ${h.reset('reset','Reset',class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
@@ -187,30 +205,35 @@
</div>
<div class="field" style="border:none;color:#888">
<ul>
- <li>${_('''All actions made on this repository will be accessible to everyone in public journal''')}
+ <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
</li>
</ul>
</div>
</div>
${h.end_form()}
- <h3>${_('Delete')}</h3>
- ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
+ <h3>${_('Locking')}</h3>
+ ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
<div class="form">
<div class="fields">
- ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
+ %if c.repo_info.locked[0]:
+ ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
+ ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
+ %else:
+ ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
+ ${_('Repository is not locked')}
+ %endif
</div>
<div class="field" style="border:none;color:#888">
<ul>
- <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
- If you need fully delete it from filesystem please do it manually''')}
+ <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
</li>
</ul>
- </div>
+ </div>
</div>
${h.end_form()}
- <h3>${_('Set as fork')}</h3>
+ <h3>${_('Set as fork of')}</h3>
${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
<div class="form">
<div class="fields">
@@ -219,13 +242,27 @@
</div>
<div class="field" style="border:none;color:#888">
<ul>
- <li>${_('''Manually set this repository as a fork of another''')}</li>
+ <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
</ul>
</div>
- </div>
+ </div>
${h.end_form()}
-
+
+ <h3>${_('Delete')}</h3>
+ ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
+ <div class="form">
+ <div class="fields">
+ ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
+ </div>
+ <div class="field" style="border:none;color:#888">
+ <ul>
+ <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
+ If you need fully delete it from filesystem please do it manually''')}
+ </li>
+ </ul>
+ </div>
+ </div>
+ ${h.end_form()}
</div>
-
</%def>
diff --git a/rhodecode/templates/admin/repos/repo_edit_perms.html b/rhodecode/templates/admin/repos/repo_edit_perms.html
index bb904012..9ccef9ee 100644
--- a/rhodecode/templates/admin/repos/repo_edit_perms.html
+++ b/rhodecode/templates/admin/repos/repo_edit_perms.html
@@ -16,7 +16,7 @@
${_('private repository')}
</span>
</td>
- <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}</td>
+ <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${_('default')}</td>
</tr>
%else:
<tr id="id${id(r2p.user.username)}">
@@ -25,7 +25,7 @@
<td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
<td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
<td style="white-space: nowrap;">
- <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
+ <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
</td>
<td>
%if r2p.user.username !='default':
@@ -46,7 +46,12 @@
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
<td style="white-space: nowrap;">
- <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
+ <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
+ %if h.HasPermissionAny('hg.admin')():
+ <a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
+ %else:
+ ${g2p.users_group.users_group_name}
+ %endif
</td>
<td>
<span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
@@ -55,20 +60,23 @@
</td>
</tr>
%endfor
- <tr id="add_perm_input">
- <td>${h.radio('perm_new_member','repository.none')}</td>
- <td>${h.radio('perm_new_member','repository.read')}</td>
- <td>${h.radio('perm_new_member','repository.write')}</td>
- <td>${h.radio('perm_new_member','repository.admin')}</td>
- <td class='ac'>
- <div class="perm_ac" id="perm_ac">
- ${h.text('perm_new_member_name',class_='yui-ac-input')}
- ${h.hidden('perm_new_member_type')}
- <div id="perm_container"></div>
- </div>
- </td>
- <td></td>
- </tr>
+ <%
+ _tmpl = h.literal("""' \
+ <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td class="ac"> \
+ <div class="perm_ac" id="perm_ac_{0}"> \
+ <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
+ <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
+ <div id="perm_container_{0}"></div> \
+ </div> \
+ </td> \
+ <td></td>'""")
+ %>
+ ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
+ <tr class="new_members last_new_member" id="add_perm_input"></tr>
<tr>
<td colspan="6">
<span id="add_perm" class="add_icon" style="cursor: pointer;">
@@ -113,16 +121,8 @@ YUE.onDOMReady(function () {
YUD.setStyle('add_perm_input', 'display', 'none');
}
YAHOO.util.Event.addListener('add_perm', 'click', function () {
- YUD.setStyle('add_perm_input', 'display', '');
- YUD.setStyle('add_perm', 'opacity', '0.6');
- YUD.setStyle('add_perm', 'cursor', 'default');
+ addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
});
- MembersAutoComplete(
- ${c.users_array|n},
- ${c.users_groups_array|n},
- "${_('Group')}",
- "${_('members')}"
- );
});
</script>
diff --git a/rhodecode/templates/admin/repos/repos.html b/rhodecode/templates/admin/repos/repos.html
index 2a3bcaeb..02582d62 100644
--- a/rhodecode/templates/admin/repos/repos.html
+++ b/rhodecode/templates/admin/repos/repos.html
@@ -5,9 +5,8 @@
${_('Repositories administration')} - ${c.rhodecode_name}
</%def>
-
<%def name="breadcrumbs_links()">
- ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Repositories')}
+ <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('repositories')}
</%def>
<%def name="page_nav()">
${self.menu('admin')}
@@ -23,102 +22,114 @@
</li>
</ul>
</div>
+ <div class="table yui-skin-sam" id="repos_list_wrap"></div>
+ <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
+
- <div class="table">
- <div id='repos_list_wrap' class="yui-skin-sam">
- <%cnt=0%>
- <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
-
- <table id="repos_list">
- <thead>
- <tr>
- <th class="left"></th>
- <th class="left">${_('Name')}</th>
- <th class="left">${_('Description')}</th>
- <th class="left">${_('Last change')}</th>
- <th class="left">${_('Tip')}</th>
- <th class="left">${_('Contact')}</th>
- <th class="left">${_('Action')}</th>
- </tr>
- </thead>
-
- %for cnt,repo in enumerate(c.repos_list):
- <tr class="parity${(cnt+1)%2}">
- <td class="quick_repo_menu">
- ${dt.quick_menu(repo['name'])}
- </td>
- <td class="reponame">
- ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'), admin=True)}
- </td>
- ##DESCRIPTION
- <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
- ${h.truncate(repo['description'],60)}</span>
- </td>
- ##LAST CHANGE
- <td>
- <span class="tooltip" title="${repo['last_change']}">${h.age(repo['last_change'])}</span>
- </td>
- ##LAST REVISION
- <td>
- ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
- </td>
- <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
- <td>
- ${h.form(url('repo', repo_name=repo['name']),method='delete')}
- ${h.submit('remove_%s' % repo['name'],_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
- ${h.end_form()}
- </td>
- </tr>
- %endfor
- </table>
- </div>
- </div>
</div>
<script>
+ var url = "${h.url('formatted_users', format='json')}";
+ var data = ${c.data|n};
+ var myDataSource = new YAHOO.util.DataSource(data);
+ myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
+
+ myDataSource.responseSchema = {
+ resultsList: "records",
+ fields: [
+ {key:"menu"},
+ {key:"raw_name"},
+ {key:"name"},
+ {key:"desc"},
+ {key:"owner"},
+ {key:"action"},
+ ]
+ };
+ myDataSource.doBeforeCallback = function(req,raw,res,cb) {
+ // This is the filter function
+ var data = res.results || [],
+ filtered = [],
+ i,l;
+
+ if (req) {
+ req = req.toLowerCase();
+ for (i = 0; i<data.length; i++) {
+ var pos = data[i].raw_name.toLowerCase().indexOf(req)
+ if (pos != -1) {
+ filtered.push(data[i]);
+ }
+ }
+ res.results = filtered;
+ }
+ YUD.get('repo_count').innerHTML = res.results.length;
+ return res;
+ }
// main table sorting
var myColumnDefs = [
{key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
{key:"name",label:"${_('Name')}",sortable:true,
- sortOptions: { sortFunction: nameSort }},
+ sortOptions: { sortFunction: nameSort }},
{key:"desc",label:"${_('Description')}",sortable:true},
- {key:"last_change",label:"${_('Last Change')}",sortable:true,
- sortOptions: { sortFunction: ageSort }},
- {key:"tip",label:"${_('Tip')}",sortable:true,
- sortOptions: { sortFunction: revisionSort }},
{key:"owner",label:"${_('Owner')}",sortable:true},
{key:"action",label:"${_('Action')}",sortable:false},
];
- var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
-
- myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
+ var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
+ sortedBy:{key:"name",dir:"asc"},
+ paginator: new YAHOO.widget.Paginator({
+ rowsPerPage: 15,
+ alwaysVisible: false,
+ template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
+ pageLinks: 5,
+ containerClass: 'pagination-wh',
+ currentPageClass: 'pager_curpage',
+ pageLinkClass: 'pager_link',
+ nextPageLinkLabel: '&gt;',
+ previousPageLinkLabel: '&lt;',
+ firstPageLinkLabel: '&lt;&lt;',
+ lastPageLinkLabel: '&gt;&gt;',
+ containers:['user-paginator']
+ }),
- myDataSource.responseSchema = {
- fields: [
- {key:"menu"},
- {key:"name"},
- {key:"desc"},
- {key:"last_change"},
- {key:"tip"},
- {key:"owner"},
- {key:"action"},
- ]
- };
-
- var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
- {
- sortedBy:{key:"name",dir:"asc"},
- MSG_SORTASC:"${_('Click to sort ascending')}",
- MSG_SORTDESC:"${_('Click to sort descending')}",
- MSG_EMPTY:"${_('No records found.')}",
- MSG_ERROR:"${_('Data error.')}",
- MSG_LOADING:"${_('Loading...')}",
- }
+ MSG_SORTASC:"${_('Click to sort ascending')}",
+ MSG_SORTDESC:"${_('Click to sort descending')}",
+ MSG_EMPTY:"${_('No records found.')}",
+ MSG_ERROR:"${_('Data error.')}",
+ MSG_LOADING:"${_('Loading...')}",
+ }
);
myDataTable.subscribe('postRenderEvent',function(oArgs) {
tooltip_activate();
quick_repo_menu();
});
+
+ var filterTimeout = null;
+
+ updateFilter = function () {
+ // Reset timeout
+ filterTimeout = null;
+
+ // Reset sort
+ var state = myDataTable.getState();
+ state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
+
+ // Get filtered data
+ myDataSource.sendRequest(YUD.get('q_filter').value,{
+ success : myDataTable.onDataReturnInitializeTable,
+ failure : myDataTable.onDataReturnInitializeTable,
+ scope : myDataTable,
+ argument: state
+ });
+
+ };
+ YUE.on('q_filter','click',function(){
+ YUD.get('q_filter').value = '';
+ });
+
+ YUE.on('q_filter','keyup',function (e) {
+ clearTimeout(filterTimeout);
+ filterTimeout = setTimeout(updateFilter,600);
+ });
</script>
+
</%def>
diff --git a/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html b/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
index 35679ac4..735b8689 100644
--- a/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
+++ b/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
@@ -15,7 +15,7 @@
<td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
<td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
<td style="white-space: nowrap;">
- <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
+ <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
</td>
<td>
%if r2p.user.username !='default':
@@ -44,20 +44,23 @@
</td>
</tr>
%endfor
- <tr id="add_perm_input">
- <td>${h.radio('perm_new_member','group.none')}</td>
- <td>${h.radio('perm_new_member','group.read')}</td>
- <td>${h.radio('perm_new_member','group.write')}</td>
- <td>${h.radio('perm_new_member','group.admin')}</td>
- <td class='ac'>
- <div class="perm_ac" id="perm_ac">
- ${h.text('perm_new_member_name',class_='yui-ac-input')}
- ${h.hidden('perm_new_member_type')}
- <div id="perm_container"></div>
- </div>
- </td>
- <td></td>
- </tr>
+<%
+ _tmpl = h.literal("""' \
+ <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
+ <td class="ac"> \
+ <div class="perm_ac" id="perm_ac_{0}"> \
+ <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
+ <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
+ <div id="perm_container_{0}"></div> \
+ </div> \
+ </td> \
+ <td></td>'""")
+ %>
+ ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
+ <tr class="new_members last_new_member" id="add_perm_input"></tr>
<tr>
<td colspan="6">
<span id="add_perm" class="add_icon" style="cursor: pointer;">
@@ -102,16 +105,8 @@ YUE.onDOMReady(function () {
YUD.setStyle('add_perm_input', 'display', 'none');
}
YAHOO.util.Event.addListener('add_perm', 'click', function () {
- YUD.setStyle('add_perm_input', 'display', '');
- YUD.setStyle('add_perm', 'opacity', '0.6');
- YUD.setStyle('add_perm', 'cursor', 'default');
+ addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
});
- MembersAutoComplete(
- ${c.users_array|n},
- ${c.users_groups_array|n},
- "${_('Group')}",
- "${_('members')}"
- );
});
</script>
diff --git a/rhodecode/templates/admin/repos_groups/repos_groups_add.html b/rhodecode/templates/admin/repos_groups/repos_groups_add.html
index 05906093..45fb20c5 100644
--- a/rhodecode/templates/admin/repos_groups/repos_groups_add.html
+++ b/rhodecode/templates/admin/repos_groups/repos_groups_add.html
@@ -55,7 +55,7 @@
</div>
<div class="buttons">
- ${h.submit('save',_('save'),class_="ui-button")}
+ ${h.submit('save',_('save'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/repos_groups/repos_groups_edit.html b/rhodecode/templates/admin/repos_groups/repos_groups_edit.html
index b3681099..3cb25920 100644
--- a/rhodecode/templates/admin/repos_groups/repos_groups_edit.html
+++ b/rhodecode/templates/admin/repos_groups/repos_groups_edit.html
@@ -60,11 +60,19 @@
<div class="input">
<%include file="repos_group_edit_perms.html"/>
</div>
-
</div>
+ <div class="field">
+ <div class="label label-checkbox">
+ <label for="enable_locking">${_('Enable locking')}:</label>
+ </div>
+ <div class="checkboxes">
+ ${h.checkbox('enable_locking',value="True")}
+ <span class="help-block">${_('Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside')}</span>
+ </div>
+ </div>
<div class="buttons">
- ${h.submit('save',_('Save'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/repos_groups/repos_groups_show.html b/rhodecode/templates/admin/repos_groups/repos_groups_show.html
index 5e33dc6b..c921f361 100644
--- a/rhodecode/templates/admin/repos_groups/repos_groups_show.html
+++ b/rhodecode/templates/admin/repos_groups/repos_groups_show.html
@@ -51,7 +51,7 @@
<td><b>${gr.repositories.count()}</b></td>
<td>
${h.form(url('repos_group', id=gr.group_id),method='delete')}
- ${h.submit('remove_%s' % gr.name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group: %s') % gr.name+"');")}
+ ${h.submit('remove_%s' % gr.name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group: %s') % gr.name+"');")}
${h.end_form()}
</td>
</tr>
diff --git a/rhodecode/templates/admin/settings/hooks.html b/rhodecode/templates/admin/settings/hooks.html
index 30040065..0ccbd56b 100644
--- a/rhodecode/templates/admin/settings/hooks.html
+++ b/rhodecode/templates/admin/settings/hooks.html
@@ -70,7 +70,7 @@
</div>
</div>
<div class="buttons" style="margin-left:280px">
- ${h.submit('save',_('Save'),class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/settings/settings.html b/rhodecode/templates/admin/settings/settings.html
index 9eab1197..2693e5c3 100644
--- a/rhodecode/templates/admin/settings/settings.html
+++ b/rhodecode/templates/admin/settings/settings.html
@@ -38,11 +38,12 @@
<span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
${_('destroy old data')}</span> </label>
</div>
+ <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
</div>
</div>
<div class="buttons">
- ${h.submit('rescan',_('Rescan repositories'),class_="ui-button")}
+ ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
</div>
</div>
</div>
@@ -67,7 +68,7 @@
</div>
<div class="buttons">
- ${h.submit('reindex',_('Reindex'),class_="ui-button")}
+ ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
</div>
</div>
</div>
@@ -108,15 +109,72 @@
</div>
<div class="buttons">
- ${h.submit('save',_('Save settings'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
+ ${h.submit('save',_('Save settings'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
${h.end_form()}
- <h3>${_('Mercurial settings')}</h3>
- ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
+ <h3>${_('Visualisation settings')}</h3>
+ ${h.form(url('admin_setting', setting_id='visual'),method='put')}
+ <div class="form">
+ <!-- fields -->
+
+ <div class="fields">
+
+ <div class="field">
+ <div class="label label-checkbox">
+ <label>${_('Icons')}:</label>
+ </div>
+ <div class="checkboxes">
+ <div class="checkbox">
+ ${h.checkbox('rhodecode_show_public_icon','True')}
+ <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
+ </div>
+ <div class="checkbox">
+ ${h.checkbox('rhodecode_show_private_icon','True')}
+ <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
+ </div>
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label label-checkbox">
+ <label>${_('Meta-Tagging')}:</label>
+ </div>
+ <div class="checkboxes">
+ <div class="checkbox">
+ ${h.checkbox('rhodecode_stylify_metatags','True')}
+ <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
+ </div>
+ <div style="padding-left: 20px;">
+ <ul> <!-- Fix style here -->
+ <li>[featured] <span class="metatag" tag="featured">featured</span></li>
+ <li>[stale] <span class="metatag" tag="stale">stale</span></li>
+ <li>[dead] <span class="metatag" tag="dead">dead</span></li>
+ <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
+ <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
+ <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
+ <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
+ <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+
+ <div class="buttons">
+ ${h.submit('save',_('Save settings'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
+ </div>
+
+ </div>
+ </div>
+ ${h.end_form()}
+
+
+ <h3>${_('VCS settings')}</h3>
+ ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
<div class="form">
<!-- fields -->
@@ -129,8 +187,9 @@
<div class="checkboxes">
<div class="checkbox">
${h.checkbox('web_push_ssl','true')}
- <label for="web_push_ssl">${_('require ssl for pushing')}</label>
+ <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
</div>
+ <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
</div>
</div>
@@ -148,18 +207,39 @@
<label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
</div>
<div class="checkbox">
- ${h.checkbox('hooks_pretxnchangegroup_push_logger','True')}
- <label for="hooks_pretxnchangegroup_push_logger">${_('Log user push commands')}</label>
+ ${h.checkbox('hooks_changegroup_push_logger','True')}
+ <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
</div>
<div class="checkbox">
- ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
- <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
+ ${h.checkbox('hooks_outgoing_pull_logger','True')}
+ <label for="hooks_outgoing_pull_logger">${_('Log user pull commands')}</label>
</div>
</div>
<div class="input" style="margin-top:10px">
${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
</div>
</div>
+ <div class="field">
+ <div class="label label-checkbox">
+ <label>${_('Mercurial Extensions')}:</label>
+ </div>
+ <div class="checkboxes">
+ <div class="checkbox">
+ ${h.checkbox('extensions_largefiles','True')}
+ <label for="extensions_hgsubversion">${_('largefiles extensions')}</label>
+ </div>
+ <div class="checkbox">
+ ${h.checkbox('extensions_hgsubversion','True')}
+ <label for="extensions_hgsubversion">${_('hgsubversion extensions')}</label>
+ </div>
+ <span class="help-block">${_('Requires hgsubversion library installed. Allows clonning from svn remote locations')}</span>
+ ##<div class="checkbox">
+ ## ${h.checkbox('extensions_hggit','True')}
+ ## <label for="extensions_hggit">${_('hg-git extensions')}</label>
+ ##</div>
+ ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span>
+ </div>
+ </div>
<div class="field">
<div class="label">
<label for="paths_root_path">${_('Repositories location')}:</label>
@@ -169,12 +249,13 @@
<span id="path_unlock" class="tooltip"
title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
${_('unlock')}</span>
+ <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
</div>
</div>
<div class="buttons">
- ${h.submit('save',_('Save settings'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
+ ${h.submit('save',_('Save settings'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
@@ -204,7 +285,7 @@
</div>
<div class="buttons">
- ${h.submit('send',_('Send'),class_="ui-button")}
+ ${h.submit('send',_('Send'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/users/user_add.html b/rhodecode/templates/admin/users/user_add.html
index 64689394..f803aabd 100644
--- a/rhodecode/templates/admin/users/user_add.html
+++ b/rhodecode/templates/admin/users/user_add.html
@@ -56,10 +56,10 @@
<div class="field">
<div class="label">
- <label for="name">${_('First Name')}:</label>
+ <label for="firstname">${_('First Name')}:</label>
</div>
<div class="input">
- ${h.text('name',class_='small')}
+ ${h.text('firstname',class_='small')}
</div>
</div>
@@ -91,7 +91,7 @@
</div>
<div class="buttons">
- ${h.submit('save',_('save'),class_="ui-button")}
+ ${h.submit('save',_('save'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/users/user_edit.html b/rhodecode/templates/admin/users/user_edit.html
index ccf85301..f094dee0 100644
--- a/rhodecode/templates/admin/users/user_edit.html
+++ b/rhodecode/templates/admin/users/user_edit.html
@@ -83,10 +83,10 @@
<div class="field">
<div class="label">
- <label for="name">${_('First Name')}:</label>
+ <label for="firstname">${_('First Name')}:</label>
</div>
<div class="input">
- ${h.text('name',class_='medium')}
+ ${h.text('firstname',class_='medium')}
</div>
</div>
@@ -126,14 +126,14 @@
</div>
</div>
<div class="buttons">
- ${h.submit('save',_('Save'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
${h.end_form()}
</div>
-<div class="box box-right">
+<div style="min-height:780px" class="box box-right">
<!-- box / title -->
<div class="title">
<h5>${_('Permissions')}</h5>
@@ -144,15 +144,138 @@
<div class="fields">
<div class="field">
<div class="label label-checkbox">
+ <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
+ </div>
+ <div class="checkboxes">
+ ${h.checkbox('inherit_default_permissions',value=True)}
+ </div>
+ <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
+ 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
+ </div>
+ <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" >
+ <div class="field">
+ <div class="label label-checkbox">
<label for="create_repo_perm">${_('Create repositories')}:</label>
</div>
<div class="checkboxes">
${h.checkbox('create_repo_perm',value=True)}
</div>
</div>
+ <div class="field">
+ <div class="label label-checkbox">
+ <label for="fork_repo_perm">${_('Fork repositories')}:</label>
+ </div>
+ <div class="checkboxes">
+ ${h.checkbox('fork_repo_perm',value=True)}
+ </div>
+ </div>
+ </div>
+ <div class="buttons">
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
+ </div>
+ </div>
+ </div>
+ ${h.end_form()}
+
+ ## permissions overview
+ <div id="perms" class="table">
+ %for section in sorted(c.perm_user.permissions.keys()):
+ <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
+ %if not c.perm_user.permissions[section]:
+ <span class="empty_data">${_('Nothing here yet')}</span>
+ %else:
+ <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
+ <table id="tbl_list_${section}">
+ <thead>
+ <tr>
+ <th class="left">${_('Name')}</th>
+ <th class="left">${_('Permission')}</th>
+ <th class="left">${_('Edit Permission')}</th>
+ </thead>
+ <tbody>
+ %for k in c.perm_user.permissions[section]:
+ <%
+ if section != 'global':
+ section_perm = c.perm_user.permissions[section].get(k)
+ _perm = section_perm.split('.')[-1]
+ else:
+ _perm = section_perm = None
+ %>
+ <tr>
+ <td>
+ %if section == 'repositories':
+ <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
+ %elif section == 'repositories_groups':
+ <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
+ %else:
+ ${h.get_permission_name(k)}
+ %endif
+ </td>
+ <td>
+ %if section == 'global':
+ ${h.bool2icon(k.split('.')[-1] != 'none')}
+ %else:
+ <span class="perm_tag ${_perm}">${section_perm}</span>
+ %endif
+ </td>
+ <td>
+ %if section == 'repositories':
+ <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
+ %elif section == 'repositories_groups':
+ <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
+ %else:
+ --
+ %endif
+ </td>
+ </tr>
+ %endfor
+ </tbody>
+ </table>
+ </div>
+ %endif
+ %endfor
+ </div>
+</div>
+<div class="box box-left">
+ <!-- box / title -->
+ <div class="title">
+ <h5>${_('Email addresses')}</h5>
+ </div>
+
+ <div class="emails_wrap">
+ <table class="noborder">
+ %for em in c.user_email_map:
+ <tr>
+ <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(em.user.email,16)}"/> </div></td>
+ <td><div class="email">${em.email}</div></td>
+ <td>
+ ${h.form(url('user_emails_delete', id=c.user.user_id),method='delete')}
+ ${h.hidden('del_email',em.email_id)}
+ ${h.submit('remove_',_('delete'),id="remove_email_%s" % em.email_id,
+ class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this email: %s') % em.email+"');")}
+ ${h.end_form()}
+ </td>
+ </tr>
+ %endfor
+ </table>
+ </div>
+
+ ${h.form(url('user_emails', id=c.user.user_id),method='put')}
+ <div class="form">
+ <!-- fields -->
+ <div class="fields">
+ <div class="field">
+ <div class="label">
+ <label for="email">${_('New email address')}:</label>
+ </div>
+ <div class="input">
+ ${h.text('new_email', class_='medium')}
+ </div>
+ </div>
<div class="buttons">
- ${h.submit('save',_('Save'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
+ ${h.submit('save',_('Add'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/users/user_edit_my_account.html b/rhodecode/templates/admin/users/user_edit_my_account.html
index 5ceefd58..be435250 100644
--- a/rhodecode/templates/admin/users/user_edit_my_account.html
+++ b/rhodecode/templates/admin/users/user_edit_my_account.html
@@ -21,158 +21,34 @@
${self.breadcrumbs()}
</div>
<!-- end box / title -->
- <div>
- ${h.form(url('admin_settings_my_account_update'),method='put')}
- <div class="form">
-
- <div class="field">
- <div class="gravatar_box">
- <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
- <p>
- %if c.use_gravatar:
- <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
- <br/>${_('Using')} ${c.user.email}
- %else:
- <br/>${c.user.email}
- %endif
- </p>
- </div>
- </div>
- <div class="field">
- <div class="label">
- <label>${_('API key')}</label> ${c.user.api_key}
- </div>
- </div>
- <div class="fields">
- <div class="field">
- <div class="label">
- <label for="username">${_('Username')}:</label>
- </div>
- <div class="input">
- ${h.text('username',class_="medium")}
- </div>
- </div>
-
- <div class="field">
- <div class="label">
- <label for="new_password">${_('New password')}:</label>
- </div>
- <div class="input">
- ${h.password('new_password',class_="medium",autocomplete="off")}
- </div>
- </div>
-
- <div class="field">
- <div class="label">
- <label for="password_confirmation">${_('New password confirmation')}:</label>
- </div>
- <div class="input">
- ${h.password('password_confirmation',class_="medium",autocomplete="off")}
- </div>
- </div>
-
- <div class="field">
- <div class="label">
- <label for="name">${_('First Name')}:</label>
- </div>
- <div class="input">
- ${h.text('name',class_="medium")}
- </div>
- </div>
-
- <div class="field">
- <div class="label">
- <label for="lastname">${_('Last Name')}:</label>
- </div>
- <div class="input">
- ${h.text('lastname',class_="medium")}
- </div>
- </div>
-
- <div class="field">
- <div class="label">
- <label for="email">${_('Email')}:</label>
- </div>
- <div class="input">
- ${h.text('email',class_="medium")}
- </div>
- </div>
-
- <div class="buttons">
- ${h.submit('save',_('Save'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
- </div>
- </div>
- </div>
- ${h.end_form()}
- </div>
+ ${c.form|n}
</div>
<div class="box box-right">
<!-- box / title -->
<div class="title">
<h5>
- <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
- <a id="show_my" class="link-white" href="#my">${_('My repos')}</a> / <a id="show_perms" class="link-white" href="#perms">${_('My permissions')}</a>
+ <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}" style="display: none"/>
</h5>
- %if h.HasPermissionAny('hg.admin','hg.create.repository')():
- <ul class="links">
+ <ul class="links" style="color:#DADADA">
+ <li>
+ <span><a id="show_perms" class="link-white current" href="#perms">${_('My permissions')}</a> </span>
+ </li>
<li>
- <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
+ <span><a id="show_my" class="link-white" href="#my">${_('My repos')}</a> </span>
</li>
+ <li>
+ <span><a id="show_pullrequests" class="link-white" href="#perms">${_('My pull requests')}</a> </span>
+ </li>
+ %if h.HasPermissionAny('hg.admin','hg.create.repository')():
+ <li>
+ <span>${h.link_to(_('Add repo'),h.url('admin_settings_create_repository'))}</span>
+ </li>
+ %endif
</ul>
- %endif
</div>
<!-- end box / title -->
- <div id="my" class="table">
- <div id='repos_list_wrap' class="yui-skin-sam">
- <table id="repos_list">
- <thead>
- <tr>
- <th></th>
- <th class="left">${_('Name')}</th>
- <th class="left">${_('Revision')}</th>
- <th class="left">${_('Action')}</th>
- <th class="left">${_('Action')}</th>
- </thead>
- <tbody>
- <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
- %if c.user_repos:
- %for repo in c.user_repos:
- <tr>
- ##QUICK MENU
- <td class="quick_repo_menu">
- ${dt.quick_menu(repo['name'])}
- </td>
- ##REPO NAME AND ICONS
- <td class="reponame">
- ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
- </td>
- ##LAST REVISION
- <td>
- ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
- </td>
- <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
- <td>
- ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
- ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
- ${h.end_form()}
- </td>
- </tr>
- %endfor
- %else:
- <div style="padding:5px 0px 10px 0px;">
- ${_('No repositories yet')}
- %if h.HasPermissionAny('hg.admin','hg.create.repository')():
- ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
- %endif
- </div>
- %endif
- </tbody>
- </table>
- </div>
- </div>
- <div id="perms" class="table" style="display:none">
+ <div id="perms" class="table">
%for section in sorted(c.rhodecode_user.permissions.keys()):
<div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
@@ -218,7 +94,13 @@
</div>
%endfor
</div>
+ <div id="my" class="table" style="display:none">
+ </div>
+ <div id="pullrequests" class="table" style="display:none"></div>
</div>
+
+
+
<script type="text/javascript">
var filter_activate = function(){
var nodes = YUQ('#my tr td a.repo_name');
@@ -227,22 +109,48 @@ var filter_activate = function(){
}
q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
}
+YUE.on('show_perms','click',function(e){
+ YUD.addClass('show_perms', 'current');
+ YUD.removeClass('show_my','current');
+ YUD.removeClass('show_pullrequests','current');
+ YUD.setStyle('my','display','none');
+ YUD.setStyle('pullrequests','display','none');
+ YUD.setStyle('perms','display','');
+ YUD.setStyle('q_filter','display','none');
+ YUE.preventDefault(e);
+})
YUE.on('show_my','click',function(e){
+ YUD.addClass('show_my', 'current');
+ YUD.removeClass('show_perms','current');
+ YUD.removeClass('show_pullrequests','current');
+
YUD.setStyle('perms','display','none');
+ YUD.setStyle('pullrequests','display','none');
YUD.setStyle('my','display','');
- YUD.get('q_filter').removeAttribute('disabled');
- filter_activate();
+ YUD.setStyle('q_filter','display','');
+
YUE.preventDefault(e);
+ var url = "${h.url('admin_settings_my_repos')}";
+ ypjax(url, 'my', function(){
+ table_sort();
+ filter_activate();
+ });
})
-YUE.on('show_perms','click',function(e){
+YUE.on('show_pullrequests','click',function(e){
+ YUD.addClass('show_pullrequests', 'current');
+ YUD.removeClass('show_my','current');
+ YUD.removeClass('show_perms','current');
+
YUD.setStyle('my','display','none');
- YUD.setStyle('perms','display','');
- YUD.setAttribute('q_filter','disabled','disabled');
+ YUD.setStyle('perms','display','none');
+ YUD.setStyle('pullrequests','display','');
+ YUD.setStyle('q_filter','display','none');
YUE.preventDefault(e);
+ var url = "${h.url('admin_settings_my_pullrequests')}";
+ ypjax(url, 'pullrequests');
})
-
// main table sorting
var myColumnDefs = [
{key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
@@ -254,6 +162,7 @@ var myColumnDefs = [
{key:"action2",label:"",sortable:false},
];
+function table_sort(){
var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
myDataSource.responseSchema = {
@@ -308,6 +217,6 @@ myDataSource3.responseSchema = {
};
new YAHOO.widget.DataTable("tbl_list_wrap_repositories_groups", permsColumnDefs, myDataSource3, trans_defs);
-
+}
</script>
</%def>
diff --git a/rhodecode/templates/admin/users/user_edit_my_account_form.html b/rhodecode/templates/admin/users/user_edit_my_account_form.html
new file mode 100644
index 00000000..4620fd9e
--- /dev/null
+++ b/rhodecode/templates/admin/users/user_edit_my_account_form.html
@@ -0,0 +1,85 @@
+<div>
+ ${h.form(url('admin_settings_my_account_update'),method='put')}
+ <div class="form">
+
+ <div class="field">
+ <div class="gravatar_box">
+ <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
+ <p>
+ %if c.use_gravatar:
+ <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
+ <br/>${_('Using')} ${c.user.email}
+ %else:
+ <br/>${c.user.email}
+ %endif
+ </p>
+ </div>
+ </div>
+ <div class="field">
+ <div class="label">
+ <label>${_('API key')}</label> ${c.user.api_key}
+ </div>
+ </div>
+ <div class="fields">
+ <div class="field">
+ <div class="label">
+ <label for="username">${_('Username')}:</label>
+ </div>
+ <div class="input">
+ ${h.text('username',class_="medium")}
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label">
+ <label for="new_password">${_('New password')}:</label>
+ </div>
+ <div class="input">
+ ${h.password('new_password',class_="medium",autocomplete="off")}
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label">
+ <label for="password_confirmation">${_('New password confirmation')}:</label>
+ </div>
+ <div class="input">
+ ${h.password('password_confirmation',class_="medium",autocomplete="off")}
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label">
+ <label for="name">${_('First Name')}:</label>
+ </div>
+ <div class="input">
+ ${h.text('firstname',class_="medium")}
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label">
+ <label for="lastname">${_('Last Name')}:</label>
+ </div>
+ <div class="input">
+ ${h.text('lastname',class_="medium")}
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label">
+ <label for="email">${_('Email')}:</label>
+ </div>
+ <div class="input">
+ ${h.text('email',class_="medium")}
+ </div>
+ </div>
+
+ <div class="buttons">
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
+ </div>
+ </div>
+ </div>
+ ${h.end_form()}
+ </div>
diff --git a/rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html b/rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html
new file mode 100644
index 00000000..898bc689
--- /dev/null
+++ b/rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html
@@ -0,0 +1,41 @@
+
+<div class="pullrequests_section_head">${_('Opened by me')}</div>
+<ul>
+ %if c.my_pull_requests:
+ %for pull_request in c.my_pull_requests:
+ <li>
+ <div style="height: 12px">
+ <div style="float:left">
+ <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
+ ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))}
+ </a>
+ </div>
+ <div style="float:left;margin-top: -5px">
+ ${h.form(url('pullrequest_delete', repo_name=pull_request.other_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')}
+ ${h.submit('remove_%s' % pull_request.pull_request_id,'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
+ ${h.end_form()}
+ </div>
+ </div>
+ </li>
+ %endfor
+ %else:
+ <li><span class="empty_data">${_('Nothing here yet')}</span></li>
+ %endif
+</ul>
+
+<div class="pullrequests_section_head" style="clear:both">${_('I participate in')}</div>
+<ul>
+ %if c.my_pull_requests:
+ %for pull_request in c.participate_in_pull_requests:
+ <li>
+ <div style="height: 12px">
+ <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
+ ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))}
+ </a>
+ </div>
+ </li>
+ %endfor
+ %else:
+ <li><span class="empty_data">${_('Nothing here yet')}</span></li>
+ %endif
+</ul>
diff --git a/rhodecode/templates/admin/users/user_edit_my_account_repos.html b/rhodecode/templates/admin/users/user_edit_my_account_repos.html
new file mode 100644
index 00000000..ad16f8af
--- /dev/null
+++ b/rhodecode/templates/admin/users/user_edit_my_account_repos.html
@@ -0,0 +1,46 @@
+<div id='repos_list_wrap' class="yui-skin-sam">
+ <table id="repos_list">
+ <thead>
+ <tr>
+ <th></th>
+ <th class="left">${_('Name')}</th>
+ <th class="left">${_('Revision')}</th>
+ <th class="left">${_('Action')}</th>
+ <th class="left">${_('Action')}</th>
+ </thead>
+ <tbody>
+ <%namespace name="dt" file="/data_table/_dt_elements.html"/>
+ %if c.user_repos:
+ %for repo in c.user_repos:
+ <tr>
+ ##QUICK MENU
+ <td class="quick_repo_menu">
+ ${dt.quick_menu(repo['name'])}
+ </td>
+ ##REPO NAME AND ICONS
+ <td class="reponame">
+ ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
+ </td>
+ ##LAST REVISION
+ <td>
+ ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
+ </td>
+ <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
+ <td>
+ ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
+ ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
+ ${h.end_form()}
+ </td>
+ </tr>
+ %endfor
+ %else:
+ <div style="padding:5px 0px 10px 0px;">
+ ${_('No repositories yet')}
+ %if h.HasPermissionAny('hg.admin','hg.create.repository')():
+ ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
+ %endif
+ </div>
+ %endif
+ </tbody>
+ </table>
+</div>
diff --git a/rhodecode/templates/admin/users/users.html b/rhodecode/templates/admin/users/users.html
index 36d8e29d..30ad31f1 100644
--- a/rhodecode/templates/admin/users/users.html
+++ b/rhodecode/templates/admin/users/users.html
@@ -6,7 +6,7 @@
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Users')}
+ <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span> ${_('users')}
</%def>
<%def name="page_nav()">
@@ -22,44 +22,126 @@
<li>
<span>${h.link_to(_(u'ADD NEW USER'),h.url('new_user'))}</span>
</li>
-
</ul>
</div>
<!-- end box / title -->
- <div class="table">
- <table class="table_disp">
- <tr class="header">
- <th></th>
- <th class="left">${_('username')}</th>
- <th class="left">${_('name')}</th>
- <th class="left">${_('lastname')}</th>
- <th class="left">${_('last login')}</th>
- <th class="left">${_('active')}</th>
- <th class="left">${_('admin')}</th>
- <th class="left">${_('ldap')}</th>
- <th class="left">${_('action')}</th>
- </tr>
- %for cnt,user in enumerate(c.users_list):
- %if user.name !='default':
- <tr class="parity${cnt%2}">
- <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div></td>
- <td>${h.link_to(user.username,h.url('edit_user', id=user.user_id))}</td>
- <td>${user.name}</td>
- <td>${user.lastname}</td>
- <td>${user.last_login}</td>
- <td>${h.bool2icon(user.active)}</td>
- <td>${h.bool2icon(user.admin)}</td>
- <td>${h.bool2icon(bool(user.ldap_dn))}</td>
- <td>
- ${h.form(url('delete_user', id=user.user_id),method='delete')}
- ${h.submit('remove_',_('delete'),id="remove_user_%s" % user.user_id,
- class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this user: %s') % user.username+"');")}
- ${h.end_form()}
- </td>
- </tr>
- %endif
- %endfor
- </table>
- </div>
+ <div class="table yui-skin-sam" id="users_list_wrap"></div>
+ <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
</div>
+
+<script>
+ var url = "${h.url('formatted_users', format='json')}";
+ var data = ${c.data|n};
+ var myDataSource = new YAHOO.util.DataSource(data);
+ myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
+
+ myDataSource.responseSchema = {
+ resultsList: "records",
+ fields: [
+ {key: "gravatar"},
+ {key: "raw_username"},
+ {key: "username"},
+ {key: "firstname"},
+ {key: "lastname"},
+ {key: "last_login"},
+ {key: "last_login_raw"},
+ {key: "active"},
+ {key: "admin"},
+ {key: "ldap"},
+ {key: "action"},
+ ]
+ };
+ myDataSource.doBeforeCallback = function(req,raw,res,cb) {
+ // This is the filter function
+ var data = res.results || [],
+ filtered = [],
+ i,l;
+
+ if (req) {
+ req = req.toLowerCase();
+ for (i = 0; i<data.length; i++) {
+ var pos = data[i].raw_username.toLowerCase().indexOf(req)
+ if (pos != -1) {
+ filtered.push(data[i]);
+ }
+ }
+ res.results = filtered;
+ }
+ YUD.get('user_count').innerHTML = res.results.length;
+ return res;
+ }
+
+ // main table sorting
+ var myColumnDefs = [
+ {key:"gravatar",label:"",sortable:false,},
+ {key:"username",label:"${_('username')}",sortable:true,
+ sortOptions: { sortFunction: linkSort }
+ },
+ {key:"firstname",label:"${_('firstname')}",sortable:true,},
+ {key:"lastname",label:"${_('lastname')}",sortable:true,},
+ {key:"last_login",label:"${_('last login')}",sortable:true,
+ sortOptions: { sortFunction: lastLoginSort }},
+ {key:"active",label:"${_('active')}",sortable:true,},
+ {key:"admin",label:"${_('admin')}",sortable:true,},
+ {key:"ldap",label:"${_('ldap')}",sortable:true,},
+ {key:"action",label:"${_('action')}",sortable:false},
+ ];
+
+ var myDataTable = new YAHOO.widget.DataTable("users_list_wrap", myColumnDefs, myDataSource,{
+ sortedBy:{key:"username",dir:"asc"},
+ paginator: new YAHOO.widget.Paginator({
+ rowsPerPage: 15,
+ alwaysVisible: false,
+ template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
+ pageLinks: 5,
+ containerClass: 'pagination-wh',
+ currentPageClass: 'pager_curpage',
+ pageLinkClass: 'pager_link',
+ nextPageLinkLabel: '&gt;',
+ previousPageLinkLabel: '&lt;',
+ firstPageLinkLabel: '&lt;&lt;',
+ lastPageLinkLabel: '&gt;&gt;',
+ containers:['user-paginator']
+ }),
+
+ MSG_SORTASC:"${_('Click to sort ascending')}",
+ MSG_SORTDESC:"${_('Click to sort descending')}",
+ MSG_EMPTY:"${_('No records found.')}",
+ MSG_ERROR:"${_('Data error.')}",
+ MSG_LOADING:"${_('Loading...')}",
+ }
+ );
+ myDataTable.subscribe('postRenderEvent',function(oArgs) {
+
+ });
+
+ var filterTimeout = null;
+
+ updateFilter = function () {
+ // Reset timeout
+ filterTimeout = null;
+
+ // Reset sort
+ var state = myDataTable.getState();
+ state.sortedBy = {key:'username', dir:YAHOO.widget.DataTable.CLASS_ASC};
+
+ // Get filtered data
+ myDataSource.sendRequest(YUD.get('q_filter').value,{
+ success : myDataTable.onDataReturnInitializeTable,
+ failure : myDataTable.onDataReturnInitializeTable,
+ scope : myDataTable,
+ argument: state
+ });
+
+ };
+ YUE.on('q_filter','click',function(){
+ YUD.get('q_filter').value = '';
+ });
+
+ YUE.on('q_filter','keyup',function (e) {
+ clearTimeout(filterTimeout);
+ filterTimeout = setTimeout(updateFilter,600);
+ });
+</script>
+
</%def>
diff --git a/rhodecode/templates/admin/users_groups/users_group_add.html b/rhodecode/templates/admin/users_groups/users_group_add.html
index 580bad9c..2eb80c13 100644
--- a/rhodecode/templates/admin/users_groups/users_group_add.html
+++ b/rhodecode/templates/admin/users_groups/users_group_add.html
@@ -46,7 +46,7 @@
</div>
<div class="buttons">
- ${h.submit('save',_('save'),class_="ui-button")}
+ ${h.submit('save',_('save'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/admin/users_groups/users_group_edit.html b/rhodecode/templates/admin/users_groups/users_group_edit.html
index b5259d54..d8083d0a 100644
--- a/rhodecode/templates/admin/users_groups/users_group_edit.html
+++ b/rhodecode/templates/admin/users_groups/users_group_edit.html
@@ -87,7 +87,7 @@
</div>
<div class="buttons">
- ${h.submit('save',_('save'),class_="ui-button")}
+ ${h.submit('save',_('save'),class_="ui-btn large")}
</div>
</div>
</div>
@@ -105,15 +105,35 @@ ${h.end_form()}
<div class="fields">
<div class="field">
<div class="label label-checkbox">
+ <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
+ </div>
+ <div class="checkboxes">
+ ${h.checkbox('inherit_default_permissions',value=True)}
+ </div>
+ <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
+ 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
+ </div>
+ <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
+ <div class="field">
+ <div class="label label-checkbox">
<label for="create_repo_perm">${_('Create repositories')}:</label>
</div>
<div class="checkboxes">
${h.checkbox('create_repo_perm',value=True)}
</div>
</div>
+ <div class="field">
+ <div class="label label-checkbox">
+ <label for="fork_repo_perm">${_('Fork repositories')}:</label>
+ </div>
+ <div class="checkboxes">
+ ${h.checkbox('fork_repo_perm',value=True)}
+ </div>
+ </div>
+ </div>
<div class="buttons">
- ${h.submit('save',_('Save'),class_="ui-button")}
- ${h.reset('reset',_('Reset'),class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
@@ -140,141 +160,6 @@ ${h.end_form()}
</div>
</div>
<script type="text/javascript">
-YAHOO.util.Event.onDOMReady(function(){
- var D = YAHOO.util.Dom;
- var E = YAHOO.util.Event;
-
- //definition of containers ID's
- var available_container = 'available_members';
- var selected_container = 'users_group_members';
-
- //form containing containers id
- var form_id = 'edit_users_group';
-
- //temp container for selected storage.
- var cache = new Array();
- var av_cache = new Array();
- var c = D.get(selected_container);
- var ac = D.get(available_container);
-
- //get only selected options for further fullfilment
- for(var i = 0;node =c.options[i];i++){
- if(node.selected){
- //push selected to my temp storage left overs :)
- cache.push(node);
- }
- }
-
- //get all available options to cache
- for(var i = 0;node =ac.options[i];i++){
- //push selected to my temp storage left overs :)
- av_cache.push(node);
- }
-
- //fill available only with those not in choosen
- ac.options.length=0;
- tmp_cache = new Array();
-
- for(var i = 0;node = av_cache[i];i++){
- var add = true;
- for(var i2 = 0;node_2 = cache[i2];i2++){
- if(node.value == node_2.value){
- add=false;
- break;
- }
- }
- if(add){
- tmp_cache.push(new Option(node.text, node.value, false, false));
- }
- }
-
- for(var i = 0;node = tmp_cache[i];i++){
- ac.options[i] = node;
- }
-
- function prompts_action_callback(e){
-
- var choosen = D.get(selected_container);
- var available = D.get(available_container);
-
- //get checked and unchecked options from field
- function get_checked(from_field){
- //temp container for storage.
- var sel_cache = new Array();
- var oth_cache = new Array();
-
- for(var i = 0;node = from_field.options[i];i++){
- if(node.selected){
- //push selected fields :)
- sel_cache.push(node);
- }
- else{
- oth_cache.push(node)
- }
- }
-
- return [sel_cache,oth_cache]
- }
-
- //fill the field with given options
- function fill_with(field,options){
- //clear firtst
- field.options.length=0;
- for(var i = 0;node = options[i];i++){
- field.options[i]=new Option(node.text, node.value,
- false, false);
- }
-
- }
- //adds to current field
- function add_to(field,options){
- for(var i = 0;node = options[i];i++){
- field.appendChild(new Option(node.text, node.value,
- false, false));
- }
- }
-
- // add action
- if (this.id=='add_element'){
- var c = get_checked(available);
- add_to(choosen,c[0]);
- fill_with(available,c[1]);
- }
- // remove action
- if (this.id=='remove_element'){
- var c = get_checked(choosen);
- add_to(available,c[0]);
- fill_with(choosen,c[1]);
- }
- // add all elements
- if(this.id=='add_all_elements'){
- for(var i=0; node = available.options[i];i++){
- choosen.appendChild(new Option(node.text,
- node.value, false, false));
- }
- available.options.length = 0;
- }
- //remove all elements
- if(this.id=='remove_all_elements'){
- for(var i=0; node = choosen.options[i];i++){
- available.appendChild(new Option(node.text,
- node.value, false, false));
- }
- choosen.options.length = 0;
- }
-
- }
-
- E.addListener(['add_element','remove_element',
- 'add_all_elements','remove_all_elements'],'click',
- prompts_action_callback)
-
- E.addListener(form_id,'submit',function(){
- var choosen = D.get(selected_container);
- for (var i = 0; i < choosen.options.length; i++) {
- choosen.options[i].selected = 'selected';
- }
- });
-});
+ MultiSelectWidget('users_group_members','available_members','edit_users_group');
</script>
</%def>
diff --git a/rhodecode/templates/admin/users_groups/users_groups.html b/rhodecode/templates/admin/users_groups/users_groups.html
index d9673c1d..b1e9bd9a 100644
--- a/rhodecode/templates/admin/users_groups/users_groups.html
+++ b/rhodecode/templates/admin/users_groups/users_groups.html
@@ -37,11 +37,11 @@
%for cnt,u_group in enumerate(c.users_groups_list):
<tr class="parity${cnt%2}">
<td>${h.link_to(u_group.users_group_name,h.url('edit_users_group', id=u_group.users_group_id))}</td>
- <td><span class="tooltip" title="${', '.join(map(h.safe_unicode,[x.user.username for x in u_group.members[:50]]))}">${len(u_group.members)}</span></td>
+ <td><span class="tooltip" title="${h.tooltip(', '.join(map(h.safe_unicode,[x.user.username for x in u_group.members[:50]])))}">${len(u_group.members)}</span></td>
<td>${h.bool2icon(u_group.users_group_active)}</td>
<td>
${h.form(url('users_group', id=u_group.users_group_id),method='delete')}
- ${h.submit('remove_','delete',id="remove_group_%s" % u_group.users_group_id,
+ ${h.submit('remove_',_('delete'),id="remove_group_%s" % u_group.users_group_id,
class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this users group: %s') % u_group.users_group_name+"');")}
${h.end_form()}
</td>
diff --git a/rhodecode/templates/base/base.html b/rhodecode/templates/base/base.html
index 6c07c6fd..d722b6a9 100644
--- a/rhodecode/templates/base/base.html
+++ b/rhodecode/templates/base/base.html
@@ -207,6 +207,9 @@
%endif
%endif
<li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
+ %if h.is_hg(c.rhodecode_repo):
+ <li>${h.link_to(_('Open new pull request'),h.url('pullrequest_home',repo_name=c.repo_name),class_='pull_request')}</li>
+ %endif
<li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
% if h.HasPermissionAll('hg.admin')('access admin main page'):
@@ -247,6 +250,14 @@
<span class="short">${c.repository_forks}</span>
</a>
</li>
+ <li>
+ <a class="menu_link" title="${_('Pull requests')}" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}">
+ <span class="icon_short">
+ <img src="${h.url('/images/icons/arrow_join.png')}" alt="${_('Pull requests')}" />
+ </span>
+ <span class="short">${c.repository_pull_requests}</span>
+ </a>
+ </li>
${usermenu()}
</ul>
<script type="text/javascript">
diff --git a/rhodecode/templates/base/root.html b/rhodecode/templates/base/root.html
index 364322f4..9851bfdc 100644
--- a/rhodecode/templates/base/root.html
+++ b/rhodecode/templates/base/root.html
@@ -36,32 +36,50 @@
## JAVASCRIPT ##
<%def name="js()">
+ <script type="text/javascript">
+ //JS translations map
+ var TRANSLATION_MAP = {
+ 'add another comment':'${_("add another comment")}',
+ 'Stop following this repository':"${_('Stop following this repository')}",
+ 'Start following this repository':"${_('Start following this repository')}",
+ 'Group':"${_('Group')}",
+ 'members':"${_('members')}",
+ 'search truncated': "${_('search truncated')}",
+ 'no matching files': "${_('no matching files')}"
+
+ };
+ var _TM = TRANSLATION_MAP;
+ </script>
<script type="text/javascript" src="${h.url('/js/yui.2.9.js')}"></script>
<!--[if lt IE 9]>
<script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script>
<![endif]-->
<script type="text/javascript" src="${h.url('/js/yui.flot.js')}"></script>
+ <script type="text/javascript" src="${h.url('/js/native.history.js')}"></script>
<script type="text/javascript" src="${h.url('/js/rhodecode.js')}"></script>
## EXTRA FOR JS
${self.js_extra()}
<script type="text/javascript">
+ (function(window,undefined){
+
+ // Prepare
+ var History = window.History; // Note: We are using a capital H instead of a lower h
+ if ( !History.enabled ) {
+ // History.js is disabled for this browser.
+ // This is because we can optionally choose to support HTML4 browsers or not.
+ return false;
+ }
+ })(window);
var follow_base_url = "${h.url('toggle_following')}";
- //JS translations map
- var TRANSLATION_MAP = {
- 'add another comment':'${_("add another comment")}',
- 'Stop following this repository':"${_('Stop following this repository')}",
- 'Start following this repository':"${_('Start following this repository')}",
- };
-
var onSuccessFollow = function(target){
var f = YUD.get(target.id);
var f_cnt = YUD.get('current_followers_count');
if(f.getAttribute('class')=='follow'){
f.setAttribute('class','following');
- f.setAttribute('title',TRANSLATION_MAP['Stop following this repository']);
+ f.setAttribute('title',_TM['Stop following this repository']);
if(f_cnt){
var cnt = Number(f_cnt.innerHTML)+1;
@@ -70,7 +88,7 @@
}
else{
f.setAttribute('class','follow');
- f.setAttribute('title',TRANSLATION_MAP['Start following this repository']);
+ f.setAttribute('title',_TM['Start following this repository']);
if(f_cnt){
var cnt = Number(f_cnt.innerHTML)+1;
f_cnt.innerHTML = cnt;
@@ -132,6 +150,8 @@
</%def>
<%def name="js_extra()"></%def>
${self.js()}
+ <%def name="head_extra()"></%def>
+ ${self.head_extra()}
</head>
<body id="body">
## IE hacks
diff --git a/rhodecode/templates/bookmarks/bookmarks.html b/rhodecode/templates/bookmarks/bookmarks.html
index 8d512297..8b04d1ca 100644
--- a/rhodecode/templates/bookmarks/bookmarks.html
+++ b/rhodecode/templates/bookmarks/bookmarks.html
@@ -2,13 +2,13 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Bookmarks')} - ${c.rhodecode_name}
+ ${_('%s Bookmarks') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
<input class="q_filter_box" id="q_filter_bookmarks" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/bookmarks/bookmarks_data.html b/rhodecode/templates/bookmarks/bookmarks_data.html
index f6cb448f..01c4d6a7 100644
--- a/rhodecode/templates/bookmarks/bookmarks_data.html
+++ b/rhodecode/templates/bookmarks/bookmarks_data.html
@@ -17,7 +17,7 @@
h.url('files_home',repo_name=c.repo_name,revision=book[1].raw_id))}</span>
</span>
</td>
- <td><span class="tooltip" title="${h.age(book[1].date)}">${book[1].date}</span></td>
+ <td><span class="tooltip" title="${h.tooltip(h.age(book[1].date))}">${h.fmt_date(book[1].date)}</span></td>
<td title="${book[1].author}">${h.person(book[1].author)}</td>
<td>
<div>
diff --git a/rhodecode/templates/branches/branches.html b/rhodecode/templates/branches/branches.html
index b9333ed5..31a05184 100644
--- a/rhodecode/templates/branches/branches.html
+++ b/rhodecode/templates/branches/branches.html
@@ -2,12 +2,12 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Branches')} - ${c.rhodecode_name}
+ ${_('%s Branches') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
<input class="q_filter_box" id="q_filter_branches" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
@@ -25,12 +25,27 @@
${self.breadcrumbs()}
</div>
<!-- end box / title -->
+ %if c.repo_branches:
+ <div class="info_box" id="compare_branches" style="clear: both;padding: 10px 19px;vertical-align: right;text-align: right;"><a href="#" class="ui-btn small">${_('Compare branches')}</a></div>
+ %endif
<div class="table">
<%include file='branches_data.html'/>
</div>
</div>
<script type="text/javascript">
+YUE.on('compare_branches','click',function(e){
+ YUE.preventDefault(e);
+ var org = YUQ('input[name=compare_org]:checked')[0];
+ var other = YUQ('input[name=compare_other]:checked')[0];
+ if(org && other){
+ var compare_url = "${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref='__ORG__',other_ref_type='branch',other_ref='__OTHER__')}";
+ var u = compare_url.replace('__ORG__',org.value)
+ .replace('__OTHER__',other.value);
+ window.location=u;
+ }
+
+})
// main table sorting
var myColumnDefs = [
{key:"name",label:"${_('Name')}",sortable:true},
@@ -39,6 +54,7 @@ var myColumnDefs = [
{key:"author",label:"${_('Author')}",sortable:true},
{key:"revision",label:"${_('Revision')}",sortable:true,
sortOptions: { sortFunction: revisionSort }},
+ {key:"compare",label:"${_('Compare')}",sortable:false,},
];
var myDataSource = new YAHOO.util.DataSource(YUD.get("branches_data"));
@@ -51,6 +67,7 @@ myDataSource.responseSchema = {
{key:"date"},
{key:"author"},
{key:"revision"},
+ {key:"compare"},
]
};
diff --git a/rhodecode/templates/branches/branches_data.html b/rhodecode/templates/branches/branches_data.html
index 6a44a9b7..2c4a088d 100644
--- a/rhodecode/templates/branches/branches_data.html
+++ b/rhodecode/templates/branches/branches_data.html
@@ -7,6 +7,7 @@
<th class="left">${_('date')}</th>
<th class="left">${_('author')}</th>
<th class="left">${_('revision')}</th>
+ <th class="left">${_('compare')}</th>
</tr>
</thead>
%for cnt,branch in enumerate(c.repo_branches.items()):
@@ -17,13 +18,17 @@
h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id))}</span>
</span>
</td>
- <td><span class="tooltip" title="${h.age(branch[1].date)}">${branch[1].date}</span></td>
+ <td><span class="tooltip" title="${h.tooltip(h.age(branch[1].date))}">${h.fmt_date(branch[1].date)}</span></td>
<td title="${branch[1].author}">${h.person(branch[1].author)}</td>
<td>
<div>
<pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id)}">r${branch[1].revision}:${h.short_id(branch[1].raw_id)}</a></pre>
</div>
</td>
+ <td>
+ <input class="branch-compare" type="radio" name="compare_org" value="${branch[0]}"/>
+ <input class="branch-compare" type="radio" name="compare_other" value="${branch[0]}"/>
+ </td>
</tr>
%endfor
% if hasattr(c,'repo_closed_branches') and c.repo_closed_branches:
@@ -35,13 +40,14 @@
h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].raw_id))}</span>
</span>
</td>
- <td><span class="tooltip" title="${h.age(branch[1].date)}">${branch[1].date}</span></td>
+ <td><span class="tooltip" title="${h.tooltip(h.age(branch[1].date))}">${h.fmt_date(branch[1].date)}</span></td>
<td title="${branch[1].author}">${h.person(branch[1].author)}</td>
<td>
<div>
<pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id)}">r${branch[1].revision}:${h.short_id(branch[1].raw_id)}</a></pre>
</div>
</td>
+ <td></td>
</tr>
%endfor
%endif
diff --git a/rhodecode/templates/changelog/changelog.html b/rhodecode/templates/changelog/changelog.html
index b107f4ec..96165f58 100644
--- a/rhodecode/templates/changelog/changelog.html
+++ b/rhodecode/templates/changelog/changelog.html
@@ -3,15 +3,16 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
-${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
+${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
- ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
+ <% size = c.size if c.size <= c.total_cs else c.total_cs %>
+ ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
</%def>
<%def name="page_nav()">
@@ -31,6 +32,14 @@ ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
<canvas id="graph_canvas"></canvas>
</div>
<div id="graph_content">
+ <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;">
+ %if c.rhodecode_db_repo.fork:
+ <a title="${_('compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref='default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name)}" class="ui-btn small">${_('Compare fork')}</a>
+ %endif
+ %if h.is_hg(c.rhodecode_repo):
+ <a href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
+ %endif
+ </div>
<div class="container_header">
${h.form(h.url.current(),method='get')}
<div class="info_box" style="float:left">
@@ -47,24 +56,24 @@ ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
<div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
<div class="left">
<div>
- ${h.checkbox(cs.short_id,class_="changeset_range")}
- <span class="tooltip" title="${h.age(cs.date)}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
+ ${h.checkbox(cs.raw_id,class_="changeset_range")}
+ <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
</div>
<div class="author">
<div class="gravatar">
- <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
</div>
- <div title="${cs.author}" class="user">${h.person(cs.author)}</div>
+ <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
</div>
- <div class="date">${cs.date}</div>
+ <div class="date">${h.fmt_date(cs.date)}</div>
</div>
<div class="mid">
- <div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
+ <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
<div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
</div>
<div class="right">
<div class="changes">
- <div id="${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</div>
+ <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
<div class="comments-container">
%if len(c.comments.get(cs.raw_id,[])) > 0:
<div class="comments-cnt" title="${('comments')}">
@@ -75,6 +84,18 @@ ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
</div>
%endif
</div>
+ <div class="changeset-status-container">
+ %if c.statuses.get(cs.raw_id):
+ <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
+ <div class="changeset-status-ico">
+ %if c.statuses.get(cs.raw_id)[2]:
+ <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
+ %else:
+ <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
+ %endif
+ </div>
+ %endif
+ </div>
</div>
%if cs.parents:
%for p_cs in reversed(cs.parents):
@@ -141,8 +162,8 @@ ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
rev_start+'...'+rev_end);
var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
- link = link.replace('__S',rev_start);
- link = link.replace('__E',rev_end);
+ link = link.replace('__S',rev_start.substr(0,6));
+ link = link.replace('__E',rev_end.substr(0,6));
YUD.get('rev_range_container').innerHTML = link;
YUD.setStyle('rev_range_container','display','');
}
@@ -182,9 +203,9 @@ ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
// Fetch changeset details
YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
- var id = e.currentTarget.id
- var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
- var url = url.replace('__CS__',id);
+ var id = e.currentTarget.id;
+ var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
+ var url = url.replace('__CS__',id.replace('changed_total_',''));
ypjax(url,id,function(){tooltip_activate()});
});
diff --git a/rhodecode/templates/changelog/changelog_details.html b/rhodecode/templates/changelog/changelog_details.html
index c90f9025..18491381 100644
--- a/rhodecode/templates/changelog/changelog_details.html
+++ b/rhodecode/templates/changelog/changelog_details.html
@@ -1,9 +1,11 @@
+## small box that displays changed/added/removed details fetched by AJAX
+
% if len(c.cs.affected_files) <= c.affected_files_cut_off:
-<span class="removed tooltip" title="<b>${_('removed')}</b>${h.changed_tooltip(c.cs.removed)}">${len(c.cs.removed)}</span>
-<span class="changed tooltip" title="<b>${_('changed')}</b>${h.changed_tooltip(c.cs.changed)}">${len(c.cs.changed)}</span>
-<span class="added tooltip" title="<b>${_('added')}</b>${h.changed_tooltip(c.cs.added)}">${len(c.cs.added)}</span>
+<span class="removed tooltip" title="<b>${h.tooltip(_('removed'))}</b>${h.changed_tooltip(c.cs.removed)}">${len(c.cs.removed)}</span>
+<span class="changed tooltip" title="<b>${h.tooltip(_('changed'))}</b>${h.changed_tooltip(c.cs.changed)}">${len(c.cs.changed)}</span>
+<span class="added tooltip" title="<b>${h.tooltip(_('added'))}</b>${h.changed_tooltip(c.cs.added)}">${len(c.cs.added)}</span>
% else:
- <span class="removed tooltip" title="${_('affected %s files') % len(c.cs.affected_files)}">!</span>
- <span class="changed tooltip" title="${_('affected %s files') % len(c.cs.affected_files)}">!</span>
- <span class="added tooltip" title="${_('affected %s files') % len(c.cs.affected_files)}">!</span>
+ <span class="removed tooltip" title="${h.tooltip(_('affected %s files') % len(c.cs.affected_files))}">!</span>
+ <span class="changed tooltip" title="${h.tooltip(_('affected %s files') % len(c.cs.affected_files))}">!</span>
+ <span class="added tooltip" title="${h.tooltip(_('affected %s files') % len(c.cs.affected_files))}">!</span>
% endif
diff --git a/rhodecode/templates/changeset/changeset.html b/rhodecode/templates/changeset/changeset.html
index f7d9047f..227291b6 100644
--- a/rhodecode/templates/changeset/changeset.html
+++ b/rhodecode/templates/changeset/changeset.html
@@ -3,11 +3,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
+ ${_('%s Changeset') % c.repo_name} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
@@ -31,15 +31,21 @@
r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
</div>
<div class="date">
- ${c.changeset.date}
+ ${h.fmt_date(c.changeset.date)}
+ </div>
+ <div class="changeset-status-container">
+ %if c.statuses:
+ <div title="${_('Changeset status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.statuses[0])}]</div>
+ <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[0])}" /></div>
+ %endif
</div>
<div class="diff-actions">
- <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
- <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
+ <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" class="tooltip" title="${h.tooltip(_('raw diff'))}"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
+ <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" class="tooltip" title="${h.tooltip(_('download diff'))}"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
${c.ignorews_url(request.GET)}
${c.context_url(request.GET)}
</div>
- <div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
+ <div class="comments-number" style="float:right;padding-right:5px">${ungettext("%d comment", "%d comments", len(c.comments)) % len(c.comments)} ${ungettext("(%d inline)", "(%d inline)", c.inline_cnt) % c.inline_cnt}</div>
</div>
</div>
<div id="changeset_content">
@@ -47,12 +53,12 @@
<div class="left">
<div class="author">
<div class="gravatar">
- <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.changeset.author),20)}"/>
</div>
<span>${h.person(c.changeset.author)}</span><br/>
<span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
</div>
- <div class="message">${h.urlify_commit(h.wrap_paragraphs(c.changeset.message),c.repo_name)}</div>
+ <div class="message">${h.urlify_commit(c.changeset.message, c.repo_name)}</div>
</div>
<div class="right">
<div class="changes">
@@ -116,24 +122,36 @@
</div>
</div>
-
+ <script>
+ var _USERS_AC_DATA = ${c.users_array|n};
+ var _GROUPS_AC_DATA = ${c.users_groups_array|n};
+ AJAX_COMMENT_URL = "${url('changeset_comment',repo_name=c.repo_name,revision=c.changeset.raw_id)}";
+ AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
+ </script>
## diff block
<%namespace name="diff_block" file="/changeset/diff_block.html"/>
${diff_block.diff_block(c.changes)}
## template for inline comment form
<%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
- ${comment.comment_inline_form(c.changeset)}
+ ${comment.comment_inline_form()}
- ## render comments
- ${comment.comments(c.changeset)}
+ ## render comments and inlines
+ ${comment.generate_comments()}
+
+ ## main comment form and it status
+ ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id),
+ h.changeset_status(c.rhodecode_db_repo, c.changeset.raw_id))}
+
+ ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
<script type="text/javascript">
YUE.onDOMReady(function(){
- AJAX_COMMENT_URL = "${url('changeset_comment',repo_name=c.repo_name,revision=c.changeset.raw_id)}";
- AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"
YUE.on(YUQ('.show-inline-comments'),'change',function(e){
var show = 'none';
var target = e.currentTarget;
+ if(target == null){
+ target = this;
+ }
if(target.checked){
var show = ''
}
@@ -150,6 +168,9 @@
YUE.on(YUQ('.line'),'click',function(e){
var tr = e.currentTarget;
+ if(tr == null){
+ tr = this;
+ }
injectInlineForm(tr);
});
diff --git a/rhodecode/templates/changeset/changeset_comment_block.html b/rhodecode/templates/changeset/changeset_comment_block.html
index 884a750a..06d510ef 100644
--- a/rhodecode/templates/changeset/changeset_comment_block.html
+++ b/rhodecode/templates/changeset/changeset_comment_block.html
@@ -1,2 +1,4 @@
+## this is a dummy html file for partial rendering on server and sending
+## generated output via ajax after comment submit
<%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
${comment.comment_block(c.co)}
diff --git a/rhodecode/templates/changeset/changeset_file_comment.html b/rhodecode/templates/changeset/changeset_file_comment.html
index cac1943f..283e0ce1 100644
--- a/rhodecode/templates/changeset/changeset_file_comment.html
+++ b/rhodecode/templates/changeset/changeset_file_comment.html
@@ -7,17 +7,24 @@
<div class="comment" id="comment-${co.comment_id}" line="${co.line_no}">
<div class="comment-wrapp">
<div class="meta">
- <span class="user">
- <img src="${h.gravatar_url(co.author.email, 20)}" />
+ <div style="float:left"> <img src="${h.gravatar_url(co.author.email, 20)}" /> </div>
+ <div class="user">
${co.author.username}
- </span>
- <span class="date">
+ </div>
+ <div class="date">
${h.age(co.modified_at)}
- </span>
+ </div>
+ %if co.status_change:
+ <div style="float:left" class="changeset-status-container">
+ <div style="float:left;padding:0px 2px 0px 2px"><span style="font-size: 18px;">&rsaquo;</span></div>
+ <div title="${_('Changeset status')}" class="changeset-status-lbl"> ${co.status_change[0].status_lbl}</div>
+ <div class="changeset-status-ico"><img src="${h.url(str('/images/icons/flag_status_%s.png' % co.status_change[0].status))}" /></div>
+ </div>
+ %endif
%if h.HasPermissionAny('hg.admin', 'repository.admin')() or co.author.user_id == c.rhodecode_user.user_id:
- <span class="buttons">
+ <div class="buttons">
<span onClick="deleteComment(${co.comment_id})" class="delete-comment ui-btn">${_('Delete')}</span>
- </span>
+ </div>
%endif
</div>
<div class="text">
@@ -28,18 +35,23 @@
</%def>
-<%def name="comment_inline_form(changeset)">
+<%def name="comment_inline_form()">
<div id='comment-inline-form-template' style="display:none">
- <div class="comment-inline-form">
+ <div class="comment-inline-form ac">
%if c.rhodecode_user.username != 'default':
<div class="overlay"><div class="overlay-text">${_('Submitting...')}</div></div>
- ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id),class_='inline-form')}
+ ${h.form('#', class_='inline-form')}
<div class="clearfix">
- <div class="comment-help">${_('Commenting on line')} {1}. ${_('Comments parsed using')}
- <a href="${h.url('rst_help')}">RST</a> ${_('syntax')} ${_('with')}
- <span style="color:#003367" class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span> ${_('support')}
+ <div class="comment-help">${_('Commenting on line {1}.')}
+ ${(_('Comments parsed using %s syntax with %s support.') % (
+ ('<a href="%s">RST</a>' % h.url('rst_help')),
+ ('<span style="color:#003367" class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
+ )
+ )|n
+ }
</div>
- <textarea id="text_{1}" name="text"></textarea>
+ <div class="mentions-container" id="mentions_container_{1}"></div>
+ <textarea id="text_{1}" name="text" class="yui-ac-input"></textarea>
</div>
<div class="comment-button">
<input type="hidden" name="f_path" value="{0}">
@@ -52,7 +64,7 @@
${h.form('')}
<div class="clearfix">
<div class="comment-help">
- ${'You need to be logged in to comment.'} <a href="${h.url('login_home',came_from=h.url.current())}">${_('Login now')}</a>
+ ${_('You need to be logged in to comment.')} <a href="${h.url('login_home',came_from=h.url.current())}">${_('Login now')}</a>
</div>
</div>
<div class="comment-button">
@@ -65,8 +77,9 @@
</%def>
-<%def name="inlines(changeset)">
- <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
+## generates inlines taken from c.comments var
+<%def name="inlines()">
+ <div class="comments-number">${ungettext("%d comment", "%d comments", len(c.comments)) % len(c.comments)} ${ungettext("(%d inline)", "(%d inline)", c.inline_cnt) % c.inline_cnt}</div>
%for path, lines in c.inline_comments:
% for line,comments in lines.iteritems():
<div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
@@ -79,11 +92,12 @@
</%def>
-<%def name="comments(changeset)">
-
+## generate inline comments and the main ones
+<%def name="generate_comments()">
<div class="comments">
<div id="inline-comments-container">
- ${inlines(changeset)}
+ ## generate inlines for this changeset
+ ${inlines()}
</div>
%for co in c.comments:
@@ -91,22 +105,59 @@
${comment_block(co)}
</div>
%endfor
+</div>
+</%def>
+
+## MAIN COMMENT FORM
+<%def name="comments(post_url, cur_status, close_btn=False)">
+
+<div class="comments">
%if c.rhodecode_user.username != 'default':
- <div class="comment-form">
- ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id))}
+ <div class="comment-form ac">
+ ${h.form(post_url)}
<strong>${_('Leave a comment')}</strong>
<div class="clearfix">
<div class="comment-help">
- ${_('Comments parsed using')} <a href="${h.url('rst_help')}">RST</a> ${_('syntax')}
- ${_('with')} <span style="color:#003367" class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span> ${_('support')}
+ ${(_('Comments parsed using %s syntax with %s support.') % (('<a href="%s">RST</a>' % h.url('rst_help')),
+ '<span style="color:#003367" class="tooltip" title="%s">@mention</span>' %
+ _('Use @username inside this text to send notification to this RhodeCode user')))|n}
+ | <label for="show_changeset_status_box" class="tooltip" title="${_('Check this to change current status of code-review for this changeset')}"> ${_('change status')}</label>
+ <input style="vertical-align: bottom;margin-bottom:-2px" id="show_changeset_status_box" type="checkbox" name="change_changeset_status" />
+ </div>
+ <div id="status_block_container" class="status-block" style="display:none">
+ %for status,lbl in c.changeset_statuses:
+ <div class="">
+ <img src="${h.url('/images/icons/flag_status_%s.png' % status)}" /> <input ${'checked="checked"' if status == cur_status else ''}" type="radio" name="changeset_status" value="${status}"> <label>${lbl}</label>
+ </div>
+ %endfor
</div>
- ${h.textarea('text')}
+ <div class="mentions-container" id="mentions_container"></div>
+ ${h.textarea('text')}
</div>
<div class="comment-button">
- ${h.submit('save', _('Comment'), class_='ui-button')}
+ ${h.submit('save', _('Comment'), class_="ui-btn large")}
+ %if close_btn:
+ ${h.submit('save_close', _('Comment and close'), class_='ui-btn blue large')}
+ %endif
</div>
${h.end_form()}
</div>
%endif
</div>
+<script>
+YUE.onDOMReady(function () {
+ MentionsAutoComplete('text', 'mentions_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
+
+ // changeset status box listener
+ YUE.on(YUD.get('show_changeset_status_box'),'change',function(e){
+ if(e.currentTarget.checked){
+ YUD.setStyle('status_block_container','display','');
+ }
+ else{
+ YUD.setStyle('status_block_container','display','none');
+ }
+ })
+
+});
+</script>
</%def>
diff --git a/rhodecode/templates/changeset/changeset_range.html b/rhodecode/templates/changeset/changeset_range.html
index 28fe45da..7265c1a9 100644
--- a/rhodecode/templates/changeset/changeset_range.html
+++ b/rhodecode/templates/changeset/changeset_range.html
@@ -2,11 +2,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
+ ${_('%s Changesets') % c.repo_name} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
@@ -35,12 +35,17 @@
<div id="changeset_compare_view_content">
<div class="container">
<table class="compare_view_commits noborder">
- %for cs in c.cs_ranges:
+ %for cnt,cs in enumerate(c.cs_ranges):
<tr>
- <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
+ <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
<td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
<td><div class="author">${h.person(cs.author)}</div></td>
<td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
+ <td>
+ %if c.statuses:
+ <div title="${h.tooltip(_('Changeset status'))}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cnt])}" /></div>
+ %endif
+ </td>
<td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
</tr>
%endfor
@@ -49,7 +54,7 @@
<div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
<div class="cs_files">
%for cs in c.cs_ranges:
- <div class="cur_cs">r${cs}</div>
+ <div class="cur_cs">${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
%for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
<div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID(cs.raw_id,filenode.path)))}</div>
%endfor
@@ -63,7 +68,12 @@
%for cs in c.cs_ranges:
##${comment.comment_inline_form(cs)}
## diff block
- <h3 style="border:none;padding-top:8px;">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</h3>
+ <h3 style="padding-top:8px;">
+ <a class="tooltip" title="${h.tooltip(cs.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</a>
+ <div class="gravatar">
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),20)}"/>
+ </div>
+ </h3>
${diff_block.diff_block(c.changes[cs.raw_id])}
##${comment.comments(cs)}
diff --git a/rhodecode/templates/changeset/diff_block.html b/rhodecode/templates/changeset/diff_block.html
index 077aa020..0d8fecf9 100644
--- a/rhodecode/templates/changeset/diff_block.html
+++ b/rhodecode/templates/changeset/diff_block.html
@@ -1,12 +1,12 @@
## -*- coding: utf-8 -*-
##usage:
## <%namespace name="diff_block" file="/changeset/diff_block.html"/>
-## ${diff_block.diff_block(changes)}
+## ${diff_block.diff_block(change)}
##
-<%def name="diff_block(changes)">
+<%def name="diff_block(change)">
-%for change,filenode,diff,cs1,cs2,stat in changes:
- %if change !='removed':
+%for op,filenode,diff,cs1,cs2,stat in change:
+ %if op !='removed':
<div id="${h.FID(filenode.changeset.raw_id,filenode.path)}_target" style="clear:both;margin-top:25px"></div>
<div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" class="diffblock margined comm">
<div class="code-header">
@@ -16,9 +16,9 @@
revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))}
</div>
<div class="diff-actions">
- <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}" title="${_('diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_go.png')}"/></a>
- <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
- <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
+ <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}" class="tooltip" title="${h.tooltip(_('diff'))}"><img class="icon" src="${h.url('/images/icons/page_white_go.png')}"/></a>
+ <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw')}" class="tooltip" title="${h.tooltip(_('raw diff'))}"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
+ <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download')}" class="tooltip" title="${h.tooltip(_('download diff'))}"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
${c.ignorews_url(request.GET, h.FID(filenode.changeset.raw_id,filenode.path))}
${c.context_url(request.GET, h.FID(filenode.changeset.raw_id,filenode.path))}
</div>
@@ -39,3 +39,23 @@
%endfor
</%def>
+
+<%def name="diff_block_simple(change)">
+
+ %for op,filenode_path,diff in change:
+ <div id="${h.FID('',filenode_path)}_target" style="clear:both;margin-top:25px"></div>
+ <div id="${h.FID('',filenode_path)}" class="diffblock margined comm">
+ <div class="code-header">
+ <div class="changeset_header">
+ <div class="changeset_file">
+ <a href="#">${h.safe_unicode(filenode_path)}</a>
+ </div>
+ </div>
+ </div>
+ <div class="code-body">
+ <div class="full_f_path" path="${h.safe_unicode(filenode_path)}"></div>
+ ${diff|n}
+ </div>
+ </div>
+ %endfor
+</%def>
diff --git a/rhodecode/templates/compare/compare_cs.html b/rhodecode/templates/compare/compare_cs.html
new file mode 100644
index 00000000..64bd01d0
--- /dev/null
+++ b/rhodecode/templates/compare/compare_cs.html
@@ -0,0 +1,27 @@
+## Changesets table !
+<div class="container">
+ <table class="compare_view_commits noborder">
+ %if not c.cs_ranges:
+ <tr><td>${_('No changesets')}</td></tr>
+ %else:
+ %for cnt, cs in enumerate(c.cs_ranges):
+ <tr>
+ <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
+ <td>
+ %if cs.raw_id in c.statuses:
+ <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
+ %endif
+ </td>
+ <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.target_repo,revision=cs.raw_id))}
+ %if c.as_form:
+ ${h.hidden('revisions',cs.raw_id)}
+ %endif
+ </td>
+ <td><div class="author">${h.person(cs.author)}</div></td>
+ <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
+ <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
+ </tr>
+ %endfor
+ %endif
+ </table>
+</div>
diff --git a/rhodecode/templates/compare/compare_diff.html b/rhodecode/templates/compare/compare_diff.html
new file mode 100644
index 00000000..df7a6675
--- /dev/null
+++ b/rhodecode/templates/compare/compare_diff.html
@@ -0,0 +1,77 @@
+## -*- coding: utf-8 -*-
+<%inherit file="/base/base.html"/>
+
+<%def name="title()">
+ ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
+</%def>
+
+<%def name="breadcrumbs_links()">
+ ${h.link_to(_(u'Home'),h.url('/'))}
+ &raquo;
+ ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
+ &raquo;
+ ${_('Compare')}
+</%def>
+
+<%def name="page_nav()">
+ ${self.menu('changelog')}
+</%def>
+
+<%def name="main()">
+<div class="box">
+ <!-- box / title -->
+ <div class="title">
+ ${self.breadcrumbs()}
+ </div>
+ <div class="table">
+ <div id="body" class="diffblock">
+ <div class="code-header cv">
+ <h3 class="code-header-title">${_('Compare View')}</h3>
+ <div>
+ ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
+ </div>
+ </div>
+ </div>
+ <div id="changeset_compare_view_content">
+ ##CS
+ <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Outgoing changesets')}</div>
+ <%include file="compare_cs.html" />
+
+ ## FILES
+ <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
+ <div class="cs_files">
+ %for fid, change, f, stat in c.files:
+ <div class="cs_${change}">
+ <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
+ <div class="changes">${h.fancy_file_stats(stat)}</div>
+ </div>
+ %endfor
+ </div>
+ </div>
+ </div>
+
+ ## diff block
+ <%namespace name="diff_block" file="/changeset/diff_block.html"/>
+ %for fid, change, f, stat in c.files:
+ ${diff_block.diff_block_simple([c.changes[fid]])}
+ %endfor
+
+ <script type="text/javascript">
+
+ YUE.onDOMReady(function(){
+
+ YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
+ var act = e.currentTarget.nextElementSibling;
+
+ if(YUD.hasClass(act,'active')){
+ YUD.removeClass(act,'active');
+ YUD.setStyle(act,'display','none');
+ }else{
+ YUD.addClass(act,'active');
+ YUD.setStyle(act,'display','');
+ }
+ });
+ })
+ </script>
+ </div>
+</%def>
diff --git a/rhodecode/templates/_data_table/_dt_elements.html b/rhodecode/templates/data_table/_dt_elements.html
index d3cddb8c..3f698367 100644
--- a/rhodecode/templates/_data_table/_dt_elements.html
+++ b/rhodecode/templates/data_table/_dt_elements.html
@@ -1,6 +1,12 @@
## DATA TABLE RE USABLE ELEMENTS
## usage:
-## <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
+## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
+
+<%def name="repo_actions(repo_name)">
+ ${h.form(h.url('repo', repo_name=repo_name),method='delete')}
+ ${h.submit('remove_%s' % repo_name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
+ ${h.end_form()}
+</%def>
<%def name="quick_menu(repo_name)">
<ul class="menu_items hidden">
@@ -57,10 +63,10 @@
%endif
##PRIVATE/PUBLIC
- %if private:
- <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
- %else:
- <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
+ %if private and c.visual.show_private_icon:
+ <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
+ %elif not private and c.visual.show_public_icon:
+ <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
%endif
##NAME
@@ -87,3 +93,18 @@
%endif
</div>
</%def>
+
+<%def name="user_gravatar(email, size=24)">
+ <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(email, size)}"/> </div>
+</%def>
+
+<%def name="user_actions(user_id, username)">
+ ${h.form(h.url('delete_user', id=user_id),method='delete')}
+ ${h.submit('remove_',_('delete'),id="remove_user_%s" % user_id,
+ class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
+ ${h.end_form()}
+</%def>
+
+<%def name="user_name(user_id, username)">
+ ${h.link_to(username,h.url('edit_user', id=user_id))}
+</%def>
diff --git a/rhodecode/templates/email_templates/changeset_comment.html b/rhodecode/templates/email_templates/changeset_comment.html
index 3bb99130..8ef1a949 100644
--- a/rhodecode/templates/email_templates/changeset_comment.html
+++ b/rhodecode/templates/email_templates/changeset_comment.html
@@ -4,3 +4,9 @@
<h4>${subject}</h4>
${body}
+
+% if status_change is not None:
+<div>
+ New status -> ${status_change}
+</div>
+% endif
diff --git a/rhodecode/templates/errors/error_document.html b/rhodecode/templates/errors/error_document.html
index d376d9dc..8d34b0ed 100755
--- a/rhodecode/templates/errors/error_document.html
+++ b/rhodecode/templates/errors/error_document.html
@@ -1,14 +1,16 @@
## -*- coding: utf-8 -*-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Error - ${c.error_message}</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <meta name="robots" content="index, nofollow"/>
+ <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" />
+
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
%if c.redirect_time:
<meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
%endif
- <link rel="icon" href="${h.url("/images/hgicon.png")}" type="image/png" />
- <meta name="robots" content="index, nofollow"/>
<!-- stylesheets -->
<link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen" />
diff --git a/rhodecode/templates/files/file_diff.html b/rhodecode/templates/files/file_diff.html
index 5324255d..5a0bb6ac 100644
--- a/rhodecode/templates/files/file_diff.html
+++ b/rhodecode/templates/files/file_diff.html
@@ -1,11 +1,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('File diff')} - ${c.rhodecode_name}
+ ${_('%s File diff') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/files/files.html b/rhodecode/templates/files/files.html
index 9221fe7e..7d9440fd 100644
--- a/rhodecode/templates/files/files.html
+++ b/rhodecode/templates/files/files.html
@@ -1,11 +1,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Files')} - ${c.rhodecode_name}
+ ${_('%s files') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
&raquo;
@@ -36,13 +36,107 @@
</div>
</div>
</div>
+
<script type="text/javascript">
-var YPJAX_TITLE = "${c.repo_name} ${_('Files')} - ${c.rhodecode_name}";
-var current_url = "${h.url.current()}";
-var node_list_url = '${h.url("files_home",repo_name=c.repo_name,revision=c.changeset.raw_id,f_path='__FPATH__')}';
-var url_base = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.path)}';
-var truncated_lbl = "${_('search truncated')}";
-var nomatch_lbl = "${_('no matching files')}";
-fileBrowserListeners(current_url, node_list_url, url_base, truncated_lbl, nomatch_lbl);
+var CACHE = {};
+var CACHE_EXPIRE = 60*1000; //cache for 60s
+//used to construct links from the search list
+var node_list_url = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
+//send the nodelist request to this url
+var url_base = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
+
+var ypjax_links = function(){
+ YUE.on(YUQ('.ypjax-link'), 'click',function(e){
+
+ //don't do ypjax on middle click
+ if(e.which == 2 || !History.enabled){
+ return true;
+ }
+
+ var el = e.currentTarget;
+ var url = el.href;
+
+ var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
+ _base_url = _base_url.replace('//','/')
+
+ //extract rev and the f_path from url.
+ parts = url.split(_base_url)
+ if(parts.length != 2){
+ return false;
+ }
+
+ var parts2 = parts[1].split('/');
+ var rev = parts2.shift(); // pop the first element which is the revision
+ var f_path = parts2.join('/');
+
+ var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
+
+ var _node_list_url = node_list_url.replace('__REV__',rev);
+ var _url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path);
+
+ // Change our States and save some data for handling events
+ var data = {url:url,title:title, url_base:_url_base,
+ node_list_url:_node_list_url};
+ History.pushState(data, title, url);
+
+ //now we're sure that we can do ypjax things
+ YUE.preventDefault(e)
+ return false;
+ });
+}
+
+var callbacks = function(State){
+ ypjax_links();
+ tooltip_activate();
+ fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
+ // Inform Google Analytics of the change
+ if ( typeof window.pageTracker !== 'undefined' ) {
+ window.pageTracker._trackPageview(State.url);
+ }
+}
+
+YUE.onDOMReady(function(){
+ ypjax_links();
+ var container = 'files_data';
+ //Bind to StateChange Event
+ History.Adapter.bind(window,'statechange',function(){
+ var State = History.getState();
+ cache_key = State.url;
+ //check if we have this request in cache maybe ?
+ var _cache_obj = CACHE[cache_key];
+ var _cur_time = new Date().getTime();
+ // get from cache if it's there and not yet expired !
+ if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
+ YUD.get(container).innerHTML=_cache_obj[1];
+ YUD.setStyle(container,'opacity','1.0');
+
+ //callbacks after ypjax call
+ callbacks(State);
+ }
+ else{
+ ypjax(State.url,container,function(o){
+ //callbacks after ypjax call
+ callbacks(State);
+ if (o !== undefined){
+ //store our request in cache
+ var _expire_on = new Date().getTime()+CACHE_EXPIRE;
+ CACHE[cache_key] = [_expire_on, o.responseText];
+ }
+ });
+ }
+ });
+
+ // init the search filter
+ var _State = {
+ url: "${h.url.current()}",
+ data: {
+ node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}"),
+ url_base: url_base.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}")
+ }
+ }
+ fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
+});
+
</script>
+
</%def>
diff --git a/rhodecode/templates/files/files_add.html b/rhodecode/templates/files/files_add.html
index 30f601fa..ed78edc0 100644
--- a/rhodecode/templates/files/files_add.html
+++ b/rhodecode/templates/files/files_add.html
@@ -1,7 +1,7 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
+ ${_('%s Edit file') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="js_extra()">
@@ -12,7 +12,7 @@
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/files/files_browser.html b/rhodecode/templates/files/files_browser.html
index cdaacbb8..69009210 100644
--- a/rhodecode/templates/files/files_browser.html
+++ b/rhodecode/templates/files/files_browser.html
@@ -11,9 +11,9 @@
${h.form(h.url.current())}
<div class="info_box">
<span class="rev">${_('view')}@rev</span>
- <a class="ui-btn" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
+ <a class="ui-btn ypjax-link" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
${h.text('at_rev',value=c.changeset.revision,size=5)}
- <a class="ui-btn" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
+ <a class="ui-btn ypjax-link" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
## ${h.submit('view',_('view'),class_="ui-btn")}
</div>
${h.end_form()}
@@ -88,14 +88,14 @@
</td>
<td>
%if node.is_file():
- <div class="tooltip" title="${node.last_changeset.message}">
+ <div class="tooltip" title="${h.tooltip(node.last_changeset.message)}">
<pre>${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</pre>
</div>
%endif
</td>
<td>
%if node.is_file():
- <span class="tooltip" title="${node.last_changeset.date}">
+ <span class="tooltip" title="${h.tooltip(h.fmt_date(node.last_changeset.date))}">
${h.age(node.last_changeset.date)}</span>
%endif
</td>
diff --git a/rhodecode/templates/files/files_edit.html b/rhodecode/templates/files/files_edit.html
index b11bf555..b2f0d257 100644
--- a/rhodecode/templates/files/files_edit.html
+++ b/rhodecode/templates/files/files_edit.html
@@ -1,7 +1,7 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
+ ${_('%s Edit file') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="js_extra()">
@@ -12,7 +12,7 @@
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/files/files_source.html b/rhodecode/templates/files/files_source.html
index 6f6a3271..aaf15e3b 100644
--- a/rhodecode/templates/files/files_source.html
+++ b/rhodecode/templates/files/files_source.html
@@ -1,22 +1,34 @@
<dl>
- <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
+ <dt class="file_history">${_('History')}</dt>
<dd>
- <div>
- ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
- ${h.hidden('diff2',c.file.changeset.raw_id)}
- ${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
- ${h.submit('diff','diff to revision',class_="ui-btn")}
- ${h.submit('show_rev','show at revision',class_="ui-btn")}
- ${h.end_form()}
- </div>
+ <div>
+ <div style="float:left">
+ ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
+ ${h.hidden('diff2',c.file.changeset.raw_id)}
+ ${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
+ ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
+ ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
+ ${h.end_form()}
+ </div>
+ <div class="file_author">
+ <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
+ %for email, user in c.authors:
+ <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
+ <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
+ </div>
+ %endfor
+ </div>
+ </div>
+ <div style="clear:both"></div>
</dd>
+
</dl>
<div id="body" class="codeblock">
<div class="code-header">
<div class="stats">
<div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
- <div class="left item"><pre class="tooltip" title="${c.file.changeset.date}">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
+ <div class="left item"><pre class="tooltip" title="${h.tooltip(h.fmt_date(c.file.changeset.date))}">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
<div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
<div class="left item last"><pre>${c.file.mimetype}</pre></div>
<div class="buttons">
@@ -36,7 +48,7 @@
</div>
<div class="author">
<div class="gravatar">
- <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/>
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file.changeset.author),16)}"/>
</div>
<div title="${c.file.changeset.author}" class="user">${h.person(c.file.changeset.author)}</div>
</div>
diff --git a/rhodecode/templates/files/files_ypjax.html b/rhodecode/templates/files/files_ypjax.html
index 85af8e4f..abf274df 100644
--- a/rhodecode/templates/files/files_ypjax.html
+++ b/rhodecode/templates/files/files_ypjax.html
@@ -9,7 +9,7 @@
<%include file='files_browser.html'/>
%else:
<%include file='files_source.html'/>
- %endif
+ %endif
%else:
<h2>
<a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
diff --git a/rhodecode/templates/followers/followers.html b/rhodecode/templates/followers/followers.html
index bb9ff837..d7374730 100644
--- a/rhodecode/templates/followers/followers.html
+++ b/rhodecode/templates/followers/followers.html
@@ -2,11 +2,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Followers')} - ${c.rhodecode_name}
+ ${_('%s Followers') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/followers/followers_data.html b/rhodecode/templates/followers/followers_data.html
index a1a6cdee..ae658410 100644
--- a/rhodecode/templates/followers/followers_data.html
+++ b/rhodecode/templates/followers/followers_data.html
@@ -9,8 +9,8 @@
<span style="font-size: 20px"> <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname})</span>
</div>
<div style="clear:both;padding-top: 10px"></div>
- <div class="follower_date">${_('Started following')} -
- <span class="tooltip" title="${f.follows_from}"> ${h.age(f.follows_from)}</span></div>
+ <div class="follower_date">${_('Started following -')}
+ <span class="tooltip" title="${h.tooltip(f.follows_from)}"> ${h.age(f.follows_from)}</span></div>
<div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
</div>
% endfor
diff --git a/rhodecode/templates/forks/fork.html b/rhodecode/templates/forks/fork.html
index 7affa1ba..8733dbc9 100644
--- a/rhodecode/templates/forks/fork.html
+++ b/rhodecode/templates/forks/fork.html
@@ -2,11 +2,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Fork')} - ${c.rhodecode_name}
+ ${_('%s Fork') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
&raquo;
@@ -36,12 +36,22 @@
${h.hidden('fork_parent_id',c.repo_info.repo_id)}
</div>
</div>
+ <div class="field">
+ <div class="label">
+ <label for="landing_rev">${_('Landing revision')}:</label>
+ </div>
+ <div class="input">
+ ${h.select('landing_rev','',c.landing_revs,class_="medium")}
+ <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
+ </div>
+ </div>
<div class="field">
<div class="label">
<label for="repo_group">${_('Repository group')}:</label>
</div>
<div class="input">
${h.select('repo_group','',c.repo_groups,class_="medium")}
+ <span class="help-block">${_('Optionaly select a group to put this repository into.')}</span>
</div>
</div>
<div class="field">
@@ -50,6 +60,7 @@
</div>
<div class="textarea text-area editor">
${h.textarea('description',cols=23,rows=5)}
+ <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
</div>
</div>
<div class="field">
@@ -58,6 +69,7 @@
</div>
<div class="checkboxes">
${h.checkbox('private',value="True")}
+ <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
</div>
</div>
<div class="field">
@@ -66,6 +78,7 @@
</div>
<div class="checkboxes">
${h.checkbox('copy_permissions',value="True", checked="checked")}
+ <span class="help-block">${_('Copy permissions from forked repository')}</span>
</div>
</div>
<div class="field">
@@ -74,10 +87,11 @@
</div>
<div class="checkboxes">
${h.checkbox('update_after_clone',value="True")}
+ <span class="help-block">${_('Checkout source after making a clone')}</span>
</div>
</div>
<div class="buttons">
- ${h.submit('',_('fork this repository'),class_="ui-button")}
+ ${h.submit('',_('fork this repository'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/forks/forks.html b/rhodecode/templates/forks/forks.html
index fb8f85f4..43a11554 100644
--- a/rhodecode/templates/forks/forks.html
+++ b/rhodecode/templates/forks/forks.html
@@ -2,11 +2,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Forks')} - ${c.rhodecode_name}
+ ${_('%s Forks') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/forks/forks_data.html b/rhodecode/templates/forks/forks_data.html
index a89a9ae0..1b3e8831 100644
--- a/rhodecode/templates/forks/forks_data.html
+++ b/rhodecode/templates/forks/forks_data.html
@@ -15,7 +15,11 @@
</div>
<div style="clear:both;padding-top: 10px"></div>
<div class="follower_date">${_('forked')} -
- <span class="tooltip" title="${f.created_on}"> ${h.age(f.created_on)}</span></div>
+ <span class="tooltip" title="${h.tooltip(h.fmt_date(f.created_on))}"> ${h.age(f.created_on)}</span>
+ <a title="${_('compare fork with %s' % c.repo_name)}"
+ href="${h.url('compare_url',repo_name=f.repo_name,org_ref_type='branch',org_ref='default',other_ref_type='branch',other_ref='default', repo=c.repo_name)}"
+ class="ui-btn small">${_('Compare fork')}</a>
+ </div>
<div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
</div>
% endfor
diff --git a/rhodecode/templates/index_base.html b/rhodecode/templates/index_base.html
index 054c86b7..f3e25aa1 100644
--- a/rhodecode/templates/index_base.html
+++ b/rhodecode/templates/index_base.html
@@ -41,7 +41,11 @@
${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
</div>
</td>
- <td>${gr.group_description}</td>
+ %if c.visual.stylify_metatags:
+ <td>${h.desc_stylize(gr.group_description)}</td>
+ %else:
+ <td>${gr.group_description}</td>
+ %endif
## this is commented out since for multi nested repos can be HEAVY!
## in number of executed queries during traversing uncomment at will
##<td><b>${gr.repositories_recursive_count}</b></td>
@@ -57,7 +61,7 @@
</div>
<div id='repos_list_wrap' class="yui-skin-sam">
<%cnt=0%>
- <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
+ <%namespace name="dt" file="/data_table/_dt_elements.html"/>
<table id="repos_list">
<thead>
@@ -85,11 +89,15 @@
</td>
##DESCRIPTION
<td><span class="tooltip" title="${h.tooltip(repo['description'])}">
+ %if c.visual.stylify_metatags:
+ ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
+ %else:
${h.truncate(repo['description'],60)}</span>
+ %endif
</td>
##LAST CHANGE DATE
<td>
- <span class="tooltip" title="${repo['last_change']}">${h.age(repo['last_change'])}</span>
+ <span class="tooltip" date="${repo['last_change']}" title="${h.tooltip(h.fmt_date(repo['last_change']))}">${h.age(repo['last_change'])}</span>
</td>
##LAST REVISION
<td>
@@ -119,11 +127,13 @@
</div>
</div>
<script>
- YUD.get('repo_count').innerHTML = ${cnt+1};
+ YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
var func = function(node){
return node.parentNode.parentNode.parentNode.parentNode;
}
+ var sort_by = "name";
+ var sort_dir = "asc";
// groups table sorting
var myColumnDefs = [
@@ -184,7 +194,7 @@
var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
{
- sortedBy:{key:"name",dir:"asc"},
+ sortedBy:{key:sort_by,dir:sort_dir},
MSG_SORTASC:"${_('Click to sort ascending')}",
MSG_SORTDESC:"${_('Click to sort descending')}",
MSG_EMPTY:"${_('No records found.')}",
diff --git a/rhodecode/templates/journal/journal.html b/rhodecode/templates/journal/journal.html
index af979d35..a52b31b7 100644
--- a/rhodecode/templates/journal/journal.html
+++ b/rhodecode/templates/journal/journal.html
@@ -9,6 +9,10 @@
<%def name="page_nav()">
${self.menu('home')}
</%def>
+<%def name="head_extra()">
+<link href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
+<link href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
+</%def>
<%def name="main()">
<div class="box box-left">
@@ -17,8 +21,13 @@
<h5>${_('Journal')}</h5>
<ul class="links">
<li>
- <span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
- </a></span>
+ <span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/></a></span>
+ </li>
+ <li>
+ <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
+ </li>
+ <li>
+ <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
</li>
</ul>
</div>
@@ -53,7 +62,7 @@
<th class="left">${_('Action')}</th>
</thead>
<tbody>
- <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
+ <%namespace name="dt" file="/data_table/_dt_elements.html"/>
%for repo in c.user_repos:
<tr>
##QUICK MENU
@@ -72,7 +81,7 @@
<td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
<td>
${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
- ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
+ ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
${h.end_form()}
</td>
</tr>
@@ -119,9 +128,9 @@
<img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
%endif
- %if entry.follows_repository.private:
+ %if entry.follows_repository.private and c.visual.show_private_icon:
<img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
- %else:
+ %elif not entry.follows_repository.private and c.visual.show_public_icon:
<img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
%endif
<span class="watched_repo">
diff --git a/rhodecode/templates/journal/journal_data.html b/rhodecode/templates/journal/journal_data.html
index 637a8e99..8dd528ee 100644
--- a/rhodecode/templates/journal/journal_data.html
+++ b/rhodecode/templates/journal/journal_data.html
@@ -11,8 +11,8 @@
<div class="journal_user">${user.name} ${user.lastname}</div>
<div class="journal_action_container">
% for entry in entries:
- <div class="journal_icon"> ${h.action_parser_icon(entry)}</div>
- <div class="journal_action">${h.action_parser(entry)[0]}</div>
+ <div class="journal_icon"> ${h.action_parser(entry)[2]()}</div>
+ <div class="journal_action">${h.action_parser(entry)[0]()}</div>
<div class="journal_repo">
<span class="journal_repo_name">
%if entry.repository is not None:
@@ -24,7 +24,7 @@
</span>
</div>
<div class="journal_action_params">${h.literal(h.action_parser(entry)[1]())}</div>
- <div class="date"><span class="tooltip" title="${entry.action_date}">${h.age(entry.action_date)}</span></div>
+ <div class="date"><span class="tooltip" title="${h.tooltip(h.fmt_date(entry.action_date))}">${h.age(entry.action_date)}</span></div>
%endfor
</div>
</div>
diff --git a/rhodecode/templates/journal/public_journal.html b/rhodecode/templates/journal/public_journal.html
index 71466adc..a4228a35 100644
--- a/rhodecode/templates/journal/public_journal.html
+++ b/rhodecode/templates/journal/public_journal.html
@@ -9,33 +9,35 @@
<%def name="page_nav()">
${self.menu('home')}
</%def>
+<%def name="head_extra()">
+<link href="${h.url('public_journal_atom')}" rel="alternate" title="${_('ATOM public journal feed')}" type="application/atom+xml" />
+<link href="${h.url('public_journal_rss')}" rel="alternate" title="${_('RSS public journal feed')}" type="application/rss+xml" />
+</%def>
<%def name="main()">
- <div class="box">
- <!-- box / title -->
- <div class="title">
- <h5>${_('Public Journal')}</h5>
- <ul class="links">
- <li>
- <span>${h.link_to(_('RSS'),h.url('public_journal_rss'),class_='rss_icon')}</span>
- </li>
- <li>
- <span>${h.link_to(_('Atom'),h.url('public_journal_atom'),class_='atom_icon')}</span>
- </li>
-
- </ul>
-
- </div>
- <script type="text/javascript">
- function show_more_event(){
- YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
- var el = e.target;
- YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
- YUD.setStyle(el.parentNode,'display','none');
- });
- }
- </script>
- <div id="journal">${c.journal_data}</div>
- </div>
+<div class="box">
+ <!-- box / title -->
+ <div class="title">
+ <h5>${_('Public Journal')}</h5>
+ <ul class="links">
+ <li>
+ <span><a href="${h.url('public_journal_rss')}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
+ </li>
+ <li>
+ <span><a href="${h.url('public_journal_atom')}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
+ </li>
+ </ul>
+ </div>
+ <script type="text/javascript">
+ function show_more_event(){
+ YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
+ var el = e.target;
+ YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
+ YUD.setStyle(el.parentNode,'display','none');
+ });
+ }
+ </script>
+ <div id="journal">${c.journal_data}</div>
+</div>
</%def>
diff --git a/rhodecode/templates/login.html b/rhodecode/templates/login.html
index a2c1142e..ef5154df 100644
--- a/rhodecode/templates/login.html
+++ b/rhodecode/templates/login.html
@@ -51,7 +51,7 @@
</div>
</div>
<div class="buttons">
- ${h.submit('sign_in',_('Sign In'),class_="ui-button")}
+ ${h.submit('sign_in',_('Sign In'),class_="ui-btn large")}
</div>
</div>
<!-- end fields -->
diff --git a/rhodecode/templates/password_reset.html b/rhodecode/templates/password_reset.html
index 5480f719..8ae90c9a 100644
--- a/rhodecode/templates/password_reset.html
+++ b/rhodecode/templates/password_reset.html
@@ -27,7 +27,7 @@
<div class="buttons">
<div class="nohighlight">
- ${h.submit('send',_('Reset my password'),class_="ui-button")}
+ ${h.submit('send',_('Reset my password'),class_="ui-btn large")}
<div class="activation_msg">${_('Password reset link will be send to matching email address')}</div>
</div>
</div>
diff --git a/rhodecode/templates/pullrequests/pullrequest.html b/rhodecode/templates/pullrequests/pullrequest.html
new file mode 100644
index 00000000..2517848b
--- /dev/null
+++ b/rhodecode/templates/pullrequests/pullrequest.html
@@ -0,0 +1,189 @@
+<%inherit file="/base/base.html"/>
+
+<%def name="title()">
+ ${c.repo_name} ${_('New pull request')}
+</%def>
+
+<%def name="breadcrumbs_links()">
+ ${h.link_to(_(u'Home'),h.url('/'))}
+ &raquo;
+ ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
+ &raquo;
+ ${_('New pull request')}
+</%def>
+
+<%def name="main()">
+
+<div class="box">
+ <!-- box / title -->
+ <div class="title">
+ ${self.breadcrumbs()}
+ </div>
+ ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
+ <div style="float:left;padding:0px 30px 30px 30px">
+ <div style="padding:0px 5px 5px 5px">
+ <span>
+ <a id="refresh" href="#">
+ <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
+ ${_('refresh overview')}
+ </a>
+ </span>
+ </div>
+ ##ORG
+ <div style="float:left">
+ <div class="fork_user">
+ <div class="gravatar">
+ <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
+ </div>
+ <span style="font-size: 20px">
+ ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
+ </span>
+ <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
+ </div>
+ <div style="clear:both;padding-top: 10px"></div>
+ </div>
+ <div style="float:left;font-size:24px;padding:0px 20px">
+ <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
+ </div>
+
+ ##OTHER, most Probably the PARENT OF THIS FORK
+ <div style="float:left">
+ <div class="fork_user">
+ <div class="gravatar">
+ <img id="other_repo_gravatar" alt="gravatar" src=""/>
+ </div>
+ <span style="font-size: 20px">
+ ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.default_revs,class_='refs')}
+ </span>
+ <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
+ </div>
+ <div style="clear:both;padding-top: 10px"></div>
+ </div>
+ <div style="clear:both;padding-top: 10px"></div>
+ ## overview pulled by ajax
+ <div style="float:left" id="pull_request_overview"></div>
+ <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
+ <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
+ </div>
+ </div>
+ <div style="float:left; border-left:1px dashed #eee">
+ <h4>${_('Pull request reviewers')}</h4>
+ <div id="reviewers" style="padding:0px 0px 0px 15px">
+ ## members goes here !
+ <div class="group_members_wrap">
+ <ul id="review_members" class="group_members">
+ %for member in c.review_members:
+ <li id="reviewer_${member.user_id}">
+ <div class="reviewers_member">
+ <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
+ <div style="float:left">${member.full_name} (${_('owner')})</div>
+ <input type="hidden" value="${member.user_id}" name="review_members" />
+ <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
+ </div>
+ </li>
+ %endfor
+ </ul>
+ </div>
+
+ <div class='ac'>
+ <div class="reviewer_ac">
+ ${h.text('user', class_='yui-ac-input')}
+ <span class="help-block">${_('Add reviewer to this pull request.')}</span>
+ <div id="reviewers_container"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <h3>${_('Create new pull request')}</h3>
+
+ <div class="form">
+ <!-- fields -->
+
+ <div class="fields">
+
+ <div class="field">
+ <div class="label">
+ <label for="pullrequest_title">${_('Title')}:</label>
+ </div>
+ <div class="input">
+ ${h.text('pullrequest_title',size=30)}
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="label label-textarea">
+ <label for="pullrequest_desc">${_('description')}:</label>
+ </div>
+ <div class="textarea text-area editor">
+ ${h.textarea('pullrequest_desc',size=30)}
+ </div>
+ </div>
+
+ <div class="buttons">
+ ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
+ </div>
+ </div>
+ </div>
+ ${h.end_form()}
+
+</div>
+
+<script type="text/javascript">
+ var _USERS_AC_DATA = ${c.users_array|n};
+ var _GROUPS_AC_DATA = ${c.users_groups_array|n};
+ PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
+
+ var other_repos_info = ${c.other_repos_info|n};
+ var loadPreview = function(){
+ YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
+ var url = "${h.url('compare_url',
+ repo_name='org_repo',
+ org_ref_type='org_ref_type', org_ref='org_ref',
+ other_ref_type='other_ref_type', other_ref='other_ref',
+ repo='other_repo',
+ as_form=True)}";
+
+ var select_refs = YUQ('#pull_request_form select.refs')
+
+ for(var i=0;i<select_refs.length;i++){
+ var select_ref = select_refs[i];
+ var select_ref_data = select_ref.value.split(':');
+ var key = null;
+ var val = null;
+ if(select_ref_data.length>1){
+ key = select_ref.name+"_type";
+ val = select_ref_data[0];
+ url = url.replace(key,val);
+
+ key = select_ref.name;
+ val = select_ref_data[1];
+ url = url.replace(key,val);
+
+ }else{
+ key = select_ref.name;
+ val = select_ref.value;
+ url = url.replace(key,val);
+ }
+ }
+
+ ypjax(url,'pull_request_overview', function(data){
+ var sel_box = YUQ('#pull_request_form #other_repo')[0];
+ var repo_name = sel_box.options[sel_box.selectedIndex].value;
+ YUD.get('pull_request_overview_url').href = url;
+ YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
+ YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
+ YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
+ YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
+ })
+ }
+ YUE.on('refresh','click',function(e){
+ loadPreview()
+ })
+
+ //lazy load overview after 0.5s
+ setTimeout(loadPreview, 500)
+
+</script>
+
+</%def>
diff --git a/rhodecode/templates/pullrequests/pullrequest_show.html b/rhodecode/templates/pullrequests/pullrequest_show.html
new file mode 100644
index 00000000..039d2bfc
--- /dev/null
+++ b/rhodecode/templates/pullrequests/pullrequest_show.html
@@ -0,0 +1,195 @@
+<%inherit file="/base/base.html"/>
+
+<%def name="title()">
+ ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
+</%def>
+
+<%def name="breadcrumbs_links()">
+ ${h.link_to(_(u'Home'),h.url('/'))}
+ &raquo;
+ ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
+ &raquo;
+ ${_('Pull request #%s') % c.pull_request.pull_request_id}
+</%def>
+
+<%def name="main()">
+
+<div class="box">
+ <!-- box / title -->
+ <div class="title">
+ ${self.breadcrumbs()}
+ </div>
+ %if c.pull_request.is_closed():
+ <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))}</div>
+ %endif
+ <h3>${_('Title')}: ${c.pull_request.title}</h3>
+
+ <div class="form">
+ <div id="summary" class="fields">
+ <div class="field">
+ <div class="label-summary">
+ <label>${_('Status')}:</label>
+ </div>
+ <div class="input">
+ <div class="changeset-status-container" style="float:none;clear:both">
+ %if c.current_changeset_status:
+ <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
+ <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
+ %endif
+ </div>
+ </div>
+ </div>
+ <div class="field">
+ <div class="label-summary">
+ <label>${_('Still not reviewed by')}:</label>
+ </div>
+ <div class="input">
+ <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
+ <div style="padding:4px 4px 10px 20px">
+ <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
+ </div>
+
+ <div style="min-height:160px">
+ ##DIFF
+ <div class="table" style="float:left;clear:none">
+ <div id="body" class="diffblock">
+ <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
+ </div>
+ <div id="changeset_compare_view_content">
+ ##CS
+ <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
+ <%include file="/compare/compare_cs.html" />
+
+ ## FILES
+ <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
+ <div class="cs_files">
+ %for fid, change, f, stat in c.files:
+ <div class="cs_${change}">
+ <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
+ <div class="changes">${h.fancy_file_stats(stat)}</div>
+ </div>
+ %endfor
+ </div>
+ </div>
+ </div>
+ ## REVIEWERS
+ <div style="float:left; border-left:1px dashed #eee">
+ <h4>${_('Pull request reviewers')}</h4>
+ <div id="reviewers" style="padding:0px 0px 0px 15px">
+ ## members goes here !
+ <div class="group_members_wrap">
+ <ul id="review_members" class="group_members">
+ %for member,status in c.pull_request_reviewers:
+ <li id="reviewer_${member.user_id}">
+ <div class="reviewers_member">
+ <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
+ <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
+ </div>
+ <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
+ <div style="float:left">${member.full_name} (${_('owner')})</div>
+ <input type="hidden" value="${member.user_id}" name="review_members" />
+ %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id):
+ <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
+ %endif
+ </div>
+ </li>
+ %endfor
+ </ul>
+ </div>
+ %if not c.pull_request.is_closed():
+ <div class='ac'>
+ %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
+ <div class="reviewer_ac">
+ ${h.text('user', class_='yui-ac-input')}
+ <span class="help-block">${_('Add reviewer to this pull request.')}</span>
+ <div id="reviewers_container"></div>
+ </div>
+ <div style="padding:0px 10px">
+ <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
+ </div>
+ %endif
+ </div>
+ %endif
+ </div>
+ </div>
+ </div>
+ <script>
+ var _USERS_AC_DATA = ${c.users_array|n};
+ var _GROUPS_AC_DATA = ${c.users_groups_array|n};
+ AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
+ AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
+ AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
+ </script>
+
+ ## diff block
+ <%namespace name="diff_block" file="/changeset/diff_block.html"/>
+ %for fid, change, f, stat in c.files:
+ ${diff_block.diff_block_simple([c.changes[fid]])}
+ %endfor
+
+ ## template for inline comment form
+ <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
+ ${comment.comment_inline_form()}
+
+ ## render comments and inlines
+ ${comment.generate_comments()}
+
+ % if not c.pull_request.is_closed():
+ ## main comment form and it status
+ ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
+ pull_request_id=c.pull_request.pull_request_id),
+ c.current_changeset_status,
+ close_btn=True)}
+ %endif
+
+ <script type="text/javascript">
+ YUE.onDOMReady(function(){
+ PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
+
+ YUE.on(YUQ('.show-inline-comments'),'change',function(e){
+ var show = 'none';
+ var target = e.currentTarget;
+ if(target.checked){
+ var show = ''
+ }
+ var boxid = YUD.getAttribute(target,'id_for');
+ var comments = YUQ('#{0} .inline-comments'.format(boxid));
+ for(c in comments){
+ YUD.setStyle(comments[c],'display',show);
+ }
+ var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
+ for(c in btns){
+ YUD.setStyle(btns[c],'display',show);
+ }
+ })
+
+ YUE.on(YUQ('.line'),'click',function(e){
+ var tr = e.currentTarget;
+ injectInlineForm(tr);
+ });
+
+ // inject comments into they proper positions
+ var file_comments = YUQ('.inline-comment-placeholder');
+ renderInlineComments(file_comments);
+
+ YUE.on(YUD.get('update_pull_request'),'click',function(e){
+
+ var reviewers_ids = [];
+ var ids = YUQ('#review_members input');
+ for(var i=0; i<ids.length;i++){
+ var id = ids[i].value
+ reviewers_ids.push(id);
+ }
+ updateReviewers(reviewers_ids);
+ })
+ })
+ </script>
+
+</div>
+
+</%def>
diff --git a/rhodecode/templates/pullrequests/pullrequest_show_all.html b/rhodecode/templates/pullrequests/pullrequest_show_all.html
new file mode 100644
index 00000000..0b163e72
--- /dev/null
+++ b/rhodecode/templates/pullrequests/pullrequest_show_all.html
@@ -0,0 +1,42 @@
+<%inherit file="/base/base.html"/>
+
+<%def name="title()">
+ ${c.repo_name} ${_('all pull requests')}
+</%def>
+
+<%def name="breadcrumbs_links()">
+ ${h.link_to(_(u'Home'),h.url('/'))}
+ &raquo;
+ ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
+ &raquo;
+ ${_('All pull requests')}
+</%def>
+
+<%def name="main()">
+
+<div class="box">
+ <!-- box / title -->
+ <div class="title">
+ ${self.breadcrumbs()}
+ </div>
+
+ %for pr in c.pull_requests:
+ <div>
+ <h4>
+ %if pr.is_closed():
+ <img src="${h.url('/images/icons/tick.png')}" alt="${_('Closed')}" />
+ %endif
+ <a href="${h.url('pullrequest_show',repo_name=c.repo_name,pull_request_id=pr.pull_request_id)}">
+ ${_('Pull request #%s opened by %s on %s') % (pr.pull_request_id, pr.author.full_name, h.fmt_date(pr.created_on))}
+ </a>
+ </h4>
+ <h5 style="border:0px;padding-bottom:0px">${_('Title')}: ${pr.title}</h5>
+ <div style="padding:0px 24px">${pr.description}</div>
+ </div>
+ %endfor
+
+</div>
+
+<script type="text/javascript"></script>
+
+</%def>
diff --git a/rhodecode/templates/register.html b/rhodecode/templates/register.html
index 43ec79c0..ba12777c 100644
--- a/rhodecode/templates/register.html
+++ b/rhodecode/templates/register.html
@@ -44,10 +44,10 @@
<div class="field">
<div class="label">
- <label for="name">${_('First Name')}:</label>
+ <label for="firstname">${_('First Name')}:</label>
</div>
<div class="input">
- ${h.text('name',class_="medium")}
+ ${h.text('firstname',class_="medium")}
</div>
</div>
@@ -71,7 +71,7 @@
<div class="buttons">
<div class="nohighlight">
- ${h.submit('sign_up',_('Sign Up'),class_="ui-button")}
+ ${h.submit('sign_up',_('Sign Up'),class_="ui-btn large")}
%if c.auto_active:
<div class="activation_msg">${_('Your account will be activated right after registration')}</div>
%else:
diff --git a/rhodecode/templates/repo_switcher_list.html b/rhodecode/templates/repo_switcher_list.html
index 5ae92d81..4d7dc458 100644
--- a/rhodecode/templates/repo_switcher_list.html
+++ b/rhodecode/templates/repo_switcher_list.html
@@ -1,17 +1,17 @@
## -*- coding: utf-8 -*-
<li class="qfilter_rs">
- <input type="text" style="border:0" value="quick filter..." name="filter" size="15" id="q_filter_rs" />
+ <input type="text" style="border:0;width:100%" value="${_('quick filter...')}" name="filter" id="q_filter_rs" />
</li>
%for repo in c.repos_list:
- %if repo['dbrepo']['private']:
+ %if repo['dbrepo']['private'] and c.visual.show_private_icon:
<li>
<img src="${h.url('/images/icons/lock.png')}" alt="${_('Private repository')}" class="repo_switcher_type"/>
${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
</li>
- %else:
+ %elif not repo['dbrepo']['private'] and c.visual.show_public_icon:
<li>
<img src="${h.url('/images/icons/lock_open.png')}" alt="${_('Public repository')}" class="repo_switcher_type" />
${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
diff --git a/rhodecode/templates/search/search.html b/rhodecode/templates/search/search.html
index 3d52b347..f4c81c83 100644
--- a/rhodecode/templates/search/search.html
+++ b/rhodecode/templates/search/search.html
@@ -1,13 +1,19 @@
## -*- coding: utf-8 -*-
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${_('Search')}
- ${'"%s"' % c.cur_query if c.cur_query else None}
+ %if c.cur_query:
%if c.repo_name:
- ${_('in repository: ') + c.repo_name}
+ ${_('Search "%s" in repository: %s') % (c.cur_query, c.repo_name)}
%else:
- ${_('in all repositories')}
+ ${_('Search "%s" in all repositories') % c.cur_query}
%endif
+ %else:
+ %if c.repo_name:
+ ${_('Search in repository: %s') % c.repo_name}
+ %else:
+ ${_('Search in all repositories')}
+ %endif
+ %endif
- ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs()">
@@ -21,11 +27,11 @@
<div class="box">
<!-- box / title -->
<div class="title">
- <h5>${_('Search')}
+ <h5>
%if c.repo_name:
- ${_('in repository: ') + c.repo_name}
+ ${_('Search in repository: %s') % c.repo_name}
%else:
- ${_('in all repositories')}
+ ${_('Search in all repositories')}
%endif
</h5>
</div>
@@ -55,7 +61,7 @@
</div>
<div class="select">
${h.select('type',c.cur_type,[('content',_('File contents')),
- ##('commit',_('Commit messages')),
+ ('commit',_('Commit messages')),
('path',_('File names')),
##('repository',_('Repository names')),
])}
@@ -65,16 +71,17 @@
</div>
</div>
${h.end_form()}
-
- %if c.cur_search == 'content':
+ <div class="search">
+ %if c.cur_type == 'content':
<%include file='search_content.html'/>
- %elif c.cur_search == 'path':
+ %elif c.cur_type == 'path':
<%include file='search_path.html'/>
- %elif c.cur_search == 'commit':
+ %elif c.cur_type == 'commit':
<%include file='search_commit.html'/>
- %elif c.cur_search == 'repository':
+ %elif c.cur_type == 'repository':
<%include file='search_repository.html'/>
%endif
+ </div>
</div>
</%def>
diff --git a/rhodecode/templates/search/search_commit.html b/rhodecode/templates/search/search_commit.html
index e69de29b..1820281e 100644
--- a/rhodecode/templates/search/search_commit.html
+++ b/rhodecode/templates/search/search_commit.html
@@ -0,0 +1,45 @@
+##commit highligthing
+
+%for cnt,sr in enumerate(c.formated_results):
+ %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
+ <div class="table">
+ <div id="body${cnt}" class="codeblock">
+ <div class="code-header">
+ <div class="search-path">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['raw_id'])),
+ h.url('changeset_home',repo_name=sr['repository'],revision=sr['raw_id']))}
+ ${h.fmt_date(h.time_to_datetime(sr['date']))}
+ </div>
+ </div>
+ <div class="left">
+ <div class="author">
+ <div class="gravatar">
+ <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(sr['author']),20)}"/>
+ </div>
+ <span>${h.person(sr['author'])}</span><br/>
+ <span><a href="mailto:${h.email_or_none(sr['author'])}">${h.email_or_none(sr['author'])}</a></span><br/>
+ </div>
+ %if sr['message_hl']:
+ <div class="search-code-body">
+ <pre>${h.literal(sr['message_hl'])}</pre>
+ </div>
+ %else:
+ <div class="message">${h.urlify_commit(sr['message'], sr['repository'])}</div>
+ %endif
+ </div>
+ </div>
+ </div>
+ %else:
+ %if cnt == 0:
+ <div class="table">
+ <div id="body${cnt}" class="codeblock">
+ <div class="error">${_('Permission denied')}</div>
+ </div>
+ </div>
+ %endif
+ %endif
+%endfor
+%if c.cur_query and c.formated_results:
+<div class="pagination-wh pagination-left">
+ ${c.formated_results.pager('$link_previous ~2~ $link_next')}
+</div>
+%endif
diff --git a/rhodecode/templates/settings/repo_settings.html b/rhodecode/templates/settings/repo_settings.html
index 004f2822..58f968fd 100644
--- a/rhodecode/templates/settings/repo_settings.html
+++ b/rhodecode/templates/settings/repo_settings.html
@@ -2,11 +2,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Settings')} - ${c.rhodecode_name}
+ ${_('%s Settings') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
&raquo;
@@ -53,6 +53,15 @@
</div>
</div>
<div class="field">
+ <div class="label">
+ <label for="landing_rev">${_('Landing revision')}:</label>
+ </div>
+ <div class="input">
+ ${h.select('landing_rev','',c.landing_revs,class_="medium")}
+ <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
+ </div>
+ </div>
+ <div class="field">
<div class="label label-textarea">
<label for="description">${_('Description')}:</label>
</div>
@@ -81,8 +90,8 @@
</div>
<div class="buttons">
- ${h.submit('save','Save',class_="ui-button")}
- ${h.reset('reset','Reset',class_="ui-button")}
+ ${h.submit('save',_('Save'),class_="ui-btn large")}
+ ${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
</div>
</div>
diff --git a/rhodecode/templates/shortlog/shortlog.html b/rhodecode/templates/shortlog/shortlog.html
index 43d5fd2f..2ec3f6e2 100644
--- a/rhodecode/templates/shortlog/shortlog.html
+++ b/rhodecode/templates/shortlog/shortlog.html
@@ -2,12 +2,12 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Shortlog')} - ${c.rhodecode_name}
+ ${_('%s Shortlog') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/shortlog/shortlog_data.html b/rhodecode/templates/shortlog/shortlog_data.html
index f5f5e0ca..0e8c2f97 100644
--- a/rhodecode/templates/shortlog/shortlog_data.html
+++ b/rhodecode/templates/shortlog/shortlog_data.html
@@ -19,7 +19,7 @@
h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
title=cs.message)}
</td>
- <td><span class="tooltip" title="${cs.date}">
+ <td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
${h.age(cs.date)}</span>
</td>
<td title="${cs.author}">${h.person(cs.author)}</td>
@@ -78,6 +78,11 @@ ${c.repo_changesets.pager('$link_previous ~2~ $link_next')}
<h4>${_('Existing repository?')}</h4>
<pre>
- ${c.rhodecode_repo.alias} push ${c.clone_repo_url}
+%if h.is_git(c.rhodecode_repo):
+ git remote add origin ${c.clone_repo_url}
+ git push -u origin master
+%else:
+ hg push ${c.clone_repo_url}
+%endif
</pre>
%endif
diff --git a/rhodecode/templates/summary/summary.html b/rhodecode/templates/summary/summary.html
index 59504fed..70799a56 100644
--- a/rhodecode/templates/summary/summary.html
+++ b/rhodecode/templates/summary/summary.html
@@ -1,11 +1,11 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
+ ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
@@ -16,6 +16,11 @@
${self.menu('summary')}
</%def>
+<%def name="head_extra()">
+<link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
+<link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
+</%def>
+
<%def name="main()">
<%
summary = lambda n:{False:'summary-short'}.get(n)
@@ -99,7 +104,11 @@
<div class="label-summary">
<label>${_('Description')}:</label>
</div>
- <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
+ %if c.visual.stylify_metatags:
+ <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
+ %else:
+ <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
+ %endif
</div>
<div class="field">
@@ -158,10 +167,10 @@
%endif
%else:
${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
- <span id="${'zip_link'}">${h.link_to('Download as zip',h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
+ <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
<span style="vertical-align: bottom">
<input id="archive_subrepos" type="checkbox" name="subrepos" />
- <label for="archive_subrepos" class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</label>
+ <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
</span>
%endif
</div>
@@ -220,11 +229,14 @@
</div>
%if c.readme_data:
-<div class="box" style="background-color: #FAFAFA">
- <div id="readme" class="title">
- <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
+<div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
+ <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
+ <div class="breadcrumbs">
+ <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
+ <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
+ </div>
</div>
- <div class="readme">
+ <div id="readme" class="readme">
<div class="readme_box">
${c.readme_data|n}
</div>
diff --git a/rhodecode/templates/switch_to_list.html b/rhodecode/templates/switch_to_list.html
index 5b415831..9c0295e7 100644
--- a/rhodecode/templates/switch_to_list.html
+++ b/rhodecode/templates/switch_to_list.html
@@ -4,7 +4,7 @@
<ul>
%if c.rhodecode_repo.branches.values():
%for cnt,branch in enumerate(c.rhodecode_repo.branches.items()):
- <li><div><pre>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</pre></div></li>
+ <li><div><pre>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[0]))}</pre></div></li>
%endfor
%else:
<li>${h.link_to(_('There are no branches yet'),'#')}</li>
@@ -16,7 +16,7 @@
<ul>
%if c.rhodecode_repo.tags.values():
%for cnt,tag in enumerate(c.rhodecode_repo.tags.items()):
- <li><div><pre>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</pre></div></li>
+ <li><div><pre>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[0]))}</pre></div></li>
%endfor
%else:
<li>${h.link_to(_('There are no tags yet'),'#')}</li>
diff --git a/rhodecode/templates/tags/tags.html b/rhodecode/templates/tags/tags.html
index 1594556d..a25b7757 100644
--- a/rhodecode/templates/tags/tags.html
+++ b/rhodecode/templates/tags/tags.html
@@ -2,13 +2,13 @@
<%inherit file="/base/base.html"/>
<%def name="title()">
- ${c.repo_name} ${_('Tags')} - ${c.rhodecode_name}
+ ${_('%s Tags') % c.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
<input class="q_filter_box" id="q_filter_tags" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
- ${h.link_to(u'Home',h.url('/'))}
+ ${h.link_to(_(u'Home'),h.url('/'))}
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
diff --git a/rhodecode/templates/tags/tags_data.html b/rhodecode/templates/tags/tags_data.html
index 82a5e6d5..93b39843 100644
--- a/rhodecode/templates/tags/tags_data.html
+++ b/rhodecode/templates/tags/tags_data.html
@@ -18,7 +18,7 @@
</span>
</span>
</td>
- <td><span class="tooltip" title="${h.age(tag[1].date)}">${tag[1].date}</span></td>
+ <td><span class="tooltip" title="${h.tooltip(h.age(tag[1].date))}">${h.fmt_date(tag[1].date)}</span></td>
<td title="${tag[1].author}">${h.person(tag[1].author)}</td>
<td>
<div>
diff --git a/rhodecode/tests/__init__.py b/rhodecode/tests/__init__.py
index ab2b8290..5f7d6b7c 100644
--- a/rhodecode/tests/__init__.py
+++ b/rhodecode/tests/__init__.py
@@ -10,6 +10,9 @@ setup-app`) and provides the base testing objects.
import os
import time
import logging
+import datetime
+import hashlib
+import tempfile
from os.path import join as jn
from unittest import TestCase
@@ -24,9 +27,11 @@ from webtest import TestApp
from rhodecode import is_windows
from rhodecode.model.meta import Session
from rhodecode.model.db import User
+from rhodecode.tests.nose_parametrized import parameterized
import pylons.test
+
os.environ['TZ'] = 'UTC'
if not is_windows:
time.tzset()
@@ -34,11 +39,15 @@ if not is_windows:
log = logging.getLogger(__name__)
__all__ = [
- 'environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
- 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
- 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
+ 'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
+ 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
+ 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
+ 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
- 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL'
+ 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
+ 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
+ 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
+ 'GIT_REMOTE_REPO', 'SCM_TESTS',
]
# Invoke websetup with the current config file
@@ -47,6 +56,7 @@ __all__ = [
##RUNNING DESIRED TESTS
# nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
# nosetests --pdb --pdb-failures
+# nosetests --with-coverage --cover-package=rhodecode.model.validators rhodecode.tests.test_validators
environ = {}
#SOME GLOBALS FOR TESTS
@@ -73,6 +83,45 @@ NEW_GIT_REPO = 'vcs_test_git_new'
HG_FORK = 'vcs_test_hg_fork'
GIT_FORK = 'vcs_test_git_fork'
+## VCS
+SCM_TESTS = ['hg', 'git']
+uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
+
+GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
+
+TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
+TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
+TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
+
+
+HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
+
+TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
+TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
+TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
+
+TEST_DIR = tempfile.gettempdir()
+TEST_REPO_PREFIX = 'vcs-test'
+
+# cached repos if any !
+# comment out to get some other repos from bb or github
+GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
+HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
+
+
+def get_new_dir(title):
+ """
+ Returns always new directory path.
+ """
+ from rhodecode.tests.vcs.utils import get_normalized_path
+ name = TEST_REPO_PREFIX
+ if title:
+ name = '-'.join((name, title))
+ hex = hashlib.sha1(str(time.time())).hexdigest()
+ name = '-'.join((name, hex))
+ path = os.path.join(TEST_DIR, name)
+ return get_normalized_path(path)
+
class TestController(TestCase):
@@ -90,8 +139,8 @@ class TestController(TestCase):
password=TEST_USER_ADMIN_PASS):
self._logged_username = username
response = self.app.post(url(controller='login', action='index'),
- {'username':username,
- 'password':password})
+ {'username': username,
+ 'password': password})
if 'invalid user name' in response.body:
self.fail('could not login using %s %s' % (username, password))
@@ -109,4 +158,8 @@ class TestController(TestCase):
def checkSessionFlash(self, response, msg):
self.assertTrue('flash' in response.session)
- self.assertTrue(msg in response.session['flash'][0][1])
+ if not msg in response.session['flash'][0][1]:
+ self.fail(
+ 'msg `%s` not found in session flash: got `%s` instead' % (
+ msg, response.session['flash'])
+ )
diff --git a/rhodecode/tests/api/__init__.py b/rhodecode/tests/api/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rhodecode/tests/api/__init__.py
diff --git a/rhodecode/tests/api/api_base.py b/rhodecode/tests/api/api_base.py
new file mode 100644
index 00000000..6b03974d
--- /dev/null
+++ b/rhodecode/tests/api/api_base.py
@@ -0,0 +1,990 @@
+from __future__ import with_statement
+import random
+import mock
+
+from rhodecode.tests import *
+from rhodecode.lib.compat import json
+from rhodecode.lib.auth import AuthUser
+from rhodecode.model.user import UserModel
+from rhodecode.model.users_group import UsersGroupModel
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.meta import Session
+from rhodecode.model.scm import ScmModel
+from rhodecode.model.db import Repository
+
+API_URL = '/_admin/api'
+
+
+def _build_data(apikey, method, **kw):
+ """
+ Builds API data with given random ID
+
+ :param random_id:
+ :type random_id:
+ """
+ random_id = random.randrange(1, 9999)
+ return random_id, json.dumps({
+ "id": random_id,
+ "api_key": apikey,
+ "method": method,
+ "args": kw
+ })
+
+jsonify = lambda obj: json.loads(json.dumps(obj))
+
+
+def crash(*args, **kwargs):
+ raise Exception('Total Crash !')
+
+
+def api_call(test_obj, params):
+ response = test_obj.app.post(API_URL, content_type='application/json',
+ params=params)
+ return response
+
+
+TEST_USERS_GROUP = 'test_users_group'
+
+
+def make_users_group(name=TEST_USERS_GROUP):
+ gr = UsersGroupModel().create(name=name)
+ UsersGroupModel().add_user_to_group(users_group=gr,
+ user=TEST_USER_ADMIN_LOGIN)
+ Session().commit()
+ return gr
+
+
+def destroy_users_group(name=TEST_USERS_GROUP):
+ UsersGroupModel().delete(users_group=name, force=True)
+ Session().commit()
+
+
+def create_repo(repo_name, repo_type):
+ # create new repo
+ form_data = dict(repo_name=repo_name,
+ repo_name_full=repo_name,
+ fork_name=None,
+ description='description %s' % repo_name,
+ repo_group=None,
+ private=False,
+ repo_type=repo_type,
+ clone_uri=None,
+ landing_rev='tip')
+ cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
+ r = RepoModel().create(form_data, cur_user)
+ Session().commit()
+ return r
+
+
+def create_fork(fork_name, fork_type, fork_of):
+ fork = RepoModel(Session())._get_repo(fork_of)
+ r = create_repo(fork_name, fork_type)
+ r.fork = fork
+ Session().add(r)
+ Session().commit()
+ return r
+
+
+def destroy_repo(repo_name):
+ RepoModel().delete(repo_name)
+ Session().commit()
+
+
+class BaseTestApi(object):
+ REPO = None
+ REPO_TYPE = None
+
+ @classmethod
+ def setUpClass(self):
+ self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
+ self.apikey = self.usr.api_key
+ self.TEST_USER = UserModel().create_or_update(
+ username='test-api',
+ password='test',
+ email='test@api.rhodecode.org',
+ firstname='first',
+ lastname='last'
+ )
+ Session().commit()
+ self.TEST_USER_LOGIN = self.TEST_USER.username
+
+ @classmethod
+ def teardownClass(self):
+ pass
+
+ def setUp(self):
+ self.maxDiff = None
+ make_users_group()
+
+ def tearDown(self):
+ destroy_users_group()
+
+ def _compare_ok(self, id_, expected, given):
+ expected = jsonify({
+ 'id': id_,
+ 'error': None,
+ 'result': expected
+ })
+ given = json.loads(given)
+ self.assertEqual(expected, given)
+
+ def _compare_error(self, id_, expected, given):
+ expected = jsonify({
+ 'id': id_,
+ 'error': expected,
+ 'result': None
+ })
+ given = json.loads(given)
+ self.assertEqual(expected, given)
+
+# def test_Optional(self):
+# from rhodecode.controllers.api.api import Optional
+# option1 = Optional(None)
+# self.assertEqual('<Optional:%s>' % None, repr(option1))
+#
+# self.assertEqual(1, Optional.extract(Optional(1)))
+# self.assertEqual('trololo', Optional.extract('trololo'))
+
+ def test_api_wrong_key(self):
+ id_, params = _build_data('trololo', 'get_user')
+ response = api_call(self, params)
+
+ expected = 'Invalid API KEY'
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_missing_non_optional_param(self):
+ id_, params = _build_data(self.apikey, 'get_user')
+ response = api_call(self, params)
+
+ expected = 'Missing non optional `userid` arg in JSON DATA'
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_get_users(self):
+ id_, params = _build_data(self.apikey, 'get_users',)
+ response = api_call(self, params)
+ ret_all = []
+ for usr in UserModel().get_all():
+ ret = usr.get_api_data()
+ ret_all.append(jsonify(ret))
+ expected = ret_all
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_get_user(self):
+ id_, params = _build_data(self.apikey, 'get_user',
+ userid=TEST_USER_ADMIN_LOGIN)
+ response = api_call(self, params)
+
+ usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
+ ret = usr.get_api_data()
+ ret['permissions'] = AuthUser(usr.user_id).permissions
+
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_get_user_that_does_not_exist(self):
+ id_, params = _build_data(self.apikey, 'get_user',
+ userid='trololo')
+ response = api_call(self, params)
+
+ expected = "user `%s` does not exist" % 'trololo'
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_pull(self):
+ #TODO: issues with rhodecode_extras here.. not sure why !
+ pass
+
+# repo_name = 'test_pull'
+# r = create_repo(repo_name, self.REPO_TYPE)
+# r.clone_uri = TEST_self.REPO
+# Session.add(r)
+# Session.commit()
+#
+# id_, params = _build_data(self.apikey, 'pull',
+# repoid=repo_name,)
+# response = self.app.post(API_URL, content_type='application/json',
+# params=params)
+#
+# expected = 'Pulled from `%s`' % repo_name
+# self._compare_ok(id_, expected, given=response.body)
+#
+# destroy_repo(repo_name)
+
+ def test_api_pull_error(self):
+ id_, params = _build_data(self.apikey, 'pull',
+ repoid=self.REPO,)
+ response = api_call(self, params)
+
+ expected = 'Unable to pull changes from `%s`' % self.REPO
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_rescan_repos(self):
+ id_, params = _build_data(self.apikey, 'rescan_repos')
+ response = api_call(self, params)
+
+ expected = {'added': [], 'removed': []}
+ self._compare_ok(id_, expected, given=response.body)
+
+ @mock.patch.object(ScmModel, 'repo_scan', crash)
+ def test_api_rescann_error(self):
+ id_, params = _build_data(self.apikey, 'rescan_repos',)
+ response = api_call(self, params)
+
+ expected = 'Error occurred during rescan repositories action'
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_lock_repo_lock_aquire(self):
+ id_, params = _build_data(self.apikey, 'lock',
+ userid=TEST_USER_ADMIN_LOGIN,
+ repoid=self.REPO,
+ locked=True)
+ response = api_call(self, params)
+ expected = ('User `%s` set lock state for repo `%s` to `%s`'
+ % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_lock_repo_lock_release(self):
+ id_, params = _build_data(self.apikey, 'lock',
+ userid=TEST_USER_ADMIN_LOGIN,
+ repoid=self.REPO,
+ locked=False)
+ response = api_call(self, params)
+ expected = ('User `%s` set lock state for repo `%s` to `%s`'
+ % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
+ self._compare_ok(id_, expected, given=response.body)
+
+ @mock.patch.object(Repository, 'lock', crash)
+ def test_api_lock_error(self):
+ id_, params = _build_data(self.apikey, 'lock',
+ userid=TEST_USER_ADMIN_LOGIN,
+ repoid=self.REPO,
+ locked=True)
+ response = api_call(self, params)
+
+ expected = 'Error occurred locking repository `%s`' % self.REPO
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_create_existing_user(self):
+ id_, params = _build_data(self.apikey, 'create_user',
+ username=TEST_USER_ADMIN_LOGIN,
+ email='test@foo.com',
+ password='trololo')
+ response = api_call(self, params)
+
+ expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_create_user_with_existing_email(self):
+ id_, params = _build_data(self.apikey, 'create_user',
+ username=TEST_USER_ADMIN_LOGIN + 'new',
+ email=TEST_USER_REGULAR_EMAIL,
+ password='trololo')
+ response = api_call(self, params)
+
+ expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_create_user(self):
+ username = 'test_new_api_user'
+ email = username + "@foo.com"
+
+ id_, params = _build_data(self.apikey, 'create_user',
+ username=username,
+ email=email,
+ password='trololo')
+ response = api_call(self, params)
+
+ usr = UserModel().get_by_username(username)
+ ret = dict(
+ msg='created new user `%s`' % username,
+ user=jsonify(usr.get_api_data())
+ )
+
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ UserModel().delete(usr.user_id)
+ Session().commit()
+
+ @mock.patch.object(UserModel, 'create_or_update', crash)
+ def test_api_create_user_when_exception_happened(self):
+
+ username = 'test_new_api_user'
+ email = username + "@foo.com"
+
+ id_, params = _build_data(self.apikey, 'create_user',
+ username=username,
+ email=email,
+ password='trololo')
+ response = api_call(self, params)
+ expected = 'failed to create user `%s`' % username
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_delete_user(self):
+ usr = UserModel().create_or_update(username=u'test_user',
+ password=u'qweqwe',
+ email=u'u232@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+ Session().commit()
+ username = usr.username
+ email = usr.email
+ usr_id = usr.user_id
+ ## DELETE THIS USER NOW
+
+ id_, params = _build_data(self.apikey, 'delete_user',
+ userid=username,)
+ response = api_call(self, params)
+
+ ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
+ 'user': None}
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ @mock.patch.object(UserModel, 'delete', crash)
+ def test_api_delete_user_when_exception_happened(self):
+ usr = UserModel().create_or_update(username=u'test_user',
+ password=u'qweqwe',
+ email=u'u232@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+ Session().commit()
+ username = usr.username
+
+ id_, params = _build_data(self.apikey, 'delete_user',
+ userid=username,)
+ response = api_call(self, params)
+ ret = 'failed to delete ID:%s %s' % (usr.user_id,
+ usr.username)
+ expected = ret
+ self._compare_error(id_, expected, given=response.body)
+
+ @parameterized.expand([('firstname', 'new_username'),
+ ('lastname', 'new_username'),
+ ('email', 'new_username'),
+ ('admin', True),
+ ('admin', False),
+ ('ldap_dn', 'test'),
+ ('ldap_dn', None),
+ ('active', False),
+ ('active', True),
+ ('password', 'newpass')
+ ])
+ def test_api_update_user(self, name, expected):
+ usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
+ kw = {name: expected,
+ 'userid': usr.user_id}
+ id_, params = _build_data(self.apikey, 'update_user', **kw)
+ response = api_call(self, params)
+
+ ret = {
+ 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
+ 'user': jsonify(UserModel()\
+ .get_by_username(self.TEST_USER_LOGIN)\
+ .get_api_data())
+ }
+
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_update_user_no_changed_params(self):
+ usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
+ ret = jsonify(usr.get_api_data())
+ id_, params = _build_data(self.apikey, 'update_user',
+ userid=TEST_USER_ADMIN_LOGIN)
+
+ response = api_call(self, params)
+ ret = {
+ 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
+ 'user': ret
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_update_user_by_user_id(self):
+ usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
+ ret = jsonify(usr.get_api_data())
+ id_, params = _build_data(self.apikey, 'update_user',
+ userid=usr.user_id)
+
+ response = api_call(self, params)
+ ret = {
+ 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
+ 'user': ret
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ @mock.patch.object(UserModel, 'update_user', crash)
+ def test_api_update_user_when_exception_happens(self):
+ usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
+ ret = jsonify(usr.get_api_data())
+ id_, params = _build_data(self.apikey, 'update_user',
+ userid=usr.user_id)
+
+ response = api_call(self, params)
+ ret = 'failed to update user `%s`' % usr.user_id
+
+ expected = ret
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_get_repo(self):
+ new_group = 'some_new_group'
+ make_users_group(new_group)
+ RepoModel().grant_users_group_permission(repo=self.REPO,
+ group_name=new_group,
+ perm='repository.read')
+ Session().commit()
+ id_, params = _build_data(self.apikey, 'get_repo',
+ repoid=self.REPO)
+ response = api_call(self, params)
+
+ repo = RepoModel().get_by_repo_name(self.REPO)
+ ret = repo.get_api_data()
+
+ members = []
+ for user in repo.repo_to_perm:
+ perm = user.permission.permission_name
+ user = user.user
+ user_data = user.get_api_data()
+ user_data['type'] = "user"
+ user_data['permission'] = perm
+ members.append(user_data)
+
+ for users_group in repo.users_group_to_perm:
+ perm = users_group.permission.permission_name
+ users_group = users_group.users_group
+ users_group_data = users_group.get_api_data()
+ users_group_data['type'] = "users_group"
+ users_group_data['permission'] = perm
+ members.append(users_group_data)
+
+ ret['members'] = members
+
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+ destroy_users_group(new_group)
+
+ def test_api_get_repo_that_doesn_not_exist(self):
+ id_, params = _build_data(self.apikey, 'get_repo',
+ repoid='no-such-repo')
+ response = api_call(self, params)
+
+ ret = 'repository `%s` does not exist' % 'no-such-repo'
+ expected = ret
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_get_repos(self):
+ id_, params = _build_data(self.apikey, 'get_repos')
+ response = api_call(self, params)
+
+ result = []
+ for repo in RepoModel().get_all():
+ result.append(repo.get_api_data())
+ ret = jsonify(result)
+
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ @parameterized.expand([('all', 'all'),
+ ('dirs', 'dirs'),
+ ('files', 'files'), ])
+ def test_api_get_repo_nodes(self, name, ret_type):
+ rev = 'tip'
+ path = '/'
+ id_, params = _build_data(self.apikey, 'get_repo_nodes',
+ repoid=self.REPO, revision=rev,
+ root_path=path,
+ ret_type=ret_type)
+ response = api_call(self, params)
+
+ # we don't the actual return types here since it's tested somewhere
+ # else
+ expected = json.loads(response.body)['result']
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_get_repo_nodes_bad_revisions(self):
+ rev = 'i-dont-exist'
+ path = '/'
+ id_, params = _build_data(self.apikey, 'get_repo_nodes',
+ repoid=self.REPO, revision=rev,
+ root_path=path,)
+ response = api_call(self, params)
+
+ expected = 'failed to get repo: `%s` nodes' % self.REPO
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_get_repo_nodes_bad_path(self):
+ rev = 'tip'
+ path = '/idontexits'
+ id_, params = _build_data(self.apikey, 'get_repo_nodes',
+ repoid=self.REPO, revision=rev,
+ root_path=path,)
+ response = api_call(self, params)
+
+ expected = 'failed to get repo: `%s` nodes' % self.REPO
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_get_repo_nodes_bad_ret_type(self):
+ rev = 'tip'
+ path = '/'
+ ret_type = 'error'
+ id_, params = _build_data(self.apikey, 'get_repo_nodes',
+ repoid=self.REPO, revision=rev,
+ root_path=path,
+ ret_type=ret_type)
+ response = api_call(self, params)
+
+ expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_create_repo(self):
+ repo_name = 'api-repo'
+ id_, params = _build_data(self.apikey, 'create_repo',
+ repo_name=repo_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ repo_type='hg',
+ )
+ response = api_call(self, params)
+
+ repo = RepoModel().get_by_repo_name(repo_name)
+ ret = {
+ 'msg': 'Created new repository `%s`' % repo_name,
+ 'repo': jsonify(repo.get_api_data())
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+ destroy_repo(repo_name)
+
+ def test_api_create_repo_unknown_owner(self):
+ repo_name = 'api-repo'
+ owner = 'i-dont-exist'
+ id_, params = _build_data(self.apikey, 'create_repo',
+ repo_name=repo_name,
+ owner=owner,
+ repo_type='hg',
+ )
+ response = api_call(self, params)
+ expected = 'user `%s` does not exist' % owner
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_create_repo_exists(self):
+ repo_name = self.REPO
+ id_, params = _build_data(self.apikey, 'create_repo',
+ repo_name=repo_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ repo_type='hg',
+ )
+ response = api_call(self, params)
+ expected = "repo `%s` already exist" % repo_name
+ self._compare_error(id_, expected, given=response.body)
+
+ @mock.patch.object(RepoModel, 'create_repo', crash)
+ def test_api_create_repo_exception_occurred(self):
+ repo_name = 'api-repo'
+ id_, params = _build_data(self.apikey, 'create_repo',
+ repo_name=repo_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ repo_type='hg',
+ )
+ response = api_call(self, params)
+ expected = 'failed to create repository `%s`' % repo_name
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_delete_repo(self):
+ repo_name = 'api_delete_me'
+ create_repo(repo_name, self.REPO_TYPE)
+
+ id_, params = _build_data(self.apikey, 'delete_repo',
+ repoid=repo_name,)
+ response = api_call(self, params)
+
+ ret = {
+ 'msg': 'Deleted repository `%s`' % repo_name,
+ 'success': True
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_delete_repo_exception_occurred(self):
+ repo_name = 'api_delete_me'
+ create_repo(repo_name, self.REPO_TYPE)
+ try:
+ with mock.patch.object(RepoModel, 'delete', crash):
+ id_, params = _build_data(self.apikey, 'delete_repo',
+ repoid=repo_name,)
+ response = api_call(self, params)
+
+ expected = 'failed to delete repository `%s`' % repo_name
+ self._compare_error(id_, expected, given=response.body)
+ finally:
+ destroy_repo(repo_name)
+
+ def test_api_fork_repo(self):
+ fork_name = 'api-repo-fork'
+ id_, params = _build_data(self.apikey, 'fork_repo',
+ repoid=self.REPO,
+ fork_name=fork_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ )
+ response = api_call(self, params)
+
+ ret = {
+ 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
+ fork_name),
+ 'success': True
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+ destroy_repo(fork_name)
+
+ def test_api_fork_repo_unknown_owner(self):
+ fork_name = 'api-repo-fork'
+ owner = 'i-dont-exist'
+ id_, params = _build_data(self.apikey, 'fork_repo',
+ repoid=self.REPO,
+ fork_name=fork_name,
+ owner=owner,
+ )
+ response = api_call(self, params)
+ expected = 'user `%s` does not exist' % owner
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_fork_repo_fork_exists(self):
+ fork_name = 'api-repo-fork'
+ create_fork(fork_name, self.REPO_TYPE, self.REPO)
+
+ try:
+ fork_name = 'api-repo-fork'
+
+ id_, params = _build_data(self.apikey, 'fork_repo',
+ repoid=self.REPO,
+ fork_name=fork_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ )
+ response = api_call(self, params)
+
+ expected = "fork `%s` already exist" % fork_name
+ self._compare_error(id_, expected, given=response.body)
+ finally:
+ destroy_repo(fork_name)
+
+ def test_api_fork_repo_repo_exists(self):
+ fork_name = self.REPO
+
+ id_, params = _build_data(self.apikey, 'fork_repo',
+ repoid=self.REPO,
+ fork_name=fork_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ )
+ response = api_call(self, params)
+
+ expected = "repo `%s` already exist" % fork_name
+ self._compare_error(id_, expected, given=response.body)
+
+ @mock.patch.object(RepoModel, 'create_fork', crash)
+ def test_api_fork_repo_exception_occurred(self):
+ fork_name = 'api-repo-fork'
+ id_, params = _build_data(self.apikey, 'fork_repo',
+ repoid=self.REPO,
+ fork_name=fork_name,
+ owner=TEST_USER_ADMIN_LOGIN,
+ )
+ response = api_call(self, params)
+
+ expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
+ fork_name)
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_get_users_group(self):
+ id_, params = _build_data(self.apikey, 'get_users_group',
+ usersgroupid=TEST_USERS_GROUP)
+ response = api_call(self, params)
+
+ users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
+ members = []
+ for user in users_group.members:
+ user = user.user
+ members.append(user.get_api_data())
+
+ ret = users_group.get_api_data()
+ ret['members'] = members
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_get_users_groups(self):
+
+ make_users_group('test_users_group2')
+
+ id_, params = _build_data(self.apikey, 'get_users_groups',)
+ response = api_call(self, params)
+
+ expected = []
+ for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
+ users_group = UsersGroupModel().get_group(gr_name)
+ ret = users_group.get_api_data()
+ expected.append(ret)
+ self._compare_ok(id_, expected, given=response.body)
+
+ UsersGroupModel().delete(users_group='test_users_group2')
+ Session().commit()
+
+ def test_api_create_users_group(self):
+ group_name = 'some_new_group'
+ id_, params = _build_data(self.apikey, 'create_users_group',
+ group_name=group_name)
+ response = api_call(self, params)
+
+ ret = {
+ 'msg': 'created new users group `%s`' % group_name,
+ 'users_group': jsonify(UsersGroupModel()\
+ .get_by_name(group_name)\
+ .get_api_data())
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ destroy_users_group(group_name)
+
+ def test_api_get_users_group_that_exist(self):
+ id_, params = _build_data(self.apikey, 'create_users_group',
+ group_name=TEST_USERS_GROUP)
+ response = api_call(self, params)
+
+ expected = "users group `%s` already exist" % TEST_USERS_GROUP
+ self._compare_error(id_, expected, given=response.body)
+
+ @mock.patch.object(UsersGroupModel, 'create', crash)
+ def test_api_get_users_group_exception_occurred(self):
+ group_name = 'exception_happens'
+ id_, params = _build_data(self.apikey, 'create_users_group',
+ group_name=group_name)
+ response = api_call(self, params)
+
+ expected = 'failed to create group `%s`' % group_name
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_add_user_to_users_group(self):
+ gr_name = 'test_group'
+ UsersGroupModel().create(gr_name)
+ Session().commit()
+ id_, params = _build_data(self.apikey, 'add_user_to_users_group',
+ usersgroupid=gr_name,
+ userid=TEST_USER_ADMIN_LOGIN)
+ response = api_call(self, params)
+
+ expected = {
+ 'msg': 'added member `%s` to users group `%s`' % (
+ TEST_USER_ADMIN_LOGIN, gr_name
+ ),
+ 'success': True}
+ self._compare_ok(id_, expected, given=response.body)
+
+ UsersGroupModel().delete(users_group=gr_name)
+ Session().commit()
+
+ def test_api_add_user_to_users_group_that_doesnt_exist(self):
+ id_, params = _build_data(self.apikey, 'add_user_to_users_group',
+ usersgroupid='false-group',
+ userid=TEST_USER_ADMIN_LOGIN)
+ response = api_call(self, params)
+
+ expected = 'users group `%s` does not exist' % 'false-group'
+ self._compare_error(id_, expected, given=response.body)
+
+ @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
+ def test_api_add_user_to_users_group_exception_occurred(self):
+ gr_name = 'test_group'
+ UsersGroupModel().create(gr_name)
+ Session().commit()
+ id_, params = _build_data(self.apikey, 'add_user_to_users_group',
+ usersgroupid=gr_name,
+ userid=TEST_USER_ADMIN_LOGIN)
+ response = api_call(self, params)
+
+ expected = 'failed to add member to users group `%s`' % gr_name
+ self._compare_error(id_, expected, given=response.body)
+
+ UsersGroupModel().delete(users_group=gr_name)
+ Session().commit()
+
+ def test_api_remove_user_from_users_group(self):
+ gr_name = 'test_group_3'
+ gr = UsersGroupModel().create(gr_name)
+ UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
+ Session().commit()
+ id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
+ usersgroupid=gr_name,
+ userid=TEST_USER_ADMIN_LOGIN)
+ response = api_call(self, params)
+
+ expected = {
+ 'msg': 'removed member `%s` from users group `%s`' % (
+ TEST_USER_ADMIN_LOGIN, gr_name
+ ),
+ 'success': True}
+ self._compare_ok(id_, expected, given=response.body)
+
+ UsersGroupModel().delete(users_group=gr_name)
+ Session().commit()
+
+ @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
+ def test_api_remove_user_from_users_group_exception_occurred(self):
+ gr_name = 'test_group_3'
+ gr = UsersGroupModel().create(gr_name)
+ UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
+ Session().commit()
+ id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
+ usersgroupid=gr_name,
+ userid=TEST_USER_ADMIN_LOGIN)
+ response = api_call(self, params)
+
+ expected = 'failed to remove member from users group `%s`' % gr_name
+ self._compare_error(id_, expected, given=response.body)
+
+ UsersGroupModel().delete(users_group=gr_name)
+ Session().commit()
+
+ @parameterized.expand([('none', 'repository.none'),
+ ('read', 'repository.read'),
+ ('write', 'repository.write'),
+ ('admin', 'repository.admin')])
+ def test_api_grant_user_permission(self, name, perm):
+ id_, params = _build_data(self.apikey, 'grant_user_permission',
+ repoid=self.REPO,
+ userid=TEST_USER_ADMIN_LOGIN,
+ perm=perm)
+ response = api_call(self, params)
+
+ ret = {
+ 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
+ perm, TEST_USER_ADMIN_LOGIN, self.REPO
+ ),
+ 'success': True
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_grant_user_permission_wrong_permission(self):
+ perm = 'haha.no.permission'
+ id_, params = _build_data(self.apikey, 'grant_user_permission',
+ repoid=self.REPO,
+ userid=TEST_USER_ADMIN_LOGIN,
+ perm=perm)
+ response = api_call(self, params)
+
+ expected = 'permission `%s` does not exist' % perm
+ self._compare_error(id_, expected, given=response.body)
+
+ @mock.patch.object(RepoModel, 'grant_user_permission', crash)
+ def test_api_grant_user_permission_exception_when_adding(self):
+ perm = 'repository.read'
+ id_, params = _build_data(self.apikey, 'grant_user_permission',
+ repoid=self.REPO,
+ userid=TEST_USER_ADMIN_LOGIN,
+ perm=perm)
+ response = api_call(self, params)
+
+ expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
+ TEST_USER_ADMIN_LOGIN, self.REPO
+ )
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_revoke_user_permission(self):
+ id_, params = _build_data(self.apikey, 'revoke_user_permission',
+ repoid=self.REPO,
+ userid=TEST_USER_ADMIN_LOGIN,)
+ response = api_call(self, params)
+
+ expected = {
+ 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
+ TEST_USER_ADMIN_LOGIN, self.REPO
+ ),
+ 'success': True
+ }
+ self._compare_ok(id_, expected, given=response.body)
+
+ @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
+ def test_api_revoke_user_permission_exception_when_adding(self):
+ id_, params = _build_data(self.apikey, 'revoke_user_permission',
+ repoid=self.REPO,
+ userid=TEST_USER_ADMIN_LOGIN,)
+ response = api_call(self, params)
+
+ expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
+ TEST_USER_ADMIN_LOGIN, self.REPO
+ )
+ self._compare_error(id_, expected, given=response.body)
+
+ @parameterized.expand([('none', 'repository.none'),
+ ('read', 'repository.read'),
+ ('write', 'repository.write'),
+ ('admin', 'repository.admin')])
+ def test_api_grant_users_group_permission(self, name, perm):
+ id_, params = _build_data(self.apikey, 'grant_users_group_permission',
+ repoid=self.REPO,
+ usersgroupid=TEST_USERS_GROUP,
+ perm=perm)
+ response = api_call(self, params)
+
+ ret = {
+ 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
+ perm, TEST_USERS_GROUP, self.REPO
+ ),
+ 'success': True
+ }
+ expected = ret
+ self._compare_ok(id_, expected, given=response.body)
+
+ def test_api_grant_users_group_permission_wrong_permission(self):
+ perm = 'haha.no.permission'
+ id_, params = _build_data(self.apikey, 'grant_users_group_permission',
+ repoid=self.REPO,
+ usersgroupid=TEST_USERS_GROUP,
+ perm=perm)
+ response = api_call(self, params)
+
+ expected = 'permission `%s` does not exist' % perm
+ self._compare_error(id_, expected, given=response.body)
+
+ @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
+ def test_api_grant_users_group_permission_exception_when_adding(self):
+ perm = 'repository.read'
+ id_, params = _build_data(self.apikey, 'grant_users_group_permission',
+ repoid=self.REPO,
+ usersgroupid=TEST_USERS_GROUP,
+ perm=perm)
+ response = api_call(self, params)
+
+ expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
+ TEST_USERS_GROUP, self.REPO
+ )
+ self._compare_error(id_, expected, given=response.body)
+
+ def test_api_revoke_users_group_permission(self):
+ RepoModel().grant_users_group_permission(repo=self.REPO,
+ group_name=TEST_USERS_GROUP,
+ perm='repository.read')
+ Session().commit()
+ id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
+ repoid=self.REPO,
+ usersgroupid=TEST_USERS_GROUP,)
+ response = api_call(self, params)
+
+ expected = {
+ 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
+ TEST_USERS_GROUP, self.REPO
+ ),
+ 'success': True
+ }
+ self._compare_ok(id_, expected, given=response.body)
+
+ @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
+ def test_api_revoke_users_group_permission_exception_when_adding(self):
+
+ id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
+ repoid=self.REPO,
+ usersgroupid=TEST_USERS_GROUP,)
+ response = api_call(self, params)
+
+ expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
+ TEST_USERS_GROUP, self.REPO
+ )
+ self._compare_error(id_, expected, given=response.body)
diff --git a/rhodecode/tests/api/test_api_git.py b/rhodecode/tests/api/test_api_git.py
new file mode 100644
index 00000000..b5aa08ae
--- /dev/null
+++ b/rhodecode/tests/api/test_api_git.py
@@ -0,0 +1,7 @@
+from rhodecode.tests import *
+from rhodecode.tests.api.api_base import BaseTestApi
+
+
+class TestGitApi(BaseTestApi, TestController):
+ REPO = GIT_REPO
+ REPO_TYPE = 'git'
diff --git a/rhodecode/tests/api/test_api_hg.py b/rhodecode/tests/api/test_api_hg.py
new file mode 100644
index 00000000..48ea8ff4
--- /dev/null
+++ b/rhodecode/tests/api/test_api_hg.py
@@ -0,0 +1,7 @@
+from rhodecode.tests import *
+from rhodecode.tests.api.api_base import BaseTestApi
+
+
+class TestHgApi(BaseTestApi, TestController):
+ REPO = HG_REPO
+ REPO_TYPE = 'hg'
diff --git a/rhodecode/tests/functional/test_admin_notifications.py b/rhodecode/tests/functional/test_admin_notifications.py
index e8ee33fe..66431a68 100644
--- a/rhodecode/tests/functional/test_admin_notifications.py
+++ b/rhodecode/tests/functional/test_admin_notifications.py
@@ -1,25 +1,25 @@
from rhodecode.tests import *
-from rhodecode.model.db import Notification, User, UserNotification
+from rhodecode.model.db import Notification, User
from rhodecode.model.user import UserModel
from rhodecode.model.notification import NotificationModel
-from rhodecode.model.meta import Session
-class TestNotificationsController(TestController):
+class TestNotificationsController(TestController):
def tearDown(self):
for n in Notification.query().all():
inst = Notification.get(n.notification_id)
- Session.delete(inst)
- Session.commit()
+ self.Session().delete(inst)
+ self.Session().commit()
def test_index(self):
self.log_user()
u1 = UserModel().create_or_update(username='u1', password='qweqwe',
email='u1@rhodecode.org',
- name='u1', lastname='u1').user_id
+ firstname='u1', lastname='u1')
+ u1 = u1.user_id
response = self.app.get(url('notifications'))
self.assertTrue('''<div class="table">No notifications here yet</div>'''
@@ -30,7 +30,7 @@ class TestNotificationsController(TestController):
NotificationModel().create(created_by=u1, subject=u'test_notification_1',
body=u'notification_1',
recipients=[cur_user])
- Session.commit()
+ self.Session().commit()
response = self.app.get(url('notifications'))
self.assertTrue(u'test_notification_1' in response.body)
@@ -58,28 +58,27 @@ class TestNotificationsController(TestController):
u1 = UserModel().create_or_update(username='u1', password='qweqwe',
email='u1@rhodecode.org',
- name='u1', lastname='u1')
+ firstname='u1', lastname='u1')
u2 = UserModel().create_or_update(username='u2', password='qweqwe',
email='u2@rhodecode.org',
- name='u2', lastname='u2')
+ firstname='u2', lastname='u2')
# make notifications
notification = NotificationModel().create(created_by=cur_user,
subject=u'test',
body=u'hi there',
recipients=[cur_user, u1, u2])
- Session.commit()
+ self.Session().commit()
u1 = User.get(u1.user_id)
u2 = User.get(u2.user_id)
# check DB
- get_notif = lambda un:[x.notification for x in un]
+ get_notif = lambda un: [x.notification for x in un]
self.assertEqual(get_notif(cur_user.notifications), [notification])
self.assertEqual(get_notif(u1.notifications), [notification])
self.assertEqual(get_notif(u2.notifications), [notification])
cur_usr_id = cur_user.user_id
-
response = self.app.delete(url('notification',
notification_id=
notification.notification_id))
@@ -87,19 +86,15 @@ class TestNotificationsController(TestController):
cur_user = User.get(cur_usr_id)
self.assertEqual(cur_user.notifications, [])
-
-# def test_delete_browser_fakeout(self):
-# response = self.app.post(url('notification', notification_id=1), params=dict(_method='delete'))
-
def test_show(self):
self.log_user()
cur_user = self._get_logged_user()
u1 = UserModel().create_or_update(username='u1', password='qweqwe',
email='u1@rhodecode.org',
- name='u1', lastname='u1')
+ firstname='u1', lastname='u1')
u2 = UserModel().create_or_update(username='u2', password='qweqwe',
email='u2@rhodecode.org',
- name='u2', lastname='u2')
+ firstname='u2', lastname='u2')
notification = NotificationModel().create(created_by=cur_user,
subject=u'test',
@@ -108,12 +103,3 @@ class TestNotificationsController(TestController):
response = self.app.get(url('notification',
notification_id=notification.notification_id))
-
-# def test_show_as_xml(self):
-# response = self.app.get(url('formatted_notification', notification_id=1, format='xml'))
-#
-# def test_edit(self):
-# response = self.app.get(url('edit_notification', notification_id=1))
-#
-# def test_edit_as_xml(self):
-# response = self.app.get(url('formatted_edit_notification', notification_id=1, format='xml'))
diff --git a/rhodecode/tests/functional/test_admin_repos.py b/rhodecode/tests/functional/test_admin_repos.py
index e957f344..ed448bba 100644
--- a/rhodecode/tests/functional/test_admin_repos.py
+++ b/rhodecode/tests/functional/test_admin_repos.py
@@ -3,8 +3,11 @@
import os
from rhodecode.lib import vcs
-from rhodecode.model.db import Repository
+from rhodecode.model.db import Repository, RepoGroup
from rhodecode.tests import *
+from rhodecode.model.repos_group import ReposGroupModel
+from rhodecode.model.repo import RepoModel
+
class TestAdminReposController(TestController):
@@ -24,17 +27,19 @@ class TestAdminReposController(TestController):
repo_name = NEW_HG_REPO
description = 'description for newly created repo'
private = False
- response = self.app.post(url('repos'), {'repo_name':repo_name,
- 'repo_type':'hg',
- 'clone_uri':'',
- 'repo_group':'',
- 'description':description,
- 'private':private})
- self.checkSessionFlash(response, 'created repository %s' % (repo_name))
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'hg',
+ 'clone_uri': '',
+ 'repo_group': '',
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
+ self.checkSessionFlash(response,
+ 'created repository %s' % (repo_name))
#test if the repo was created in the database
- new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
- repo_name).one()
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name).one()
self.assertEqual(new_repo.repo_name, repo_name)
self.assertEqual(new_repo.description, description)
@@ -42,15 +47,13 @@ class TestAdminReposController(TestController):
#test if repository is visible in the list ?
response = response.follow()
- self.assertTrue(repo_name in response.body)
-
+ response.mustcontain(repo_name)
#test if repository was created on filesystem
try:
vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
except:
- self.fail('no repo in filesystem')
-
+ self.fail('no repo %s in filesystem' % repo_name)
def test_create_hg_non_ascii(self):
self.log_user()
@@ -60,18 +63,19 @@ class TestAdminReposController(TestController):
description = 'description for newly created repo' + non_ascii
description_unicode = description.decode('utf8')
private = False
- response = self.app.post(url('repos'), {'repo_name':repo_name,
- 'repo_type':'hg',
- 'clone_uri':'',
- 'repo_group':'',
- 'description':description,
- 'private':private})
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'hg',
+ 'clone_uri': '',
+ 'repo_group': '',
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
self.checkSessionFlash(response,
'created repository %s' % (repo_name_unicode))
#test if the repo was created in the database
- new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
- repo_name_unicode).one()
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name_unicode).one()
self.assertEqual(new_repo.repo_name, repo_name_unicode)
self.assertEqual(new_repo.description, description_unicode)
@@ -79,52 +83,129 @@ class TestAdminReposController(TestController):
#test if repository is visible in the list ?
response = response.follow()
- self.assertTrue(repo_name in response.body)
+ response.mustcontain(repo_name)
#test if repository was created on filesystem
try:
vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
except:
- self.fail('no repo in filesystem')
-
+ self.fail('no repo %s in filesystem' % repo_name)
def test_create_hg_in_group(self):
- #TODO: write test !
- pass
+ self.log_user()
+
+ ## create GROUP
+ group_name = 'sometest'
+ gr = ReposGroupModel().create(group_name=group_name,
+ group_description='test',)
+ self.Session().commit()
+
+ repo_name = 'ingroup'
+ repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
+ description = 'description for newly created repo'
+ private = False
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'hg',
+ 'clone_uri': '',
+ 'repo_group': gr.group_id,
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
+ self.checkSessionFlash(response,
+ 'created repository %s' % (repo_name))
+
+ #test if the repo was created in the database
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name_full).one()
+
+ self.assertEqual(new_repo.repo_name, repo_name_full)
+ self.assertEqual(new_repo.description, description)
+
+ #test if repository is visible in the list ?
+ response = response.follow()
+
+ response.mustcontain(repo_name_full)
+
+ #test if repository was created on filesystem
+ try:
+ vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full))
+ except:
+ ReposGroupModel().delete(group_name)
+ self.Session().commit()
+ self.fail('no repo %s in filesystem' % repo_name)
+
+ RepoModel().delete(repo_name_full)
+ ReposGroupModel().delete(group_name)
+ self.Session().commit()
def test_create_git(self):
- return
self.log_user()
repo_name = NEW_GIT_REPO
description = 'description for newly created repo'
private = False
- response = self.app.post(url('repos'), {'repo_name':repo_name,
- 'repo_type':'git',
- 'clone_uri':'',
- 'repo_group':'',
- 'description':description,
- 'private':private})
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'git',
+ 'clone_uri': '',
+ 'repo_group': '',
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
+ self.checkSessionFlash(response,
+ 'created repository %s' % (repo_name))
+
+ #test if the repo was created in the database
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name).one()
+
+ self.assertEqual(new_repo.repo_name, repo_name)
+ self.assertEqual(new_repo.description, description)
+
+ #test if repository is visible in the list ?
+ response = response.follow()
+
+ response.mustcontain(repo_name)
+ #test if repository was created on filesystem
+ try:
+ vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
+ except:
+ self.fail('no repo %s in filesystem' % repo_name)
- #test if we have a message for that repository
- assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo'
+ def test_create_git_non_ascii(self):
+ self.log_user()
+ non_ascii = "ąęł"
+ repo_name = "%s%s" % (NEW_GIT_REPO, non_ascii)
+ repo_name_unicode = repo_name.decode('utf8')
+ description = 'description for newly created repo' + non_ascii
+ description_unicode = description.decode('utf8')
+ private = False
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'git',
+ 'clone_uri': '',
+ 'repo_group': '',
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
+ self.checkSessionFlash(response,
+ 'created repository %s' % (repo_name_unicode))
- #test if the fork was created in the database
- new_repo = self.Session.query(Repository).filter(Repository.repo_name == repo_name).one()
+ #test if the repo was created in the database
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name_unicode).one()
- assert new_repo.repo_name == repo_name, 'wrong name of repo name in db'
- assert new_repo.description == description, 'wrong description'
+ self.assertEqual(new_repo.repo_name, repo_name_unicode)
+ self.assertEqual(new_repo.description, description_unicode)
#test if repository is visible in the list ?
response = response.follow()
- assert repo_name in response.body, 'missing new repo from the main repos list'
+ response.mustcontain(repo_name)
#test if repository was created on filesystem
try:
vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
except:
- assert False , 'no repo in filesystem'
+ self.fail('no repo %s in filesystem' % repo_name)
def test_new(self):
self.log_user()
@@ -140,27 +221,73 @@ class TestAdminReposController(TestController):
response = self.app.post(url('repo', repo_name=HG_REPO),
params=dict(_method='put'))
- def test_delete(self):
+ def test_delete_hg(self):
self.log_user()
repo_name = 'vcs_test_new_to_delete'
description = 'description for newly created repo'
private = False
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'hg',
+ 'clone_uri': '',
+ 'repo_group': '',
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
+ self.checkSessionFlash(response,
+ 'created repository %s' % (repo_name))
+
+ #test if the repo was created in the database
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name).one()
+
+ self.assertEqual(new_repo.repo_name, repo_name)
+ self.assertEqual(new_repo.description, description)
+
+ #test if repository is visible in the list ?
+ response = response.follow()
+
+ response.mustcontain(repo_name)
- response = self.app.post(url('repos'), {'repo_name':repo_name,
- 'repo_type':'hg',
- 'clone_uri':'',
- 'repo_group':'',
- 'description':description,
- 'private':private})
- self.assertTrue('flash' in response.session)
+ #test if repository was created on filesystem
+ try:
+ vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
+ except:
+ self.fail('no repo %s in filesystem' % repo_name)
+
+ response = self.app.delete(url('repo', repo_name=repo_name))
- #test if we have a message for that repository
- self.assertTrue('''created repository %s''' % (repo_name) in
+ self.assertTrue('''deleted repository %s''' % (repo_name) in
response.session['flash'][0])
+ response.follow()
+
+ #check if repo was deleted from db
+ deleted_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name).scalar()
+
+ self.assertEqual(deleted_repo, None)
+
+ self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
+ False)
+
+ def test_delete_git(self):
+ self.log_user()
+ repo_name = 'vcs_test_new_to_delete'
+ description = 'description for newly created repo'
+ private = False
+ response = self.app.post(url('repos'), {'repo_name': repo_name,
+ 'repo_type': 'git',
+ 'clone_uri': '',
+ 'repo_group': '',
+ 'description': description,
+ 'private': private,
+ 'landing_rev': 'tip'})
+ self.checkSessionFlash(response,
+ 'created repository %s' % (repo_name))
+
#test if the repo was created in the database
- new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
- repo_name).one()
+ new_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name).one()
self.assertEqual(new_repo.repo_name, repo_name)
self.assertEqual(new_repo.description, description)
@@ -168,8 +295,13 @@ class TestAdminReposController(TestController):
#test if repository is visible in the list ?
response = response.follow()
- self.assertTrue(repo_name in response.body)
+ response.mustcontain(repo_name)
+ #test if repository was created on filesystem
+ try:
+ vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
+ except:
+ self.fail('no repo %s in filesystem' % repo_name)
response = self.app.delete(url('repo', repo_name=repo_name))
@@ -179,32 +311,30 @@ class TestAdminReposController(TestController):
response.follow()
#check if repo was deleted from db
- deleted_repo = self.Session.query(Repository).filter(Repository.repo_name
- == repo_name).scalar()
+ deleted_repo = self.Session().query(Repository)\
+ .filter(Repository.repo_name == repo_name).scalar()
self.assertEqual(deleted_repo, None)
+ self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
+ False)
def test_delete_repo_with_group(self):
#TODO:
pass
-
def test_delete_browser_fakeout(self):
response = self.app.post(url('repo', repo_name=HG_REPO),
params=dict(_method='delete'))
- def test_show(self):
+ def test_show_hg(self):
self.log_user()
response = self.app.get(url('repo', repo_name=HG_REPO))
- def test_show_as_xml(self):
- response = self.app.get(url('formatted_repo', repo_name=HG_REPO,
- format='xml'))
+ def test_show_git(self):
+ self.log_user()
+ response = self.app.get(url('repo', repo_name=GIT_REPO))
+
def test_edit(self):
response = self.app.get(url('edit_repo', repo_name=HG_REPO))
-
- def test_edit_as_xml(self):
- response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO,
- format='xml'))
diff --git a/rhodecode/tests/functional/test_admin_settings.py b/rhodecode/tests/functional/test_admin_settings.py
index 80707737..c4f72d98 100644
--- a/rhodecode/tests/functional/test_admin_settings.py
+++ b/rhodecode/tests/functional/test_admin_settings.py
@@ -1,8 +1,12 @@
# -*- coding: utf-8 -*-
from rhodecode.lib.auth import get_crypt_password, check_password
-from rhodecode.model.db import User, RhodeCodeSetting
+from rhodecode.model.db import User, RhodeCodeSetting, Repository
from rhodecode.tests import *
+from rhodecode.lib import helpers as h
+from rhodecode.model.user import UserModel
+from rhodecode.model.scm import ScmModel
+
class TestAdminSettingsController(TestController):
@@ -47,7 +51,6 @@ class TestAdminSettingsController(TestController):
response = self.app.get(url('formatted_admin_edit_setting',
setting_id=1, format='xml'))
-
def test_ga_code_active(self):
self.log_user()
old_title = 'RhodeCode'
@@ -67,8 +70,7 @@ class TestAdminSettingsController(TestController):
.get_app_settings()['rhodecode_ga_code'], new_ga_code)
response = response.follow()
- self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
- in response.body)
+ response.mustcontain("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code)
def test_ga_code_inactive(self):
self.log_user()
@@ -89,9 +91,8 @@ class TestAdminSettingsController(TestController):
.get_app_settings()['rhodecode_ga_code'], new_ga_code)
response = response.follow()
- self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
- not in response.body)
-
+ self.assertFalse("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
+ in response.body)
def test_title_change(self):
self.log_user()
@@ -114,9 +115,7 @@ class TestAdminSettingsController(TestController):
new_title.decode('utf-8'))
response = response.follow()
- self.assertTrue("""<h1><a href="/">%s</a></h1>""" % new_title
- in response.body)
-
+ response.mustcontain("""<h1><a href="/">%s</a></h1>""" % new_title)
def test_my_account(self):
self.log_user()
@@ -124,89 +123,144 @@ class TestAdminSettingsController(TestController):
self.assertTrue('value="test_admin' in response.body)
- def test_my_account_update(self):
- self.log_user()
-
- new_email = 'new@mail.pl'
- new_name = 'NewName'
- new_lastname = 'NewLastname'
- new_password = 'test123'
-
-
- response = self.app.post(url('admin_settings_my_account_update'),
- params=dict(_method='put',
- username='test_admin',
- new_password=new_password,
- password_confirmation = new_password,
- password='',
- name=new_name,
- lastname=new_lastname,
- email=new_email,))
- response.follow()
-
- assert 'Your account was updated successfully' in response.session['flash'][0][1], 'no flash message about success of change'
- user = self.Session.query(User).filter(User.username == 'test_admin').one()
- assert user.email == new_email , 'incorrect user email after update got %s vs %s' % (user.email, new_email)
- assert user.name == new_name, 'updated field mismatch %s vs %s' % (user.name, new_name)
- assert user.lastname == new_lastname, 'updated field mismatch %s vs %s' % (user.lastname, new_lastname)
- assert check_password(new_password, user.password) is True, 'password field mismatch %s vs %s' % (user.password, new_password)
-
- #bring back the admin settings
- old_email = 'test_admin@mail.com'
- old_name = 'RhodeCode'
- old_lastname = 'Admin'
- old_password = 'test12'
-
- response = self.app.post(url('admin_settings_my_account_update'), params=dict(
- _method='put',
- username='test_admin',
- new_password=old_password,
- password_confirmation = old_password,
- password='',
- name=old_name,
- lastname=old_lastname,
- email=old_email,))
-
- response.follow()
- self.checkSessionFlash(response,
- 'Your account was updated successfully')
-
- user = self.Session.query(User).filter(User.username == 'test_admin').one()
- assert user.email == old_email , 'incorrect user email after update got %s vs %s' % (user.email, old_email)
-
- assert user.email == old_email , 'incorrect user email after update got %s vs %s' % (user.email, old_email)
- assert user.name == old_name, 'updated field mismatch %s vs %s' % (user.name, old_name)
- assert user.lastname == old_lastname, 'updated field mismatch %s vs %s' % (user.lastname, old_lastname)
- assert check_password(old_password, user.password) is True , 'password updated field mismatch %s vs %s' % (user.password, old_password)
-
+ @parameterized.expand([('firstname', 'new_username'),
+ ('lastname', 'new_username'),
+ ('admin', True),
+ ('admin', False),
+ ('ldap_dn', 'test'),
+ ('ldap_dn', None),
+ ('active', False),
+ ('active', True),
+ ('email', 'some@email.com'),
+ ])
+ def test_my_account_update(self, name, expected):
+ uname = 'testme'
+ usr = UserModel().create_or_update(username=uname, password='qweqwe',
+ email='testme@rhodecod.org')
+ self.Session().commit()
+ params = usr.get_api_data()
+ user_id = usr.user_id
+ self.log_user(username=uname, password='qweqwe')
+ params.update({name: expected})
+ params.update({'password_confirmation': ''})
+ params.update({'new_password': ''})
+
+ try:
+ response = self.app.put(url('admin_settings_my_account_update',
+ id=user_id), params)
+
+ self.checkSessionFlash(response,
+ 'Your account was updated successfully')
+
+ updated_user = User.get_by_username(uname)
+ updated_params = updated_user.get_api_data()
+ updated_params.update({'password_confirmation': ''})
+ updated_params.update({'new_password': ''})
+
+ params['last_login'] = updated_params['last_login']
+ if name == 'email':
+ params['emails'] = [expected]
+ if name == 'ldap_dn':
+ #cannot update this via form
+ params['ldap_dn'] = None
+ if name == 'active':
+ #my account cannot deactivate account
+ params['active'] = True
+ if name == 'admin':
+ #my account cannot make you an admin !
+ params['admin'] = False
+
+ self.assertEqual(params, updated_params)
+
+ finally:
+ UserModel().delete('testme')
def test_my_account_update_err_email_exists(self):
self.log_user()
- new_email = 'test_regular@mail.com'#already exisitn email
- response = self.app.post(url('admin_settings_my_account_update'), params=dict(
- _method='put',
- username='test_admin',
- new_password='test12',
- password_confirmation = 'test122',
- name='NewName',
- lastname='NewLastname',
- email=new_email,))
-
- assert 'This e-mail address is already taken' in response.body, 'Missing error message about existing email'
+ new_email = 'test_regular@mail.com' # already exisitn email
+ response = self.app.put(url('admin_settings_my_account_update'),
+ params=dict(
+ username='test_admin',
+ new_password='test12',
+ password_confirmation='test122',
+ firstname='NewName',
+ lastname='NewLastname',
+ email=new_email,)
+ )
+ response.mustcontain('This e-mail address is already taken')
def test_my_account_update_err(self):
self.log_user('test_regular2', 'test12')
new_email = 'newmail.pl'
- response = self.app.post(url('admin_settings_my_account_update'), params=dict(
- _method='put',
- username='test_admin',
- new_password='test12',
- password_confirmation = 'test122',
- name='NewName',
- lastname='NewLastname',
- email=new_email,))
- assert 'An email address must contain a single @' in response.body, 'Missing error message about wrong email'
- assert 'This username already exists' in response.body, 'Missing error message about existing user'
+ response = self.app.post(url('admin_settings_my_account_update'),
+ params=dict(
+ _method='put',
+ username='test_admin',
+ new_password='test12',
+ password_confirmation='test122',
+ firstname='NewName',
+ lastname='NewLastname',
+ email=new_email,)
+ )
+
+ response.mustcontain('An email address must contain a single @')
+ from rhodecode.model import validators
+ msg = validators.ValidUsername(edit=False,
+ old_data={})._messages['username_exists']
+ msg = h.html_escape(msg % {'username': 'test_admin'})
+ response.mustcontain(u"%s" % msg)
+
+ def test_set_repo_fork_has_no_self_id(self):
+ self.log_user()
+ repo = Repository.get_by_repo_name(HG_REPO)
+ response = self.app.get(url('edit_repo', repo_name=HG_REPO))
+ opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
+ assert opt not in response.body
+
+ def test_set_fork_of_repo(self):
+ self.log_user()
+ repo = Repository.get_by_repo_name(HG_REPO)
+ repo2 = Repository.get_by_repo_name(GIT_REPO)
+ response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
+ params=dict(
+ id_fork_of=repo2.repo_id
+ ))
+ repo = Repository.get_by_repo_name(HG_REPO)
+ repo2 = Repository.get_by_repo_name(GIT_REPO)
+ self.checkSessionFlash(response,
+ 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
+
+ assert repo.fork == repo2
+ response = response.follow()
+ # check if given repo is selected
+
+ opt = """<option value="%s" selected="selected">%s</option>""" % (
+ repo2.repo_id, repo2.repo_name)
+ response.mustcontain(opt)
+
+ # clean session flash
+ #response = self.app.get(url('edit_repo', repo_name=HG_REPO))
+
+ ## mark it as None
+ response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
+ params=dict(
+ id_fork_of=None
+ ))
+ repo = Repository.get_by_repo_name(HG_REPO)
+ repo2 = Repository.get_by_repo_name(GIT_REPO)
+ self.checkSessionFlash(response,
+ 'Marked repo %s as fork of %s' % (repo.repo_name, "Nothing"))
+ assert repo.fork == None
+
+ def test_set_fork_of_same_repo(self):
+ self.log_user()
+ repo = Repository.get_by_repo_name(HG_REPO)
+ response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
+ params=dict(
+ id_fork_of=repo.repo_id
+ ))
+ self.checkSessionFlash(response,
+ 'An error occurred during this operation')
diff --git a/rhodecode/tests/functional/test_admin_users.py b/rhodecode/tests/functional/test_admin_users.py
index 435639c5..a1d3eb35 100644
--- a/rhodecode/tests/functional/test_admin_users.py
+++ b/rhodecode/tests/functional/test_admin_users.py
@@ -1,8 +1,13 @@
+from sqlalchemy.orm.exc import NoResultFound
+
from rhodecode.tests import *
from rhodecode.model.db import User, Permission
from rhodecode.lib.auth import check_password
-from sqlalchemy.orm.exc import NoResultFound
from rhodecode.model.user import UserModel
+from rhodecode.model import validators
+from rhodecode.lib import helpers as h
+from rhodecode.model.meta import Session
+
class TestAdminUsersController(TestController):
@@ -24,30 +29,28 @@ class TestAdminUsersController(TestController):
email = 'mail@mail.com'
response = self.app.post(url('users'),
- {'username':username,
- 'password':password,
- 'password_confirmation':password_confirmation,
- 'name':name,
- 'active':True,
- 'lastname':lastname,
- 'email':email})
+ {'username': username,
+ 'password': password,
+ 'password_confirmation': password_confirmation,
+ 'firstname': name,
+ 'active': True,
+ 'lastname': lastname,
+ 'email': email})
-
- self.assertTrue('''created user %s''' % (username) in
- response.session['flash'][0])
+ self.checkSessionFlash(response, '''created user %s''' % (username))
new_user = self.Session.query(User).\
filter(User.username == username).one()
- self.assertEqual(new_user.username,username)
- self.assertEqual(check_password(password, new_user.password),True)
- self.assertEqual(new_user.name,name)
- self.assertEqual(new_user.lastname,lastname)
- self.assertEqual(new_user.email,email)
+ self.assertEqual(new_user.username, username)
+ self.assertEqual(check_password(password, new_user.password), True)
+ self.assertEqual(new_user.name, name)
+ self.assertEqual(new_user.lastname, lastname)
+ self.assertEqual(new_user.email, email)
response.follow()
response = response.follow()
- self.assertTrue("""edit">newtestuser</a>""" in response.body)
+ response.mustcontain("""newtestuser""")
def test_create_err(self):
self.log_user()
@@ -57,16 +60,18 @@ class TestAdminUsersController(TestController):
lastname = 'lastname'
email = 'errmail.com'
- response = self.app.post(url('users'), {'username':username,
- 'password':password,
- 'name':name,
- 'active':False,
- 'lastname':lastname,
- 'email':email})
+ response = self.app.post(url('users'), {'username': username,
+ 'password': password,
+ 'name': name,
+ 'active': False,
+ 'lastname': lastname,
+ 'email': email})
- self.assertTrue("""<span class="error-message">Invalid username</span>""" in response.body)
- self.assertTrue("""<span class="error-message">Please enter a value</span>""" in response.body)
- self.assertTrue("""<span class="error-message">An email address must contain a single @</span>""" in response.body)
+ msg = validators.ValidUsername(False, {})._messages['system_invalid_username']
+ msg = h.html_escape(msg % {'username': 'new_user'})
+ response.mustcontain("""<span class="error-message">%s</span>""" % msg)
+ response.mustcontain("""<span class="error-message">Please enter a value</span>""")
+ response.mustcontain("""<span class="error-message">An email address must contain a single @</span>""")
def get_user():
self.Session.query(User).filter(User.username == username).one()
@@ -80,8 +85,45 @@ class TestAdminUsersController(TestController):
def test_new_as_xml(self):
response = self.app.get(url('formatted_new_user', format='xml'))
- def test_update(self):
- response = self.app.put(url('user', id=1))
+ @parameterized.expand([('firstname', 'new_username'),
+ ('lastname', 'new_username'),
+ ('admin', True),
+ ('admin', False),
+ ('ldap_dn', 'test'),
+ ('ldap_dn', None),
+ ('active', False),
+ ('active', True),
+ ('email', 'some@email.com'),
+ ])
+ def test_update(self, name, expected):
+ self.log_user()
+ uname = 'testme'
+ usr = UserModel().create_or_update(username=uname, password='qweqwe',
+ email='testme@rhodecod.org')
+ self.Session().commit()
+ params = usr.get_api_data()
+ params.update({name: expected})
+ params.update({'password_confirmation': ''})
+ params.update({'new_password': ''})
+ if name == 'email':
+ params['emails'] = [expected]
+ if name == 'ldap_dn':
+ #cannot update this via form
+ params['ldap_dn'] = None
+ try:
+ response = self.app.put(url('user', id=usr.user_id), params)
+
+ self.checkSessionFlash(response, '''User updated successfully''')
+
+ updated_user = User.get_by_username(uname)
+ updated_params = updated_user.get_api_data()
+ updated_params.update({'password_confirmation': ''})
+ updated_params.update({'new_password': ''})
+
+ self.assertEqual(params, updated_params)
+
+ finally:
+ UserModel().delete('testme')
def test_update_browser_fakeout(self):
response = self.app.post(url('user', id=1), params=dict(_method='put'))
@@ -94,13 +136,13 @@ class TestAdminUsersController(TestController):
lastname = 'lastname'
email = 'todeletemail@mail.com'
- response = self.app.post(url('users'), {'username':username,
- 'password':password,
- 'password_confirmation':password,
- 'name':name,
- 'active':True,
- 'lastname':lastname,
- 'email':email})
+ response = self.app.post(url('users'), {'username': username,
+ 'password': password,
+ 'password_confirmation': password,
+ 'firstname': name,
+ 'active': True,
+ 'lastname': lastname,
+ 'email': email})
response = response.follow()
@@ -111,7 +153,6 @@ class TestAdminUsersController(TestController):
self.assertTrue("""successfully deleted user""" in
response.session['flash'][0])
-
def test_delete_browser_fakeout(self):
response = self.app.post(url('user', id=1),
params=dict(_method='delete'))
@@ -127,53 +168,123 @@ class TestAdminUsersController(TestController):
user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
response = self.app.get(url('edit_user', id=user.user_id))
-
def test_add_perm_create_repo(self):
self.log_user()
perm_none = Permission.get_by_key('hg.create.none')
perm_create = Permission.get_by_key('hg.create.repository')
- user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
-
+ user = UserModel().create_or_update(username='dummy', password='qwe',
+ email='dummy', firstname='a',
+ lastname='b')
+ Session().commit()
+ uid = user.user_id
- #User should have None permission on creation repository
- self.assertEqual(UserModel().has_perm(user, perm_none), False)
- self.assertEqual(UserModel().has_perm(user, perm_create), False)
+ try:
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(user, perm_none), False)
+ self.assertEqual(UserModel().has_perm(user, perm_create), False)
- response = self.app.post(url('user_perm', id=user.user_id),
- params=dict(_method='put',
- create_repo_perm=True))
+ response = self.app.post(url('user_perm', id=uid),
+ params=dict(_method='put',
+ create_repo_perm=True))
- perm_none = Permission.get_by_key('hg.create.none')
- perm_create = Permission.get_by_key('hg.create.repository')
+ perm_none = Permission.get_by_key('hg.create.none')
+ perm_create = Permission.get_by_key('hg.create.repository')
- user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
- #User should have None permission on creation repository
- self.assertEqual(UserModel().has_perm(user, perm_none), False)
- self.assertEqual(UserModel().has_perm(user, perm_create), True)
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(uid, perm_none), False)
+ self.assertEqual(UserModel().has_perm(uid, perm_create), True)
+ finally:
+ UserModel().delete(uid)
+ Session().commit()
def test_revoke_perm_create_repo(self):
self.log_user()
perm_none = Permission.get_by_key('hg.create.none')
perm_create = Permission.get_by_key('hg.create.repository')
- user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
+ user = UserModel().create_or_update(username='dummy', password='qwe',
+ email='dummy', firstname='a',
+ lastname='b')
+ Session().commit()
+ uid = user.user_id
+ try:
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(user, perm_none), False)
+ self.assertEqual(UserModel().has_perm(user, perm_create), False)
- #User should have None permission on creation repository
- self.assertEqual(UserModel().has_perm(user, perm_none), False)
- self.assertEqual(UserModel().has_perm(user, perm_create), False)
+ response = self.app.post(url('user_perm', id=uid),
+ params=dict(_method='put'))
- response = self.app.post(url('user_perm', id=user.user_id),
- params=dict(_method='put'))
+ perm_none = Permission.get_by_key('hg.create.none')
+ perm_create = Permission.get_by_key('hg.create.repository')
- perm_none = Permission.get_by_key('hg.create.none')
- perm_create = Permission.get_by_key('hg.create.repository')
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(uid, perm_none), True)
+ self.assertEqual(UserModel().has_perm(uid, perm_create), False)
+ finally:
+ UserModel().delete(uid)
+ Session().commit()
- user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
- #User should have None permission on creation repository
- self.assertEqual(UserModel().has_perm(user, perm_none), True)
- self.assertEqual(UserModel().has_perm(user, perm_create), False)
+ def test_add_perm_fork_repo(self):
+ self.log_user()
+ perm_none = Permission.get_by_key('hg.fork.none')
+ perm_fork = Permission.get_by_key('hg.fork.repository')
+
+ user = UserModel().create_or_update(username='dummy', password='qwe',
+ email='dummy', firstname='a',
+ lastname='b')
+ Session().commit()
+ uid = user.user_id
+
+ try:
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(user, perm_none), False)
+ self.assertEqual(UserModel().has_perm(user, perm_fork), False)
+
+ response = self.app.post(url('user_perm', id=uid),
+ params=dict(_method='put',
+ create_repo_perm=True))
+
+ perm_none = Permission.get_by_key('hg.create.none')
+ perm_create = Permission.get_by_key('hg.create.repository')
+
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(uid, perm_none), False)
+ self.assertEqual(UserModel().has_perm(uid, perm_create), True)
+ finally:
+ UserModel().delete(uid)
+ Session().commit()
+
+ def test_revoke_perm_fork_repo(self):
+ self.log_user()
+ perm_none = Permission.get_by_key('hg.fork.none')
+ perm_fork = Permission.get_by_key('hg.fork.repository')
+
+ user = UserModel().create_or_update(username='dummy', password='qwe',
+ email='dummy', firstname='a',
+ lastname='b')
+ Session().commit()
+ uid = user.user_id
+
+ try:
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(user, perm_none), False)
+ self.assertEqual(UserModel().has_perm(user, perm_fork), False)
+
+ response = self.app.post(url('user_perm', id=uid),
+ params=dict(_method='put'))
+
+ perm_none = Permission.get_by_key('hg.create.none')
+ perm_create = Permission.get_by_key('hg.create.repository')
+
+ #User should have None permission on creation repository
+ self.assertEqual(UserModel().has_perm(uid, perm_none), True)
+ self.assertEqual(UserModel().has_perm(uid, perm_create), False)
+ finally:
+ UserModel().delete(uid)
+ Session().commit()
def test_edit_as_xml(self):
response = self.app.get(url('formatted_edit_user', id=1, format='xml'))
diff --git a/rhodecode/tests/functional/test_admin_users_groups.py b/rhodecode/tests/functional/test_admin_users_groups.py
index 5e43e9a1..0bd036f8 100644
--- a/rhodecode/tests/functional/test_admin_users_groups.py
+++ b/rhodecode/tests/functional/test_admin_users_groups.py
@@ -65,27 +65,48 @@ class TestAdminUsersGroupsController(TestController):
users_group_name = TEST_USERS_GROUP + 'another2'
response = self.app.post(url('users_groups'),
{'users_group_name': users_group_name,
- 'active':True})
+ 'active': True})
response.follow()
ug = UsersGroup.get_by_group_name(users_group_name)
self.checkSessionFlash(response,
'created users group %s' % users_group_name)
-
+ ## ENABLE REPO CREATE ON A GROUP
response = self.app.put(url('users_group_perm', id=ug.users_group_id),
{'create_repo_perm': True})
response.follow()
ug = UsersGroup.get_by_group_name(users_group_name)
p = Permission.get_by_key('hg.create.repository')
- # check if user has this perm
+ p2 = Permission.get_by_key('hg.fork.none')
+ # check if user has this perms, they should be here since
+ # defaults are on
perms = UsersGroupToPerm.query()\
.filter(UsersGroupToPerm.users_group == ug).all()
- perms = [[x.__dict__['users_group_id'],
- x.__dict__['permission_id'],] for x in perms]
+
self.assertEqual(
- perms,
- [[ug.users_group_id, p.permission_id]]
+ [[x.users_group_id, x.permission_id, ] for x in perms],
+ [[ug.users_group_id, p.permission_id],
+ [ug.users_group_id, p2.permission_id]]
+ )
+
+ ## DISABLE REPO CREATE ON A GROUP
+ response = self.app.put(url('users_group_perm', id=ug.users_group_id),
+ {})
+
+ response.follow()
+ ug = UsersGroup.get_by_group_name(users_group_name)
+ p = Permission.get_by_key('hg.create.none')
+ p2 = Permission.get_by_key('hg.fork.none')
+ # check if user has this perms, they should be here since
+ # defaults are on
+ perms = UsersGroupToPerm.query()\
+ .filter(UsersGroupToPerm.users_group == ug).all()
+
+ self.assertEqual(
+ sorted([[x.users_group_id, x.permission_id, ] for x in perms]),
+ sorted([[ug.users_group_id, p.permission_id],
+ [ug.users_group_id, p2.permission_id]])
)
# DELETE !
@@ -101,8 +122,77 @@ class TestAdminUsersGroupsController(TestController):
p = Permission.get_by_key('hg.create.repository')
perms = UsersGroupToPerm.query()\
.filter(UsersGroupToPerm.users_group_id == ugid).all()
- perms = [[x.__dict__['users_group_id'],
- x.__dict__['permission_id'],] for x in perms]
+ perms = [[x.users_group_id,
+ x.permission_id, ] for x in perms]
+ self.assertEqual(
+ perms,
+ []
+ )
+
+ def test_enable_repository_fork_on_group(self):
+ self.log_user()
+ users_group_name = TEST_USERS_GROUP + 'another2'
+ response = self.app.post(url('users_groups'),
+ {'users_group_name': users_group_name,
+ 'active': True})
+ response.follow()
+
+ ug = UsersGroup.get_by_group_name(users_group_name)
+ self.checkSessionFlash(response,
+ 'created users group %s' % users_group_name)
+ ## ENABLE REPO CREATE ON A GROUP
+ response = self.app.put(url('users_group_perm', id=ug.users_group_id),
+ {'fork_repo_perm': True})
+
+ response.follow()
+ ug = UsersGroup.get_by_group_name(users_group_name)
+ p = Permission.get_by_key('hg.create.none')
+ p2 = Permission.get_by_key('hg.fork.repository')
+ # check if user has this perms, they should be here since
+ # defaults are on
+ perms = UsersGroupToPerm.query()\
+ .filter(UsersGroupToPerm.users_group == ug).all()
+
+ self.assertEqual(
+ [[x.users_group_id, x.permission_id, ] for x in perms],
+ [[ug.users_group_id, p.permission_id],
+ [ug.users_group_id, p2.permission_id]]
+ )
+
+ ## DISABLE REPO CREATE ON A GROUP
+ response = self.app.put(url('users_group_perm', id=ug.users_group_id),
+ {})
+
+ response.follow()
+ ug = UsersGroup.get_by_group_name(users_group_name)
+ p = Permission.get_by_key('hg.create.none')
+ p2 = Permission.get_by_key('hg.fork.none')
+ # check if user has this perms, they should be here since
+ # defaults are on
+ perms = UsersGroupToPerm.query()\
+ .filter(UsersGroupToPerm.users_group == ug).all()
+
+ self.assertEqual(
+ [[x.users_group_id, x.permission_id, ] for x in perms],
+ [[ug.users_group_id, p.permission_id],
+ [ug.users_group_id, p2.permission_id]]
+ )
+
+ # DELETE !
+ ug = UsersGroup.get_by_group_name(users_group_name)
+ ugid = ug.users_group_id
+ response = self.app.delete(url('users_group', id=ug.users_group_id))
+ response = response.follow()
+ gr = self.Session.query(UsersGroup)\
+ .filter(UsersGroup.users_group_name ==
+ users_group_name).scalar()
+
+ self.assertEqual(gr, None)
+ p = Permission.get_by_key('hg.fork.repository')
+ perms = UsersGroupToPerm.query()\
+ .filter(UsersGroupToPerm.users_group_id == ugid).all()
+ perms = [[x.users_group_id,
+ x.permission_id, ] for x in perms]
self.assertEqual(
perms,
[]
diff --git a/rhodecode/tests/functional/test_changelog.py b/rhodecode/tests/functional/test_changelog.py
index e23306fe..07783c24 100644
--- a/rhodecode/tests/functional/test_changelog.py
+++ b/rhodecode/tests/functional/test_changelog.py
@@ -10,8 +10,10 @@ class TestChangelogController(TestController):
response.mustcontain("""<div id="chg_20" class="container tablerow1">""")
response.mustcontain(
- """<input class="changeset_range" id="5e204e7583b9" """
- """name="5e204e7583b9" type="checkbox" value="1" />"""
+ """<input class="changeset_range" """
+ """id="5e204e7583b9c8e7b93a020bd036564b1e731dae" """
+ """name="5e204e7583b9c8e7b93a020bd036564b1e731dae" """
+ """type="checkbox" value="1" />"""
)
response.mustcontain(
"""<span class="changeset_id">154:"""
@@ -21,7 +23,7 @@ class TestChangelogController(TestController):
response.mustcontain("""Small update at simplevcs app""")
response.mustcontain(
- """<div id="5e204e7583b9c8e7b93a020bd036564b1e731dae" """
+ """<div id="changed_total_5e204e7583b9c8e7b93a020bd036564b1e731dae" """
"""style="float:right;" class="changed_total tooltip" """
"""title="Affected number of files, click to show """
"""more details">3</div>"""
@@ -29,22 +31,24 @@ class TestChangelogController(TestController):
#pagination
response = self.app.get(url(controller='changelog', action='index',
- repo_name=HG_REPO), {'page':1})
+ repo_name=HG_REPO), {'page': 1})
response = self.app.get(url(controller='changelog', action='index',
- repo_name=HG_REPO), {'page':2})
+ repo_name=HG_REPO), {'page': 2})
response = self.app.get(url(controller='changelog', action='index',
- repo_name=HG_REPO), {'page':3})
+ repo_name=HG_REPO), {'page': 3})
response = self.app.get(url(controller='changelog', action='index',
- repo_name=HG_REPO), {'page':4})
+ repo_name=HG_REPO), {'page': 4})
response = self.app.get(url(controller='changelog', action='index',
- repo_name=HG_REPO), {'page':5})
+ repo_name=HG_REPO), {'page': 5})
response = self.app.get(url(controller='changelog', action='index',
- repo_name=HG_REPO), {'page':6})
+ repo_name=HG_REPO), {'page': 6})
# Test response after pagination...
response.mustcontain(
- """<input class="changeset_range" id="46ad32a4f974" """
- """name="46ad32a4f974" type="checkbox" value="1" />"""
+ """<input class="changeset_range" """
+ """id="46ad32a4f974e45472a898c6b0acb600320579b1" """
+ """name="46ad32a4f974e45472a898c6b0acb600320579b1" """
+ """type="checkbox" value="1" />"""
)
response.mustcontain(
"""<span class="changeset_id">64:"""
@@ -52,7 +56,7 @@ class TestChangelogController(TestController):
)
response.mustcontain(
- """<div id="46ad32a4f974e45472a898c6b0acb600320579b1" """
+ """<div id="changed_total_46ad32a4f974e45472a898c6b0acb600320579b1" """
"""style="float:right;" class="changed_total tooltip" """
"""title="Affected number of files, click to show """
"""more details">21</div>"""
diff --git a/rhodecode/tests/functional/test_changeset_comments.py b/rhodecode/tests/functional/test_changeset_comments.py
index fb77ab68..fc0ce1a6 100644
--- a/rhodecode/tests/functional/test_changeset_comments.py
+++ b/rhodecode/tests/functional/test_changeset_comments.py
@@ -40,8 +40,8 @@ class TestChangeSetCommentsController(TestController):
repo_name=HG_REPO, revision=rev))
# test DB
self.assertEqual(ChangesetComment.query().count(), 1)
- self.assertTrue('''<div class="comments-number">%s '''
- '''comment(s) (0 inline)</div>''' % 1 in response.body)
+ response.mustcontain('''<div class="comments-number">%s comment '''
+ '''(0 inline)</div>''' % 1)
self.assertEqual(Notification.query().count(), 1)
self.assertEqual(ChangesetComment.query().count(), 1)
@@ -76,7 +76,7 @@ class TestChangeSetCommentsController(TestController):
#test DB
self.assertEqual(ChangesetComment.query().count(), 1)
response.mustcontain(
- '''<div class="comments-number">0 comment(s)'''
+ '''<div class="comments-number">0 comments'''
''' (%s inline)</div>''' % 1
)
response.mustcontain(
@@ -115,8 +115,8 @@ class TestChangeSetCommentsController(TestController):
repo_name=HG_REPO, revision=rev))
# test DB
self.assertEqual(ChangesetComment.query().count(), 1)
- self.assertTrue('''<div class="comments-number">%s '''
- '''comment(s) (0 inline)</div>''' % 1 in response.body)
+ response.mustcontain('''<div class="comments-number">%s '''
+ '''comment (0 inline)</div>''' % 1)
self.assertEqual(Notification.query().count(), 2)
users = [x.user.username for x in UserNotification.query().all()]
@@ -148,5 +148,5 @@ class TestChangeSetCommentsController(TestController):
response = self.app.get(url(controller='changeset', action='index',
repo_name=HG_REPO, revision=rev))
- self.assertTrue('''<div class="comments-number">0 comment(s)'''
- ''' (0 inline)</div>''' in response.body)
+ response.mustcontain('''<div class="comments-number">0 comments'''
+ ''' (0 inline)</div>''')
diff --git a/rhodecode/tests/functional/test_compare.py b/rhodecode/tests/functional/test_compare.py
new file mode 100644
index 00000000..6ca8b311
--- /dev/null
+++ b/rhodecode/tests/functional/test_compare.py
@@ -0,0 +1,189 @@
+from rhodecode.tests import *
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.meta import Session
+from rhodecode.model.db import Repository
+from rhodecode.model.scm import ScmModel
+from rhodecode.lib.vcs.backends.base import EmptyChangeset
+
+
+class TestCompareController(TestController):
+
+ def test_index_tag(self):
+ self.log_user()
+ tag1 = '0.1.3'
+ tag2 = '0.1.2'
+ response = self.app.get(url(controller='compare', action='index',
+ repo_name=HG_REPO,
+ org_ref_type="tag",
+ org_ref=tag1,
+ other_ref_type="tag",
+ other_ref=tag2,
+ ))
+ response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, tag1, HG_REPO, tag2))
+ ## outgoing changesets between tags
+ response.mustcontain('''<a href="/%s/changeset/17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">r120:17544fbfcd33</a>''' % HG_REPO)
+ response.mustcontain('''<a href="/%s/changeset/36e0fc9d2808c5022a24f49d6658330383ed8666">r119:36e0fc9d2808</a>''' % HG_REPO)
+ response.mustcontain('''<a href="/%s/changeset/bb1a3ab98cc45cb934a77dcabf87a5a598b59e97">r118:bb1a3ab98cc4</a>''' % HG_REPO)
+ response.mustcontain('''<a href="/%s/changeset/41fda979f02fda216374bf8edac4e83f69e7581c">r117:41fda979f02f</a>''' % HG_REPO)
+ response.mustcontain('''<a href="/%s/changeset/9749bfbfc0d2eba208d7947de266303b67c87cda">r116:9749bfbfc0d2</a>''' % HG_REPO)
+ response.mustcontain('''<a href="/%s/changeset/70d4cef8a37657ee4cf5aabb3bd9f68879769816">r115:70d4cef8a376</a>''' % HG_REPO)
+ response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO)
+
+ ## files diff
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--1c5cf9e91c12">docs/api/utils/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--e3305437df55">test_and_report.sh</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--c8e92ef85cd1">.hgignore</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--6e08b694d687">.hgtags</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2c14b00f3393">docs/api/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--430ccbc82bdf">vcs/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--9c390eb52cd6">vcs/backends/hg.py</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--ebb592c595c0">vcs/utils/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--7abc741b5052">vcs/utils/annotate.py</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2ef0ef106c56">vcs/utils/diffs.py</a></div>''' % (HG_REPO, tag1, tag2))
+ response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--3150cb87d4b7">vcs/utils/lazy.py</a></div>''' % (HG_REPO, tag1, tag2))
+
+ def test_index_branch(self):
+ self.log_user()
+ response = self.app.get(url(controller='compare', action='index',
+ repo_name=HG_REPO,
+ org_ref_type="branch",
+ org_ref='default',
+ other_ref_type="branch",
+ other_ref='default',
+ ))
+
+ response.mustcontain('%s@default -> %s@default' % (HG_REPO, HG_REPO))
+ # branch are equal
+ response.mustcontain('<tr><td>No changesets</td></tr>')
+
+ def test_compare_revisions(self):
+ self.log_user()
+ rev1 = '3d8f361e72ab'
+ rev2 = 'b986218ba1c9'
+ response = self.app.get(url(controller='compare', action='index',
+ repo_name=HG_REPO,
+ org_ref_type="rev",
+ org_ref=rev1,
+ other_ref_type="rev",
+ other_ref=rev2,
+ ))
+ response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_REPO, rev2))
+ ## outgoing changesets between those revisions
+ response.mustcontain("""<a href="/%s/changeset/3d8f361e72ab303da48d799ff1ac40d5ac37c67e">r1:%s</a>""" % (HG_REPO, rev1))
+
+ ## files
+ response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--c8e92ef85cd1">.hgignore</a>""" % (HG_REPO, rev1, rev2))
+
+ def test_compare_remote_repos(self):
+ self.log_user()
+
+ form_data = dict(
+ repo_name=HG_FORK,
+ repo_name_full=HG_FORK,
+ repo_group=None,
+ repo_type='hg',
+ description='',
+ private=False,
+ copy_permissions=False,
+ landing_rev='tip',
+ update_after_clone=False,
+ fork_parent_id=Repository.get_by_repo_name(HG_REPO),
+ )
+ RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN)
+
+ Session().commit()
+
+ rev1 = '7d4bc8ec6be5'
+ rev2 = '56349e29c2af'
+
+ response = self.app.get(url(controller='compare', action='index',
+ repo_name=HG_REPO,
+ org_ref_type="rev",
+ org_ref=rev1,
+ other_ref_type="rev",
+ other_ref=rev2,
+ repo=HG_FORK
+ ))
+
+ try:
+ response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
+ ## outgoing changesets between those revisions
+
+ response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev1))
+ response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
+ response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
+
+ ## files
+ response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
+ response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
+ response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
+ finally:
+ RepoModel().delete(HG_FORK)
+
+ def test_compare_extra_commits(self):
+ self.log_user()
+
+ repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
+ description='diff-test',
+ owner=TEST_USER_ADMIN_LOGIN)
+
+ repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
+ description='diff-test',
+ owner=TEST_USER_ADMIN_LOGIN)
+
+ Session().commit()
+ r1_id = repo1.repo_id
+ r1_name = repo1.repo_name
+ r2_id = repo2.repo_id
+ r2_name = repo2.repo_name
+
+ #commit something !
+ cs0 = ScmModel().create_node(
+ repo=repo1.scm_instance, repo_name=r1_name,
+ cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
+ author=TEST_USER_ADMIN_LOGIN,
+ message='commit1',
+ content='line1',
+ f_path='file1'
+ )
+
+ cs0_prim = ScmModel().create_node(
+ repo=repo2.scm_instance, repo_name=r2_name,
+ cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
+ author=TEST_USER_ADMIN_LOGIN,
+ message='commit1',
+ content='line1',
+ f_path='file1'
+ )
+
+ cs1 = ScmModel().commit_change(
+ repo=repo2.scm_instance, repo_name=r2_name,
+ cs=cs0_prim, user=TEST_USER_ADMIN_LOGIN, author=TEST_USER_ADMIN_LOGIN,
+ message='commit2',
+ content='line1\nline2',
+ f_path='file1'
+ )
+
+ rev1 = 'default'
+ rev2 = 'default'
+ response = self.app.get(url(controller='compare', action='index',
+ repo_name=r2_name,
+ org_ref_type="branch",
+ org_ref=rev1,
+ other_ref_type="branch",
+ other_ref=rev2,
+ repo=r1_name
+ ))
+
+ try:
+ response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
+
+ response.mustcontain("""<div class="message">commit2</div>""")
+ response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (r2_name, cs1.raw_id, cs1.short_id))
+ ## files
+ response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (r2_name, rev1, rev2))
+
+
+ finally:
+ RepoModel().delete(r1_id)
+ RepoModel().delete(r2_id)
diff --git a/rhodecode/tests/functional/test_files.py b/rhodecode/tests/functional/test_files.py
index 15175459..6d8be091 100644
--- a/rhodecode/tests/functional/test_files.py
+++ b/rhodecode/tests/functional/test_files.py
@@ -186,6 +186,14 @@ class TestFilesController(TestController):
response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
+ def test_file_annotation_git(self):
+ self.log_user()
+ response = self.app.get(url(controller='files', action='index',
+ repo_name=GIT_REPO,
+ revision='master',
+ f_path='vcs/nodes.py',
+ annotate=True))
+
def test_archival(self):
self.log_user()
@@ -200,9 +208,10 @@ class TestFilesController(TestController):
self.assertEqual(response.status, '200 OK')
heads = [
- ('Content-Type', 'text/html; charset=utf-8'),
- ('Content-Length', '0'), ('Pragma', 'no-cache'),
- ('Cache-Control', 'no-cache')
+ ('Pragma', 'no-cache'),
+ ('Cache-Control', 'no-cache'),
+ ('Content-Disposition', 'attachment; filename=%s' % filename),
+ ('Content-Type', '%s; charset=utf-8' % info[0]),
]
self.assertEqual(response.response._headers.items(), heads)
@@ -212,7 +221,8 @@ class TestFilesController(TestController):
for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
- response = self.app.get(url(controller='files', action='archivefile',
+ response = self.app.get(url(controller='files',
+ action='archivefile',
repo_name=HG_REPO,
fname=fname))
response.mustcontain('Unknown archive type')
@@ -220,10 +230,11 @@ class TestFilesController(TestController):
def test_archival_wrong_revision(self):
self.log_user()
- for rev in ['00x000000', 'tar', 'wrong', '@##$@$424213232', '232dffcd']:
+ for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
fname = '%s.zip' % rev
- response = self.app.get(url(controller='files', action='archivefile',
+ response = self.app.get(url(controller='files',
+ action='archivefile',
repo_name=HG_REPO,
fname=fname))
response.mustcontain('Unknown revision')
diff --git a/rhodecode/tests/functional/test_forks.py b/rhodecode/tests/functional/test_forks.py
index 0cf40800..c56e0c71 100644
--- a/rhodecode/tests/functional/test_forks.py
+++ b/rhodecode/tests/functional/test_forks.py
@@ -3,6 +3,7 @@ from rhodecode.tests import *
from rhodecode.model.db import Repository
from rhodecode.model.repo import RepoModel
from rhodecode.model.user import UserModel
+from rhodecode.model.meta import Session
class TestForksController(TestController):
@@ -12,13 +13,13 @@ class TestForksController(TestController):
self.password = u'qweqwe'
self.u1 = UserModel().create_or_update(
username=self.username, password=self.password,
- email=u'fork_king@rhodecode.org', name=u'u1', lastname=u'u1'
+ email=u'fork_king@rhodecode.org', firstname=u'u1', lastname=u'u1'
)
- self.Session.commit()
+ Session().commit()
def tearDown(self):
- self.Session.delete(self.u1)
- self.Session.commit()
+ Session().delete(self.u1)
+ Session().commit()
def test_index(self):
self.log_user()
@@ -28,7 +29,21 @@ class TestForksController(TestController):
self.assertTrue("""There are no forks yet""" in response.body)
- def test_index_with_fork(self):
+ def test_no_permissions_to_fork(self):
+ usr = self.log_user(TEST_USER_REGULAR_LOGIN,
+ TEST_USER_REGULAR_PASS)['user_id']
+ user_model = UserModel()
+ user_model.revoke_perm(usr, 'hg.fork.repository')
+ user_model.grant_perm(usr, 'hg.fork.none')
+ u = UserModel().get(usr)
+ u.inherit_default_permissions = False
+ Session().commit()
+ # try create a fork
+ repo_name = HG_REPO
+ self.app.post(url(controller='forks', action='fork_create',
+ repo_name=repo_name), {}, status=403)
+
+ def test_index_with_fork_hg(self):
self.log_user()
# create a fork
@@ -39,19 +54,49 @@ class TestForksController(TestController):
response = self.app.post(url(controller='forks',
action='fork_create',
repo_name=repo_name),
- {'repo_name':fork_name,
- 'repo_group':'',
- 'fork_parent_id':org_repo.repo_id,
- 'repo_type':'hg',
- 'description':description,
- 'private':'False'})
+ {'repo_name': fork_name,
+ 'repo_group': '',
+ 'fork_parent_id': org_repo.repo_id,
+ 'repo_type': 'hg',
+ 'description': description,
+ 'private': 'False',
+ 'landing_rev': 'tip'})
response = self.app.get(url(controller='forks', action='forks',
repo_name=repo_name))
- self.assertTrue("""<a href="/%s/summary">"""
- """vcs_test_hg_fork</a>""" % fork_name
- in response.body)
+ response.mustcontain(
+ """<a href="/%s/summary">%s</a>""" % (fork_name, fork_name)
+ )
+
+ #remove this fork
+ response = self.app.delete(url('repo', repo_name=fork_name))
+
+ def test_index_with_fork_git(self):
+ self.log_user()
+
+ # create a fork
+ fork_name = GIT_FORK
+ description = 'fork of vcs test'
+ repo_name = GIT_REPO
+ org_repo = Repository.get_by_repo_name(repo_name)
+ response = self.app.post(url(controller='forks',
+ action='fork_create',
+ repo_name=repo_name),
+ {'repo_name': fork_name,
+ 'repo_group': '',
+ 'fork_parent_id': org_repo.repo_id,
+ 'repo_type': 'git',
+ 'description': description,
+ 'private': 'False',
+ 'landing_rev': 'tip'})
+
+ response = self.app.get(url(controller='forks', action='forks',
+ repo_name=repo_name))
+
+ response.mustcontain(
+ """<a href="/%s/summary">%s</a>""" % (fork_name, fork_name)
+ )
#remove this fork
response = self.app.delete(url('repo', repo_name=fork_name))
@@ -69,14 +114,15 @@ class TestForksController(TestController):
'fork_parent_id':org_repo.repo_id,
'repo_type':'hg',
'description':description,
- 'private':'False'})
+ 'private':'False',
+ 'landing_rev': 'tip'})
#test if we have a message that fork is ok
- self.assertTrue('forked %s repository as %s' \
- % (repo_name, fork_name) in response.session['flash'][0])
+ self.checkSessionFlash(response,
+ 'forked %s repository as %s' % (repo_name, fork_name))
#test if the fork was created in the database
- fork_repo = self.Session.query(Repository)\
+ fork_repo = Session().query(Repository)\
.filter(Repository.repo_name == fork_name).one()
self.assertEqual(fork_repo.repo_name, fork_name)
@@ -85,10 +131,6 @@ class TestForksController(TestController):
#test if fork is visible in the list ?
response = response.follow()
- # check if fork is marked as fork
- # wait for cache to expire
- import time
- time.sleep(10)
response = self.app.get(url(controller='summary', action='index',
repo_name=fork_name))
@@ -98,7 +140,7 @@ class TestForksController(TestController):
usr = self.log_user(self.username, self.password)['user_id']
repo_name = HG_REPO
- forks = self.Session.query(Repository)\
+ forks = Session().query(Repository)\
.filter(Repository.fork_id != None)\
.all()
self.assertEqual(1, len(forks))
@@ -107,7 +149,7 @@ class TestForksController(TestController):
RepoModel().grant_user_permission(repo=forks[0],
user=usr,
perm='repository.read')
- self.Session.commit()
+ Session().commit()
response = self.app.get(url(controller='forks', action='forks',
repo_name=repo_name))
@@ -118,7 +160,7 @@ class TestForksController(TestController):
usr = self.log_user(self.username, self.password)['user_id']
repo_name = HG_REPO
- forks = self.Session.query(Repository)\
+ forks = Session().query(Repository)\
.filter(Repository.fork_id != None)\
.all()
self.assertEqual(1, len(forks))
@@ -126,7 +168,7 @@ class TestForksController(TestController):
# set none
RepoModel().grant_user_permission(repo=forks[0],
user=usr, perm='repository.none')
- self.Session.commit()
+ Session().commit()
# fork shouldn't be there
response = self.app.get(url(controller='forks', action='forks',
repo_name=repo_name))
diff --git a/rhodecode/tests/functional/test_home.py b/rhodecode/tests/functional/test_home.py
index 48f94ac1..e52b6579 100644
--- a/rhodecode/tests/functional/test_home.py
+++ b/rhodecode/tests/functional/test_home.py
@@ -1,4 +1,7 @@
+import time
from rhodecode.tests import *
+from rhodecode.model.meta import Session
+from rhodecode.model.db import User
class TestHomeController(TestController):
@@ -18,5 +21,41 @@ class TestHomeController(TestController):
"""open.png"/>""")
response.mustcontain(
-"""<a title="Marcin Kuzminski &lt;marcin@python-works.com&gt;:\n
-merge" class="tooltip" href="/vcs_test_hg/changeset/27cd5cce30c96924232dffcd24178a07ffeb5dfc">r173:27cd5cce30c9</a>""")
+"""<a title="Marcin Kuzminski &amp;lt;marcin@python-works.com&amp;gt;:\n
+merge" class="tooltip" href="/vcs_test_hg/changeset/27cd5cce30c96924232"""
+"""dffcd24178a07ffeb5dfc">r173:27cd5cce30c9</a>"""
+)
+
+ def test_repo_summary_with_anonymous_access_disabled(self):
+ anon = User.get_by_username('default')
+ anon.active = False
+ Session().add(anon)
+ Session().commit()
+ time.sleep(1.5) # must sleep for cache (1s to expire)
+ try:
+ response = self.app.get(url(controller='summary',
+ action='index', repo_name=HG_REPO),
+ status=302)
+ assert 'login' in response.location
+
+ finally:
+ anon = User.get_by_username('default')
+ anon.active = True
+ Session().add(anon)
+ Session().commit()
+
+ def test_index_with_anonymous_access_disabled(self):
+ anon = User.get_by_username('default')
+ anon.active = False
+ Session().add(anon)
+ Session().commit()
+ time.sleep(1.5) # must sleep for cache (1s to expire)
+ try:
+ response = self.app.get(url(controller='home', action='index'),
+ status=302)
+ assert 'login' in response.location
+ finally:
+ anon = User.get_by_username('default')
+ anon.active = True
+ Session().add(anon)
+ Session().commit()
diff --git a/rhodecode/tests/functional/test_login.py b/rhodecode/tests/functional/test_login.py
index 7fb830db..2b6258eb 100644
--- a/rhodecode/tests/functional/test_login.py
+++ b/rhodecode/tests/functional/test_login.py
@@ -3,16 +3,17 @@ from rhodecode.tests import *
from rhodecode.model.db import User, Notification
from rhodecode.lib.utils2 import generate_api_key
from rhodecode.lib.auth import check_password
-from rhodecode.model.meta import Session
+from rhodecode.lib import helpers as h
+from rhodecode.model import validators
class TestLoginController(TestController):
def tearDown(self):
for n in Notification.query().all():
- Session.delete(n)
+ self.Session().delete(n)
- Session.commit()
+ self.Session().commit()
self.assertEqual(Notification.query().all(), [])
def test_index(self):
@@ -22,21 +23,21 @@ class TestLoginController(TestController):
def test_login_admin_ok(self):
response = self.app.post(url(controller='login', action='index'),
- {'username':'test_admin',
- 'password':'test12'})
+ {'username': 'test_admin',
+ 'password': 'test12'})
self.assertEqual(response.status, '302 Found')
- self.assertEqual(response.session['rhodecode_user'].get('username') ,
+ self.assertEqual(response.session['rhodecode_user'].get('username'),
'test_admin')
response = response.follow()
self.assertTrue('%s repository' % HG_REPO in response.body)
def test_login_regular_ok(self):
response = self.app.post(url(controller='login', action='index'),
- {'username':'test_regular',
- 'password':'test12'})
+ {'username': 'test_regular',
+ 'password': 'test12'})
self.assertEqual(response.status, '302 Found')
- self.assertEqual(response.session['rhodecode_user'].get('username') ,
+ self.assertEqual(response.session['rhodecode_user'].get('username'),
'test_regular')
response = response.follow()
self.assertTrue('%s repository' % HG_REPO in response.body)
@@ -46,27 +47,45 @@ class TestLoginController(TestController):
test_came_from = '/_admin/users'
response = self.app.post(url(controller='login', action='index',
came_from=test_came_from),
- {'username':'test_admin',
- 'password':'test12'})
+ {'username': 'test_admin',
+ 'password': 'test12'})
self.assertEqual(response.status, '302 Found')
response = response.follow()
self.assertEqual(response.status, '200 OK')
self.assertTrue('Users administration' in response.body)
+ @parameterized.expand([
+ ('data:text/html,<script>window.alert("xss")</script>',),
+ ('mailto:test@rhodecode.org',),
+ ('file:///etc/passwd',),
+ ('ftp://some.ftp.server',),
+ ('http://other.domain',),
+ ])
+ def test_login_bad_came_froms(self, url_came_from):
+ response = self.app.post(url(controller='login', action='index',
+ came_from=url_came_from),
+ {'username': 'test_admin',
+ 'password': 'test12'})
+ self.assertEqual(response.status, '302 Found')
+ self.assertEqual(response._environ['paste.testing_variables']
+ ['tmpl_context'].came_from, '/')
+ response = response.follow()
+
+ self.assertEqual(response.status, '200 OK')
+
def test_login_short_password(self):
response = self.app.post(url(controller='login', action='index'),
- {'username':'test_admin',
- 'password':'as'})
+ {'username': 'test_admin',
+ 'password': 'as'})
self.assertEqual(response.status, '200 OK')
self.assertTrue('Enter 3 characters or more' in response.body)
def test_login_wrong_username_password(self):
response = self.app.post(url(controller='login', action='index'),
- {'username':'error',
- 'password':'test12'})
- self.assertEqual(response.status , '200 OK')
+ {'username': 'error',
+ 'password': 'test12'})
self.assertTrue('invalid user name' in response.body)
self.assertTrue('invalid password' in response.body)
@@ -79,62 +98,63 @@ class TestLoginController(TestController):
self.assertTrue('Sign Up to RhodeCode' in response.body)
def test_register_err_same_username(self):
+ uname = 'test_admin'
response = self.app.post(url(controller='login', action='register'),
- {'username':'test_admin',
- 'password':'test12',
- 'password_confirmation':'test12',
- 'email':'goodmail@domain.com',
- 'name':'test',
- 'lastname':'test'})
+ {'username': uname,
+ 'password': 'test12',
+ 'password_confirmation': 'test12',
+ 'email': 'goodmail@domain.com',
+ 'firstname': 'test',
+ 'lastname': 'test'})
- self.assertEqual(response.status , '200 OK')
- self.assertTrue('This username already exists' in response.body)
+ msg = validators.ValidUsername()._messages['username_exists']
+ msg = h.html_escape(msg % {'username': uname})
+ response.mustcontain(msg)
def test_register_err_same_email(self):
response = self.app.post(url(controller='login', action='register'),
- {'username':'test_admin_0',
- 'password':'test12',
- 'password_confirmation':'test12',
- 'email':'test_admin@mail.com',
- 'name':'test',
- 'lastname':'test'})
+ {'username': 'test_admin_0',
+ 'password': 'test12',
+ 'password_confirmation': 'test12',
+ 'email': 'test_admin@mail.com',
+ 'firstname': 'test',
+ 'lastname': 'test'})
- self.assertEqual(response.status , '200 OK')
- response.mustcontain('This e-mail address is already taken')
+ msg = validators.UniqSystemEmail()()._messages['email_taken']
+ response.mustcontain(msg)
def test_register_err_same_email_case_sensitive(self):
response = self.app.post(url(controller='login', action='register'),
- {'username':'test_admin_1',
- 'password':'test12',
- 'password_confirmation':'test12',
- 'email':'TesT_Admin@mail.COM',
- 'name':'test',
- 'lastname':'test'})
- self.assertEqual(response.status , '200 OK')
- response.mustcontain('This e-mail address is already taken')
+ {'username': 'test_admin_1',
+ 'password': 'test12',
+ 'password_confirmation': 'test12',
+ 'email': 'TesT_Admin@mail.COM',
+ 'firstname': 'test',
+ 'lastname': 'test'})
+ msg = validators.UniqSystemEmail()()._messages['email_taken']
+ response.mustcontain(msg)
def test_register_err_wrong_data(self):
response = self.app.post(url(controller='login', action='register'),
- {'username':'xs',
- 'password':'test',
- 'password_confirmation':'test',
- 'email':'goodmailm',
- 'name':'test',
- 'lastname':'test'})
- self.assertEqual(response.status , '200 OK')
+ {'username': 'xs',
+ 'password': 'test',
+ 'password_confirmation': 'test',
+ 'email': 'goodmailm',
+ 'firstname': 'test',
+ 'lastname': 'test'})
+ self.assertEqual(response.status, '200 OK')
response.mustcontain('An email address must contain a single @')
response.mustcontain('Enter a value 6 characters long or more')
def test_register_err_username(self):
response = self.app.post(url(controller='login', action='register'),
- {'username':'error user',
- 'password':'test12',
- 'password_confirmation':'test12',
- 'email':'goodmailm',
- 'name':'test',
- 'lastname':'test'})
-
- self.assertEqual(response.status , '200 OK')
+ {'username': 'error user',
+ 'password': 'test12',
+ 'password_confirmation': 'test12',
+ 'email': 'goodmailm',
+ 'firstname': 'test',
+ 'lastname': 'test'})
+
response.mustcontain('An email address must contain a single @')
response.mustcontain('Username may only contain '
'alphanumeric characters underscores, '
@@ -142,41 +162,42 @@ class TestLoginController(TestController):
'alphanumeric character')
def test_register_err_case_sensitive(self):
+ usr = 'Test_Admin'
response = self.app.post(url(controller='login', action='register'),
- {'username':'Test_Admin',
- 'password':'test12',
- 'password_confirmation':'test12',
- 'email':'goodmailm',
- 'name':'test',
- 'lastname':'test'})
+ {'username': usr,
+ 'password': 'test12',
+ 'password_confirmation': 'test12',
+ 'email': 'goodmailm',
+ 'firstname': 'test',
+ 'lastname': 'test'})
- self.assertEqual(response.status , '200 OK')
- self.assertTrue('An email address must contain a single @' in response.body)
- self.assertTrue('This username already exists' in response.body)
+ response.mustcontain('An email address must contain a single @')
+ msg = validators.ValidUsername()._messages['username_exists']
+ msg = h.html_escape(msg % {'username': usr})
+ response.mustcontain(msg)
def test_register_special_chars(self):
response = self.app.post(url(controller='login', action='register'),
- {'username':'xxxaxn',
- 'password':'ąćźżąśśśś',
- 'password_confirmation':'ąćźżąśśśś',
- 'email':'goodmailm@test.plx',
- 'name':'test',
- 'lastname':'test'})
+ {'username': 'xxxaxn',
+ 'password': 'ąćźżąśśśś',
+ 'password_confirmation': 'ąćźżąśśśś',
+ 'email': 'goodmailm@test.plx',
+ 'firstname': 'test',
+ 'lastname': 'test'})
- self.assertEqual(response.status , '200 OK')
- self.assertTrue('Invalid characters in password' in response.body)
+ msg = validators.ValidPassword()._messages['invalid_password']
+ response.mustcontain(msg)
def test_register_password_mismatch(self):
response = self.app.post(url(controller='login', action='register'),
- {'username':'xs',
- 'password':'123qwe',
- 'password_confirmation':'qwe123',
- 'email':'goodmailm@test.plxa',
- 'name':'test',
- 'lastname':'test'})
-
- self.assertEqual(response.status, '200 OK')
- response.mustcontain('Passwords do not match')
+ {'username': 'xs',
+ 'password': '123qwe',
+ 'password_confirmation': 'qwe123',
+ 'email': 'goodmailm@test.plxa',
+ 'firstname': 'test',
+ 'lastname': 'test'})
+ msg = validators.ValidPasswordsMatch()._messages['password_mismatch']
+ response.mustcontain(msg)
def test_register_ok(self):
username = 'test_regular4'
@@ -186,17 +207,17 @@ class TestLoginController(TestController):
lastname = 'testlastname'
response = self.app.post(url(controller='login', action='register'),
- {'username':username,
- 'password':password,
- 'password_confirmation':password,
- 'email':email,
- 'name':name,
- 'lastname':lastname,
- 'admin':True}) # This should be overriden
+ {'username': username,
+ 'password': password,
+ 'password_confirmation': password,
+ 'email': email,
+ 'firstname': name,
+ 'lastname': lastname,
+ 'admin': True}) # This should be overriden
self.assertEqual(response.status, '302 Found')
self.checkSessionFlash(response, 'You have successfully registered into rhodecode')
- ret = self.Session.query(User).filter(User.username == 'test_regular4').one()
+ ret = self.Session().query(User).filter(User.username == 'test_regular4').one()
self.assertEqual(ret.username, username)
self.assertEqual(check_password(password, ret.password), True)
self.assertEqual(ret.email, email)
@@ -206,12 +227,15 @@ class TestLoginController(TestController):
self.assertEqual(ret.admin, False)
def test_forgot_password_wrong_mail(self):
+ bad_email = 'marcin@wrongmail.org'
response = self.app.post(
url(controller='login', action='password_reset'),
- {'email': 'marcin@wrongmail.org',}
+ {'email': bad_email, }
)
- response.mustcontain("This e-mail address doesn't exist")
+ msg = validators.ValidSystemEmail()._messages['non_existing_email']
+ msg = h.html_escape(msg % {'email': bad_email})
+ response.mustcontain()
def test_forgot_password(self):
response = self.app.get(url(controller='login',
@@ -231,12 +255,12 @@ class TestLoginController(TestController):
new.name = name
new.lastname = lastname
new.api_key = generate_api_key(username)
- self.Session.add(new)
- self.Session.commit()
+ self.Session().add(new)
+ self.Session().commit()
response = self.app.post(url(controller='login',
action='password_reset'),
- {'email':email, })
+ {'email': email, })
self.checkSessionFlash(response, 'Your password reset link was sent')
diff --git a/rhodecode/tests/functional/test_pullrequests.py b/rhodecode/tests/functional/test_pullrequests.py
new file mode 100644
index 00000000..cdaa180e
--- /dev/null
+++ b/rhodecode/tests/functional/test_pullrequests.py
@@ -0,0 +1,9 @@
+from rhodecode.tests import *
+
+
+class TestPullrequestsController(TestController):
+
+ def test_index(self):
+ self.log_user()
+ response = self.app.get(url(controller='pullrequests', action='index',
+ repo_name=HG_REPO))
diff --git a/rhodecode/tests/functional/test_search.py b/rhodecode/tests/functional/test_search.py
index 922f1b25..c96f2f3b 100644
--- a/rhodecode/tests/functional/test_search.py
+++ b/rhodecode/tests/functional/test_search.py
@@ -1,7 +1,8 @@
-from rhodecode.tests import *
import os
+from rhodecode.tests import *
from nose.plugins.skip import SkipTest
+
class TestSearchController(TestController):
def test_index(self):
@@ -18,20 +19,96 @@ class TestSearchController(TestController):
else:
self.log_user()
response = self.app.get(url(controller='search', action='index'),
- {'q':HG_REPO})
+ {'q': HG_REPO})
self.assertTrue('There is no index to search in. '
'Please run whoosh indexer' in response.body)
def test_normal_search(self):
self.log_user()
response = self.app.get(url(controller='search', action='index'),
- {'q':'def repo'})
- self.assertTrue('10 results' in response.body)
- self.assertTrue('Permission denied' not in response.body)
+ {'q': 'def repo'})
+ response.mustcontain('39 results')
def test_repo_search(self):
self.log_user()
response = self.app.get(url(controller='search', action='index'),
- {'q':'repository:%s def test' % HG_REPO})
- self.assertTrue('4 results' in response.body)
- self.assertTrue('Permission denied' not in response.body)
+ {'q': 'repository:%s def test' % HG_REPO})
+
+ response.mustcontain('4 results')
+
+ def test_search_last(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'last:t', 'type': 'commit'})
+
+ response.mustcontain('2 results')
+
+ def test_search_commit_message(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'bother to ask where to fetch repo during tests',
+ 'type': 'commit'})
+
+ response.mustcontain('2 results')
+ response.mustcontain('a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')
+ response.mustcontain('c6eb379775c578a95dad8ddab53f963b80894850')
+
+ def test_search_commit_message_hg_repo(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index',
+ search_repo=HG_REPO),
+ {'q': 'bother to ask where to fetch repo during tests',
+ 'type': 'commit'})
+
+ response.mustcontain('1 results')
+ response.mustcontain('a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')
+
+ def test_search_commit_changed_file(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'changed:tests/utils.py',
+ 'type': 'commit'})
+
+ response.mustcontain('20 results')
+
+ def test_search_commit_changed_files_get_commit(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'changed:vcs/utils/lazy.py',
+ 'type': 'commit'})
+
+ response.mustcontain('7 results')
+ response.mustcontain('36e0fc9d2808c5022a24f49d6658330383ed8666')
+ response.mustcontain('af182745859d779f17336241a0815d15166ae1ee')
+ response.mustcontain('17438a11f72b93f56d0e08e7d1fa79a378578a82')
+ response.mustcontain('33fa3223355104431402a888fa77a4e9956feb3e')
+ response.mustcontain('d1f898326327e20524fe22417c22d71064fe54a1')
+ response.mustcontain('fe568b4081755c12abf6ba673ba777fc02a415f3')
+ response.mustcontain('bafe786f0d8c2ff7da5c1dcfcfa577de0b5e92f1')
+
+ def test_search_commit_added_file(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'added:README.rst',
+ 'type': 'commit'})
+
+ response.mustcontain('2 results')
+ #HG
+ response.mustcontain('3803844fdbd3b711175fc3da9bdacfcd6d29a6fb')
+ #GIT
+ response.mustcontain('ff7ca51e58c505fec0dd2491de52c622bb7a806b')
+
+ def test_search_author(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'author:marcin@python-blog.com raw_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545',
+ 'type': 'commit'})
+
+ response.mustcontain('1 results')
+
+ def test_search_file_name(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'),
+ {'q': 'README.rst', 'type': 'path'})
+
+ response.mustcontain('2 results')
diff --git a/rhodecode/tests/functional/test_summary.py b/rhodecode/tests/functional/test_summary.py
index f2477f89..4d270ddc 100644
--- a/rhodecode/tests/functional/test_summary.py
+++ b/rhodecode/tests/functional/test_summary.py
@@ -15,8 +15,8 @@ class TestSummaryController(TestController):
#repo type
response.mustcontain(
"""<img style="margin-bottom:2px" class="icon" """
- """title="Mercurial repository" alt="Mercurial """
- """repository" src="/images/icons/hgicon.png"/>"""
+ """title="Mercurial repository" alt="Mercurial repository" """
+ """src="/images/icons/hgicon.png"/>"""
)
response.mustcontain(
"""<img style="margin-bottom:2px" class="icon" """
@@ -41,10 +41,33 @@ class TestSummaryController(TestController):
)
# clone url...
- response.mustcontain("""<input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/vcs_test_hg"/>""")
- response.mustcontain("""<input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_1"/>""")
+ response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % HG_REPO)
+ response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
- def test_index_by_id(self):
+ def test_index_git(self):
+ self.log_user()
+ ID = Repository.get_by_repo_name(GIT_REPO).repo_id
+ response = self.app.get(url(controller='summary',
+ action='index',
+ repo_name=GIT_REPO))
+
+ #repo type
+ response.mustcontain(
+ """<img style="margin-bottom:2px" class="icon" """
+ """title="Git repository" alt="Git repository" """
+ """src="/images/icons/giticon.png"/>"""
+ )
+ response.mustcontain(
+ """<img style="margin-bottom:2px" class="icon" """
+ """title="public repository" alt="public """
+ """repository" src="/images/icons/lock_open.png"/>"""
+ )
+
+ # clone url...
+ response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % GIT_REPO)
+ response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
+
+ def test_index_by_id_hg(self):
self.log_user()
ID = Repository.get_by_repo_name(HG_REPO).repo_id
response = self.app.get(url(controller='summary',
@@ -59,6 +82,21 @@ class TestSummaryController(TestController):
"""title="public repository" alt="public """
"""repository" src="/images/icons/lock_open.png"/>""")
+ def test_index_by_id_git(self):
+ self.log_user()
+ ID = Repository.get_by_repo_name(GIT_REPO).repo_id
+ response = self.app.get(url(controller='summary',
+ action='index',
+ repo_name='_%s' % ID))
+
+ #repo type
+ response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
+ """title="Git repository" alt="Git """
+ """repository" src="/images/icons/giticon.png"/>""")
+ response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
+ """title="public repository" alt="public """
+ """repository" src="/images/icons/lock_open.png"/>""")
+
def _enable_stats(self):
r = Repository.get_by_repo_name(HG_REPO)
r.enable_statistics = True
diff --git a/rhodecode/tests/models/__init__.py b/rhodecode/tests/models/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rhodecode/tests/models/__init__.py
diff --git a/rhodecode/tests/models/test_notifications.py b/rhodecode/tests/models/test_notifications.py
new file mode 100644
index 00000000..4b5d82de
--- /dev/null
+++ b/rhodecode/tests/models/test_notifications.py
@@ -0,0 +1,187 @@
+import os
+import unittest
+from rhodecode.tests import *
+
+from rhodecode.model.db import User, Notification, UserNotification
+from rhodecode.model.user import UserModel
+
+from rhodecode.model.meta import Session
+from rhodecode.model.notification import NotificationModel
+
+
+class TestNotifications(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ Session.remove()
+ self.u1 = UserModel().create_or_update(username=u'u1',
+ password=u'qweqwe',
+ email=u'u1@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+ Session().commit()
+ self.u1 = self.u1.user_id
+
+ self.u2 = UserModel().create_or_update(username=u'u2',
+ password=u'qweqwe',
+ email=u'u2@rhodecode.org',
+ firstname=u'u2', lastname=u'u3')
+ Session().commit()
+ self.u2 = self.u2.user_id
+
+ self.u3 = UserModel().create_or_update(username=u'u3',
+ password=u'qweqwe',
+ email=u'u3@rhodecode.org',
+ firstname=u'u3', lastname=u'u3')
+ Session().commit()
+ self.u3 = self.u3.user_id
+
+ super(TestNotifications, self).__init__(methodName=methodName)
+
+ def _clean_notifications(self):
+ for n in Notification.query().all():
+ Session().delete(n)
+
+ Session().commit()
+ self.assertEqual(Notification.query().all(), [])
+
+ def tearDown(self):
+ self._clean_notifications()
+
+ def test_create_notification(self):
+ self.assertEqual([], Notification.query().all())
+ self.assertEqual([], UserNotification.query().all())
+
+ usrs = [self.u1, self.u2]
+ notification = NotificationModel().create(created_by=self.u1,
+ subject=u'subj', body=u'hi there',
+ recipients=usrs)
+ Session().commit()
+ u1 = User.get(self.u1)
+ u2 = User.get(self.u2)
+ u3 = User.get(self.u3)
+ notifications = Notification.query().all()
+ self.assertEqual(len(notifications), 1)
+
+ self.assertEqual(notifications[0].recipients, [u1, u2])
+ self.assertEqual(notification.notification_id,
+ notifications[0].notification_id)
+
+ unotification = UserNotification.query()\
+ .filter(UserNotification.notification == notification).all()
+
+ self.assertEqual(len(unotification), len(usrs))
+ self.assertEqual(set([x.user.user_id for x in unotification]),
+ set(usrs))
+
+ def test_user_notifications(self):
+ self.assertEqual([], Notification.query().all())
+ self.assertEqual([], UserNotification.query().all())
+
+ notification1 = NotificationModel().create(created_by=self.u1,
+ subject=u'subj', body=u'hi there1',
+ recipients=[self.u3])
+ Session().commit()
+ notification2 = NotificationModel().create(created_by=self.u1,
+ subject=u'subj', body=u'hi there2',
+ recipients=[self.u3])
+ Session().commit()
+ u3 = Session().query(User).get(self.u3)
+
+ self.assertEqual(sorted([x.notification for x in u3.notifications]),
+ sorted([notification2, notification1]))
+
+ def test_delete_notifications(self):
+ self.assertEqual([], Notification.query().all())
+ self.assertEqual([], UserNotification.query().all())
+
+ notification = NotificationModel().create(created_by=self.u1,
+ subject=u'title', body=u'hi there3',
+ recipients=[self.u3, self.u1, self.u2])
+ Session().commit()
+ notifications = Notification.query().all()
+ self.assertTrue(notification in notifications)
+
+ Notification.delete(notification.notification_id)
+ Session().commit()
+
+ notifications = Notification.query().all()
+ self.assertFalse(notification in notifications)
+
+ un = UserNotification.query().filter(UserNotification.notification
+ == notification).all()
+ self.assertEqual(un, [])
+
+ def test_delete_association(self):
+
+ self.assertEqual([], Notification.query().all())
+ self.assertEqual([], UserNotification.query().all())
+
+ notification = NotificationModel().create(created_by=self.u1,
+ subject=u'title', body=u'hi there3',
+ recipients=[self.u3, self.u1, self.u2])
+ Session().commit()
+
+ unotification = UserNotification.query()\
+ .filter(UserNotification.notification ==
+ notification)\
+ .filter(UserNotification.user_id == self.u3)\
+ .scalar()
+
+ self.assertEqual(unotification.user_id, self.u3)
+
+ NotificationModel().delete(self.u3,
+ notification.notification_id)
+ Session().commit()
+
+ u3notification = UserNotification.query()\
+ .filter(UserNotification.notification ==
+ notification)\
+ .filter(UserNotification.user_id == self.u3)\
+ .scalar()
+
+ self.assertEqual(u3notification, None)
+
+ # notification object is still there
+ self.assertEqual(Notification.query().all(), [notification])
+
+ #u1 and u2 still have assignments
+ u1notification = UserNotification.query()\
+ .filter(UserNotification.notification ==
+ notification)\
+ .filter(UserNotification.user_id == self.u1)\
+ .scalar()
+ self.assertNotEqual(u1notification, None)
+ u2notification = UserNotification.query()\
+ .filter(UserNotification.notification ==
+ notification)\
+ .filter(UserNotification.user_id == self.u2)\
+ .scalar()
+ self.assertNotEqual(u2notification, None)
+
+ def test_notification_counter(self):
+ self._clean_notifications()
+ self.assertEqual([], Notification.query().all())
+ self.assertEqual([], UserNotification.query().all())
+
+ NotificationModel().create(created_by=self.u1,
+ subject=u'title', body=u'hi there_delete',
+ recipients=[self.u3, self.u1])
+ Session().commit()
+
+ self.assertEqual(NotificationModel()
+ .get_unread_cnt_for_user(self.u1), 1)
+ self.assertEqual(NotificationModel()
+ .get_unread_cnt_for_user(self.u2), 0)
+ self.assertEqual(NotificationModel()
+ .get_unread_cnt_for_user(self.u3), 1)
+
+ notification = NotificationModel().create(created_by=self.u1,
+ subject=u'title', body=u'hi there3',
+ recipients=[self.u3, self.u1, self.u2])
+ Session().commit()
+
+ self.assertEqual(NotificationModel()
+ .get_unread_cnt_for_user(self.u1), 2)
+ self.assertEqual(NotificationModel()
+ .get_unread_cnt_for_user(self.u2), 1)
+ self.assertEqual(NotificationModel()
+ .get_unread_cnt_for_user(self.u3), 2)
diff --git a/rhodecode/tests/models/test_permissions.py b/rhodecode/tests/models/test_permissions.py
new file mode 100644
index 00000000..a2566982
--- /dev/null
+++ b/rhodecode/tests/models/test_permissions.py
@@ -0,0 +1,438 @@
+import os
+import unittest
+from rhodecode.tests import *
+
+from rhodecode.model.repos_group import ReposGroupModel
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
+from rhodecode.model.user import UserModel
+
+from rhodecode.model.meta import Session
+from rhodecode.model.users_group import UsersGroupModel
+from rhodecode.lib.auth import AuthUser
+
+
+def _make_group(path, desc='desc', parent_id=None,
+ skip_if_exists=False):
+
+ gr = RepoGroup.get_by_group_name(path)
+ if gr and skip_if_exists:
+ return gr
+
+ gr = ReposGroupModel().create(path, desc, parent_id)
+ return gr
+
+
+class TestPermissions(unittest.TestCase):
+ def __init__(self, methodName='runTest'):
+ super(TestPermissions, self).__init__(methodName=methodName)
+
+ def setUp(self):
+ self.u1 = UserModel().create_or_update(
+ username=u'u1', password=u'qweqwe',
+ email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1'
+ )
+ self.u2 = UserModel().create_or_update(
+ username=u'u2', password=u'qweqwe',
+ email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2'
+ )
+ self.u3 = UserModel().create_or_update(
+ username=u'u3', password=u'qweqwe',
+ email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3'
+ )
+ self.anon = User.get_by_username('default')
+ self.a1 = UserModel().create_or_update(
+ username=u'a1', password=u'qweqwe',
+ email=u'a1@rhodecode.org', firstname=u'a1', lastname=u'a1', admin=True
+ )
+ Session().commit()
+
+ def tearDown(self):
+ if hasattr(self, 'test_repo'):
+ RepoModel().delete(repo=self.test_repo)
+ UserModel().delete(self.u1)
+ UserModel().delete(self.u2)
+ UserModel().delete(self.u3)
+ UserModel().delete(self.a1)
+ if hasattr(self, 'g1'):
+ ReposGroupModel().delete(self.g1.group_id)
+ if hasattr(self, 'g2'):
+ ReposGroupModel().delete(self.g2.group_id)
+
+ if hasattr(self, 'ug1'):
+ UsersGroupModel().delete(self.ug1, force=True)
+
+ Session().commit()
+
+ def test_default_perms_set(self):
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ perms = {
+ 'repositories_groups': {},
+ 'global': set([u'hg.create.repository', u'repository.read',
+ u'hg.register.manual_activate']),
+ 'repositories': {u'vcs_test_hg': u'repository.read'}
+ }
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ perms['repositories'][HG_REPO])
+ new_perm = 'repository.write'
+ RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
+ perm=new_perm)
+ Session().commit()
+
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ new_perm)
+
+ def test_default_admin_perms_set(self):
+ a1_auth = AuthUser(user_id=self.a1.user_id)
+ perms = {
+ 'repositories_groups': {},
+ 'global': set([u'hg.admin']),
+ 'repositories': {u'vcs_test_hg': u'repository.admin'}
+ }
+ self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
+ perms['repositories'][HG_REPO])
+ new_perm = 'repository.write'
+ RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1,
+ perm=new_perm)
+ Session().commit()
+ # cannot really downgrade admins permissions !? they still get's set as
+ # admin !
+ u1_auth = AuthUser(user_id=self.a1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ perms['repositories'][HG_REPO])
+
+ def test_default_group_perms(self):
+ self.g1 = _make_group('test1', skip_if_exists=True)
+ self.g2 = _make_group('test2', skip_if_exists=True)
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ perms = {
+ 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
+ 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
+ 'repositories': {u'vcs_test_hg': u'repository.read'}
+ }
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ perms['repositories'][HG_REPO])
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ perms['repositories_groups'])
+
+ def test_default_admin_group_perms(self):
+ self.g1 = _make_group('test1', skip_if_exists=True)
+ self.g2 = _make_group('test2', skip_if_exists=True)
+ a1_auth = AuthUser(user_id=self.a1.user_id)
+ perms = {
+ 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
+ 'global': set(['hg.admin']),
+ 'repositories': {u'vcs_test_hg': 'repository.admin'}
+ }
+
+ self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
+ perms['repositories'][HG_REPO])
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ perms['repositories_groups'])
+
+ def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
+ # make group
+ self.ug1 = UsersGroupModel().create('G1')
+ # add user to group
+
+ UsersGroupModel().add_user_to_group(self.ug1, self.u1)
+
+ # set permission to lower
+ new_perm = 'repository.none'
+ RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
+ Session().commit()
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ new_perm)
+
+ # grant perm for group this should not override permission from user
+ # since it has explicitly set
+ new_perm_gr = 'repository.write'
+ RepoModel().grant_users_group_permission(repo=HG_REPO,
+ group_name=self.ug1,
+ perm=new_perm_gr)
+ # check perms
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ perms = {
+ 'repositories_groups': {},
+ 'global': set([u'hg.create.repository', u'repository.read',
+ u'hg.register.manual_activate']),
+ 'repositories': {u'vcs_test_hg': u'repository.read'}
+ }
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ new_perm)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ perms['repositories_groups'])
+
+ def test_propagated_permission_from_users_group(self):
+ # make group
+ self.ug1 = UsersGroupModel().create('G1')
+ # add user to group
+
+ UsersGroupModel().add_user_to_group(self.ug1, self.u3)
+
+ # grant perm for group this should override default permission from user
+ new_perm_gr = 'repository.write'
+ RepoModel().grant_users_group_permission(repo=HG_REPO,
+ group_name=self.ug1,
+ perm=new_perm_gr)
+ # check perms
+ u3_auth = AuthUser(user_id=self.u3.user_id)
+ perms = {
+ 'repositories_groups': {},
+ 'global': set([u'hg.create.repository', u'repository.read',
+ u'hg.register.manual_activate']),
+ 'repositories': {u'vcs_test_hg': u'repository.read'}
+ }
+ self.assertEqual(u3_auth.permissions['repositories'][HG_REPO],
+ new_perm_gr)
+ self.assertEqual(u3_auth.permissions['repositories_groups'],
+ perms['repositories_groups'])
+
+ def test_propagated_permission_from_users_group_lower_weight(self):
+ # make group
+ self.ug1 = UsersGroupModel().create('G1')
+ # add user to group
+ UsersGroupModel().add_user_to_group(self.ug1, self.u1)
+
+ # set permission to lower
+ new_perm_h = 'repository.write'
+ RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
+ perm=new_perm_h)
+ Session().commit()
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ new_perm_h)
+
+ # grant perm for group this should NOT override permission from user
+ # since it's lower than granted
+ new_perm_l = 'repository.read'
+ RepoModel().grant_users_group_permission(repo=HG_REPO,
+ group_name=self.ug1,
+ perm=new_perm_l)
+ # check perms
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ perms = {
+ 'repositories_groups': {},
+ 'global': set([u'hg.create.repository', u'repository.read',
+ u'hg.register.manual_activate']),
+ 'repositories': {u'vcs_test_hg': u'repository.write'}
+ }
+ self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
+ new_perm_h)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ perms['repositories_groups'])
+
+ def test_repo_in_group_permissions(self):
+ self.g1 = _make_group('group1', skip_if_exists=True)
+ self.g2 = _make_group('group2', skip_if_exists=True)
+ Session().commit()
+ # both perms should be read !
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.read', u'group2': u'group.read'})
+
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.read', u'group2': u'group.read'})
+
+ #Change perms to none for both groups
+ ReposGroupModel().grant_user_permission(repos_group=self.g1,
+ user=self.anon,
+ perm='group.none')
+ ReposGroupModel().grant_user_permission(repos_group=self.g2,
+ user=self.anon,
+ perm='group.none')
+
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none', u'group2': u'group.none'})
+
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none', u'group2': u'group.none'})
+
+ # add repo to group
+ name = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm'])
+ self.test_repo = RepoModel().create_repo(
+ repo_name=name,
+ repo_type='hg',
+ description='',
+ repos_group=self.g1,
+ owner=self.u1,
+ )
+ Session().commit()
+
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none', u'group2': u'group.none'})
+
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none', u'group2': u'group.none'})
+
+ #grant permission for u2 !
+ ReposGroupModel().grant_user_permission(repos_group=self.g1,
+ user=self.u2,
+ perm='group.read')
+ ReposGroupModel().grant_user_permission(repos_group=self.g2,
+ user=self.u2,
+ perm='group.read')
+ Session().commit()
+ self.assertNotEqual(self.u1, self.u2)
+ #u1 and anon should have not change perms while u2 should !
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none', u'group2': u'group.none'})
+
+ u2_auth = AuthUser(user_id=self.u2.user_id)
+ self.assertEqual(u2_auth.permissions['repositories_groups'],
+ {u'group1': u'group.read', u'group2': u'group.read'})
+
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none', u'group2': u'group.none'})
+
+ def test_repo_group_user_as_user_group_member(self):
+ # create Group1
+ self.g1 = _make_group('group1', skip_if_exists=True)
+ Session().commit()
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.read'})
+
+ # set default permission to none
+ ReposGroupModel().grant_user_permission(repos_group=self.g1,
+ user=self.anon,
+ perm='group.none')
+ # make group
+ self.ug1 = UsersGroupModel().create('G1')
+ # add user to group
+ UsersGroupModel().add_user_to_group(self.ug1, self.u1)
+ Session().commit()
+
+ # check if user is in the group
+ membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
+ self.assertEqual(membrs, [self.u1.user_id])
+ # add some user to that group
+
+ # check his permissions
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none'})
+
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none'})
+
+ # grant ug1 read permissions for
+ ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
+ group_name=self.ug1,
+ perm='group.read')
+ Session().commit()
+ # check if the
+ obj = Session().query(UsersGroupRepoGroupToPerm)\
+ .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
+ .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
+ .scalar()
+ self.assertEqual(obj.permission.permission_name, 'group.read')
+
+ a1_auth = AuthUser(user_id=self.anon.user_id)
+
+ self.assertEqual(a1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.none'})
+
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ self.assertEqual(u1_auth.permissions['repositories_groups'],
+ {u'group1': u'group.read'})
+
+ def test_inherited_permissions_from_default_on_user_enabled(self):
+ user_model = UserModel()
+ # enable fork and create on default user
+ usr = 'default'
+ user_model.revoke_perm(usr, 'hg.create.none')
+ user_model.grant_perm(usr, 'hg.create.repository')
+ user_model.revoke_perm(usr, 'hg.fork.none')
+ user_model.grant_perm(usr, 'hg.fork.repository')
+ # make sure inherit flag is turned on
+ self.u1.inherit_default_permissions = True
+ Session().commit()
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ # this user will have inherited permissions from default user
+ self.assertEqual(u1_auth.permissions['global'],
+ set(['hg.create.repository', 'hg.fork.repository',
+ 'hg.register.manual_activate',
+ 'repository.read']))
+
+ def test_inherited_permissions_from_default_on_user_disabled(self):
+ user_model = UserModel()
+ # disable fork and create on default user
+ usr = 'default'
+ user_model.revoke_perm(usr, 'hg.create.repository')
+ user_model.grant_perm(usr, 'hg.create.none')
+ user_model.revoke_perm(usr, 'hg.fork.repository')
+ user_model.grant_perm(usr, 'hg.fork.none')
+ # make sure inherit flag is turned on
+ self.u1.inherit_default_permissions = True
+ Session().commit()
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ # this user will have inherited permissions from default user
+ self.assertEqual(u1_auth.permissions['global'],
+ set(['hg.create.none', 'hg.fork.none',
+ 'hg.register.manual_activate',
+ 'repository.read']))
+
+ def test_non_inherited_permissions_from_default_on_user_enabled(self):
+ user_model = UserModel()
+ # enable fork and create on default user
+ usr = 'default'
+ user_model.revoke_perm(usr, 'hg.create.none')
+ user_model.grant_perm(usr, 'hg.create.repository')
+ user_model.revoke_perm(usr, 'hg.fork.none')
+ user_model.grant_perm(usr, 'hg.fork.repository')
+
+ #disable global perms on specific user
+ user_model.revoke_perm(self.u1, 'hg.create.repository')
+ user_model.grant_perm(self.u1, 'hg.create.none')
+ user_model.revoke_perm(self.u1, 'hg.fork.repository')
+ user_model.grant_perm(self.u1, 'hg.fork.none')
+
+ # make sure inherit flag is turned off
+ self.u1.inherit_default_permissions = False
+ Session().commit()
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ # this user will have non inherited permissions from he's
+ # explicitly set permissions
+ self.assertEqual(u1_auth.permissions['global'],
+ set(['hg.create.none', 'hg.fork.none',
+ 'hg.register.manual_activate',
+ 'repository.read']))
+
+ def test_non_inherited_permissions_from_default_on_user_disabled(self):
+ user_model = UserModel()
+ # disable fork and create on default user
+ usr = 'default'
+ user_model.revoke_perm(usr, 'hg.create.repository')
+ user_model.grant_perm(usr, 'hg.create.none')
+ user_model.revoke_perm(usr, 'hg.fork.repository')
+ user_model.grant_perm(usr, 'hg.fork.none')
+
+ #enable global perms on specific user
+ user_model.revoke_perm(self.u1, 'hg.create.none')
+ user_model.grant_perm(self.u1, 'hg.create.repository')
+ user_model.revoke_perm(self.u1, 'hg.fork.none')
+ user_model.grant_perm(self.u1, 'hg.fork.repository')
+
+ # make sure inherit flag is turned off
+ self.u1.inherit_default_permissions = False
+ Session().commit()
+ u1_auth = AuthUser(user_id=self.u1.user_id)
+ # this user will have non inherited permissions from he's
+ # explicitly set permissions
+ self.assertEqual(u1_auth.permissions['global'],
+ set(['hg.create.repository', 'hg.fork.repository',
+ 'hg.register.manual_activate',
+ 'repository.read']))
+
diff --git a/rhodecode/tests/models/test_repos_groups.py b/rhodecode/tests/models/test_repos_groups.py
new file mode 100644
index 00000000..500cbd1a
--- /dev/null
+++ b/rhodecode/tests/models/test_repos_groups.py
@@ -0,0 +1,172 @@
+import os
+import unittest
+from rhodecode.tests import *
+
+from rhodecode.model.repos_group import ReposGroupModel
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.db import RepoGroup, User
+from rhodecode.model.meta import Session
+from sqlalchemy.exc import IntegrityError
+
+
+def _make_group(path, desc='desc', parent_id=None,
+ skip_if_exists=False):
+
+ gr = RepoGroup.get_by_group_name(path)
+ if gr and skip_if_exists:
+ return gr
+
+ gr = ReposGroupModel().create(path, desc, parent_id)
+ return gr
+
+
+class TestReposGroups(unittest.TestCase):
+
+ def setUp(self):
+ self.g1 = _make_group('test1', skip_if_exists=True)
+ Session().commit()
+ self.g2 = _make_group('test2', skip_if_exists=True)
+ Session().commit()
+ self.g3 = _make_group('test3', skip_if_exists=True)
+ Session().commit()
+
+ def tearDown(self):
+ print 'out'
+
+ def __check_path(self, *path):
+ """
+ Checks the path for existance !
+ """
+ path = [TESTS_TMP_PATH] + list(path)
+ path = os.path.join(*path)
+ return os.path.isdir(path)
+
+ def _check_folders(self):
+ print os.listdir(TESTS_TMP_PATH)
+
+ def __delete_group(self, id_):
+ ReposGroupModel().delete(id_)
+
+ def __update_group(self, id_, path, desc='desc', parent_id=None):
+ form_data = dict(
+ group_name=path,
+ group_description=desc,
+ group_parent_id=parent_id,
+ perms_updates=[],
+ perms_new=[],
+ enable_locking=False
+ )
+ gr = ReposGroupModel().update(id_, form_data)
+ return gr
+
+ def test_create_group(self):
+ g = _make_group('newGroup')
+ self.assertEqual(g.full_path, 'newGroup')
+
+ self.assertTrue(self.__check_path('newGroup'))
+
+ def test_create_same_name_group(self):
+ self.assertRaises(IntegrityError, lambda: _make_group('newGroup'))
+ Session().rollback()
+
+ def test_same_subgroup(self):
+ sg1 = _make_group('sub1', parent_id=self.g1.group_id)
+ self.assertEqual(sg1.parent_group, self.g1)
+ self.assertEqual(sg1.full_path, 'test1/sub1')
+ self.assertTrue(self.__check_path('test1', 'sub1'))
+
+ ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
+ self.assertEqual(ssg1.parent_group, sg1)
+ self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
+ self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
+
+ def test_remove_group(self):
+ sg1 = _make_group('deleteme')
+ self.__delete_group(sg1.group_id)
+
+ self.assertEqual(RepoGroup.get(sg1.group_id), None)
+ self.assertFalse(self.__check_path('deteteme'))
+
+ sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
+ self.__delete_group(sg1.group_id)
+
+ self.assertEqual(RepoGroup.get(sg1.group_id), None)
+ self.assertFalse(self.__check_path('test1', 'deteteme'))
+
+ def test_rename_single_group(self):
+ sg1 = _make_group('initial')
+
+ new_sg1 = self.__update_group(sg1.group_id, 'after')
+ self.assertTrue(self.__check_path('after'))
+ self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
+
+ def test_update_group_parent(self):
+
+ sg1 = _make_group('initial', parent_id=self.g1.group_id)
+
+ new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
+ self.assertTrue(self.__check_path('test1', 'after'))
+ self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
+
+ new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
+ self.assertTrue(self.__check_path('test3', 'after'))
+ self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
+
+ new_sg1 = self.__update_group(sg1.group_id, 'hello')
+ self.assertTrue(self.__check_path('hello'))
+
+ self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
+
+ def test_subgrouping_with_repo(self):
+
+ g1 = _make_group('g1')
+ g2 = _make_group('g2')
+
+ # create new repo
+ form_data = dict(repo_name='john',
+ repo_name_full='john',
+ fork_name=None,
+ description=None,
+ repo_group=None,
+ private=False,
+ repo_type='hg',
+ clone_uri=None,
+ landing_rev='tip',
+ enable_locking=False)
+ cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
+ r = RepoModel().create(form_data, cur_user)
+
+ self.assertEqual(r.repo_name, 'john')
+
+ # put repo into group
+ form_data = form_data
+ form_data['repo_group'] = g1.group_id
+ form_data['perms_new'] = []
+ form_data['perms_updates'] = []
+ RepoModel().update(r.repo_name, form_data)
+ self.assertEqual(r.repo_name, 'g1/john')
+
+ self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
+ self.assertTrue(self.__check_path('g2', 'g1'))
+
+ # test repo
+ self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1',
+ r.just_name]))
+
+ def test_move_to_root(self):
+ g1 = _make_group('t11')
+ Session().commit()
+ g2 = _make_group('t22', parent_id=g1.group_id)
+ Session().commit()
+
+ self.assertEqual(g2.full_path, 't11/t22')
+ self.assertTrue(self.__check_path('t11', 't22'))
+
+ g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
+ Session().commit()
+
+ self.assertEqual(g2.group_name, 'g22')
+ # we moved out group from t1 to '' so it's full path should be 'g2'
+ self.assertEqual(g2.full_path, 'g22')
+ self.assertFalse(self.__check_path('t11', 't22'))
+ self.assertTrue(self.__check_path('g22'))
diff --git a/rhodecode/tests/models/test_users.py b/rhodecode/tests/models/test_users.py
new file mode 100644
index 00000000..04868f88
--- /dev/null
+++ b/rhodecode/tests/models/test_users.py
@@ -0,0 +1,124 @@
+import unittest
+from rhodecode.tests import *
+
+from rhodecode.model.db import User, UsersGroup, UsersGroupMember, UserEmailMap,\
+ Permission
+from rhodecode.model.user import UserModel
+
+from rhodecode.model.meta import Session
+from rhodecode.model.users_group import UsersGroupModel
+
+
+class TestUser(unittest.TestCase):
+ def __init__(self, methodName='runTest'):
+ Session.remove()
+ super(TestUser, self).__init__(methodName=methodName)
+
+ def test_create_and_remove(self):
+ usr = UserModel().create_or_update(username=u'test_user',
+ password=u'qweqwe',
+ email=u'u232@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+ Session().commit()
+ self.assertEqual(User.get_by_username(u'test_user'), usr)
+
+ # make users group
+ users_group = UsersGroupModel().create('some_example_group')
+ Session().commit()
+
+ UsersGroupModel().add_user_to_group(users_group, usr)
+ Session().commit()
+
+ self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
+ self.assertEqual(UsersGroupMember.query().count(), 1)
+ UserModel().delete(usr.user_id)
+ Session().commit()
+
+ self.assertEqual(UsersGroupMember.query().all(), [])
+
+ def test_additonal_email_as_main(self):
+ usr = UserModel().create_or_update(username=u'test_user',
+ password=u'qweqwe',
+ email=u'main_email@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+ Session().commit()
+
+ def do():
+ m = UserEmailMap()
+ m.email = u'main_email@rhodecode.org'
+ m.user = usr
+ Session().add(m)
+ Session().commit()
+ self.assertRaises(AttributeError, do)
+
+ UserModel().delete(usr.user_id)
+ Session().commit()
+
+ def test_extra_email_map(self):
+ usr = UserModel().create_or_update(username=u'test_user',
+ password=u'qweqwe',
+ email=u'main_email@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+ Session().commit()
+
+ m = UserEmailMap()
+ m.email = u'main_email2@rhodecode.org'
+ m.user = usr
+ Session().add(m)
+ Session().commit()
+
+ u = User.get_by_email(email='main_email@rhodecode.org')
+ self.assertEqual(usr.user_id, u.user_id)
+ self.assertEqual(usr.username, u.username)
+
+ u = User.get_by_email(email='main_email2@rhodecode.org')
+ self.assertEqual(usr.user_id, u.user_id)
+ self.assertEqual(usr.username, u.username)
+ u = User.get_by_email(email='main_email3@rhodecode.org')
+ self.assertEqual(None, u)
+
+ UserModel().delete(usr.user_id)
+ Session().commit()
+
+
+class TestUsers(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ super(TestUsers, self).__init__(methodName=methodName)
+
+ def setUp(self):
+ self.u1 = UserModel().create_or_update(username=u'u1',
+ password=u'qweqwe',
+ email=u'u1@rhodecode.org',
+ firstname=u'u1', lastname=u'u1')
+
+ def tearDown(self):
+ perm = Permission.query().all()
+ for p in perm:
+ UserModel().revoke_perm(self.u1, p)
+
+ UserModel().delete(self.u1)
+ Session().commit()
+
+ def test_add_perm(self):
+ perm = Permission.query().all()[0]
+ UserModel().grant_perm(self.u1, perm)
+ Session().commit()
+ self.assertEqual(UserModel().has_perm(self.u1, perm), True)
+
+ def test_has_perm(self):
+ perm = Permission.query().all()
+ for p in perm:
+ has_p = UserModel().has_perm(self.u1, p)
+ self.assertEqual(False, has_p)
+
+ def test_revoke_perm(self):
+ perm = Permission.query().all()[0]
+ UserModel().grant_perm(self.u1, perm)
+ Session().commit()
+ self.assertEqual(UserModel().has_perm(self.u1, perm), True)
+
+ #revoke
+ UserModel().revoke_perm(self.u1, perm)
+ Session().commit()
+ self.assertEqual(UserModel().has_perm(self.u1, perm), False)
diff --git a/rhodecode/tests/nose_parametrized.py b/rhodecode/tests/nose_parametrized.py
new file mode 100644
index 00000000..d06f45aa
--- /dev/null
+++ b/rhodecode/tests/nose_parametrized.py
@@ -0,0 +1,238 @@
+import re
+import new
+import inspect
+import logging
+import logging.handlers
+from functools import wraps
+
+from nose.tools import nottest
+from unittest import TestCase
+
+
+def _terrible_magic_get_defining_classes():
+ """ Returns the set of parent classes of the class currently being defined.
+ Will likely only work if called from the ``parameterized`` decorator.
+ This function is entirely @brandon_rhodes's fault, as he suggested
+ the implementation: http://stackoverflow.com/a/8793684/71522
+ """
+ stack = inspect.stack()
+ if len(stack) <= 4:
+ return []
+ frame = stack[3]
+ code_context = frame[4][0].strip()
+ if not code_context.startswith("class "):
+ return []
+ _, parents = code_context.split("(", 1)
+ parents, _ = parents.rsplit(")", 1)
+ return eval("[" + parents + "]", frame[0].f_globals, frame[0].f_locals)
+
+
+def parameterized(input):
+ """ Parameterize a test case:
+ >>> add1_tests = [(1, 2), (2, 3)]
+ >>> class TestFoo(object):
+ ... @parameterized(add1_tests)
+ ... def test_add1(self, input, expected):
+ ... assert_equal(add1(input), expected)
+ >>> @parameterized(add1_tests)
+ ... def test_add1(input, expected):
+ ... assert_equal(add1(input), expected)
+ >>>
+ """
+
+ if not hasattr(input, "__iter__"):
+ raise ValueError("expected iterable input; got %r" % (input,))
+
+ def parameterized_helper(f):
+ attached_instance_method = [False]
+
+ parent_classes = _terrible_magic_get_defining_classes()
+ if any(issubclass(cls, TestCase) for cls in parent_classes):
+ raise Exception("Warning: '@parameterized' tests won't work "
+ "inside subclasses of 'TestCase' - use "
+ "'@parameterized.expand' instead")
+
+ @wraps(f)
+ def parameterized_helper_method(self=None):
+ if self is not None and not attached_instance_method[0]:
+ # confusingly, we need to create a named instance method and
+ # attach that to the class...
+ cls = self.__class__
+ im_f = new.instancemethod(f, None, cls)
+ setattr(cls, f.__name__, im_f)
+ attached_instance_method[0] = True
+ for args in input:
+ if isinstance(args, basestring):
+ args = [args]
+ # ... then pull that named instance method off, turning it into
+ # a bound method ...
+ if self is not None:
+ args = [getattr(self, f.__name__)] + list(args)
+ else:
+ args = [f] + list(args)
+ # ... then yield that as a tuple. If those steps aren't
+ # followed precicely, Nose gets upset and doesn't run the test
+ # or doesn't run setup methods.
+ yield tuple(args)
+
+ f.__name__ = "_helper_for_%s" % (f.__name__,)
+ parameterized_helper_method.parameterized_input = input
+ parameterized_helper_method.parameterized_func = f
+ return parameterized_helper_method
+
+ return parameterized_helper
+
+
+def to_safe_name(s):
+ return re.sub("[^a-zA-Z0-9_]", "", s)
+
+
+def parameterized_expand_helper(func_name, func, args):
+ def parameterized_expand_helper_helper(self=()):
+ if self != ():
+ self = (self,)
+ return func(*(self + args))
+ parameterized_expand_helper_helper.__name__ = func_name
+ return parameterized_expand_helper_helper
+
+
+def parameterized_expand(input):
+ """ A "brute force" method of parameterizing test cases. Creates new test
+ cases and injects them into the namespace that the wrapped function
+ is being defined in. Useful for parameterizing tests in subclasses
+ of 'UnitTest', where Nose test generators don't work.
+
+ >>> @parameterized.expand([("foo", 1, 2)])
+ ... def test_add1(name, input, expected):
+ ... actual = add1(input)
+ ... assert_equal(actual, expected)
+ ...
+ >>> locals()
+ ... 'test_add1_foo_0': <function ...> ...
+ >>>
+ """
+
+ def parameterized_expand_wrapper(f):
+ stack = inspect.stack()
+ frame = stack[1]
+ frame_locals = frame[0].f_locals
+
+ base_name = f.__name__
+ for num, args in enumerate(input):
+ name_suffix = "_%s" % (num,)
+ if len(args) > 0 and isinstance(args[0], basestring):
+ name_suffix += "_" + to_safe_name(args[0])
+ name = base_name + name_suffix
+ new_func = parameterized_expand_helper(name, f, args)
+ frame_locals[name] = new_func
+ return nottest(f)
+ return parameterized_expand_wrapper
+
+parameterized.expand = parameterized_expand
+
+
+def assert_contains(haystack, needle):
+ if needle not in haystack:
+ raise AssertionError("%r not in %r" % (needle, haystack))
+
+
+def assert_not_contains(haystack, needle):
+ if needle in haystack:
+ raise AssertionError("%r in %r" % (needle, haystack))
+
+
+def imported_from_test():
+ """ Returns true if it looks like this module is being imported by unittest
+ or nose. """
+ import re
+ import inspect
+ nose_re = re.compile(r"\bnose\b")
+ unittest_re = re.compile(r"\bunittest2?\b")
+ for frame in inspect.stack():
+ file = frame[1]
+ if nose_re.search(file) or unittest_re.search(file):
+ return True
+ return False
+
+
+def assert_raises(func, exc_type, str_contains=None, repr_contains=None):
+ try:
+ func()
+ except exc_type, e:
+ if str_contains is not None and str_contains not in str(e):
+ raise AssertionError("%s raised, but %r does not contain %r"
+ % (exc_type, str(e), str_contains))
+ if repr_contains is not None and repr_contains not in repr(e):
+ raise AssertionError("%s raised, but %r does not contain %r"
+ % (exc_type, repr(e), repr_contains))
+ return e
+ else:
+ raise AssertionError("%s not raised" % (exc_type,))
+
+
+log_handler = None
+
+
+def setup_logging():
+ """ Configures a log handler which will capure log messages during a test.
+ The ``logged_messages`` and ``assert_no_errors_logged`` functions can be
+ used to make assertions about these logged messages.
+
+ For example::
+
+ from ensi_common.testing import (
+ setup_logging, teardown_logging, assert_no_errors_logged,
+ assert_logged,
+ )
+
+ class TestWidget(object):
+ def setup(self):
+ setup_logging()
+
+ def teardown(self):
+ assert_no_errors_logged()
+ teardown_logging()
+
+ def test_that_will_fail(self):
+ log.warning("this warning message will trigger a failure")
+
+ def test_that_will_pass(self):
+ log.info("but info messages are ok")
+ assert_logged("info messages are ok")
+ """
+
+ global log_handler
+ if log_handler is not None:
+ logging.getLogger().removeHandler(log_handler)
+ log_handler = logging.handlers.BufferingHandler(1000)
+ formatter = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
+ log_handler.setFormatter(formatter)
+ logging.getLogger().addHandler(log_handler)
+
+
+def teardown_logging():
+ global log_handler
+ if log_handler is not None:
+ logging.getLogger().removeHandler(log_handler)
+ log_handler = None
+
+
+def logged_messages():
+ assert log_handler, "setup_logging not called"
+ return [(log_handler.format(record), record) for record in log_handler.buffer]
+
+
+def assert_no_errors_logged():
+ for _, record in logged_messages():
+ if record.levelno >= logging.WARNING:
+ # Assume that the nose log capture plugin is being used, so it will
+ # show the exception.
+ raise AssertionError("an unexpected error was logged")
+
+
+def assert_logged(expected_msg_contents):
+ for msg, _ in logged_messages():
+ if expected_msg_contents in msg:
+ return
+ raise AssertionError("no logged message contains %r"
+ % (expected_msg_contents,))
diff --git a/rhodecode/tests/scripts/create_rc.sh b/rhodecode/tests/scripts/create_rc.sh
new file mode 100755
index 00000000..197589c7
--- /dev/null
+++ b/rhodecode/tests/scripts/create_rc.sh
@@ -0,0 +1,12 @@
+psql -U postgres -h localhost -c 'drop database if exists rhodecode;'
+psql -U postgres -h localhost -c 'create database rhodecode;'
+paster setup-rhodecode rc.ini -q --user=marcink --password=qweqwe --email=marcin@python-blog.com --repos=/home/marcink/repos
+API_KEY=`psql -R " " -A -U postgres -h localhost -c "select api_key from users where admin=TRUE" -d rhodecode | awk '{print $2}'`
+echo "run those after running server"
+echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo1 password:qweqwe email:demo1@rhodecode.org"
+echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo2 password:qweqwe email:demo2@rhodecode.org"
+echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo3 password:qweqwe email:demo3@rhodecode.org"
+echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_users_group group_name:demo12"
+echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo1"
+echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo2"
+
diff --git a/rhodecode/tests/mem_watch b/rhodecode/tests/scripts/mem_watch
index 81f65557..81f65557 100755
--- a/rhodecode/tests/mem_watch
+++ b/rhodecode/tests/scripts/mem_watch
diff --git a/rhodecode/tests/_test_concurency.py b/rhodecode/tests/scripts/test_concurency.py
index dd2686a0..b75c3133 100644
--- a/rhodecode/tests/_test_concurency.py
+++ b/rhodecode/tests/scripts/test_concurency.py
@@ -79,6 +79,7 @@ class Command(object):
print stdout, stderr
return stdout, stderr
+
def get_session():
engine = engine_from_config(conf, 'sqlalchemy.db1.')
init_model(engine)
@@ -124,7 +125,6 @@ def create_test_repo(force=True):
if user is None:
raise Exception('user not found')
-
repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
if repo is None:
@@ -140,6 +140,7 @@ def create_test_repo(force=True):
print 'done'
+
def set_anonymous_access(enable=True):
sa = get_session()
user = sa.query(User).filter(User.username == 'default').one()
@@ -147,6 +148,7 @@ def set_anonymous_access(enable=True):
sa.add(user)
sa.commit()
+
def get_anonymous_access():
sa = get_session()
return sa.query(User).filter(User.username == 'default').one().active
diff --git a/rhodecode/tests/rhodecode_crawler.py b/rhodecode/tests/scripts/test_crawler.py
index 4da18a7f..4da18a7f 100755
--- a/rhodecode/tests/rhodecode_crawler.py
+++ b/rhodecode/tests/scripts/test_crawler.py
diff --git a/rhodecode/tests/scripts/test_vcs_operations.py b/rhodecode/tests/scripts/test_vcs_operations.py
new file mode 100755
index 00000000..81ef6fa7
--- /dev/null
+++ b/rhodecode/tests/scripts/test_vcs_operations.py
@@ -0,0 +1,425 @@
+# -*- coding: utf-8 -*-
+"""
+ rhodecode.tests.test_scm_operations
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Test suite for making push/pull operations.
+ Run using::
+
+ RC_WHOOSH_TEST_DISABLE=1 RC_NO_TMP_PATH=1 nosetests rhodecode/tests/scripts/test_vcs_operations.py
+
+ :created_on: Dec 30, 2010
+ :author: marcink
+ :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
+ :license: GPLv3, see COPYING for more details.
+"""
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+import os
+import tempfile
+import unittest
+from os.path import join as jn
+from os.path import dirname as dn
+
+from tempfile import _RandomNameSequence
+from subprocess import Popen, PIPE
+
+from rhodecode.tests import *
+from rhodecode.model.db import User, Repository, UserLog
+from rhodecode.model.meta import Session
+from rhodecode.model.repo import RepoModel
+
+DEBUG = True
+HOST = '127.0.0.1:5000' # test host
+
+
+class Command(object):
+
+ def __init__(self, cwd):
+ self.cwd = cwd
+
+ def execute(self, cmd, *args):
+ """
+ Runs command on the system with given ``args``.
+ """
+
+ command = cmd + ' ' + ' '.join(args)
+ if DEBUG:
+ print '*** CMD %s ***' % command
+ p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
+ stdout, stderr = p.communicate()
+ if DEBUG:
+ print stdout, stderr
+ return stdout, stderr
+
+
+def _get_tmp_dir():
+ return tempfile.mkdtemp(prefix='rc_integration_test')
+
+
+def _construct_url(repo, dest=None, **kwargs):
+ if dest is None:
+ #make temp clone
+ dest = _get_tmp_dir()
+ params = {
+ 'user': TEST_USER_ADMIN_LOGIN,
+ 'passwd': TEST_USER_ADMIN_PASS,
+ 'host': HOST,
+ 'cloned_repo': repo,
+ 'dest': dest
+ }
+ params.update(**kwargs)
+ if params['user'] and params['passwd']:
+ _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s %(dest)s' % params
+ else:
+ _url = 'http://(host)s/%(cloned_repo)s %(dest)s' % params
+ return _url
+
+
+def _add_files_and_push(vcs, DEST, **kwargs):
+ """
+ Generate some files, add it to DEST repo and push back
+ vcs is git or hg and defines what VCS we want to make those files for
+
+ :param vcs:
+ :param DEST:
+ """
+ # commit some stuff into this repo
+ cwd = path = jn(DEST)
+ #added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next())
+ added_file = jn(path, '%ssetup.py' % _RandomNameSequence().next())
+ Command(cwd).execute('touch %s' % added_file)
+ Command(cwd).execute('%s add %s' % (vcs, added_file))
+
+ for i in xrange(3):
+ cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
+ Command(cwd).execute(cmd)
+ if vcs == 'hg':
+ cmd = """hg commit -m 'commited new %s' -u '%s' %s """ % (
+ i, 'Marcin Kuźminski <marcin@python-blog.com>', added_file
+ )
+ elif vcs == 'git':
+ cmd = """git ci -m 'commited new %s' --author '%s' %s """ % (
+ i, 'Marcin Kuźminski <marcin@python-blog.com>', added_file
+ )
+ Command(cwd).execute(cmd)
+ # PUSH it back
+ if vcs == 'hg':
+ _REPO = HG_REPO
+ elif vcs == 'git':
+ _REPO = GIT_REPO
+
+ kwargs['dest'] = ''
+ clone_url = _construct_url(_REPO, **kwargs)
+ if 'clone_url' in kwargs:
+ clone_url = kwargs['clone_url']
+ if vcs == 'hg':
+ stdout, stderr = Command(cwd).execute('hg push --verbose', clone_url)
+ elif vcs == 'git':
+ stdout, stderr = Command(cwd).execute('git push', clone_url + " master")
+
+ return stdout, stderr
+
+
+def set_anonymous_access(enable=True):
+ user = User.get_by_username(User.DEFAULT_USER)
+ user.active = enable
+ Session().add(user)
+ Session().commit()
+ print '\tanonymous access is now:', enable
+ if enable != User.get_by_username(User.DEFAULT_USER).active:
+ raise Exception('Cannot set anonymous access')
+
+
+#==============================================================================
+# TESTS
+#==============================================================================
+
+class TestVCSOperations(unittest.TestCase):
+
+ @classmethod
+ def setup_class(cls):
+ #DISABLE ANONYMOUS ACCESS
+ set_anonymous_access(False)
+
+ def setUp(self):
+ r = Repository.get_by_repo_name(GIT_REPO)
+ Repository.unlock(r)
+ r.enable_locking = False
+ Session().add(r)
+ Session().commit()
+
+ r = Repository.get_by_repo_name(HG_REPO)
+ Repository.unlock(r)
+ r.enable_locking = False
+ Session().add(r)
+ Session().commit()
+
+ def test_clone_hg_repo_by_admin(self):
+ clone_url = _construct_url(HG_REPO)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ assert 'requesting all changes' in stdout
+ assert 'adding changesets' in stdout
+ assert 'adding manifests' in stdout
+ assert 'adding file changes' in stdout
+
+ assert stderr == ''
+
+ def test_clone_git_repo_by_admin(self):
+ clone_url = _construct_url(GIT_REPO)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+
+ assert 'Cloning into' in stdout
+ assert stderr == ''
+
+ def test_clone_wrong_credentials_hg(self):
+ clone_url = _construct_url(HG_REPO, passwd='bad!')
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+ assert 'abort: authorization failed' in stderr
+
+ def test_clone_wrong_credentials_git(self):
+ clone_url = _construct_url(GIT_REPO, passwd='bad!')
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+ assert 'fatal: Authentication failed' in stderr
+
+ def test_clone_git_dir_as_hg(self):
+ clone_url = _construct_url(GIT_REPO)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+ assert 'HTTP Error 404: Not Found' in stderr
+
+ def test_clone_hg_repo_as_git(self):
+ clone_url = _construct_url(HG_REPO)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+ assert 'not found:' in stderr
+
+ def test_clone_non_existing_path_hg(self):
+ clone_url = _construct_url('trololo')
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+ assert 'HTTP Error 404: Not Found' in stderr
+
+ def test_clone_non_existing_path_git(self):
+ clone_url = _construct_url('trololo')
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+ assert 'not found:' in stderr
+
+ def test_push_new_file_hg(self):
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(HG_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ stdout, stderr = _add_files_and_push('hg', DEST)
+
+ assert 'pushing to' in stdout
+ assert 'Repository size' in stdout
+ assert 'Last revision is now' in stdout
+
+ def test_push_new_file_git(self):
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(GIT_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+
+ # commit some stuff into this repo
+ stdout, stderr = _add_files_and_push('git', DEST)
+
+ #WTF git stderr ?!
+ assert 'master -> master' in stderr
+
+ def test_push_wrong_credentials_hg(self):
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(HG_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ stdout, stderr = _add_files_and_push('hg', DEST, user='bad',
+ passwd='name')
+
+ assert 'abort: authorization failed' in stderr
+
+ def test_push_wrong_credentials_git(self):
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(GIT_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+
+ stdout, stderr = _add_files_and_push('git', DEST, user='bad',
+ passwd='name')
+
+ assert 'fatal: Authentication failed' in stderr
+
+ def test_push_back_to_wrong_url_hg(self):
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(HG_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ stdout, stderr = _add_files_and_push('hg', DEST,
+ clone_url='http://127.0.0.1:5000/tmp',)
+
+ assert 'HTTP Error 404: Not Found' in stderr
+
+ def test_push_back_to_wrong_url_git(self):
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(GIT_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+
+ stdout, stderr = _add_files_and_push('git', DEST,
+ clone_url='http://127.0.0.1:5000/tmp',)
+
+ assert 'not found:' in stderr
+
+ def test_clone_and_create_lock_hg(self):
+ # enable locking
+ r = Repository.get_by_repo_name(HG_REPO)
+ r.enable_locking = True
+ Session().add(r)
+ Session().commit()
+ # clone
+ clone_url = _construct_url(HG_REPO)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ #check if lock was made
+ r = Repository.get_by_repo_name(HG_REPO)
+ assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
+
+ def test_clone_and_create_lock_git(self):
+ # enable locking
+ r = Repository.get_by_repo_name(GIT_REPO)
+ r.enable_locking = True
+ Session().add(r)
+ Session().commit()
+ # clone
+ clone_url = _construct_url(GIT_REPO)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+
+ #check if lock was made
+ r = Repository.get_by_repo_name(GIT_REPO)
+ assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
+
+ def test_clone_after_repo_was_locked_hg(self):
+ #lock repo
+ r = Repository.get_by_repo_name(HG_REPO)
+ Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
+ #pull fails since repo is locked
+ clone_url = _construct_url(HG_REPO)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+ msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
+ % (HG_REPO, TEST_USER_ADMIN_LOGIN))
+ assert msg in stderr
+
+ def test_clone_after_repo_was_locked_git(self):
+ #lock repo
+ r = Repository.get_by_repo_name(GIT_REPO)
+ Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
+ #pull fails since repo is locked
+ clone_url = _construct_url(GIT_REPO)
+ stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+ msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
+ % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
+ #TODO: fix this somehow later on GIT, GIT is stupid and even if we throw
+ # back 423 to it, it makes ANOTHER request and we fail there with 405 :/
+ msg = "405 Method Not Allowed"
+ assert msg in stderr
+
+ def test_push_on_locked_repo_by_other_user_hg(self):
+ #clone some temp
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(HG_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ #lock repo
+ r = Repository.get_by_repo_name(HG_REPO)
+ # let this user actually push !
+ RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN,
+ perm='repository.write')
+ Session().commit()
+ Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
+
+ #push fails repo is locked by other user !
+ stdout, stderr = _add_files_and_push('hg', DEST,
+ user=TEST_USER_REGULAR_LOGIN,
+ passwd=TEST_USER_REGULAR_PASS)
+ msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
+ % (HG_REPO, TEST_USER_ADMIN_LOGIN))
+ assert msg in stderr
+
+#TODO: fix me ! somehow during tests hooks don't get called on GIT
+# def test_push_on_locked_repo_by_other_user_git(self):
+# #clone some temp
+# DEST = _get_tmp_dir()
+# clone_url = _construct_url(GIT_REPO, dest=DEST)
+# stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+#
+# #lock repo
+# r = Repository.get_by_repo_name(GIT_REPO)
+# # let this user actually push !
+# RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN,
+# perm='repository.write')
+# Session().commit()
+# Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
+#
+# #push fails repo is locked by other user !
+# stdout, stderr = _add_files_and_push('git', DEST,
+# user=TEST_USER_REGULAR_LOGIN,
+# passwd=TEST_USER_REGULAR_PASS)
+# msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
+# % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
+# #TODO: fix this somehow later on GIT, GIT is stupid and even if we throw
+# # back 423 to it, it makes ANOTHER request and we fail there with 405 :/
+# msg = "405 Method Not Allowed"
+# assert msg in stderr
+
+ def test_push_unlocks_repository_hg(self):
+ # enable locking
+ r = Repository.get_by_repo_name(HG_REPO)
+ r.enable_locking = True
+ Session().add(r)
+ Session().commit()
+ #clone some temp
+ DEST = _get_tmp_dir()
+ clone_url = _construct_url(HG_REPO, dest=DEST)
+ stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+ #check for lock repo after clone
+ r = Repository.get_by_repo_name(HG_REPO)
+ assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
+
+ #push is ok and repo is now unlocked
+ stdout, stderr = _add_files_and_push('hg', DEST)
+ assert ('remote: Released lock on repo `%s`' % HG_REPO) in stdout
+ #we need to cleanup the Session Here !
+ Session.remove()
+ r = Repository.get_by_repo_name(HG_REPO)
+ assert r.locked == [None, None]
+
+#TODO: fix me ! somehow during tests hooks don't get called on GIT
+# def test_push_unlocks_repository_git(self):
+# # enable locking
+# r = Repository.get_by_repo_name(GIT_REPO)
+# r.enable_locking = True
+# Session().add(r)
+# Session().commit()
+# #clone some temp
+# DEST = _get_tmp_dir()
+# clone_url = _construct_url(GIT_REPO, dest=DEST)
+# stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+#
+# #check for lock repo after clone
+# r = Repository.get_by_repo_name(GIT_REPO)
+# assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
+#
+# #push is ok and repo is now unlocked
+# stdout, stderr = _add_files_and_push('git', DEST)
+# #assert ('remote: Released lock on repo `%s`' % GIT_REPO) in stdout
+# #we need to cleanup the Session Here !
+# Session.remove()
+# r = Repository.get_by_repo_name(GIT_REPO)
+# assert r.locked == [None, None]
diff --git a/rhodecode/tests/test_hg_operations.py b/rhodecode/tests/test_hg_operations.py
deleted file mode 100755
index 4879a3a5..00000000
--- a/rhodecode/tests/test_hg_operations.py
+++ /dev/null
@@ -1,401 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- rhodecode.tests.test_hg_operations
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Test suite for making push/pull operations
-
- :created_on: Dec 30, 2010
- :author: marcink
- :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
- :license: GPLv3, see COPYING for more details.
-"""
-# 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 3 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, see <http://www.gnu.org/licenses/>.
-
-import os
-import time
-import sys
-import shutil
-import logging
-
-from os.path import join as jn
-from os.path import dirname as dn
-
-from tempfile import _RandomNameSequence
-from subprocess import Popen, PIPE
-
-from paste.deploy import appconfig
-from pylons import config
-from sqlalchemy import engine_from_config
-
-from rhodecode.lib.utils import add_cache
-from rhodecode.model import init_model
-from rhodecode.model import meta
-from rhodecode.model.db import User, Repository, UserLog
-from rhodecode.lib.auth import get_crypt_password
-
-from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
-from rhodecode.config.environment import load_environment
-
-rel_path = dn(dn(dn(os.path.abspath(__file__))))
-
-conf = appconfig('config:%s' % sys.argv[1], relative_to=rel_path)
-load_environment(conf.global_conf, conf.local_conf)
-
-add_cache(conf)
-
-USER = 'test_admin'
-PASS = 'test12'
-HOST = '127.0.0.1:5000'
-DEBUG = False
-print 'DEBUG:', DEBUG
-log = logging.getLogger(__name__)
-
-engine = engine_from_config(conf, 'sqlalchemy.db1.')
-init_model(engine)
-sa = meta.Session
-
-class Command(object):
-
- def __init__(self, cwd):
- self.cwd = cwd
-
- def execute(self, cmd, *args):
- """Runs command on the system with given ``args``.
- """
-
- command = cmd + ' ' + ' '.join(args)
- log.debug('Executing %s' % command)
- if DEBUG:
- print command
- p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
- stdout, stderr = p.communicate()
- if DEBUG:
- print stdout, stderr
- return stdout, stderr
-
-
-def test_wrapp(func):
-
- def __wrapp(*args, **kwargs):
- print '>>>%s' % func.__name__
- try:
- res = func(*args, **kwargs)
- except Exception, e:
- print ('###############\n-'
- '--%s failed %s--\n'
- '###############\n' % (func.__name__, e))
- sys.exit()
- print '++OK++'
- return res
- return __wrapp
-
-
-def create_test_user(force=True):
- print '\tcreating test user'
-
- user = User.get_by_username(USER)
-
- if force and user is not None:
- print '\tremoving current user'
- for repo in Repository.query().filter(Repository.user == user).all():
- sa.delete(repo)
- sa.delete(user)
- sa.commit()
-
- if user is None or force:
- print '\tcreating new one'
- new_usr = User()
- new_usr.username = USER
- new_usr.password = get_crypt_password(PASS)
- new_usr.email = 'mail@mail.com'
- new_usr.name = 'test'
- new_usr.lastname = 'lasttestname'
- new_usr.active = True
- new_usr.admin = True
- sa.add(new_usr)
- sa.commit()
-
- print '\tdone'
-
-
-def create_test_repo(force=True):
- from rhodecode.model.repo import RepoModel
-
- user = User.get_by_username(USER)
- if user is None:
- raise Exception('user not found')
-
-
- repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
-
- if repo is None:
- print '\trepo not found creating'
-
- form_data = {'repo_name':HG_REPO,
- 'repo_type':'hg',
- 'private':False,
- 'clone_uri':'' }
- rm = RepoModel(sa)
- rm.base_path = '/home/hg'
- rm.create(form_data, user)
-
-
-def set_anonymous_access(enable=True):
- user = User.get_by_username('default')
- user.active = enable
- sa.add(user)
- sa.commit()
- print '\tanonymous access is now:', enable
- if enable != User.get_by_username('default').active:
- raise Exception('Cannot set anonymous access')
-
-def get_anonymous_access():
- user = User.get_by_username('default')
- return user.active
-
-
-#==============================================================================
-# TESTS
-#==============================================================================
-@test_wrapp
-def test_clone_with_credentials(no_errors=False):
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-
- try:
- shutil.rmtree(path, ignore_errors=True)
- os.makedirs(path)
- #print 'made dirs %s' % jn(path)
- except OSError:
- raise
-
- print '\tchecking if anonymous access is enabled'
- anonymous_access = get_anonymous_access()
- if anonymous_access:
- print '\tenabled, disabling it '
- set_anonymous_access(enable=False)
-
- clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
- {'user':USER,
- 'pass':PASS,
- 'host':HOST,
- 'cloned_repo':HG_REPO,
- 'dest':path}
-
- stdout, stderr = Command(cwd).execute('hg clone', clone_url)
-
- if no_errors is False:
- assert """adding file changes""" in stdout, 'no messages about cloning'
- assert """abort""" not in stderr , 'got error from clone'
-
-
-@test_wrapp
-def test_clone_anonymous():
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-
- try:
- shutil.rmtree(path, ignore_errors=True)
- os.makedirs(path)
- #print 'made dirs %s' % jn(path)
- except OSError:
- raise
-
-
- print '\tchecking if anonymous access is enabled'
- anonymous_access = get_anonymous_access()
- if not anonymous_access:
- print '\tnot enabled, enabling it '
- set_anonymous_access(enable=True)
-
- clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
- {'user':USER,
- 'pass':PASS,
- 'host':HOST,
- 'cloned_repo':HG_REPO,
- 'dest':path}
-
- stdout, stderr = Command(cwd).execute('hg clone', clone_url)
-
- assert """adding file changes""" in stdout, 'no messages about cloning'
- assert """abort""" not in stderr , 'got error from clone'
-
- #disable if it was enabled
- if not anonymous_access:
- print '\tdisabling anonymous access'
- set_anonymous_access(enable=False)
-
-@test_wrapp
-def test_clone_wrong_credentials():
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-
- try:
- shutil.rmtree(path, ignore_errors=True)
- os.makedirs(path)
- #print 'made dirs %s' % jn(path)
- except OSError:
- raise
-
- print '\tchecking if anonymous access is enabled'
- anonymous_access = get_anonymous_access()
- if anonymous_access:
- print '\tenabled, disabling it '
- set_anonymous_access(enable=False)
-
- clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
- {'user':USER + 'error',
- 'pass':PASS,
- 'host':HOST,
- 'cloned_repo':HG_REPO,
- 'dest':path}
-
- stdout, stderr = Command(cwd).execute('hg clone', clone_url)
-
- if not """abort: authorization failed""" in stderr:
- raise Exception('Failure')
-
-@test_wrapp
-def test_pull():
- pass
-
-@test_wrapp
-def test_push_modify_file(f_name='setup.py'):
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
- modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
- for i in xrange(5):
- cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
- Command(cwd).execute(cmd)
-
- cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file)
- Command(cwd).execute(cmd)
-
- Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
-
-@test_wrapp
-def test_push_new_file(commits=15, with_clone=True):
-
- if with_clone:
- test_clone_with_credentials(no_errors=True)
-
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
- added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next())
-
- Command(cwd).execute('touch %s' % added_file)
-
- Command(cwd).execute('hg add %s' % added_file)
-
- for i in xrange(commits):
- cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
- Command(cwd).execute(cmd)
-
- cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (i,
- 'Marcin Kuźminski <marcin@python-blog.com>',
- added_file)
- Command(cwd).execute(cmd)
-
- push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
- {'user':USER,
- 'pass':PASS,
- 'host':HOST,
- 'cloned_repo':HG_REPO,
- 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
-
- Command(cwd).execute('hg push --verbose --debug %s' % push_url)
-
-@test_wrapp
-def test_push_wrong_credentials():
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
- clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
- {'user':USER + 'xxx',
- 'pass':PASS,
- 'host':HOST,
- 'cloned_repo':HG_REPO,
- 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
-
- modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py')
- for i in xrange(5):
- cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
- Command(cwd).execute(cmd)
-
- cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file)
- Command(cwd).execute(cmd)
-
- Command(cwd).execute('hg push %s' % clone_url)
-
-@test_wrapp
-def test_push_wrong_path():
- cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
- added_file = jn(path, 'somefile.py')
-
- try:
- shutil.rmtree(path, ignore_errors=True)
- os.makedirs(path)
- print '\tmade dirs %s' % jn(path)
- except OSError:
- raise
-
- Command(cwd).execute("""echo '' > %s""" % added_file)
- Command(cwd).execute("""hg init %s""" % path)
- Command(cwd).execute("""hg add %s""" % added_file)
-
- for i in xrange(2):
- cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
- Command(cwd).execute(cmd)
-
- cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
- Command(cwd).execute(cmd)
-
- clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
- {'user':USER,
- 'pass':PASS,
- 'host':HOST,
- 'cloned_repo':HG_REPO + '_error',
- 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
-
- stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
- if not """abort: HTTP Error 403: Forbidden""" in stderr:
- raise Exception('Failure')
-
-@test_wrapp
-def get_logs():
- return UserLog.query().all()
-
-@test_wrapp
-def test_logs(initial):
- logs = UserLog.query().all()
- operations = 4
- if len(initial) + operations != len(logs):
- raise Exception("missing number of logs initial:%s vs current:%s" % \
- (len(initial), len(logs)))
-
-
-if __name__ == '__main__':
- create_test_user(force=False)
- create_test_repo()
-
- initial_logs = get_logs()
- print 'initial activity logs: %s' % len(initial_logs)
- s = time.time()
- #test_push_modify_file()
- test_clone_with_credentials()
- test_clone_wrong_credentials()
-
- test_push_new_file(commits=2, with_clone=True)
-
- test_clone_anonymous()
- test_push_wrong_path()
-
- test_push_wrong_credentials()
-
- test_logs(initial_logs)
- print 'finished ok in %.3f' % (time.time() - s)
diff --git a/rhodecode/tests/test_libs.py b/rhodecode/tests/test_libs.py
index de657b9d..13ec3c9e 100644
--- a/rhodecode/tests/test_libs.py
+++ b/rhodecode/tests/test_libs.py
@@ -23,9 +23,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
import unittest
+import datetime
+import hashlib
+import mock
from rhodecode.tests import *
proto = 'http'
@@ -116,3 +117,61 @@ class TestLibs(unittest.TestCase):
'marian.user', 'marco-polo', 'marco_polo'
], key=lambda k: k.lower())
self.assertEqual(s, extract_mentioned_users(sample))
+
+ def test_age(self):
+ import calendar
+ from rhodecode.lib.utils2 import age
+ n = datetime.datetime.now()
+ delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
+ self.assertEqual(age(n), u'just now')
+ self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
+ self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
+ self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
+ self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
+ self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
+ self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
+ u'1 month and 2 days ago')
+ self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
+
+ def test_tag_exctrator(self):
+ sample = (
+ "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
+ "[requires] [stale] [see<>=>] [see => http://url.com]"
+ "[requires => url] [lang => python] [just a tag]"
+ "[,d] [ => ULR ] [obsolete] [desc]]"
+ )
+ from rhodecode.lib.helpers import desc_stylize
+ res = desc_stylize(sample)
+ self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
+ self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
+ self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
+ self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
+ self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
+ self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
+
+ def test_alternative_gravatar(self):
+ from rhodecode.lib.helpers import gravatar_url
+ _md5 = lambda s: hashlib.md5(s).hexdigest()
+
+ def fake_conf(**kwargs):
+ from pylons import config
+ config['app_conf'] = {}
+ config['app_conf']['use_gravatar'] = True
+ config['app_conf'].update(kwargs)
+ return config
+ fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
+ with mock.patch('pylons.config', fake):
+ grav = gravatar_url(email_address='test@foo.com', size=24)
+ assert grav == 'http://test.com/test@foo.com'
+
+ fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
+ with mock.patch('pylons.config', fake):
+ em = 'test@foo.com'
+ grav = gravatar_url(email_address=em, size=24)
+ assert grav == 'http://test.com/%s' % (_md5(em))
+
+ fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
+ with mock.patch('pylons.config', fake):
+ em = 'test@foo.com'
+ grav = gravatar_url(email_address=em, size=24)
+ assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
diff --git a/rhodecode/tests/test_models.py b/rhodecode/tests/test_models.py
deleted file mode 100644
index 7c50c970..00000000
--- a/rhodecode/tests/test_models.py
+++ /dev/null
@@ -1,715 +0,0 @@
-import os
-import unittest
-from rhodecode.tests import *
-
-from rhodecode.model.repos_group import ReposGroupModel
-from rhodecode.model.repo import RepoModel
-from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
- UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm,\
- Repository
-from sqlalchemy.exc import IntegrityError
-from rhodecode.model.user import UserModel
-
-from rhodecode.model.meta import Session
-from rhodecode.model.notification import NotificationModel
-from rhodecode.model.users_group import UsersGroupModel
-from rhodecode.lib.auth import AuthUser
-
-
-def _make_group(path, desc='desc', parent_id=None,
- skip_if_exists=False):
-
- gr = RepoGroup.get_by_group_name(path)
- if gr and skip_if_exists:
- return gr
-
- gr = ReposGroupModel().create(path, desc, parent_id)
- return gr
-
-
-class TestReposGroups(unittest.TestCase):
-
- def setUp(self):
- self.g1 = _make_group('test1', skip_if_exists=True)
- Session.commit()
- self.g2 = _make_group('test2', skip_if_exists=True)
- Session.commit()
- self.g3 = _make_group('test3', skip_if_exists=True)
- Session.commit()
-
- def tearDown(self):
- print 'out'
-
- def __check_path(self, *path):
- """
- Checks the path for existance !
- """
- path = [TESTS_TMP_PATH] + list(path)
- path = os.path.join(*path)
- return os.path.isdir(path)
-
- def _check_folders(self):
- print os.listdir(TESTS_TMP_PATH)
-
- def __delete_group(self, id_):
- ReposGroupModel().delete(id_)
-
- def __update_group(self, id_, path, desc='desc', parent_id=None):
- form_data = dict(
- group_name=path,
- group_description=desc,
- group_parent_id=parent_id,
- perms_updates=[],
- perms_new=[]
- )
- gr = ReposGroupModel().update(id_, form_data)
- return gr
-
- def test_create_group(self):
- g = _make_group('newGroup')
- self.assertEqual(g.full_path, 'newGroup')
-
- self.assertTrue(self.__check_path('newGroup'))
-
- def test_create_same_name_group(self):
- self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
- Session.rollback()
-
- def test_same_subgroup(self):
- sg1 = _make_group('sub1', parent_id=self.g1.group_id)
- self.assertEqual(sg1.parent_group, self.g1)
- self.assertEqual(sg1.full_path, 'test1/sub1')
- self.assertTrue(self.__check_path('test1', 'sub1'))
-
- ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
- self.assertEqual(ssg1.parent_group, sg1)
- self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
- self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
-
- def test_remove_group(self):
- sg1 = _make_group('deleteme')
- self.__delete_group(sg1.group_id)
-
- self.assertEqual(RepoGroup.get(sg1.group_id), None)
- self.assertFalse(self.__check_path('deteteme'))
-
- sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
- self.__delete_group(sg1.group_id)
-
- self.assertEqual(RepoGroup.get(sg1.group_id), None)
- self.assertFalse(self.__check_path('test1', 'deteteme'))
-
- def test_rename_single_group(self):
- sg1 = _make_group('initial')
-
- new_sg1 = self.__update_group(sg1.group_id, 'after')
- self.assertTrue(self.__check_path('after'))
- self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
-
- def test_update_group_parent(self):
-
- sg1 = _make_group('initial', parent_id=self.g1.group_id)
-
- new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
- self.assertTrue(self.__check_path('test1', 'after'))
- self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
-
- new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
- self.assertTrue(self.__check_path('test3', 'after'))
- self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
-
- new_sg1 = self.__update_group(sg1.group_id, 'hello')
- self.assertTrue(self.__check_path('hello'))
-
- self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
-
- def test_subgrouping_with_repo(self):
-
- g1 = _make_group('g1')
- g2 = _make_group('g2')
-
- # create new repo
- form_data = dict(repo_name='john',
- repo_name_full='john',
- fork_name=None,
- description=None,
- repo_group=None,
- private=False,
- repo_type='hg',
- clone_uri=None)
- cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
- r = RepoModel().create(form_data, cur_user)
-
- self.assertEqual(r.repo_name, 'john')
-
- # put repo into group
- form_data = form_data
- form_data['repo_group'] = g1.group_id
- form_data['perms_new'] = []
- form_data['perms_updates'] = []
- RepoModel().update(r.repo_name, form_data)
- self.assertEqual(r.repo_name, 'g1/john')
-
- self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
- self.assertTrue(self.__check_path('g2', 'g1'))
-
- # test repo
- self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1', r.just_name]))
-
- def test_move_to_root(self):
- g1 = _make_group('t11')
- Session.commit()
- g2 = _make_group('t22', parent_id=g1.group_id)
- Session.commit()
-
- self.assertEqual(g2.full_path, 't11/t22')
- self.assertTrue(self.__check_path('t11', 't22'))
-
- g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
- Session.commit()
-
- self.assertEqual(g2.group_name, 'g22')
- # we moved out group from t1 to '' so it's full path should be 'g2'
- self.assertEqual(g2.full_path, 'g22')
- self.assertFalse(self.__check_path('t11', 't22'))
- self.assertTrue(self.__check_path('g22'))
-
-
-class TestUser(unittest.TestCase):
- def __init__(self, methodName='runTest'):
- Session.remove()
- super(TestUser, self).__init__(methodName=methodName)
-
- def test_create_and_remove(self):
- usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
- email=u'u232@rhodecode.org',
- name=u'u1', lastname=u'u1')
- Session.commit()
- self.assertEqual(User.get_by_username(u'test_user'), usr)
-
- # make users group
- users_group = UsersGroupModel().create('some_example_group')
- Session.commit()
-
- UsersGroupModel().add_user_to_group(users_group, usr)
- Session.commit()
-
- self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
- self.assertEqual(UsersGroupMember.query().count(), 1)
- UserModel().delete(usr.user_id)
- Session.commit()
-
- self.assertEqual(UsersGroupMember.query().all(), [])
-
-
-class TestNotifications(unittest.TestCase):
-
- def __init__(self, methodName='runTest'):
- Session.remove()
- self.u1 = UserModel().create_or_update(username=u'u1',
- password=u'qweqwe',
- email=u'u1@rhodecode.org',
- name=u'u1', lastname=u'u1')
- Session.commit()
- self.u1 = self.u1.user_id
-
- self.u2 = UserModel().create_or_update(username=u'u2',
- password=u'qweqwe',
- email=u'u2@rhodecode.org',
- name=u'u2', lastname=u'u3')
- Session.commit()
- self.u2 = self.u2.user_id
-
- self.u3 = UserModel().create_or_update(username=u'u3',
- password=u'qweqwe',
- email=u'u3@rhodecode.org',
- name=u'u3', lastname=u'u3')
- Session.commit()
- self.u3 = self.u3.user_id
-
- super(TestNotifications, self).__init__(methodName=methodName)
-
- def _clean_notifications(self):
- for n in Notification.query().all():
- Session.delete(n)
-
- Session.commit()
- self.assertEqual(Notification.query().all(), [])
-
- def tearDown(self):
- self._clean_notifications()
-
- def test_create_notification(self):
- self.assertEqual([], Notification.query().all())
- self.assertEqual([], UserNotification.query().all())
-
- usrs = [self.u1, self.u2]
- notification = NotificationModel().create(created_by=self.u1,
- subject=u'subj', body=u'hi there',
- recipients=usrs)
- Session.commit()
- u1 = User.get(self.u1)
- u2 = User.get(self.u2)
- u3 = User.get(self.u3)
- notifications = Notification.query().all()
- self.assertEqual(len(notifications), 1)
-
- unotification = UserNotification.query()\
- .filter(UserNotification.notification == notification).all()
-
- self.assertEqual(notifications[0].recipients, [u1, u2])
- self.assertEqual(notification.notification_id,
- notifications[0].notification_id)
- self.assertEqual(len(unotification), len(usrs))
- self.assertEqual([x.user.user_id for x in unotification], usrs)
-
- def test_user_notifications(self):
- self.assertEqual([], Notification.query().all())
- self.assertEqual([], UserNotification.query().all())
-
- notification1 = NotificationModel().create(created_by=self.u1,
- subject=u'subj', body=u'hi there1',
- recipients=[self.u3])
- Session.commit()
- notification2 = NotificationModel().create(created_by=self.u1,
- subject=u'subj', body=u'hi there2',
- recipients=[self.u3])
- Session.commit()
- u3 = Session.query(User).get(self.u3)
-
- self.assertEqual(sorted([x.notification for x in u3.notifications]),
- sorted([notification2, notification1]))
-
- def test_delete_notifications(self):
- self.assertEqual([], Notification.query().all())
- self.assertEqual([], UserNotification.query().all())
-
- notification = NotificationModel().create(created_by=self.u1,
- subject=u'title', body=u'hi there3',
- recipients=[self.u3, self.u1, self.u2])
- Session.commit()
- notifications = Notification.query().all()
- self.assertTrue(notification in notifications)
-
- Notification.delete(notification.notification_id)
- Session.commit()
-
- notifications = Notification.query().all()
- self.assertFalse(notification in notifications)
-
- un = UserNotification.query().filter(UserNotification.notification
- == notification).all()
- self.assertEqual(un, [])
-
- def test_delete_association(self):
-
- self.assertEqual([], Notification.query().all())
- self.assertEqual([], UserNotification.query().all())
-
- notification = NotificationModel().create(created_by=self.u1,
- subject=u'title', body=u'hi there3',
- recipients=[self.u3, self.u1, self.u2])
- Session.commit()
-
- unotification = UserNotification.query()\
- .filter(UserNotification.notification ==
- notification)\
- .filter(UserNotification.user_id == self.u3)\
- .scalar()
-
- self.assertEqual(unotification.user_id, self.u3)
-
- NotificationModel().delete(self.u3,
- notification.notification_id)
- Session.commit()
-
- u3notification = UserNotification.query()\
- .filter(UserNotification.notification ==
- notification)\
- .filter(UserNotification.user_id == self.u3)\
- .scalar()
-
- self.assertEqual(u3notification, None)
-
- # notification object is still there
- self.assertEqual(Notification.query().all(), [notification])
-
- #u1 and u2 still have assignments
- u1notification = UserNotification.query()\
- .filter(UserNotification.notification ==
- notification)\
- .filter(UserNotification.user_id == self.u1)\
- .scalar()
- self.assertNotEqual(u1notification, None)
- u2notification = UserNotification.query()\
- .filter(UserNotification.notification ==
- notification)\
- .filter(UserNotification.user_id == self.u2)\
- .scalar()
- self.assertNotEqual(u2notification, None)
-
- def test_notification_counter(self):
- self._clean_notifications()
- self.assertEqual([], Notification.query().all())
- self.assertEqual([], UserNotification.query().all())
-
- NotificationModel().create(created_by=self.u1,
- subject=u'title', body=u'hi there_delete',
- recipients=[self.u3, self.u1])
- Session.commit()
-
- self.assertEqual(NotificationModel()
- .get_unread_cnt_for_user(self.u1), 1)
- self.assertEqual(NotificationModel()
- .get_unread_cnt_for_user(self.u2), 0)
- self.assertEqual(NotificationModel()
- .get_unread_cnt_for_user(self.u3), 1)
-
- notification = NotificationModel().create(created_by=self.u1,
- subject=u'title', body=u'hi there3',
- recipients=[self.u3, self.u1, self.u2])
- Session.commit()
-
- self.assertEqual(NotificationModel()
- .get_unread_cnt_for_user(self.u1), 2)
- self.assertEqual(NotificationModel()
- .get_unread_cnt_for_user(self.u2), 1)
- self.assertEqual(NotificationModel()
- .get_unread_cnt_for_user(self.u3), 2)
-
-
-class TestUsers(unittest.TestCase):
-
- def __init__(self, methodName='runTest'):
- super(TestUsers, self).__init__(methodName=methodName)
-
- def setUp(self):
- self.u1 = UserModel().create_or_update(username=u'u1',
- password=u'qweqwe',
- email=u'u1@rhodecode.org',
- name=u'u1', lastname=u'u1')
-
- def tearDown(self):
- perm = Permission.query().all()
- for p in perm:
- UserModel().revoke_perm(self.u1, p)
-
- UserModel().delete(self.u1)
- Session.commit()
-
- def test_add_perm(self):
- perm = Permission.query().all()[0]
- UserModel().grant_perm(self.u1, perm)
- Session.commit()
- self.assertEqual(UserModel().has_perm(self.u1, perm), True)
-
- def test_has_perm(self):
- perm = Permission.query().all()
- for p in perm:
- has_p = UserModel().has_perm(self.u1, p)
- self.assertEqual(False, has_p)
-
- def test_revoke_perm(self):
- perm = Permission.query().all()[0]
- UserModel().grant_perm(self.u1, perm)
- Session.commit()
- self.assertEqual(UserModel().has_perm(self.u1, perm), True)
-
- #revoke
- UserModel().revoke_perm(self.u1, perm)
- Session.commit()
- self.assertEqual(UserModel().has_perm(self.u1, perm), False)
-
-
-class TestPermissions(unittest.TestCase):
- def __init__(self, methodName='runTest'):
- super(TestPermissions, self).__init__(methodName=methodName)
-
- def setUp(self):
- self.u1 = UserModel().create_or_update(
- username=u'u1', password=u'qweqwe',
- email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
- )
- self.u2 = UserModel().create_or_update(
- username=u'u2', password=u'qweqwe',
- email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
- )
- self.anon = User.get_by_username('default')
- self.a1 = UserModel().create_or_update(
- username=u'a1', password=u'qweqwe',
- email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
- )
- Session.commit()
-
- def tearDown(self):
- if hasattr(self, 'test_repo'):
- RepoModel().delete(repo=self.test_repo)
- UserModel().delete(self.u1)
- UserModel().delete(self.u2)
- UserModel().delete(self.a1)
- if hasattr(self, 'g1'):
- ReposGroupModel().delete(self.g1.group_id)
- if hasattr(self, 'g2'):
- ReposGroupModel().delete(self.g2.group_id)
-
- if hasattr(self, 'ug1'):
- UsersGroupModel().delete(self.ug1, force=True)
-
- Session.commit()
-
- def test_default_perms_set(self):
- u1_auth = AuthUser(user_id=self.u1.user_id)
- perms = {
- 'repositories_groups': {},
- 'global': set([u'hg.create.repository', u'repository.read',
- u'hg.register.manual_activate']),
- 'repositories': {u'vcs_test_hg': u'repository.read'}
- }
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- perms['repositories'][HG_REPO])
- new_perm = 'repository.write'
- RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
- Session.commit()
-
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
-
- def test_default_admin_perms_set(self):
- a1_auth = AuthUser(user_id=self.a1.user_id)
- perms = {
- 'repositories_groups': {},
- 'global': set([u'hg.admin']),
- 'repositories': {u'vcs_test_hg': u'repository.admin'}
- }
- self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
- perms['repositories'][HG_REPO])
- new_perm = 'repository.write'
- RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
- Session.commit()
- # cannot really downgrade admins permissions !? they still get's set as
- # admin !
- u1_auth = AuthUser(user_id=self.a1.user_id)
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- perms['repositories'][HG_REPO])
-
- def test_default_group_perms(self):
- self.g1 = _make_group('test1', skip_if_exists=True)
- self.g2 = _make_group('test2', skip_if_exists=True)
- u1_auth = AuthUser(user_id=self.u1.user_id)
- perms = {
- 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
- 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
- 'repositories': {u'vcs_test_hg': u'repository.read'}
- }
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- perms['repositories'][HG_REPO])
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- perms['repositories_groups'])
-
- def test_default_admin_group_perms(self):
- self.g1 = _make_group('test1', skip_if_exists=True)
- self.g2 = _make_group('test2', skip_if_exists=True)
- a1_auth = AuthUser(user_id=self.a1.user_id)
- perms = {
- 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
- 'global': set(['hg.admin']),
- 'repositories': {u'vcs_test_hg': 'repository.admin'}
- }
-
- self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
- perms['repositories'][HG_REPO])
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- perms['repositories_groups'])
-
- def test_propagated_permission_from_users_group(self):
- # make group
- self.ug1 = UsersGroupModel().create('G1')
- # add user to group
- UsersGroupModel().add_user_to_group(self.ug1, self.u1)
-
- # set permission to lower
- new_perm = 'repository.none'
- RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
- Session.commit()
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- new_perm)
-
- # grant perm for group this should override permission from user
- new_perm = 'repository.write'
- RepoModel().grant_users_group_permission(repo=HG_REPO,
- group_name=self.ug1,
- perm=new_perm)
- # check perms
- u1_auth = AuthUser(user_id=self.u1.user_id)
- perms = {
- 'repositories_groups': {},
- 'global': set([u'hg.create.repository', u'repository.read',
- u'hg.register.manual_activate']),
- 'repositories': {u'vcs_test_hg': u'repository.read'}
- }
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- new_perm)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- perms['repositories_groups'])
-
- def test_propagated_permission_from_users_group_lower_weight(self):
- # make group
- self.ug1 = UsersGroupModel().create('G1')
- # add user to group
- UsersGroupModel().add_user_to_group(self.ug1, self.u1)
-
- # set permission to lower
- new_perm_h = 'repository.write'
- RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
- perm=new_perm_h)
- Session.commit()
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- new_perm_h)
-
- # grant perm for group this should NOT override permission from user
- # since it's lower than granted
- new_perm_l = 'repository.read'
- RepoModel().grant_users_group_permission(repo=HG_REPO,
- group_name=self.ug1,
- perm=new_perm_l)
- # check perms
- u1_auth = AuthUser(user_id=self.u1.user_id)
- perms = {
- 'repositories_groups': {},
- 'global': set([u'hg.create.repository', u'repository.read',
- u'hg.register.manual_activate']),
- 'repositories': {u'vcs_test_hg': u'repository.write'}
- }
- self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
- new_perm_h)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- perms['repositories_groups'])
-
- def test_repo_in_group_permissions(self):
- self.g1 = _make_group('group1', skip_if_exists=True)
- self.g2 = _make_group('group2', skip_if_exists=True)
- Session.commit()
- # both perms should be read !
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- {u'group1': u'group.read', u'group2': u'group.read'})
-
- a1_auth = AuthUser(user_id=self.anon.user_id)
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.read', u'group2': u'group.read'})
-
- #Change perms to none for both groups
- ReposGroupModel().grant_user_permission(repos_group=self.g1,
- user=self.anon,
- perm='group.none')
- ReposGroupModel().grant_user_permission(repos_group=self.g2,
- user=self.anon,
- perm='group.none')
-
-
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none', u'group2': u'group.none'})
-
- a1_auth = AuthUser(user_id=self.anon.user_id)
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none', u'group2': u'group.none'})
-
- # add repo to group
- form_data = {
- 'repo_name':HG_REPO,
- 'repo_name_full':RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
- 'repo_type':'hg',
- 'clone_uri':'',
- 'repo_group':self.g1.group_id,
- 'description':'desc',
- 'private':False
- }
- self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
- Session.commit()
-
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none', u'group2': u'group.none'})
-
- a1_auth = AuthUser(user_id=self.anon.user_id)
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none', u'group2': u'group.none'})
-
- #grant permission for u2 !
- ReposGroupModel().grant_user_permission(repos_group=self.g1,
- user=self.u2,
- perm='group.read')
- ReposGroupModel().grant_user_permission(repos_group=self.g2,
- user=self.u2,
- perm='group.read')
- Session.commit()
- self.assertNotEqual(self.u1, self.u2)
- #u1 and anon should have not change perms while u2 should !
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none', u'group2': u'group.none'})
-
- u2_auth = AuthUser(user_id=self.u2.user_id)
- self.assertEqual(u2_auth.permissions['repositories_groups'],
- {u'group1': u'group.read', u'group2': u'group.read'})
-
- a1_auth = AuthUser(user_id=self.anon.user_id)
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none', u'group2': u'group.none'})
-
- def test_repo_group_user_as_user_group_member(self):
- # create Group1
- self.g1 = _make_group('group1', skip_if_exists=True)
- Session.commit()
- a1_auth = AuthUser(user_id=self.anon.user_id)
-
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.read'})
-
- # set default permission to none
- ReposGroupModel().grant_user_permission(repos_group=self.g1,
- user=self.anon,
- perm='group.none')
- # make group
- self.ug1 = UsersGroupModel().create('G1')
- # add user to group
- UsersGroupModel().add_user_to_group(self.ug1, self.u1)
- Session.commit()
-
- # check if user is in the group
- membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
- self.assertEqual(membrs, [self.u1.user_id])
- # add some user to that group
-
- # check his permissions
- a1_auth = AuthUser(user_id=self.anon.user_id)
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none'})
-
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none'})
-
- # grant ug1 read permissions for
- ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
- group_name=self.ug1,
- perm='group.read')
- Session.commit()
- # check if the
- obj = Session.query(UsersGroupRepoGroupToPerm)\
- .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
- .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
- .scalar()
- self.assertEqual(obj.permission.permission_name, 'group.read')
-
- a1_auth = AuthUser(user_id=self.anon.user_id)
-
- self.assertEqual(a1_auth.permissions['repositories_groups'],
- {u'group1': u'group.none'})
-
- u1_auth = AuthUser(user_id=self.u1.user_id)
- self.assertEqual(u1_auth.permissions['repositories_groups'],
- {u'group1': u'group.read'})
diff --git a/rhodecode/tests/test_validators.py b/rhodecode/tests/test_validators.py
new file mode 100644
index 00000000..c1b2f0d2
--- /dev/null
+++ b/rhodecode/tests/test_validators.py
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+import unittest
+import formencode
+
+from rhodecode.tests import *
+
+from rhodecode.model import validators as v
+from rhodecode.model.users_group import UsersGroupModel
+
+from rhodecode.model.meta import Session
+from rhodecode.model.repos_group import ReposGroupModel
+from rhodecode.config.routing import ADMIN_PREFIX
+from rhodecode.model.db import ChangesetStatus
+from rhodecode.model.changeset_status import ChangesetStatusModel
+from rhodecode.model.comment import ChangesetCommentsModel
+
+
+class TestReposGroups(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_Message_extractor(self):
+ validator = v.ValidUsername()
+ self.assertRaises(formencode.Invalid, validator.to_python, 'default')
+
+ class StateObj(object):
+ pass
+
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, 'default', StateObj)
+
+ def test_ValidUsername(self):
+ validator = v.ValidUsername()
+
+ self.assertRaises(formencode.Invalid, validator.to_python, 'default')
+ self.assertRaises(formencode.Invalid, validator.to_python, 'new_user')
+ self.assertRaises(formencode.Invalid, validator.to_python, '.,')
+ self.assertRaises(formencode.Invalid, validator.to_python,
+ TEST_USER_ADMIN_LOGIN)
+ self.assertEqual('test', validator.to_python('test'))
+
+ validator = v.ValidUsername(edit=True, old_data={'user_id': 1})
+
+ def test_ValidRepoUser(self):
+ validator = v.ValidRepoUser()
+ self.assertRaises(formencode.Invalid, validator.to_python, 'nouser')
+ self.assertEqual(TEST_USER_ADMIN_LOGIN,
+ validator.to_python(TEST_USER_ADMIN_LOGIN))
+
+ def test_ValidUsersGroup(self):
+ validator = v.ValidUsersGroup()
+ self.assertRaises(formencode.Invalid, validator.to_python, 'default')
+ self.assertRaises(formencode.Invalid, validator.to_python, '.,')
+
+ gr = UsersGroupModel().create('test')
+ gr2 = UsersGroupModel().create('tes2')
+ Session.commit()
+ self.assertRaises(formencode.Invalid, validator.to_python, 'test')
+ assert gr.users_group_id != None
+ validator = v.ValidUsersGroup(edit=True,
+ old_data={'users_group_id':
+ gr2.users_group_id})
+
+ self.assertRaises(formencode.Invalid, validator.to_python, 'test')
+ self.assertRaises(formencode.Invalid, validator.to_python, 'TesT')
+ self.assertRaises(formencode.Invalid, validator.to_python, 'TEST')
+ UsersGroupModel().delete(gr)
+ UsersGroupModel().delete(gr2)
+ Session.commit()
+
+ def test_ValidReposGroup(self):
+ validator = v.ValidReposGroup()
+ model = ReposGroupModel()
+ self.assertRaises(formencode.Invalid, validator.to_python,
+ {'group_name': HG_REPO, })
+ gr = model.create(group_name='test_gr', group_description='desc',
+ parent=None,
+ just_db=True)
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {'group_name': gr.group_name, })
+
+ validator = v.ValidReposGroup(edit=True,
+ old_data={'group_id': gr.group_id})
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {
+ 'group_name': gr.group_name + 'n',
+ 'group_parent_id': gr.group_id
+ })
+ model.delete(gr)
+
+ def test_ValidPassword(self):
+ validator = v.ValidPassword()
+ self.assertEqual('lol', validator.to_python('lol'))
+ self.assertEqual(None, validator.to_python(None))
+ self.assertRaises(formencode.Invalid, validator.to_python, 'ąćżź')
+
+ def test_ValidPasswordsMatch(self):
+ validator = v.ValidPasswordsMatch()
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {'password': 'pass',
+ 'password_confirmation': 'pass2'})
+
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {'new_password': 'pass',
+ 'password_confirmation': 'pass2'})
+
+ self.assertEqual({'new_password': 'pass',
+ 'password_confirmation': 'pass'},
+ validator.to_python({'new_password': 'pass',
+ 'password_confirmation': 'pass'}))
+
+ self.assertEqual({'password': 'pass',
+ 'password_confirmation': 'pass'},
+ validator.to_python({'password': 'pass',
+ 'password_confirmation': 'pass'}))
+
+ def test_ValidAuth(self):
+ validator = v.ValidAuth()
+ valid_creds = {
+ 'username': TEST_USER_REGULAR2_LOGIN,
+ 'password': TEST_USER_REGULAR2_PASS,
+ }
+ invalid_creds = {
+ 'username': 'err',
+ 'password': 'err',
+ }
+ self.assertEqual(valid_creds, validator.to_python(valid_creds))
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, invalid_creds)
+
+ def test_ValidAuthToken(self):
+ validator = v.ValidAuthToken()
+ # this is untestable without a threadlocal
+# self.assertRaises(formencode.Invalid,
+# validator.to_python, 'BadToken')
+ validator
+
+ def test_ValidRepoName(self):
+ validator = v.ValidRepoName()
+
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {'repo_name': ''})
+
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {'repo_name': HG_REPO})
+
+ gr = ReposGroupModel().create(group_name='group_test',
+ group_description='desc',
+ parent=None,)
+ self.assertRaises(formencode.Invalid,
+ validator.to_python, {'repo_name': gr.group_name})
+
+ #TODO: write an error case for that ie. create a repo withinh a group
+# self.assertRaises(formencode.Invalid,
+# validator.to_python, {'repo_name': 'some',
+# 'repo_group': gr.group_id})
+
+ def test_ValidForkName(self):
+ # this uses ValidRepoName validator
+ assert True
+
+ @parameterized.expand([
+ ('test', 'test'), ('lolz!', 'lolz'), (' aavv', 'aavv'),
+ ('ala ma kota', 'ala-ma-kota'), ('@nooo', 'nooo'),
+ ('$!haha lolz !', 'haha-lolz'), ('$$$$$', ''), ('{}OK!', 'OK'),
+ ('/]re po', 're-po')])
+ def test_SlugifyName(self, name, expected):
+ validator = v.SlugifyName()
+ self.assertEqual(expected, validator.to_python(name))
+
+ def test_ValidCloneUri(self):
+ #TODO: write this one
+ pass
+
+ def test_ValidForkType(self):
+ validator = v.ValidForkType(old_data={'repo_type': 'hg'})
+ self.assertEqual('hg', validator.to_python('hg'))
+ self.assertRaises(formencode.Invalid, validator.to_python, 'git')
+
+ def test_ValidPerms(self):
+ #TODO: write this one
+ pass
+
+ def test_ValidSettings(self):
+ validator = v.ValidSettings()
+ self.assertEqual({'pass': 'pass'},
+ validator.to_python(value={'user': 'test',
+ 'pass': 'pass'}))
+
+ self.assertEqual({'user2': 'test', 'pass': 'pass'},
+ validator.to_python(value={'user2': 'test',
+ 'pass': 'pass'}))
+
+ def test_ValidPath(self):
+ validator = v.ValidPath()
+ self.assertEqual(TESTS_TMP_PATH,
+ validator.to_python(TESTS_TMP_PATH))
+ self.assertRaises(formencode.Invalid, validator.to_python,
+ '/no_such_dir')
+
+ def test_UniqSystemEmail(self):
+ validator = v.UniqSystemEmail(old_data={})
+
+ self.assertEqual('mail@python.org',
+ validator.to_python('MaiL@Python.org'))
+
+ email = TEST_USER_REGULAR2_EMAIL
+ self.assertRaises(formencode.Invalid, validator.to_python, email)
+
+ def test_ValidSystemEmail(self):
+ validator = v.ValidSystemEmail()
+ email = TEST_USER_REGULAR2_EMAIL
+
+ self.assertEqual(email, validator.to_python(email))
+ self.assertRaises(formencode.Invalid, validator.to_python, 'err')
+
+ def test_LdapLibValidator(self):
+ validator = v.LdapLibValidator()
+ self.assertRaises(v.LdapImportError, validator.to_python, 'err')
+
+ def test_AttrLoginValidator(self):
+ validator = v.AttrLoginValidator()
+ self.assertRaises(formencode.Invalid, validator.to_python, 123)
+
+ def test_NotReviewedRevisions(self):
+ validator = v.NotReviewedRevisions()
+ rev = '0' * 40
+ # add status for a rev, that should throw an error because it is already
+ # reviewed
+ new_status = ChangesetStatus()
+ new_status.author = ChangesetStatusModel()._get_user(TEST_USER_ADMIN_LOGIN)
+ new_status.repo = ChangesetStatusModel()._get_repo(HG_REPO)
+ new_status.status = ChangesetStatus.STATUS_APPROVED
+ new_status.comment = None
+ new_status.revision = rev
+ Session().add(new_status)
+ Session().commit()
+ try:
+ self.assertRaises(formencode.Invalid, validator.to_python, [rev])
+ finally:
+ Session().delete(new_status)
+ Session().commit()
diff --git a/rhodecode/tests/vcs/__init__.py b/rhodecode/tests/vcs/__init__.py
new file mode 100644
index 00000000..33622602
--- /dev/null
+++ b/rhodecode/tests/vcs/__init__.py
@@ -0,0 +1,56 @@
+"""
+Unit tests for vcs_ library.
+
+In order to run tests we need to prepare our environment first. Tests would be
+run for each engine listed at ``conf.SCM_TESTS`` - keys are aliases from
+``vcs.backends.BACKENDS``.
+
+For each SCM we run tests for, we need some repository. We would use
+repositories location from system environment variables or test suite defaults
+- see ``conf`` module for more detail. We simply try to check if repository at
+certain location exists, if not we would try to fetch them. At ``test_vcs`` or
+``test_common`` we run unit tests common for each repository type and for
+example specific mercurial tests are located at ``test_hg`` module.
+
+Oh, and tests are run with ``unittest.collector`` wrapped by ``collector``
+function at ``tests/__init__.py``.
+
+.. _vcs: http://bitbucket.org/marcinkuzminski/vcs
+.. _unittest: http://pypi.python.org/pypi/unittest
+
+"""
+import os
+from rhodecode.lib import vcs
+from rhodecode.lib.vcs.utils.compat import unittest
+from utils import VCSTestError, SCMFetcher
+from rhodecode.tests import *
+
+
+def setup_package():
+ """
+ Prepares whole package for tests which mainly means it would try to fetch
+ test repositories or use already existing ones.
+ """
+ fetchers = {
+ 'hg': {
+ 'alias': 'hg',
+ 'test_repo_path': TEST_HG_REPO,
+ 'remote_repo': HG_REMOTE_REPO,
+ 'clone_cmd': 'hg clone',
+ },
+ 'git': {
+ 'alias': 'git',
+ 'test_repo_path': TEST_GIT_REPO,
+ 'remote_repo': GIT_REMOTE_REPO,
+ 'clone_cmd': 'git clone --bare',
+ },
+ }
+ try:
+ for scm, fetcher_info in fetchers.items():
+ fetcher = SCMFetcher(**fetcher_info)
+ fetcher.setup()
+ except VCSTestError, err:
+ raise RuntimeError(str(err))
+
+#start_dir = os.path.abspath(os.path.dirname(__file__))
+#unittest.defaultTestLoader.discover(start_dir)
diff --git a/rhodecode/tests/vcs/aconfig b/rhodecode/tests/vcs/aconfig
new file mode 100644
index 00000000..90800e3a
--- /dev/null
+++ b/rhodecode/tests/vcs/aconfig
@@ -0,0 +1,10 @@
+[user]
+name = Foo Bar
+email = foo.bar@example.com
+
+[ui]
+username = Foo Bar foo.bar@example.com
+
+[universal]
+foo = bar
+
diff --git a/rhodecode/tests/vcs/base.py b/rhodecode/tests/vcs/base.py
new file mode 100644
index 00000000..1ab1a1b9
--- /dev/null
+++ b/rhodecode/tests/vcs/base.py
@@ -0,0 +1,111 @@
+"""
+Module providing backend independent mixin class. It requires that
+InMemoryChangeset class is working properly at backend class.
+"""
+import os
+from rhodecode.lib import vcs
+import time
+import shutil
+import datetime
+from rhodecode.lib.vcs.utils.compat import unittest
+
+from conf import SCM_TESTS, get_new_dir
+
+from rhodecode.lib.vcs.nodes import FileNode
+
+
+class BackendTestMixin(object):
+ """
+ This is a backend independent test case class which should be created
+ with ``type`` method.
+
+ It is required to set following attributes at subclass:
+
+ - ``backend_alias``: alias of used backend (see ``vcs.BACKENDS``)
+ - ``repo_path``: path to the repository which would be created for set of
+ tests
+ - ``recreate_repo_per_test``: If set to ``False``, repo would NOT be created
+ before every single test. Defaults to ``True``.
+ """
+ recreate_repo_per_test = True
+
+ @classmethod
+ def get_backend(cls):
+ return vcs.get_backend(cls.backend_alias)
+
+ @classmethod
+ def _get_commits(cls):
+ commits = [
+ {
+ 'message': u'Initial commit',
+ 'author': u'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foobar', content='Foobar'),
+ FileNode('foobar2', content='Foobar II'),
+ FileNode('foo/bar/baz', content='baz here!'),
+ ],
+ },
+ {
+ 'message': u'Changes...',
+ 'author': u'Jane Doe <jane.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 21),
+ 'added': [
+ FileNode('some/new.txt', content='news...'),
+ ],
+ 'changed': [
+ FileNode('foobar', 'Foobar I'),
+ ],
+ 'removed': [],
+ },
+ ]
+ return commits
+
+ @classmethod
+ def setUpClass(cls):
+ Backend = cls.get_backend()
+ cls.backend_class = Backend
+ cls.repo_path = get_new_dir(str(time.time()))
+ cls.repo = Backend(cls.repo_path, create=True)
+ cls.imc = cls.repo.in_memory_changeset
+
+ for commit in cls._get_commits():
+ for node in commit.get('added', []):
+ cls.imc.add(FileNode(node.path, content=node.content))
+ for node in commit.get('changed', []):
+ cls.imc.change(FileNode(node.path, content=node.content))
+ for node in commit.get('removed', []):
+ cls.imc.remove(FileNode(node.path))
+
+ cls.tip = cls.imc.commit(message=unicode(commit['message']),
+ author=unicode(commit['author']),
+ date=commit['date'])
+
+ @classmethod
+ def tearDownClass(cls):
+ if not getattr(cls, 'recreate_repo_per_test', False) and \
+ 'VCS_REMOVE_TEST_DIRS' in os.environ:
+ shutil.rmtree(cls.repo_path)
+
+ def setUp(self):
+ if getattr(self, 'recreate_repo_per_test', False):
+ self.__class__.setUpClass()
+
+ def tearDown(self):
+ if getattr(self, 'recreate_repo_per_test', False) and \
+ 'VCS_REMOVE_TEST_DIRS' in os.environ:
+ shutil.rmtree(self.repo_path)
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s base backend test' % alias).title().split())
+ bases = (BackendTestMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/conf.py b/rhodecode/tests/vcs/conf.py
new file mode 100644
index 00000000..5fa0fb55
--- /dev/null
+++ b/rhodecode/tests/vcs/conf.py
@@ -0,0 +1,62 @@
+"""
+Unit tests configuration module for vcs.
+"""
+
+import os
+import time
+import hashlib
+import tempfile
+import datetime
+from rhodecode.tests import *
+from utils import get_normalized_path
+from os.path import join as jn
+
+TEST_TMP_PATH = TESTS_TMP_PATH
+#__all__ = (
+# 'TEST_HG_REPO', 'TEST_GIT_REPO', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO',
+# 'SCM_TESTS',
+#)
+#
+#SCM_TESTS = ['hg', 'git']
+#uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
+#
+THIS = os.path.abspath(os.path.dirname(__file__))
+#
+#GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
+#
+#TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp')
+#TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO',
+# jn(TEST_TMP_PATH, 'vcs-git'))
+#TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE',
+# jn(TEST_TMP_PATH, 'vcsgitclone%s' % uniq_suffix))
+#TEST_GIT_REPO_PULL = os.environ.get('VCS_TEST_GIT_REPO_PULL',
+# jn(TEST_TMP_PATH, 'vcsgitpull%s' % uniq_suffix))
+#
+#HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
+#TEST_HG_REPO = os.environ.get('VCS_TEST_HG_REPO',
+# jn(TEST_TMP_PATH, 'vcs-hg'))
+#TEST_HG_REPO_CLONE = os.environ.get('VCS_TEST_HG_REPO_CLONE',
+# jn(TEST_TMP_PATH, 'vcshgclone%s' % uniq_suffix))
+#TEST_HG_REPO_PULL = os.environ.get('VCS_TEST_HG_REPO_PULL',
+# jn(TEST_TMP_PATH, 'vcshgpull%s' % uniq_suffix))
+#
+#TEST_DIR = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir())
+#TEST_REPO_PREFIX = 'vcs-test'
+#
+#
+#def get_new_dir(title):
+# """
+# Returns always new directory path.
+# """
+# name = TEST_REPO_PREFIX
+# if title:
+# name = '-'.join((name, title))
+# hex = hashlib.sha1(str(time.time())).hexdigest()
+# name = '-'.join((name, hex))
+# path = os.path.join(TEST_DIR, name)
+# return get_normalized_path(path)
+
+PACKAGE_DIR = os.path.abspath(os.path.join(
+ os.path.dirname(__file__), '..'))
+
+TEST_USER_CONFIG_FILE = jn(THIS, 'aconfig')
diff --git a/rhodecode/tests/vcs/test_archives.py b/rhodecode/tests/vcs/test_archives.py
new file mode 100644
index 00000000..604777ba
--- /dev/null
+++ b/rhodecode/tests/vcs/test_archives.py
@@ -0,0 +1,108 @@
+from __future__ import with_statement
+
+import os
+import tarfile
+import zipfile
+import datetime
+import tempfile
+import StringIO
+from base import BackendTestMixin
+from conf import SCM_TESTS
+from rhodecode.lib.vcs.exceptions import VCSError
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class ArchivesTestCaseMixin(BackendTestMixin):
+
+ @classmethod
+ def _get_commits(cls):
+ start_date = datetime.datetime(2010, 1, 1, 20)
+ for x in xrange(5):
+ yield {
+ 'message': 'Commit %d' % x,
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': start_date + datetime.timedelta(hours=12 * x),
+ 'added': [
+ FileNode('%d/file_%d.txt' % (x, x),
+ content='Foobar %d' % x),
+ ],
+ }
+
+ def test_archive_zip(self):
+ path = tempfile.mkstemp()[1]
+ with open(path, 'wb') as f:
+ self.tip.fill_archive(stream=f, kind='zip', prefix='repo')
+ out = zipfile.ZipFile(path)
+
+ for x in xrange(5):
+ node_path = '%d/file_%d.txt' % (x, x)
+ decompressed = StringIO.StringIO()
+ decompressed.write(out.read('repo/' + node_path))
+ self.assertEqual(
+ decompressed.getvalue(),
+ self.tip.get_node(node_path).content)
+
+ def test_archive_tgz(self):
+ path = tempfile.mkstemp()[1]
+ with open(path, 'wb') as f:
+ self.tip.fill_archive(stream=f, kind='tgz', prefix='repo')
+ outdir = tempfile.mkdtemp()
+
+ outfile = tarfile.open(path, 'r|gz')
+ outfile.extractall(outdir)
+
+ for x in xrange(5):
+ node_path = '%d/file_%d.txt' % (x, x)
+ self.assertEqual(
+ open(os.path.join(outdir, 'repo/' + node_path)).read(),
+ self.tip.get_node(node_path).content)
+
+ def test_archive_tbz2(self):
+ path = tempfile.mkstemp()[1]
+ with open(path, 'w+b') as f:
+ self.tip.fill_archive(stream=f, kind='tbz2', prefix='repo')
+ outdir = tempfile.mkdtemp()
+
+ outfile = tarfile.open(path, 'r|bz2')
+ outfile.extractall(outdir)
+
+ for x in xrange(5):
+ node_path = '%d/file_%d.txt' % (x, x)
+ self.assertEqual(
+ open(os.path.join(outdir, 'repo/' + node_path)).read(),
+ self.tip.get_node(node_path).content)
+
+ def test_archive_default_stream(self):
+ tmppath = tempfile.mkstemp()[1]
+ with open(tmppath, 'w') as stream:
+ self.tip.fill_archive(stream=stream)
+ mystream = StringIO.StringIO()
+ self.tip.fill_archive(stream=mystream)
+ mystream.seek(0)
+ with open(tmppath, 'r') as f:
+ self.assertEqual(f.read(), mystream.read())
+
+ def test_archive_wrong_kind(self):
+ with self.assertRaises(VCSError):
+ self.tip.fill_archive(kind='wrong kind')
+
+ def test_archive_empty_prefix(self):
+ with self.assertRaises(VCSError):
+ self.tip.fill_archive(prefix='')
+
+ def test_archive_prefix_with_leading_slash(self):
+ with self.assertRaises(VCSError):
+ self.tip.fill_archive(prefix='/any')
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s archive test' % alias).title().split())
+ bases = (ArchivesTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_branches.py b/rhodecode/tests/vcs/test_branches.py
new file mode 100644
index 00000000..0d74d454
--- /dev/null
+++ b/rhodecode/tests/vcs/test_branches.py
@@ -0,0 +1,118 @@
+from __future__ import with_statement
+
+from rhodecode.lib import vcs
+import datetime
+from rhodecode.lib.vcs.utils.compat import unittest
+
+from base import BackendTestMixin
+from conf import SCM_TESTS
+
+from rhodecode.lib.vcs.nodes import FileNode
+
+
+class BranchesTestCaseMixin(BackendTestMixin):
+
+ @classmethod
+ def _get_commits(cls):
+ commits = [
+ {
+ 'message': 'Initial commit',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foobar', content='Foobar'),
+ FileNode('foobar2', content='Foobar II'),
+ FileNode('foo/bar/baz', content='baz here!'),
+ ],
+ },
+ {
+ 'message': 'Changes...',
+ 'author': 'Jane Doe <jane.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 21),
+ 'added': [
+ FileNode('some/new.txt', content='news...'),
+ ],
+ 'changed': [
+ FileNode('foobar', 'Foobar I'),
+ ],
+ 'removed': [],
+ },
+ ]
+ return commits
+
+ def test_simple(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(tip.date, datetime.datetime(2010, 1, 1, 21))
+
+ def test_new_branch(self):
+ # This check must not be removed to ensure the 'branches' LazyProperty
+ # gets hit *before* the new 'foobar' branch got created:
+ self.assertFalse('foobar' in self.repo.branches)
+ self.imc.add(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\n'))
+ foobar_tip = self.imc.commit(
+ message=u'New branch: foobar',
+ author=u'joe',
+ branch='foobar',
+ )
+ self.assertTrue('foobar' in self.repo.branches)
+ self.assertEqual(foobar_tip.branch, 'foobar')
+
+ def test_new_head(self):
+ tip = self.repo.get_changeset()
+ self.imc.add(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\n'))
+ foobar_tip = self.imc.commit(
+ message=u'New branch: foobar',
+ author=u'joe',
+ branch='foobar',
+ parents=[tip],
+ )
+ self.imc.change(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\nand more...\n'))
+ newtip = self.imc.commit(
+ message=u'At default branch',
+ author=u'joe',
+ branch=foobar_tip.branch,
+ parents=[foobar_tip],
+ )
+
+ newest_tip = self.imc.commit(
+ message=u'Merged with %s' % foobar_tip.raw_id,
+ author=u'joe',
+ branch=self.backend_class.DEFAULT_BRANCH_NAME,
+ parents=[newtip, foobar_tip],
+ )
+
+ self.assertEqual(newest_tip.branch,
+ self.backend_class.DEFAULT_BRANCH_NAME)
+
+ def test_branch_with_slash_in_name(self):
+ self.imc.add(vcs.nodes.FileNode('extrafile', content='Some data\n'))
+ self.imc.commit(u'Branch with a slash!', author=u'joe',
+ branch='issue/123')
+ self.assertTrue('issue/123' in self.repo.branches)
+
+ def test_branch_with_slash_in_name_and_similar_without(self):
+ self.imc.add(vcs.nodes.FileNode('extrafile', content='Some data\n'))
+ self.imc.commit(u'Branch with a slash!', author=u'joe',
+ branch='issue/123')
+ self.imc.add(vcs.nodes.FileNode('extrafile II', content='Some data\n'))
+ self.imc.commit(u'Branch without a slash...', author=u'joe',
+ branch='123')
+ self.assertIn('issue/123', self.repo.branches)
+ self.assertIn('123', self.repo.branches)
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s branches test' % alias).title().split())
+ bases = (BranchesTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_changesets.py b/rhodecode/tests/vcs/test_changesets.py
new file mode 100644
index 00000000..05e51934
--- /dev/null
+++ b/rhodecode/tests/vcs/test_changesets.py
@@ -0,0 +1,344 @@
+from __future__ import with_statement
+
+from rhodecode.lib import vcs
+import datetime
+from base import BackendTestMixin
+from conf import SCM_TESTS
+from rhodecode.lib.vcs.backends.base import BaseChangeset
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError
+from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
+from rhodecode.lib.vcs.exceptions import RepositoryError
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class TestBaseChangeset(unittest.TestCase):
+
+ def test_as_dict(self):
+ changeset = BaseChangeset()
+ changeset.id = 'ID'
+ changeset.raw_id = 'RAW_ID'
+ changeset.short_id = 'SHORT_ID'
+ changeset.revision = 1009
+ changeset.date = datetime.datetime(2011, 1, 30, 1, 45)
+ changeset.message = 'Message of a commit'
+ changeset.author = 'Joe Doe <joe.doe@example.com>'
+ changeset.added = [FileNode('foo/bar/baz'), FileNode('foobar')]
+ changeset.changed = []
+ changeset.removed = []
+ self.assertEqual(changeset.as_dict(), {
+ 'id': 'ID',
+ 'raw_id': 'RAW_ID',
+ 'short_id': 'SHORT_ID',
+ 'revision': 1009,
+ 'date': datetime.datetime(2011, 1, 30, 1, 45),
+ 'message': 'Message of a commit',
+ 'author': {
+ 'name': 'Joe Doe',
+ 'email': 'joe.doe@example.com',
+ },
+ 'added': ['foo/bar/baz', 'foobar'],
+ 'changed': [],
+ 'removed': [],
+ })
+
+class ChangesetsWithCommitsTestCaseixin(BackendTestMixin):
+ recreate_repo_per_test = True
+
+ @classmethod
+ def _get_commits(cls):
+ start_date = datetime.datetime(2010, 1, 1, 20)
+ for x in xrange(5):
+ yield {
+ 'message': 'Commit %d' % x,
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': start_date + datetime.timedelta(hours=12 * x),
+ 'added': [
+ FileNode('file_%d.txt' % x, content='Foobar %d' % x),
+ ],
+ }
+
+ def test_new_branch(self):
+ self.imc.add(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\n'))
+ foobar_tip = self.imc.commit(
+ message=u'New branch: foobar',
+ author=u'joe',
+ branch='foobar',
+ )
+ self.assertTrue('foobar' in self.repo.branches)
+ self.assertEqual(foobar_tip.branch, 'foobar')
+ # 'foobar' should be the only branch that contains the new commit
+ self.assertNotEqual(*self.repo.branches.values())
+
+ def test_new_head_in_default_branch(self):
+ tip = self.repo.get_changeset()
+ self.imc.add(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\n'))
+ foobar_tip = self.imc.commit(
+ message=u'New branch: foobar',
+ author=u'joe',
+ branch='foobar',
+ parents=[tip],
+ )
+ self.imc.change(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\nand more...\n'))
+ newtip = self.imc.commit(
+ message=u'At default branch',
+ author=u'joe',
+ branch=foobar_tip.branch,
+ parents=[foobar_tip],
+ )
+
+ newest_tip = self.imc.commit(
+ message=u'Merged with %s' % foobar_tip.raw_id,
+ author=u'joe',
+ branch=self.backend_class.DEFAULT_BRANCH_NAME,
+ parents=[newtip, foobar_tip],
+ )
+
+ self.assertEqual(newest_tip.branch,
+ self.backend_class.DEFAULT_BRANCH_NAME)
+
+ def test_get_changesets_respects_branch_name(self):
+ tip = self.repo.get_changeset()
+ self.imc.add(vcs.nodes.FileNode('docs/index.txt',
+ content='Documentation\n'))
+ doc_changeset = self.imc.commit(
+ message=u'New branch: docs',
+ author=u'joe',
+ branch='docs',
+ )
+ self.imc.add(vcs.nodes.FileNode('newfile', content=''))
+ self.imc.commit(
+ message=u'Back in default branch',
+ author=u'joe',
+ parents=[tip],
+ )
+ default_branch_changesets = self.repo.get_changesets(
+ branch_name=self.repo.DEFAULT_BRANCH_NAME)
+ self.assertNotIn(doc_changeset, default_branch_changesets)
+
+ def test_get_changeset_by_branch(self):
+ for branch, sha in self.repo.branches.iteritems():
+ self.assertEqual(sha, self.repo.get_changeset(branch).raw_id)
+
+ def test_get_changeset_by_tag(self):
+ for tag, sha in self.repo.tags.iteritems():
+ self.assertEqual(sha, self.repo.get_changeset(tag).raw_id)
+
+
+class ChangesetsTestCaseMixin(BackendTestMixin):
+ recreate_repo_per_test = False
+
+ @classmethod
+ def _get_commits(cls):
+ start_date = datetime.datetime(2010, 1, 1, 20)
+ for x in xrange(5):
+ yield {
+ 'message': u'Commit %d' % x,
+ 'author': u'Joe Doe <joe.doe@example.com>',
+ 'date': start_date + datetime.timedelta(hours=12 * x),
+ 'added': [
+ FileNode('file_%d.txt' % x, content='Foobar %d' % x),
+ ],
+ }
+
+ def test_simple(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(tip.date, datetime.datetime(2010, 1, 3, 20))
+
+ def test_get_changesets_is_ordered_by_date(self):
+ changesets = list(self.repo.get_changesets())
+ ordered_by_date = sorted(changesets,
+ key=lambda cs: cs.date)
+ self.assertItemsEqual(changesets, ordered_by_date)
+
+ def test_get_changesets_respects_start(self):
+ second_id = self.repo.revisions[1]
+ changesets = list(self.repo.get_changesets(start=second_id))
+ self.assertEqual(len(changesets), 4)
+
+ def test_get_changesets_numerical_id_respects_start(self):
+ second_id = 1
+ changesets = list(self.repo.get_changesets(start=second_id))
+ self.assertEqual(len(changesets), 4)
+
+ def test_get_changesets_includes_start_changeset(self):
+ second_id = self.repo.revisions[1]
+ changesets = list(self.repo.get_changesets(start=second_id))
+ self.assertEqual(changesets[0].raw_id, second_id)
+
+ def test_get_changesets_respects_end(self):
+ second_id = self.repo.revisions[1]
+ changesets = list(self.repo.get_changesets(end=second_id))
+ self.assertEqual(changesets[-1].raw_id, second_id)
+ self.assertEqual(len(changesets), 2)
+
+ def test_get_changesets_numerical_id_respects_end(self):
+ second_id = 1
+ changesets = list(self.repo.get_changesets(end=second_id))
+ self.assertEqual(changesets.index(changesets[-1]), second_id)
+ self.assertEqual(len(changesets), 2)
+
+ def test_get_changesets_respects_both_start_and_end(self):
+ second_id = self.repo.revisions[1]
+ third_id = self.repo.revisions[2]
+ changesets = list(self.repo.get_changesets(start=second_id,
+ end=third_id))
+ self.assertEqual(len(changesets), 2)
+
+ def test_get_changesets_numerical_id_respects_both_start_and_end(self):
+ changesets = list(self.repo.get_changesets(start=2, end=3))
+ self.assertEqual(len(changesets), 2)
+
+ def test_get_changesets_includes_end_changeset(self):
+ second_id = self.repo.revisions[1]
+ changesets = list(self.repo.get_changesets(end=second_id))
+ self.assertEqual(changesets[-1].raw_id, second_id)
+
+ def test_get_changesets_respects_start_date(self):
+ start_date = datetime.datetime(2010, 2, 1)
+ for cs in self.repo.get_changesets(start_date=start_date):
+ self.assertGreaterEqual(cs.date, start_date)
+
+ def test_get_changesets_respects_end_date(self):
+ end_date = datetime.datetime(2010, 2, 1)
+ for cs in self.repo.get_changesets(end_date=end_date):
+ self.assertLessEqual(cs.date, end_date)
+
+ def test_get_changesets_respects_reverse(self):
+ changesets_id_list = [cs.raw_id for cs in
+ self.repo.get_changesets(reverse=True)]
+ self.assertItemsEqual(changesets_id_list, reversed(self.repo.revisions))
+
+ def test_get_filenodes_generator(self):
+ tip = self.repo.get_changeset()
+ filepaths = [node.path for node in tip.get_filenodes_generator()]
+ self.assertItemsEqual(filepaths, ['file_%d.txt' % x for x in xrange(5)])
+
+ def test_size(self):
+ tip = self.repo.get_changeset()
+ size = 5 * len('Foobar N') # Size of 5 files
+ self.assertEqual(tip.size, size)
+
+ def test_author(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(tip.author, u'Joe Doe <joe.doe@example.com>')
+
+ def test_author_name(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(tip.author_name, u'Joe Doe')
+
+ def test_author_email(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(tip.author_email, u'joe.doe@example.com')
+
+ def test_get_changesets_raise_changesetdoesnotexist_for_wrong_start(self):
+ with self.assertRaises(ChangesetDoesNotExistError):
+ list(self.repo.get_changesets(start='foobar'))
+
+ def test_get_changesets_raise_changesetdoesnotexist_for_wrong_end(self):
+ with self.assertRaises(ChangesetDoesNotExistError):
+ list(self.repo.get_changesets(end='foobar'))
+
+ def test_get_changesets_raise_branchdoesnotexist_for_wrong_branch_name(self):
+ with self.assertRaises(BranchDoesNotExistError):
+ list(self.repo.get_changesets(branch_name='foobar'))
+
+ def test_get_changesets_raise_repositoryerror_for_wrong_start_end(self):
+ start = self.repo.revisions[-1]
+ end = self.repo.revisions[0]
+ with self.assertRaises(RepositoryError):
+ list(self.repo.get_changesets(start=start, end=end))
+
+ def test_get_changesets_numerical_id_reversed(self):
+ with self.assertRaises(RepositoryError):
+ [x for x in self.repo.get_changesets(start=3, end=2)]
+
+ def test_get_changesets_numerical_id_respects_both_start_and_end_last(self):
+ with self.assertRaises(RepositoryError):
+ last = len(self.repo.revisions)
+ list(self.repo.get_changesets(start=last-1, end=last-2))
+
+ def test_get_changesets_numerical_id_last_zero_error(self):
+ with self.assertRaises(RepositoryError):
+ last = len(self.repo.revisions)
+ list(self.repo.get_changesets(start=last-1, end=0))
+
+
+class ChangesetsChangesTestCaseMixin(BackendTestMixin):
+ recreate_repo_per_test = False
+
+ @classmethod
+ def _get_commits(cls):
+ return [
+ {
+ 'message': u'Initial',
+ 'author': u'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foo/bar', content='foo'),
+ FileNode('foobar', content='foo'),
+ FileNode('qwe', content='foo'),
+ ],
+ },
+ {
+ 'message': u'Massive changes',
+ 'author': u'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 22),
+ 'added': [FileNode('fallout', content='War never changes')],
+ 'changed': [
+ FileNode('foo/bar', content='baz'),
+ FileNode('foobar', content='baz'),
+ ],
+ 'removed': [FileNode('qwe')],
+ },
+ ]
+
+ def test_initial_commit(self):
+ changeset = self.repo.get_changeset(0)
+ self.assertItemsEqual(changeset.added, [
+ changeset.get_node('foo/bar'),
+ changeset.get_node('foobar'),
+ changeset.get_node('qwe'),
+ ])
+ self.assertItemsEqual(changeset.changed, [])
+ self.assertItemsEqual(changeset.removed, [])
+
+ def test_head_added(self):
+ changeset = self.repo.get_changeset()
+ self.assertItemsEqual(changeset.added, [
+ changeset.get_node('fallout'),
+ ])
+ self.assertItemsEqual(changeset.changed, [
+ changeset.get_node('foo/bar'),
+ changeset.get_node('foobar'),
+ ])
+ self.assertEqual(len(changeset.removed), 1)
+ self.assertEqual(list(changeset.removed)[0].path, 'qwe')
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ # tests with additional commits
+ cls_name = ''.join(('%s changesets with commits test' % alias).title().split())
+ bases = (ChangesetsWithCommitsTestCaseixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+ # tests without additional commits
+ cls_name = ''.join(('%s changesets test' % alias).title().split())
+ bases = (ChangesetsTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+ # tests changes
+ cls_name = ''.join(('%s changesets changes test' % alias).title().split())
+ bases = (ChangesetsChangesTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_filenodes_unicode_path.py b/rhodecode/tests/vcs/test_filenodes_unicode_path.py
new file mode 100644
index 00000000..9d4e727d
--- /dev/null
+++ b/rhodecode/tests/vcs/test_filenodes_unicode_path.py
@@ -0,0 +1,49 @@
+# encoding: utf8
+
+from __future__ import with_statement
+
+import datetime
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+from test_inmemchangesets import BackendBaseTestCase
+from conf import SCM_TESTS
+
+
+class FileNodeUnicodePathTestsMixin(object):
+
+ fname = 'ąśðąęłąć.txt'
+ ufname = (fname).decode('utf-8')
+
+ def get_commits(self):
+ self.nodes = [
+ FileNode(self.fname, content='Foobar'),
+ ]
+
+ commits = [
+ {
+ 'message': 'Initial commit',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': self.nodes,
+ },
+ ]
+ return commits
+
+ def test_filenode_path(self):
+ node = self.tip.get_node(self.fname)
+ unode = self.tip.get_node(self.ufname)
+ self.assertEqual(node, unode)
+
+
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s file node unicode path test' % alias).title()
+ .split())
+ bases = (FileNodeUnicodePathTestsMixin, BackendBaseTestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_getitem.py b/rhodecode/tests/vcs/test_getitem.py
new file mode 100644
index 00000000..c08b5c2d
--- /dev/null
+++ b/rhodecode/tests/vcs/test_getitem.py
@@ -0,0 +1,44 @@
+from __future__ import with_statement
+
+import datetime
+from base import BackendTestMixin
+from conf import SCM_TESTS
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class GetitemTestCaseMixin(BackendTestMixin):
+
+ @classmethod
+ def _get_commits(cls):
+ start_date = datetime.datetime(2010, 1, 1, 20)
+ for x in xrange(5):
+ yield {
+ 'message': 'Commit %d' % x,
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': start_date + datetime.timedelta(hours=12 * x),
+ 'added': [
+ FileNode('file_%d.txt' % x, content='Foobar %d' % x),
+ ],
+ }
+
+ def test__getitem__last_item_is_tip(self):
+ self.assertEqual(self.repo[-1], self.repo.get_changeset())
+
+ def test__getitem__returns_correct_items(self):
+ changesets = [self.repo[x] for x in xrange(len(self.repo.revisions))]
+ self.assertEqual(changesets, list(self.repo.get_changesets()))
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s getitem test' % alias).title().split())
+ bases = (GetitemTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_getslice.py b/rhodecode/tests/vcs/test_getslice.py
new file mode 100644
index 00000000..b49daaa9
--- /dev/null
+++ b/rhodecode/tests/vcs/test_getslice.py
@@ -0,0 +1,56 @@
+from __future__ import with_statement
+
+import datetime
+from base import BackendTestMixin
+from conf import SCM_TESTS
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class GetsliceTestCaseMixin(BackendTestMixin):
+
+ @classmethod
+ def _get_commits(cls):
+ start_date = datetime.datetime(2010, 1, 1, 20)
+ for x in xrange(5):
+ yield {
+ 'message': 'Commit %d' % x,
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': start_date + datetime.timedelta(hours=12 * x),
+ 'added': [
+ FileNode('file_%d.txt' % x, content='Foobar %d' % x),
+ ],
+ }
+
+ def test__getslice__last_item_is_tip(self):
+ self.assertEqual(list(self.repo[-1:])[0], self.repo.get_changeset())
+
+ def test__getslice__respects_start_index(self):
+ self.assertEqual(list(self.repo[2:]),
+ [self.repo.get_changeset(rev) for rev in self.repo.revisions[2:]])
+
+ def test__getslice__respects_negative_start_index(self):
+ self.assertEqual(list(self.repo[-2:]),
+ [self.repo.get_changeset(rev) for rev in self.repo.revisions[-2:]])
+
+ def test__getslice__respects_end_index(self):
+ self.assertEqual(list(self.repo[:2]),
+ [self.repo.get_changeset(rev) for rev in self.repo.revisions[:2]])
+
+ def test__getslice__respects_negative_end_index(self):
+ self.assertEqual(list(self.repo[:-2]),
+ [self.repo.get_changeset(rev) for rev in self.repo.revisions[:-2]])
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s getslice test' % alias).title().split())
+ bases = (GetsliceTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_git.py b/rhodecode/tests/vcs/test_git.py
new file mode 100644
index 00000000..f43e1542
--- /dev/null
+++ b/rhodecode/tests/vcs/test_git.py
@@ -0,0 +1,702 @@
+from __future__ import with_statement
+
+import os
+import mock
+import datetime
+from rhodecode.lib.vcs.backends.git import GitRepository, GitChangeset
+from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
+from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState
+from rhodecode.lib.vcs.utils.compat import unittest
+from rhodecode.tests.vcs.base import BackendTestMixin
+from conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
+
+
+class GitRepositoryTest(unittest.TestCase):
+
+ def __check_for_existing_repo(self):
+ if os.path.exists(TEST_GIT_REPO_CLONE):
+ self.fail('Cannot test git clone repo as location %s already '
+ 'exists. You should manually remove it first.'
+ % TEST_GIT_REPO_CLONE)
+
+ def setUp(self):
+ self.repo = GitRepository(TEST_GIT_REPO)
+
+ def test_wrong_repo_path(self):
+ wrong_repo_path = '/tmp/errorrepo'
+ self.assertRaises(RepositoryError, GitRepository, wrong_repo_path)
+
+ def test_repo_clone(self):
+ self.__check_for_existing_repo()
+ repo = GitRepository(TEST_GIT_REPO)
+ repo_clone = GitRepository(TEST_GIT_REPO_CLONE,
+ src_url=TEST_GIT_REPO, create=True, update_after_clone=True)
+ self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
+ # Checking hashes of changesets should be enough
+ for changeset in repo.get_changesets():
+ raw_id = changeset.raw_id
+ self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
+
+ def test_repo_clone_without_create(self):
+ self.assertRaises(RepositoryError, GitRepository,
+ TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO)
+
+ def test_repo_clone_with_update(self):
+ repo = GitRepository(TEST_GIT_REPO)
+ clone_path = TEST_GIT_REPO_CLONE + '_with_update'
+ repo_clone = GitRepository(clone_path,
+ create=True, src_url=TEST_GIT_REPO, update_after_clone=True)
+ self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
+
+ #check if current workdir was updated
+ fpath = os.path.join(clone_path, 'MANIFEST.in')
+ self.assertEqual(True, os.path.isfile(fpath),
+ 'Repo was cloned and updated but file %s could not be found'
+ % fpath)
+
+ def test_repo_clone_without_update(self):
+ repo = GitRepository(TEST_GIT_REPO)
+ clone_path = TEST_GIT_REPO_CLONE + '_without_update'
+ repo_clone = GitRepository(clone_path,
+ create=True, src_url=TEST_GIT_REPO, update_after_clone=False)
+ self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
+ #check if current workdir was *NOT* updated
+ fpath = os.path.join(clone_path, 'MANIFEST.in')
+ # Make sure it's not bare repo
+ self.assertFalse(repo_clone._repo.bare)
+ self.assertEqual(False, os.path.isfile(fpath),
+ 'Repo was cloned and updated but file %s was found'
+ % fpath)
+
+ def test_repo_clone_into_bare_repo(self):
+ repo = GitRepository(TEST_GIT_REPO)
+ clone_path = TEST_GIT_REPO_CLONE + '_bare.git'
+ repo_clone = GitRepository(clone_path, create=True,
+ src_url=repo.path, bare=True)
+ self.assertTrue(repo_clone._repo.bare)
+
+ def test_create_repo_is_not_bare_by_default(self):
+ repo = GitRepository(get_new_dir('not-bare-by-default'), create=True)
+ self.assertFalse(repo._repo.bare)
+
+ def test_create_bare_repo(self):
+ repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True)
+ self.assertTrue(repo._repo.bare)
+
+ def test_revisions(self):
+ # there are 112 revisions (by now)
+ # so we can assume they would be available from now on
+ subset = set([
+ 'c1214f7e79e02fc37156ff215cd71275450cffc3',
+ '38b5fe81f109cb111f549bfe9bb6b267e10bc557',
+ 'fa6600f6848800641328adbf7811fd2372c02ab2',
+ '102607b09cdd60e2793929c4f90478be29f85a17',
+ '49d3fd156b6f7db46313fac355dca1a0b94a0017',
+ '2d1028c054665b962fa3d307adfc923ddd528038',
+ 'd7e0d30fbcae12c90680eb095a4f5f02505ce501',
+ 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
+ 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
+ '8430a588b43b5d6da365400117c89400326e7992',
+ 'd955cd312c17b02143c04fa1099a352b04368118',
+ 'f67b87e5c629c2ee0ba58f85197e423ff28d735b',
+ 'add63e382e4aabc9e1afdc4bdc24506c269b7618',
+ 'f298fe1189f1b69779a4423f40b48edf92a703fc',
+ 'bd9b619eb41994cac43d67cf4ccc8399c1125808',
+ '6e125e7c890379446e98980d8ed60fba87d0f6d1',
+ 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd',
+ '0b05e4ed56c802098dfc813cbe779b2f49e92500',
+ '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
+ '45223f8f114c64bf4d6f853e3c35a369a6305520',
+ 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
+ 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68',
+ '27d48942240f5b91dfda77accd2caac94708cc7d',
+ '622f0eb0bafd619d2560c26f80f09e3b0b0d78af',
+ 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'])
+ self.assertTrue(subset.issubset(set(self.repo.revisions)))
+
+
+
+ def test_slicing(self):
+ #4 1 5 10 95
+ for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
+ (10, 20, 10), (5, 100, 95)]:
+ revs = list(self.repo[sfrom:sto])
+ self.assertEqual(len(revs), size)
+ self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
+ self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
+
+
+ def test_branches(self):
+ # TODO: Need more tests here
+ # Removed (those are 'remotes' branches for cloned repo)
+ #self.assertTrue('master' in self.repo.branches)
+ #self.assertTrue('gittree' in self.repo.branches)
+ #self.assertTrue('web-branch' in self.repo.branches)
+ for name, id in self.repo.branches.items():
+ self.assertTrue(isinstance(
+ self.repo.get_changeset(id), GitChangeset))
+
+ def test_tags(self):
+ # TODO: Need more tests here
+ self.assertTrue('v0.1.1' in self.repo.tags)
+ self.assertTrue('v0.1.2' in self.repo.tags)
+ for name, id in self.repo.tags.items():
+ self.assertTrue(isinstance(
+ self.repo.get_changeset(id), GitChangeset))
+
+ def _test_single_changeset_cache(self, revision):
+ chset = self.repo.get_changeset(revision)
+ self.assertTrue(revision in self.repo.changesets)
+ self.assertTrue(chset is self.repo.changesets[revision])
+
+ def test_initial_changeset(self):
+ id = self.repo.revisions[0]
+ init_chset = self.repo.get_changeset(id)
+ self.assertEqual(init_chset.message, 'initial import\n')
+ self.assertEqual(init_chset.author,
+ 'Marcin Kuzminski <marcin@python-blog.com>')
+ for path in ('vcs/__init__.py',
+ 'vcs/backends/BaseRepository.py',
+ 'vcs/backends/__init__.py'):
+ self.assertTrue(isinstance(init_chset.get_node(path), FileNode))
+ for path in ('', 'vcs', 'vcs/backends'):
+ self.assertTrue(isinstance(init_chset.get_node(path), DirNode))
+
+ self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
+
+ node = init_chset.get_node('vcs/')
+ self.assertTrue(hasattr(node, 'kind'))
+ self.assertEqual(node.kind, NodeKind.DIR)
+
+ node = init_chset.get_node('vcs')
+ self.assertTrue(hasattr(node, 'kind'))
+ self.assertEqual(node.kind, NodeKind.DIR)
+
+ node = init_chset.get_node('vcs/__init__.py')
+ self.assertTrue(hasattr(node, 'kind'))
+ self.assertEqual(node.kind, NodeKind.FILE)
+
+ def test_not_existing_changeset(self):
+ self.assertRaises(RepositoryError, self.repo.get_changeset,
+ 'f' * 40)
+
+ def test_changeset10(self):
+
+ chset10 = self.repo.get_changeset(self.repo.revisions[9])
+ README = """===
+VCS
+===
+
+Various Version Control System management abstraction layer for Python.
+
+Introduction
+------------
+
+TODO: To be written...
+
+"""
+ node = chset10.get_node('README.rst')
+ self.assertEqual(node.kind, NodeKind.FILE)
+ self.assertEqual(node.content, README)
+
+
+class GitChangesetTest(unittest.TestCase):
+
+ def setUp(self):
+ self.repo = GitRepository(TEST_GIT_REPO)
+
+ def test_default_changeset(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(tip, self.repo.get_changeset(None))
+ self.assertEqual(tip, self.repo.get_changeset('tip'))
+
+ def test_root_node(self):
+ tip = self.repo.get_changeset()
+ self.assertTrue(tip.root is tip.get_node(''))
+
+ def test_lazy_fetch(self):
+ """
+ Test if changeset's nodes expands and are cached as we walk through
+ the revision. This test is somewhat hard to write as order of tests
+ is a key here. Written by running command after command in a shell.
+ """
+ hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
+ self.assertTrue(hex in self.repo.revisions)
+ chset = self.repo.get_changeset(hex)
+ self.assertTrue(len(chset.nodes) == 0)
+ root = chset.root
+ self.assertTrue(len(chset.nodes) == 1)
+ self.assertTrue(len(root.nodes) == 8)
+ # accessing root.nodes updates chset.nodes
+ self.assertTrue(len(chset.nodes) == 9)
+
+ docs = root.get_node('docs')
+ # we haven't yet accessed anything new as docs dir was already cached
+ self.assertTrue(len(chset.nodes) == 9)
+ self.assertTrue(len(docs.nodes) == 8)
+ # accessing docs.nodes updates chset.nodes
+ self.assertTrue(len(chset.nodes) == 17)
+
+ self.assertTrue(docs is chset.get_node('docs'))
+ self.assertTrue(docs is root.nodes[0])
+ self.assertTrue(docs is root.dirs[0])
+ self.assertTrue(docs is chset.get_node('docs'))
+
+ def test_nodes_with_changeset(self):
+ hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
+ chset = self.repo.get_changeset(hex)
+ root = chset.root
+ docs = root.get_node('docs')
+ self.assertTrue(docs is chset.get_node('docs'))
+ api = docs.get_node('api')
+ self.assertTrue(api is chset.get_node('docs/api'))
+ index = api.get_node('index.rst')
+ self.assertTrue(index is chset.get_node('docs/api/index.rst'))
+ self.assertTrue(index is chset.get_node('docs')\
+ .get_node('api')\
+ .get_node('index.rst'))
+
+ def test_branch_and_tags(self):
+ '''
+ rev0 = self.repo.revisions[0]
+ chset0 = self.repo.get_changeset(rev0)
+ self.assertEqual(chset0.branch, 'master')
+ self.assertEqual(chset0.tags, [])
+
+ rev10 = self.repo.revisions[10]
+ chset10 = self.repo.get_changeset(rev10)
+ self.assertEqual(chset10.branch, 'master')
+ self.assertEqual(chset10.tags, [])
+
+ rev44 = self.repo.revisions[44]
+ chset44 = self.repo.get_changeset(rev44)
+ self.assertEqual(chset44.branch, 'web-branch')
+
+ tip = self.repo.get_changeset('tip')
+ self.assertTrue('tip' in tip.tags)
+ '''
+ # Those tests would fail - branches are now going
+ # to be changed at main API in order to support git backend
+ pass
+
+ def _test_slices(self, limit, offset):
+ count = self.repo.count()
+ changesets = self.repo.get_changesets(limit=limit, offset=offset)
+ idx = 0
+ for changeset in changesets:
+ rev = offset + idx
+ idx += 1
+ rev_id = self.repo.revisions[rev]
+ if idx > limit:
+ self.fail("Exceeded limit already (getting revision %s, "
+ "there are %s total revisions, offset=%s, limit=%s)"
+ % (rev_id, count, offset, limit))
+ self.assertEqual(changeset, self.repo.get_changeset(rev_id))
+ result = list(self.repo.get_changesets(limit=limit, offset=offset))
+ start = offset
+ end = limit and offset + limit or None
+ sliced = list(self.repo[start:end])
+ self.failUnlessEqual(result, sliced,
+ msg="Comparison failed for limit=%s, offset=%s"
+ "(get_changeset returned: %s and sliced: %s"
+ % (limit, offset, result, sliced))
+
+ def _test_file_size(self, revision, path, size):
+ node = self.repo.get_changeset(revision).get_node(path)
+ self.assertTrue(node.is_file())
+ self.assertEqual(node.size, size)
+
+ def test_file_size(self):
+ to_check = (
+ ('c1214f7e79e02fc37156ff215cd71275450cffc3',
+ 'vcs/backends/BaseRepository.py', 502),
+ ('d7e0d30fbcae12c90680eb095a4f5f02505ce501',
+ 'vcs/backends/hg.py', 854),
+ ('6e125e7c890379446e98980d8ed60fba87d0f6d1',
+ 'setup.py', 1068),
+
+ ('d955cd312c17b02143c04fa1099a352b04368118',
+ 'vcs/backends/base.py', 2921),
+ ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
+ 'vcs/backends/base.py', 3936),
+ ('f50f42baeed5af6518ef4b0cb2f1423f3851a941',
+ 'vcs/backends/base.py', 6189),
+ )
+ for revision, path, size in to_check:
+ self._test_file_size(revision, path, size)
+
+ def test_file_history(self):
+ # we can only check if those revisions are present in the history
+ # as we cannot update this test every time file is changed
+ files = {
+ 'setup.py': [
+ '54386793436c938cff89326944d4c2702340037d',
+ '51d254f0ecf5df2ce50c0b115741f4cf13985dab',
+ '998ed409c795fec2012b1c0ca054d99888b22090',
+ '5e0eb4c47f56564395f76333f319d26c79e2fb09',
+ '0115510b70c7229dbc5dc49036b32e7d91d23acd',
+ '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e',
+ '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
+ '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
+ 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
+ ],
+ 'vcs/nodes.py': [
+ '33fa3223355104431402a888fa77a4e9956feb3e',
+ 'fa014c12c26d10ba682fadb78f2a11c24c8118e1',
+ 'e686b958768ee96af8029fe19c6050b1a8dd3b2b',
+ 'ab5721ca0a081f26bf43d9051e615af2cc99952f',
+ 'c877b68d18e792a66b7f4c529ea02c8f80801542',
+ '4313566d2e417cb382948f8d9d7c765330356054',
+ '6c2303a793671e807d1cfc70134c9ca0767d98c2',
+ '54386793436c938cff89326944d4c2702340037d',
+ '54000345d2e78b03a99d561399e8e548de3f3203',
+ '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b',
+ '2d03ca750a44440fb5ea8b751176d1f36f8e8f46',
+ '2a08b128c206db48c2f0b8f70df060e6db0ae4f8',
+ '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b',
+ 'ac71e9503c2ca95542839af0ce7b64011b72ea7c',
+ '12669288fd13adba2a9b7dd5b870cc23ffab92d2',
+ '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382',
+ '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5',
+ '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
+ 'f50f42baeed5af6518ef4b0cb2f1423f3851a941',
+ 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25',
+ 'f15c21f97864b4f071cddfbf2750ec2e23859414',
+ 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade',
+ 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b',
+ '84dec09632a4458f79f50ddbbd155506c460b4f9',
+ '0115510b70c7229dbc5dc49036b32e7d91d23acd',
+ '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
+ '3bf1c5868e570e39569d094f922d33ced2fa3b2b',
+ 'b8d04012574729d2c29886e53b1a43ef16dd00a1',
+ '6970b057cffe4aab0a792aa634c89f4bebf01441',
+ 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
+ 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
+ ],
+ 'vcs/backends/git.py': [
+ '4cf116ad5a457530381135e2f4c453e68a1b0105',
+ '9a751d84d8e9408e736329767387f41b36935153',
+ 'cb681fb539c3faaedbcdf5ca71ca413425c18f01',
+ '428f81bb652bcba8d631bce926e8834ff49bdcc6',
+ '180ab15aebf26f98f714d8c68715e0f05fa6e1c7',
+ '2b8e07312a2e89e92b90426ab97f349f4bce2a3a',
+ '50e08c506174d8645a4bb517dd122ac946a0f3bf',
+ '54000345d2e78b03a99d561399e8e548de3f3203',
+ ],
+ }
+ for path, revs in files.items():
+ node = self.repo.get_changeset(revs[0]).get_node(path)
+ node_revs = [chset.raw_id for chset in node.history]
+ self.assertTrue(set(revs).issubset(set(node_revs)),
+ "We assumed that %s is subset of revisions for which file %s "
+ "has been changed, and history of that node returned: %s"
+ % (revs, path, node_revs))
+
+ def test_file_annotate(self):
+ files = {
+ 'vcs/backends/__init__.py': {
+ 'c1214f7e79e02fc37156ff215cd71275450cffc3': {
+ 'lines_no': 1,
+ 'changesets': [
+ 'c1214f7e79e02fc37156ff215cd71275450cffc3',
+ ],
+ },
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': {
+ 'lines_no': 21,
+ 'changesets': [
+ '49d3fd156b6f7db46313fac355dca1a0b94a0017',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ ],
+ },
+ 'e29b67bd158580fc90fc5e9111240b90e6e86064': {
+ 'lines_no': 32,
+ 'changesets': [
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '54000345d2e78b03a99d561399e8e548de3f3203',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '78c3f0c23b7ee935ec276acb8b8212444c33c396',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '78c3f0c23b7ee935ec276acb8b8212444c33c396',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '992f38217b979d0b0987d0bae3cc26dac85d9b19',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
+ ],
+ },
+ },
+ }
+
+ for fname, revision_dict in files.items():
+ for rev, data in revision_dict.items():
+ cs = self.repo.get_changeset(rev)
+ ann = cs.get_file_annotate(fname)
+
+ l1 = [x[1].raw_id for x in ann]
+ l2 = files[fname][rev]['changesets']
+ self.assertTrue(l1 == l2 , "The lists of revision for %s@rev %s"
+ "from annotation list should match each other, "
+ "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
+
+ def test_files_state(self):
+ """
+ Tests state of FileNodes.
+ """
+ node = self.repo\
+ .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0')\
+ .get_node('vcs/utils/diffs.py')
+ self.assertTrue(node.state, NodeState.ADDED)
+ self.assertTrue(node.added)
+ self.assertFalse(node.changed)
+ self.assertFalse(node.not_changed)
+ self.assertFalse(node.removed)
+
+ node = self.repo\
+ .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e')\
+ .get_node('.hgignore')
+ self.assertTrue(node.state, NodeState.CHANGED)
+ self.assertFalse(node.added)
+ self.assertTrue(node.changed)
+ self.assertFalse(node.not_changed)
+ self.assertFalse(node.removed)
+
+ node = self.repo\
+ .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064')\
+ .get_node('setup.py')
+ self.assertTrue(node.state, NodeState.NOT_CHANGED)
+ self.assertFalse(node.added)
+ self.assertFalse(node.changed)
+ self.assertTrue(node.not_changed)
+ self.assertFalse(node.removed)
+
+ # If node has REMOVED state then trying to fetch it would raise
+ # ChangesetError exception
+ chset = self.repo.get_changeset(
+ 'fa6600f6848800641328adbf7811fd2372c02ab2')
+ path = 'vcs/backends/BaseRepository.py'
+ self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
+ # but it would be one of ``removed`` (changeset's attribute)
+ self.assertTrue(path in [rf.path for rf in chset.removed])
+
+ chset = self.repo.get_changeset(
+ '54386793436c938cff89326944d4c2702340037d')
+ changed = ['setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py',
+ 'vcs/nodes.py']
+ self.assertEqual(set(changed), set([f.path for f in chset.changed]))
+
+ def test_commit_message_is_unicode(self):
+ for cs in self.repo:
+ self.assertEqual(type(cs.message), unicode)
+
+ def test_changeset_author_is_unicode(self):
+ for cs in self.repo:
+ self.assertEqual(type(cs.author), unicode)
+
+ def test_repo_files_content_is_unicode(self):
+ changeset = self.repo.get_changeset()
+ for node in changeset.get_node('/'):
+ if node.is_file():
+ self.assertEqual(type(node.content), unicode)
+
+ def test_wrong_path(self):
+ # There is 'setup.py' in the root dir but not there:
+ path = 'foo/bar/setup.py'
+ tip = self.repo.get_changeset()
+ self.assertRaises(VCSError, tip.get_node, path)
+
+ def test_author_email(self):
+ self.assertEqual('marcin@python-blog.com',
+ self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3')\
+ .author_email)
+ self.assertEqual('lukasz.balcerzak@python-center.pl',
+ self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b')\
+ .author_email)
+ self.assertEqual('none@none',
+ self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992')\
+ .author_email)
+
+ def test_author_username(self):
+ self.assertEqual('Marcin Kuzminski',
+ self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3')\
+ .author_name)
+ self.assertEqual('Lukasz Balcerzak',
+ self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b')\
+ .author_name)
+ self.assertEqual('marcink',
+ self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992')\
+ .author_name)
+
+
+class GitSpecificTest(unittest.TestCase):
+
+ def test_error_is_raised_for_added_if_diff_name_status_is_wrong(self):
+ repo = mock.MagicMock()
+ changeset = GitChangeset(repo, 'foobar')
+ changeset._diff_name_status = 'foobar'
+ with self.assertRaises(VCSError):
+ changeset.added
+
+ def test_error_is_raised_for_changed_if_diff_name_status_is_wrong(self):
+ repo = mock.MagicMock()
+ changeset = GitChangeset(repo, 'foobar')
+ changeset._diff_name_status = 'foobar'
+ with self.assertRaises(VCSError):
+ changeset.added
+
+ def test_error_is_raised_for_removed_if_diff_name_status_is_wrong(self):
+ repo = mock.MagicMock()
+ changeset = GitChangeset(repo, 'foobar')
+ changeset._diff_name_status = 'foobar'
+ with self.assertRaises(VCSError):
+ changeset.added
+
+
+class GitSpecificWithRepoTest(BackendTestMixin, unittest.TestCase):
+ backend_alias = 'git'
+
+ @classmethod
+ def _get_commits(cls):
+ return [
+ {
+ 'message': 'Initial',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foobar/static/js/admin/base.js', content='base'),
+ FileNode('foobar/static/admin', content='admin',
+ mode=0120000), # this is a link
+ FileNode('foo', content='foo'),
+ ],
+ },
+ {
+ 'message': 'Second',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 22),
+ 'added': [
+ FileNode('foo2', content='foo2'),
+ ],
+ },
+ ]
+
+ def test_paths_slow_traversing(self):
+ cs = self.repo.get_changeset()
+ self.assertEqual(cs.get_node('foobar').get_node('static').get_node('js')
+ .get_node('admin').get_node('base.js').content, 'base')
+
+ def test_paths_fast_traversing(self):
+ cs = self.repo.get_changeset()
+ self.assertEqual(cs.get_node('foobar/static/js/admin/base.js').content,
+ 'base')
+
+ def test_workdir_get_branch(self):
+ self.repo.run_git_command('checkout -b production')
+ # Regression test: one of following would fail if we don't check
+ # .git/HEAD file
+ self.repo.run_git_command('checkout production')
+ self.assertEqual(self.repo.workdir.get_branch(), 'production')
+ self.repo.run_git_command('checkout master')
+ self.assertEqual(self.repo.workdir.get_branch(), 'master')
+
+ def test_get_diff_runs_git_command_with_hashes(self):
+ self.repo.run_git_command = mock.Mock(return_value=['', ''])
+ self.repo.get_diff(0, 1)
+ self.repo.run_git_command.assert_called_once_with('diff -U%s %s %s' %
+ (3, self.repo._get_revision(0), self.repo._get_revision(1)))
+
+ def test_get_diff_runs_git_command_with_str_hashes(self):
+ self.repo.run_git_command = mock.Mock(return_value=['', ''])
+ self.repo.get_diff(self.repo.EMPTY_CHANGESET, 1)
+ self.repo.run_git_command.assert_called_once_with('show -U%s %s' %
+ (3, self.repo._get_revision(1)))
+
+ def test_get_diff_runs_git_command_with_path_if_its_given(self):
+ self.repo.run_git_command = mock.Mock(return_value=['', ''])
+ self.repo.get_diff(0, 1, 'foo')
+ self.repo.run_git_command.assert_called_once_with('diff -U%s %s %s -- "foo"'
+ % (3, self.repo._get_revision(0), self.repo._get_revision(1)))
+
+
+class GitRegressionTest(BackendTestMixin, unittest.TestCase):
+ backend_alias = 'git'
+
+ @classmethod
+ def _get_commits(cls):
+ return [
+ {
+ 'message': 'Initial',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('bot/__init__.py', content='base'),
+ FileNode('bot/templates/404.html', content='base'),
+ FileNode('bot/templates/500.html', content='base'),
+ ],
+ },
+ {
+ 'message': 'Second',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 22),
+ 'added': [
+ FileNode('bot/build/migrations/1.py', content='foo2'),
+ FileNode('bot/build/migrations/2.py', content='foo2'),
+ FileNode('bot/build/static/templates/f.html', content='foo2'),
+ FileNode('bot/build/static/templates/f1.html', content='foo2'),
+ FileNode('bot/build/templates/err.html', content='foo2'),
+ FileNode('bot/build/templates/err2.html', content='foo2'),
+ ],
+ },
+ ]
+
+ def test_similar_paths(self):
+ cs = self.repo.get_changeset()
+ paths = lambda *n:[x.path for x in n]
+ self.assertEqual(paths(*cs.get_nodes('bot')), ['bot/build', 'bot/templates', 'bot/__init__.py'])
+ self.assertEqual(paths(*cs.get_nodes('bot/build')), ['bot/build/migrations', 'bot/build/static', 'bot/build/templates'])
+ self.assertEqual(paths(*cs.get_nodes('bot/build/static')), ['bot/build/static/templates'])
+ # this get_nodes below causes troubles !
+ self.assertEqual(paths(*cs.get_nodes('bot/build/static/templates')), ['bot/build/static/templates/f.html', 'bot/build/static/templates/f1.html'])
+ self.assertEqual(paths(*cs.get_nodes('bot/build/templates')), ['bot/build/templates/err.html', 'bot/build/templates/err2.html'])
+ self.assertEqual(paths(*cs.get_nodes('bot/templates/')), ['bot/templates/404.html', 'bot/templates/500.html'])
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_hg.py b/rhodecode/tests/vcs/test_hg.py
new file mode 100644
index 00000000..2b9a5e5d
--- /dev/null
+++ b/rhodecode/tests/vcs/test_hg.py
@@ -0,0 +1,557 @@
+from __future__ import with_statement
+
+import os
+from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
+from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
+from rhodecode.lib.vcs.nodes import NodeKind, NodeState
+from conf import PACKAGE_DIR, TEST_HG_REPO, TEST_HG_REPO_CLONE, \
+ TEST_HG_REPO_PULL
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+# Use only clean mercurial's ui
+import mercurial.scmutil
+mercurial.scmutil.rcpath()
+if mercurial.scmutil._rcpath:
+ mercurial.scmutil._rcpath = mercurial.scmutil._rcpath[:1]
+
+
+class MercurialRepositoryTest(unittest.TestCase):
+
+ def __check_for_existing_repo(self):
+ if os.path.exists(TEST_HG_REPO_CLONE):
+ self.fail('Cannot test mercurial clone repo as location %s already '
+ 'exists. You should manually remove it first.'
+ % TEST_HG_REPO_CLONE)
+
+ def setUp(self):
+ self.repo = MercurialRepository(TEST_HG_REPO)
+
+ def test_wrong_repo_path(self):
+ wrong_repo_path = '/tmp/errorrepo'
+ self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
+
+ def test_unicode_path_repo(self):
+ self.assertRaises(VCSError,lambda:MercurialRepository(u'iShouldFail'))
+
+ def test_repo_clone(self):
+ self.__check_for_existing_repo()
+ repo = MercurialRepository(TEST_HG_REPO)
+ repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
+ src_url=TEST_HG_REPO, update_after_clone=True)
+ self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
+ # Checking hashes of changesets should be enough
+ for changeset in repo.get_changesets():
+ raw_id = changeset.raw_id
+ self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
+
+ def test_repo_clone_with_update(self):
+ repo = MercurialRepository(TEST_HG_REPO)
+ repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
+ src_url=TEST_HG_REPO, update_after_clone=True)
+ self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
+
+ #check if current workdir was updated
+ self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
+ + '_w_update',
+ 'MANIFEST.in')), True,)
+
+ def test_repo_clone_without_update(self):
+ repo = MercurialRepository(TEST_HG_REPO)
+ repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
+ src_url=TEST_HG_REPO, update_after_clone=False)
+ self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
+ self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
+ + '_wo_update',
+ 'MANIFEST.in')), False,)
+
+ def test_pull(self):
+ if os.path.exists(TEST_HG_REPO_PULL):
+ self.fail('Cannot test mercurial pull command as location %s '
+ 'already exists. You should manually remove it first'
+ % TEST_HG_REPO_PULL)
+ repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
+ self.assertTrue(len(self.repo.revisions) > len(repo_new.revisions))
+
+ repo_new.pull(self.repo.path)
+ repo_new = MercurialRepository(TEST_HG_REPO_PULL)
+ self.assertTrue(len(self.repo.revisions) == len(repo_new.revisions))
+
+ def test_revisions(self):
+ # there are 21 revisions at bitbucket now
+ # so we can assume they would be available from now on
+ subset = set(['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
+ '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
+ '6cba7170863a2411822803fa77a0a264f1310b35',
+ '56349e29c2af3ac913b28bde9a2c6154436e615b',
+ '2dda4e345facb0ccff1a191052dd1606dba6781d',
+ '6fff84722075f1607a30f436523403845f84cd9e',
+ '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
+ '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
+ 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
+ 'be90031137367893f1c406e0a8683010fd115b79',
+ 'db8e58be770518cbb2b1cdfa69146e47cd481481',
+ '84478366594b424af694a6c784cb991a16b87c21',
+ '17f8e105dddb9f339600389c6dc7175d395a535c',
+ '20a662e756499bde3095ffc9bc0643d1def2d0eb',
+ '2e319b85e70a707bba0beff866d9f9de032aa4f9',
+ '786facd2c61deb9cf91e9534735124fb8fc11842',
+ '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
+ 'aa6a0de05b7612707db567078e130a6cd114a9a7',
+ 'eada5a770da98ab0dd7325e29d00e0714f228d09'
+ ])
+ self.assertTrue(subset.issubset(set(self.repo.revisions)))
+
+
+ # check if we have the proper order of revisions
+ org = ['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
+ '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
+ '6cba7170863a2411822803fa77a0a264f1310b35',
+ '56349e29c2af3ac913b28bde9a2c6154436e615b',
+ '2dda4e345facb0ccff1a191052dd1606dba6781d',
+ '6fff84722075f1607a30f436523403845f84cd9e',
+ '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
+ '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
+ 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
+ 'be90031137367893f1c406e0a8683010fd115b79',
+ 'db8e58be770518cbb2b1cdfa69146e47cd481481',
+ '84478366594b424af694a6c784cb991a16b87c21',
+ '17f8e105dddb9f339600389c6dc7175d395a535c',
+ '20a662e756499bde3095ffc9bc0643d1def2d0eb',
+ '2e319b85e70a707bba0beff866d9f9de032aa4f9',
+ '786facd2c61deb9cf91e9534735124fb8fc11842',
+ '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
+ 'aa6a0de05b7612707db567078e130a6cd114a9a7',
+ 'eada5a770da98ab0dd7325e29d00e0714f228d09',
+ '2c1885c735575ca478bf9e17b0029dca68824458',
+ 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
+ '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
+ '4fb8326d78e5120da2c7468dcf7098997be385da',
+ '62b4a097164940bd66030c4db51687f3ec035eed',
+ '536c1a19428381cfea92ac44985304f6a8049569',
+ '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
+ '9bb326a04ae5d98d437dece54be04f830cf1edd9',
+ 'f8940bcb890a98c4702319fbe36db75ea309b475',
+ 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
+ '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
+ 'ee87846a61c12153b51543bf860e1026c6d3dcba', ]
+ self.assertEqual(org, self.repo.revisions[:31])
+
+ def test_iter_slice(self):
+ sliced = list(self.repo[:10])
+ itered = list(self.repo)[:10]
+ self.assertEqual(sliced, itered)
+
+ def test_slicing(self):
+ #4 1 5 10 95
+ for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
+ (10, 20, 10), (5, 100, 95)]:
+ revs = list(self.repo[sfrom:sto])
+ self.assertEqual(len(revs), size)
+ self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
+ self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
+
+ def test_branches(self):
+ # TODO: Need more tests here
+
+ #active branches
+ self.assertTrue('default' in self.repo.branches)
+ self.assertTrue('git' in self.repo.branches)
+
+ # closed
+ self.assertTrue('web' in self.repo._get_branches(closed=True))
+
+ for name, id in self.repo.branches.items():
+ self.assertTrue(isinstance(
+ self.repo.get_changeset(id), MercurialChangeset))
+
+ def test_tip_in_tags(self):
+ # tip is always a tag
+ self.assertIn('tip', self.repo.tags)
+
+ def test_tip_changeset_in_tags(self):
+ tip = self.repo.get_changeset()
+ self.assertEqual(self.repo.tags['tip'], tip.raw_id)
+
+ def test_initial_changeset(self):
+
+ init_chset = self.repo.get_changeset(0)
+ self.assertEqual(init_chset.message, 'initial import')
+ self.assertEqual(init_chset.author,
+ 'Marcin Kuzminski <marcin@python-blog.com>')
+ self.assertEqual(sorted(init_chset._file_paths),
+ sorted([
+ 'vcs/__init__.py',
+ 'vcs/backends/BaseRepository.py',
+ 'vcs/backends/__init__.py',
+ ])
+ )
+ self.assertEqual(sorted(init_chset._dir_paths),
+ sorted(['', 'vcs', 'vcs/backends']))
+
+ self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
+
+ node = init_chset.get_node('vcs/')
+ self.assertTrue(hasattr(node, 'kind'))
+ self.assertEqual(node.kind, NodeKind.DIR)
+
+ node = init_chset.get_node('vcs')
+ self.assertTrue(hasattr(node, 'kind'))
+ self.assertEqual(node.kind, NodeKind.DIR)
+
+ node = init_chset.get_node('vcs/__init__.py')
+ self.assertTrue(hasattr(node, 'kind'))
+ self.assertEqual(node.kind, NodeKind.FILE)
+
+ def test_not_existing_changeset(self):
+ #rawid
+ self.assertRaises(RepositoryError, self.repo.get_changeset,
+ 'abcd' * 10)
+ #shortid
+ self.assertRaises(RepositoryError, self.repo.get_changeset,
+ 'erro' * 4)
+ #numeric
+ self.assertRaises(RepositoryError, self.repo.get_changeset,
+ self.repo.count() + 1)
+
+
+ # Small chance we ever get to this one
+ revision = pow(2, 30)
+ self.assertRaises(RepositoryError, self.repo.get_changeset, revision)
+
+ def test_changeset10(self):
+
+ chset10 = self.repo.get_changeset(10)
+ README = """===
+VCS
+===
+
+Various Version Control System management abstraction layer for Python.
+
+Introduction
+------------
+
+TODO: To be written...
+
+"""
+ node = chset10.get_node('README.rst')
+ self.assertEqual(node.kind, NodeKind.FILE)
+ self.assertEqual(node.content, README)
+
+
+class MercurialChangesetTest(unittest.TestCase):
+
+ def setUp(self):
+ self.repo = MercurialRepository(TEST_HG_REPO)
+
+ def _test_equality(self, changeset):
+ revision = changeset.revision
+ self.assertEqual(changeset, self.repo.get_changeset(revision))
+
+ def test_equality(self):
+ self.setUp()
+ revs = [0, 10, 20]
+ changesets = [self.repo.get_changeset(rev) for rev in revs]
+ for changeset in changesets:
+ self._test_equality(changeset)
+
+ def test_default_changeset(self):
+ tip = self.repo.get_changeset('tip')
+ self.assertEqual(tip, self.repo.get_changeset())
+ self.assertEqual(tip, self.repo.get_changeset(revision=None))
+ self.assertEqual(tip, list(self.repo[-1:])[0])
+
+ def test_root_node(self):
+ tip = self.repo.get_changeset('tip')
+ self.assertTrue(tip.root is tip.get_node(''))
+
+ def test_lazy_fetch(self):
+ """
+ Test if changeset's nodes expands and are cached as we walk through
+ the revision. This test is somewhat hard to write as order of tests
+ is a key here. Written by running command after command in a shell.
+ """
+ self.setUp()
+ chset = self.repo.get_changeset(45)
+ self.assertTrue(len(chset.nodes) == 0)
+ root = chset.root
+ self.assertTrue(len(chset.nodes) == 1)
+ self.assertTrue(len(root.nodes) == 8)
+ # accessing root.nodes updates chset.nodes
+ self.assertTrue(len(chset.nodes) == 9)
+
+ docs = root.get_node('docs')
+ # we haven't yet accessed anything new as docs dir was already cached
+ self.assertTrue(len(chset.nodes) == 9)
+ self.assertTrue(len(docs.nodes) == 8)
+ # accessing docs.nodes updates chset.nodes
+ self.assertTrue(len(chset.nodes) == 17)
+
+ self.assertTrue(docs is chset.get_node('docs'))
+ self.assertTrue(docs is root.nodes[0])
+ self.assertTrue(docs is root.dirs[0])
+ self.assertTrue(docs is chset.get_node('docs'))
+
+ def test_nodes_with_changeset(self):
+ self.setUp()
+ chset = self.repo.get_changeset(45)
+ root = chset.root
+ docs = root.get_node('docs')
+ self.assertTrue(docs is chset.get_node('docs'))
+ api = docs.get_node('api')
+ self.assertTrue(api is chset.get_node('docs/api'))
+ index = api.get_node('index.rst')
+ self.assertTrue(index is chset.get_node('docs/api/index.rst'))
+ self.assertTrue(index is chset.get_node('docs')\
+ .get_node('api')\
+ .get_node('index.rst'))
+
+ def test_branch_and_tags(self):
+ chset0 = self.repo.get_changeset(0)
+ self.assertEqual(chset0.branch, 'default')
+ self.assertEqual(chset0.tags, [])
+
+ chset10 = self.repo.get_changeset(10)
+ self.assertEqual(chset10.branch, 'default')
+ self.assertEqual(chset10.tags, [])
+
+ chset44 = self.repo.get_changeset(44)
+ self.assertEqual(chset44.branch, 'web')
+
+ tip = self.repo.get_changeset('tip')
+ self.assertTrue('tip' in tip.tags)
+
+ def _test_file_size(self, revision, path, size):
+ node = self.repo.get_changeset(revision).get_node(path)
+ self.assertTrue(node.is_file())
+ self.assertEqual(node.size, size)
+
+ def test_file_size(self):
+ to_check = (
+ (10, 'setup.py', 1068),
+ (20, 'setup.py', 1106),
+ (60, 'setup.py', 1074),
+
+ (10, 'vcs/backends/base.py', 2921),
+ (20, 'vcs/backends/base.py', 3936),
+ (60, 'vcs/backends/base.py', 6189),
+ )
+ for revision, path, size in to_check:
+ self._test_file_size(revision, path, size)
+
+ def test_file_history(self):
+ # we can only check if those revisions are present in the history
+ # as we cannot update this test every time file is changed
+ files = {
+ 'setup.py': [7, 18, 45, 46, 47, 69, 77],
+ 'vcs/nodes.py': [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60,
+ 61, 73, 76],
+ 'vcs/backends/hg.py': [4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23,
+ 26, 27, 28, 30, 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47,
+ 48, 49, 53, 54, 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79,
+ 82],
+ }
+ for path, revs in files.items():
+ tip = self.repo.get_changeset(revs[-1])
+ node = tip.get_node(path)
+ node_revs = [chset.revision for chset in node.history]
+ self.assertTrue(set(revs).issubset(set(node_revs)),
+ "We assumed that %s is subset of revisions for which file %s "
+ "has been changed, and history of that node returned: %s"
+ % (revs, path, node_revs))
+
+ def test_file_annotate(self):
+ files = {
+ 'vcs/backends/__init__.py':
+ {89: {'lines_no': 31,
+ 'changesets': [32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
+ 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
+ 32, 32, 32, 32, 37, 32, 37, 37, 32,
+ 32, 32]},
+ 20: {'lines_no': 1,
+ 'changesets': [4]},
+ 55: {'lines_no': 31,
+ 'changesets': [32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
+ 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
+ 32, 32, 32, 32, 37, 32, 37, 37, 32,
+ 32, 32]}},
+ 'vcs/exceptions.py':
+ {89: {'lines_no': 18,
+ 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 17, 16, 16, 18, 18, 18]},
+ 20: {'lines_no': 18,
+ 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 17, 16, 16, 18, 18, 18]},
+ 55: {'lines_no': 18, 'changesets': [16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16,
+ 17, 16, 16, 18, 18, 18]}},
+ 'MANIFEST.in': {89: {'lines_no': 5,
+ 'changesets': [7, 7, 7, 71, 71]},
+ 20: {'lines_no': 3,
+ 'changesets': [7, 7, 7]},
+ 55: {'lines_no': 3,
+ 'changesets': [7, 7, 7]}}}
+
+
+ for fname, revision_dict in files.items():
+ for rev, data in revision_dict.items():
+ cs = self.repo.get_changeset(rev)
+ ann = cs.get_file_annotate(fname)
+
+ l1 = [x[1].revision for x in ann]
+ l2 = files[fname][rev]['changesets']
+ self.assertTrue(l1 == l2 , "The lists of revision for %s@rev%s"
+ "from annotation list should match each other,"
+ "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
+
+ def test_changeset_state(self):
+ """
+ Tests which files have been added/changed/removed at particular revision
+ """
+
+ # rev 46ad32a4f974:
+ # hg st --rev 46ad32a4f974
+ # changed: 13
+ # added: 20
+ # removed: 1
+ changed = set(['.hgignore'
+ , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py'
+ , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py'
+ , 'vcs/backends/__init__.py' , 'vcs/backends/base.py'
+ , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py'])
+
+ added = set(['docs/api/backends/hg.rst'
+ , 'docs/api/backends/index.rst' , 'docs/api/index.rst'
+ , 'docs/api/nodes.rst' , 'docs/api/web/index.rst'
+ , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst'
+ , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py'
+ , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py'
+ , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py'
+ , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py'
+ , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py'
+ , 'vcs/web/simplevcs/views.py'])
+
+ removed = set(['docs/api.rst'])
+
+ chset64 = self.repo.get_changeset('46ad32a4f974')
+ self.assertEqual(set((node.path for node in chset64.added)), added)
+ self.assertEqual(set((node.path for node in chset64.changed)), changed)
+ self.assertEqual(set((node.path for node in chset64.removed)), removed)
+
+ # rev b090f22d27d6:
+ # hg st --rev b090f22d27d6
+ # changed: 13
+ # added: 20
+ # removed: 1
+ chset88 = self.repo.get_changeset('b090f22d27d6')
+ self.assertEqual(set((node.path for node in chset88.added)), set())
+ self.assertEqual(set((node.path for node in chset88.changed)),
+ set(['.hgignore']))
+ self.assertEqual(set((node.path for node in chset88.removed)), set())
+#
+ # 85:
+ # added: 2 ['vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
+ # changed: 4 ['vcs/web/simplevcs/models.py', ...]
+ # removed: 1 ['vcs/utils/web.py']
+ chset85 = self.repo.get_changeset(85)
+ self.assertEqual(set((node.path for node in chset85.added)), set([
+ 'vcs/utils/diffs.py',
+ 'vcs/web/simplevcs/views/diffs.py']))
+ self.assertEqual(set((node.path for node in chset85.changed)), set([
+ 'vcs/web/simplevcs/models.py',
+ 'vcs/web/simplevcs/utils.py',
+ 'vcs/web/simplevcs/views/__init__.py',
+ 'vcs/web/simplevcs/views/repository.py',
+ ]))
+ self.assertEqual(set((node.path for node in chset85.removed)),
+ set(['vcs/utils/web.py']))
+
+
+ def test_files_state(self):
+ """
+ Tests state of FileNodes.
+ """
+ chset = self.repo.get_changeset(85)
+ node = chset.get_node('vcs/utils/diffs.py')
+ self.assertTrue(node.state, NodeState.ADDED)
+ self.assertTrue(node.added)
+ self.assertFalse(node.changed)
+ self.assertFalse(node.not_changed)
+ self.assertFalse(node.removed)
+
+ chset = self.repo.get_changeset(88)
+ node = chset.get_node('.hgignore')
+ self.assertTrue(node.state, NodeState.CHANGED)
+ self.assertFalse(node.added)
+ self.assertTrue(node.changed)
+ self.assertFalse(node.not_changed)
+ self.assertFalse(node.removed)
+
+ chset = self.repo.get_changeset(85)
+ node = chset.get_node('setup.py')
+ self.assertTrue(node.state, NodeState.NOT_CHANGED)
+ self.assertFalse(node.added)
+ self.assertFalse(node.changed)
+ self.assertTrue(node.not_changed)
+ self.assertFalse(node.removed)
+
+ # If node has REMOVED state then trying to fetch it would raise
+ # ChangesetError exception
+ chset = self.repo.get_changeset(2)
+ path = 'vcs/backends/BaseRepository.py'
+ self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
+ # but it would be one of ``removed`` (changeset's attribute)
+ self.assertTrue(path in [rf.path for rf in chset.removed])
+
+ def test_commit_message_is_unicode(self):
+ for cm in self.repo:
+ self.assertEqual(type(cm.message), unicode)
+
+ def test_changeset_author_is_unicode(self):
+ for cm in self.repo:
+ self.assertEqual(type(cm.author), unicode)
+
+ def test_repo_files_content_is_unicode(self):
+ test_changeset = self.repo.get_changeset(100)
+ for node in test_changeset.get_node('/'):
+ if node.is_file():
+ self.assertEqual(type(node.content), unicode)
+
+ def test_wrong_path(self):
+ # There is 'setup.py' in the root dir but not there:
+ path = 'foo/bar/setup.py'
+ self.assertRaises(VCSError, self.repo.get_changeset().get_node, path)
+
+
+ def test_archival_file(self):
+ #TODO:
+ pass
+
+ def test_archival_as_generator(self):
+ #TODO:
+ pass
+
+ def test_archival_wrong_kind(self):
+ tip = self.repo.get_changeset()
+ self.assertRaises(VCSError, tip.fill_archive, kind='error')
+
+ def test_archival_empty_prefix(self):
+ #TODO:
+ pass
+
+
+ def test_author_email(self):
+ self.assertEqual('marcin@python-blog.com',
+ self.repo.get_changeset('b986218ba1c9').author_email)
+ self.assertEqual('lukasz.balcerzak@python-center.pl',
+ self.repo.get_changeset('3803844fdbd3').author_email)
+ self.assertEqual('',
+ self.repo.get_changeset('84478366594b').author_email)
+
+ def test_author_username(self):
+ self.assertEqual('Marcin Kuzminski',
+ self.repo.get_changeset('b986218ba1c9').author_name)
+ self.assertEqual('Lukasz Balcerzak',
+ self.repo.get_changeset('3803844fdbd3').author_name)
+ self.assertEqual('marcink',
+ self.repo.get_changeset('84478366594b').author_name)
diff --git a/rhodecode/tests/vcs/test_inmemchangesets.py b/rhodecode/tests/vcs/test_inmemchangesets.py
new file mode 100644
index 00000000..b884fb9a
--- /dev/null
+++ b/rhodecode/tests/vcs/test_inmemchangesets.py
@@ -0,0 +1,340 @@
+"""
+Tests so called "in memory changesets" commit API of vcs.
+"""
+from __future__ import with_statement
+
+from rhodecode.lib import vcs
+import time
+import datetime
+from conf import SCM_TESTS, get_new_dir
+from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
+from rhodecode.lib.vcs.exceptions import NodeAlreadyAddedError
+from rhodecode.lib.vcs.exceptions import NodeAlreadyExistsError
+from rhodecode.lib.vcs.exceptions import NodeAlreadyRemovedError
+from rhodecode.lib.vcs.exceptions import NodeAlreadyChangedError
+from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
+from rhodecode.lib.vcs.exceptions import NodeNotChangedError
+from rhodecode.lib.vcs.nodes import DirNode
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class InMemoryChangesetTestMixin(object):
+ """
+ This is a backend independent test case class which should be created
+ with ``type`` method.
+
+ It is required to set following attributes at subclass:
+
+ - ``backend_alias``: alias of used backend (see ``vcs.BACKENDS``)
+ - ``repo_path``: path to the repository which would be created for set of
+ tests
+ """
+
+ def get_backend(self):
+ return vcs.get_backend(self.backend_alias)
+
+ def setUp(self):
+ Backend = self.get_backend()
+ self.repo_path = get_new_dir(str(time.time()))
+ self.repo = Backend(self.repo_path, create=True)
+ self.imc = self.repo.in_memory_changeset
+ self.nodes = [
+ FileNode('foobar', content='Foo & bar'),
+ FileNode('foobar2', content='Foo & bar, doubled!'),
+ FileNode('foo bar with spaces', content=''),
+ FileNode('foo/bar/baz', content='Inside'),
+ ]
+
+ def test_add(self):
+ rev_count = len(self.repo.revisions)
+ to_add = [FileNode(node.path, content=node.content)
+ for node in self.nodes]
+ for node in to_add:
+ self.imc.add(node)
+ message = u'Added: %s' % ', '.join((node.path for node in self.nodes))
+ author = unicode(self.__class__)
+ changeset = self.imc.commit(message=message, author=author)
+
+ newtip = self.repo.get_changeset()
+ self.assertEqual(changeset, newtip)
+ self.assertEqual(rev_count + 1, len(self.repo.revisions))
+ self.assertEqual(newtip.message, message)
+ self.assertEqual(newtip.author, author)
+ self.assertTrue(not any((self.imc.added, self.imc.changed,
+ self.imc.removed)))
+ for node in to_add:
+ self.assertEqual(newtip.get_node(node.path).content, node.content)
+
+ def test_add_in_bulk(self):
+ rev_count = len(self.repo.revisions)
+ to_add = [FileNode(node.path, content=node.content)
+ for node in self.nodes]
+ self.imc.add(*to_add)
+ message = u'Added: %s' % ', '.join((node.path for node in self.nodes))
+ author = unicode(self.__class__)
+ changeset = self.imc.commit(message=message, author=author)
+
+ newtip = self.repo.get_changeset()
+ self.assertEqual(changeset, newtip)
+ self.assertEqual(rev_count + 1, len(self.repo.revisions))
+ self.assertEqual(newtip.message, message)
+ self.assertEqual(newtip.author, author)
+ self.assertTrue(not any((self.imc.added, self.imc.changed,
+ self.imc.removed)))
+ for node in to_add:
+ self.assertEqual(newtip.get_node(node.path).content, node.content)
+
+ def test_add_actually_adds_all_nodes_at_second_commit_too(self):
+ self.imc.add(FileNode('foo/bar/image.png', content='\0'))
+ self.imc.add(FileNode('foo/README.txt', content='readme!'))
+ changeset = self.imc.commit(u'Initial', u'joe.doe@example.com')
+ self.assertTrue(isinstance(changeset.get_node('foo'), DirNode))
+ self.assertTrue(isinstance(changeset.get_node('foo/bar'), DirNode))
+ self.assertEqual(changeset.get_node('foo/bar/image.png').content, '\0')
+ self.assertEqual(changeset.get_node('foo/README.txt').content, 'readme!')
+
+ # commit some more files again
+ to_add = [
+ FileNode('foo/bar/foobaz/bar', content='foo'),
+ FileNode('foo/bar/another/bar', content='foo'),
+ FileNode('foo/baz.txt', content='foo'),
+ FileNode('foobar/foobaz/file', content='foo'),
+ FileNode('foobar/barbaz', content='foo'),
+ ]
+ self.imc.add(*to_add)
+ changeset = self.imc.commit(u'Another', u'joe.doe@example.com')
+ self.assertEqual(changeset.get_node('foo/bar/foobaz/bar').content, 'foo')
+ self.assertEqual(changeset.get_node('foo/bar/another/bar').content, 'foo')
+ self.assertEqual(changeset.get_node('foo/baz.txt').content, 'foo')
+ self.assertEqual(changeset.get_node('foobar/foobaz/file').content, 'foo')
+ self.assertEqual(changeset.get_node('foobar/barbaz').content, 'foo')
+
+ def test_add_raise_already_added(self):
+ node = FileNode('foobar', content='baz')
+ self.imc.add(node)
+ self.assertRaises(NodeAlreadyAddedError, self.imc.add, node)
+
+ def test_check_integrity_raise_already_exist(self):
+ node = FileNode('foobar', content='baz')
+ self.imc.add(node)
+ self.imc.commit(message=u'Added foobar', author=unicode(self))
+ self.imc.add(node)
+ self.assertRaises(NodeAlreadyExistsError, self.imc.commit,
+ message='new message',
+ author=str(self))
+
+ def test_change(self):
+ self.imc.add(FileNode('foo/bar/baz', content='foo'))
+ self.imc.add(FileNode('foo/fbar', content='foobar'))
+ tip = self.imc.commit(u'Initial', u'joe.doe@example.com')
+
+ # Change node's content
+ node = FileNode('foo/bar/baz', content='My **changed** content')
+ self.imc.change(node)
+ self.imc.commit(u'Changed %s' % node.path, u'joe.doe@example.com')
+
+ newtip = self.repo.get_changeset()
+ self.assertNotEqual(tip, newtip)
+ self.assertNotEqual(tip.id, newtip.id)
+ self.assertEqual(newtip.get_node('foo/bar/baz').content,
+ 'My **changed** content')
+
+ def test_change_raise_empty_repository(self):
+ node = FileNode('foobar')
+ self.assertRaises(EmptyRepositoryError, self.imc.change, node)
+
+ def test_check_integrity_change_raise_node_does_not_exist(self):
+ node = FileNode('foobar', content='baz')
+ self.imc.add(node)
+ self.imc.commit(message=u'Added foobar', author=unicode(self))
+ node = FileNode('not-foobar', content='')
+ self.imc.change(node)
+ self.assertRaises(NodeDoesNotExistError, self.imc.commit,
+ message='Changed not existing node',
+ author=str(self))
+
+ def test_change_raise_node_already_changed(self):
+ node = FileNode('foobar', content='baz')
+ self.imc.add(node)
+ self.imc.commit(message=u'Added foobar', author=unicode(self))
+ node = FileNode('foobar', content='more baz')
+ self.imc.change(node)
+ self.assertRaises(NodeAlreadyChangedError, self.imc.change, node)
+
+ def test_check_integrity_change_raise_node_not_changed(self):
+ self.test_add() # Performs first commit
+
+ node = FileNode(self.nodes[0].path, content=self.nodes[0].content)
+ self.imc.change(node)
+ self.assertRaises(NodeNotChangedError, self.imc.commit,
+ message=u'Trying to mark node as changed without touching it',
+ author=unicode(self))
+
+ def test_change_raise_node_already_removed(self):
+ node = FileNode('foobar', content='baz')
+ self.imc.add(node)
+ self.imc.commit(message=u'Added foobar', author=unicode(self))
+ self.imc.remove(FileNode('foobar'))
+ self.assertRaises(NodeAlreadyRemovedError, self.imc.change, node)
+
+ def test_remove(self):
+ self.test_add() # Performs first commit
+
+ tip = self.repo.get_changeset()
+ node = self.nodes[0]
+ self.assertEqual(node.content, tip.get_node(node.path).content)
+ self.imc.remove(node)
+ self.imc.commit(message=u'Removed %s' % node.path, author=unicode(self))
+
+ newtip = self.repo.get_changeset()
+ self.assertNotEqual(tip, newtip)
+ self.assertNotEqual(tip.id, newtip.id)
+ self.assertRaises(NodeDoesNotExistError, newtip.get_node, node.path)
+
+ def test_remove_last_file_from_directory(self):
+ node = FileNode('omg/qwe/foo/bar', content='foobar')
+ self.imc.add(node)
+ self.imc.commit(u'added', u'joe doe')
+
+ self.imc.remove(node)
+ tip = self.imc.commit(u'removed', u'joe doe')
+ self.assertRaises(NodeDoesNotExistError, tip.get_node, 'omg/qwe/foo/bar')
+
+ def test_remove_raise_node_does_not_exist(self):
+ self.imc.remove(self.nodes[0])
+ self.assertRaises(NodeDoesNotExistError, self.imc.commit,
+ message='Trying to remove node at empty repository',
+ author=str(self))
+
+ def test_check_integrity_remove_raise_node_does_not_exist(self):
+ self.test_add() # Performs first commit
+
+ node = FileNode('no-such-file')
+ self.imc.remove(node)
+ self.assertRaises(NodeDoesNotExistError, self.imc.commit,
+ message=u'Trying to remove not existing node',
+ author=unicode(self))
+
+ def test_remove_raise_node_already_removed(self):
+ self.test_add() # Performs first commit
+
+ node = FileNode(self.nodes[0].path)
+ self.imc.remove(node)
+ self.assertRaises(NodeAlreadyRemovedError, self.imc.remove, node)
+
+ def test_remove_raise_node_already_changed(self):
+ self.test_add() # Performs first commit
+
+ node = FileNode(self.nodes[0].path, content='Bending time')
+ self.imc.change(node)
+ self.assertRaises(NodeAlreadyChangedError, self.imc.remove, node)
+
+ def test_reset(self):
+ self.imc.add(FileNode('foo', content='bar'))
+ #self.imc.change(FileNode('baz', content='new'))
+ #self.imc.remove(FileNode('qwe'))
+ self.imc.reset()
+ self.assertTrue(not any((self.imc.added, self.imc.changed,
+ self.imc.removed)))
+
+ def test_multiple_commits(self):
+ N = 3 # number of commits to perform
+ last = None
+ for x in xrange(N):
+ fname = 'file%s' % str(x).rjust(5, '0')
+ content = 'foobar\n' * x
+ node = FileNode(fname, content=content)
+ self.imc.add(node)
+ commit = self.imc.commit(u"Commit no. %s" % (x + 1), author=u'vcs')
+ self.assertTrue(last != commit)
+ last = commit
+
+ # Check commit number for same repo
+ self.assertEqual(len(self.repo.revisions), N)
+
+ # Check commit number for recreated repo
+ backend = self.get_backend()
+ repo = backend(self.repo_path)
+ self.assertEqual(len(repo.revisions), N)
+
+ def test_date_attr(self):
+ node = FileNode('foobar.txt', content='Foobared!')
+ self.imc.add(node)
+ date = datetime.datetime(1985, 1, 30, 1, 45)
+ commit = self.imc.commit(u"Committed at time when I was born ;-)",
+ author=u'lb', date=date)
+
+ self.assertEqual(commit.date, date)
+
+
+class BackendBaseTestCase(unittest.TestCase):
+ """
+ Base test class for tests which requires repository.
+ """
+ backend_alias = 'hg'
+ commits = [
+ {
+ 'message': 'Initial commit',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foobar', content='Foobar'),
+ FileNode('foobar2', content='Foobar II'),
+ FileNode('foo/bar/baz', content='baz here!'),
+ ],
+ },
+ ]
+
+ def get_backend(self):
+ return vcs.get_backend(self.backend_alias)
+
+ def get_commits(self):
+ """
+ Returns list of commits which builds repository for each tests.
+ """
+ if hasattr(self, 'commits'):
+ return self.commits
+
+ def get_new_repo_path(self):
+ """
+ Returns newly created repository's directory.
+ """
+ backend = self.get_backend()
+ key = '%s-%s' % (backend.alias, str(time.time()))
+ repo_path = get_new_dir(key)
+ return repo_path
+
+ def setUp(self):
+ Backend = self.get_backend()
+ self.backend_class = Backend
+ self.repo_path = self.get_new_repo_path()
+ self.repo = Backend(self.repo_path, create=True)
+ self.imc = self.repo.in_memory_changeset
+
+ for commit in self.get_commits():
+ for node in commit.get('added', []):
+ self.imc.add(FileNode(node.path, content=node.content))
+ for node in commit.get('changed', []):
+ self.imc.change(FileNode(node.path, content=node.content))
+ for node in commit.get('removed', []):
+ self.imc.remove(FileNode(node.path))
+ self.imc.commit(message=unicode(commit['message']),
+ author=unicode(commit['author']),
+ date=commit['date'])
+
+ self.tip = self.repo.get_changeset()
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s in memory changeset test' % alias).title().split())
+ bases = (InMemoryChangesetTestMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_nodes.py b/rhodecode/tests/vcs/test_nodes.py
new file mode 100644
index 00000000..59922129
--- /dev/null
+++ b/rhodecode/tests/vcs/test_nodes.py
@@ -0,0 +1,183 @@
+from __future__ import with_statement
+
+import stat
+from rhodecode.lib.vcs.nodes import DirNode
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.nodes import Node
+from rhodecode.lib.vcs.nodes import NodeError
+from rhodecode.lib.vcs.nodes import NodeKind
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class NodeBasicTest(unittest.TestCase):
+
+ def test_init(self):
+ """
+ Cannot innitialize Node objects with path with slash at the beginning.
+ """
+ wrong_paths = (
+ '/foo',
+ '/foo/bar'
+ )
+ for path in wrong_paths:
+ self.assertRaises(NodeError, Node, path, NodeKind.FILE)
+
+ wrong_paths = (
+ '/foo/',
+ '/foo/bar/'
+ )
+ for path in wrong_paths:
+ self.assertRaises(NodeError, Node, path, NodeKind.DIR)
+
+ def test_name(self):
+ node = Node('', NodeKind.DIR)
+ self.assertEqual(node.name, '')
+
+ node = Node('path', NodeKind.FILE)
+ self.assertEqual(node.name, 'path')
+
+ node = Node('path/', NodeKind.DIR)
+ self.assertEqual(node.name, 'path')
+
+ node = Node('some/path', NodeKind.FILE)
+ self.assertEqual(node.name, 'path')
+
+ node = Node('some/path/', NodeKind.DIR)
+ self.assertEqual(node.name, 'path')
+
+ def test_root_node(self):
+ self.assertRaises(NodeError, Node, '', NodeKind.FILE)
+
+ def test_kind_setter(self):
+ node = Node('', NodeKind.DIR)
+ self.assertRaises(NodeError, setattr, node, 'kind', NodeKind.FILE)
+
+ def _test_parent_path(self, node_path, expected_parent_path):
+ """
+ Tests if node's parent path are properly computed.
+ """
+ node = Node(node_path, NodeKind.DIR)
+ parent_path = node.get_parent_path()
+ self.assertTrue(parent_path.endswith('/') or \
+ node.is_root() and parent_path == '')
+ self.assertEqual(parent_path, expected_parent_path,
+ "Node's path is %r and parent path is %r but should be %r"
+ % (node.path, parent_path, expected_parent_path))
+
+ def test_parent_path(self):
+ test_paths = (
+ # (node_path, expected_parent_path)
+ ('', ''),
+ ('some/path/', 'some/'),
+ ('some/longer/path/', 'some/longer/'),
+ )
+ for node_path, expected_parent_path in test_paths:
+ self._test_parent_path(node_path, expected_parent_path)
+
+ '''
+ def _test_trailing_slash(self, path):
+ if not path.endswith('/'):
+ self.fail("Trailing slash tests needs paths to end with slash")
+ for kind in NodeKind.FILE, NodeKind.DIR:
+ self.assertRaises(NodeError, Node, path=path, kind=kind)
+
+ def test_trailing_slash(self):
+ for path in ('/', 'foo/', 'foo/bar/', 'foo/bar/biz/'):
+ self._test_trailing_slash(path)
+ '''
+
+ def test_is_file(self):
+ node = Node('any', NodeKind.FILE)
+ self.assertTrue(node.is_file())
+
+ node = FileNode('any')
+ self.assertTrue(node.is_file())
+ self.assertRaises(AttributeError, getattr, node, 'nodes')
+
+ def test_is_dir(self):
+ node = Node('any_dir', NodeKind.DIR)
+ self.assertTrue(node.is_dir())
+
+ node = DirNode('any_dir')
+
+ self.assertTrue(node.is_dir())
+ self.assertRaises(NodeError, getattr, node, 'content')
+
+ def test_dir_node_iter(self):
+ nodes = [
+ DirNode('docs'),
+ DirNode('tests'),
+ FileNode('bar'),
+ FileNode('foo'),
+ FileNode('readme.txt'),
+ FileNode('setup.py'),
+ ]
+ dirnode = DirNode('', nodes=nodes)
+ for node in dirnode:
+ node == dirnode.get_node(node.path)
+
+ def test_node_state(self):
+ """
+ Without link to changeset nodes should raise NodeError.
+ """
+ node = FileNode('anything')
+ self.assertRaises(NodeError, getattr, node, 'state')
+ node = DirNode('anything')
+ self.assertRaises(NodeError, getattr, node, 'state')
+
+ def test_file_node_stat(self):
+ node = FileNode('foobar', 'empty... almost')
+ mode = node.mode # default should be 0100644
+ self.assertTrue(mode & stat.S_IRUSR)
+ self.assertTrue(mode & stat.S_IWUSR)
+ self.assertTrue(mode & stat.S_IRGRP)
+ self.assertTrue(mode & stat.S_IROTH)
+ self.assertFalse(mode & stat.S_IWGRP)
+ self.assertFalse(mode & stat.S_IWOTH)
+ self.assertFalse(mode & stat.S_IXUSR)
+ self.assertFalse(mode & stat.S_IXGRP)
+ self.assertFalse(mode & stat.S_IXOTH)
+
+ def test_file_node_is_executable(self):
+ node = FileNode('foobar', 'empty... almost', mode=0100755)
+ self.assertTrue(node.is_executable())
+
+ node = FileNode('foobar', 'empty... almost', mode=0100500)
+ self.assertTrue(node.is_executable())
+
+ node = FileNode('foobar', 'empty... almost', mode=0100644)
+ self.assertFalse(node.is_executable())
+
+ def test_mimetype(self):
+ py_node = FileNode('test.py')
+ tar_node = FileNode('test.tar.gz')
+
+ ext = 'CustomExtension'
+
+ my_node2 = FileNode('myfile2')
+ my_node2._mimetype = [ext]
+
+ my_node3 = FileNode('myfile3')
+ my_node3._mimetype = [ext,ext]
+
+ self.assertEqual(py_node.mimetype,'text/x-python')
+ self.assertEqual(py_node.get_mimetype(),('text/x-python',None))
+
+ self.assertEqual(tar_node.mimetype,'application/x-tar')
+ self.assertEqual(tar_node.get_mimetype(),('application/x-tar','gzip'))
+
+ self.assertRaises(NodeError,my_node2.get_mimetype)
+
+ self.assertEqual(my_node3.mimetype,ext)
+ self.assertEqual(my_node3.get_mimetype(),[ext,ext])
+
+class NodeContentTest(unittest.TestCase):
+
+ def test_if_binary(self):
+ data = """\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f??a\x00\x00\x00\x04gAMA\x00\x00\xaf?7\x05\x8a?\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq?e<\x00\x00\x025IDAT8?\xa5\x93?K\x94Q\x14\x87\x9f\xf7?Q\x1bs4?\x03\x9a\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?"\x14j?\xa2M\x7fB\x14F\x9aQ?&\x842?\x0b\x89"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?=\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04\'`EpNp\xa2X\'U?pVq"Sw.\x1e?\x08\x01D?jw????\xbc??7{|\x9b?\x89$\x01??W@\x15\x9c\x05q`Lt/\x97?\x94\xa1d?\x18~?\x18?\x18W[%\xb0?\x83??\x14\x88\x8dB?\xa6H\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$"q[\x19?d\x00(o\n\xa0??\x7f\xb9\xa4?\x1bF\x1f\x8e\xac\xa8?j??eUU}?.?\x9f\x8cE??x\x94??\r\xbdtoJU5"0N\x10U?\x00??V\t\x02\x9f\x81?U?\x00\x9eM\xae2?r\x9b7\x83\x82\x8aP3????.?&"?\xb7ZP \x0c<?O\xa5\t}\xb8?\x99\xa6?\x87?\x1di|/\xa0??0\xbe\x1fp?d&\x1a\xad\x95\x8a\x07?\t*\x10??b:?d?.\x13C\x8a?\x12\xbe\xbf\x8e?{???\x08?\x80\xa7\x13+d\x13>J?\x80\x15T\x95\x9a\x00??S\x8c\r?\xa1\x03\x07?\x96\x9b\xa7\xab=E??\xa4\xb3?\x19q??B\x91=\x8d??k?J\x0bV"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X\xb7=\xb9\x10?Z\x1by???cI??\x1ag?\x92\xbc?T?t[\x92\x81?<_\x17~\x92\x88?H%?\x10Q\x02\x9f\n\x81qQ\x0bm?\x1bX?\xb1AK\xa6\x9e\xb9?u\xb2?1\xbe|/\x92M@\xa2!F?\xa9>"\r<DT?>\x92\x8e?>\x9a9Qv\x127?a\xac?Y?8?:??]X???9\x80\xb7?u?\x0b#BZ\x8d=\x1d?p\x00\x00\x00\x00IEND\xaeB`\x82"""
+ filenode = FileNode('calendar.png', content=data)
+ self.assertTrue(filenode.is_binary)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_repository.py b/rhodecode/tests/vcs/test_repository.py
new file mode 100644
index 00000000..997f5cde
--- /dev/null
+++ b/rhodecode/tests/vcs/test_repository.py
@@ -0,0 +1,215 @@
+from __future__ import with_statement
+import datetime
+from base import BackendTestMixin
+from conf import SCM_TESTS
+from conf import TEST_USER_CONFIG_FILE
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
+
+
+class RepositoryBaseTest(BackendTestMixin):
+ recreate_repo_per_test = False
+
+ @classmethod
+ def _get_commits(cls):
+ return super(RepositoryBaseTest, cls)._get_commits()[:1]
+
+ def test_get_config_value(self):
+ self.assertEqual(self.repo.get_config_value('universal', 'foo',
+ TEST_USER_CONFIG_FILE), 'bar')
+
+ def test_get_config_value_defaults_to_None(self):
+ self.assertEqual(self.repo.get_config_value('universal', 'nonexist',
+ TEST_USER_CONFIG_FILE), None)
+
+ def test_get_user_name(self):
+ self.assertEqual(self.repo.get_user_name(TEST_USER_CONFIG_FILE),
+ 'Foo Bar')
+
+ def test_get_user_email(self):
+ self.assertEqual(self.repo.get_user_email(TEST_USER_CONFIG_FILE),
+ 'foo.bar@example.com')
+
+
+
+class RepositoryGetDiffTest(BackendTestMixin):
+
+ @classmethod
+ def _get_commits(cls):
+ commits = [
+ {
+ 'message': 'Initial commit',
+ 'author': 'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foobar', content='foobar'),
+ FileNode('foobar2', content='foobar2'),
+ ],
+ },
+ {
+ 'message': 'Changed foobar, added foobar3',
+ 'author': 'Jane Doe <jane.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 21),
+ 'added': [
+ FileNode('foobar3', content='foobar3'),
+ ],
+ 'changed': [
+ FileNode('foobar', 'FOOBAR'),
+ ],
+ },
+ {
+ 'message': 'Removed foobar, changed foobar3',
+ 'author': 'Jane Doe <jane.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 22),
+ 'changed': [
+ FileNode('foobar3', content='FOOBAR\nFOOBAR\nFOOBAR\n'),
+ ],
+ 'removed': [FileNode('foobar')],
+ },
+ ]
+ return commits
+
+ def test_raise_for_wrong(self):
+ with self.assertRaises(ChangesetDoesNotExistError):
+ self.repo.get_diff('a' * 40, 'b' * 40)
+
+class GitRepositoryGetDiffTest(RepositoryGetDiffTest, unittest.TestCase):
+ backend_alias = 'git'
+
+ def test_initial_commit_diff(self):
+ initial_rev = self.repo.revisions[0]
+ self.assertEqual(self.repo.get_diff(self.repo.EMPTY_CHANGESET, initial_rev), '''diff --git a/foobar b/foobar
+new file mode 100644
+index 0000000..f6ea049
+--- /dev/null
++++ b/foobar
+@@ -0,0 +1 @@
++foobar
+\ No newline at end of file
+diff --git a/foobar2 b/foobar2
+new file mode 100644
+index 0000000..e8c9d6b
+--- /dev/null
++++ b/foobar2
+@@ -0,0 +1 @@
++foobar2
+\ No newline at end of file
+''')
+
+ def test_second_changeset_diff(self):
+ revs = self.repo.revisions
+ self.assertEqual(self.repo.get_diff(revs[0], revs[1]), '''diff --git a/foobar b/foobar
+index f6ea049..389865b 100644
+--- a/foobar
++++ b/foobar
+@@ -1 +1 @@
+-foobar
+\ No newline at end of file
++FOOBAR
+\ No newline at end of file
+diff --git a/foobar3 b/foobar3
+new file mode 100644
+index 0000000..c11c37d
+--- /dev/null
++++ b/foobar3
+@@ -0,0 +1 @@
++foobar3
+\ No newline at end of file
+''')
+
+ def test_third_changeset_diff(self):
+ revs = self.repo.revisions
+ self.assertEqual(self.repo.get_diff(revs[1], revs[2]), '''diff --git a/foobar b/foobar
+deleted file mode 100644
+index 389865b..0000000
+--- a/foobar
++++ /dev/null
+@@ -1 +0,0 @@
+-FOOBAR
+\ No newline at end of file
+diff --git a/foobar3 b/foobar3
+index c11c37d..f932447 100644
+--- a/foobar3
++++ b/foobar3
+@@ -1 +1,3 @@
+-foobar3
+\ No newline at end of file
++FOOBAR
++FOOBAR
++FOOBAR
+''')
+
+
+class HgRepositoryGetDiffTest(RepositoryGetDiffTest, unittest.TestCase):
+ backend_alias = 'hg'
+
+ def test_initial_commit_diff(self):
+ initial_rev = self.repo.revisions[0]
+ self.assertEqual(self.repo.get_diff(self.repo.EMPTY_CHANGESET, initial_rev), '''diff --git a/foobar b/foobar
+new file mode 100755
+--- /dev/null
++++ b/foobar
+@@ -0,0 +1,1 @@
++foobar
+\ No newline at end of file
+diff --git a/foobar2 b/foobar2
+new file mode 100755
+--- /dev/null
++++ b/foobar2
+@@ -0,0 +1,1 @@
++foobar2
+\ No newline at end of file
+''')
+
+ def test_second_changeset_diff(self):
+ revs = self.repo.revisions
+ self.assertEqual(self.repo.get_diff(revs[0], revs[1]), '''diff --git a/foobar b/foobar
+--- a/foobar
++++ b/foobar
+@@ -1,1 +1,1 @@
+-foobar
+\ No newline at end of file
++FOOBAR
+\ No newline at end of file
+diff --git a/foobar3 b/foobar3
+new file mode 100755
+--- /dev/null
++++ b/foobar3
+@@ -0,0 +1,1 @@
++foobar3
+\ No newline at end of file
+''')
+
+ def test_third_changeset_diff(self):
+ revs = self.repo.revisions
+ self.assertEqual(self.repo.get_diff(revs[1], revs[2]), '''diff --git a/foobar b/foobar
+deleted file mode 100755
+--- a/foobar
++++ /dev/null
+@@ -1,1 +0,0 @@
+-FOOBAR
+\ No newline at end of file
+diff --git a/foobar3 b/foobar3
+--- a/foobar3
++++ b/foobar3
+@@ -1,1 +1,3 @@
+-foobar3
+\ No newline at end of file
++FOOBAR
++FOOBAR
++FOOBAR
+''')
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = alias.capitalize() + RepositoryBaseTest.__name__
+ bases = (RepositoryBaseTest, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_tags.py b/rhodecode/tests/vcs/test_tags.py
new file mode 100644
index 00000000..83ace2ca
--- /dev/null
+++ b/rhodecode/tests/vcs/test_tags.py
@@ -0,0 +1,61 @@
+from __future__ import with_statement
+
+from base import BackendTestMixin
+from conf import SCM_TESTS
+from rhodecode.lib.vcs.exceptions import TagAlreadyExistError
+from rhodecode.lib.vcs.exceptions import TagDoesNotExistError
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class TagsTestCaseMixin(BackendTestMixin):
+
+ def test_new_tag(self):
+ tip = self.repo.get_changeset()
+ tagsize = len(self.repo.tags)
+ tag = self.repo.tag('last-commit', 'joe', tip.raw_id)
+
+ self.assertEqual(len(self.repo.tags), tagsize + 1)
+ for top, dirs, files in tip.walk():
+ self.assertEqual(top, tag.get_node(top.path))
+
+ def test_tag_already_exist(self):
+ tip = self.repo.get_changeset()
+ self.repo.tag('last-commit', 'joe', tip.raw_id)
+
+ self.assertRaises(TagAlreadyExistError,
+ self.repo.tag, 'last-commit', 'joe', tip.raw_id)
+
+ chset = self.repo.get_changeset(0)
+ self.assertRaises(TagAlreadyExistError,
+ self.repo.tag, 'last-commit', 'jane', chset.raw_id)
+
+ def test_remove_tag(self):
+ tip = self.repo.get_changeset()
+ self.repo.tag('last-commit', 'joe', tip.raw_id)
+ tagsize = len(self.repo.tags)
+
+ self.repo.remove_tag('last-commit', user='evil joe')
+ self.assertEqual(len(self.repo.tags), tagsize - 1)
+
+ def test_remove_tag_which_does_not_exist(self):
+ self.assertRaises(TagDoesNotExistError,
+ self.repo.remove_tag, 'last-commit', user='evil joe')
+
+ def test_name_with_slash(self):
+ self.repo.tag('19/10/11', 'joe')
+ self.assertTrue('19/10/11' in self.repo.tags)
+ self.repo.tag('11', 'joe')
+ self.assertTrue('11' in self.repo.tags)
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s tags test' % alias).title().split())
+ bases = (TagsTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_utils.py b/rhodecode/tests/vcs/test_utils.py
new file mode 100644
index 00000000..81be0ade
--- /dev/null
+++ b/rhodecode/tests/vcs/test_utils.py
@@ -0,0 +1,279 @@
+from __future__ import with_statement
+
+import os
+import mock
+import time
+import shutil
+import tempfile
+import datetime
+from rhodecode.lib.vcs.utils.compat import unittest
+from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
+from rhodecode.lib.vcs.utils.helpers import get_dict_for_attrs
+from rhodecode.lib.vcs.utils.helpers import get_scm
+from rhodecode.lib.vcs.utils.helpers import get_scms_for_path
+from rhodecode.lib.vcs.utils.helpers import get_total_seconds
+from rhodecode.lib.vcs.utils.helpers import parse_changesets
+from rhodecode.lib.vcs.utils.helpers import parse_datetime
+from rhodecode.lib.vcs.utils import author_email, author_name
+from rhodecode.lib.vcs.utils.paths import get_user_home
+from rhodecode.lib.vcs.exceptions import VCSError
+
+from conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH
+
+
+class PathsTest(unittest.TestCase):
+
+ def _test_get_dirs_for_path(self, path, expected):
+ """
+ Tests if get_dirs_for_path returns same as expected.
+ """
+ expected = sorted(expected)
+ result = sorted(get_dirs_for_path(path))
+ self.assertEqual(result, expected,
+ msg="%s != %s which was expected result for path %s"
+ % (result, expected, path))
+
+ def test_get_dirs_for_path(self):
+ path = 'foo/bar/baz/file'
+ paths_and_results = (
+ ('foo/bar/baz/file', ['foo', 'foo/bar', 'foo/bar/baz']),
+ ('foo/bar/', ['foo', 'foo/bar']),
+ ('foo/bar', ['foo']),
+ )
+ for path, expected in paths_and_results:
+ self._test_get_dirs_for_path(path, expected)
+
+
+ def test_get_scm(self):
+ self.assertEqual(('hg', TEST_HG_REPO), get_scm(TEST_HG_REPO))
+ self.assertEqual(('git', TEST_GIT_REPO), get_scm(TEST_GIT_REPO))
+
+ def test_get_two_scms_for_path(self):
+ multialias_repo_path = os.path.join(TEST_TMP_PATH, 'hg-git-repo-2')
+ if os.path.isdir(multialias_repo_path):
+ shutil.rmtree(multialias_repo_path)
+
+ os.mkdir(multialias_repo_path)
+
+ self.assertRaises(VCSError, get_scm, multialias_repo_path)
+
+ def test_get_scm_error_path(self):
+ self.assertRaises(VCSError, get_scm, 'err')
+
+ def test_get_scms_for_path(self):
+ dirpath = tempfile.gettempdir()
+ new = os.path.join(dirpath, 'vcs-scms-for-path-%s' % time.time())
+ os.mkdir(new)
+ self.assertEqual(get_scms_for_path(new), [])
+
+ os.mkdir(os.path.join(new, '.tux'))
+ self.assertEqual(get_scms_for_path(new), [])
+
+ os.mkdir(os.path.join(new, '.git'))
+ self.assertEqual(set(get_scms_for_path(new)), set(['git']))
+
+ os.mkdir(os.path.join(new, '.hg'))
+ self.assertEqual(set(get_scms_for_path(new)), set(['git', 'hg']))
+
+
+class TestParseChangesets(unittest.TestCase):
+
+ def test_main_is_returned_correctly(self):
+ self.assertEqual(parse_changesets('123456'), {
+ 'start': None,
+ 'main': '123456',
+ 'end': None,
+ })
+
+ def test_start_is_returned_correctly(self):
+ self.assertEqual(parse_changesets('aaabbb..'), {
+ 'start': 'aaabbb',
+ 'main': None,
+ 'end': None,
+ })
+
+ def test_end_is_returned_correctly(self):
+ self.assertEqual(parse_changesets('..cccddd'), {
+ 'start': None,
+ 'main': None,
+ 'end': 'cccddd',
+ })
+
+ def test_that_two_or_three_dots_are_allowed(self):
+ text1 = 'a..b'
+ text2 = 'a...b'
+ self.assertEqual(parse_changesets(text1), parse_changesets(text2))
+
+ def test_that_input_is_stripped_first(self):
+ text1 = 'a..bb'
+ text2 = ' a..bb\t\n\t '
+ self.assertEqual(parse_changesets(text1), parse_changesets(text2))
+
+ def test_that_exception_is_raised(self):
+ text = '123456.789012' # single dot is not recognized
+ with self.assertRaises(ValueError):
+ parse_changesets(text)
+
+ def test_non_alphanumeric_raises_exception(self):
+ with self.assertRaises(ValueError):
+ parse_changesets('aaa@bbb')
+
+
+class TestParseDatetime(unittest.TestCase):
+
+ def test_datetime_text(self):
+ self.assertEqual(parse_datetime('2010-04-07 21:29:41'),
+ datetime.datetime(2010, 4, 7, 21, 29, 41))
+
+ def test_no_seconds(self):
+ self.assertEqual(parse_datetime('2010-04-07 21:29'),
+ datetime.datetime(2010, 4, 7, 21, 29))
+
+ def test_date_only(self):
+ self.assertEqual(parse_datetime('2010-04-07'),
+ datetime.datetime(2010, 4, 7))
+
+ def test_another_format(self):
+ self.assertEqual(parse_datetime('04/07/10 21:29:41'),
+ datetime.datetime(2010, 4, 7, 21, 29, 41))
+
+ def test_now(self):
+ self.assertTrue(parse_datetime('now') - datetime.datetime.now() <
+ datetime.timedelta(seconds=1))
+
+ def test_today(self):
+ today = datetime.date.today()
+ self.assertEqual(parse_datetime('today'),
+ datetime.datetime(*today.timetuple()[:3]))
+
+ def test_yesterday(self):
+ yesterday = datetime.date.today() - datetime.timedelta(days=1)
+ self.assertEqual(parse_datetime('yesterday'),
+ datetime.datetime(*yesterday.timetuple()[:3]))
+
+ def test_tomorrow(self):
+ tomorrow = datetime.date.today() + datetime.timedelta(days=1)
+ args = tomorrow.timetuple()[:3] + (23, 59, 59)
+ self.assertEqual(parse_datetime('tomorrow'), datetime.datetime(*args))
+
+ def test_days(self):
+ timestamp = datetime.datetime.today() - datetime.timedelta(days=3)
+ args = timestamp.timetuple()[:3] + (0, 0, 0, 0)
+ expected = datetime.datetime(*args)
+ self.assertEqual(parse_datetime('3d'), expected)
+ self.assertEqual(parse_datetime('3 d'), expected)
+ self.assertEqual(parse_datetime('3 day'), expected)
+ self.assertEqual(parse_datetime('3 days'), expected)
+
+ def test_weeks(self):
+ timestamp = datetime.datetime.today() - datetime.timedelta(days=3 * 7)
+ args = timestamp.timetuple()[:3] + (0, 0, 0, 0)
+ expected = datetime.datetime(*args)
+ self.assertEqual(parse_datetime('3w'), expected)
+ self.assertEqual(parse_datetime('3 w'), expected)
+ self.assertEqual(parse_datetime('3 week'), expected)
+ self.assertEqual(parse_datetime('3 weeks'), expected)
+
+ def test_mixed(self):
+ timestamp = datetime.datetime.today() - datetime.timedelta(days=2 * 7 + 3)
+ args = timestamp.timetuple()[:3] + (0, 0, 0, 0)
+ expected = datetime.datetime(*args)
+ self.assertEqual(parse_datetime('2w3d'), expected)
+ self.assertEqual(parse_datetime('2w 3d'), expected)
+ self.assertEqual(parse_datetime('2w 3 days'), expected)
+ self.assertEqual(parse_datetime('2 weeks 3 days'), expected)
+
+
+class TestAuthorExtractors(unittest.TestCase):
+ TEST_AUTHORS = [('Marcin Kuzminski <marcin@python-works.com>',
+ ('Marcin Kuzminski', 'marcin@python-works.com')),
+ ('Marcin Kuzminski Spaces < marcin@python-works.com >',
+ ('Marcin Kuzminski Spaces', 'marcin@python-works.com')),
+ ('Marcin Kuzminski <marcin.kuzminski@python-works.com>',
+ ('Marcin Kuzminski', 'marcin.kuzminski@python-works.com')),
+ ('mrf RFC_SPEC <marcin+kuzminski@python-works.com>',
+ ('mrf RFC_SPEC', 'marcin+kuzminski@python-works.com')),
+ ('username <user@email.com>',
+ ('username', 'user@email.com')),
+ ('username <user@email.com',
+ ('username', 'user@email.com')),
+ ('broken missing@email.com',
+ ('broken', 'missing@email.com')),
+ ('<justemail@mail.com>',
+ ('', 'justemail@mail.com')),
+ ('justname',
+ ('justname', '')),
+ ('Mr Double Name withemail@email.com ',
+ ('Mr Double Name', 'withemail@email.com')),
+ ]
+
+ def test_author_email(self):
+
+ for test_str, result in self.TEST_AUTHORS:
+ self.assertEqual(result[1], author_email(test_str))
+
+
+ def test_author_name(self):
+
+ for test_str, result in self.TEST_AUTHORS:
+ self.assertEqual(result[0], author_name(test_str))
+
+
+class TestGetDictForAttrs(unittest.TestCase):
+
+ def test_returned_dict_has_expected_attrs(self):
+ obj = mock.Mock()
+ obj.NOT_INCLUDED = 'this key/value should not be included'
+ obj.CONST = True
+ obj.foo = 'aaa'
+ obj.attrs = {'foo': 'bar'}
+ obj.date = datetime.datetime(2010, 12, 31)
+ obj.count = 1001
+
+ self.assertEqual(get_dict_for_attrs(obj, ['CONST', 'foo', 'attrs',
+ 'date', 'count']), {
+ 'CONST': True,
+ 'foo': 'aaa',
+ 'attrs': {'foo': 'bar'},
+ 'date': datetime.datetime(2010, 12, 31),
+ 'count': 1001,
+ })
+
+
+class TestGetTotalSeconds(unittest.TestCase):
+
+ def assertTotalSecondsEqual(self, timedelta, expected_seconds):
+ result = get_total_seconds(timedelta)
+ self.assertEqual(result, expected_seconds,
+ "We computed %s seconds for %s but expected %s"
+ % (result, timedelta, expected_seconds))
+
+ def test_get_total_seconds_returns_proper_value(self):
+ self.assertTotalSecondsEqual(datetime.timedelta(seconds=1001), 1001)
+
+ def test_get_total_seconds_returns_proper_value_for_partial_seconds(self):
+ self.assertTotalSecondsEqual(datetime.timedelta(seconds=50.65), 50.65)
+
+
+class TestGetUserHome(unittest.TestCase):
+
+ @mock.patch.object(os, 'environ', {})
+ def test_defaults_to_none(self):
+ self.assertEqual(get_user_home(), '')
+
+ @mock.patch.object(os, 'environ', {'HOME': '/home/foobar'})
+ def test_unix_like(self):
+ self.assertEqual(get_user_home(), '/home/foobar')
+
+ @mock.patch.object(os, 'environ', {'USERPROFILE': '/Users/foobar'})
+ def test_windows_like(self):
+ self.assertEqual(get_user_home(), '/Users/foobar')
+
+ @mock.patch.object(os, 'environ', {'HOME': '/home/foobar',
+ 'USERPROFILE': '/Users/foobar'})
+ def test_prefers_home_over_userprofile(self):
+ self.assertEqual(get_user_home(), '/home/foobar')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_utils_filesize.py b/rhodecode/tests/vcs/test_utils_filesize.py
new file mode 100644
index 00000000..44795a79
--- /dev/null
+++ b/rhodecode/tests/vcs/test_utils_filesize.py
@@ -0,0 +1,26 @@
+from __future__ import with_statement
+
+from rhodecode.lib.vcs.utils.filesize import filesizeformat
+from rhodecode.lib.vcs.utils.compat import unittest
+
+
+class TestFilesizeformat(unittest.TestCase):
+
+ def test_bytes(self):
+ self.assertEqual(filesizeformat(10), '10 B')
+
+ def test_kilobytes(self):
+ self.assertEqual(filesizeformat(1024 * 2), '2 KB')
+
+ def test_megabytes(self):
+ self.assertEqual(filesizeformat(1024 * 1024 * 2.3), '2.3 MB')
+
+ def test_gigabytes(self):
+ self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 12.92), '12.92 GB')
+
+ def test_that_function_respects_sep_paramtere(self):
+ self.assertEqual(filesizeformat(1, ''), '1B')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/test_vcs.py b/rhodecode/tests/vcs/test_vcs.py
new file mode 100644
index 00000000..d6179c46
--- /dev/null
+++ b/rhodecode/tests/vcs/test_vcs.py
@@ -0,0 +1,84 @@
+from __future__ import with_statement
+
+from rhodecode.lib.vcs import VCSError, get_repo, get_backend
+from rhodecode.lib.vcs.backends.hg import MercurialRepository
+from rhodecode.lib.vcs.utils.compat import unittest
+from conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH
+import os
+import shutil
+
+
+class VCSTest(unittest.TestCase):
+ """
+ Tests for main module's methods.
+ """
+
+ def test_get_backend(self):
+ hg = get_backend('hg')
+ self.assertEqual(hg, MercurialRepository)
+
+ def test_alias_detect_hg(self):
+ alias = 'hg'
+ path = TEST_HG_REPO
+ backend = get_backend(alias)
+ repo = backend(path)
+ self.assertEqual('hg',repo.alias)
+
+ def test_alias_detect_git(self):
+ alias = 'git'
+ path = TEST_GIT_REPO
+ backend = get_backend(alias)
+ repo = backend(path)
+ self.assertEqual('git',repo.alias)
+
+ def test_wrong_alias(self):
+ alias = 'wrong_alias'
+ self.assertRaises(VCSError, get_backend, alias)
+
+ def test_get_repo(self):
+ alias = 'hg'
+ path = TEST_HG_REPO
+ backend = get_backend(alias)
+ repo = backend(path)
+
+ self.assertEqual(repo.__class__, get_repo(path, alias).__class__)
+ self.assertEqual(repo.path, get_repo(path, alias).path)
+
+ def test_get_repo_autoalias_hg(self):
+ alias = 'hg'
+ path = TEST_HG_REPO
+ backend = get_backend(alias)
+ repo = backend(path)
+
+ self.assertEqual(repo.__class__, get_repo(path).__class__)
+ self.assertEqual(repo.path, get_repo(path).path)
+
+ def test_get_repo_autoalias_git(self):
+ alias = 'git'
+ path = TEST_GIT_REPO
+ backend = get_backend(alias)
+ repo = backend(path)
+
+ self.assertEqual(repo.__class__, get_repo(path).__class__)
+ self.assertEqual(repo.path, get_repo(path).path)
+
+
+ def test_get_repo_err(self):
+ blank_repo_path = os.path.join(TEST_TMP_PATH, 'blank-error-repo')
+ if os.path.isdir(blank_repo_path):
+ shutil.rmtree(blank_repo_path)
+
+ os.mkdir(blank_repo_path)
+ self.assertRaises(VCSError, get_repo, blank_repo_path)
+ self.assertRaises(VCSError, get_repo, blank_repo_path + 'non_existing')
+
+ def test_get_repo_multialias(self):
+ multialias_repo_path = os.path.join(TEST_TMP_PATH, 'hg-git-repo')
+ if os.path.isdir(multialias_repo_path):
+ shutil.rmtree(multialias_repo_path)
+
+ os.mkdir(multialias_repo_path)
+
+ os.mkdir(os.path.join(multialias_repo_path, '.git'))
+ os.mkdir(os.path.join(multialias_repo_path, '.hg'))
+ self.assertRaises(VCSError, get_repo, multialias_repo_path)
diff --git a/rhodecode/tests/vcs/test_workdirs.py b/rhodecode/tests/vcs/test_workdirs.py
new file mode 100644
index 00000000..b8e421ee
--- /dev/null
+++ b/rhodecode/tests/vcs/test_workdirs.py
@@ -0,0 +1,90 @@
+from __future__ import with_statement
+
+import datetime
+from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib.vcs.utils.compat import unittest
+from base import BackendTestMixin
+from conf import SCM_TESTS
+
+
+class WorkdirTestCaseMixin(BackendTestMixin):
+
+ @classmethod
+ def _get_commits(cls):
+ commits = [
+ {
+ 'message': u'Initial commit',
+ 'author': u'Joe Doe <joe.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 20),
+ 'added': [
+ FileNode('foobar', content='Foobar'),
+ FileNode('foobar2', content='Foobar II'),
+ FileNode('foo/bar/baz', content='baz here!'),
+ ],
+ },
+ {
+ 'message': u'Changes...',
+ 'author': u'Jane Doe <jane.doe@example.com>',
+ 'date': datetime.datetime(2010, 1, 1, 21),
+ 'added': [
+ FileNode('some/new.txt', content='news...'),
+ ],
+ 'changed': [
+ FileNode('foobar', 'Foobar I'),
+ ],
+ 'removed': [],
+ },
+ ]
+ return commits
+
+ def test_get_branch_for_default_branch(self):
+ self.assertEqual(self.repo.workdir.get_branch(),
+ self.repo.DEFAULT_BRANCH_NAME)
+
+ def test_get_branch_after_adding_one(self):
+ self.imc.add(FileNode('docs/index.txt',
+ content='Documentation\n'))
+ self.imc.commit(
+ message=u'New branch: foobar',
+ author=u'joe',
+ branch='foobar',
+ )
+
+ def test_get_changeset(self):
+ self.imc.add(FileNode('docs/index.txt',
+ content='Documentation\n'))
+ head = self.imc.commit(
+ message=u'New branch: foobar',
+ author=u'joe',
+ branch='foobar',
+ )
+ self.assertEqual(self.repo.workdir.get_changeset(), head)
+
+ def test_checkout_branch(self):
+ from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError
+ # first, 'foobranch' does not exist.
+ self.assertRaises(BranchDoesNotExistError, self.repo.workdir.checkout_branch,
+ branch='foobranch')
+ # create new branch 'foobranch'.
+ self.imc.add(FileNode('file1', content='blah'))
+ self.imc.commit(message=u'asd', author=u'john', branch='foobranch')
+ # go back to the default branch
+ self.repo.workdir.checkout_branch()
+ self.assertEqual(self.repo.workdir.get_branch(), self.backend_class.DEFAULT_BRANCH_NAME)
+ # checkout 'foobranch'
+ self.repo.workdir.checkout_branch('foobranch')
+ self.assertEqual(self.repo.workdir.get_branch(), 'foobranch')
+
+
+# For each backend create test case class
+for alias in SCM_TESTS:
+ attrs = {
+ 'backend_alias': alias,
+ }
+ cls_name = ''.join(('%s branch test' % alias).title().split())
+ bases = (WorkdirTestCaseMixin, unittest.TestCase)
+ globals()[cls_name] = type(cls_name, bases, attrs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/rhodecode/tests/vcs/utils.py b/rhodecode/tests/vcs/utils.py
new file mode 100644
index 00000000..859a0b96
--- /dev/null
+++ b/rhodecode/tests/vcs/utils.py
@@ -0,0 +1,97 @@
+"""
+Utilities for tests only. These are not or should not be used normally -
+functions here are crafted as we don't want to use ``vcs`` to verify tests.
+"""
+import os
+import re
+import sys
+
+from subprocess import Popen
+
+
+class VCSTestError(Exception):
+ pass
+
+
+def run_command(cmd, args):
+ """
+ Runs command on the system with given ``args``.
+ """
+ command = ' '.join((cmd, args))
+ p = Popen(command, shell=True)
+ status = os.waitpid(p.pid, 0)[1]
+ return status
+
+
+def eprint(msg):
+ """
+ Prints given ``msg`` into sys.stderr as nose test runner hides all output
+ from sys.stdout by default and if we want to pipe stream somewhere we don't
+ need those verbose messages anyway.
+ Appends line break.
+ """
+ sys.stderr.write(msg)
+ sys.stderr.write('\n')
+
+
+class SCMFetcher(object):
+
+ def __init__(self, alias, test_repo_path, remote_repo, clone_cmd):
+ """
+ :param clone_cmd: command which would clone remote repository; pass
+ only first bits - remote path and destination would be appended
+ using ``remote_repo`` and ``test_repo_path``
+ """
+ self.alias = alias
+ self.test_repo_path = test_repo_path
+ self.remote_repo = remote_repo
+ self.clone_cmd = clone_cmd
+
+ def setup(self):
+ if not os.path.isdir(self.test_repo_path):
+ self.fetch_repo()
+
+ def fetch_repo(self):
+ """
+ Tries to fetch repository from remote path.
+ """
+ remote = self.remote_repo
+ eprint("Fetching repository %s into %s" % (remote, self.test_repo_path))
+ run_command(self.clone_cmd, '%s %s' % (remote, self.test_repo_path))
+
+
+def get_normalized_path(path):
+ """
+ If given path exists, new path would be generated and returned. Otherwise
+ same whats given is returned. Assumes that there would be no more than
+ 10000 same named files.
+ """
+ if os.path.exists(path):
+ dir, basename = os.path.split(path)
+ splitted_name = basename.split('.')
+ if len(splitted_name) > 1:
+ ext = splitted_name[-1]
+ else:
+ ext = None
+ name = '.'.join(splitted_name[:-1])
+ matcher = re.compile(r'^.*-(\d{5})$')
+ start = 0
+ m = matcher.match(name)
+ if not m:
+ # Haven't append number yet so return first
+ newname = '%s-00000' % name
+ newpath = os.path.join(dir, newname)
+ if ext:
+ newpath = '.'.join((newpath, ext))
+ return get_normalized_path(newpath)
+ else:
+ start = int(m.group(1)[-5:]) + 1
+ for x in xrange(start, 10000):
+ newname = name[:-5] + str(x).rjust(5, '0')
+ newpath = os.path.join(dir, newname)
+ if ext:
+ newpath = '.'.join((newpath, ext))
+ if not os.path.exists(newpath):
+ return newpath
+ raise VCSTestError("Couldn't compute new path for %s" % path)
+ return path
diff --git a/rhodecode/tests/vcs_test_git.tar.gz b/rhodecode/tests/vcs_test_git.tar.gz
new file mode 100644
index 00000000..5f6dbf4c
--- /dev/null
+++ b/rhodecode/tests/vcs_test_git.tar.gz
Binary files differ
diff --git a/rhodecode/tests/vcs_test_hg.tar.gz b/rhodecode/tests/vcs_test_hg.tar.gz
index e811c841..39e442c4 100644
--- a/rhodecode/tests/vcs_test_hg.tar.gz
+++ b/rhodecode/tests/vcs_test_hg.tar.gz
Binary files differ
diff --git a/rhodecode/websetup.py b/rhodecode/websetup.py
index 4033ad0d..79321041 100644
--- a/rhodecode/websetup.py
+++ b/rhodecode/websetup.py
@@ -38,7 +38,7 @@ def setup_app(command, conf, vars):
dbconf = conf['sqlalchemy.db1.url']
dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'],
tests=False)
- dbmanage.create_tables(override=True)
+ dbmanage.create_tables(override=True, defaults=command.options.__dict__)
dbmanage.set_db_version()
opts = dbmanage.config_prompt(None, defaults=command.options.__dict__)
dbmanage.create_settings(opts)
diff --git a/setup.cfg b/setup.cfg
index c2623138..29cbbff0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[egg_info]
-tag_build =
+tag_build =
tag_svn_revision = true
[easy_install]
diff --git a/setup.py b/setup.py
index 6df00df8..ed833608 100644
--- a/setup.py
+++ b/setup.py
@@ -1,12 +1,71 @@
+import os
import sys
-from rhodecode import get_version
-from rhodecode import __license__
-from rhodecode import __py_version__
-from rhodecode import requirements
+import platform
-if __py_version__ < (2, 5):
+if sys.version_info < (2, 5):
raise Exception('RhodeCode requires python 2.5 or later')
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+def _get_meta_var(name, data, callback_handler=None):
+ import re
+ matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
+ if matches:
+ if not callable(callback_handler):
+ callback_handler = lambda v: v
+
+ return callback_handler(eval(matches.groups()[0]))
+
+_meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
+_metadata = _meta.read()
+_meta.close()
+
+callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
+__version__ = _get_meta_var('VERSION', _metadata, callback)
+__license__ = _get_meta_var('__license__', _metadata)
+__author__ = _get_meta_var('__author__', _metadata)
+__url__ = _get_meta_var('__url__', _metadata)
+# defines current platform
+__platform__ = platform.system()
+
+is_windows = __platform__ in _get_meta_var('PLATFORM_WIN', _metadata)
+
+requirements = [
+ "waitress==0.8.1",
+ "webob==1.0.8",
+ "Pylons==1.0.0",
+ "Beaker==1.6.4",
+ "WebHelpers==1.3",
+ "formencode==1.2.4",
+ "SQLAlchemy==0.7.8",
+ "Mako==0.7.2",
+ "pygments>=1.5",
+ "whoosh>=2.4.0,<2.5",
+ "celery>=2.2.5,<2.3",
+ "babel",
+ "python-dateutil>=1.5.0,<2.0.0",
+ "dulwich>=0.8.5,<0.9.0",
+ "markdown==2.1.1",
+ "docutils==0.8.1",
+ "simplejson==2.5.2",
+ "mock",
+]
+
+if sys.version_info < (2, 6):
+ requirements.append("pysqlite")
+
+if sys.version_info < (2, 7):
+ requirements.append("unittest2")
+
+if is_windows:
+ requirements.append("mercurial>=2.3.0,<2.4")
+else:
+ requirements.append("py-bcrypt")
+ requirements.append("mercurial>=2.3.0,<2.4")
+
+
dependency_links = [
]
@@ -62,15 +121,15 @@ packages = find_packages(exclude=['ez_setup'])
setup(
name='RhodeCode',
- version=get_version(),
+ version=__version__,
description=description,
long_description=long_description,
keywords=keywords,
license=__license__,
- author='Marcin Kuzminski',
+ author=__author__,
author_email='marcin@python-works.com',
dependency_links=dependency_links,
- url='http://rhodecode.org',
+ url=__url__,
install_requires=requirements,
classifiers=classifiers,
setup_requires=["PasteScript>=1.6.3"],
@@ -87,6 +146,9 @@ setup(
zip_safe=False,
paster_plugins=['PasteScript', 'Pylons'],
entry_points="""
+ [console_scripts]
+ rhodecode-api = rhodecode.bin.rhodecode_api:main
+
[paste.app_factory]
main = rhodecode.config.middleware:make_app
@@ -95,6 +157,7 @@ setup(
[paste.global_paster_command]
setup-rhodecode=rhodecode.config.setup_rhodecode:SetupCommand
+ cleanup-repos=rhodecode.lib.cleanup:CleanupCommand
make-index=rhodecode.lib.indexers:MakeIndex
make-rcext=rhodecode.config.rcextensions.make_rcextensions:MakeRcExt
upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb
diff --git a/test.ini b/test.ini
index 448aa2ab..67a4f0a7 100644
--- a/test.ini
+++ b/test.ini
@@ -30,15 +30,16 @@ pdebug = false
[server:main]
##nr of threads to spawn
-threadpool_workers = 5
+#threadpool_workers = 5
##max request before thread respawn
-threadpool_max_requests = 2
+#threadpool_max_requests = 2
##option to use threads of process
-use_threadpool = true
+#use_threadpool = true
-use = egg:Paste#http
+#use = egg:Paste#http
+use = egg:waitress#main
host = 127.0.0.1
port = 5000
@@ -91,7 +92,12 @@ issue_prefix = #
## a prefix key for this instance used for cache invalidation when running
## multiple instances of rhodecode, make sure it's globally unique for
## all running rhodecode instances. Leave empty if you don't use it
-instance_id =
+instance_id =
+
+## alternative return HTTP header for failed authentication. Default HTTP
+## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
+## handling that. Set this variable to 403 to return HTTPForbidden
+auth_ret_code =
####################################
### CELERY CONFIG ####
@@ -204,9 +210,9 @@ logview.pylons.util = #eee
#########################################################
### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
#########################################################
-sqlalchemy.db1.url = sqlite:///%(here)s/test.db
-#sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_tests
-#sqlalchemy.db1.url = mysql://root:qwe123qwe@localhost/rhodecode_tests
+sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.sqlite
+#sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_test
+#sqlalchemy.db1.url = mysql://root:qwe@localhost/rhodecode_test
sqlalchemy.db1.echo = false
sqlalchemy.db1.pool_recycle = 3600
@@ -228,11 +234,11 @@ keys = generic, color_formatter
## LOGGERS ##
#############
[logger_root]
-level = ERROR
+level = DEBUG
handlers = console
[logger_routes]
-level = ERROR
+level = DEBUG
handlers =
qualname = routes.middleware
# "level = DEBUG" logs the route matched and routing variables.
@@ -251,7 +257,7 @@ qualname = pylons.templating
propagate = 1
[logger_rhodecode]
-level = ERROR
+level = DEBUG
handlers =
qualname = rhodecode
propagate = 1
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..53ba7a49
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,112 @@
+[tox]
+envlist = py25-sqlite,
+ py25-mysql,
+ py25-postgresql,
+ py26-sqlite,
+ py26-mysql,
+ py26-postgresql,
+ py27-sqlite,
+ py27-mysql,
+ py27-postgresql
+
+
+#### PYTHON 2.5 ####
+[testenv:py25-sqlite]
+basepython =
+ python2.5
+commands =
+ nosetests []
+setenv =
+ TEST_DB=sqlite:////tmp/rhodecode_test.sqlite
+
+[testenv:py25-mysql]
+basepython =
+ python2.5
+commands =
+ mysql -uroot -pqwe -hlocalhost -e 'drop database if exists rhodecode_test;'
+ mysql -uroot -pqwe -hlocalhost -e 'create database rhodecode_test;'
+ nosetests []
+deps =
+ mysql-python
+setenv =
+ TEST_DB=mysql://root:qwe@localhost/rhodecode_test
+
+[testenv:py25-postgresql]
+basepython =
+ python2.5
+commands =
+ psql -Upostgres -Wqwe -hlocalhost -c 'drop database if exists rhodecode_test;'
+ psql -Upostgres -Wqwe -hlocalhost -c 'create database rhodecode_test;'
+ nosetests []
+deps =
+ psycopg2
+setenv =
+ TEST_DB=postgresql://postgres:qwe@localhost/rhodecode_test
+
+
+#### PYTHON 2.6 ####
+[testenv:py26-sqlite]
+basepython =
+ python2.6
+commands =
+ nosetests []
+setenv =
+ TEST_DB=sqlite:////tmp/rhodecode_test.sqlite
+
+[testenv:py26-mysql]
+basepython =
+ python2.6
+commands =
+ mysql -uroot -pqwe -hlocalhost -e 'drop database if exists rhodecode_test;'
+ mysql -uroot -pqwe -hlocalhost -e 'create database rhodecode_test;'
+ nosetests []
+deps =
+ mysql-python
+setenv =
+ TEST_DB=mysql://root:qwe@localhost/rhodecode_test
+
+[testenv:py26-postgresql]
+basepython =
+ python2.6
+commands =
+ psql -Upostgres -Wqwe -hlocalhost -c 'drop database if exists rhodecode_test;'
+ psql -Upostgres -Wqwe -hlocalhost -c 'create database rhodecode_test;'
+ nosetests []
+deps =
+ psycopg2
+setenv =
+ TEST_DB=postgresql://postgres:qwe@localhost/rhodecode_test
+
+
+#### PYTHON 2.7 ####
+[testenv:py27-sqlite]
+basepython =
+ python2.7
+commands =
+ nosetests []
+setenv =
+ TEST_DB=sqlite:////tmp/rhodecode_test.sqlite
+
+[testenv:py27-mysql]
+basepython =
+ python2.7
+commands =
+ mysql -uroot -pqwe -hlocalhost -e 'drop database if exists rhodecode_test;'
+ mysql -uroot -pqwe -hlocalhost -e 'create database rhodecode_test;'
+ nosetests []
+deps =
+ mysql-python
+setenv =
+ TEST_DB=mysql://root:qwe@localhost/rhodecode_test
+
+[testenv:py27-postgresql]
+basepython =
+ python2.7
+commands =
+ psql -Upostgres -Wqwe -hlocalhost -c 'drop database if exists rhodecode_test;'
+ psql -Upostgres -Wqwe -hlocalhost -c 'create database rhodecode_test;'
+ nosetests []
+deps =
+ psycopg2
+setenv =
+ TEST_DB=postgresql://postgres:qwe@localhost/rhodecode_test \ No newline at end of file