Changeset - 148360f533a4
.hgtags
Show inline comments
 
@@ -14,48 +14,49 @@ ccbe729908844884aea89d00fb14a6cb92e10c06
 
ca41d544dbdfd2f81bd0304168492a26276aadb6 rhodecode-0.0.0.8.3
 
2fa16ec5822da0c6fade3dd1ed9b6c0655e5dbbf rhodecode-0.0.0.8.4
 
16ba57d8fe2317c49dbd422afd07ab497687aa02 rhodecode-0.0.0.8.5
 
53128b6b9a4ddb6ee9554cbb83a082a6d1316b42 rhodecode-0.0.1.0.0rc4
 
afd98d1f817e6a6b52172735c22160239e615a6b rhodecode-0.0.1.0.0
 
bee56f209c40a6880f2f633b02227b5ee1f8ff5a rhodecode-0.0.1.0.1
 
d85b0948e53925ebbbc49e9f7967013a04f866e9 rhodecode-0.0.1.0.2
 
d9c8dddb96af521e346f05b88d515c536eef3d17 rhodecode-0.0.1.1.0
 
344f748517814ed0408a49e392dc625f4cc37fdc rhodecode-0.0.1.1.1
 
6c01c12eafb8cc72d4c4cbd121400fad755b2862 rhodecode-0.0.1.1.2
 
4fa80e0484ef5c33feaa9c39fc66916f410ba353 rhodecode-0.0.1.1.3
 
cb77867d69d3c5931712aac486c980a42ee90745 rhodecode-0.0.1.1.5
 
cb77867d69d3c5931712aac486c980a42ee90745 rhodecode-0.0.1.1.5
 
008bdfdd95c8bd31ae6d89f76c75c1f49cbcd0bc rhodecode-0.0.1.1.5
 
c5af1d3c861fb36b156224e75c2f55a97f54657d rhodecode-0.0.1.1.6
 
7327a0d1584cf28d33e738048af1f6809d499451 rhodecode-0.0.1.1.7
 
bd102f45950f779995a1beae42b6eb099cdd27b3 rhodecode-0.0.1.1.7
 
c8974135732aa0ceb841cee6df66e29f089b4963 rhodecode-0.0.1.1.8
 
c252049af24cd98eef5f4143fa3abbff3c912e29 rhodecode-0.0.1.2.0
 
0b8fba8ab90b01f811a50e6e7384989cced21d38 rhodecode-0.0.1.2.1
 
22273bec00ba2fd860c60a9277d3d7229e288e18 rhodecode-0.0.1.2.2
 
1ff606a7858dbd8a5f70b3da5cc89524bd0d84f9 rhodecode-0.0.1.2.3
 
a7a282a902b207ce34e830d643c79b7ab52e3b35 rhodecode-0.0.1.2.4
 
b6b611e7722e754abebaae6e265cbb4c823d344d rhodecode-0.0.1.2.5
 
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
 
@@ -788,98 +788,99 @@ Regular users cannot specify owner param
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "create_repo"
 
    args:     {
 
                "repo_name" :        "<reponame>",
 
                "owner" :            "<onwer_name_or_id = Optional(=apiuser)>",
 
                "repo_type" :        "<repo_type> = Optional('hg')",
 
                "description" :      "<description> = Optional('')",
 
                "private" :          "<bool> = Optional(False)",
 
                "clone_uri" :        "<clone_uri> = Optional(None)",
 
                "landing_rev" :      "<landing_rev> = Optional('tip')",
 
                "enable_downloads":  "<bool> = Optional(False)",
 
                "enable_locking":    "<bool> = Optional(False)",
 
                "enable_statistics": "<bool> = Optional(False)",
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: {
 
              "msg": "Created new repository `<reponame>`",
 
              "repo": {
 
                "repo_id" :          "<repo_id>",
 
                "repo_name" :        "<reponame>"
 
                "repo_type" :        "<repo_type>",
 
                "clone_uri" :        "<clone_uri>",
 
                "private": :         "<bool>",
 
                "created_on" :       "<datetimecreated>",
 
                "description" :      "<description>",
 
                "landing_rev":       "<landing_rev>",
 
                "owner":             "<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>`",
 
              "success": true
 
            }
 
    error:  null
 

	
 

	
 
delete_repo
 
-----------
 

	
 
Delete a repository.
 
This command can only be executed using the api_key of a user with admin rights,
 
or that of a regular user with admin access to the repository.
 
When ``forks`` param is set it is possible to detach or delete forks of the deleted repository.
 

	
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "delete_repo"
 
    args:     {
 
                "repoid" : "<reponame or repo_id>",
 
                "forks"  : "`delete` or `detach` = Optional(None)"
 
              }
 

	
kallithea/__init__.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
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:
 
    from kallithea.brand import BRAND
 
except ImportError:
 
    pass
 

	
 
# Prefix for the ui and settings table names
 
DB_PREFIX = (BRAND + "_") if BRAND != "kallithea" else ""
 

	
 
# Users.extern_type and .extern_name value for local users
 
EXTERN_TYPE_INTERNAL = BRAND if BRAND != 'kallithea' else 'internal'
 

	
 
# db_migrate_version.repository_id value, same as kallithea/lib/dbmigrate/migrate.cfg
 
DB_MIGRATIONS = BRAND + "_db_migrations"
 

	
 
try:
 
    from kallithea.lib import get_current_revision
 
    _rev = get_current_revision(quiet=True)
 
    if _rev and len(VERSION) > 3:
 
        VERSION += ('%s' % _rev[0],)
 
except ImportError:
 
    pass
 

	
 
__version__ = ('.'.join((str(each) for each in VERSION[:3])))
 
__dbversion__ = 31  # defines current db version for migrations
 
__platform__ = platform.system()
kallithea/controllers/admin/my_account.py
Show inline comments
 
@@ -215,58 +215,58 @@ class MyAccountController(BaseController
 
            h.flash(_("Added email %s to user") % email, category='success')
 
        except formencode.Invalid, error:
 
            msg = error.error_dict['email']
 
            h.flash(msg, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during email saving'),
 
                    category='error')
 
        return redirect(url('my_account_emails'))
 

	
 
    def my_account_emails_delete(self):
 
        email_id = request.POST.get('del_email_id')
 
        user_model = UserModel()
 
        user_model.delete_extra_email(self.authuser.user_id, email_id)
 
        Session().commit()
 
        h.flash(_("Removed email from user"), category='success')
 
        return redirect(url('my_account_emails'))
 

	
 
    def my_account_api_keys(self):
 
        c.active = 'api_keys'
 
        self.__load_data()
 
        show_expired = True
 
        c.lifetime_values = [
 
            (str(-1), _('Forever')),
 
            (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
 
@@ -272,97 +272,97 @@ class UsersController(BaseController):
 
            'create_user_group_perm': umodel.has_perm(c.user,
 
                                                      'hg.usergroup.create.true'),
 
            'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'),
 
        })
 
        return htmlfill.render(
 
            render('admin/users/user_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def edit_api_keys(self, id):
 
        c.user = self._get_user_or_raise_if_default(id)
 
        c.active = 'api_keys'
 
        show_expired = True
 
        c.lifetime_values = [
 
            (str(-1), _('Forever')),
 
            (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(c.user.user_id,
 
                                                     show_expired=show_expired)
 
        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,
 
                                                      'hg.usergroup.create.true'),
 
            'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'),
 
        })
 
        return htmlfill.render(
 
            render('admin/users/user_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def update_perms(self, id):
 
        """PUT /users_perm/id: Update an existing item"""
 
        # url('user_perm', id=ID, method='put')
 
        user = self._get_user_or_raise_if_default(id)
 

	
 
        try:
 
            form = CustomDefaultPermissionsForm()()
 
            form_result = form.to_python(request.POST)
 

	
 
            inherit_perms = form_result['inherit_default_permissions']
 
            user.inherit_default_permissions = inherit_perms
 
            Session().add(user)
 
            user_model = UserModel()
 

	
 
            defs = UserToPerm.query()\
kallithea/controllers/api/api.py
Show inline comments
 
@@ -1511,96 +1511,107 @@ class ApiController(JSONRPCController):
 
                task_id = task.task_id
 
            # no commit, it's done in RepoModel, or async via celery
 
            return dict(
 
                msg="Created new repository `%s`" % (repo_name,),
 
                success=True,  # cannot return the repo data here since fork
 
                               # can be done async
 
                task=task_id
 
            )
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError(
 
                'failed to create repository `%s`' % (repo_name,))
 

	
 
    # permission check inside
 
    def update_repo(self, apiuser, repoid, name=Optional(None),
 
                    owner=Optional(OAttr('apiuser')),
 
                    group=Optional(None),
 
                    description=Optional(''), private=Optional(False),
 
                    clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
 
                    enable_statistics=Optional(False),
 
                    enable_locking=Optional(False),
 
                    enable_downloads=Optional(False)):
 

	
 
        """
 
        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),
 
                repository=repo.get_api_data()
 
            )
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError('failed to update repo `%s`' % repoid)
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
 
    def fork_repo(self, apiuser, repoid, fork_name,
 
                  owner=Optional(OAttr('apiuser')),
 
                  description=Optional(''), copy_permissions=Optional(False),
 
                  private=Optional(False), landing_rev=Optional('rev:tip')):
 
        """
 
        Creates a fork of given repo. In case of using celery this will
 
        immediately return success message, while fork is going to be created
 
        asynchronous. This command can be executed only using api_key belonging to
 
        user with admin rights or regular user that have fork permission, and at least
 
        read access to forking repository. Regular users cannot specify owner parameter.
 

	
 
        :param apiuser: filled automatically from apikey
 
        :type apiuser: AuthUser
 
        :param repoid: repository name or repository id
 
        :type repoid: str or int
 
        :param fork_name:
 
        :param owner:
 
@@ -1608,96 +1619,99 @@ class ApiController(JSONRPCController):
 
        :param copy_permissions:
 
        :param private:
 
        :param landing_rev:
 

	
 
        INPUT::
 

	
 
            id : <id_for_response>
 
            api_key : "<api_key>"
 
            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>`",
 
                      "success": true,
 
                      "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,
 
            )
 
            task = RepoModel().create_fork(form_data, cur_user=owner)
 
            # no commit, it's done in RepoModel, or async via celery
 
            from celery.result import BaseAsyncResult
 
            task_id = None
 
            if isinstance(task, BaseAsyncResult):
 
                task_id = task.task_id
 
            return dict(
 
                msg='Created fork of `%s` as `%s`' % (repo.repo_name,
 
                                                      fork_name),
 
                success=True,  # cannot return the repo data here since fork
 
                               # can be done async
 
                task=task_id
 
            )
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError(
 
                'failed to fork repository `%s` as `%s`' % (repo_name,
 
                                                            fork_name)
 
            )
 

	
 
    # permission check inside
 
    def delete_repo(self, apiuser, repoid, forks=Optional('')):
 
        """
 
        Deletes a repository. This command can be executed only using api_key belonging
kallithea/i18n/be/LC_MESSAGES/kallithea.po
Show inline comments
 
@@ -962,105 +962,105 @@ msgstr "Мэтавая група не можа быць такі ж"
 
msgid "User Group permissions updated"
 
msgstr "Прывілеі групы карыстачоў абноўлены"
 

	
 
#: kallithea/controllers/admin/user_groups.py:440
 
#: kallithea/controllers/admin/users.py:396
 
msgid "Updated permissions"
 
msgstr "Абноўлены прывілеі"
 

	
 
#: kallithea/controllers/admin/user_groups.py:444
 
#: kallithea/controllers/admin/users.py:400
 
msgid "An error occurred during permissions saving"
 
msgstr "Адбылася памылка пры захаванні прывілеяў"
 

	
 
#: kallithea/controllers/admin/users.py:132
 
#, python-format
 
msgid "Created user %s"
 
msgstr "Карыстач %s створаны"
 

	
 
#: kallithea/controllers/admin/users.py:147
 
#, python-format
 
msgid "Error occurred during creation of user %s"
 
msgstr "Адбылася памылка пры стварэнні карыстача %s"
 

	
 
#: kallithea/controllers/admin/users.py:186
 
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
 
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
 
#, python-format
 
msgid "Deleted branch: %s"
 
msgstr "Выдалена галінка: %s"
 

	
 
#: kallithea/lib/helpers.py:630
 
#, python-format
 
msgid "Created tag: %s"
 
msgstr "Створаны тэг: %s"
 

	
 
@@ -1996,97 +1996,97 @@ msgstr ""
 

	
 
#: kallithea/model/validators.py:500
 
msgid "Fork has to be the same type as parent"
 
msgstr "Тып форка будзе супадаць з бацькоўскім"
 

	
 
#: kallithea/model/validators.py:515
 
msgid "You don't have permissions to create repository in this group"
 
msgstr "У вас недастаткова мае рацыю для стварэння рэпазітароў у гэтай групе"
 

	
 
#: kallithea/model/validators.py:517
 
msgid "no permission to create repository in root location"
 
msgstr "недастаткова мае рацыю для стварэння рэпазітара ў каранёвым каталогу"
 

	
 
#: kallithea/model/validators.py:566
 
msgid "You don't have permissions to create a group in this location"
 
msgstr "У Вас недастаткова прывілеяў для стварэння групы ў гэтым месцы"
 

	
 
#: kallithea/model/validators.py:607
 
msgid "This username or user group name is not valid"
 
msgstr "Дадзенае імя карыстача ці групы карыстачоў недапушчальна"
 

	
 
#: kallithea/model/validators.py:700
 
msgid "This is not a valid path"
 
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
 
msgid "About"
 
msgstr "Пра праграму"
 

	
 
#: kallithea/templates/index.html:5
 
msgid "Dashboard"
 
msgstr "Панэль кіравання"
 

	
 
#: kallithea/templates/index_base.html:6
 
#: kallithea/templates/admin/my_account/my_account_repos.html:3
 
#: kallithea/templates/admin/my_account/my_account_watched.html:3
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:9
 
#: kallithea/templates/admin/repos/repos.html:9
 
#: kallithea/templates/admin/user_groups/user_groups.html:9
 
#: kallithea/templates/admin/users/users.html:9
 
#: kallithea/templates/bookmarks/bookmarks.html:9
 
#: kallithea/templates/branches/branches.html:9
 
#: kallithea/templates/journal/journal.html:9
 
#: kallithea/templates/journal/journal.html:48
 
#: kallithea/templates/journal/journal.html:49
 
#: kallithea/templates/tags/tags.html:9
 
msgid "quick filter..."
 
msgstr "фільтр..."
 

	
 
#: kallithea/templates/index_base.html:6
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 ""
 

	
 
#: kallithea/controllers/error.py:99
 
msgid "Unauthorized access to resource"
 
msgstr ""
 

	
 
#: kallithea/controllers/error.py:101
 
msgid "You don't have permission to view this page"
 
msgstr ""
 

	
 
#: kallithea/controllers/error.py:103
 
msgid "The resource could not be found"
 
msgstr ""
 

	
 
#: kallithea/controllers/error.py:105
 
msgid "The server encountered an unexpected condition which prevented it from fulfilling the request."
 
msgstr ""
 

	
 
#: kallithea/controllers/feed.py:55
 
#, python-format
 
msgid "Changes on %s repository"
 
msgstr ""
 

	
 
#: kallithea/controllers/feed.py:56
 
#, python-format
 
msgid "%s %s feed"
 
@@ -158,136 +158,136 @@ msgstr ""
 

	
 
#: kallithea/controllers/files.py:392
 
#, python-format
 
msgid "Edited file %s via Kallithea"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:408
 
msgid "No changes"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:417 kallithea/controllers/files.py:491
 
#, python-format
 
msgid "Successfully committed to %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:444
 
msgid "Added file via Kallithea"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:465
 
msgid "No content"
 
msgstr ""
 

	
 
#: kallithea/controllers/files.py:469
 
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"
 
msgstr ""
 

	
 
#: kallithea/controllers/login.py:188 kallithea/controllers/login.py:234
 
msgid "bad captcha"
 
msgstr ""
 

	
 
#: kallithea/controllers/login.py:194
 
msgid "You have successfully registered into Kallithea"
 
msgstr ""
 

	
 
#: kallithea/controllers/login.py:239
 
msgid "Your password reset link was sent"
 
msgstr ""
 

	
 
#: kallithea/controllers/login.py:260
 
msgid "Your password reset was successful, new password has been sent to your email"
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:130
 
#, python-format
 
msgid "%s (closed)"
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:158
 
@@ -368,102 +368,102 @@ msgstr ""
 
#: kallithea/controllers/pullrequests.py:518
 
msgid "Successfully deleted pull request"
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:577
 
#, python-format
 
msgid "This pull request has already been merged to %s."
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:579
 
msgid "This pull request has been closed and can not be updated."
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:597
 
#, python-format
 
msgid "This pull request can be updated with changes on %s:"
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:600
 
msgid "No changesets found for updating this pull request."
 
msgstr ""
 

	
 
#: kallithea/controllers/pullrequests.py:608
 
#, python-format
 
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 ""
 

	
 
#: kallithea/controllers/admin/gists.py:60
 
#: kallithea/controllers/admin/my_account.py:239
 
#: kallithea/controllers/admin/users.py:289
 
msgid "5 minutes"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/gists.py:61
 
#: kallithea/controllers/admin/my_account.py:240
 
#: kallithea/controllers/admin/users.py:290
 
msgid "1 hour"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/gists.py:62
 
#: kallithea/controllers/admin/my_account.py:241
 
#: kallithea/controllers/admin/users.py:291
 
msgid "1 day"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/gists.py:63
 
#: kallithea/controllers/admin/my_account.py:242
 
#: kallithea/controllers/admin/users.py:292
 
msgid "1 month"
 
msgstr ""
 
@@ -541,100 +541,100 @@ msgstr ""
 
#: kallithea/controllers/admin/my_account.py:254
 
#: kallithea/controllers/admin/users.py:314
 
msgid "Api key successfully created"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/my_account.py:266
 
#: kallithea/controllers/admin/users.py:330
 
msgid "Api key successfully reset"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/my_account.py:270
 
#: kallithea/controllers/admin/users.py:334
 
msgid "Api key successfully deleted"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:63
 
#: kallithea/controllers/admin/permissions.py:67
 
#: kallithea/controllers/admin/permissions.py:71
 
msgid "Read"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:64
 
#: kallithea/controllers/admin/permissions.py:68
 
#: kallithea/controllers/admin/permissions.py:72
 
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
 
#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1543
 
#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1564
 
#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1603
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1655
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1682 kallithea/model/db.py:1684
 
msgid "Manual activation of external account"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:84
 
#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1440
 
#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1486
 
#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1543
 
#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1544
 
#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1565
 
#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1604
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1656
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1683 kallithea/model/db.py:1685
 
msgid "Automatic activation of external account"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/permissions.py:88
 
#: kallithea/controllers/admin/permissions.py:91
 
#: kallithea/controllers/admin/permissions.py:96
 
#: kallithea/controllers/admin/permissions.py:99
 
@@ -656,240 +656,236 @@ msgid "Created repository group %s"
 
msgstr ""
 

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

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

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

	
 
#: kallithea/controllers/admin/repo_groups.py:289
 
#, python-format
 
msgid "This group contains %s repositories and cannot be deleted"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/repo_groups.py:296
 
#, python-format
 
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 ""
 

	
 
#: kallithea/controllers/admin/settings.py:327
 
msgid "Updated visualisation settings"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:332
 
msgid "Error occurred during updating visualisation settings"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:358
 
msgid "Please enter email address"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:373
 
msgid "Send email task created"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:404
 
msgid "Added new hook"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/settings.py:418
 
msgid "Updated hooks"
 
msgstr ""
 
@@ -949,281 +945,281 @@ msgid "An error occurred during permissi
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/users.py:132
 
#, python-format
 
msgid "Created user %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/users.py:147
 
#, python-format
 
msgid "Error occurred during creation of user %s"
 
msgstr ""
 

	
 
#: kallithea/controllers/admin/users.py:186
 
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"
 
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] ""
 

	
 
#: kallithea/lib/utils2.py:428
 
#, python-format
 
msgid "%d hour"
 
msgid_plural "%d hours"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/lib/utils2.py:429
 
#, python-format
 
msgid "%d minute"
 
msgid_plural "%d minutes"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/lib/utils2.py:430
 
#, python-format
 
msgid "%d second"
 
msgid_plural "%d seconds"
 
msgstr[0] ""
 
msgstr[1] ""
 

	
 
#: kallithea/lib/utils2.py:446
 
#, python-format
 
@@ -1640,177 +1636,177 @@ msgstr ""
 
#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1435
 
#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1481
 
#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1538
 
#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1539
 
#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1560
 
#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1599
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1651
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1678 kallithea/model/db.py:1680
 
msgid "Registration disabled"
 
msgstr ""
 

	
 
#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1436
 
#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1482
 
#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1539
 
#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1540
 
#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1561
 
#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1600
 
#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1652
 
#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1679 kallithea/model/db.py:1681
 
msgid "User Registration with manual account activation"
 
msgstr ""
 

	
 
#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1437
 
#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1483
 
#: 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 ""
 

	
 
#: kallithea/model/notification.py:252
 
#, python-format
 
msgid "%(user)s commented on changeset at %(when)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:253
 
#, python-format
 
msgid "%(user)s sent message at %(when)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:254
 
#, python-format
 
msgid "%(user)s mentioned you at %(when)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:255
 
#, python-format
 
msgid "%(user)s registered in Kallithea at %(when)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:256
 
#, python-format
 
msgid "%(user)s opened new pull request at %(when)s"
 
msgstr ""
 

	
 
#: kallithea/model/notification.py:257
 
#, python-format
 
msgid "%(user)s commented on pull request at %(when)s"
 
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 ""
 

	
 
#: kallithea/model/user.py:268
 
#, python-format
 
msgid "User \"%s\" still owns %s repository groups and cannot be removed. Switch owners or remove those repository groups: %s"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:275
 
#, python-format
 
msgid "User \"%s\" still owns %s user groups and cannot be removed. Switch owners or remove those user groups: %s"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:305
 
msgid "Password reset link"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:328
 
msgid "Your new password"
 
msgstr ""
 

	
 
#: kallithea/model/user.py:329
 
#, python-format
 
msgid "Your new Kallithea password:%s"
 
msgstr ""
 

	
 
@@ -1847,96 +1843,100 @@ msgstr ""
 

	
 
#: kallithea/model/validators.py:159
 
#, python-format
 
msgid "User group \"%(usergroup)s\" already exists"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:161
 
msgid "user group name may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:199
 
msgid "Cannot assign this group as parent"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:200
 
#, python-format
 
msgid "Group \"%(group_name)s\" already exists"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:202
 
#, python-format
 
msgid "Repository with name \"%(group_name)s\" already exists"
 
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 ""
 

	
 
#: kallithea/model/validators.py:475
 
msgid "Invalid clone URL, provide a valid clone http(s)/svn+http(s)/ssh URL"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:500
 
msgid "Fork has to be the same type as parent"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:515
 
msgid "You don't have permissions to create repository in this group"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:517
 
msgid "no permission to create repository in root location"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:566
 
msgid "You don't have permissions to create a group in this location"
 
msgstr ""
 

	
 
#: kallithea/model/validators.py:607
 
msgid "This username or user group name is not valid"
 
msgstr ""
 

	
 
@@ -2014,237 +2014,237 @@ msgstr ""
 

	
 
#: kallithea/templates/index_base.html:20
 
#: kallithea/templates/index_base.html:25
 
#: kallithea/templates/admin/repos/repo_add.html:5
 
#: kallithea/templates/admin/repos/repo_add.html:19
 
#: kallithea/templates/admin/repos/repos.html:22
 
msgid "Add Repository"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:22
 
#: kallithea/templates/index_base.html:27
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:5
 
#: kallithea/templates/admin/repo_groups/repo_group_add.html:13
 
#: kallithea/templates/admin/repo_groups/repo_groups.html:26
 
msgid "Add Repository Group"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:32
 
msgid "You have admin right to this group, and can edit it"
 
msgstr ""
 

	
 
#: kallithea/templates/index_base.html:32
 
msgid "Edit Repository Group"
 
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 ""
 

	
 
#: kallithea/templates/password_reset.html:46
 
msgid "Send Password Reset Email"
 
msgstr ""
 

	
 
#: kallithea/templates/password_reset.html:47
 
msgid "Password reset link will be sent to the email address matching your username."
 
msgstr ""
 

	
 
#: kallithea/templates/register.html:5 kallithea/templates/register.html:14
 
#: kallithea/templates/register.html:90
 
msgid "Sign Up"
 
msgstr ""
 

	
 
#: kallithea/templates/register.html:12
 
#, python-format
 
msgid "Sign Up to %s"
 
msgstr ""
 

	
 
#: kallithea/templates/register.html:42
 
msgid "Re-enter password"
 
msgstr ""
 

	
 
#: kallithea/templates/register.html:51
 
@@ -2352,790 +2352,781 @@ msgstr ""
 
#: kallithea/templates/admin/admin_log.html:9
 
msgid "From IP"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/admin_log.html:63
 
msgid "No actions yet"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:5
 
msgid "Authentication Settings"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:11
 
#: kallithea/templates/base/base.html:65
 
msgid "Authentication"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:28
 
msgid "Authentication Plugins"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/auth/auth_settings.html:31
 
msgid "Enabled Plugins"
 
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 ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:61
 
#: kallithea/templates/admin/gists/edit.html:63
 
#: kallithea/templates/admin/gists/index.html:57
 
#: kallithea/templates/admin/gists/index.html:59
 
#: kallithea/templates/admin/gists/show.html:47
 
#: kallithea/templates/admin/gists/show.html:49
 
msgid "Expires"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:61
 
#: kallithea/templates/admin/gists/index.html:57
 
#: kallithea/templates/admin/gists/show.html:47
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:8
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:27
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:8
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:27
 
msgid "never"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/gists/edit.html:145
 
msgid "Update Gist"
 
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"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account.html:41
 
msgid "My Permissions"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:6
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:6
 
msgid "Built-in"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:8
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:27
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:32
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:8
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:27
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:32
 
msgid "expires"
 
msgstr ""
 

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

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:15
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:15
 
msgid "reset"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_api_keys.html:30
 
#: kallithea/templates/admin/users/user_edit_api_keys.html:30
 
msgid "expired"
 
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
 
msgid "Current password"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_password.html:16
 
#: kallithea/templates/admin/users/user_edit_profile.html:69
 
msgid "New password"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_password.html:25
 
msgid "Confirm new password"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_profile.html:11
 
msgid "Change your avatar at"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_profile.html:12
 
#: kallithea/templates/admin/users/user_edit_profile.html:9
 
msgid "Using"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_profile.html:14
 
#: kallithea/templates/admin/users/user_edit_profile.html:11
 
msgid "Avatars are disabled"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_profile.html:15
 
msgid "Missing email, please update your user email address."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_profile.html:16
 
#: kallithea/templates/admin/users/user_edit_profile.html:15
 
msgid "current IP"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_profile.html:28
 
msgid "Your user is in an external Source of Record; some details cannot be managed here"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_repos.html:1
 
msgid "Repositories You Own"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/my_account/my_account_repos.html:59
 
#: kallithea/templates/admin/my_account/my_account_watched.html:59
 
#: kallithea/templates/base/root.html:46
 
#: kallithea/templates/bookmarks/bookmarks.html:81
 
#: kallithea/templates/branches/branches.html:81
 
#: 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 ""
 

	
 
#: kallithea/templates/admin/permissions/permissions.html:30
 
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 ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:9
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:10
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:9
 
#: kallithea/templates/base/perms_summary.html:16
 
msgid "write"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:10
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:11
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:10
 
#: kallithea/templates/base/perms_summary.html:17
 
msgid "admin"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:11
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:12
 
#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:11
 
msgid "user/user group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:28
 
#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:45
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:24
 
#: kallithea/templates/admin/repos/repo_edit_permissions.html:37
 
@@ -3240,310 +3231,310 @@ msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_add_base.html:68
 
#: kallithea/templates/admin/repos/repo_edit_settings.html:51
 
#: kallithea/templates/forks/fork.html:58
 
msgid "Landing revision"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_add_base.html:72
 
msgid "Default revision for files page, downloads, full text search index and readme generation"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_creating.html:9
 
#, python-format
 
msgid "%s Creating Repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_creating.html:13
 
msgid "Creating repository"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_creating.html:27
 
#, python-format
 
msgid "Repository \"%(repo_name)s\" is being created, you will be redirected when this process is finished.repo_name"
 
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
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:6
 
msgid "Key"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_caches.html:17
 
#: kallithea/templates/admin/user_groups/user_group_add.html:49
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:24
 
#: kallithea/templates/admin/user_groups/user_groups.html:49
 
#: kallithea/templates/admin/users/user_add.html:88
 
#: kallithea/templates/admin/users/user_edit_profile.html:105
 
#: kallithea/templates/admin/users/users.html:54
 
msgid "Active"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:5
 
msgid "Label"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:19
 
#, python-format
 
msgid "Confirm to delete this field: %s"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:33
 
msgid "New field key"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:41
 
msgid "New field label"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:44
 
msgid "Enter short label"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:50
 
msgid "New field description"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:53
 
msgid "Enter description of a field"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_fields.html:66
 
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
 
msgid "Processed progress"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_statistics.html:10
 
msgid "Reset Statistics"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repo_edit_statistics.html:10
 
msgid "Confirm to remove current statistics."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repos.html:5
 
msgid "Repositories Administration"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/repos/repos.html:51
 
msgid "State"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/settings/settings.html:5
 
msgid "Settings Administration"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/settings/settings.html:27
 
@@ -3912,455 +3903,463 @@ msgid "Stylify recognised meta tags:"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/settings/settings_visual.html:111
 
msgid "Parses meta tags from the repository description field and turns them into colored tags."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_add.html:5
 
msgid "Add user group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_add.html:10
 
#: kallithea/templates/admin/user_groups/user_group_edit.html:11
 
#: kallithea/templates/base/base.html:63 kallithea/templates/base/base.html:83
 
msgid "User Groups"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_add.html:12
 
#: kallithea/templates/admin/user_groups/user_groups.html:25
 
msgid "Add User Group"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/user_groups/user_group_add.html:44
 
#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:19
 
msgid "Short, optional description for this user group."
 
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 ""
 

	
 
#: kallithea/templates/admin/user_groups/user_groups.html:10
 
msgid "user groups"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_add.html:5
 
msgid "Add user"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_add.html:10
 
#: kallithea/templates/admin/users/user_edit.html:11
 
#: kallithea/templates/admin/users/users.html:10
 
#: kallithea/templates/base/base.html:62
 
msgid "Users"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_add.html:12
 
#: kallithea/templates/admin/users/users.html:24
 
msgid "Add User"
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_add.html:50
 
msgid "Password confirmation"
 
msgstr ""
 

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

	
 
#: kallithea/templates/admin/users/user_edit.html:32
 
msgid "Default Permissions"
 
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."
 
msgstr ""
 

	
 
#: kallithea/templates/admin/users/user_edit_profile.html:60
 
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 ""
 

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

	
 
#: kallithea/templates/base/default_perms_box.html:52
 
msgid "Fork repositories"
 
msgstr ""
 

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

	
 
#: kallithea/templates/base/perms_summary.html:13
 
msgid "show"
 
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
 
msgid "Search truncated"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:30
 
msgid "No matching files"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:31
 
#: kallithea/templates/pullrequests/pullrequest_show_all.html:30
 
msgid "Open New Pull Request"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:32
 
msgid "Open New Pull Request for Selected Changesets"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:33
 
msgid "Show Selected Changesets __S &rarr; __E"
 
msgstr ""
 

	
 
#: kallithea/templates/base/root.html:34
 
msgid "Show Selected Changeset __S"
 
msgstr ""
 

	
 
@@ -4519,111 +4518,111 @@ msgid "Branch %s"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog.html:290
 
msgid "There are no changes yet"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_details.html:4
 
#: kallithea/templates/changeset/changeset.html:77
 
msgid "Removed"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_details.html:5
 
#: kallithea/templates/changeset/changeset.html:78
 
msgid "Changed"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_details.html:6
 
#: kallithea/templates/changeset/changeset.html:79
 
#: kallithea/templates/changeset/diff_block.html:80
 
msgid "Added"
 
msgstr ""
 

	
 
#: kallithea/templates/changelog/changelog_details.html:8
 
#: kallithea/templates/changelog/changelog_details.html:9
 
#: 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
 
#: kallithea/templates/files/diff_2way.html:49
 
msgid "Raw diff"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:57
 
msgid "Patch diff"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:60
 
#: kallithea/templates/changeset/diff_block.html:30
 
#: kallithea/templates/files/diff_2way.html:52
 
msgid "Download diff"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:89
 
#: kallithea/templates/changeset/changeset_range.html:88
 
msgid "merge"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:123
 
msgid "Grafted from:"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:129
 
@@ -4651,152 +4650,155 @@ msgstr[1] ""
 
#: kallithea/templates/changeset/changeset.html:153
 
#: kallithea/templates/changeset/changeset.html:166
 
#: kallithea/templates/pullrequests/pullrequest_show.html:328
 
#: kallithea/templates/pullrequests/pullrequest_show.html:351
 
msgid "Show full diff anyway"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset.html:224
 
#: kallithea/templates/changeset/changeset.html:261
 
msgid "no revisions"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:24
 
msgid "Status change from pull request"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:25
 
#: kallithea/templates/changeset/changeset_file_comment.html:28
 
msgid "No title"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:27
 
msgid "Comment from pull request"
 
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."
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:98
 
msgid "Login now"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/changeset_file_comment.html:102
 
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 ""
 

	
 
#: kallithea/templates/changeset/diff_block.html:21
 
#: kallithea/templates/files/diff_2way.html:43
 
msgid "Show full diff for this file"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/diff_block.html:24
 
#: kallithea/templates/changeset/diff_block.html:99
 
#: kallithea/templates/files/diff_2way.html:46
 
msgid "Show full side-by-side diff for this file"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/diff_block.html:38
 
msgid "Show inline comments"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/diff_block.html:87
 
msgid "Deleted"
 
msgstr ""
 

	
 
#: kallithea/templates/changeset/diff_block.html:90
 
msgid "Renamed"
 
msgstr ""
 

	
 
@@ -4826,129 +4828,121 @@ msgid "is"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_cs.html:84
 
#, python-format
 
msgid "%s changesets"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_cs.html:85
 
msgid "behind"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:6
 
#: kallithea/templates/compare/compare_diff.html:8
 
#, python-format
 
msgid "%s Compare"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:13
 
#: kallithea/templates/compare/compare_diff.html:35
 
msgid "Compare Revisions"
 
msgstr ""
 

	
 
#: kallithea/templates/compare/compare_diff.html:33
 
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"
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/password_reset.html:6
 
msgid "We received a request to create a new password for your account."
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/password_reset.html:7
 
msgid "You can generate it by clicking following URL"
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/password_reset.html:10
 
msgid "Please ignore this email if you did not request a new password ."
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/pull_request.html:5
 
#, python-format
 
msgid "%s mentioned you on %s pull request \"%s\""
 
msgstr ""
 

	
 
#: kallithea/templates/email_templates/pull_request.html:7
 
#, python-format
 
msgid "%s requested your review of %s pull request \"%s\""
 
msgstr ""
 

	
 
@@ -5135,174 +5129,174 @@ msgstr ""
 
#: kallithea/templates/files/files_source.html:30
 
msgid "Show Source"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_source.html:38
 
#, python-format
 
msgid "Edit on Branch:%s"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_source.html:41
 
msgid "Editing binary files not allowed"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_source.html:44
 
msgid "Editing files allowed only when on branch head revision"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_source.html:45
 
msgid "Deleting files allowed only when on branch head revision"
 
msgstr ""
 

	
 
#: kallithea/templates/files/files_source.html:63
 
#, python-format
 
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 ""
 

	
 
#: kallithea/templates/forks/fork.html:77
 
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
 
#: kallithea/templates/pullrequests/pullrequest_show.html:54
 
msgid "Title"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:34
 
msgid "Summarize the changes - or leave empty"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:43
 
#: kallithea/templates/pullrequests/pullrequest_show.html:66
 
msgid "Write a short description on this pull request"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:49
 
msgid "Changeset flow"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:56
 
msgid "Origin repository"
 
msgstr ""
 

	
 
#: kallithea/templates/pullrequests/pullrequest.html:72
 
msgid "Destination repository"
 
msgstr ""
 
@@ -5523,189 +5517,189 @@ msgstr ""
 
#: kallithea/templates/search/search.html:16
 
msgid "Search in All Repositories"
 
msgstr ""
 

	
 
#: kallithea/templates/search/search.html:50
 
msgid "Search term"
 
msgstr ""
 

	
 
#: kallithea/templates/search/search.html:62
 
msgid "Search in"
 
msgstr ""
 

	
 
#: kallithea/templates/search/search.html:65
 
msgid "File contents"
 
msgstr ""
 

	
 
#: kallithea/templates/search/search.html:66
 
msgid "Commit messages"
 
msgstr ""
 

	
 
#: kallithea/templates/search/search.html:67
 
msgid "File names"
 
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
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
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, \
 
    RepoGroup, UserGroupRepoGroupToPerm, UserIpMap, UserGroupUserGroupToPerm, \
 
    UserGroup, UserApiKeys
 

	
 
from kallithea.lib.utils2 import safe_unicode, aslist
 
from kallithea.lib.utils import get_repo_slug, get_repo_group_slug, \
 
    get_user_group_slug, conditional_cache
 
from kallithea.lib.caching_query import FromCache
 

	
 

	
 
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):
 
        """
 
        Checks matching password with it's hashed value, runs different
 
        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()}
 

	
 
    def _choose_perm(new_perm, cur_perm):
 
        new_perm_val = PERM_WEIGHTS[new_perm]
 
        cur_perm_val = PERM_WEIGHTS[cur_perm]
 
        if algo == 'higherwin':
 
            if new_perm_val > cur_perm_val:
 
                return new_perm
 
            return cur_perm
 
        elif algo == 'lowerwin':
 
            if new_perm_val < cur_perm_val:
 
                return new_perm
 
            return cur_perm
 

	
 
    #======================================================================
 
    # fetch default permissions
 
    #======================================================================
 
    default_user = User.get_by_username('default', cache=True)
 
    default_user_id = default_user.user_id
 

	
 
    default_repo_perms = Permission.get_default_perms(default_user_id)
 
    default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
 
    default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
 

	
 
    if user_is_admin:
 
@@ -230,233 +231,245 @@ def _cached_perms_data(user_id, user_is_
 
    for perm in default_global_perms:
 
        permissions[GLOBAL].add(perm.permission.permission_name)
 

	
 
    # defaults for repositories, taken from default user
 
    for perm in default_repo_perms:
 
        r_k = perm.UserRepoToPerm.repository.repo_name
 
        if perm.Repository.private and not (perm.Repository.user_id == uid):
 
            # disable defaults for private repos,
 
            p = 'repository.none'
 
        elif perm.Repository.user_id == uid:
 
            # set admin if owner
 
            p = 'repository.admin'
 
        else:
 
            p = perm.Permission.permission_name
 

	
 
        permissions[RK][r_k] = p
 

	
 
    # defaults for repository groups taken from default user permission
 
    # on given group
 
    for perm in default_repo_groups_perms:
 
        rg_k = perm.UserRepoGroupToPerm.group.group_name
 
        p = perm.Permission.permission_name
 
        permissions[GK][rg_k] = p
 

	
 
    # 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()
 

	
 
    if not user_inherit_default_permissions:
 
        # NEED TO IGNORE all configurable permissions and
 
        # 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:
 
        r_k = perm.UserRepoToPerm.repository.repo_name
 
        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
 

	
 

	
 
def allowed_api_access(controller_name, whitelist=None, api_key=None):
 
    """
 
    Check if given controller_name is in whitelist API access
 
    """
 
    if not whitelist:
 
        from kallithea import CONFIG
 
        whitelist = aslist(CONFIG.get('api_access_controllers_whitelist'),
 
                           sep=',')
 
        log.debug('whitelist of API access is: %s' % (whitelist))
 
    api_access_valid = controller_name in whitelist
 
    if api_access_valid:
 
        log.debug('controller:%s is in API whitelist' % (controller_name))
 
    else:
 
        msg = 'controller: %s is *NOT* in API whitelist' % (controller_name)
 
        if api_key:
 
            #if we use API key and don't have access it's a warning
 
            log.warning(msg)
 
        else:
 
            log.debug(msg)
 
    return api_access_valid
 

	
 

	
 
class AuthUser(object):
kallithea/lib/dbmigrate/schema/db_1_2_0.py
Show inline comments
 
@@ -291,97 +291,97 @@ class User(Base, BaseModel):
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.name, self.lastname, self.email)
 

	
 
    @property
 
    def short_contact(self):
 
        return '%s %s' % (self.name, self.lastname)
 

	
 
    @property
 
    def is_admin(self):
 
        return self.admin
 

	
 
    def __repr__(self):
 
        try:
 
            return "<%s('id:%s:%s')>" % (self.__class__.__name__,
 
                                             self.user_id, self.username)
 
        except:
 
            return self.__class__.__name__
 

	
 
    @classmethod
 
    def get_by_username(cls, username, case_insensitive=False):
 
        if case_insensitive:
 
            return Session.query(cls).filter(cls.username.ilike(username)).scalar()
 
        else:
 
            return Session.query(cls).filter(cls.username == username).scalar()
 

	
 
    @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')
 
    repository = relationship('Repository')
 

	
 

	
 
class UserGroup(Base, BaseModel):
 
    __tablename__ = 'users_groups'
 
    __table_args__ = {'extend_existing':True}
 

	
 
    users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
 

	
 
    members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
 

	
 
    def __repr__(self):
 
        return '<userGroup(%s)>' % (self.users_group_name)
 

	
 
    @classmethod
 
    def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
 
        if case_insensitive:
 
            gr = cls.query()\
 
                .filter(cls.users_group_name.ilike(group_name))
 
        else:
 
            gr = cls.query()\
 
                .filter(cls.users_group_name == group_name)
kallithea/lib/helpers.py
Show inline comments
 
@@ -306,112 +306,112 @@ def pygmentize_annotation(repo_name, fil
 

	
 
        :returns: RGB tuple
 
        """
 

	
 
        def hsv_to_rgb(h, s, v):
 
            if s == 0.0:
 
                return v, v, v
 
            i = int(h * 6.0)  # XXX assume int() truncates!
 
            f = (h * 6.0) - i
 
            p = v * (1.0 - s)
 
            q = v * (1.0 - s * f)
 
            t = v * (1.0 - s * (1.0 - f))
 
            i = i % 6
 
            if i == 0:
 
                return v, t, p
 
            if i == 1:
 
                return q, v, p
 
            if i == 2:
 
                return p, v, t
 
            if i == 3:
 
                return p, q, v
 
            if i == 4:
 
                return t, p, v
 
            if i == 5:
 
                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.
 
    """
 

	
 
    def __init__(self, category, message):
 
        self.category = category
 
        self.message = message
 

	
 
    def __str__(self):
 
        return self.message
 

	
 
    __unicode__ = __str__
 

	
 
    def __html__(self):
 
        return escape(safe_unicode(self.message))
 

	
 
class Flash(_Flash):
 

	
 
    def __call__(self, message, category=None, ignore_duplicate=False, logf=None):
 
        """
 
        Show a message to the user _and_ log it through the specified function
 

	
 
        category: notice (default), warning, error, success
 
        logf: a custom log function - such as log.debug
 

	
 
        logf defaults to log.info, unless category equals 'success', in which
 
        case logf defaults to log.debug.
 
        """
kallithea/lib/utils2.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
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(']')
 

	
 
            for suffix in s[start + 1:stop]:
 
                exts.append(s[:s.find('[')] + suffix)
 
            return map(lower, exts)
 
        else:
 
            return map(lower, [s])
 

	
 
    for lx, t in sorted(lexers.LEXERS.items()):
 
        m = map(__clean, t[-2])
 
        if m:
 
            m = reduce(lambda x, y: x + y, m)
 
            for ext in m:
 
                desc = lx.replace('Lexer', '')
 
                d[ext].append(desc)
 

	
 
    return dict(d)
 

	
 

	
 
def str2bool(_str):
 
    """
 
    returs True/False value from given string, it tries to translate the
 
    string into boolean
 

	
 
    :param _str: string value to translate into boolean
 
@@ -116,113 +118,101 @@ def aslist(obj, sep=None, strip=True):
 

	
 
def convert_line_endings(line, mode):
 
    """
 
    Converts a given line  "line end" according to given mode
 

	
 
    Available modes are::
 
        0 - Unix
 
        1 - Mac
 
        2 - DOS
 

	
 
    :param line: given line to convert
 
    :param mode: mode to convert to
 
    :rtype: str
 
    :return: converted line according to mode
 
    """
 
    from string import replace
 

	
 
    if mode == 0:
 
            line = replace(line, '\r\n', '\n')
 
            line = replace(line, '\r', '\n')
 
    elif mode == 1:
 
            line = replace(line, '\r\n', '\r')
 
            line = replace(line, '\n', '\r')
 
    elif mode == 2:
 
            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
 
    by chardet library if it fails fallback to unicode with errors replaced
 

	
 
    :param str_: string to decode
 
    :rtype: unicode
 
    :returns: unicode object
 
    """
 
    if isinstance(str_, unicode):
 
        return str_
 

	
 
    if not from_encoding:
 
        import kallithea
 
        DEFAULT_ENCODINGS = aslist(kallithea.CONFIG.get('default_encoding',
 
                                                        'utf8'), sep=',')
 
        from_encoding = DEFAULT_ENCODINGS
 

	
 
    if not isinstance(from_encoding, (list, tuple)):
 
        from_encoding = [from_encoding]
 

	
 
    try:
 
        return unicode(str_)
 
    except UnicodeDecodeError:
 
        pass
 

	
 
    for enc in from_encoding:
kallithea/model/api_key.py
Show inline comments
 
@@ -5,79 +5,79 @@
 
# (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.model.api_key
 
~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
API key model for Kallithea
 

	
 
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: Sep 8, 2013
 
: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 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()\
 
            .filter(UserApiKeys.user_id == user.user_id)
 
        if not show_expired:
 
            user_api_keys = user_api_keys\
 
                .filter(or_(UserApiKeys.expires == -1,
 
                            UserApiKeys.expires >= time.time()))
 
        return user_api_keys
kallithea/model/db.py
Show inline comments
 
@@ -510,96 +510,99 @@ class User(Base, BaseModel):
 
        if not self._user_data:
 
            return {}
 

	
 
        try:
 
            return json.loads(self._user_data)
 
        except TypeError:
 
            return {}
 

	
 
    @user_data.setter
 
    def user_data(self, val):
 
        try:
 
            self._user_data = json.dumps(val)
 
        except Exception:
 
            log.error(traceback.format_exc())
 

	
 
    def __unicode__(self):
 
        return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
 
                                      self.user_id, self.username)
 

	
 
    @classmethod
 
    def get_or_404(cls, id_, allow_default=True):
 
        '''
 
        Overridden version of BaseModel.get_or_404, with an extra check on
 
        the default user.
 
        '''
 
        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)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_email_key_%s" % email))
 

	
 
        ret = q.scalar()
 
        if ret is None:
 
            q = UserEmailMap.query()
 
            # try fetching in alternate email map
 
            if case_insensitive:
 
                q = q.filter(UserEmailMap.email.ilike(email))
 
            else:
 
                q = q.filter(UserEmailMap.email == email)
 
            q = q.options(joinedload(UserEmailMap.user))
 
            if cache:
 
                q = q.options(FromCache("sql_cache_short",
 
                                        "get_email_map_key_%s" % email))
 
            ret = getattr(q.scalar(), 'user', None)
 

	
 
        return ret
 

	
 
    @classmethod
 
    def get_from_cs_author(cls, author):
 
        """
 
@@ -1617,132 +1620,132 @@ class RepoGroup(Base, BaseModel):
 
        """
 
        Recursive return all groups, with repositories in those groups
 
        """
 
        return self._recursive_objects()
 

	
 
    def recursive_groups(self):
 
        """
 
        Returns all children groups for this group including children of children
 
        """
 
        return self._recursive_objects(include_repos=False)
 

	
 
    def get_new_name(self, group_name):
 
        """
 
        returns new full group name based on parent and new name
 

	
 
        :param group_name:
 
        """
 
        path_prefix = (self.parent_group.full_path_splitted if
 
                       self.parent_group else [])
 
        return RepoGroup.url_sep().join(path_prefix + [group_name])
 

	
 
    def get_api_data(self):
 
        """
 
        Common function for generating api data
 

	
 
        """
 
        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,
 
        'group.read': 1,
 
        'group.write': 3,
 
        'group.admin': 4,
 

	
 
        'usergroup.none': 0,
 
        'usergroup.read': 1,
 
        'usergroup.write': 3,
 
        'usergroup.admin': 4,
 
        'hg.repogroup.create.false': 0,
 
        'hg.repogroup.create.true': 1,
 

	
 
        'hg.usergroup.create.false': 0,
 
        'hg.usergroup.create.true': 1,
 

	
 
        'hg.fork.none': 0,
 
        'hg.fork.repository': 1,
 
        'hg.create.none': 0,
 
        'hg.create.repository': 1
 
    }
 

	
 
    permission_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
 
    permission_name = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
 
    permission_longname = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
 

	
kallithea/model/user.py
Show inline comments
 
@@ -51,159 +51,159 @@ class UserModel(BaseModel):
 

	
 
    def get(self, user_id, cache=False):
 
        user = self.sa.query(User)
 
        if cache:
 
            user = user.options(FromCache("sql_cache_short",
 
                                          "get_user_%s" % user_id))
 
        return user.get(user_id)
 

	
 
    def get_user(self, user):
 
        return self._get_user(user)
 

	
 
    def get_by_username(self, username, cache=False, case_insensitive=False):
 
        return User.get_by_username(username, case_insensitive, cache)
 

	
 
    def get_by_email(self, email, cache=False, case_insensitive=False):
 
        return User.get_by_email(email, case_insensitive, cache)
 

	
 
    def get_by_api_key(self, api_key, cache=False):
 
        return User.get_by_api_key(api_key, cache)
 

	
 
    def create(self, form_data, cur_user=None):
 
        if not cur_user:
 
            cur_user = getattr(get_current_authuser(), 'username', None)
 

	
 
        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:
 
            cur_user = getattr(get_current_authuser(), 'username', None)
 

	
 
        from kallithea.lib.auth import get_crypt_password, check_password
 
        from kallithea.lib.hooks import log_create_user, \
 
            check_allowed_create_user
 
        user_data = {
 
            'username': username, 'password': password,
 
            'email': email, 'firstname': firstname, 'lastname': lastname,
 
            'active': active, 'admin': admin
 
        }
 
        # raises UserCreationError if it's not allowed
 
        check_allowed_create_user(user_data, cur_user)
 

	
 
        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
 
        form_data['extern_name'] = EXTERN_TYPE_INTERNAL
 
        form_data['extern_type'] = EXTERN_TYPE_INTERNAL
 
        new_user = self.create(form_data)
 

	
 
        self.sa.add(new_user)
 
        self.sa.flush()
 

	
 
        # notification to admins
 
        subject = _('New user registration')
 
        body = (
 
            'New user registration\n'
 
            '---------------------\n'
 
            '- Username: {user.username}\n'
 
            '- Full Name: {user.full_name}\n'
 
            '- Email: {user.email}\n'
 
            ).format(user=new_user)
 
        edit_url = h.canonical_url('edit_user', id=new_user.user_id)
 
        email_kwargs = {
 
            'registered_user_url': edit_url,
 
            'new_username': new_user.username}
 
        NotificationModel().create(created_by=new_user, subject=subject,
 
                                   body=body, recipients=None,
 
                                   type_=Notification.TYPE_REGISTRATION,
 
                                   email_kwargs=email_kwargs)
kallithea/model/user_group.py
Show inline comments
 
@@ -201,97 +201,97 @@ class UserGroupModel(BaseModel):
 
    def remove_user_from_group(self, user_group, user):
 
        user_group = self._get_user_group(user_group)
 
        user = self._get_user(user)
 

	
 
        user_group_member = None
 
        for m in user_group.members:
 
            if m.user.user_id == user.user_id:
 
                # Found this user's membership row
 
                user_group_member = m
 
                break
 

	
 
        if user_group_member:
 
            try:
 
                self.sa.delete(user_group_member)
 
                return True
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                raise
 
        else:
 
            # User isn't in that group
 
            return False
 

	
 
    def has_perm(self, user_group, perm):
 
        user_group = self._get_user_group(user_group)
 
        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)
 

	
 
        # check if we have that permission already
 
        obj = self.sa.query(UserUserGroupToPerm)\
 
            .filter(UserUserGroupToPerm.user == user)\
 
            .filter(UserUserGroupToPerm.user_group == user_group)\
 
            .scalar()
 
        if obj is None:
 
            # create new !
 
            obj = UserUserGroupToPerm()
 
        obj.user_group = user_group
 
        obj.user = user
 
        obj.permission = permission
 
        self.sa.add(obj)
 
        log.debug('Granted perm %s to %s on %s' % (perm, user, user_group))
 
        return obj
 

	
 
    def revoke_user_permission(self, user_group, user):
 
        """
 
        Revoke permission for user on given repository group
 

	
 
        :param user_group: Instance of RepoGroup, repositories_group_id,
 
            or repositories_group name
 
        :param user: Instance of User, user_id or username
 
        """
 

	
kallithea/public/js/base.js
Show inline comments
 
@@ -464,115 +464,121 @@ var toggleFollowingRepo = function(targe
 
    $.post(TOGGLE_FOLLOW_URL, args, function(data){
 
            _onSuccessFollow(target);
 
        });
 
    return false;
 
};
 

	
 
var showRepoSize = function(target, repo_name){
 
    var args = '_authentication_token=' + _authentication_token;
 

	
 
    if(!$("#" + target).hasClass('loaded')){
 
        $("#" + target).html(_TM['Loading ...']);
 
        var url = pyroutes.url('repo_size', {"repo_name":repo_name});
 
        $.post(url, args, function(data) {
 
            $("#" + target).html(data);
 
            $("#" + target).addClass('loaded');
 
        });
 
    }
 
    return false;
 
};
 

	
 
/**
 
 * tooltips
 
 */
 

	
 
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');
 
        $tipBox.html(tipText);
 
        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.
 
 * @param display_element function that takes current node from nodes and
 
 *    does hide or show based on the node
 
 */
 
var q_filter = (function() {
 
    var _namespace = {};
 
    var namespace = function (target) {
 
        if (!(target in _namespace)) {
 
            _namespace[target] = {};
 
        }
 
        return _namespace[target];
 
    };
 
    return function (target, $nodes, display_element) {
 
        var $nodes = $nodes;
 
        var $q_filter_field = $('#' + target);
 
        var F = namespace(target);
 

	
 
        $q_filter_field.keyup(function (e) {
 
            clearTimeout(F.filterTimeout);
 
            F.filterTimeout = setTimeout(F.updateFilter, 600);
 
        });
 

	
 
        F.filterTimeout = null;
 

	
 
        F.updateFilter = function () {
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>
 
${h.end_form()}
kallithea/templates/data_table/_dt_elements.html
Show inline comments
 
@@ -48,97 +48,97 @@
 
       </span>
 
       <span>${_('Settings')}</span>
 
       </a>
 
    </li>
 
  </ul>
 
</%def>
 

	
 
<%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
 
    <%
 
    def get_name(name,short_name=short_name):
 
      if short_name:
 
        return name.split('/')[-1]
 
      else:
 
        return name
 
    %>
 
  <div class="dt_repo ${'dt_repo_pending' if rstate == 'repo_state_pending' else ''}">
 
    ##NAME
 
    <a href="${h.url('edit_repo' if admin else 'summary_home', repo_name=name)}">
 

	
 
    ##TYPE OF REPO
 
    ${base.repotag(rtype)}
 

	
 
    ##PRIVATE/PUBLIC
 
    %if private and c.visual.show_private_icon:
 
      <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>
 
    <div style="float:left; margin-right:5px;" class="grid_edit">
 
      <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
 
        <i class="icon-pencil"></i> ${h.submit('edit_%s' % repo_name,_('Edit'),class_="action_button")}
 
      </a>
 
    </div>
 
    <div style="float:left" class="grid_delete">
 
      ${h.form(h.url('repo', repo_name=repo_name),method='delete')}
 
        <i class="icon-minus-circled" style="color:#FF4444"></i>
 
        ${h.submit('remove_%s' % repo_name,_('Delete'),class_="action_button",
 
        onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
 
      ${h.end_form()}
 
    </div>
 
  </div>
 
</%def>
 

	
 
<%def name="repo_state(repo_state)">
 
  <div>
 
    %if repo_state == 'repo_state_pending':
 
        <div class="btn btn-mini btn-info disabled">${_('Creating')}</div>
 
    %elif repo_state == 'repo_state_created':
 
        <div class="btn btn-mini btn-success disabled">${_('Created')}</div>
 
    %else:
 
        <div class="btn btn-mini btn-danger disabled" title="${repo_state}">invalid</div>
 
    %endif
kallithea/tests/api/api_base.py
Show inline comments
 
@@ -1133,96 +1133,155 @@ class _BaseTestApi(object):
 
                'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
 
                'repository': repo.get_api_data()
 
            }
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 
            if changing_attr == 'repo_group':
 
                fixture.destroy_repo_group(updates['group'])
 

	
 
    def test_api_update_repo_repo_group_does_not_exist(self):
 
        repo_name = 'admin_owned'
 
        fixture.create_repo(repo_name)
 
        updates = {'group': 'test_group_for_update'}
 
        id_, params = _build_data(self.apikey, 'update_repo',
 
                                  repoid=repo_name, **updates)
 
        response = api_call(self, params)
 
        try:
 
            expected = 'repository group `%s` does not exist' % updates['group']
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    def test_api_update_repo_regular_user_not_allowed(self):
 
        repo_name = 'admin_owned'
 
        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, )
 
        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_no_permission(self):
 
        repo_name = 'api_delete_me'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'delete_repo',
 
                                      repoid=repo_name, )
 
            response = api_call(self, params)
 
            expected = 'repository `%s` does not exist' % (repo_name)
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
@@ -1258,96 +1317,117 @@ class _BaseTestApi(object):
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 
        fixture.destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_non_admin(self):
 
        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)
 

	
 
        ret = {
 
            'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
 
                                                     fork_name),
 
            'success': True,
 
            'task': None,
 
        }
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 
        fixture.destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_non_admin_specify_owner(self):
 
        fork_name = 'api-repo-fork'
 
        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,
 
            )
 
            response = api_call(self, params)
 

	
 
            expected = "fork `%s` already exist" % fork_name
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(fork_name)
 

	
 
    def test_api_fork_repo_repo_exists(self):
 
        fork_name = self.REPO
 

	
 
        id_, params = _build_data(self.apikey, 'fork_repo',
 
                                  repoid=self.REPO,
 
                                  fork_name=fork_name,
 
                                  owner=TEST_USER_ADMIN_LOGIN,
 
        )
 
        response = api_call(self, params)
 

	
 
        expected = "repo `%s` already exist" % fork_name
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    @mock.patch.object(RepoModel, 'create_fork', crash)
 
    def test_api_fork_repo_exception_occurred(self):
 
        fork_name = 'api-repo-fork'
 
        id_, params = _build_data(self.apikey, 'fork_repo',
kallithea/tests/functional/test_login.py
Show inline comments
 
@@ -285,161 +285,163 @@ class TestLoginController(TestController
 
        lastname = 'testlastname'
 

	
 
        response = self.app.post(url(controller='login', action='register'),
 
                                            {'username': username,
 
                                             'password': password,
 
                                             'password_confirmation': password,
 
                                             'email': email,
 
                                             'firstname': name,
 
                                             'lastname': lastname,
 
                                             'admin': True})  # This should be overriden
 
        self.assertEqual(response.status, '302 Found')
 
        self.checkSessionFlash(response, 'You have successfully registered into Kallithea')
 

	
 
        ret = Session().query(User).filter(User.username == 'test_regular4').one()
 
        self.assertEqual(ret.username, username)
 
        self.assertEqual(check_password(password, ret.password), True)
 
        self.assertEqual(ret.email, email)
 
        self.assertEqual(ret.name, name)
 
        self.assertEqual(ret.lastname, lastname)
 
        self.assertNotEqual(ret.api_key, None)
 
        self.assertEqual(ret.admin, False)
 

	
 
    def test_forgot_password_wrong_mail(self):
 
        bad_email = 'username%wrongmail.org'
 
        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',
 
                                    action='password_reset_confirmation',
 
                                    key=key))
 
        self.assertEqual(response.status, '302 Found')
 
        self.assertTrue(response.location.endswith(url('login_home')))
 

	
 
        self.checkSessionFlash(response,
 
                               ('Your password reset was successful, '
 
                                'new password has been sent to your email'))
 

	
 
        response = response.follow()
 

	
 
    def _get_api_whitelist(self, values=None):
 
        config = {'api_access_controllers_whitelist': values or []}
 
        return config
 

	
 
    @parameterized.expand([
 
        ('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()
 
            with fixture.anon_access(False):
 
                self.app.get(url(controller='changeset',
 
                                 action='changeset_raw',
 
                                 repo_name=HG_REPO, revision='tip', api_key=new_api_key.api_key),
 
                             status=200)
 

	
 
    def test_access_page_via_expired_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()
 
            #patch the API key and make it expired
 
            new_api_key.expires = 0
 
            Session().add(new_api_key)
 
            Session().commit()
 
            with fixture.anon_access(False):
 
                self.app.get(url(controller='changeset',
 
                                 action='changeset_raw',
 
                                 repo_name=HG_REPO, revision='tip',
 
                                 api_key=new_api_key.api_key),
 
                             status=302)
kallithea/tests/models/test_permissions.py
Show inline comments
 
@@ -16,96 +16,98 @@ fixture = Fixture()
 

	
 

	
 
class TestPermissions(BaseTestCase):
 
    def __init__(self, methodName='runTest'):
 
        super(TestPermissions, self).__init__(methodName=methodName)
 

	
 
    @classmethod
 
    def setUpClass(cls):
 
        #recreate default user to get a clean start
 
        PermissionModel().create_default_permissions(user=User.DEFAULT_USER,
 
                                                     force=True)
 
        Session().commit()
 

	
 
    def setUp(self):
 
        self.u1 = UserModel().create_or_update(
 
            username=u'u1', password=u'qweqwe',
 
            email=u'u1@example.com', firstname=u'u1', lastname=u'u1'
 
        )
 
        self.u2 = UserModel().create_or_update(
 
            username=u'u2', password=u'qweqwe',
 
            email=u'u2@example.com', firstname=u'u2', lastname=u'u2'
 
        )
 
        self.u3 = UserModel().create_or_update(
 
            username=u'u3', password=u'qweqwe',
 
            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)
 
        perms = {
 
            'repositories_groups': {},
 
            'global': set([u'hg.admin', 'hg.create.write_on_repogroup.true']),
 
            'repositories': {HG_REPO: u'repository.admin'}
 
        }
 
        self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
 
                         perms['repositories'][HG_REPO])
 
        new_perm = 'repository.write'
 
        RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1,
 
                                          perm=new_perm)
 
        Session().commit()
 
        # cannot really downgrade admins permissions !? they still gets set as
 
        # admin !
 
        u1_auth = AuthUser(user_id=self.a1.user_id)
 
        self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
 
                         perms['repositories'][HG_REPO])
 

	
 
    def test_default_group_perms(self):
 
        self.g1 = fixture.create_repo_group('test1', skip_if_exists=True)
 
        self.g2 = fixture.create_repo_group('test2', skip_if_exists=True)
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        perms = {
 
            'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
 
            'global': set(Permission.DEFAULT_USER_PERMISSIONS),
 
@@ -389,96 +391,308 @@ class TestPermissions(BaseTestCase):
 

	
 
        #disable global perms on specific user
 
        user_model.revoke_perm(self.u1, 'hg.create.repository')
 
        user_model.grant_perm(self.u1, 'hg.create.none')
 
        user_model.revoke_perm(self.u1, 'hg.fork.repository')
 
        user_model.grant_perm(self.u1, 'hg.fork.none')
 

	
 
        # make sure inherit flag is turned off
 
        self.u1.inherit_default_permissions = False
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        # this user will have non inherited permissions from he's
 
        # explicitly set permissions
 
        self.assertEqual(u1_auth.permissions['global'],
 
                         set(['hg.create.none', 'hg.fork.none',
 
                              'hg.register.manual_activate',
 
                              'hg.extern_activate.auto',
 
                              'repository.read', 'group.read',
 
                              'usergroup.read', 'hg.create.write_on_repogroup.true']))
 

	
 
    def test_non_inherited_permissions_from_default_on_user_disabled(self):
 
        user_model = UserModel()
 
        # disable fork and create on default user
 
        usr = 'default'
 
        user_model.revoke_perm(usr, 'hg.create.repository')
 
        user_model.grant_perm(usr, 'hg.create.none')
 
        user_model.revoke_perm(usr, 'hg.fork.repository')
 
        user_model.grant_perm(usr, 'hg.fork.none')
 

	
 
        #enable global perms on specific user
 
        user_model.revoke_perm(self.u1, 'hg.create.none')
 
        user_model.grant_perm(self.u1, 'hg.create.repository')
 
        user_model.revoke_perm(self.u1, 'hg.fork.none')
 
        user_model.grant_perm(self.u1, 'hg.fork.repository')
 

	
 
        # make sure inherit flag is turned off
 
        self.u1.inherit_default_permissions = False
 
        Session().commit()
 
        u1_auth = AuthUser(user_id=self.u1.user_id)
 
        # this user will have non inherited permissions from he's
 
        # explicitly set permissions
 
        self.assertEqual(u1_auth.permissions['global'],
 
                         set(['hg.create.repository', 'hg.fork.repository',
 
                              'hg.register.manual_activate',
 
                              '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,
 
        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, he should still be admin
 
        RepoModel().grant_user_permission(self.test_repo, user=self.u1,
 
                                          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_def_perm_equal(self, user, change_factor=0):
 
        perms = UserToPerm.query()\
 
                .filter(UserToPerm.user == user)\
 
                .all()
 
        self.assertEqual(len(perms),
 
                         len(Permission.DEFAULT_USER_PERMISSIONS,)+change_factor,
 
                         msg=perms)
 

	
0 comments (0 inline, 0 general)