Changeset - 148360f533a4
.hgtags
Show inline comments
 
@@ -38,24 +38,25 @@ b6b611e7722e754abebaae6e265cbb4c823d344d
 
dbc82e3362a25d2aece42060089824c4342efd17 rhodecode-0.0.1.3.0
 
79a95f338fd0115b2cdb77118f39e17d22ff505c rhodecode-0.0.1.3.1
 
9ab21c5ddb84935bea5c743b4e147ed5a398b30c rhodecode-0.0.1.3.2
 
934906f028b582a254e0028ba25e5d20dd32b9cd rhodecode-0.0.1.3.3
 
af21362474e3ab5aa0e2fbb1c872356f2c16c4f3 rhodecode-0.0.1.3.4
 
0e2792e04bd316fe64335cbe6a476031ac60b29b rhodecode-0.0.1.3.5
 
edfff9f37916389144d3a3644d0a7d7adfd79b11 rhodecode-0.0.1.3.6
 
9ae95fdeca184f2404205645f06c6597b74ef2db rhodecode-0.0.1.4.0
 
909143a4dde53c46d4f24abb426ec870471c7de1 rhodecode-0.0.1.4.1
 
d998cc84cf726798486a438763053f0e1dc1b646 rhodecode-0.0.1.4.2
 
3f5d40b9dd99ccb009ea2211ee2d4b594c634946 rhodecode-0.0.1.4.3
 
3148c08cf86f1849917e2d50f7ab7766c1550b0a rhodecode-0.0.1.4.4
 
a5f0bc867edc88be23eb808693e5393a97d4c54a rhodecode-0.0.1.5.0
 
3259dc7caea48687eab018ee646ae6ad7e7ef377 rhodecode-0.0.1.5.1
 
efe23d6c178c11d575a0214181276a3452776e48 rhodecode-0.0.1.5.2
 
1a498b11f1540f5b94b6f6009298f5dc3eaad9e9 rhodecode-0.0.1.5.3
 
3447862ad8c9ceba85857774c526e39fde3a2281 rhodecode-0.0.1.5.4
 
c15d7b336af58df9f1bbc8f8957464e7ea618d4c rhodecode-0.0.1.6.0rc1
 
78b53ee0d247f90d51b028307ff5717851b6c265 rhodecode-0.0.1.6.0
 
351ad34d56321349ff5bd38f537bd768b8efef2e rhodecode-0.0.1.7.0
 
1f71ef689d2a3c9978cea6591a1f4e9107a5ca83 rhodecode-0.0.1.7.1
 
d17e88a1a88a29f6fac948c94498129e405a40d3 0.1
 
ad0ce803b40cb17fc3988373052943e041030b02 0.2
 
c6e32714336345403adf76abb6ebf9b8116fcdc7 0.2.1
 
14f488a5dc4ca6647bc6acf12534fd137e968aa8 0.2.2
docs/api/api.rst
Show inline comments
 
@@ -812,50 +812,51 @@ OUTPUT::
 
                "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":             "<username or user_id>",
 
                "fork_of":           "<name_of_fork_parent>",
 
                "enable_downloads":  "<bool>",
 
                "enable_locking":    "<bool>",
 
                "enable_statistics": "<bool>",
 
              },
 
            }
 
    error:  null
 

	
 

	
 
fork_repo
 
---------
 

	
 
Create a fork of the given repo. If using Celery, this will
 
return success message immediately and a fork will be created
 
asynchronously.
 
This command can only be executed using the api_key of a user with admin rights,
 
or that of a regular user with fork permission and at least read access to the repository.
 
This command can only be executed using the api_key of a user with admin
 
rights, or with the global fork permission, by a regular user with create
 
repository permission and at least read access to the repository.
 
Regular users cannot specify owner parameter.
 

	
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "fork_repo"
 
    args:     {
 
                "repoid" :          "<reponame or repo_id>",
 
                "fork_name":        "<forkname>",
 
                "owner":            "<username or user_id = Optional(=apiuser)>",
 
                "description":      "<description>",
 
                "copy_permissions": "<bool>",
 
                "private":          "<bool>",
 
                "landing_rev":      "<landing_rev>"
 

	
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "Created fork of `<reponame>` as `<forkname>`",
kallithea/__init__.py
Show inline comments
 
@@ -8,49 +8,49 @@
 
# 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/>.
 
"""
 
kallithea.__init__
 
~~~~~~~~~~~~~~~~~~
 

	
 
Kallithea, a web based repository management based on pylons
 
versioning implementation: http://www.python.org/dev/peps/pep-0386/
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 9, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, (C) 2014 Bradley M. Kuhn, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import sys
 
import platform
 

	
 
VERSION = (0, 2, 1)
 
VERSION = (0, 2, 2)
 
BACKENDS = {
 
    'hg': 'Mercurial repository',
 
    'git': 'Git repository',
 
}
 

	
 
CELERY_ON = False
 
CELERY_EAGER = False
 

	
 
# link to config for pylons
 
CONFIG = {}
 

	
 
# Linked module for extensions
 
EXTENSIONS = {}
 

	
 
# BRAND controls internal references in database and config to the products
 
# own name.
 
#
 
# NOTE: If you want compatibility with a database that was originally created
 
#  for use with the RhodeCode software product, change BRAND to "rhodecode",
 
#  either by editing here or by creating a new file:
 
#  echo "BRAND = 'rhodecode'" > kallithea/brand.py
 

	
 
BRAND = "kallithea"
 
try:
kallithea/controllers/admin/my_account.py
Show inline comments
 
@@ -239,34 +239,34 @@ class MyAccountController(BaseController
 
            (str(5), _('5 minutes')),
 
            (str(60), _('1 hour')),
 
            (str(60 * 24), _('1 day')),
 
            (str(60 * 24 * 30), _('1 month')),
 
        ]
 
        c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
 
        c.user_api_keys = ApiKeyModel().get_api_keys(self.authuser.user_id,
 
                                                     show_expired=show_expired)
 
        return render('admin/my_account/my_account.html')
 

	
 
    def my_account_api_keys_add(self):
 
        lifetime = safe_int(request.POST.get('lifetime'), -1)
 
        description = request.POST.get('description')
 
        ApiKeyModel().create(self.authuser.user_id, description, lifetime)
 
        Session().commit()
 
        h.flash(_("API key successfully created"), category='success')
 
        return redirect(url('my_account_api_keys'))
 

	
 
    def my_account_api_keys_delete(self):
 
        api_key = request.POST.get('del_api_key')
 
        user_id = self.authuser.user_id
 
        if request.POST.get('del_api_key_builtin'):
 
            user = User.get(user_id)
 
            if user:
 
                user.api_key = generate_api_key(user.username)
 
                user.api_key = generate_api_key()
 
                Session().add(user)
 
                Session().commit()
 
                h.flash(_("API key successfully reset"), category='success')
 
        elif api_key:
 
            ApiKeyModel().delete(api_key, self.authuser.user_id)
 
            Session().commit()
 
            h.flash(_("API key successfully deleted"), category='success')
 

	
 
        return redirect(url('my_account_api_keys'))
kallithea/controllers/admin/users.py
Show inline comments
 
@@ -296,49 +296,49 @@ class UsersController(BaseController):
 
        defaults = c.user.get_dict()
 
        return htmlfill.render(
 
            render('admin/users/user_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def add_api_key(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 

	
 
        lifetime = safe_int(request.POST.get('lifetime'), -1)
 
        description = request.POST.get('description')
 
        ApiKeyModel().create(c.user.user_id, description, lifetime)
 
        Session().commit()
 
        h.flash(_("API key successfully created"), category='success')
 
        return redirect(url('edit_user_api_keys', id=c.user.user_id))
 

	
 
    def delete_api_key(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 

	
 
        api_key = request.POST.get('del_api_key')
 
        if request.POST.get('del_api_key_builtin'):
 
            user = User.get(c.user.user_id)
 
            if user:
 
                user.api_key = generate_api_key(user.username)
 
                user.api_key = generate_api_key()
 
                Session().add(user)
 
                Session().commit()
 
                h.flash(_("API key successfully reset"), category='success')
 
        elif api_key:
 
            ApiKeyModel().delete(api_key, c.user.user_id)
 
            Session().commit()
 
            h.flash(_("API key successfully deleted"), category='success')
 

	
 
        return redirect(url('edit_user_api_keys', id=c.user.user_id))
 

	
 
    def update_account(self, id):
 
        pass
 

	
 
    def edit_perms(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 
        c.active = 'perms'
 
        c.perm_user = AuthUser(user_id=id)
 
        c.ip_addr = self.ip_addr
 

	
 
        umodel = UserModel()
 
        defaults = c.user.get_dict()
 
        defaults.update({
 
            'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'),
 
            'create_user_group_perm': umodel.has_perm(c.user,
kallithea/controllers/api/api.py
Show inline comments
 
@@ -1535,48 +1535,59 @@ class ApiController(JSONRPCController):
 
        Updates repo
 

	
 
        :param apiuser: filled automatically from apikey
 
        :type apiuser: AuthUser
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param name:
 
        :param owner:
 
        :param group:
 
        :param description:
 
        :param private:
 
        :param clone_uri:
 
        :param landing_rev:
 
        :param enable_statistics:
 
        :param enable_locking:
 
        :param enable_downloads:
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if not HasPermissionAnyApi('hg.admin')(user=apiuser):
 
            # check if we have admin permission for this repo !
 
            if not HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
 
                                                               repo_name=repo.repo_name):
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 

	
 
            if (name != repo.repo_name and
 
                not HasPermissionAnyApi('hg.create.repository')(user=apiuser)
 
                ):
 
                raise JSONRPCError('no permission to create (or move) repositories')
 

	
 
            if not isinstance(owner, Optional):
 
                #forbid setting owner for non-admins
 
                raise JSONRPCError(
 
                    'Only Kallithea admin can specify `owner` param'
 
                )
 

	
 
        updates = {
 
            # update function requires this.
 
            'repo_name': repo.repo_name
 
        }
 
        repo_group = group
 
        if not isinstance(repo_group, Optional):
 
            repo_group = get_repo_group_or_error(repo_group)
 
            repo_group = repo_group.group_id
 
        try:
 
            store_update(updates, name, 'repo_name')
 
            store_update(updates, repo_group, 'repo_group')
 
            store_update(updates, owner, 'user')
 
            store_update(updates, description, 'repo_description')
 
            store_update(updates, private, 'repo_private')
 
            store_update(updates, clone_uri, 'clone_uri')
 
            store_update(updates, landing_rev, 'repo_landing_rev')
 
            store_update(updates, enable_statistics, 'repo_enable_statistics')
 
            store_update(updates, enable_locking, 'repo_enable_locking')
 
            store_update(updates, enable_downloads, 'repo_enable_downloads')
 

	
 
            RepoModel().update(repo, **updates)
 
            Session().commit()
 
            return dict(
 
                msg='updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
 
@@ -1632,48 +1643,51 @@ class ApiController(JSONRPCController):
 
                      "task": "<celery task id or None if done sync>"
 
                    }
 
            error:  null
 

	
 
        """
 
        repo = get_repo_or_error(repoid)
 
        repo_name = repo.repo_name
 

	
 
        _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))
 

	
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser):
 
            pass
 
        elif HasRepoPermissionAnyApi('repository.admin',
 
                                     'repository.write',
 
                                     'repository.read')(user=apiuser,
 
                                                        repo_name=repo.repo_name):
 
            if not isinstance(owner, Optional):
 
                #forbid setting owner for non-admins
 
                raise JSONRPCError(
 
                    'Only Kallithea admin can specify `owner` param'
 
                )
 

	
 
            if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
 
                raise JSONRPCError('no permission to create repositories')
 
        else:
 
            raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 

	
 
        if isinstance(owner, Optional):
 
            owner = apiuser.user_id
 

	
 
        owner = get_user_or_error(owner)
 

	
 
        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,
 
            )
kallithea/i18n/be/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -986,57 +986,57 @@ msgstr "Адбылася памылка пры стварэнні карыстача %s"
 
msgid "User updated successfully"
 
msgstr "Карыстач паспяхова абноўлены"
 

	
 
#: kallithea/controllers/admin/users.py:222
 
msgid "Successfully deleted user"
 
msgstr "Карыстач паспяхова выдалены"
 

	
 
#: kallithea/controllers/admin/users.py:227
 
msgid "An error occurred during deletion of user"
 
msgstr "Адбылася памылка пры выдаленні карыстача"
 

	
 
#: kallithea/controllers/admin/users.py:241
 
#: kallithea/controllers/admin/users.py:259
 
#: kallithea/controllers/admin/users.py:282
 
#: kallithea/controllers/admin/users.py:307
 
#: kallithea/controllers/admin/users.py:320
 
#: kallithea/controllers/admin/users.py:344
 
#: kallithea/controllers/admin/users.py:407
 
#: kallithea/controllers/admin/users.py:454
 
msgid "You can't edit this user"
 
msgstr "Вы не можаце рэдагаваць дадзенага карыстача"
 

	
 
#: kallithea/controllers/admin/users.py:482
 
#, python-format
 
msgid "Added IP address %s to user whitelist"
 
msgid "Added ip %s to user whitelist"
 
msgstr "Дададзены IP %s у белы спіс карыстача"
 

	
 
#: kallithea/controllers/admin/users.py:488
 
msgid "An error occurred during ip saving"
 
msgstr "Адбылася памылка пры захаванні IP"
 

	
 
#: kallithea/controllers/admin/users.py:502
 
msgid "Removed IP address from user whitelist"
 
msgid "Removed ip address from user whitelist"
 
msgstr "Выдалены IP %s з белага спісу карыстача"
 

	
 
#: kallithea/lib/auth.py:745
 
#, python-format
 
msgid "IP %s not allowed"
 
msgstr "IP %s заблакаваны"
 

	
 
#: kallithea/lib/auth.py:806
 
msgid "You need to be a registered user to perform this action"
 
msgstr "Вы павінны быць зарэгістраваным карыстачом, каб выканаць гэта дзеянне"
 

	
 
#: kallithea/lib/auth.py:843
 
msgid "You need to be signed in to view this page"
 
msgstr "Старонка даступная толькі аўтарызаваным карыстачам"
 

	
 
#: kallithea/lib/base.py:427
 
msgid "Repository not found in the filesystem"
 
msgstr "Рэпазітар не знойдзены на файлавай сістэме"
 

	
 
#: kallithea/lib/base.py:453 kallithea/lib/helpers.py:643
 
msgid "Changeset not found"
 
msgstr "Набор змен не знойдзены"
 

	
 
#: kallithea/lib/diffs.py:66
 
@@ -2020,49 +2020,49 @@ msgstr "Гэты шлях хібны"
 

	
 
#: kallithea/model/validators.py:715
 
msgid "This e-mail address is already taken"
 
msgstr "Гэты E-mail ужо заняты"
 

	
 
#: kallithea/model/validators.py:735
 
#, python-format
 
msgid "e-mail \"%(email)s\" does not exist."
 
msgstr "\"%(email)s\" не існуе."
 

	
 
#: kallithea/model/validators.py:772
 
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 - гэта "
 
"эквівалент імя карыстача"
 

	
 
#: kallithea/model/validators.py:785
 
#, python-format
 
msgid "Revisions %(revs)s are already part of pull request or have set status"
 
msgstr "Рэвізіі %(revs)s ужо ўключаны ў pull-request ці маюць усталяваны статус"
 

	
 
#: kallithea/model/validators.py:817
 
msgid "Please enter a valid IPv4 or IPv6 address"
 
msgid "Please enter a valid IPv4 or IpV6 address"
 
msgstr "Калі ласка, увядзіце існы IPv4 ці IPv6 адрас"
 

	
 
#: kallithea/model/validators.py:818
 
#, python-format
 
msgid "The network size (bits) must be within the range of 0-32 (not %(bits)r)"
 
msgstr ""
 
"Значэнне маскі падсеткі павінна быць у межах ад 0 да 32 (%(bits)r - няслушна)"
 

	
 
#: kallithea/model/validators.py:851
 
msgid "Key name can only consist of letters, underscore, dash or numbers"
 
msgstr ""
 
"Ключавое імя можа толькі складацца з літар, знака падкрэслення, працяжнік ці "
 
"лікаў"
 

	
 
#: kallithea/model/validators.py:865
 
msgid "Filename cannot be inside a directory"
 
msgstr "Файла няма ў каталогу"
 

	
 
#: kallithea/model/validators.py:881
 
#, python-format
 
msgid "Plugins %(loaded)s and %(next_to_load)s both export the same name"
 
msgstr ""
 

	
 
#: kallithea/templates/about.html:4 kallithea/templates/about.html:17
kallithea/i18n/kallithea.pot
Show inline comments
 
# Translations template for Kallithea.
 
# Copyright (C) 2015 Various authors, licensing as GPLv3
 
# This file is distributed under the same license as the Kallithea project.
 
# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
 
##, fuzzy
 
msgid ""
 
msgstr ""
 
"Project-Id-Version: Kallithea 0.1\n"
 
"Project-Id-Version: Kallithea 0.2.2\n"
 
"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 
"POT-Creation-Date: 2015-04-01 03:17+0200\n"
 
"POT-Creation-Date: 2015-07-12 18:32+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"
 
"MIME-Version: 1.0\n"
 
"Content-Type: text/plain; charset=UTF-8\n"
 
"Content-Transfer-Encoding: 8bit\n"
 

	
 
#: kallithea/controllers/changelog.py:86
 
#: kallithea/controllers/pullrequests.py:247 kallithea/lib/base.py:449
 
msgid "There are no changesets yet"
 
msgstr ""
 

	
 
#: kallithea/controllers/changelog.py:157
 
#: kallithea/controllers/admin/permissions.py:62
 
#: kallithea/controllers/admin/permissions.py:66
 
#: kallithea/controllers/admin/permissions.py:70
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:104
 
msgid "None"
 
msgstr ""
 

	
 
#: kallithea/controllers/changelog.py:160 kallithea/controllers/files.py:197
 
msgid "(closed)"
 
msgstr ""
 

	
 
#: kallithea/controllers/changeset.py:89
 
msgid "Show whitespace"
 
msgstr ""
 

	
 
#: kallithea/controllers/changeset.py:96 kallithea/controllers/changeset.py:103
 
#: kallithea/templates/files/diff_2way.html:55
 
msgid "Ignore whitespace"
 
msgstr ""
 

	
 
#: kallithea/controllers/changeset.py:169
 
#, python-format
 
msgid "increase diff context to %(num)s lines"
 
msgstr ""
 

	
 
#: kallithea/controllers/changeset.py:212 kallithea/controllers/files.py:97
 
#: kallithea/controllers/files.py:117 kallithea/controllers/files.py:746
 
#: kallithea/controllers/files.py:117 kallithea/controllers/files.py:745
 
msgid "Such revision does not exist for this repository"
 
msgstr ""
 

	
 
#: kallithea/controllers/changeset.py:352
 
#: kallithea/controllers/pullrequests.py:699
 
msgid "No comments."
 
msgstr ""
 

	
 
#: kallithea/controllers/changeset.py:382
 
msgid "Changing status on a changeset associated with a closed pull request is not allowed"
 
msgstr ""
 

	
 
#: kallithea/controllers/compare.py:158 kallithea/templates/base/root.html:42
 
msgid "Select changeset"
 
msgstr ""
 

	
 
#: kallithea/controllers/compare.py:255
 
msgid "Cannot compare repositories without using common ancestor"
 
msgstr ""
 

	
 
#: kallithea/controllers/error.py:96
 
msgid "The request could not be understood by the server due to malformed syntax."
 
msgstr ""
 

	
 
@@ -182,88 +182,88 @@ msgstr ""
 
msgid "No filename"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:494
 
msgid "Location must be relative path and must not contain .. in path"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:528
 
msgid "Downloads disabled"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:539
 
#, python-format
 
msgid "Unknown revision %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:541
 
msgid "Empty repository"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:543
 
msgid "Unknown archive type"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:775
 
#: kallithea/controllers/files.py:774
 
#: kallithea/templates/changeset/changeset_range.html:9
 
#: kallithea/templates/email_templates/pull_request.html:15
 
#: kallithea/templates/pullrequests/pullrequest.html:116
 
msgid "Changesets"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:776 kallithea/controllers/pullrequests.py:182
 
#: kallithea/controllers/files.py:775 kallithea/controllers/pullrequests.py:182
 
#: kallithea/controllers/summary.py:74 kallithea/model/scm.py:816
 
#: kallithea/templates/switch_to_list.html:3
 
#: kallithea/templates/branches/branches.html:10
 
msgid "Branches"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:777 kallithea/controllers/pullrequests.py:183
 
#: kallithea/controllers/files.py:776 kallithea/controllers/pullrequests.py:183
 
#: kallithea/controllers/summary.py:75 kallithea/model/scm.py:827
 
#: kallithea/templates/switch_to_list.html:25
 
#: kallithea/templates/tags/tags.html:10
 
msgid "Tags"
 
msgstr ""
 

	
 
#: kallithea/controllers/forks.py:187
 
#, python-format
 
msgid "An error occurred during repository forking %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/home.py:84
 
msgid "Groups"
 
msgstr ""
 

	
 
#: kallithea/controllers/home.py:89
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:106
 
#: kallithea/templates/admin/repos/repo_add.html:12
 
#: kallithea/templates/admin/repos/repo_add.html:16
 
#: kallithea/templates/admin/repos/repos.html:9
 
#: kallithea/templates/admin/users/user_edit_advanced.html:6
 
#: kallithea/templates/base/base.html:60 kallithea/templates/base/base.html:77
 
#: kallithea/templates/base/base.html:127
 
#: kallithea/templates/base/base.html:390
 
#: kallithea/templates/base/base.html:562
 
#: kallithea/templates/base/base.html:131
 
#: kallithea/templates/base/base.html:394
 
#: kallithea/templates/base/base.html:566
 
msgid "Repositories"
 
msgstr ""
 

	
 
#: kallithea/controllers/home.py:130
 
#: kallithea/templates/files/files_add.html:32
 
#: kallithea/templates/files/files_delete.html:23
 
#: kallithea/templates/files/files_edit.html:32
 
msgid "Branch"
 
msgstr ""
 

	
 
#: kallithea/controllers/home.py:136
 
msgid "Tag"
 
msgstr ""
 

	
 
#: kallithea/controllers/home.py:142
 
msgid "Bookmark"
 
msgstr ""
 

	
 
#: kallithea/controllers/journal.py:111 kallithea/controllers/journal.py:153
 
msgid "public journal"
 
msgstr ""
 

	
 
#: kallithea/controllers/journal.py:115 kallithea/controllers/journal.py:157
 
msgid "journal"
 
@@ -392,54 +392,54 @@ msgstr ""
 
msgid "Note: Branch %s has another head: %s."
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:614
 
msgid "Git pull requests don't support updates yet."
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:701
 
msgid "Closing."
 
msgstr ""
 

	
 
#: kallithea/controllers/search.py:135
 
msgid "Invalid search query. Try quoting it."
 
msgstr ""
 

	
 
#: kallithea/controllers/search.py:140
 
msgid "There is no index to search in. Please run whoosh indexer"
 
msgstr ""
 

	
 
#: kallithea/controllers/search.py:144
 
msgid "An error occurred during search operation."
 
msgstr ""
 

	
 
#: kallithea/controllers/summary.py:199
 
#: kallithea/templates/summary/summary.html:387
 
#: kallithea/templates/summary/summary.html:388
 
msgid "No data ready yet"
 
msgstr ""
 

	
 
#: kallithea/controllers/summary.py:202
 
#: kallithea/templates/summary/summary.html:101
 
#: kallithea/templates/summary/summary.html:102
 
msgid "Statistics are disabled for this repository"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/auth_settings.py:125
 
msgid "Auth settings updated successfully"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/auth_settings.py:136
 
msgid "error occurred during update of auth settings"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/defaults.py:97
 
msgid "Default settings updated successfully"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/defaults.py:112
 
msgid "Error occurred during update of defaults"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/gists.py:59
 
#: kallithea/controllers/admin/my_account.py:238
 
#: kallithea/controllers/admin/users.py:288
 
msgid "forever"
 
msgstr ""
 
@@ -565,52 +565,52 @@ msgstr ""
 
msgid "Write"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:65
 
#: kallithea/controllers/admin/permissions.py:69
 
#: kallithea/controllers/admin/permissions.py:73
 
#: kallithea/templates/admin/auth/auth_settings.html:9
 
#: kallithea/templates/admin/defaults/defaults.html:9
 
#: kallithea/templates/admin/permissions/permissions.html:9
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:9
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:9
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:10
 
#: kallithea/templates/admin/repos/repo_add.html:10
 
#: kallithea/templates/admin/repos/repo_add.html:14
 
#: kallithea/templates/admin/repos/repos.html:9
 
#: kallithea/templates/admin/settings/settings.html:9
 
#: kallithea/templates/admin/user_groups/user_group_add.html:8
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:9
 
#: kallithea/templates/admin/user_groups/user_groups.html:10
 
#: kallithea/templates/admin/users/user_add.html:8
 
#: kallithea/templates/admin/users/user_edit.html:9
 
#: kallithea/templates/admin/users/user_edit_profile.html:114
 
#: kallithea/templates/admin/users/users.html:10
 
#: kallithea/templates/admin/users/users.html:55
 
#: kallithea/templates/base/base.html:255
 
#: kallithea/templates/base/base.html:256
 
#: kallithea/templates/base/base.html:262
 
#: kallithea/templates/base/base.html:263
 
#: kallithea/templates/base/base.html:259
 
#: kallithea/templates/base/base.html:260
 
#: kallithea/templates/base/base.html:266
 
#: kallithea/templates/base/base.html:267
 
msgid "Admin"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:76
 
#: kallithea/controllers/admin/permissions.py:87
 
#: kallithea/controllers/admin/permissions.py:92
 
#: kallithea/controllers/admin/permissions.py:95
 
#: kallithea/controllers/admin/permissions.py:98
 
#: kallithea/controllers/admin/permissions.py:101
 
msgid "Disabled"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:78
 
msgid "Allowed with manual account activation"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:80
 
msgid "Allowed with automatic account activation"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:83
 
#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1439
 
#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1485
 
#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1542
 
@@ -680,192 +680,188 @@ msgstr ""
 
msgid "This group contains %s subgroups and cannot be deleted"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repo_groups.py:302
 
#, python-format
 
msgid "Removed repository group %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repo_groups.py:307
 
#, python-format
 
msgid "Error occurred during deletion of repository group %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repo_groups.py:420
 
#: kallithea/controllers/admin/repo_groups.py:455
 
#: kallithea/controllers/admin/user_groups.py:340
 
msgid "Cannot revoke permission for yourself as admin"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repo_groups.py:435
 
msgid "Repository Group permissions updated"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repo_groups.py:472
 
#: kallithea/controllers/admin/repos.py:430
 
#: kallithea/controllers/admin/repos.py:429
 
#: kallithea/controllers/admin/user_groups.py:352
 
msgid "An error occurred during revoking of permission"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:163
 
#: kallithea/controllers/admin/repos.py:162
 
#, python-format
 
msgid "Error creating repository %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:238
 
#: kallithea/controllers/admin/repos.py:237
 
#, python-format
 
msgid "Created repository %s from %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:247
 
#: kallithea/controllers/admin/repos.py:246
 
#, python-format
 
msgid "Forked repository %s as %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:250
 
#: kallithea/controllers/admin/repos.py:249
 
#, python-format
 
msgid "Created repository %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:290
 
#: kallithea/controllers/admin/repos.py:289
 
#, python-format
 
msgid "Repository %s updated successfully"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:309
 
#: kallithea/controllers/admin/repos.py:308
 
#, python-format
 
msgid "Error occurred during update of repository %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:336
 
#: kallithea/controllers/admin/repos.py:335
 
#, python-format
 
msgid "Detached %s forks"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:339
 
#: kallithea/controllers/admin/repos.py:338
 
#, python-format
 
msgid "Deleted %s forks"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:344
 
#: kallithea/controllers/admin/repos.py:343
 
#, python-format
 
msgid "Deleted repository %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:347
 
#: kallithea/controllers/admin/repos.py:346
 
#, python-format
 
msgid "Cannot delete %s it still contains attached forks"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:352
 
#: kallithea/controllers/admin/repos.py:351
 
#, python-format
 
msgid "An error occurred during deletion of %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:406
 
#: kallithea/controllers/admin/repos.py:405
 
msgid "Repository permissions updated"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:462
 
#: kallithea/controllers/admin/repos.py:461
 
msgid "An error occurred during creation of field"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:476
 
#: kallithea/controllers/admin/repos.py:475
 
msgid "An error occurred during removal of field"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:492
 
#: kallithea/controllers/admin/repos.py:491
 
msgid "-- Not a fork --"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:522
 
msgid "Updated repository visibility in public journal"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:526
 
msgid "Updated repository visibility in public journal"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:530
 
msgid "An error occurred during setting this repository in public journal"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:535 kallithea/model/validators.py:340
 
msgid "Token mismatch"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:550
 
#: kallithea/controllers/admin/repos.py:543
 
msgid "Nothing"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:545
 
#, python-format
 
msgid "Marked repo %s as fork of %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:552
 
#, python-format
 
msgid "Marked repo %s as fork of %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:559
 
msgid "An error occurred during this operation"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:575
 
#: kallithea/controllers/admin/repos.py:568
 
msgid "Locked repository"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:578
 
#: kallithea/controllers/admin/repos.py:571
 
msgid "Unlocked repository"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:581
 
#: kallithea/controllers/admin/repos.py:608
 
#: kallithea/controllers/admin/repos.py:574
 
#: kallithea/controllers/admin/repos.py:601
 
msgid "An error occurred during unlocking"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:599
 
#: kallithea/controllers/admin/repos.py:592
 
msgid "Unlocked"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:602
 
#: kallithea/controllers/admin/repos.py:595
 
msgid "Locked"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:604
 
#: kallithea/controllers/admin/repos.py:597
 
#, python-format
 
msgid "Repository has been %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:622
 
#: kallithea/controllers/admin/repos.py:615
 
msgid "Cache invalidation successful"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:626
 
#: kallithea/controllers/admin/repos.py:619
 
msgid "An error occurred during cache invalidation"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:641
 
#: kallithea/controllers/admin/repos.py:634
 
msgid "Pulled from remote location"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:644
 
#: kallithea/controllers/admin/repos.py:637
 
msgid "An error occurred during pull from remote location"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repos.py:677
 
#: kallithea/controllers/admin/repos.py:670
 
msgid "An error occurred during deletion of repository stats"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:170
 
msgid "Updated VCS settings"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:174
 
msgid "Unable to activate hgsubversion support. The \"hgsubversion\" library is missing"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:180
 
#: kallithea/controllers/admin/settings.py:274
 
msgid "Error occurred during updating application settings"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:213
 
#, python-format
 
msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:270
 
msgid "Updated application settings"
 
msgstr ""
 
@@ -973,233 +969,233 @@ msgstr ""
 
#: kallithea/controllers/admin/users.py:241
 
#: kallithea/controllers/admin/users.py:259
 
#: kallithea/controllers/admin/users.py:282
 
#: kallithea/controllers/admin/users.py:307
 
#: kallithea/controllers/admin/users.py:320
 
#: kallithea/controllers/admin/users.py:344
 
#: kallithea/controllers/admin/users.py:407
 
#: kallithea/controllers/admin/users.py:454
 
msgid "You can't edit this user"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/users.py:482
 
#, python-format
 
msgid "Added IP address %s to user whitelist"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/users.py:488
 
msgid "An error occurred during ip saving"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/users.py:502
 
msgid "Removed IP address from user whitelist"
 
msgstr ""
 

	
 
#: kallithea/lib/auth.py:745
 
#: kallithea/lib/auth.py:746
 
#, python-format
 
msgid "IP %s not allowed"
 
msgstr ""
 

	
 
#: kallithea/lib/auth.py:806
 
#: kallithea/lib/auth.py:814
 
msgid "You need to be a registered user to perform this action"
 
msgstr ""
 

	
 
#: kallithea/lib/auth.py:843
 
#: kallithea/lib/auth.py:851
 
msgid "You need to be signed in to view this page"
 
msgstr ""
 

	
 
#: kallithea/lib/base.py:427
 
msgid "Repository not found in the filesystem"
 
msgstr ""
 

	
 
#: kallithea/lib/base.py:453 kallithea/lib/helpers.py:643
 
#: kallithea/lib/base.py:453 kallithea/lib/helpers.py:626
 
msgid "Changeset not found"
 
msgstr ""
 

	
 
#: kallithea/lib/diffs.py:66
 
msgid "Binary file"
 
msgstr ""
 

	
 
#: kallithea/lib/diffs.py:82
 
msgid "Changeset was too big and was cut off, use diff menu to display this diff"
 
msgstr ""
 

	
 
#: kallithea/lib/diffs.py:92
 
msgid "No changes detected"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:627
 
#: kallithea/lib/helpers.py:610
 
#, python-format
 
msgid "Deleted branch: %s"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:630
 
#: kallithea/lib/helpers.py:613
 
#, python-format
 
msgid "Created tag: %s"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:693
 
#: kallithea/lib/helpers.py:676
 
#, python-format
 
msgid "Show all combined changesets %s->%s"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:699
 
#: kallithea/lib/helpers.py:682
 
msgid "compare view"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:718
 
#: kallithea/lib/helpers.py:701
 
msgid "and"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:719
 
#: kallithea/lib/helpers.py:702
 
#, python-format
 
msgid "%s more"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:720 kallithea/templates/changelog/changelog.html:44
 
#: kallithea/lib/helpers.py:703 kallithea/templates/changelog/changelog.html:44
 
msgid "revisions"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:727
 
#, python-format
 
msgid "fork name %s"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:744
 
#, python-format
 
msgid "fork name %s"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:761
 
#, python-format
 
msgid "Pull request #%s"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:771
 
#: kallithea/lib/helpers.py:754
 
msgid "[deleted] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:773 kallithea/lib/helpers.py:785
 
#: kallithea/lib/helpers.py:756 kallithea/lib/helpers.py:768
 
msgid "[created] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:775
 
#: kallithea/lib/helpers.py:758
 
msgid "[created] repository as fork"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:777 kallithea/lib/helpers.py:787
 
#: kallithea/lib/helpers.py:760 kallithea/lib/helpers.py:770
 
msgid "[forked] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:779 kallithea/lib/helpers.py:789
 
#: kallithea/lib/helpers.py:762 kallithea/lib/helpers.py:772
 
msgid "[updated] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:781
 
#: kallithea/lib/helpers.py:764
 
msgid "[downloaded] archive from repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:783
 
#: kallithea/lib/helpers.py:766
 
msgid "[delete] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:791
 
#: kallithea/lib/helpers.py:774
 
msgid "[created] user"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:793
 
#: kallithea/lib/helpers.py:776
 
msgid "[updated] user"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:795
 
#: kallithea/lib/helpers.py:778
 
msgid "[created] user group"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:797
 
#: kallithea/lib/helpers.py:780
 
msgid "[updated] user group"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:799
 
#: kallithea/lib/helpers.py:782
 
msgid "[commented] on revision in repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:801
 
#: kallithea/lib/helpers.py:784
 
msgid "[commented] on pull request for"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:803
 
#: kallithea/lib/helpers.py:786
 
msgid "[closed] pull request for"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:805
 
#: kallithea/lib/helpers.py:788
 
msgid "[pushed] into"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:807
 
#: kallithea/lib/helpers.py:790
 
msgid "[committed via Kallithea] into repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:809
 
#: kallithea/lib/helpers.py:792
 
msgid "[pulled from remote] into repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:811
 
#: kallithea/lib/helpers.py:794
 
msgid "[pulled] from"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:813
 
#: kallithea/lib/helpers.py:796
 
msgid "[started following] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:815
 
#: kallithea/lib/helpers.py:798
 
msgid "[stopped following] repository"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1144
 
#: kallithea/lib/helpers.py:1127
 
#, python-format
 
msgid " and %s more"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1148
 
#: kallithea/lib/helpers.py:1131
 
msgid "No Files"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1214
 
#: kallithea/lib/helpers.py:1197
 
msgid "new file"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1217
 
#: kallithea/lib/helpers.py:1200
 
msgid "mod"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1220
 
#: kallithea/lib/helpers.py:1203
 
msgid "del"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1223
 
#: kallithea/lib/helpers.py:1206
 
msgid "rename"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1228
 
#: kallithea/lib/helpers.py:1211
 
msgid "chmod"
 
msgstr ""
 

	
 
#: kallithea/lib/helpers.py:1460
 
#: kallithea/lib/helpers.py:1443
 
#, 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 ""
 

	
 
#: kallithea/lib/utils2.py:425
 
#, python-format
 
msgid "%d year"
 
msgid_plural "%d years"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/lib/utils2.py:426
 
#, python-format
 
msgid "%d month"
 
msgid_plural "%d months"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/lib/utils2.py:427
 
#, python-format
 
msgid "%d day"
 
msgid_plural "%d days"
 
msgstr[0] ""
 
msgstr[1] ""
 
@@ -1664,49 +1660,49 @@ msgstr ""
 
#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1540
 
#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1541
 
#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1562
 
#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1601
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1653
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1680 kallithea/model/db.py:1682
 
msgid "User Registration with automatic account activation"
 
msgstr ""
 

	
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1645
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1672 kallithea/model/db.py:1674
 
msgid "Repository creation enabled with write permission to a repository group"
 
msgstr ""
 

	
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1646
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1673 kallithea/model/db.py:1675
 
msgid "Repository creation disabled with write permission to a repository group"
 
msgstr ""
 

	
 
#: kallithea/model/comment.py:76
 
#, python-format
 
msgid "on line %s"
 
msgstr ""
 

	
 
#: kallithea/model/comment.py:231 kallithea/model/pull_request.py:164
 
#: kallithea/model/comment.py:231 kallithea/model/pull_request.py:165
 
msgid "[Mention]"
 
msgstr ""
 

	
 
#: kallithea/model/forms.py:57
 
msgid "Please enter a login"
 
msgstr ""
 

	
 
#: kallithea/model/forms.py:58
 
#, python-format
 
msgid "Enter a value %(min)i characters long or more"
 
msgstr ""
 

	
 
#: kallithea/model/forms.py:66
 
msgid "Please enter a password"
 
msgstr ""
 

	
 
#: kallithea/model/forms.py:67
 
#, python-format
 
msgid "Enter %(min)i characters or more"
 
msgstr ""
 

	
 
#: kallithea/model/forms.py:156
 
msgid "Name must not contain only digits"
 
msgstr ""
 
@@ -1744,49 +1740,49 @@ msgstr ""
 
#: kallithea/model/notification.py:296
 
#, python-format
 
msgid "Comment on %(repo_name)s changeset %(short_id)s on %(branch)s by %(comment_username)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:299
 
#, python-format
 
msgid "New user %(new_username)s registered"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:301
 
#, python-format
 
msgid "Review request on %(repo_name)s pull request #%(pr_id)s from %(ref)s by %(pr_username)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:302
 
#, python-format
 
msgid "Comment on %(repo_name)s pull request #%(pr_id)s from %(ref)s by %(comment_username)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:315
 
msgid "Closing"
 
msgstr ""
 

	
 
#: kallithea/model/pull_request.py:132
 
#: kallithea/model/pull_request.py:133
 
#, python-format
 
msgid "%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s"
 
msgstr ""
 

	
 
#: kallithea/model/scm.py:808
 
msgid "latest tip"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:194
 
msgid "New user registration"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:214 kallithea/model/user.py:236
 
msgid "You can't Edit this user since it's crucial for entire application"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:255
 
msgid "You can't remove this user since it's crucial for entire application"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:261
 
#, python-format
 
msgid "User \"%s\" still owns %s repositories and cannot be removed. Switch owners or remove those repositories: %s"
 
msgstr ""
 
@@ -1871,48 +1867,52 @@ msgstr ""
 
#: kallithea/model/validators.py:260
 
msgid "Invalid characters (non-ascii) in password"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:275
 
msgid "Invalid old password"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:291
 
msgid "Passwords do not match"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:308
 
msgid "invalid password"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:309
 
msgid "invalid user name"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:310
 
msgid "Your account is disabled"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:340
 
msgid "Token mismatch"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:354
 
#, python-format
 
msgid "Repository name %(repo)s is disallowed"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:356
 
#, python-format
 
msgid "Repository named %(repo)s already exists"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:357
 
#, python-format
 
msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:359
 
#, python-format
 
msgid "Repository group with name \"%(repo)s\" already exists"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:474
 
msgid "invalid clone URL"
 
msgstr ""
 

	
 
@@ -2038,189 +2038,189 @@ msgstr ""
 

	
 
#: kallithea/templates/index_base.html:45
 
msgid "Group Name"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:46
 
#: kallithea/templates/index_base.html:131
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:64
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:42
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:17
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:47
 
#: kallithea/templates/admin/repos/repo_add_base.html:32
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:72
 
#: kallithea/templates/admin/repos/repos.html:48
 
#: kallithea/templates/admin/user_groups/user_group_add.html:40
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:15
 
#: kallithea/templates/admin/user_groups/user_groups.html:47
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:64
 
#: kallithea/templates/email_templates/changeset_comment.html:18
 
#: kallithea/templates/email_templates/pull_request.html:12
 
#: kallithea/templates/forks/fork.html:38
 
#: kallithea/templates/pullrequests/pullrequest.html:40
 
#: kallithea/templates/pullrequests/pullrequest_show.html:38
 
#: kallithea/templates/pullrequests/pullrequest_show.html:63
 
#: kallithea/templates/summary/summary.html:84
 
#: kallithea/templates/summary/summary.html:85
 
msgid "Description"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:129
 
#: kallithea/templates/admin/my_account/my_account_repos.html:46
 
#: kallithea/templates/admin/my_account/my_account_watched.html:46
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:46
 
#: kallithea/templates/admin/repos/repo_add_base.html:9
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:7
 
#: kallithea/templates/admin/repos/repos.html:47
 
#: kallithea/templates/admin/user_groups/user_groups.html:46
 
#: kallithea/templates/base/perms_summary.html:53
 
#: kallithea/templates/bookmarks/bookmarks.html:49
 
#: kallithea/templates/bookmarks/bookmarks_data.html:7
 
#: kallithea/templates/branches/branches.html:49
 
#: kallithea/templates/branches/branches_data.html:7
 
#: kallithea/templates/files/files_browser.html:60
 
#: kallithea/templates/journal/journal.html:187
 
#: kallithea/templates/journal/journal.html:278
 
#: kallithea/templates/tags/tags.html:49
 
#: kallithea/templates/tags/tags_data.html:7
 
msgid "Name"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:132
 
msgid "Last Change"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:134
 
#: kallithea/templates/admin/my_account/my_account_repos.html:48
 
#: kallithea/templates/admin/my_account/my_account_watched.html:48
 
#: kallithea/templates/admin/repos/repos.html:49
 
#: kallithea/templates/journal/journal.html:189
 
#: kallithea/templates/journal/journal.html:280
 
msgid "Tip"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:136
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:10
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:49
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:60
 
#: kallithea/templates/admin/repos/repos.html:50
 
#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:8
 
#: kallithea/templates/admin/user_groups/user_groups.html:50
 
#: kallithea/templates/summary/summary.html:137
 
#: kallithea/templates/summary/summary.html:138
 
msgid "Owner"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:144
 
#: kallithea/templates/admin/my_account/my_account_repos.html:57
 
#: kallithea/templates/admin/my_account/my_account_watched.html:57
 
#: kallithea/templates/base/root.html:44
 
#: kallithea/templates/bookmarks/bookmarks.html:79
 
#: kallithea/templates/branches/branches.html:79
 
#: kallithea/templates/journal/journal.html:198
 
#: kallithea/templates/journal/journal.html:289
 
#: kallithea/templates/tags/tags.html:79
 
msgid "Click to sort ascending"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:145
 
#: kallithea/templates/admin/my_account/my_account_repos.html:58
 
#: kallithea/templates/admin/my_account/my_account_watched.html:58
 
#: kallithea/templates/base/root.html:45
 
#: kallithea/templates/bookmarks/bookmarks.html:80
 
#: kallithea/templates/branches/branches.html:80
 
#: kallithea/templates/journal/journal.html:199
 
#: kallithea/templates/journal/journal.html:290
 
#: kallithea/templates/tags/tags.html:80
 
msgid "Click to sort descending"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:146
 
msgid "No repositories found."
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:147
 
#: kallithea/templates/admin/my_account/my_account_repos.html:60
 
#: kallithea/templates/admin/my_account/my_account_watched.html:60
 
#: kallithea/templates/base/root.html:47
 
#: kallithea/templates/bookmarks/bookmarks.html:82
 
#: kallithea/templates/branches/branches.html:82
 
#: kallithea/templates/journal/journal.html:201
 
#: kallithea/templates/journal/journal.html:292
 
#: kallithea/templates/tags/tags.html:82
 
msgid "Data error."
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:148
 
#: kallithea/templates/admin/my_account/my_account_repos.html:61
 
#: kallithea/templates/admin/my_account/my_account_watched.html:61
 
#: kallithea/templates/base/base.html:143 kallithea/templates/base/root.html:48
 
#: kallithea/templates/base/base.html:147 kallithea/templates/base/root.html:48
 
#: kallithea/templates/bookmarks/bookmarks.html:83
 
#: kallithea/templates/branches/branches.html:83
 
#: kallithea/templates/journal/journal.html:202
 
#: kallithea/templates/journal/journal.html:293
 
#: kallithea/templates/tags/tags.html:83
 
msgid "Loading..."
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:5 kallithea/templates/login.html:15
 
#: kallithea/templates/base/base.html:329
 
#: kallithea/templates/base/base.html:333
 
msgid "Log In"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:13
 
#, python-format
 
msgid "Log In to %s"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:27 kallithea/templates/register.html:24
 
#: kallithea/templates/admin/admin_log.html:5
 
#: kallithea/templates/admin/my_account/my_account_profile.html:32
 
#: kallithea/templates/admin/users/user_add.html:32
 
#: kallithea/templates/admin/users/user_edit_profile.html:33
 
#: kallithea/templates/admin/users/users.html:50
 
#: kallithea/templates/base/base.html:305
 
#: kallithea/templates/base/base.html:309
 
msgid "Username"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:36 kallithea/templates/register.html:33
 
#: kallithea/templates/admin/my_account/my_account.html:36
 
#: kallithea/templates/admin/users/user_add.html:41
 
#: kallithea/templates/base/base.html:314
 
#: kallithea/templates/base/base.html:318
 
msgid "Password"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:46
 
msgid "Remember me"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:50
 
msgid "Sign In"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:56
 
msgid "Forgot your password ?"
 
msgstr ""
 

	
 
#: kallithea/templates/login.html:59 kallithea/templates/base/base.html:325
 
#: kallithea/templates/login.html:59 kallithea/templates/base/base.html:329
 
msgid "Don't have an account ?"
 
msgstr ""
 

	
 
#: kallithea/templates/password_reset.html:5
 
msgid "Password Reset"
 
msgstr ""
 

	
 
#: kallithea/templates/password_reset.html:12
 
#, python-format
 
msgid "Reset Your Password to %s"
 
msgstr ""
 

	
 
#: kallithea/templates/password_reset.html:14
 
msgid "Reset Your Password"
 
msgstr ""
 

	
 
#: kallithea/templates/password_reset.html:25
 
msgid "Email Address"
 
msgstr ""
 

	
 
#: kallithea/templates/password_reset.html:35
 
#: kallithea/templates/register.html:79
 
msgid "Captcha"
 
msgstr ""
 
@@ -2376,124 +2376,123 @@ msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:33
 
msgid "Comma separated list of plugins. Order of plugins is also order in which Kallithea will try to authenticate user"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:34
 
msgid "Available built-in plugins"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:40
 
#: kallithea/templates/base/root.html:40
 
msgid "enabled"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:40
 
#: kallithea/templates/base/root.html:41
 
msgid "disabled"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:51
 
msgid "Plugin"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:101
 
#: kallithea/templates/admin/defaults/defaults.html:84
 
#: kallithea/templates/admin/defaults/defaults.html:82
 
#: kallithea/templates/admin/my_account/my_account_password.html:33
 
#: kallithea/templates/admin/my_account/my_account_profile.html:70
 
#: kallithea/templates/admin/permissions/permissions_globals.html:108
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:69
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:114
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:42
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:101
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:134
 
#: kallithea/templates/admin/settings/settings_hooks.html:53
 
#: kallithea/templates/admin/user_groups/user_group_add.html:57
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:104
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:60
 
#: kallithea/templates/admin/users/user_add.html:96
 
#: kallithea/templates/admin/users/user_edit_profile.html:122
 
#: kallithea/templates/base/default_perms_box.html:64
 
msgid "Save"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:5
 
#: kallithea/templates/admin/defaults/defaults.html:25
 
msgid "Repository Defaults"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:11
 
#: kallithea/templates/base/base.html:66
 
msgid "Defaults"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:35
 
#: kallithea/templates/admin/defaults/defaults.html:33
 
#: kallithea/templates/admin/repos/repo_add_base.html:59
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:7
 
msgid "Type"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:44
 
#: kallithea/templates/admin/defaults/defaults.html:42
 
#: kallithea/templates/admin/repos/repo_add_base.html:77
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:82
 
#: kallithea/templates/data_table/_dt_elements.html:74
 
#: kallithea/templates/data_table/_dt_elements.html:72
 
msgid "Private repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:48
 
#: kallithea/templates/admin/defaults/defaults.html:46
 
#: kallithea/templates/admin/repos/repo_add_base.html:81
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:86
 
#: kallithea/templates/forks/fork.html:72
 
msgid "Private repositories are only visible to people explicitly added as collaborators."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:55
 
#: kallithea/templates/admin/defaults/defaults.html:53
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:91
 
msgid "Enable statistics"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:59
 
#: kallithea/templates/admin/defaults/defaults.html:57
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:95
 
msgid "Enable statistics window on summary page."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:65
 
#: kallithea/templates/admin/defaults/defaults.html:63
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:100
 
msgid "Enable downloads"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:69
 
#: kallithea/templates/admin/defaults/defaults.html:67
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:104
 
msgid "Enable download menu on summary page."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:75
 
#: kallithea/templates/admin/defaults/defaults.html:73
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:34
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:109
 
msgid "Enable locking"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/defaults/defaults.html:79
 
#: kallithea/templates/admin/defaults/defaults.html:77
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:113
 
msgid "Enable lock-by-pulling on repository."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:5
 
#: kallithea/templates/admin/gists/edit.html:18
 
msgid "Edit Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:36
 
#, python-format
 
msgid "Gist was update since you started editing. Copy your changes and click %(here)s to reload new version."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:55
 
#: kallithea/templates/admin/gists/new.html:39
 
msgid "Gist description ..."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:57
 
#: kallithea/templates/admin/gists/new.html:41
 
msgid "Gist lifetime"
 
msgstr ""
 

	
 
@@ -2523,169 +2522,169 @@ msgstr ""
 
#: kallithea/templates/admin/gists/edit.html:146
 
#: kallithea/templates/changeset/changeset_file_comment.html:89
 
msgid "Cancel"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/index.html:6
 
#: kallithea/templates/admin/gists/index.html:16
 
#, python-format
 
msgid "Private Gists for User %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/index.html:8
 
#: kallithea/templates/admin/gists/index.html:18
 
#, python-format
 
msgid "Public Gists for User %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/index.html:10
 
#: kallithea/templates/admin/gists/index.html:20
 
msgid "Public Gists"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/index.html:37
 
#: kallithea/templates/admin/gists/show.html:25
 
#: kallithea/templates/base/base.html:240
 
#: kallithea/templates/base/base.html:244
 
msgid "Create New Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/index.html:54
 
#: kallithea/templates/data_table/_dt_elements.html:143
 
#: kallithea/templates/data_table/_dt_elements.html:141
 
msgid "Created"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/index.html:74
 
msgid "There are no gists yet"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/new.html:5
 
#: kallithea/templates/admin/gists/new.html:18
 
msgid "New Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/new.html:47
 
msgid "name this file..."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/new.html:56
 
msgid "Create Private Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/new.html:57
 
msgid "Create Public Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/new.html:58
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:70
 
#: kallithea/templates/admin/my_account/my_account_emails.html:46
 
#: kallithea/templates/admin/my_account/my_account_password.html:34
 
#: kallithea/templates/admin/my_account/my_account_profile.html:71
 
#: kallithea/templates/admin/permissions/permissions_globals.html:109
 
#: kallithea/templates/admin/permissions/permissions_ips.html:41
 
#: kallithea/templates/admin/permissions/permissions_ips.html:39
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:115
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:43
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:59
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:102
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:135
 
#: kallithea/templates/admin/settings/settings_global.html:57
 
#: kallithea/templates/admin/settings/settings_vcs.html:81
 
#: kallithea/templates/admin/settings/settings_visual.html:117
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:105
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:70
 
#: kallithea/templates/admin/users/user_edit_emails.html:46
 
#: kallithea/templates/admin/users/user_edit_ips.html:50
 
#: kallithea/templates/admin/users/user_edit_profile.html:123
 
#: kallithea/templates/base/default_perms_box.html:65
 
#: kallithea/templates/files/files_add.html:65
 
#: kallithea/templates/files/files_delete.html:44
 
#: kallithea/templates/files/files_edit.html:68
 
#: kallithea/templates/pullrequests/pullrequest.html:89
 
msgid "Reset"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:5
 
#: kallithea/templates/admin/gists/show.html:9
 
msgid "Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:10
 
#: kallithea/templates/email_templates/changeset_comment.html:15
 
#: kallithea/templates/email_templates/pull_request.html:10
 
#: kallithea/templates/email_templates/pull_request_comment.html:15
 
msgid "URL"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:37
 
msgid "Public Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:39
 
msgid "Private Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:56
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:76
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:75
 
#: kallithea/templates/changeset/changeset_file_comment.html:50
 
#: kallithea/templates/files/files_source.html:39
 
#: kallithea/templates/files/files_source.html:42
 
#: kallithea/templates/files/files_source.html:45
 
msgid "Delete"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:56
 
msgid "Confirm to delete this Gist"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:63
 
#: kallithea/templates/changeset/changeset_file_comment.html:91
 
#: kallithea/templates/changeset/changeset_file_comment.html:207
 
#: kallithea/templates/data_table/_dt_elements.html:167
 
#: kallithea/templates/data_table/_dt_elements.html:183
 
#: kallithea/templates/data_table/_dt_elements.html:165
 
#: kallithea/templates/data_table/_dt_elements.html:181
 
#: kallithea/templates/files/diff_2way.html:56
 
#: kallithea/templates/files/files_source.html:41
 
#: kallithea/templates/files/files_source.html:44
 
#: kallithea/templates/pullrequests/pullrequest_show.html:41
 
msgid "Edit"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:65
 
#: kallithea/templates/files/files_edit.html:49
 
#: kallithea/templates/files/files_source.html:34
 
msgid "Show as Raw"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:73
 
msgid "created"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/show.html:86
 
#: kallithea/templates/files/files_source.html:73
 
msgid "Show as raw"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:5
 
#: kallithea/templates/admin/my_account/my_account.html:9
 
#: kallithea/templates/base/base.html:346
 
#: kallithea/templates/base/base.html:350
 
msgid "My Account"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:35
 
#: kallithea/templates/admin/users/user_edit.html:29
 
msgid "Profile"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:37
 
#: kallithea/templates/admin/users/user_edit.html:30
 
msgid "API Keys"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:38
 
msgid "My Emails"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:39
 
msgid "My Repositories"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:40
 
#: kallithea/templates/journal/journal.html:53
 
msgid "Watched"
 
@@ -2727,72 +2726,72 @@ msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:40
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:40
 
#, python-format
 
msgid "Confirm to remove this api key: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:42
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:42
 
msgid "remove"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:49
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:49
 
msgid "No additional api keys specified"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:61
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:61
 
msgid "New api key"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:69
 
#: kallithea/templates/admin/my_account/my_account_emails.html:45
 
#: kallithea/templates/admin/permissions/permissions_ips.html:40
 
#: kallithea/templates/admin/permissions/permissions_ips.html:38
 
#: kallithea/templates/admin/repos/repo_add_base.html:85
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:58
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:69
 
#: kallithea/templates/admin/users/user_edit_emails.html:45
 
#: kallithea/templates/admin/users/user_edit_ips.html:49
 
msgid "Add"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_emails.html:7
 
#: kallithea/templates/admin/users/user_edit_emails.html:7
 
msgid "Primary"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_emails.html:19
 
#: kallithea/templates/admin/permissions/permissions_ips.html:14
 
#: kallithea/templates/admin/permissions/permissions_ips.html:12
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:18
 
#: kallithea/templates/admin/settings/settings_hooks.html:36
 
#: kallithea/templates/admin/users/user_edit_emails.html:19
 
#: kallithea/templates/admin/users/user_edit_ips.html:22
 
#: kallithea/templates/data_table/_dt_elements.html:131
 
#: kallithea/templates/data_table/_dt_elements.html:159
 
#: kallithea/templates/data_table/_dt_elements.html:175
 
#: kallithea/templates/data_table/_dt_elements.html:191
 
#: kallithea/templates/data_table/_dt_elements.html:129
 
#: kallithea/templates/data_table/_dt_elements.html:157
 
#: kallithea/templates/data_table/_dt_elements.html:173
 
#: kallithea/templates/data_table/_dt_elements.html:189
 
msgid "delete"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_emails.html:20
 
#: kallithea/templates/admin/users/user_edit_emails.html:20
 
#, python-format
 
msgid "Confirm to delete this email: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_emails.html:26
 
#: kallithea/templates/admin/users/user_edit_emails.html:26
 
msgid "No additional emails specified."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_emails.html:38
 
#: kallithea/templates/admin/users/user_edit_emails.html:38
 
msgid "New email address"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_password.html:1
 
msgid "Change Your Account Password"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_password.html:7
 
@@ -2847,67 +2846,67 @@ msgstr ""
 
#: kallithea/templates/journal/journal.html:200
 
#: kallithea/templates/journal/journal.html:291
 
#: kallithea/templates/tags/tags.html:81
 
msgid "No records found."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_watched.html:1
 
msgid "Repositories You are Watching"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/notifications.html:5
 
#: kallithea/templates/admin/notifications/notifications.html:9
 
msgid "My Notifications"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/notifications.html:24
 
msgid "All"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/notifications.html:25
 
msgid "Comments"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/notifications.html:26
 
#: kallithea/templates/base/base.html:186
 
#: kallithea/templates/base/base.html:190
 
msgid "Pull Requests"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/notifications.html:30
 
msgid "Mark All Read"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/notifications_data.html:40
 
msgid "No notifications here yet"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/show_notification.html:5
 
#: kallithea/templates/admin/notifications/show_notification.html:11
 
msgid "Show Notification"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/notifications/show_notification.html:9
 
#: kallithea/templates/base/base.html:345
 
#: kallithea/templates/base/base.html:349
 
msgid "Notifications"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions.html:5
 
msgid "Permissions Administration"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions.html:11
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:42
 
#: kallithea/templates/admin/repos/repo_edit.html:43
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:32
 
#: kallithea/templates/base/base.html:64
 
msgid "Permissions"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions.html:28
 
#: kallithea/templates/admin/settings/settings.html:29
 
msgid "Global"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions.html:29
 
#: kallithea/templates/admin/users/user_edit.html:34
 
msgid "IP Whitelist"
 
msgstr ""
 
@@ -2917,201 +2916,193 @@ msgid "Overview"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:7
 
msgid "Anonymous access"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:13
 
#, python-format
 
msgid "Allow access to Kallithea without needing to log in. Anonymous users use %s user permissions."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:26
 
msgid "All default permissions on each repository will be reset to chosen permission, note that all custom default permission on repositories will be lost"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:27
 
#: kallithea/templates/admin/permissions/permissions_globals.html:40
 
#: kallithea/templates/admin/permissions/permissions_globals.html:54
 
msgid "Overwrite existing settings"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:32
 
#: kallithea/templates/admin/repos/repo_add_base.html:41
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:42
 
#: kallithea/templates/data_table/_dt_elements.html:204
 
#: kallithea/templates/data_table/_dt_elements.html:202
 
#: kallithea/templates/forks/fork.html:48
 
msgid "Repository group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:39
 
msgid "All default permissions on each repository group will be reset to chosen permission, note that all custom default permission on repository groups will be lost"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:46
 
#: kallithea/templates/data_table/_dt_elements.html:211
 
#: kallithea/templates/data_table/_dt_elements.html:209
 
msgid "User group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:53
 
msgid "All default permissions on each user group will be reset to chosen permission, note that all custom default permission on repository groups will be lost"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:60
 
msgid "Repository creation"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:68
 
msgid "Repository creation with group write access"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:72
 
msgid "Write permission to a repository group allows creating repositories inside that group."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:77
 
msgid "User group creation"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:85
 
msgid "Repository forking"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:93
 
msgid "Registration"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_globals.html:101
 
msgid "External auth account activation"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_ips.html:1
 
msgid "Default IP Whitelist for All Users"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_ips.html:15
 
#: kallithea/templates/admin/permissions/permissions_ips.html:13
 
#: kallithea/templates/admin/users/user_edit_ips.html:23
 
#, python-format
 
msgid "Confirm to delete this ip: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_ips.html:21
 
#: kallithea/templates/admin/permissions/permissions_ips.html:19
 
#: kallithea/templates/admin/users/user_edit_ips.html:30
 
msgid "All IP addresses are allowed."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_ips.html:32
 
#: kallithea/templates/admin/permissions/permissions_ips.html:30
 
#: kallithea/templates/admin/users/user_edit_ips.html:42
 
msgid "New IP address"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/permissions/permissions_perms.html:1
 
msgid "Default User Permissions Overview"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:11
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:11
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:105
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:10
 
#: kallithea/templates/base/base.html:61 kallithea/templates/base/base.html:80
 
msgid "Repository Groups"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:33
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:8
 
#: kallithea/templates/admin/user_groups/user_group_add.html:32
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:7
 
msgid "Group name"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:51
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:26
 
msgid "Group parent"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:60
 
#: kallithea/templates/admin/repos/repo_add_base.html:50
 
msgid "Copy parent group permissions"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:64
 
#: kallithea/templates/admin/repos/repo_add_base.html:54
 
msgid "Copy permission set from parent repository group."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:5
 
#, python-format
 
msgid "%s Repository Group Settings"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:21
 
msgid "Add Child Group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:40
 
#: kallithea/templates/admin/repos/repo_edit.html:12
 
#: kallithea/templates/admin/repos/repo_edit.html:40
 
#: kallithea/templates/admin/settings/settings.html:11
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:29
 
#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:154
 
#: kallithea/templates/data_table/_dt_elements.html:43
 
#: kallithea/templates/data_table/_dt_elements.html:47
 
#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:158
 
#: kallithea/templates/data_table/_dt_elements.html:45
 
#: kallithea/templates/data_table/_dt_elements.html:49
 
msgid "Settings"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit.html:41
 
#: kallithea/templates/admin/repos/repo_edit.html:46
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:30
 
#: kallithea/templates/admin/users/user_edit.html:31
 
msgid "Advanced"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:1
 
#, python-format
 
msgid "Repository Group: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:6
 
msgid "Top level repositories"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:7
 
msgid "Total repositories"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:8
 
msgid "Children groups"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:9
 
#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:7
 
#: kallithea/templates/admin/users/user_edit_advanced.html:8
 
#: kallithea/templates/pullrequests/pullrequest_show.html:147
 
msgid "Created on"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:21
 
#: kallithea/templates/data_table/_dt_elements.html:192
 
#: kallithea/templates/data_table/_dt_elements.html:190
 
#, python-format
 
msgid "Confirm to delete this group: %s with %s repository"
 
msgid_plural "Confirm to delete this group: %s with %s repositories"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:25
 
msgid "Delete this repository group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:7
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:8
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:7
 
#: kallithea/templates/base/perms_summary.html:14
 
msgid "none"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:8
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:9
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:8
 
#: kallithea/templates/base/perms_summary.html:15
 
msgid "read"
 
msgstr ""
 

	
 
@@ -3264,137 +3255,137 @@ msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_creating.html:39
 
msgid "We're sorry but error occurred during this operation. Please check your Kallithea server logs, or contact administrator."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit.html:8
 
#, python-format
 
msgid "%s Repository Settings"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit.html:49
 
msgid "Extra Fields"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit.html:52
 
msgid "Caches"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit.html:55
 
msgid "Remote"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit.html:58
 
#: kallithea/templates/summary/statistics.html:8
 
#: kallithea/templates/summary/summary.html:174
 
#: kallithea/templates/summary/summary.html:175
 
#: kallithea/templates/summary/summary.html:176
 
msgid "Statistics"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:1
 
msgid "Parent"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:5
 
#: kallithea/templates/admin/repos/repo_edit_fork.html:5
 
msgid "Set"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:8
 
#: kallithea/templates/admin/repos/repo_edit_fork.html:9
 
msgid "Manually set this repository as a fork of another from the list."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:22
 
msgid "Public Journal Visibility"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:30
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:29
 
msgid "Remove from public journal"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:35
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:34
 
msgid "Add to Public Journal"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:41
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:40
 
msgid "All actions done in this repository will be visible to everyone in the public journal."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:47
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:46
 
msgid "Change Locking"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:53
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:52
 
msgid "Confirm to unlock repository."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:55
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:54
 
msgid "Unlock Repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:61
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:60
 
msgid "Confirm to lock repository."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:63
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:62
 
msgid "Lock Repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:65
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:64
 
msgid "Repository is not locked"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:69
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:68
 
msgid "Force locking on the repository. Works only when anonymous access is disabled. Triggering a pull locks the repository.  The user who is pulling locks the repository; only the user who pulled and locked it can unlock it by doing a push."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:80
 
#: kallithea/templates/data_table/_dt_elements.html:132
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:79
 
#: kallithea/templates/data_table/_dt_elements.html:130
 
#, python-format
 
msgid "Confirm to delete this repository: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:82
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:81
 
msgid "Delete this Repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:85
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:84
 
#, python-format
 
msgid "This repository has %s fork"
 
msgid_plural "This repository has %s forks"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:86
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:85
 
msgid "Detach forks"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:87
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:86
 
msgid "Delete forks"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:91
 
#: kallithea/templates/admin/repos/repo_edit_advanced.html:90
 
msgid "The deleted repository will be moved away and hidden until the administrator expires it. The administrator can both permanently delete it or restore it."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:4
 
msgid "Invalidate Repository Cache"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:4
 
msgid "Confirm to invalidate repository cache."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:7
 
msgid "Manually invalidate cache for this repository. On first access, the repository will be cached again."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:12
 
msgid "List of Cached Values"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:15
 
msgid "Prefix"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:16
 
@@ -3445,81 +3436,81 @@ msgstr ""
 
msgid "Extra fields are disabled."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:21
 
msgid "private repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_remote.html:3
 
msgid "Remote URL"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_remote.html:8
 
msgid "Pull Changes from Remote Location"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_remote.html:8
 
msgid "Confirm to pull changes from remote side."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_remote.html:14
 
msgid "This repository does not have a remote URL set."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:11
 
msgid "Non-changeable id"
 
msgid "Permanent Repository ID"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:11
 
msgid "What is that?"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:13
 
msgid "URL by id"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:14
 
msgid ""
 
"In case this repository is renamed or moved into another group the repository URL changes.\n"
 
"                               Using the above URL guarantees that this repository will always be accessible under such URL.\n"
 
"                               Useful for CI systems, or any other cases that you need to hardcode the URL into 3rd party service."
 
"                               Using the above permanent URL guarantees that this repository always will be accessible on that URL.\n"
 
"                               This is useful for CI systems, or any other cases that you need to hardcode the URL into a 3rd party service."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:21
 
#: kallithea/templates/summary/summary.html:72
 
msgid "Clone URL"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:27
 
#: kallithea/templates/base/perms_summary.html:43
 
#: kallithea/templates/base/perms_summary.html:79
 
#: kallithea/templates/base/perms_summary.html:81
 
#: kallithea/templates/data_table/_dt_elements.html:124
 
#: kallithea/templates/data_table/_dt_elements.html:125
 
#: kallithea/templates/data_table/_dt_elements.html:152
 
#: kallithea/templates/data_table/_dt_elements.html:153
 
#: kallithea/templates/data_table/_dt_elements.html:169
 
#: kallithea/templates/data_table/_dt_elements.html:185
 
#: kallithea/templates/data_table/_dt_elements.html:122
 
#: kallithea/templates/data_table/_dt_elements.html:123
 
#: kallithea/templates/data_table/_dt_elements.html:150
 
#: kallithea/templates/data_table/_dt_elements.html:151
 
#: kallithea/templates/data_table/_dt_elements.html:167
 
#: kallithea/templates/data_table/_dt_elements.html:183
 
msgid "edit"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:30
 
msgid "new value"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:37
 
msgid "URL used for doing remote pulls."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:55
 
msgid "Default revision for files page, downloads, whoosh and readme"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:65
 
msgid "Change owner of this repository."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_statistics.html:6
 
msgid "Processed commits"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_statistics.html:7
 
@@ -3936,49 +3927,49 @@ msgid "Short, optional description for t
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:5
 
#, python-format
 
msgid "%s user group settings"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:31
 
msgid "Default permissions"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:33
 
#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:6
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:32
 
#: kallithea/templates/admin/user_groups/user_groups.html:48
 
msgid "Members"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:1
 
#, python-format
 
msgid "User Group: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:19
 
#: kallithea/templates/data_table/_dt_elements.html:176
 
#: kallithea/templates/data_table/_dt_elements.html:174
 
#, python-format
 
msgid "Confirm to delete this user group: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:21
 
msgid "Delete this user group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit_members.html:17
 
msgid "No members yet"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:40
 
msgid "Chosen group members"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:49
 
msgid "Available members"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_groups.html:5
 
msgid "User Groups Administration"
 
msgstr ""
 

	
 
@@ -4018,49 +4009,49 @@ msgstr ""
 
#: kallithea/templates/admin/users/user_edit.html:33
 
msgid "Emails"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_advanced.html:1
 
#, python-format
 
msgid "User: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_advanced.html:7
 
#: kallithea/templates/admin/users/user_edit_profile.html:51
 
msgid "Source of Record"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_advanced.html:9
 
#: kallithea/templates/admin/users/users.html:53
 
msgid "Last Login"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_advanced.html:10
 
msgid "Member of User groups"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_advanced.html:21
 
#: kallithea/templates/data_table/_dt_elements.html:160
 
#: kallithea/templates/data_table/_dt_elements.html:158
 
#, python-format
 
msgid "Confirm to delete this user: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_advanced.html:23
 
msgid "Delete this user"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_ips.html:8
 
#, python-format
 
msgid "Inherited from %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_profile.html:8
 
msgid "Change avatar at"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_profile.html:12
 
msgid "Missing email, please update this user email address."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_profile.html:27
 
#, python-format
 
msgid "This user is in an external Source of Record (%s); some details cannot be managed here."
 
@@ -4070,209 +4061,217 @@ msgstr ""
 
msgid "Name in Source of Record"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_profile.html:78
 
msgid "New password confirmation"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/users.html:5
 
msgid "Users Administration"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/users.html:56
 
msgid "Auth Type"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:18
 
#, python-format
 
msgid "Server instance: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:30
 
msgid "Support"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:122
 
#: kallithea/templates/base/base.html:90
 
msgid "Mercurial repository"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:93
 
msgid "Git repository"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:126
 
msgid "Create Fork"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:133
 
#: kallithea/templates/data_table/_dt_elements.html:11
 
#: kallithea/templates/data_table/_dt_elements.html:15
 
#: kallithea/templates/base/base.html:137
 
#: kallithea/templates/data_table/_dt_elements.html:13
 
#: kallithea/templates/data_table/_dt_elements.html:17
 
#: kallithea/templates/summary/summary.html:8
 
msgid "Summary"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:135
 
#: kallithea/templates/base/base.html:137
 
#: kallithea/templates/changelog/changelog.html:14
 
#: kallithea/templates/data_table/_dt_elements.html:19
 
#: kallithea/templates/data_table/_dt_elements.html:23
 
msgid "Changelog"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:139
 
#: kallithea/templates/data_table/_dt_elements.html:27
 
#: kallithea/templates/data_table/_dt_elements.html:31
 
#: kallithea/templates/base/base.html:141
 
#: kallithea/templates/changelog/changelog.html:14
 
#: kallithea/templates/data_table/_dt_elements.html:21
 
#: kallithea/templates/data_table/_dt_elements.html:25
 
msgid "Changelog"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:143
 
#: kallithea/templates/data_table/_dt_elements.html:29
 
#: kallithea/templates/data_table/_dt_elements.html:33
 
#: kallithea/templates/files/files.html:11
 
msgid "Files"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:141
 
#: kallithea/templates/base/base.html:145
 
msgid "Switch To"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:148
 
#: kallithea/templates/base/base.html:150
 
#: kallithea/templates/base/base.html:152
 
#: kallithea/templates/base/base.html:154
 
msgid "Options"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:158
 
#: kallithea/templates/base/base.html:162
 
#: kallithea/templates/forks/forks_data.html:21
 
msgid "Compare Fork"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:160
 
#: kallithea/templates/base/base.html:164
 
#: kallithea/templates/bookmarks/bookmarks.html:56
 
#: kallithea/templates/bookmarks/bookmarks_data.html:13
 
#: kallithea/templates/branches/branches.html:56
 
#: kallithea/templates/branches/branches_data.html:13
 
#: kallithea/templates/tags/tags.html:56
 
#: kallithea/templates/tags/tags_data.html:13
 
msgid "Compare"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:162
 
#: kallithea/templates/base/base.html:250
 
#: kallithea/templates/base/base.html:166
 
#: kallithea/templates/base/base.html:254
 
#: kallithea/templates/search/search.html:14
 
#: kallithea/templates/search/search.html:54
 
msgid "Search"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:166
 
#: kallithea/templates/base/base.html:170
 
msgid "Unlock"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:168
 
#: kallithea/templates/base/base.html:172
 
msgid "Lock"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:176
 
msgid "Follow"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:177
 
msgid "Unfollow"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:180
 
#: kallithea/templates/data_table/_dt_elements.html:35
 
#: kallithea/templates/data_table/_dt_elements.html:39
 
#: kallithea/templates/forks/fork.html:9
 
msgid "Fork"
 
msgid "Follow"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:181
 
msgid "Unfollow"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:184
 
#: kallithea/templates/data_table/_dt_elements.html:37
 
#: kallithea/templates/data_table/_dt_elements.html:41
 
#: kallithea/templates/forks/fork.html:9
 
msgid "Fork"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:185
 
#: kallithea/templates/pullrequests/pullrequest.html:88
 
msgid "Create Pull Request"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:186
 
#: kallithea/templates/base/base.html:190
 
#, python-format
 
msgid "Show Pull Requests for %s"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:224
 
#: kallithea/templates/base/base.html:228
 
msgid "Show recent activity"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:225
 
#: kallithea/templates/base/base.html:229
 
#: kallithea/templates/journal/journal.html:4
 
#: kallithea/templates/journal/journal.html:12
 
msgid "Journal"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:230
 
#: kallithea/templates/base/base.html:231
 
#: kallithea/templates/base/base.html:234
 
#: kallithea/templates/base/base.html:235
 
msgid "Public journal"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:236
 
#: kallithea/templates/base/base.html:240
 
msgid "Show public gists"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:237
 
msgid "Gists"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:241
 
msgid "Gists"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:245
 
msgid "All Public Gists"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:243
 
#: kallithea/templates/base/base.html:247
 
msgid "My Public Gists"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:244
 
#: kallithea/templates/base/base.html:248
 
msgid "My Private Gists"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:249
 
#: kallithea/templates/base/base.html:253
 
msgid "Search in repositories"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:272
 
#: kallithea/templates/base/base.html:273
 
#: kallithea/templates/base/base.html:276
 
#: kallithea/templates/base/base.html:277
 
#: kallithea/templates/pullrequests/pullrequest_show_my.html:4
 
#: kallithea/templates/pullrequests/pullrequest_show_my.html:8
 
msgid "My Pull Requests"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:292
 
#: kallithea/templates/base/base.html:296
 
msgid "Not Logged In"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:299
 
#: kallithea/templates/base/base.html:303
 
msgid "Login to Your Account"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:322
 
#: kallithea/templates/base/base.html:326
 
msgid "Forgot password ?"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:347
 
#: kallithea/templates/base/base.html:351
 
msgid "Log Out"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:395
 
#: kallithea/templates/base/base.html:399
 
msgid "No matches found"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:524
 
#: kallithea/templates/base/base.html:528
 
msgid "Keyboard shortcuts"
 
msgstr ""
 

	
 
#: kallithea/templates/base/base.html:533
 
#: kallithea/templates/base/base.html:537
 
msgid "Site-wide shortcuts"
 
msgstr ""
 

	
 
#: kallithea/templates/base/default_perms_box.html:14
 
msgid "Inherit from defaults"
 
msgstr ""
 

	
 
#: kallithea/templates/base/default_perms_box.html:19
 
#, python-format
 
msgid "Select to inherit permissions from %s permissions settings, and default IP address whitelist."
 
msgstr ""
 

	
 
#: kallithea/templates/base/default_perms_box.html:28
 
msgid "Create repositories"
 
msgstr ""
 

	
 
#: kallithea/templates/base/default_perms_box.html:33
 
msgid "Select this option to allow repository creation for this user"
 
msgstr ""
 

	
 
#: kallithea/templates/base/default_perms_box.html:40
 
msgid "Create user groups"
 
msgstr ""
 

	
 
@@ -4294,49 +4293,49 @@ msgstr ""
 

	
 
#: kallithea/templates/base/perms_summary.html:22
 
msgid "No permissions defined yet"
 
msgstr ""
 

	
 
#: kallithea/templates/base/perms_summary.html:30
 
#: kallithea/templates/base/perms_summary.html:54
 
msgid "Permission"
 
msgstr ""
 

	
 
#: kallithea/templates/base/perms_summary.html:32
 
#: kallithea/templates/base/perms_summary.html:56
 
msgid "Edit Permission"
 
msgstr ""
 

	
 
#: kallithea/templates/base/perms_summary.html:90
 
msgid "No permission defined"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:22
 
msgid "Add Another Comment"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:23
 
#: kallithea/templates/data_table/_dt_elements.html:216
 
#: kallithea/templates/data_table/_dt_elements.html:214
 
msgid "Stop following this repository"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:24
 
msgid "Start following this repository"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:25
 
msgid "Group"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:26
 
msgid "members"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:27
 
msgid "Loading ..."
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:28
 
msgid "loading ..."
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:29
 
@@ -4543,63 +4542,63 @@ msgstr ""
 
#: kallithea/templates/changelog/changelog_details.html:10
 
#: kallithea/templates/changeset/changeset.html:81
 
#: kallithea/templates/changeset/changeset.html:82
 
#: kallithea/templates/changeset/changeset.html:83
 
#, python-format
 
msgid "Affected %s files"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:8
 
#: kallithea/templates/files/files_add.html:60
 
#: kallithea/templates/files/files_delete.html:39
 
#: kallithea/templates/files/files_edit.html:63
 
msgid "Commit Message"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:9
 
#: kallithea/templates/pullrequests/pullrequest_data.html:17
 
msgid "Age"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:11
 
msgid "Refs"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:91
 
#: kallithea/templates/changelog/changelog_summary_data.html:81
 
msgid "Add or upload files directly via Kallithea"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:94
 
#: kallithea/templates/changelog/changelog_summary_data.html:84
 
#: kallithea/templates/files/files_add.html:21
 
#: kallithea/templates/files/files_ypjax.html:9
 
msgid "Add New File"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:100
 
#: kallithea/templates/changelog/changelog_summary_data.html:90
 
msgid "Push new repo"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_summary_data.html:108
 
#: kallithea/templates/changelog/changelog_summary_data.html:98
 
msgid "Existing repository?"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:8
 
#, python-format
 
msgid "%s Changeset"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:36
 
msgid "parent rev."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:42
 
msgid "child rev."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:50
 
#: kallithea/templates/changeset/changeset_file_comment.html:43
 
#: kallithea/templates/changeset/changeset_range.html:48
 
msgid "Changeset status"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:54
 
#: kallithea/templates/changeset/diff_block.html:27
 
@@ -4675,49 +4674,48 @@ msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:32
 
msgid "Status change on changeset"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:34
 
msgid "Comment on changeset"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:50
 
msgid "Delete comment?"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:67
 
msgid "Commenting on line {1}."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:68
 
#: kallithea/templates/changeset/changeset_file_comment.html:163
 
#, python-format
 
msgid "Comments parsed using %s syntax with %s support."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:70
 
#: kallithea/templates/changeset/changeset_file_comment.html:165
 
msgid "Use @username inside this text to notify another user"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:80
 
#: kallithea/templates/changeset/changeset_file_comment.html:199
 
msgid "Comment preview"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:85
 
msgid "Submitting ..."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:88
 
#: kallithea/templates/changeset/changeset_file_comment.html:205
 
msgid "Comment"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:90
 
#: kallithea/templates/changeset/changeset_file_comment.html:206
 
msgid "Preview"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:98
 
msgid "You need to be logged in to comment."
 
@@ -4731,48 +4729,52 @@ msgstr ""
 
msgid "Hide"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:114
 
#, python-format
 
msgid "%d comment"
 
msgid_plural "%d comments"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:115
 
#, python-format
 
msgid "%d inline"
 
msgid_plural "%d inline"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:116
 
#, python-format
 
msgid "%d general"
 
msgid_plural "%d general"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:165
 
msgid "Use @username inside this text to notify another user."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:172
 
msgid "Vote for pull request status"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:174
 
msgid "Set changeset status"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:178
 
msgid "No change"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:191
 
msgid "Close"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_range.html:5
 
#, python-format
 
msgid "%s Changesets"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_range.html:56
 
msgid "Files affected"
 
msgstr ""
 
@@ -4850,81 +4852,73 @@ msgid "Swap"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:42
 
msgid "Compare revisions, branches, bookmarks, or tags."
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:47
 
#: kallithea/templates/pullrequests/pullrequest_show.html:294
 
#, python-format
 
msgid "Showing %s commit"
 
msgid_plural "Showing %s commits"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:65
 
#: kallithea/templates/pullrequests/pullrequest_show.html:315
 
msgid "No files"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:78
 
#: kallithea/templates/compare/compare_diff.html:89
 
msgid "Show full diff"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:67
 
msgid "Mercurial repository"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:69
 
msgid "Git repository"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:76
 
#: kallithea/templates/data_table/_dt_elements.html:74
 
msgid "Public repository"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:86
 
#: kallithea/templates/data_table/_dt_elements.html:84
 
msgid "Repository creation in progress..."
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:100
 
#: kallithea/templates/data_table/_dt_elements.html:98
 
msgid "No changesets yet"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:105
 
#: kallithea/templates/data_table/_dt_elements.html:107
 
#: kallithea/templates/data_table/_dt_elements.html:109
 
#, python-format
 
msgid "Subscribe to %s rss feed"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:113
 
#: kallithea/templates/data_table/_dt_elements.html:115
 
#: kallithea/templates/data_table/_dt_elements.html:117
 
#, python-format
 
msgid "Subscribe to %s atom feed"
 
msgstr ""
 

	
 
#: kallithea/templates/data_table/_dt_elements.html:141
 
#: kallithea/templates/data_table/_dt_elements.html:139
 
msgid "Creating"
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/changeset_comment.html:5
 
#, python-format
 
msgid "Comment from %s on %s changeset %s mentioned you"
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/changeset_comment.html:7
 
#, python-format
 
msgid "Comment from %s on %s changeset %s"
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/changeset_comment.html:12
 
msgid "The changeset status was changed to"
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/main.html:6
 
msgid "This is an automatic notification. Don't reply to this mail."
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/password_reset.html:4
 
#, python-format
 
msgid "Hello %s"
 
@@ -5159,50 +5153,50 @@ msgid "Binary file (%s)"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_source.html:73
 
msgid "File is too big to display"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_ypjax.html:5
 
msgid "annotation"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_ypjax.html:23
 
msgid "Go Back"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_ypjax.html:24
 
msgid "No files at given path"
 
msgstr ""
 

	
 
#: kallithea/templates/followers/followers.html:5
 
#, python-format
 
msgid "%s Followers"
 
msgstr ""
 

	
 
#: kallithea/templates/followers/followers.html:9
 
#: kallithea/templates/summary/summary.html:145
 
#: kallithea/templates/summary/summary.html:146
 
#: kallithea/templates/summary/summary.html:147
 
msgid "Followers"
 
msgstr ""
 

	
 
#: kallithea/templates/followers/followers_data.html:12
 
msgid "Started following -"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:5
 
#, python-format
 
msgid "Fork repository %s"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:27
 
msgid "Fork name"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:62
 
msgid "Default revision for files page, downloads, whoosh, and readme."
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:68
 
msgid "Private"
 
msgstr ""
 

	
 
@@ -5211,74 +5205,74 @@ msgid "Copy permissions"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:81
 
msgid "Copy permissions from forked repository"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:87
 
msgid "Update after clone"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:91
 
msgid "Checkout source after making a clone"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/fork.html:96
 
msgid "Fork this Repository"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/forks.html:5
 
#, python-format
 
msgid "%s Forks"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/forks.html:9
 
#: kallithea/templates/summary/summary.html:151
 
#: kallithea/templates/summary/summary.html:152
 
#: kallithea/templates/summary/summary.html:153
 
msgid "Forks"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/forks_data.html:17
 
msgid "Forked"
 
msgstr ""
 

	
 
#: kallithea/templates/forks/forks_data.html:43
 
#: kallithea/templates/forks/forks_data.html:30
 
msgid "There are no forks yet"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/journal.html:21
 
msgid "ATOM journal feed"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/journal.html:22
 
msgid "RSS journal feed"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/journal.html:56
 
msgid "My Repos"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/journal_data.html:61
 
#: kallithea/templates/journal/journal_data.html:43
 
msgid "No entries yet"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/public_journal.html:4
 
#: kallithea/templates/journal/public_journal.html:21
 
msgid "Public Journal"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/public_journal.html:13
 
msgid "ATOM public journal feed"
 
msgstr ""
 

	
 
#: kallithea/templates/journal/public_journal.html:14
 
msgid "RSS public journal feed"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:4
 
#: kallithea/templates/pullrequests/pullrequest.html:8
 
msgid "New Pull Request"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:31
 
#: kallithea/templates/pullrequests/pullrequest_data.html:15
 
#: kallithea/templates/pullrequests/pullrequest_show.html:29
 
@@ -5547,165 +5541,165 @@ msgstr ""
 
#: kallithea/templates/search/search_commit.html:35
 
#: kallithea/templates/search/search_content.html:21
 
#: kallithea/templates/search/search_path.html:15
 
msgid "Permission denied"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:4
 
#, python-format
 
msgid "%s Statistics"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:16
 
#: kallithea/templates/summary/summary.html:39
 
#, python-format
 
msgid "%s ATOM feed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:17
 
#: kallithea/templates/summary/summary.html:40
 
#, python-format
 
msgid "%s RSS feed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:36
 
#: kallithea/templates/summary/summary.html:103
 
#: kallithea/templates/summary/summary.html:119
 
#: kallithea/templates/summary/summary.html:104
 
#: kallithea/templates/summary/summary.html:120
 
msgid "Enable"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:39
 
msgid "Stats gathered: "
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:88
 
#: kallithea/templates/summary/summary.html:352
 
#: kallithea/templates/summary/statistics.html:89
 
#: kallithea/templates/summary/summary.html:353
 
msgid "files"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:112
 
#: kallithea/templates/summary/summary.html:376
 
#: kallithea/templates/summary/statistics.html:113
 
#: kallithea/templates/summary/summary.html:377
 
msgid "Show more"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:389
 
msgid "commits"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:390
 
msgid "files added"
 
msgid "commits"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:391
 
msgid "files changed"
 
msgid "files added"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:392
 
msgid "files changed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:393
 
msgid "files removed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:394
 
msgid "commit"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:395
 
msgid "file added"
 
msgid "commit"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:396
 
msgid "file changed"
 
msgid "file added"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:397
 
msgid "file changed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/statistics.html:398
 
msgid "file removed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:4
 
#, python-format
 
msgid "%s Summary"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:13
 
#, python-format
 
msgid "Repository locked by %s"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:15
 
msgid "Repository unlocked"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:22
 
msgid "Fork of"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:77
 
#: kallithea/templates/summary/summary.html:78
 
msgid "Show by Name"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:78
 
#: kallithea/templates/summary/summary.html:79
 
msgid "Show by ID"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:95
 
#: kallithea/templates/summary/summary.html:96
 
msgid "Trending files"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:111
 
#: kallithea/templates/summary/summary.html:112
 
msgid "Download"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:115
 
#: kallithea/templates/summary/summary.html:116
 
msgid "There are no downloads yet"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:117
 
#: kallithea/templates/summary/summary.html:118
 
msgid "Downloads are disabled for this repository"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:123
 
#: kallithea/templates/summary/summary.html:124
 
msgid "Download as zip"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:128
 
#: kallithea/templates/summary/summary.html:129
 
msgid "Check this to download archive with subrepos"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:128
 
#: kallithea/templates/summary/summary.html:129
 
msgid "with subrepos"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:159
 
#: kallithea/templates/summary/summary.html:160
 
msgid "Repository Size"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:166
 
#: kallithea/templates/summary/summary.html:168
 
#: kallithea/templates/summary/summary.html:167
 
#: kallithea/templates/summary/summary.html:169
 
msgid "Feed"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:189
 
#: kallithea/templates/summary/summary.html:190
 
msgid "Latest Changes"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:191
 
#: kallithea/templates/summary/summary.html:192
 
msgid "Quick Start"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:205
 
#: kallithea/templates/summary/summary.html:206
 
#, python-format
 
msgid "Readme file from revision %s:%s"
 
msgstr ""
 

	
 
#: kallithea/templates/summary/summary.html:296
 
#: kallithea/templates/summary/summary.html:297
 
#, python-format
 
msgid "Download %s as %s"
 
msgstr ""
 

	
 
#: kallithea/templates/tags/tags.html:5
 
#, python-format
 
msgid "%s Tags"
 
msgstr ""
 

	
 
#: kallithea/templates/tags/tags.html:26
 
msgid "Compare Tags"
 
msgstr ""
 

	
kallithea/lib/auth.py
Show inline comments
 
@@ -5,49 +5,49 @@
 
# (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/>.
 
"""
 
kallithea.lib.auth
 
~~~~~~~~~~~~~~~~~~
 

	
 
authentication and permission libraries
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Apr 4, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 
from __future__ import with_statement
 
import time
 
import random
 
import os
 
import logging
 
import traceback
 
import hashlib
 
import itertools
 
import collections
 

	
 
from tempfile import _RandomNameSequence
 
from decorator import decorator
 

	
 
from pylons import url, request
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 
from webhelpers.pylonslib import secure_form
 
from sqlalchemy import or_
 
from sqlalchemy.orm.exc import ObjectDeletedError
 
from sqlalchemy.orm import joinedload
 

	
 
from kallithea import __platform__, is_windows, is_unix
 
from kallithea.lib.vcs.utils.lazy import LazyProperty
 
from kallithea.model import meta
 
from kallithea.model.meta import Session
 
from kallithea.model.user import UserModel
 
from kallithea.model.db import User, Repository, Permission, \
 
    UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
 
@@ -64,56 +64,56 @@ log = logging.getLogger(__name__)
 

	
 

	
 
class PasswordGenerator(object):
 
    """
 
    This is a simple class for generating password from different sets of
 
    characters
 
    usage::
 

	
 
        passwd_gen = PasswordGenerator()
 
        #print 8-letter password containing only big and small letters
 
            of alphabet
 
        passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
 
    """
 
    ALPHABETS_NUM = r'''1234567890'''
 
    ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
 
    ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
 
    ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
 
    ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
 
        + ALPHABETS_NUM + ALPHABETS_SPECIAL
 
    ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
 
    ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
 
    ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
 
    ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
 

	
 
    def __init__(self, passwd=''):
 
        self.passwd = passwd
 

	
 
    def gen_password(self, length, type_=None):
 
        if type_ is None:
 
            type_ = self.ALPHABETS_FULL
 
        self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
 
        return self.passwd
 
    def gen_password(self, length, alphabet=ALPHABETS_FULL):
 
        assert len(alphabet) <= 256, alphabet
 
        l = []
 
        while len(l) < length:
 
            i = ord(os.urandom(1))
 
            if i < len(alphabet):
 
                l.append(alphabet[i])
 
        return ''.join(l)
 

	
 

	
 
class KallitheaCrypto(object):
 

	
 
    @classmethod
 
    def hash_string(cls, str_):
 
        """
 
        Cryptographic function used for password hashing based on pybcrypt
 
        or pycrypto in windows
 

	
 
        :param password: password to hash
 
        """
 
        if is_windows:
 
            from hashlib import sha256
 
            return sha256(str_).hexdigest()
 
        elif is_unix:
 
            import bcrypt
 
            return bcrypt.hashpw(str_, bcrypt.gensalt(10))
 
        else:
 
            raise Exception('Unknown or unsupported platform %s' \
 
                            % __platform__)
 

	
 
    @classmethod
 
    def hash_check(cls, password, hashed):
 
@@ -122,48 +122,49 @@ class KallitheaCrypto(object):
 
        implementation based on platform it runs on
 

	
 
        :param password: password
 
        :param hashed: password in hashed form
 
        """
 

	
 
        if is_windows:
 
            from hashlib import sha256
 
            return sha256(password).hexdigest() == hashed
 
        elif is_unix:
 
            import bcrypt
 
            return bcrypt.hashpw(password, hashed) == hashed
 
        else:
 
            raise Exception('Unknown or unsupported platform %s' \
 
                            % __platform__)
 

	
 

	
 
def get_crypt_password(password):
 
    return KallitheaCrypto.hash_string(password)
 

	
 

	
 
def check_password(password, hashed):
 
    return KallitheaCrypto.hash_check(password, hashed)
 

	
 

	
 
class CookieStoreWrapper(object):
 

	
 
    def __init__(self, cookie_store):
 
        self.cookie_store = cookie_store
 

	
 
    def __repr__(self):
 
        return 'CookieStore<%s>' % (self.cookie_store)
 

	
 
    def get(self, key, other=None):
 
        if isinstance(self.cookie_store, dict):
 
            return self.cookie_store.get(key, other)
 
        elif isinstance(self.cookie_store, AuthUser):
 
            return self.cookie_store.__dict__.get(key, other)
 

	
 

	
 

	
 
def _cached_perms_data(user_id, user_is_admin, user_inherit_default_permissions,
 
                       explicit, algo):
 
    RK = 'repositories'
 
    GK = 'repositories_groups'
 
    UK = 'user_groups'
 
    GLOBAL = 'global'
 
    PERM_WEIGHTS = Permission.PERM_WEIGHTS
 
    permissions = {RK: {}, GK: {}, UK: {}, GLOBAL: set()}
 
@@ -254,48 +255,51 @@ def _cached_perms_data(user_id, user_is_
 
    # defaults for user groups taken from default user permission
 
    # on given user group
 
    for perm in default_user_group_perms:
 
        u_k = perm.UserUserGroupToPerm.user_group.users_group_name
 
        p = perm.Permission.permission_name
 
        permissions[UK][u_k] = p
 

	
 
    #======================================================================
 
    # !! 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',
 
        'hg.usergroup.create.false', 'hg.usergroup.create.true'
 
    ])
 

	
 
    # USER GROUPS comes first
 
    # user group global permissions
 
    user_perms_from_users_groups = Session().query(UserGroupToPerm)\
 
        .options(joinedload(UserGroupToPerm.permission))\
 
        .join((UserGroupMember, UserGroupToPerm.users_group_id ==
 
               UserGroupMember.users_group_id))\
 
        .filter(UserGroupMember.user_id == uid)\
 
        .join((UserGroup, UserGroupMember.users_group_id ==
 
               UserGroup.users_group_id))\
 
        .filter(UserGroup.users_group_active == True)\
 
        .order_by(UserGroupToPerm.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
 
            permissions[GLOBAL] = permissions[GLOBAL]\
 
                                            .difference(_configurable)
 
        for perm in perms:
 
            permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
    # user specific global permissions
 
    user_perms = Session().query(UserToPerm)\
 
            .options(joinedload(UserToPerm.permission))\
 
            .filter(UserToPerm.user_id == uid).all()
 

	
 
@@ -304,48 +308,51 @@ def _cached_perms_data(user_id, user_is_
 
        # replace them with explicitly set
 
        permissions[GLOBAL] = permissions[GLOBAL]\
 
                                        .difference(_configurable)
 

	
 
        for perm in user_perms:
 
            permissions[GLOBAL].add(perm.permission.permission_name)
 
    ## END GLOBAL PERMISSIONS
 

	
 
    #======================================================================
 
    # !! PERMISSIONS FOR REPOSITORIES !!
 
    #======================================================================
 
    #======================================================================
 
    # check if user is part of user groups for this repository and
 
    # fill in his permission from it. _choose_perm decides of which
 
    # permission should be selected based on selected method
 
    #======================================================================
 

	
 
    # user group for repositories permissions
 
    user_repo_perms_from_users_groups = \
 
     Session().query(UserGroupRepoToPerm, Permission, Repository,)\
 
        .join((Repository, UserGroupRepoToPerm.repository_id ==
 
               Repository.repo_id))\
 
        .join((Permission, UserGroupRepoToPerm.permission_id ==
 
               Permission.permission_id))\
 
        .join((UserGroup, UserGroupRepoToPerm.users_group_id ==
 
               UserGroup.users_group_id))\
 
        .filter(UserGroup.users_group_active == True)\
 
        .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
 
               UserGroupMember.users_group_id))\
 
        .filter(UserGroupMember.user_id == uid)\
 
        .all()
 

	
 
    multiple_counter = collections.defaultdict(int)
 
    for perm in user_repo_perms_from_users_groups:
 
        r_k = perm.UserGroupRepoToPerm.repository.repo_name
 
        multiple_counter[r_k] += 1
 
        p = perm.Permission.permission_name
 
        cur_perm = permissions[RK][r_k]
 

	
 
        if perm.Repository.user_id == uid:
 
            # set admin if owner
 
            p = 'repository.admin'
 
        else:
 
            if multiple_counter[r_k] > 1:
 
                p = _choose_perm(p, cur_perm)
 
        permissions[RK][r_k] = p
 

	
 
    # user explicit permissions for repositories, overrides any specified
 
    # by the group permission
 
    user_repo_perms = Permission.get_default_perms(uid)
 
    for perm in user_repo_perms:
 
@@ -353,86 +360,92 @@ def _cached_perms_data(user_id, user_is_
 
        cur_perm = permissions[RK][r_k]
 
        # set admin if owner
 
        if perm.Repository.user_id == uid:
 
            p = 'repository.admin'
 
        else:
 
            p = perm.Permission.permission_name
 
            if not explicit:
 
                p = _choose_perm(p, cur_perm)
 
        permissions[RK][r_k] = p
 

	
 
    #======================================================================
 
    # !! PERMISSIONS FOR REPOSITORY GROUPS !!
 
    #======================================================================
 
    #======================================================================
 
    # check if user is part of user groups for this repository groups and
 
    # fill in his permission from it. _choose_perm decides of which
 
    # permission should be selected based on selected method
 
    #======================================================================
 
    # user group for repo groups permissions
 
    user_repo_group_perms_from_users_groups = \
 
     Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
 
     .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
 
     .join((Permission, UserGroupRepoGroupToPerm.permission_id
 
            == Permission.permission_id))\
 
     .join((UserGroup, UserGroupRepoGroupToPerm.users_group_id ==
 
            UserGroup.users_group_id))\
 
     .filter(UserGroup.users_group_active == True)\
 
     .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
 
            == UserGroupMember.users_group_id))\
 
     .filter(UserGroupMember.user_id == uid)\
 
     .all()
 

	
 
    multiple_counter = collections.defaultdict(int)
 
    for perm in user_repo_group_perms_from_users_groups:
 
        g_k = perm.UserGroupRepoGroupToPerm.group.group_name
 
        multiple_counter[g_k] += 1
 
        p = perm.Permission.permission_name
 
        cur_perm = permissions[GK][g_k]
 
        if multiple_counter[g_k] > 1:
 
            p = _choose_perm(p, cur_perm)
 
        permissions[GK][g_k] = p
 

	
 
    # user explicit permissions for repository groups
 
    user_repo_groups_perms = Permission.get_default_group_perms(uid)
 
    for perm in user_repo_groups_perms:
 
        rg_k = perm.UserRepoGroupToPerm.group.group_name
 
        p = perm.Permission.permission_name
 
        cur_perm = permissions[GK][rg_k]
 
        if not explicit:
 
            p = _choose_perm(p, cur_perm)
 
        permissions[GK][rg_k] = p
 

	
 
    #======================================================================
 
    # !! PERMISSIONS FOR USER GROUPS !!
 
    #======================================================================
 
    # user group for user group permissions
 
    user_group_user_groups_perms = \
 
     Session().query(UserGroupUserGroupToPerm, Permission, UserGroup)\
 
     .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
 
            == UserGroup.users_group_id))\
 
     .join((Permission, UserGroupUserGroupToPerm.permission_id
 
            == Permission.permission_id))\
 
     .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
 
            == UserGroupMember.users_group_id))\
 
     .filter(UserGroupMember.user_id == uid)\
 
     .join((UserGroup, UserGroupMember.users_group_id ==
 
            UserGroup.users_group_id), aliased=True, from_joinpoint=True)\
 
     .filter(UserGroup.users_group_active == True)\
 
     .all()
 

	
 
    multiple_counter = collections.defaultdict(int)
 
    for perm in user_group_user_groups_perms:
 
        g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
 
        multiple_counter[g_k] += 1
 
        p = perm.Permission.permission_name
 
        cur_perm = permissions[UK][g_k]
 
        if multiple_counter[g_k] > 1:
 
            p = _choose_perm(p, cur_perm)
 
        permissions[UK][g_k] = p
 

	
 
    #user explicit permission for user groups
 
    user_user_groups_perms = Permission.get_default_user_group_perms(uid)
 
    for perm in user_user_groups_perms:
 
        u_k = perm.UserUserGroupToPerm.user_group.users_group_name
 
        p = perm.Permission.permission_name
 
        cur_perm = permissions[UK][u_k]
 
        if not explicit:
 
            p = _choose_perm(p, cur_perm)
 
        permissions[UK][u_k] = p
 

	
 
    return permissions
 

	
kallithea/lib/dbmigrate/schema/db_1_2_0.py
Show inline comments
 
@@ -315,49 +315,49 @@ class User(Base, BaseModel):
 

	
 
    @classmethod
 
    def get_by_api_key(cls, api_key):
 
        return cls.query().filter(cls.api_key == api_key).one()
 

	
 
    def update_lastlogin(self):
 
        """Update user lastlogin"""
 

	
 
        self.last_login = datetime.datetime.now()
 
        Session.add(self)
 
        Session.commit()
 
        log.debug('updated user %s lastlogin' % self.username)
 

	
 
    @classmethod
 
    def create(cls, form_data):
 
        from kallithea.lib.auth import get_crypt_password
 

	
 
        try:
 
            new_user = cls()
 
            for k, v in form_data.items():
 
                if k == 'password':
 
                    v = get_crypt_password(v)
 
                setattr(new_user, k, v)
 

	
 
            new_user.api_key = generate_api_key(form_data['username'])
 
            new_user.api_key = generate_api_key()
 
            Session.add(new_user)
 
            Session.commit()
 
            return new_user
 
        except:
 
            log.error(traceback.format_exc())
 
            Session.rollback()
 
            raise
 

	
 
class UserLog(Base, BaseModel):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'extend_existing':True}
 
    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=False, unique=None, default=None)
 
    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 date(*self.action_date.timetuple()[:3])
 

	
 
    user = relationship('User')
kallithea/lib/helpers.py
Show inline comments
 
@@ -330,64 +330,64 @@ def pygmentize_annotation(repo_name, fil
 
                return v, p, q
 

	
 
        golden_ratio = 0.618033988749895
 
        h = 0.22717784590367374
 

	
 
        for _unused in xrange(n):
 
            h += golden_ratio
 
            h %= 1
 
            HSV_tuple = [h, 0.95, 0.95]
 
            RGB_tuple = hsv_to_rgb(*HSV_tuple)
 
            yield map(lambda x: str(int(x * 256)), RGB_tuple)
 

	
 
    cgenerator = gen_color()
 

	
 
    def get_color_string(cs):
 
        if cs in color_dict:
 
            col = color_dict[cs]
 
        else:
 
            col = color_dict[cs] = cgenerator.next()
 
        return "color: rgb(%s)! important;" % (', '.join(col))
 

	
 
    def url_func(repo_name):
 

	
 
        def _url_func(changeset):
 
            author = changeset.author
 
            author = escape(changeset.author)
 
            date = changeset.date
 
            message = tooltip(changeset.message)
 
            message = escape(changeset.message)
 

	
 
            tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
 
                            " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
 
                            "</b> %s<br/></div>")
 

	
 
            tooltip_html = tooltip_html % (author, date, message)
 
            lnk_format = show_id(changeset)
 
            uri = link_to(
 
                    lnk_format,
 
                    url('changeset_home', repo_name=repo_name,
 
                        revision=changeset.raw_id),
 
                    style=get_color_string(changeset.raw_id),
 
                    class_='tooltip',
 
                    class_='tooltip safe-html-title',
 
                    title=tooltip_html
 
                  )
 

	
 
            uri += '\n'
 
            return uri
 
        return _url_func
 

	
 
    return literal(markup_whitespace(annotate_highlight(filenode, url_func(repo_name), **kwargs)))
 

	
 

	
 
def is_following_repo(repo_name, user_id):
 
    from kallithea.model.scm import ScmModel
 
    return ScmModel().is_following_repo(repo_name, user_id)
 

	
 
class _Message(object):
 
    """A message returned by ``Flash.pop_messages()``.
 

	
 
    Converting the message to a string returns the message text. Instances
 
    also have the following attributes:
 

	
 
    * ``message``: the message text.
 
    * ``category``: the category specified when the message was created.
 
    """
 

	
kallithea/lib/utils2.py
Show inline comments
 
@@ -11,50 +11,52 @@
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
kallithea.lib.utils
 
~~~~~~~~~~~~~~~~~~~
 

	
 
Some simple helper functions
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Jan 5, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 

	
 
import os
 
import re
 
import sys
 
import time
 
import uuid
 
import datetime
 
import urllib
 
import binascii
 

	
 
import webob
 
import urllib
 
import urlobject
 

	
 
from pylons.i18n.translation import _, ungettext
 
from kallithea.lib.vcs.utils.lazy import LazyProperty
 
from kallithea.lib.compat import json
 

	
 

	
 
def __get_lem():
 
    """
 
    Get language extension map based on what's inside pygments lexers
 
    """
 
    from pygments import lexers
 
    from string import lower
 
    from collections import defaultdict
 

	
 
    d = defaultdict(lambda: [])
 

	
 
    def __clean(s):
 
        s = s.lstrip('*')
 
        s = s.lstrip('.')
 

	
 
        if s.find('[') != -1:
 
            exts = []
 
            start, stop = s.find('['), s.find(']')
 
@@ -140,65 +142,53 @@ def convert_line_endings(line, mode):
 
            line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
 
    return line
 

	
 

	
 
def detect_mode(line, default):
 
    """
 
    Detects line break for given line, if line break couldn't be found
 
    given default value is returned
 

	
 
    :param line: str line
 
    :param default: default
 
    :rtype: int
 
    :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
 
    """
 
    if line.endswith('\r\n'):
 
        return 2
 
    elif line.endswith('\n'):
 
        return 0
 
    elif line.endswith('\r'):
 
        return 1
 
    else:
 
        return default
 

	
 

	
 
def generate_api_key(username, salt=None):
 
def generate_api_key():
 
    """
 
    Generates unique API key for given username, if salt is not given
 
    it'll be generated from some random string
 

	
 
    :param username: username as string
 
    :param salt: salt to hash generate KEY
 
    :rtype: str
 
    :returns: sha1 hash from username+salt
 
    Generates a random (presumably unique) API key.
 
    """
 
    from tempfile import _RandomNameSequence
 
    import hashlib
 

	
 
    if salt is None:
 
        salt = _RandomNameSequence().next()
 

	
 
    return hashlib.sha1(username + salt).hexdigest()
 
    return binascii.hexlify(os.urandom(20))
 

	
 

	
 
def safe_int(val, default=None):
 
    """
 
    Returns int() of val if val is not convertable to int use default
 
    instead
 

	
 
    :param val:
 
    :param default:
 
    """
 

	
 
    try:
 
        val = int(val)
 
    except (ValueError, TypeError):
 
        val = default
 

	
 
    return val
 

	
 

	
 
def safe_unicode(str_, from_encoding=None):
 
    """
 
    safe unicode function. Does few trick to turn str_ into unicode
 

	
 
    In case of UnicodeDecode error we try to return it with encoding detected
kallithea/model/api_key.py
Show inline comments
 
@@ -29,49 +29,49 @@ from __future__ import with_statement
 
import time
 
import logging
 
from sqlalchemy import or_
 

	
 
from kallithea.lib.utils2 import generate_api_key
 
from kallithea.model import BaseModel
 
from kallithea.model.db import UserApiKeys
 
from kallithea.model.meta import Session
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ApiKeyModel(BaseModel):
 
    cls = UserApiKeys
 

	
 
    def create(self, user, description, lifetime=-1):
 
        """
 
        :param user: user or user_id
 
        :param description: description of ApiKey
 
        :param lifetime: expiration time in seconds
 
        """
 
        user = self._get_user(user)
 

	
 
        new_api_key = UserApiKeys()
 
        new_api_key.api_key = generate_api_key(user.username)
 
        new_api_key.api_key = generate_api_key()
 
        new_api_key.user_id = user.user_id
 
        new_api_key.description = description
 
        new_api_key.expires = time.time() + (lifetime * 60) if lifetime != -1 else -1
 
        Session().add(new_api_key)
 

	
 
        return new_api_key
 

	
 
    def delete(self, api_key, user=None):
 
        """
 
        Deletes given api_key, if user is set it also filters the object for
 
        deletion by given user.
 
        """
 
        api_key = UserApiKeys.query().filter(UserApiKeys.api_key == api_key)
 

	
 
        if user:
 
            user = self._get_user(user)
 
            api_key = api_key.filter(UserApiKeys.user_id == user.user_id)
 

	
 
        api_key = api_key.scalar()
 
        Session().delete(api_key)
 

	
 
    def get_api_keys(self, user, show_expired=True):
 
        user = self._get_user(user)
 
        user_api_keys = UserApiKeys.query()\
kallithea/model/db.py
Show inline comments
 
@@ -534,48 +534,51 @@ class User(Base, BaseModel):
 
        '''
 
        user = super(User, cls).get_or_404(id_)
 
        if allow_default == False:
 
            if user.username == User.DEFAULT_USER:
 
                raise DefaultUserException
 
        return user
 

	
 
    @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, fallback=True):
 
        if len(api_key) != 40 or not api_key.isalnum():
 
            return None
 

	
 
        q = cls.query().filter(cls.api_key == api_key)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_api_key_%s" % api_key))
 
        res = q.scalar()
 

	
 
        if fallback and not res:
 
            #fallback to additional keys
 
            _res = UserApiKeys.query()\
 
                .filter(UserApiKeys.api_key == api_key)\
 
                .filter(or_(UserApiKeys.expires == -1,
 
                            UserApiKeys.expires >= time.time()))\
 
                .first()
 
            if _res:
 
                res = _res.user
 
        return res
 

	
 
    @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)
 
@@ -1641,84 +1644,84 @@ class RepoGroup(Base, BaseModel):
 

	
 
        """
 
        group = self
 
        data = dict(
 
            group_id=group.group_id,
 
            group_name=group.group_name,
 
            group_description=group.group_description,
 
            parent_group=group.parent_group.group_name if group.parent_group else None,
 
            repositories=[x.repo_name for x in group.repositories],
 
            owner=group.user.username
 
        )
 
        return data
 

	
 

	
 
class Permission(Base, BaseModel):
 
    __tablename__ = 'permissions'
 
    __table_args__ = (
 
        Index('p_perm_name_idx', 'permission_name'),
 
        {'extend_existing': True, 'mysql_engine': 'InnoDB',
 
         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
 
    )
 
    PERMS = [
 
        ('hg.admin', _('Kallithea Administrator')),
 

	
 
        ('repository.none', _('Repository no access')),
 
        ('repository.read', _('Repository read access')),
 
        ('repository.write', _('Repository write access')),
 
        ('repository.admin', _('Repository admin access')),
 

	
 
        ('group.none', _('Repository group no access')),
 
        ('group.read', _('Repository group read access')),
 
        ('group.write', _('Repository group write access')),
 
        ('group.admin', _('Repository group admin access')),
 

	
 
        ('usergroup.none', _('User group no access')),
 
        ('usergroup.read', _('User group read access')),
 
        ('usergroup.write', _('User group write access')),
 
        ('usergroup.admin', _('User group admin access')),
 

	
 
        ('hg.repogroup.create.false', _('Repository Group creation disabled')),
 
        ('hg.repogroup.create.true', _('Repository Group creation enabled')),
 

	
 
        ('hg.usergroup.create.false', _('User Group creation disabled')),
 
        ('hg.usergroup.create.true', _('User Group creation enabled')),
 

	
 
        ('hg.create.none', _('Repository creation disabled')),
 
        ('hg.create.repository', _('Repository creation enabled')),
 
        ('repository.none', _('Default user has no access to new Repositories')),
 
        ('repository.read', _('Default user has read access to new Repositories')),
 
        ('repository.write', _('Default user has write access to new Repositories')),
 
        ('repository.admin', _('Default user has admin access to new Repositories')),
 

	
 
        ('group.none', _('Default user has no access to new Repository Groups')),
 
        ('group.read', _('Default user has read access to new Repository Groups')),
 
        ('group.write', _('Default user has write access to new Repository Groups')),
 
        ('group.admin', _('Default user has admin access to new Repository Groups')),
 

	
 
        ('usergroup.none', _('Default user has no access to new User Groups')),
 
        ('usergroup.read', _('Default user has read access to new User Groups')),
 
        ('usergroup.write', _('Default user has write access to new User Groups')),
 
        ('usergroup.admin', _('Default user has admin access to new User Groups')),
 

	
 
        ('hg.repogroup.create.false', _('Only admins can create Repository Groups')),
 
        ('hg.repogroup.create.true', _('Non-admins can create Repository Groups')),
 

	
 
        ('hg.usergroup.create.false', _('Only admins can create User Groups')),
 
        ('hg.usergroup.create.true', _('Non-admins can create User Groups')),
 

	
 
        ('hg.create.none', _('Only admins can create top level Repositories')),
 
        ('hg.create.repository', _('Non-admins can create top level Repositories')),
 

	
 
        ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
 
        ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
 

	
 
        ('hg.fork.none', _('Repository forking disabled')),
 
        ('hg.fork.repository', _('Repository forking enabled')),
 
        ('hg.fork.none', _('Only admins can fork repositories')),
 
        ('hg.fork.repository', _('Non-admins can can fork repositories')),
 

	
 
        ('hg.register.none', _('Registration disabled')),
 
        ('hg.register.manual_activate', _('User Registration with manual account activation')),
 
        ('hg.register.auto_activate', _('User Registration with automatic account activation')),
 

	
 
        ('hg.extern_activate.manual', _('Manual activation of external account')),
 
        ('hg.extern_activate.auto', _('Automatic activation of external account')),
 

	
 
    ]
 

	
 
    #definition of system default permissions for DEFAULT user
 
    DEFAULT_USER_PERMISSIONS = [
 
        'repository.read',
 
        'group.read',
 
        'usergroup.read',
 
        'hg.create.repository',
 
        'hg.create.write_on_repogroup.true',
 
        'hg.fork.repository',
 
        'hg.register.manual_activate',
 
        'hg.extern_activate.auto',
 
    ]
 

	
 
    # defines which permissions are more important higher the more important
 
    # Weight defines which permissions are more important.
 
    # The higher number the more important.
 
    PERM_WEIGHTS = {
 
        'repository.none': 0,
 
        'repository.read': 1,
 
        'repository.write': 3,
 
        'repository.admin': 4,
 

	
 
        'group.none': 0,
kallithea/model/user.py
Show inline comments
 
@@ -75,49 +75,49 @@ class UserModel(BaseModel):
 
        from kallithea.lib.hooks import log_create_user, \
 
            check_allowed_create_user
 
        _fd = form_data
 
        user_data = {
 
            'username': _fd['username'],
 
            'password': _fd['password'],
 
            'email': _fd['email'],
 
            'firstname': _fd['firstname'],
 
            'lastname': _fd['lastname'],
 
            'active': _fd['active'],
 
            'admin': False
 
        }
 
        # raises UserCreationError if it's not allowed
 
        check_allowed_create_user(user_data, cur_user)
 
        from kallithea.lib.auth import get_crypt_password
 

	
 
        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'])
 
        new_user.api_key = generate_api_key()
 
        self.sa.add(new_user)
 

	
 
        log_create_user(new_user.get_dict(), cur_user)
 
        return new_user
 

	
 
    def create_or_update(self, username, password, email, firstname='',
 
                         lastname='', active=True, admin=False,
 
                         extern_type=None, extern_name=None, cur_user=None):
 
        """
 
        Creates a new instance if not found, or updates current one
 

	
 
        :param username:
 
        :param password:
 
        :param email:
 
        :param active:
 
        :param firstname:
 
        :param lastname:
 
        :param active:
 
        :param admin:
 
        :param extern_name:
 
        :param extern_type:
 
        :param cur_user:
 
        """
 
        if not cur_user:
 
@@ -137,49 +137,49 @@ class UserModel(BaseModel):
 
        log.debug('Checking for %s account in Kallithea database' % username)
 
        user = User.get_by_username(username, case_insensitive=True)
 
        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.email = email
 
            new_user.active = active
 
            new_user.extern_name = safe_unicode(extern_name) \
 
                if extern_name else None
 
            new_user.extern_type = safe_unicode(extern_type) \
 
                if extern_type else None
 
            new_user.name = firstname
 
            new_user.lastname = lastname
 

	
 
            if not edit:
 
                new_user.api_key = generate_api_key(username)
 
                new_user.api_key = generate_api_key()
 

	
 
            # set password only if creating an user or password is changed
 
            password_change = new_user.password and \
 
                not check_password(password, new_user.password)
 
            if not edit or password_change:
 
                reason = 'new password' if edit else 'new user'
 
                log.debug('Updating password reason=>%s' % (reason,))
 
                new_user.password = get_crypt_password(password) \
 
                    if password else None
 

	
 
            self.sa.add(new_user)
 

	
 
            if not edit:
 
                log_create_user(new_user.get_dict(), cur_user)
 
            return new_user
 
        except (DatabaseError,):
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def create_registration(self, form_data):
 
        from kallithea.model.notification import NotificationModel
 
        import kallithea.lib.helpers as h
 

	
 
        form_data['admin'] = False
kallithea/model/user_group.py
Show inline comments
 
@@ -225,49 +225,49 @@ class UserGroupModel(BaseModel):
 
        perm = self._get_perm(perm)
 

	
 
        return UserGroupToPerm.query()\
 
            .filter(UserGroupToPerm.users_group == user_group)\
 
            .filter(UserGroupToPerm.permission == perm).scalar() is not None
 

	
 
    def grant_perm(self, user_group, perm):
 
        user_group = self._get_user_group(user_group)
 
        perm = self._get_perm(perm)
 

	
 
        # if this permission is already granted skip it
 
        _perm = UserGroupToPerm.query()\
 
            .filter(UserGroupToPerm.users_group == user_group)\
 
            .filter(UserGroupToPerm.permission == perm)\
 
            .scalar()
 
        if _perm:
 
            return
 

	
 
        new = UserGroupToPerm()
 
        new.users_group = user_group
 
        new.permission = perm
 
        self.sa.add(new)
 
        return new
 

	
 
    def revokehas_permrevoke_permgrant_perm_perm(self, user_group, perm):
 
    def revoke_perm(self, user_group, perm):
 
        user_group = self._get_user_group(user_group)
 
        perm = self._get_perm(perm)
 

	
 
        obj = UserGroupToPerm.query()\
 
            .filter(UserGroupToPerm.users_group == user_group)\
 
            .filter(UserGroupToPerm.permission == perm).scalar()
 
        if obj:
 
            self.sa.delete(obj)
 

	
 
    def grant_user_permission(self, user_group, user, perm):
 
        """
 
        Grant permission for user on given user group, or update
 
        existing one if found
 

	
 
        :param user_group: Instance of UserGroup, users_group_id,
 
            or users_group_name
 
        :param user: Instance of User, user_id or username
 
        :param perm: Instance of Permission, or permission_name
 
        """
 

	
 
        user_group = self._get_user_group(user_group)
 
        user = self._get_user(user)
 
        permission = self._get_perm(perm)
 

	
kallithea/public/js/base.js
Show inline comments
 
@@ -488,67 +488,73 @@ var showRepoSize = function(target, repo
 
var tooltip_activate = function(){
 
    $(document).ready(_init_tooltip);
 
};
 

	
 
var _activate_tooltip = function($tt){
 
    $tt.mouseover(_show_tooltip);
 
    $tt.mousemove(_move_tooltip);
 
    $tt.mouseout(_close_tooltip);
 
};
 

	
 
var _init_tooltip = function(){
 
    var $tipBox = $('#tip-box');
 
    if(!$tipBox.length){
 
        $tipBox = $('<div id="tip-box"></div>')
 
        $(document.body).append($tipBox);
 
    }
 

	
 
    $tipBox.hide();
 
    $tipBox.css('position', 'absolute');
 
    $tipBox.css('max-width', '600px');
 

	
 
    _activate_tooltip($('.tooltip'));
 
};
 

	
 
var _show_tooltip = function(e, tipText){
 
var _show_tooltip = function(e, tipText, safe){
 
    e.stopImmediatePropagation();
 
    var el = e.currentTarget;
 
    var $el = $(el);
 
    if(tipText){
 
        // just use it
 
    } else if(el.tagName.toLowerCase() === 'img'){
 
        tipText = el.alt ? el.alt : '';
 
    } else {
 
        tipText = el.title ? el.title : '';
 
        safe = safe || $el.hasClass("safe-html-title");
 
    }
 

	
 
    if(tipText !== ''){
 
        // save org title
 
        $(el).attr('tt_title', tipText);
 
        $el.attr('tt_title', tipText);
 
        // reset title to not show org tooltips
 
        $(el).attr('title', '');
 
        $el.attr('title', '');
 

	
 
        var $tipBox = $('#tip-box');
 
        if (safe) {
 
        $tipBox.html(tipText);
 
        } else {
 
            $tipBox.text(tipText);
 
        }
 
        $tipBox.css('display', 'block');
 
    }
 
};
 

	
 
var _move_tooltip = function(e){
 
    e.stopImmediatePropagation();
 
    var $tipBox = $('#tip-box');
 
    $tipBox.css('top', (e.pageY + 15) + 'px');
 
    $tipBox.css('left', (e.pageX + 15) + 'px');
 
};
 

	
 
var _close_tooltip = function(e){
 
    e.stopImmediatePropagation();
 
    var $tipBox = $('#tip-box');
 
    $tipBox.hide();
 
    var el = e.currentTarget;
 
    $(el).attr('title', $(el).attr('tt_title'));
 
};
 

	
 
/**
 
 * Quick filter widget
 
 *
 
 * @param target: filter input target
 
 * @param nodes: list of nodes in html we want to filter.
kallithea/templates/admin/permissions/permissions_globals.html
Show inline comments
 
${h.form(url('admin_permissions'), method='post')}
 
    <div class="form">
 
        <!-- fields -->
 
        <div class="fields">
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="anonymous">${_('Anonymous access')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    <div class="checkbox">
 
                        ${h.checkbox('anonymous',True)}
 
                    </div>
 
                     <span class="help-block">${h.literal(_('Allow access to Kallithea without needing to log in. Anonymous users use %s user permissions.' % (h.link_to('*default*',h.url('admin_permissions_perms')))))}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
                <div class="label">
 
                    <label for="default_repo_perm">${_('Repository')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_repo_perm','',c.repo_perms_choices)}
 

	
 
                    ${h.checkbox('overwrite_default_repo','true')}
 
                    <label for="overwrite_default_repo">
 
                    <span class="tooltip"
 
                    title="${h.tooltip(_('All default permissions on each repository will be reset to chosen permission, note that all custom default permission on repositories will be lost'))}">
 
                    ${_('Overwrite existing settings')}</span> </label>
 
                    ${_('Apply to all existing repositories')}</span> </label>
 
                    <span class="help-block">${_('Permissions for the Default user on new repositories.')}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
                <div class="label">
 
                    <label for="default_group_perm">${_('Repository group')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_group_perm','',c.group_perms_choices)}
 
                    ${h.checkbox('overwrite_default_group','true')}
 
                    <label for="overwrite_default_group">
 
                    <span class="tooltip"
 
                    title="${h.tooltip(_('All default permissions on each repository group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
 
                    ${_('Overwrite existing settings')}</span> </label>
 

	
 
                    ${_('Apply to all existing repository groups')}</span> </label>
 
                    <span class="help-block">${_('Permissions for the Default user on new repository groups.')}</span>
 
                </div>
 
            </div>
 
            <div class="field">
 
                <div class="label">
 
                    <label for="default_group_perm">${_('User group')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_user_group_perm','',c.user_group_perms_choices)}
 
                    ${h.checkbox('overwrite_default_user_group','true')}
 
                    <label for="overwrite_default_user_group">
 
                    <span class="tooltip"
 
                    title="${h.tooltip(_('All default permissions on each user group will be reset to chosen permission, note that all custom default permission on user groups will be lost'))}">
 
                    ${_('Overwrite existing settings')}</span> </label>
 

	
 
                    ${_('Apply to all existing user groups')}</span></label>
 
                    <span class="help-block">${_('Permissions for the Default user on new user groups.')}</span>
 
                </div>
 
            </div>
 
             <div class="field">
 
                <div class="label">
 
                    <label for="default_repo_create">${_('Repository creation')}:</label>
 
                    <label for="default_repo_create">${_('Top level repository creation')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_repo_create','',c.repo_create_choices)}
 
                    <span class="help-block">${_('Enable this to allow non-admins to create repositories at the top level.')}</span>
 
                    <span class="help-block">${_('Note: This will also give all users API access to create repositories everywhere. That might change in future versions.')}</span>
 
                </div>
 
             </div>
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="create_on_write">${_('Repository creation with group write access')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('create_on_write','',c.repo_create_on_write_choices)}
 
                    <span class="help-block">${_('Write permission to a repository group allows creating repositories inside that group.')}</span>
 
                    <span class="help-block">${_('With this, write permission to a repository group allows creating repositories inside that group. Without this, group write permissions mean nothing.')}</span>
 
                </div>
 
            </div>
 
             <div class="field">
 
                <div class="label">
 
                    <label for="default_user_group_create">${_('User group creation')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_user_group_create','',c.user_group_create_choices)}
 
                    <span class="help-block">${_('Enable this to allow non-admins to create user groups.')}</span>
 
                </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)}
 
                    <span class="help-block">${_('Enable this to allow non-admins to fork repositories.')}</span>
 
                </div>
 
             </div>
 
             <div class="field">
 
                <div class="label">
 
                    <label for="default_register">${_('Registration')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_register','',c.register_choices)}
 
                </div>
 
             </div>
 
             <div class="field">
 
                <div class="label">
 
                    <label for="default_extern_activate">${_('External auth account activation')}:</label>
 
                </div>
 
                <div class="select">
 
                    ${h.select('default_extern_activate','',c.extern_activate_choices)}
 
                </div>
 
             </div>
 
            <div class="buttons">
 
              ${h.submit('save',_('Save'),class_="btn")}
 
              ${h.reset('reset',_('Reset'),class_="btn")}
 
            </div>
 
        </div>
 
    </div>
kallithea/templates/data_table/_dt_elements.html
Show inline comments
 
@@ -72,49 +72,49 @@
 
      <i class="icon-keyhole-circled" title="${_('Private repository')}"></i>
 
    %elif not private and c.visual.show_public_icon:
 
      <i class="icon-globe" title="${_('Public repository')}"></i>
 
    %else:
 
      <span style="margin: 0px 8px 0px 8px"></span>
 
    %endif
 
    <span class="dt_repo_name">${get_name(name)}</span>
 
    </a>
 
    %if fork_of:
 
      <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-fork"></i></a>
 
    %endif
 
    %if rstate == 'repo_state_pending':
 
      <i class="icon-wrench" title="${_('Repository creation in progress...')}"></i>
 
    %endif
 
  </div>
 
</%def>
 

	
 
<%def name="last_change(last_change)">
 
  <span class="tooltip" date="${last_change}" title="${h.tooltip(h.fmt_date(last_change))}">${h.age(last_change)}</span>
 
</%def>
 

	
 
<%def name="revision(name,rev,tip,author,last_msg)">
 
  <div>
 
  %if rev >= 0:
 
      <a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip revision-link" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a>
 
      <a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip revision-link safe-html-title" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a>
 
  %else:
 
      ${_('No changesets yet')}
 
  %endif
 
  </div>
 
</%def>
 

	
 
<%def name="rss(name)">
 
  %if c.authuser.username != 'default':
 
    <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
 
  %else:
 
    <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
 
  %endif
 
</%def>
 

	
 
<%def name="atom(name)">
 
  %if c.authuser.username != 'default':
 
    <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
 
  %else:
 
    <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
 
  %endif
 
</%def>
 

	
 
<%def name="repo_actions(repo_name, super_user=True)">
 
  <div>
kallithea/tests/api/api_base.py
Show inline comments
 
@@ -1157,48 +1157,107 @@ class _BaseTestApi(object):
 
        fixture.create_repo(repo_name)
 
        updates = {'active': False}
 
        id_, params = _build_data(self.apikey_regular, 'update_repo',
 
                                  repoid=repo_name, **updates)
 
        response = api_call(self, params)
 
        try:
 
            expected = 'repository `%s` does not exist' % repo_name
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    @mock.patch.object(RepoModel, 'update', crash)
 
    def test_api_update_repo_exception_occurred(self):
 
        repo_name = 'api_update_me'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        id_, params = _build_data(self.apikey, 'update_repo',
 
                                  repoid=repo_name, owner=TEST_USER_ADMIN_LOGIN,)
 
        response = api_call(self, params)
 
        try:
 
            expected = 'failed to update repo `%s`' % repo_name
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    def test_api_update_repo_regular_user_change_repo_name(self):
 
        repo_name = 'admin_owned'
 
        new_repo_name = 'new_repo_name'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        RepoModel().grant_user_permission(repo=repo_name,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm='repository.admin')
 
        UserModel().revoke_perm('default', 'hg.create.repository')
 
        UserModel().grant_perm('default', 'hg.create.none')
 
        updates = {'name': new_repo_name}
 
        id_, params = _build_data(self.apikey_regular, 'update_repo',
 
                                  repoid=repo_name, **updates)
 
        response = api_call(self, params)
 
        try:
 
            expected = 'no permission to create (or move) repositories'
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 
            fixture.destroy_repo(new_repo_name)
 

	
 
    def test_api_update_repo_regular_user_change_repo_name_allowed(self):
 
        repo_name = 'admin_owned'
 
        new_repo_name = 'new_repo_name'
 
        repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        RepoModel().grant_user_permission(repo=repo_name,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm='repository.admin')
 
        UserModel().revoke_perm('default', 'hg.create.none')
 
        UserModel().grant_perm('default', 'hg.create.repository')
 
        updates = {'name': new_repo_name}
 
        id_, params = _build_data(self.apikey_regular, 'update_repo',
 
                                  repoid=repo_name, **updates)
 
        response = api_call(self, params)
 
        try:
 
            expected = {
 
                'msg': 'updated repo ID:%s %s' % (repo.repo_id, new_repo_name),
 
                'repository': repo.get_api_data()
 
            }
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 
            fixture.destroy_repo(new_repo_name)
 

	
 
    def test_api_update_repo_regular_user_change_owner(self):
 
        repo_name = 'admin_owned'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        RepoModel().grant_user_permission(repo=repo_name,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm='repository.admin')
 
        updates = {'owner': TEST_USER_ADMIN_LOGIN}
 
        id_, params = _build_data(self.apikey_regular, 'update_repo',
 
                                  repoid=repo_name, **updates)
 
        response = api_call(self, params)
 
        try:
 
            expected = 'Only Kallithea admin can specify `owner` param'
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    def test_api_delete_repo(self):
 
        repo_name = 'api_delete_me'
 
        fixture.create_repo(repo_name, repo_type=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
 
        }
 
        try:
 
            expected = ret
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    def test_api_delete_repo_by_non_admin(self):
 
        repo_name = 'api_delete_me'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
 
                            cur_user=self.TEST_USER_LOGIN)
 
        id_, params = _build_data(self.apikey_regular, 'delete_repo',
 
                                  repoid=repo_name, )
 
@@ -1282,48 +1341,69 @@ class _BaseTestApi(object):
 
        id_, params = _build_data(self.apikey_regular, 'fork_repo',
 
                                  repoid=self.REPO,
 
                                  fork_name=fork_name,
 
                                  owner=TEST_USER_ADMIN_LOGIN,
 
        )
 
        response = api_call(self, params)
 
        expected = 'Only Kallithea admin can specify `owner` param'
 
        self._compare_error(id_, expected, given=response.body)
 
        fixture.destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_non_admin_no_permission_to_fork(self):
 
        RepoModel().grant_user_permission(repo=self.REPO,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm='repository.none')
 
        fork_name = 'api-repo-fork'
 
        id_, params = _build_data(self.apikey_regular, 'fork_repo',
 
                                  repoid=self.REPO,
 
                                  fork_name=fork_name,
 
        )
 
        response = api_call(self, params)
 
        expected = 'repository `%s` does not exist' % (self.REPO)
 
        self._compare_error(id_, expected, given=response.body)
 
        fixture.destroy_repo(fork_name)
 

	
 
    @parameterized.expand([('read', 'repository.read'),
 
                           ('write', 'repository.write'),
 
                           ('admin', 'repository.admin')])
 
    def test_api_fork_repo_non_admin_no_create_repo_permission(self, name, perm):
 
        fork_name = 'api-repo-fork'
 
        # regardless of base repository permission, forking is disallowed
 
        # when repository creation is disabled
 
        RepoModel().grant_user_permission(repo=self.REPO,
 
                                          user=self.TEST_USER_LOGIN,
 
                                          perm=perm)
 
        UserModel().revoke_perm('default', 'hg.create.repository')
 
        UserModel().grant_perm('default', 'hg.create.none')
 
        id_, params = _build_data(self.apikey_regular, 'fork_repo',
 
                                  repoid=self.REPO,
 
                                  fork_name=fork_name,
 
        )
 
        response = api_call(self, params)
 
        expected = 'no permission to create repositories'
 
        self._compare_error(id_, expected, given=response.body)
 
        fixture.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'
 
        fixture.create_fork(self.REPO, fork_name)
 

	
 
        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,
 
            )
kallithea/tests/functional/test_login.py
Show inline comments
 
@@ -309,49 +309,49 @@ class TestLoginController(TestController
 
        response = self.app.post(
 
                        url(controller='login', action='password_reset'),
 
                            {'email': bad_email, }
 
        )
 

	
 
        response.mustcontain('An email address must contain a single @')
 

	
 
    def test_forgot_password(self):
 
        response = self.app.get(url(controller='login',
 
                                    action='password_reset'))
 
        self.assertEqual(response.status, '200 OK')
 

	
 
        username = 'test_password_reset_1'
 
        password = 'qweqwe'
 
        email = 'username@python-works.com'
 
        name = 'passwd'
 
        lastname = 'reset'
 

	
 
        new = User()
 
        new.username = username
 
        new.password = password
 
        new.email = email
 
        new.name = name
 
        new.lastname = lastname
 
        new.api_key = generate_api_key(username)
 
        new.api_key = generate_api_key()
 
        Session().add(new)
 
        Session().commit()
 

	
 
        response = self.app.post(url(controller='login',
 
                                     action='password_reset'),
 
                                 {'email': email, })
 

	
 
        self.checkSessionFlash(response, 'Your password reset link was sent')
 

	
 
        response = response.follow()
 

	
 
        # BAD KEY
 

	
 
        key = "bad"
 
        response = self.app.get(url(controller='login',
 
                                    action='password_reset_confirmation',
 
                                    key=key))
 
        self.assertEqual(response.status, '302 Found')
 
        self.assertTrue(response.location.endswith(url('reset_password')))
 

	
 
        # GOOD KEY
 

	
 
        key = User.get_by_username(username).api_key
 
        response = self.app.get(url(controller='login',
 
@@ -374,48 +374,50 @@ class TestLoginController(TestController
 
        ('none', None),
 
        ('empty_string', ''),
 
        ('fake_number', '123456'),
 
        ('proper_api_key', None)
 
    ])
 
    def test_access_not_whitelisted_page_via_api_key(self, test_name, api_key):
 
        whitelist = self._get_api_whitelist([])
 
        with mock.patch('kallithea.CONFIG', whitelist):
 
            self.assertEqual([],
 
                             whitelist['api_access_controllers_whitelist'])
 
            if test_name == 'proper_api_key':
 
                #use builtin if api_key is None
 
                api_key = User.get_first_admin().api_key
 

	
 
            with fixture.anon_access(False):
 
                self.app.get(url(controller='changeset',
 
                                 action='changeset_raw',
 
                                 repo_name=HG_REPO, revision='tip', api_key=api_key),
 
                             status=403)
 

	
 
    @parameterized.expand([
 
        ('none', None, 302),
 
        ('empty_string', '', 302),
 
        ('fake_number', '123456', 302),
 
        ('fake_not_alnum', 'a-z', 302),
 
        ('fake_api_key', '0123456789abcdef0123456789ABCDEF01234567', 302),
 
        ('proper_api_key', None, 200)
 
    ])
 
    def test_access_whitelisted_page_via_api_key(self, test_name, api_key, code):
 
        whitelist = self._get_api_whitelist(['ChangesetController:changeset_raw'])
 
        with mock.patch('kallithea.CONFIG', whitelist):
 
            self.assertEqual(['ChangesetController:changeset_raw'],
 
                             whitelist['api_access_controllers_whitelist'])
 
            if test_name == 'proper_api_key':
 
                api_key = User.get_first_admin().api_key
 

	
 
            with fixture.anon_access(False):
 
                self.app.get(url(controller='changeset',
 
                                 action='changeset_raw',
 
                                 repo_name=HG_REPO, revision='tip', api_key=api_key),
 
                             status=code)
 

	
 
    def test_access_page_via_extra_api_key(self):
 
        whitelist = self._get_api_whitelist(['ChangesetController:changeset_raw'])
 
        with mock.patch('kallithea.CONFIG', whitelist):
 
            self.assertEqual(['ChangesetController:changeset_raw'],
 
                             whitelist['api_access_controllers_whitelist'])
 

	
 
            new_api_key = ApiKeyModel().create(TEST_USER_ADMIN_LOGIN, u'test')
 
            Session().commit()
kallithea/tests/models/test_permissions.py
Show inline comments
 
@@ -40,48 +40,50 @@ class TestPermissions(BaseTestCase):
 
            email=u'u3@example.com', firstname=u'u3', lastname=u'u3'
 
        )
 
        self.anon = User.get_default_user()
 
        self.a1 = UserModel().create_or_update(
 
            username=u'a1', password=u'qweqwe',
 
            email=u'a1@example.com', 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'):
 
            RepoGroupModel().delete(self.g1.group_id)
 
        if hasattr(self, 'g2'):
 
            RepoGroupModel().delete(self.g2.group_id)
 

	
 
        if hasattr(self, 'ug1'):
 
            UserGroupModel().delete(self.ug1, force=True)
 
        if hasattr(self, 'ug2'):
 
            UserGroupModel().delete(self.ug2, 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': {HG_REPO: 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)
 
@@ -413,48 +415,260 @@ class TestPermissions(BaseTestCase):
 
        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',
 
                              'hg.extern_activate.auto',
 
                              'repository.read', 'group.read',
 
                              'usergroup.read', 'hg.create.write_on_repogroup.true']))
 

	
 
    def test_inactive_user_group_does_not_affect_global_permissions(self):
 
        # Issue #138: Inactive User Groups affecting permissions
 
        # Add user to inactive user group, set specific permissions on user
 
        # group and disable inherit-from-default. User permissions should still
 
        # inherit from default.
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        # enable fork and create on user group
 
        user_group_model.revoke_perm(self.ug1, perm='hg.create.none')
 
        user_group_model.grant_perm(self.ug1, perm='hg.create.repository')
 
        user_group_model.revoke_perm(self.ug1, perm='hg.fork.none')
 
        user_group_model.grant_perm(self.ug1, perm='hg.fork.repository')
 

	
 
        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')
 

	
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 

	
 
        self.assertEqual(u1_auth.permissions['global'],
 
                         set(['hg.create.none', 'hg.fork.none',
 
                              'hg.register.manual_activate',
 
                              'hg.extern_activate.auto',
 
                              'repository.read', 'group.read',
 
                              'usergroup.read',
 
                              'hg.create.write_on_repogroup.true']))
 

	
 
    def test_inactive_user_group_does_not_affect_global_permissions_inverse(self):
 
        # Issue #138: Inactive User Groups affecting permissions
 
        # Add user to inactive user group, set specific permissions on user
 
        # group and disable inherit-from-default. User permissions should still
 
        # inherit from default.
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        # disable fork and create on user group
 
        user_group_model.revoke_perm(self.ug1, perm='hg.create.repository')
 
        user_group_model.grant_perm(self.ug1, perm='hg.create.none')
 
        user_group_model.revoke_perm(self.ug1, perm='hg.fork.repository')
 
        user_group_model.grant_perm(self.ug1, perm='hg.fork.none')
 

	
 
        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')
 

	
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 

	
 
        self.assertEqual(u1_auth.permissions['global'],
 
                         set(['hg.create.repository', 'hg.fork.repository',
 
                              'hg.register.manual_activate',
 
                              'hg.extern_activate.auto',
 
                              'repository.read', 'group.read',
 
                              'usergroup.read',
 
                              'hg.create.write_on_repogroup.true']))
 

	
 
    def test_inactive_user_group_does_not_affect_repo_permissions(self):
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        # note: make u2 repo owner rather than u1, because the owner always has
 
        # admin permissions
 
        self.test_repo = fixture.create_repo(name='myownrepo',
 
                                             repo_type='hg',
 
                                             cur_user=self.u2)
 

	
 
        # enable admin access for user group on repo
 
        RepoModel().grant_user_group_permission(self.test_repo,
 
                                                group_name=self.ug1,
 
                                                perm='repository.admin')
 
        # enable only write access for default user on repo
 
        RepoModel().grant_user_permission(self.test_repo,
 
                                          user='default',
 
                                          perm='repository.write')
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
 
                         'repository.write')
 

	
 
    def test_inactive_user_group_does_not_affect_repo_permissions_inverse(self):
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        # note: make u2 repo owner rather than u1, because the owner always has
 
        # admin permissions
 
        self.test_repo = fixture.create_repo(name='myownrepo',
 
                                             repo_type='hg',
 
                                             cur_user=self.u2)
 

	
 
        # enable only write access for user group on repo
 
        RepoModel().grant_user_group_permission(self.test_repo,
 
                                                group_name=self.ug1,
 
                                                perm='repository.write')
 
        # enable admin access for default user on repo
 
        RepoModel().grant_user_permission(self.test_repo,
 
                                          user='default',
 
                                          perm='repository.admin')
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
 
                         'repository.admin')
 

	
 
    def test_inactive_user_group_does_not_affect_repo_group_permissions(self):
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
 

	
 
        # enable admin access for user group on repo group
 
        RepoGroupModel().grant_user_group_permission(self.g1,
 
                                                     group_name=self.ug1,
 
                                                     perm='group.admin')
 
        # enable only write access for default user on repo group
 
        RepoGroupModel().grant_user_permission(self.g1,
 
                                               user='default',
 
                                               perm='group.write')
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories_groups'],
 
                         {u'group1': u'group.write'})
 

	
 
    def test_inactive_user_group_does_not_affect_repo_group_permissions_inverse(self):
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
 

	
 
        # enable only write access for user group on repo group
 
        RepoGroupModel().grant_user_group_permission(self.g1,
 
                                                     group_name=self.ug1,
 
                                                     perm='group.write')
 
        # enable admin access for default user on repo group
 
        RepoGroupModel().grant_user_permission(self.g1,
 
                                               user='default',
 
                                               perm='group.admin')
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories_groups'],
 
                         {u'group1': u'group.admin'})
 

	
 
    def test_inactive_user_group_does_not_affect_user_group_permissions(self):
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        self.ug2 = fixture.create_user_group('G2')
 

	
 
        # enable admin access for user group on user group
 
        UserGroupModel().grant_user_group_permission(self.ug2,
 
                                                     user_group=self.ug1,
 
                                                     perm='usergroup.admin')
 
        # enable only write access for default user on user group
 
        UserGroupModel().grant_user_permission(self.ug2,
 
                                               user='default',
 
                                               perm='usergroup.write')
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['user_groups'][u'G1'], u'usergroup.read')
 
        self.assertEqual(u1_auth.permissions['user_groups'][u'G2'], u'usergroup.write')
 

	
 
    def test_inactive_user_group_does_not_affect_user_group_permissions_inverse(self):
 
        self.ug1 = fixture.create_user_group('G1')
 
        self.ug1.inherit_default_permissions = False
 
        user_group_model = UserGroupModel()
 
        user_group_model.add_user_to_group(self.ug1, self.u1)
 
        user_group_model.update(self.ug1, {'users_group_active': False})
 

	
 
        self.ug2 = fixture.create_user_group('G2')
 

	
 
        # enable only write access for user group on user group
 
        UserGroupModel().grant_user_group_permission(self.ug2,
 
                                                     user_group=self.ug1,
 
                                                     perm='usergroup.write')
 
        # enable admin access for default user on user group
 
        UserGroupModel().grant_user_permission(self.ug2,
 
                                               user='default',
 
                                               perm='usergroup.admin')
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['user_groups'][u'G1'], u'usergroup.read')
 
        self.assertEqual(u1_auth.permissions['user_groups'][u'G2'], u'usergroup.admin')
 

	
 
    def test_owner_permissions_doesnot_get_overwritten_by_group(self):
 
        #create repo as USER,
 
        self.test_repo = fixture.create_repo(name='myownrepo',
 
                                             repo_type='hg',
 
                                             cur_user=self.u1)
 

	
 
        #he has permissions of admin as owner
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
 
                         'repository.admin')
 
        #set his permission as user group, he should still be admin
 
        self.ug1 = fixture.create_user_group('G1')
 
        UserGroupModel().add_user_to_group(self.ug1, self.u1)
 
        RepoModel().grant_user_group_permission(self.test_repo,
 
                                                 group_name=self.ug1,
 
                                                 perm='repository.none')
 

	
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
 
                         'repository.admin')
 

	
 
    def test_owner_permissions_doesnot_get_overwritten_by_others(self):
 
        #create repo as USER,
0 comments (0 inline, 0 general)