changeset 1289:f56533aa1caa beta

Automated merge with https://rhodecode.org/rhodecode
author "Lorenzo M. Catucci" <lorenzo@sancho.ccd.uniroma2.it>
date Tue, 26 Apr 2011 14:03:00 +0200
parents d6524f3025e8 (diff) a781d315191c (current diff)
children 74685a31cc43
files
diffstat 45 files changed, 2790 insertions(+), 644 deletions(-) [+]
line wrap: on
line diff
--- a/development.ini	Tue Apr 26 14:02:53 2011 +0200
+++ b/development.ini	Tue Apr 26 14:03:00 2011 +0200
@@ -141,8 +141,8 @@
 #########################################################
 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG    ###
 #########################################################
-#sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
-sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
+sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
+#sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 sqlalchemy.db1.echo = True
 sqlalchemy.db1.pool_recycle = 3600
 sqlalchemy.convert_unicode = true
@@ -229,4 +229,4 @@
 [formatter_color_formatter_sql]
 class=rhodecode.lib.colored_formatter.ColorFormatterSql
 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
-datefmt = %Y-%m-%d %H:%M:%S
\ No newline at end of file
+datefmt = %Y-%m-%d %H:%M:%S
--- a/init.d/rhodecode-daemon2	Tue Apr 26 14:02:53 2011 +0200
+++ b/init.d/rhodecode-daemon2	Tue Apr 26 14:03:00 2011 +0200
@@ -1,6 +1,6 @@
 #!/bin/sh -e
 ########################################
-#### THIS IS AN DEBIAN INIT.D SCRIPT####
+#### THIS IS A DEBIAN INIT.D SCRIPT ####
 ########################################
 
 ### BEGIN INIT INFO
@@ -29,49 +29,48 @@
 DAEMON="$PYTHON_PATH/bin/paster"
 
 DAEMON_OPTS="serve --daemon \
---user=$RUN_AS \
---group=$RUN_AS \
---pid-file=$PID_PATH \
---log-file=$LOG_PATH  $APP_PATH/$CONF_NAME"
+  --user=$RUN_AS \
+  --group=$RUN_AS \
+  --pid-file=$PID_PATH \
+  --log-file=$LOG_PATH  $APP_PATH/$CONF_NAME"
+
 
+start() {
+  echo "Starting $APP_NAME"
+  PYTHON_EGG_CACHE="/tmp" start-stop-daemon -d $APP_PATH \
+      --start --quiet \
+      --pidfile $PID_PATH \
+      --user $RUN_AS \
+      --exec $DAEMON -- $DAEMON_OPTS
+}
+
+stop() {
+  echo "Stopping $APP_NAME"
+  start-stop-daemon -d $APP_PATH \
+      --stop --quiet \
+      --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
+  
+  if [ -f $PID_PATH ]; then
+    rm $PID_PATH
+  fi
+}
 
 case "$1" in
   start)
-    echo "Starting $APP_NAME"
-    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
-        --start --quiet \
-        --pidfile $PID_PATH \
-        --user $RUN_AS \
-        --exec $DAEMON -- $DAEMON_OPTS
+    start
     ;;
   stop)
-    echo "Stopping $APP_NAME"
-    start-stop-daemon -d $APP_PATH \
-        --stop --quiet \
-        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
-    if [ -f $PID_PATH ]; then
-        rm $PID_PATH
-    fi
+    stop
     ;;
   restart)
     echo "Restarting $APP_NAME"
     ### stop ###
-    echo "Stopping $APP_NAME"
-    start-stop-daemon -d $APP_PATH \
-        --stop --quiet \
-        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
-    if [ -f $PID_PATH ]; then
-        rm $PID_PATH
-    fi
+    stop
+    wait
     ### start ###
-    echo "Starting $APP_NAME"
-    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
-        --start --quiet \
-        --pidfile $PID_PATH \
-        --user $RUN_AS \
-        --exec $DAEMON -- $DAEMON_OPTS
+    start
     ;;
   *)
     echo "Usage: $0 {start|stop|restart}"
     exit 1
-esac
\ No newline at end of file
+esac
--- a/rhodecode/config/routing.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/config/routing.py	Tue Apr 26 14:03:00 2011 +0200
@@ -105,16 +105,69 @@
                   controller='admin/repos_groups', path_prefix='/_admin')
 
     #ADMIN USER REST ROUTES
-    rmap.resource('user', 'users', controller='admin/users',
-                  path_prefix='/_admin')
+    with rmap.submapper(path_prefix='/_admin', controller='admin/users') as m:
+        m.connect("users", "/users",
+                  action="create", conditions=dict(method=["POST"]))
+        m.connect("users", "/users",
+                  action="index", conditions=dict(method=["GET"]))
+        m.connect("formatted_users", "/users.{format}",
+                  action="index", conditions=dict(method=["GET"]))
+        m.connect("new_user", "/users/new",
+                  action="new", conditions=dict(method=["GET"]))
+        m.connect("formatted_new_user", "/users/new.{format}",
+                  action="new", conditions=dict(method=["GET"]))
+        m.connect("update_user", "/users/{id}",
+                  action="update", conditions=dict(method=["PUT"]))
+        m.connect("delete_user", "/users/{id}",
+                  action="delete", conditions=dict(method=["DELETE"]))
+        m.connect("edit_user", "/users/{id}/edit",
+                  action="edit", conditions=dict(method=["GET"]))
+        m.connect("formatted_edit_user",
+                  "/users/{id}.{format}/edit",
+                  action="edit", conditions=dict(method=["GET"]))
+        m.connect("user", "/users/{id}",
+                  action="show", conditions=dict(method=["GET"]))
+        m.connect("formatted_user", "/users/{id}.{format}",
+                  action="show", conditions=dict(method=["GET"]))
+
+        #EXTRAS USER ROUTES
+        m.connect("user_perm", "/users_perm/{id}",
+                  action="update_perm", conditions=dict(method=["PUT"]))
 
     #ADMIN USERS REST ROUTES
-    rmap.resource('users_group', 'users_groups',
-                  controller='admin/users_groups', path_prefix='/_admin')
+    with rmap.submapper(path_prefix='/_admin',
+                        controller='admin/users_groups') as m:
+        m.connect("users_groups", "/users_groups",
+                  action="create", conditions=dict(method=["POST"]))
+        m.connect("users_groups", "/users_groups",
+                  action="index", conditions=dict(method=["GET"]))
+        m.connect("formatted_users_groups", "/users_groups.{format}",
+                  action="index", conditions=dict(method=["GET"]))
+        m.connect("new_users_group", "/users_groups/new",
+                  action="new", conditions=dict(method=["GET"]))
+        m.connect("formatted_new_users_group", "/users_groups/new.{format}",
+                  action="new", conditions=dict(method=["GET"]))
+        m.connect("update_users_group", "/users_groups/{id}",
+                  action="update", conditions=dict(method=["PUT"]))
+        m.connect("delete_users_group", "/users_groups/{id}",
+                  action="delete", conditions=dict(method=["DELETE"]))
+        m.connect("edit_users_group", "/users_groups/{id}/edit",
+                  action="edit", conditions=dict(method=["GET"]))
+        m.connect("formatted_edit_users_group",
+                  "/users_groups/{id}.{format}/edit",
+                  action="edit", conditions=dict(method=["GET"]))
+        m.connect("users_group", "/users_groups/{id}",
+                  action="show", conditions=dict(method=["GET"]))
+        m.connect("formatted_users_group", "/users_groups/{id}.{format}",
+                  action="show", conditions=dict(method=["GET"]))
+
+        #EXTRAS USER ROUTES
+        m.connect("users_group_perm", "/users_groups_perm/{id}",
+                  action="update_perm", conditions=dict(method=["PUT"]))
 
     #ADMIN GROUP REST ROUTES
-    rmap.resource('group', 'groups', controller='admin/groups',
-                  path_prefix='/_admin')
+    rmap.resource('group', 'groups',
+                  controller='admin/groups', path_prefix='/_admin')
 
     #ADMIN PERMISSIONS REST ROUTES
     rmap.resource('permission', 'permissions',
@@ -124,6 +177,7 @@
     rmap.connect('ldap_settings', '/_admin/ldap',
                  controller='admin/ldap_settings', action='ldap_settings',
                  conditions=dict(method=["POST"]))
+
     rmap.connect('ldap_home', '/_admin/ldap',
                  controller='admin/ldap_settings')
 
@@ -207,7 +261,9 @@
                 controller='feed', action='atom',
                 conditions=dict(function=check_repo))
 
-    #REPOSITORY ROUTES
+    #==========================================================================
+    # REPOSITORY ROUTES
+    #==========================================================================
     rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
                 controller='changeset', revision='tip',
                 conditions=dict(function=check_repo))
@@ -282,4 +338,7 @@
                 controller='settings', action='fork',
                 conditions=dict(function=check_repo))
 
+    rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
+                 controller='followers', action='followers',
+                 conditions=dict(function=check_repo))
     return rmap
--- a/rhodecode/controllers/admin/permissions.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/admin/permissions.py	Tue Apr 26 14:03:00 2011 +0200
@@ -33,7 +33,6 @@
 from rhodecode.lib.base import BaseController, render
 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
 from rhodecode.model.permission import PermissionModel
-from rhodecode.model.settings import SettingsModel
 from rhodecode.model.user import UserModel
 import formencode
 import logging
--- a/rhodecode/controllers/admin/users.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/admin/users.py	Tue Apr 26 14:03:00 2011 +0200
@@ -38,7 +38,7 @@
 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 from rhodecode.lib.base import BaseController, render
 
-from rhodecode.model.db import User
+from rhodecode.model.db import User, RepoToPerm, UserToPerm, Permission
 from rhodecode.model.forms import UserForm
 from rhodecode.model.user import UserModel
 
@@ -101,7 +101,7 @@
         # Forms posted to this method should contain a hidden field:
         #    <input type="hidden" name="_method" value="PUT" />
         # Or using helpers:
-        #    h.form(url('user', id=ID),
+        #    h.form(url('update_user', id=ID),
         #           method='put')
         # url('user', id=ID)
         user_model = UserModel()
@@ -113,13 +113,16 @@
         try:
             form_result = _form.to_python(dict(request.POST))
             user_model.update(id, form_result)
-            h.flash(_('User updated succesfully'), category='success')
+            h.flash(_('User updated successfully'), category='success')
 
         except formencode.Invalid, errors:
+            e = errors.error_dict or {}
+            perm = Permission.get_by_key('hg.create.repository')
+            e.update({'create_repo_perm': UserToPerm.has_perm(id, perm)})
             return htmlfill.render(
                 render('admin/users/user_edit.html'),
                 defaults=errors.value,
-                errors=errors.error_dict or {},
+                errors=e,
                 prefix_error=False,
                 encoding="UTF-8")
         except Exception:
@@ -134,7 +137,7 @@
         # Forms posted to this method should contain a hidden field:
         #    <input type="hidden" name="_method" value="DELETE" />
         # Or using helpers:
-        #    h.form(url('user', id=ID),
+        #    h.form(url('delete_user', id=ID),
         #           method='delete')
         # url('user', id=ID)
         user_model = UserModel()
@@ -167,6 +170,8 @@
             .permissions['global']
 
         defaults = c.user.get_dict()
+        perm = Permission.get_by_key('hg.create.repository')
+        defaults.update({'create_repo_perm': UserToPerm.has_perm(id, perm)})
 
         return htmlfill.render(
             render('admin/users/user_edit.html'),
@@ -174,3 +179,29 @@
             encoding="UTF-8",
             force_defaults=False
         )
+
+    def update_perm(self, id):
+        """PUT /users_perm/id: Update an existing item"""
+        # url('user_perm', id=ID, method='put')
+
+        grant_perm = request.POST.get('create_repo_perm', False)
+
+        if grant_perm:
+            perm = Permission.get_by_key('hg.create.none')
+            UserToPerm.revoke_perm(id, perm)
+
+            perm = Permission.get_by_key('hg.create.repository')
+            UserToPerm.grant_perm(id, perm)
+            h.flash(_("Granted 'repository create' permission to user"),
+                    category='success')
+
+        else:
+            perm = Permission.get_by_key('hg.create.repository')
+            UserToPerm.revoke_perm(id, perm)
+
+            perm = Permission.get_by_key('hg.create.none')
+            UserToPerm.grant_perm(id, perm)
+            h.flash(_("Revoked 'repository create' permission to user"),
+                    category='success')
+
+        return redirect(url('edit_user', id=id))
--- a/rhodecode/controllers/admin/users_groups.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/admin/users_groups.py	Tue Apr 26 14:03:00 2011 +0200
@@ -36,9 +36,8 @@
 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 from rhodecode.lib.base import BaseController, render
 
-from rhodecode.model.db import User, UsersGroup
+from rhodecode.model.db import User, UsersGroup, Permission, UsersGroupToPerm
 from rhodecode.model.forms import UserForm, UsersGroupForm
-from rhodecode.model.user import UserModel
 from rhodecode.model.users_group import UsersGroupModel
 
 log = logging.getLogger(__name__)
@@ -123,10 +122,16 @@
                     category='success')
             #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
         except formencode.Invalid, errors:
+            e = errors.error_dict or {}
+
+            perm = Permission.get_by_key('hg.create.repository')
+            e.update({'create_repo_perm':
+                         UsersGroupToPerm.has_perm(id, perm)})
+
             return htmlfill.render(
                 render('admin/users_groups/users_group_edit.html'),
                 defaults=errors.value,
-                errors=errors.error_dict or {},
+                errors=e,
                 prefix_error=False,
                 encoding="UTF-8")
         except Exception:
@@ -171,10 +176,38 @@
         c.available_members = [(x.user_id, x.username) for x in
                                self.sa.query(User).all()]
         defaults = c.users_group.get_dict()
-
+        perm = Permission.get_by_key('hg.create.repository')
+        defaults.update({'create_repo_perm':
+                         UsersGroupToPerm.has_perm(id, perm)})
         return htmlfill.render(
             render('admin/users_groups/users_group_edit.html'),
             defaults=defaults,
             encoding="UTF-8",
             force_defaults=False
         )
+
+    def update_perm(self, id):
+        """PUT /users_perm/id: Update an existing item"""
+        # url('users_group_perm', id=ID, method='put')
+
+        grant_perm = request.POST.get('create_repo_perm', False)
+
+        if grant_perm:
+            perm = Permission.get_by_key('hg.create.none')
+            UsersGroupToPerm.revoke_perm(id, perm)
+
+            perm = Permission.get_by_key('hg.create.repository')
+            UsersGroupToPerm.grant_perm(id, perm)
+            h.flash(_("Granted 'repository create' permission to user"),
+                    category='success')
+
+        else:
+            perm = Permission.get_by_key('hg.create.repository')
+            UsersGroupToPerm.revoke_perm(id, perm)
+
+            perm = Permission.get_by_key('hg.create.none')
+            UsersGroupToPerm.grant_perm(id, perm)
+            h.flash(_("Revoked 'repository create' permission to user"),
+                    category='success')
+
+        return redirect(url('edit_users_group', id=id))
--- a/rhodecode/controllers/changelog.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/changelog.py	Tue Apr 26 14:03:00 2011 +0200
@@ -84,7 +84,7 @@
         :param size: number of commits to show
         :param p: page number
         """
-        if not repo.revisions or repo.alias == 'git':
+        if not repo.revisions:
             c.jsdata = json.dumps([])
             return
 
@@ -93,12 +93,19 @@
         rev_start = repo.revisions.index(repo.revisions[(-1 * offset)])
         rev_end = max(0, rev_start - revcount)
 
-        dag = graph_rev(repo._repo, rev_start, rev_end)
-        c.dag = tree = list(colored(dag))
         data = []
-        for (id, type, ctx, vtx, edges) in tree:
-            if type != CHANGESET:
-                continue
-            data.append(('', vtx, edges))
+        if repo.alias == 'git':
+            for _ in xrange(rev_end, rev_start):
+                vtx = [0, 1]
+                edges = [[0, 0, 1]]
+                data.append(['', vtx, edges])
+
+        elif repo.alias == 'hg':
+            dag = graph_rev(repo._repo, rev_start, rev_end)
+            c.dag = tree = list(colored(dag))
+            for (id, type, ctx, vtx, edges) in tree:
+                if type != CHANGESET:
+                    continue
+                data.append(['', vtx, edges])
 
         c.jsdata = json.dumps(data)
--- a/rhodecode/controllers/changeset.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/changeset.py	Tue Apr 26 14:03:00 2011 +0200
@@ -86,8 +86,11 @@
         c.changes = OrderedDict()
         c.sum_added = 0
         c.sum_removed = 0
-        c.cut_off = False
+        c.lines_added = 0
+        c.lines_deleted = 0
+        c.cut_off = False  # defines if cut off limit is reached
 
+        # Iterate over ranges (default changeset view is always one changeset)
         for changeset in c.cs_ranges:
             c.changes[changeset.raw_id] = []
             try:
@@ -99,15 +102,22 @@
             # ADDED FILES
             #==================================================================
             for node in changeset.added:
+
                 filenode_old = FileNode(node.path, '', EmptyChangeset())
                 if filenode_old.is_binary or node.is_binary:
                     diff = wrap_to_table(_('binary file'))
+                    st = (0, 0)
                 else:
+                    # in this case node.size is good parameter since those are
+                    # added nodes and their size defines how many changes were
+                    # made
                     c.sum_added += node.size
                     if c.sum_added < self.cut_off_limit:
                         f_gitdiff = differ.get_gitdiff(filenode_old, node)
-                        diff = differ.DiffProcessor(f_gitdiff,
-                                                    format='gitdiff').as_html()
+                        d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
+
+                        st = d.stat()
+                        diff = d.as_html()
 
                     else:
                         diff = wrap_to_table(_('Changeset is to big and '
@@ -118,8 +128,10 @@
 
                 cs1 = None
                 cs2 = node.last_changeset.raw_id
-                c.changes[changeset.raw_id].append(('added', node,
-                                                    diff, cs1, cs2))
+                c.lines_added += st[0]
+                c.lines_deleted += st[1]
+                c.changes[changeset.raw_id].append(('added', node, diff,
+                                                    cs1, cs2, st))
 
             #==================================================================
             # CHANGED FILES
@@ -129,18 +141,27 @@
                     try:
                         filenode_old = changeset_parent.get_node(node.path)
                     except ChangesetError:
+                        log.warning('Unable to fetch parent node for diff')
                         filenode_old = FileNode(node.path, '',
                                                 EmptyChangeset())
 
                     if filenode_old.is_binary or node.is_binary:
                         diff = wrap_to_table(_('binary file'))
+                        st = (0, 0)
                     else:
 
                         if c.sum_removed < self.cut_off_limit:
                             f_gitdiff = differ.get_gitdiff(filenode_old, node)
-                            diff = differ.DiffProcessor(f_gitdiff,
-                                                        format='gitdiff')\
-                                                        .as_html()
+                            d = differ.DiffProcessor(f_gitdiff,
+                                                     format='gitdiff')
+                            st = d.stat()
+                            if (st[0] + st[1]) * 256 > self.cut_off_limit:
+                                diff = wrap_to_table(_('Diff is to big '
+                                                       'and was cut off, see '
+                                                       'raw diff instead'))
+                            else:
+                                diff = d.as_html()
+
                             if diff:
                                 c.sum_removed += len(diff)
                         else:
@@ -152,8 +173,10 @@
 
                     cs1 = filenode_old.last_changeset.raw_id
                     cs2 = node.last_changeset.raw_id
-                    c.changes[changeset.raw_id].append(('changed', node,
-                                                        diff, cs1, cs2))
+                    c.lines_added += st[0]
+                    c.lines_deleted += st[1]
+                    c.changes[changeset.raw_id].append(('changed', node, diff,
+                                                        cs1, cs2, st))
 
             #==================================================================
             # REMOVED FILES
@@ -161,7 +184,7 @@
             if not c.cut_off:
                 for node in changeset.removed:
                     c.changes[changeset.raw_id].append(('removed', node, None,
-                                                        None, None))
+                                                        None, None, (0, 0)))
 
         if len(c.cs_ranges) == 1:
             c.changeset = c.cs_ranges[0]
--- a/rhodecode/controllers/files.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/files.py	Tue Apr 26 14:03:00 2011 +0200
@@ -245,6 +245,7 @@
         c.action = request.GET.get('diff')
         c.no_changes = diff1 == diff2
         c.f_path = f_path
+        c.big_diff = False
 
         try:
             if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
@@ -286,7 +287,8 @@
                 c.cur_diff = _('Binary file')
             elif node1.size > self.cut_off_limit or \
                     node2.size > self.cut_off_limit:
-                c.cur_diff = _('Diff is too big to display')
+                c.cur_diff = ''
+                c.big_diff = True
             else:
                 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
                                         format='gitdiff')
@@ -298,13 +300,15 @@
                 c.cur_diff = _('Binary file')
             elif node1.size > self.cut_off_limit or \
                     node2.size > self.cut_off_limit:
-                c.cur_diff = _('Diff is too big to display')
+                c.cur_diff = ''
+                c.big_diff = True
+
             else:
                 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
                                         format='gitdiff')
                 c.cur_diff = diff.as_html()
 
-        if not c.cur_diff:
+        if not c.cur_diff and not c.big_diff:
             c.no_changes = True
         return render('files/file_diff.html')
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/controllers/followers.py	Tue Apr 26 14:03:00 2011 +0200
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""
+    rhodecode.controllers.followers
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Followers controller for rhodecode
+
+    :created_on: Apr 23, 2011
+    :author: marcink
+    :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
+    :license: GPLv3, see COPYING for more details.
+"""
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+import logging
+
+from pylons import tmpl_context as c, request
+
+from rhodecode.lib.helpers import Page
+from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from rhodecode.lib.base import BaseRepoController, render
+from rhodecode.model.db import Repository, User, UserFollowing
+
+log = logging.getLogger(__name__)
+
+
+class FollowersController(BaseRepoController):
+
+    @LoginRequired()
+    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
+                                   'repository.admin')
+    def __before__(self):
+        super(FollowersController, self).__before__()
+
+    def followers(self, repo_name):
+        p = int(request.params.get('page', 1))
+        repo_id = getattr(Repository.by_repo_name(repo_name), 'repo_id')
+        d = UserFollowing.get_repo_followers(repo_id)\
+            .order_by(UserFollowing.follows_from)
+        c.followers_pager = Page(d, page=p, items_per_page=20)
+
+        c.followers_data = render('/followers/followers_data.html')
+
+        if request.params.get('partial'):
+            return c.followers_data
+
+        return render('/followers/followers.html')
--- a/rhodecode/controllers/summary.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/controllers/summary.py	Tue Apr 26 14:03:00 2011 +0200
@@ -134,13 +134,13 @@
 
         if stats and stats.languages:
             c.no_data = False is dbrepo.enable_statistics
-            lang_stats = json.loads(stats.languages)
+            lang_stats_d = json.loads(stats.languages)
             c.commit_data = stats.commit_activity
             c.overview_data = stats.commit_activity_combined
 
             lang_stats = [(x, {"count": y,
                                "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
-                          for x, y in lang_stats.items()]
+                          for x, y in lang_stats_d.items()]
 
             c.trending_languages = json.dumps(OrderedDict(
                                        sorted(lang_stats, reverse=True,
--- a/rhodecode/i18n/rhodecode.pot	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/i18n/rhodecode.pot	Tue Apr 26 14:03:00 2011 +0200
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: RhodeCode 1.2.0\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2011-02-25 19:12+0100\n"
+"POT-Creation-Date: 2011-04-21 00:05+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,361 +17,422 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.5\n"
 
-#: rhodecode/controllers/changeset.py:104 rhodecode/controllers/changeset.py:129
-#: rhodecode/controllers/changeset.py:179 rhodecode/controllers/changeset.py:191
+#: rhodecode/controllers/changeset.py:106 rhodecode/controllers/changeset.py:141
+#: rhodecode/controllers/changeset.py:202 rhodecode/controllers/changeset.py:215
 msgid "binary file"
 msgstr ""
 
-#: rhodecode/controllers/changeset.py:112 rhodecode/controllers/changeset.py:138
+#: rhodecode/controllers/changeset.py:116 rhodecode/controllers/changeset.py:154
 msgid "Changeset is to big and was cut off, see raw changeset instead"
 msgstr ""
 
-#: rhodecode/controllers/error.py:71
+#: rhodecode/controllers/error.py:69
 msgid "Home page"
 msgstr ""
 
+#: rhodecode/controllers/error.py:98
+msgid "The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: rhodecode/controllers/error.py:101
-msgid "The request could not be understood by the server due to malformed syntax."
+msgid "Unauthorized access to resource"
 msgstr ""
 
 #: rhodecode/controllers/error.py:103
-msgid "Unauthorized access to resource"
+msgid "You don't have permission to view this page"
 msgstr ""
 
 #: rhodecode/controllers/error.py:105
-msgid "You don't have permission to view this page"
+msgid "The resource could not be found"
 msgstr ""
 
 #: rhodecode/controllers/error.py:107
-msgid "The resource could not be found"
-msgstr ""
-
-#: rhodecode/controllers/error.py:109
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
 msgstr ""
 
-#: rhodecode/controllers/feed.py:48
+#: rhodecode/controllers/feed.py:47
 #, python-format
 msgid "Changes on %s repository"
 msgstr ""
 
-#: rhodecode/controllers/files.py:67 rhodecode/controllers/files.py:120
+#: rhodecode/controllers/feed.py:48
+#, python-format
+msgid "%s %s feed"
+msgstr ""
+
+#: rhodecode/controllers/files.py:70
 msgid "There are no files yet"
 msgstr ""
 
-#: rhodecode/controllers/files.py:186
+#: rhodecode/controllers/files.py:225
 msgid "downloads disabled"
 msgstr ""
 
-#: rhodecode/controllers/files.py:191
+#: rhodecode/controllers/files.py:230
 #, python-format
 msgid "Unknown revision %s"
 msgstr ""
 
-#: rhodecode/controllers/files.py:193
+#: rhodecode/controllers/files.py:232
 msgid "Empty repository"
 msgstr ""
 
-#: rhodecode/controllers/files.py:195
+#: rhodecode/controllers/files.py:234
 msgid "Unknown archive type"
 msgstr ""
 
-#: rhodecode/controllers/files.py:248 rhodecode/controllers/files.py:257
-msgid "Diff is to big to display"
-msgstr ""
-
-#: rhodecode/controllers/files.py:250 rhodecode/controllers/files.py:259
+#: rhodecode/controllers/files.py:286 rhodecode/controllers/files.py:298
 msgid "Binary file"
 msgstr ""
 
-#: rhodecode/controllers/files.py:272
+#: rhodecode/controllers/files.py:289 rhodecode/controllers/files.py:301
+msgid "Diff is too big to display"
+msgstr ""
+
+#: rhodecode/controllers/files.py:315
+#: rhodecode/templates/changeset/changeset_range.html:4
+#: rhodecode/templates/changeset/changeset_range.html:12
+#: rhodecode/templates/changeset/changeset_range.html:29
 msgid "Changesets"
 msgstr ""
 
-#: rhodecode/controllers/files.py:273 rhodecode/controllers/summary.py:157
+#: rhodecode/controllers/files.py:316 rhodecode/controllers/summary.py:174
+#: rhodecode/templates/branches/branches.html:5
+#: rhodecode/templates/summary/summary.html:694
 msgid "Branches"
 msgstr ""
 
-#: rhodecode/controllers/files.py:274 rhodecode/controllers/summary.py:158
+#: rhodecode/controllers/files.py:317 rhodecode/controllers/summary.py:175
+#: rhodecode/templates/summary/summary.html:683
+#: rhodecode/templates/tags/tags.html:5
 msgid "Tags"
 msgstr ""
 
-#: rhodecode/controllers/login.py:112
+#: rhodecode/controllers/journal.py:50
+#, python-format
+msgid "%s public journal %s feed"
+msgstr ""
+
+#: rhodecode/controllers/journal.py:178 rhodecode/controllers/journal.py:212
+#: rhodecode/templates/admin/repos/repo_edit.html:357
+#: rhodecode/templates/base/base.html:14
+msgid "Public journal"
+msgstr ""
+
+#: rhodecode/controllers/login.py:110
 msgid "You have successfully registered into rhodecode"
 msgstr ""
 
-#: rhodecode/controllers/login.py:134
+#: rhodecode/controllers/login.py:132
 msgid "Your new password was sent"
 msgstr ""
 
-#: rhodecode/controllers/search.py:110
+#: rhodecode/controllers/search.py:107
 msgid "Invalid search query. Try quoting it."
 msgstr ""
 
-#: rhodecode/controllers/search.py:115
+#: rhodecode/controllers/search.py:112
 msgid "There is no index to search in. Please run whoosh indexer"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:62 rhodecode/controllers/settings.py:171
+#: rhodecode/controllers/settings.py:61 rhodecode/controllers/settings.py:170
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from the "
 "file system please run the application again in order to rescan repositories"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:109 rhodecode/controllers/admin/repos.py:146
+#: rhodecode/controllers/settings.py:109 rhodecode/controllers/admin/repos.py:235
 #, python-format
 msgid "Repository %s updated successfully"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:126 rhodecode/controllers/admin/repos.py:187
+#: rhodecode/controllers/settings.py:126 rhodecode/controllers/admin/repos.py:253
 #, python-format
 msgid "error occurred during update of repository %s"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:145 rhodecode/controllers/admin/repos.py:206
+#: rhodecode/controllers/settings.py:144 rhodecode/controllers/admin/repos.py:271
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was moved or renamed  from the "
 "filesystem please run the application again in order to rescan repositories"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:157 rhodecode/controllers/admin/repos.py:218
+#: rhodecode/controllers/settings.py:156 rhodecode/controllers/admin/repos.py:283
 #, python-format
 msgid "deleted repository %s"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:159 rhodecode/controllers/admin/repos.py:222
+#: rhodecode/controllers/settings.py:158 rhodecode/controllers/admin/repos.py:287
 #, python-format
 msgid "An error occurred during deletion of %s"
 msgstr ""
 
-#: rhodecode/controllers/settings.py:193
+#: rhodecode/controllers/settings.py:192
 #, python-format
 msgid "forked %s repository as %s"
 msgstr ""
 
-#: rhodecode/controllers/summary.py:117
+#: rhodecode/controllers/summary.py:122
 msgid "No data loaded yet"
 msgstr ""
 
-#: rhodecode/controllers/summary.py:120
+#: rhodecode/controllers/summary.py:125
 msgid "Statistics are disabled for this repository"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:51
+#: rhodecode/controllers/admin/ldap_settings.py:48
 msgid "BASE"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:52
+#: rhodecode/controllers/admin/ldap_settings.py:49
 msgid "ONELEVEL"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:53
+#: rhodecode/controllers/admin/ldap_settings.py:50
 msgid "SUBTREE"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:57
+#: rhodecode/controllers/admin/ldap_settings.py:54
 msgid "NEVER"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:58
+#: rhodecode/controllers/admin/ldap_settings.py:55
 msgid "ALLOW"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:59
+#: rhodecode/controllers/admin/ldap_settings.py:56
 msgid "TRY"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:60
+#: rhodecode/controllers/admin/ldap_settings.py:57
 msgid "DEMAND"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:61
+#: rhodecode/controllers/admin/ldap_settings.py:58
 msgid "HARD"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:103
+#: rhodecode/controllers/admin/ldap_settings.py:100
 msgid "Ldap settings updated successfully"
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:108
+#: rhodecode/controllers/admin/ldap_settings.py:105
 msgid "Unable to activate ldap. The \"python-ldap\" library is missing."
 msgstr ""
 
-#: rhodecode/controllers/admin/ldap_settings.py:124
+#: rhodecode/controllers/admin/ldap_settings.py:121
 msgid "error occurred during update of ldap settings"
 msgstr ""
 
+#: rhodecode/controllers/admin/permissions.py:57
+msgid "None"
+msgstr ""
+
+#: rhodecode/controllers/admin/permissions.py:58
+msgid "Read"
+msgstr ""
+
 #: rhodecode/controllers/admin/permissions.py:59
-msgid "None"
+msgid "Write"
 msgstr ""
 
 #: rhodecode/controllers/admin/permissions.py:60
-msgid "Read"
-msgstr ""
-
-#: rhodecode/controllers/admin/permissions.py:61
-msgid "Write"
-msgstr ""
-
-#: rhodecode/controllers/admin/permissions.py:62
+#: rhodecode/templates/admin/ldap/ldap.html:9
+#: rhodecode/templates/admin/permissions/permissions.html:9
+#: rhodecode/templates/admin/repos/repo_add.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:9
+#: rhodecode/templates/admin/repos/repos.html:10
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/admin/users/user_add.html:8
+#: rhodecode/templates/admin/users/user_edit.html:9
+#: rhodecode/templates/admin/users/user_edit.html:110
+#: rhodecode/templates/admin/users/users.html:9
+#: rhodecode/templates/admin/users_groups/users_group_add.html:8
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:9
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+#: rhodecode/templates/base/base.html:255 rhodecode/templates/base/base.html:344
+#: rhodecode/templates/base/base.html:346 rhodecode/templates/base/base.html:348
 msgid "Admin"
 msgstr ""
 
+#: rhodecode/controllers/admin/permissions.py:63
+msgid "disabled"
+msgstr ""
+
 #: rhodecode/controllers/admin/permissions.py:65
-msgid "disabled"
+msgid "allowed with manual account activation"
 msgstr ""
 
 #: rhodecode/controllers/admin/permissions.py:67
-msgid "allowed with manual account activation"
-msgstr ""
-
-#: rhodecode/controllers/admin/permissions.py:69
 msgid "allowed with automatic account activation"
 msgstr ""
 
-#: rhodecode/controllers/admin/permissions.py:71
+#: rhodecode/controllers/admin/permissions.py:69
 msgid "Disabled"
 msgstr ""
 
-#: rhodecode/controllers/admin/permissions.py:72
+#: rhodecode/controllers/admin/permissions.py:70
 msgid "Enabled"
 msgstr ""
 
-#: rhodecode/controllers/admin/permissions.py:106
+#: rhodecode/controllers/admin/permissions.py:103
 msgid "Default permissions updated successfully"
 msgstr ""
 
-#: rhodecode/controllers/admin/permissions.py:123
+#: rhodecode/controllers/admin/permissions.py:120
 msgid "error occurred during update of permissions"
 msgstr ""
 
-#: rhodecode/controllers/admin/repos.py:86
-#, python-format
-msgid "created repository %s"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:113
-#, python-format
-msgid "error occurred during creation of repository %s"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:239
-msgid "An error occurred during deletion of repository user"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:254
-msgid "An error occurred during deletion of repository users groups"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:271
-msgid "An error occurred during deletion of repository stats"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:286
-msgid "An error occurred during cache invalidation"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:306
-msgid "Updated repository visibility in public journal"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:309
-msgid "An error occurred during setting this repository in public journal"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:314 rhodecode/model/forms.py:54
-msgid "Token mismatch"
-msgstr ""
-
-#: rhodecode/controllers/admin/repos.py:334
+#: rhodecode/controllers/admin/repos.py:95
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from the "
 "filesystem please run the application again in order to rescan repositories"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:113
+#: rhodecode/controllers/admin/repos.py:169
+#, python-format
+msgid "created repository %s from %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:173
+#, python-format
+msgid "created repository %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:201
+#, python-format
+msgid "error occurred during creation of repository %s"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:304
+msgid "An error occurred during deletion of repository user"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:319
+msgid "An error occurred during deletion of repository users groups"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:336
+msgid "An error occurred during deletion of repository stats"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:351
+msgid "An error occurred during cache invalidation"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:371
+msgid "Updated repository visibility in public journal"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:374
+msgid "An error occurred during setting this repository in public journal"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:379 rhodecode/model/forms.py:53
+msgid "Token mismatch"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:392
+msgid "Pulled from remote location"
+msgstr ""
+
+#: rhodecode/controllers/admin/repos.py:394
+msgid "An error occurred during pull from remote location"
+msgstr ""
+
+#: rhodecode/controllers/admin/settings.py:109
 #, python-format
 msgid "Repositories successfully rescanned added: %s,removed: %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:122
+#: rhodecode/controllers/admin/settings.py:118
 msgid "Whoosh reindex task scheduled"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:147
+#: rhodecode/controllers/admin/settings.py:144
 msgid "Updated application settings"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:152
-#: rhodecode/controllers/admin/settings.py:213
+#: rhodecode/controllers/admin/settings.py:149
+#: rhodecode/controllers/admin/settings.py:216
 msgid "error occurred during updating application settings"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:208
+#: rhodecode/controllers/admin/settings.py:211
 msgid "Updated mercurial settings"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:265
+#: rhodecode/controllers/admin/settings.py:268
 msgid "You can't edit this user since it's crucial for entire application"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:293
+#: rhodecode/controllers/admin/settings.py:297
 msgid "Your account was updated successfully"
 msgstr ""
 
-#: rhodecode/controllers/admin/settings.py:313
-#: rhodecode/controllers/admin/users.py:128
+#: rhodecode/controllers/admin/settings.py:317
+#: rhodecode/controllers/admin/users.py:130
 #, python-format
 msgid "error occurred during update of user %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/users.py:79
+#: rhodecode/controllers/admin/users.py:78
 #, python-format
 msgid "created user %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/users.py:91
+#: rhodecode/controllers/admin/users.py:90
 #, python-format
 msgid "error occurred during creation of user %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/users.py:117
-msgid "User updated succesfully"
+#: rhodecode/controllers/admin/users.py:116
+msgid "User updated successfully"
 msgstr ""
 
-#: rhodecode/controllers/admin/users.py:144
+#: rhodecode/controllers/admin/users.py:146
 msgid "successfully deleted user"
 msgstr ""
 
-#: rhodecode/controllers/admin/users.py:148
+#: rhodecode/controllers/admin/users.py:150
 msgid "An error occurred during deletion of user"
 msgstr ""
 
-#: rhodecode/controllers/admin/users.py:164
+#: rhodecode/controllers/admin/users.py:166
 msgid "You can't edit this user"
 msgstr ""
 
-#: rhodecode/controllers/admin/users_groups.py:78
+#: rhodecode/controllers/admin/users.py:195
+#: rhodecode/controllers/admin/users_groups.py:201
+msgid "Granted 'repository create' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users.py:204
+#: rhodecode/controllers/admin/users_groups.py:210
+msgid "Revoked 'repository create' permission to user"
+msgstr ""
+
+#: rhodecode/controllers/admin/users_groups.py:74
 #, python-format
 msgid "created users group %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/users_groups.py:90
+#: rhodecode/controllers/admin/users_groups.py:86
 #, python-format
 msgid "error occurred during creation of users group %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/users_groups.py:125
+#: rhodecode/controllers/admin/users_groups.py:120
 #, python-format
 msgid "updated users group %s"
 msgstr ""
 
-#: rhodecode/controllers/admin/users_groups.py:137
+#: rhodecode/controllers/admin/users_groups.py:139
 #, python-format
 msgid "error occurred during update of users group %s"
 msgstr ""
@@ -384,218 +445,1651 @@
 msgid "An error occurred during deletion of users group"
 msgstr ""
 
-#: rhodecode/lib/auth.py:390
+#: rhodecode/lib/auth.py:380
 msgid "You need to be a registered user to perform this action"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:394
+#: rhodecode/lib/helpers.py:343
 msgid "ago"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:397
+#: rhodecode/lib/helpers.py:346
 msgid "just now"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:416
+#: rhodecode/lib/helpers.py:365
 msgid "True"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:420
+#: rhodecode/lib/helpers.py:369
 msgid "False"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:461
+#: rhodecode/lib/helpers.py:411
 #, python-format
 msgid "Show all combined changesets %s->%s"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:465
+#: rhodecode/lib/helpers.py:415
 msgid "compare view"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:473
+#: rhodecode/lib/helpers.py:424
 msgid "and"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:473
+#: rhodecode/lib/helpers.py:424
 #, python-format
 msgid "%s more"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:475
+#: rhodecode/lib/helpers.py:426 rhodecode/templates/changelog/changelog.html:14
+#: rhodecode/templates/changelog/changelog.html:39
 msgid "revisions"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:489
+#: rhodecode/lib/helpers.py:444
 msgid "fork name "
 msgstr ""
 
-#: rhodecode/lib/helpers.py:492
+#: rhodecode/lib/helpers.py:447
 msgid "[deleted] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:493 rhodecode/lib/helpers.py:497
+#: rhodecode/lib/helpers.py:448 rhodecode/lib/helpers.py:452
 msgid "[created] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:494 rhodecode/lib/helpers.py:498
+#: rhodecode/lib/helpers.py:449 rhodecode/lib/helpers.py:453
 msgid "[forked] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:495 rhodecode/lib/helpers.py:499
+#: rhodecode/lib/helpers.py:450 rhodecode/lib/helpers.py:454
 msgid "[updated] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:496
+#: rhodecode/lib/helpers.py:451
 msgid "[delete] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:500
+#: rhodecode/lib/helpers.py:455
 msgid "[pushed] into"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:501
+#: rhodecode/lib/helpers.py:456
+msgid "[pulled from remote] into"
+msgstr ""
+
+#: rhodecode/lib/helpers.py:457
 msgid "[pulled] from"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:502
+#: rhodecode/lib/helpers.py:458
 msgid "[started following] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:503
+#: rhodecode/lib/helpers.py:459
 msgid "[stopped following] repository"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:590
+#: rhodecode/lib/helpers.py:624
 #, python-format
 msgid " and %s more"
 msgstr ""
 
-#: rhodecode/lib/helpers.py:593
+#: rhodecode/lib/helpers.py:628
 msgid "No Files"
 msgstr ""
 
-#: rhodecode/model/forms.py:67
+#: rhodecode/model/forms.py:66
 msgid "Invalid username"
 msgstr ""
 
-#: rhodecode/model/forms.py:76
+#: rhodecode/model/forms.py:75
 msgid "This username already exists"
 msgstr ""
 
-#: rhodecode/model/forms.py:81
+#: rhodecode/model/forms.py:79
 msgid ""
 "Username may only contain alphanumeric characters underscores, periods or "
 "dashes and must begin with alphanumeric character"
 msgstr ""
 
-#: rhodecode/model/forms.py:99
+#: rhodecode/model/forms.py:94
 msgid "Invalid group name"
 msgstr ""
 
-#: rhodecode/model/forms.py:109
+#: rhodecode/model/forms.py:104
 msgid "This users group already exists"
 msgstr ""
 
-#: rhodecode/model/forms.py:114
+#: rhodecode/model/forms.py:110
 msgid ""
 "Group name may only contain alphanumeric characters underscores, periods or "
 "dashes and must begin with alphanumeric character"
 msgstr ""
 
-#: rhodecode/model/forms.py:134 rhodecode/model/forms.py:142
-#: rhodecode/model/forms.py:150
+#: rhodecode/model/forms.py:130 rhodecode/model/forms.py:138
+#: rhodecode/model/forms.py:146
 msgid "Invalid characters in password"
 msgstr ""
 
-#: rhodecode/model/forms.py:161
+#: rhodecode/model/forms.py:157
 msgid "Password do not match"
 msgstr ""
 
-#: rhodecode/model/forms.py:166
+#: rhodecode/model/forms.py:162
 msgid "invalid password"
 msgstr ""
 
-#: rhodecode/model/forms.py:167
+#: rhodecode/model/forms.py:163
 msgid "invalid user name"
 msgstr ""
 
-#: rhodecode/model/forms.py:168
+#: rhodecode/model/forms.py:164
 msgid "Your account is disabled"
 msgstr ""
 
-#: rhodecode/model/forms.py:205
+#: rhodecode/model/forms.py:201
 msgid "This username is not valid"
 msgstr ""
 
+#: rhodecode/model/forms.py:214
+msgid "This repository name is disallowed"
+msgstr ""
+
 #: rhodecode/model/forms.py:218
-msgid "This repository name is disallowed"
-msgstr ""
-
-#: rhodecode/model/forms.py:222
 msgid "This repository already exists"
 msgstr ""
 
-#: rhodecode/model/forms.py:234
+#: rhodecode/model/forms.py:237 rhodecode/model/forms.py:243
+msgid "invalid clone url"
+msgstr ""
+
+#: rhodecode/model/forms.py:246
+msgid "Invalid clone url, provide a valid clone http\\s url"
+msgstr ""
+
+#: rhodecode/model/forms.py:257
 msgid "Fork have to be the same type as original"
 msgstr ""
 
-#: rhodecode/model/forms.py:240
+#: rhodecode/model/forms.py:263
 msgid "This username or users group name is not valid"
 msgstr ""
 
-#: rhodecode/model/forms.py:303
+#: rhodecode/model/forms.py:326
 msgid "This is not a valid path"
 msgstr ""
 
-#: rhodecode/model/forms.py:317
+#: rhodecode/model/forms.py:340
 msgid "This e-mail address is already taken"
 msgstr ""
 
-#: rhodecode/model/forms.py:333
+#: rhodecode/model/forms.py:356
 msgid "This e-mail address doesn't exist."
 msgstr ""
 
-#: rhodecode/model/forms.py:355
+#: rhodecode/model/forms.py:378
 msgid ""
 "The LDAP Login attribute of the CN must be specified - this is the name of "
 "the attribute that is equivalent to 'username'"
 msgstr ""
 
-#: rhodecode/model/forms.py:374
+#: rhodecode/model/forms.py:397
 msgid "Please enter a login"
 msgstr ""
 
-#: rhodecode/model/forms.py:375
+#: rhodecode/model/forms.py:398
 #, python-format
 msgid "Enter a value %(min)i characters long or more"
 msgstr ""
 
-#: rhodecode/model/forms.py:383
+#: rhodecode/model/forms.py:406
 msgid "Please enter a password"
 msgstr ""
 
-#: rhodecode/model/forms.py:384
+#: rhodecode/model/forms.py:407
 #, python-format
 msgid "Enter %(min)i characters or more"
 msgstr ""
 
-#: rhodecode/model/user.py:128
+#: rhodecode/model/user.py:145
 msgid "[RhodeCode] New User registration"
 msgstr ""
 
-#: rhodecode/model/user.py:140 rhodecode/model/user.py:161
+#: rhodecode/model/user.py:157 rhodecode/model/user.py:179
 msgid "You can't Edit this user since it's crucial for entire application"
 msgstr ""
 
-#: rhodecode/model/user.py:182
+#: rhodecode/model/user.py:201
 msgid "You can't remove this user since it's crucial for entire application"
 msgstr ""
 
-#: rhodecode/model/user.py:185
+#: rhodecode/model/user.py:204
 #, python-format
 msgid ""
 "This user still owns %s repositories and cannot be removed. Switch owners or "
 "remove those repositories"
 msgstr ""
 
+#: rhodecode/templates/index.html:4 rhodecode/templates/index.html:35
+msgid "Dashboard"
+msgstr ""
+
+#: rhodecode/templates/index.html:33
+#: rhodecode/templates/admin/users/user_edit_my_account.html:102
+msgid "quick filter..."
+msgstr ""
+
+#: rhodecode/templates/index.html:35 rhodecode/templates/base/base.html:276
+msgid "repositories"
+msgstr ""
+
+#: rhodecode/templates/index.html:41
+msgid "ADD NEW REPOSITORY"
+msgstr ""
+
+#: rhodecode/templates/index.html:52
+#: rhodecode/templates/admin/repos/repo_add_base.html:9
+#: rhodecode/templates/admin/repos/repo_edit.html:32
+#: rhodecode/templates/admin/repos/repos.html:30
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:39
+#: rhodecode/templates/admin/users/user_edit_my_account.html:117
+#: rhodecode/templates/files/files_browser.html:44
+#: rhodecode/templates/settings/repo_settings.html:31
+#: rhodecode/templates/summary/summary.html:31
+#: rhodecode/templates/summary/summary.html:109
+msgid "Name"
+msgstr ""
+
+#: rhodecode/templates/index.html:53
+#: rhodecode/templates/admin/repos/repo_add_base.html:42
+#: rhodecode/templates/admin/repos/repo_edit.html:65
+#: rhodecode/templates/admin/repos/repos.html:31
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:40
+#: rhodecode/templates/settings/repo_fork.html:40
+#: rhodecode/templates/settings/repo_settings.html:40
+#: rhodecode/templates/summary/summary.html:92
+msgid "Description"
+msgstr ""
+
+#: rhodecode/templates/index.html:54 rhodecode/templates/admin/repos/repos.html:32
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:41
+#: rhodecode/templates/summary/summary.html:116
+msgid "Last change"
+msgstr ""
+
+#: rhodecode/templates/index.html:55 rhodecode/templates/admin/repos/repos.html:33
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:42
+msgid "Tip"
+msgstr ""
+
+#: rhodecode/templates/index.html:56
+#: rhodecode/templates/admin/repos/repo_edit.html:98
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:43
+msgid "Owner"
+msgstr ""
+
+#: rhodecode/templates/index.html:57
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:44
+#: rhodecode/templates/journal/public_journal.html:20
+#: rhodecode/templates/summary/summary.html:179
+#: rhodecode/templates/summary/summary.html:182
+msgid "RSS"
+msgstr ""
+
+#: rhodecode/templates/index.html:58
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:45
+#: rhodecode/templates/journal/public_journal.html:23
+#: rhodecode/templates/summary/summary.html:180
+#: rhodecode/templates/summary/summary.html:183
+msgid "Atom"
+msgstr ""
+
+#: rhodecode/templates/index.html:68 rhodecode/templates/admin/repos/repos.html:42
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:55
+#: rhodecode/templates/admin/users/user_edit_my_account.html:127
+#: rhodecode/templates/summary/summary.html:48
+msgid "Mercurial repository"
+msgstr ""
+
+#: rhodecode/templates/index.html:70 rhodecode/templates/admin/repos/repos.html:44
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:57
+#: rhodecode/templates/admin/users/user_edit_my_account.html:129
+#: rhodecode/templates/summary/summary.html:51
+msgid "Git repository"
+msgstr ""
+
+#: rhodecode/templates/index.html:77
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:27
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:64
+#: rhodecode/templates/journal/journal.html:53
+#: rhodecode/templates/summary/summary.html:56
+msgid "private repository"
+msgstr ""
+
+#: rhodecode/templates/index.html:79
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:66
+#: rhodecode/templates/journal/journal.html:55
+#: rhodecode/templates/summary/summary.html:58
+msgid "public repository"
+msgstr ""
+
+#: rhodecode/templates/index.html:87
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:74
+#: rhodecode/templates/base/base.html:267
+#: rhodecode/templates/settings/repo_fork.html:13
+msgid "fork"
+msgstr ""
+
+#: rhodecode/templates/index.html:88 rhodecode/templates/admin/repos/repos.html:60
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:75
+#: rhodecode/templates/admin/users/user_edit_my_account.html:143
+#: rhodecode/templates/summary/summary.html:69
+#: rhodecode/templates/summary/summary.html:71
+msgid "Fork of"
+msgstr ""
+
+#: rhodecode/templates/index.html:109 rhodecode/templates/admin/repos/repos.html:73
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:96
+msgid "No changesets yet"
+msgstr ""
+
+#: rhodecode/templates/index.html:115 rhodecode/templates/index.html:117
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:102
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:104
+#, python-format
+msgid "Subscribe to %s rss feed"
+msgstr ""
+
+#: rhodecode/templates/index.html:122 rhodecode/templates/index.html:124
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:109
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:111
+#, python-format
+msgid "Subscribe to %s atom feed"
+msgstr ""
+
+#: rhodecode/templates/login.html:5
+msgid "Sign In"
+msgstr ""
+
+#: rhodecode/templates/login.html:21
+msgid "Sign In to"
+msgstr ""
+
+#: rhodecode/templates/login.html:31 rhodecode/templates/register.html:20
+#: rhodecode/templates/admin/admin_log.html:5
+#: rhodecode/templates/admin/users/user_add.html:32
+#: rhodecode/templates/admin/users/user_edit.html:47
+#: rhodecode/templates/admin/users/user_edit_my_account.html:45
+#: rhodecode/templates/summary/summary.html:108
+msgid "Username"
+msgstr ""
+
+#: rhodecode/templates/login.html:40 rhodecode/templates/register.html:29
+#: rhodecode/templates/admin/ldap/ldap.html:46
+#: rhodecode/templates/admin/users/user_add.html:41
+msgid "Password"
+msgstr ""
+
+#: rhodecode/templates/login.html:60
+msgid "Forgot your password ?"
+msgstr ""
+
+#: rhodecode/templates/login.html:63
+msgid "Don't have an account ?"
+msgstr ""
+
+#: rhodecode/templates/password_reset.html:5
+msgid "Reset You password"
+msgstr ""
+
+#: rhodecode/templates/password_reset.html:11
+msgid "Reset You password to"
+msgstr ""
+
+#: rhodecode/templates/password_reset.html:21
+msgid "Email address"
+msgstr ""
+
+#: rhodecode/templates/password_reset.html:31
+msgid "Your new password will be send to matching email address"
+msgstr ""
+
+#: rhodecode/templates/register.html:5
+msgid "Sign Up"
+msgstr ""
+
+#: rhodecode/templates/register.html:11
+msgid "Sign Up to"
+msgstr ""
+
+#: rhodecode/templates/register.html:38
+msgid "Re-enter password"
+msgstr ""
+
+#: rhodecode/templates/register.html:47
+#: rhodecode/templates/admin/users/user_add.html:50
+#: rhodecode/templates/admin/users/user_edit.html:74
+#: rhodecode/templates/admin/users/user_edit_my_account.html:63
+msgid "First Name"
+msgstr ""
+
+#: rhodecode/templates/register.html:56
+#: rhodecode/templates/admin/users/user_add.html:59
+#: rhodecode/templates/admin/users/user_edit.html:83
+#: rhodecode/templates/admin/users/user_edit_my_account.html:72
+msgid "Last Name"
+msgstr ""
+
+#: rhodecode/templates/register.html:65
+#: rhodecode/templates/admin/users/user_add.html:68
+#: rhodecode/templates/admin/users/user_edit.html:92
+#: rhodecode/templates/admin/users/user_edit_my_account.html:81
+#: rhodecode/templates/summary/summary.html:110
+msgid "Email"
+msgstr ""
+
+#: rhodecode/templates/register.html:76
+msgid "Your account will be activated right after registration"
+msgstr ""
+
+#: rhodecode/templates/register.html:78
+msgid "Your account must wait for activation by administrator"
+msgstr ""
+
+#: rhodecode/templates/repo_switcher_list.html:14
+msgid "Private repository"
+msgstr ""
+
+#: rhodecode/templates/repo_switcher_list.html:19
+msgid "Public repository"
+msgstr ""
+
+#: rhodecode/templates/admin/admin.html:5 rhodecode/templates/admin/admin.html:9
+msgid "Admin journal"
+msgstr ""
+
+#: rhodecode/templates/admin/admin_log.html:6
+msgid "Action"
+msgstr ""
+
+#: rhodecode/templates/admin/admin_log.html:7
+msgid "Repository"
+msgstr ""
+
+#: rhodecode/templates/admin/admin_log.html:8
+msgid "Date"
+msgstr ""
+
+#: rhodecode/templates/admin/admin_log.html:9
+msgid "From IP"
+msgstr ""
+
+#: rhodecode/templates/admin/admin_log.html:61
+msgid "No actions yet"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:5
+msgid "LDAP administration"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:11
+msgid "Ldap"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:28
+msgid "Connection settings"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:30
+msgid "Enable LDAP"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:34
+msgid "Host"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:38
+msgid "Port"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:42
+msgid "Account"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:50
+msgid "Enable LDAPS"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:54
+msgid "Certificate Checks"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:57
+msgid "Search settings"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:59
+msgid "Base DN"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:63
+msgid "LDAP Filter"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:67
+msgid "LDAP Search Scope"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:70
+msgid "Attribute mappings"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:72
+msgid "Login Attribute"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:76
+msgid "First Name Attribute"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:80
+msgid "Last Name Attribute"
+msgstr ""
+
+#: rhodecode/templates/admin/ldap/ldap.html:84
+msgid "E-mail Attribute"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:5
+msgid "Permissions administration"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:110
+#: rhodecode/templates/admin/users/user_edit.html:127
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:248
+#: rhodecode/templates/settings/repo_settings.html:58
+msgid "Permissions"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:24
+msgid "Default permissions"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:31
+msgid "Anonymous access"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:41
+msgid "Repository permission"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:49
+msgid ""
+"All default permissions on each repository will be reset to choosen "
+"permission, note that all custom default permission on repositories will be "
+"lost"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:50
+msgid "overwrite existing settings"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:55
+msgid "Registration"
+msgstr ""
+
+#: rhodecode/templates/admin/permissions/permissions.html:63
+msgid "Repository creation"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add.html:5
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:5
+msgid "Add repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add.html:11
+#: rhodecode/templates/admin/repos/repo_edit.html:11
+#: rhodecode/templates/admin/repos/repos.html:10
+msgid "Repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add.html:13
+msgid "add new"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:17
+#: rhodecode/templates/summary/summary.html:80
+#: rhodecode/templates/summary/summary.html:82
+msgid "Clone from"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:25
+#: rhodecode/templates/admin/repos/repo_edit.html:48
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:5
+msgid "Repository group"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:29
+#: rhodecode/templates/admin/repos/repo_edit.html:52
+msgid "add new group"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:34
+#: rhodecode/templates/admin/repos/repo_edit.html:57
+msgid "Type"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_base.html:50
+#: rhodecode/templates/admin/repos/repo_edit.html:74
+#: rhodecode/templates/settings/repo_fork.html:48
+#: rhodecode/templates/settings/repo_settings.html:49
+msgid "Private"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_add_create_repository.html:9
+msgid "add new repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:5
+msgid "Edit repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:13
+#: rhodecode/templates/admin/users/user_edit.html:13
+#: rhodecode/templates/admin/users/user_edit_my_account.html:148
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:13
+msgid "edit"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:40
+msgid "Clone uri"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:82
+msgid "Enable statistics"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:90
+msgid "Enable downloads"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:227
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:10
+msgid "Group"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:228
+#: rhodecode/templates/admin/users_groups/users_groups.html:33
+msgid "members"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:313
+msgid "Administration"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:316
+msgid "Statistics"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:320
+msgid "Reset current statistics"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:323
+msgid "Fetched to rev"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:324
+msgid "Percentage of stats gathered"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:333
+msgid "Remote"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:337
+msgid "Pull changes from remote location"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:348
+msgid "Cache"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:352
+msgid "Invalidate repository cache"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:363
+msgid "Remove from public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:365
+msgid "Add to public journal"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:371
+msgid "Delete"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit.html:375
+msgid "Remove this repository"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:3
+msgid "none"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:4
+msgid "read"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:5
+msgid "write"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:6
+#: rhodecode/templates/admin/users/users.html:38
+#: rhodecode/templates/base/base.html:272
+msgid "admin"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:7
+msgid "member"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:18
+msgid "Failed to remove user"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:57
+msgid "Failed to remove users group"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repo_edit_perms.html:91
+msgid "Add another member"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repos.html:5
+msgid "Repositories administration"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repos.html:34
+#: rhodecode/templates/summary/summary.html:102
+msgid "Contact"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repos.html:35
+#: rhodecode/templates/admin/users/user_edit_my_account.html:119
+#: rhodecode/templates/admin/users/users.html:40
+#: rhodecode/templates/admin/users_groups/users_groups.html:35
+msgid "action"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repos.html:51
+#: rhodecode/templates/admin/users/user_edit_my_account.html:134
+#: rhodecode/templates/admin/users/user_edit_my_account.html:148
+msgid "private"
+msgstr ""
+
+#: rhodecode/templates/admin/repos/repos.html:53
+#: rhodecode/templates/admin/repos/repos.html:59
+#: rhodecode/templates/admin/users/user_edit_my_account.html:136
+#: rhodecode/templates/admin/users/user_edit_my_account.html:142
+#: rhodecode/templates/summary/summary.html:68
+msgid "public"
+msgstr ""
+
+#: rhodecode/templates/admin/repos_groups/repos_groups.html:10
+#, python-format
+msgid " %s repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:5
+msgid "Settings administration"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:9
+#: rhodecode/templates/settings/repo_settings.html:5
+#: rhodecode/templates/settings/repo_settings.html:13
+msgid "Settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:24
+msgid "Remap and rescan repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:32
+msgid "rescan option"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:38
+msgid ""
+"In case a repository was deleted from filesystem and there are leftovers in "
+"the database check this option to scan obsolete data in database and remove "
+"it."
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:39
+msgid "destroy old data"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:51
+msgid "Whoosh indexing"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:59
+msgid "index build option"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:64
+msgid "build from scratch"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:76
+msgid "Global application settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:85
+msgid "Application name"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:94
+msgid "Realm text"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:103
+msgid "GA code"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:118
+msgid "Mercurial settings"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:127
+msgid "Web"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:132
+msgid "require ssl for pushing"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:139
+msgid "Hooks"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:144
+msgid "Update repository after push (hg update)"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:148
+msgid "Show repository size after push"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:152
+msgid "Log user push commands"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:156
+msgid "Log user pull commands"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:163
+msgid "Repositories location"
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:168
+msgid ""
+"This a crucial application setting. If You really sure you need to change "
+"this, you must restart application in order to make this settings take "
+"effect. Click this label to unlock."
+msgstr ""
+
+#: rhodecode/templates/admin/settings/settings.html:169
+msgid "unlock"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_add.html:5
+msgid "Add user"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_add.html:10
+#: rhodecode/templates/admin/users/user_edit.html:11
+#: rhodecode/templates/admin/users/users.html:9
+msgid "Users"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_add.html:12
+msgid "add new user"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_add.html:77
+#: rhodecode/templates/admin/users/user_edit.html:101
+#: rhodecode/templates/admin/users_groups/users_group_add.html:41
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:42
+msgid "Active"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:5
+msgid "Edit user"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:34
+#: rhodecode/templates/admin/users/user_edit_my_account.html:33
+msgid "Using"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:40
+#: rhodecode/templates/admin/users/user_edit_my_account.html:39
+msgid "API key"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:56
+msgid "LDAP DN"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:65
+#: rhodecode/templates/admin/users/user_edit_my_account.html:54
+msgid "New password"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit.html:135
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:256
+msgid "Create repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:5
+msgid "My account"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:9
+msgid "My Account"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:101
+msgid "My repositories"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:107
+msgid "ADD REPOSITORY"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:118
+#: rhodecode/templates/branches/branches_data.html:7
+#: rhodecode/templates/shortlog/shortlog_data.html:8
+#: rhodecode/templates/tags/tags_data.html:7
+msgid "revision"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:157
+msgid "No repositories yet"
+msgstr ""
+
+#: rhodecode/templates/admin/users/user_edit_my_account.html:159
+msgid "create one now"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:5
+msgid "Users administration"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:33
+msgid "username"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:34
+#: rhodecode/templates/branches/branches_data.html:5
+#: rhodecode/templates/tags/tags_data.html:5
+msgid "name"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:35
+msgid "lastname"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:36
+msgid "last login"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:37
+#: rhodecode/templates/admin/users_groups/users_groups.html:34
+msgid "active"
+msgstr ""
+
+#: rhodecode/templates/admin/users/users.html:39
+#: rhodecode/templates/base/base.html:280
+msgid "ldap"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:5
+msgid "Add users group"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:10
+#: rhodecode/templates/admin/users_groups/users_groups.html:9
+msgid "Users groups"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:12
+msgid "add new users group"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_add.html:32
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:33
+msgid "Group name"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:5
+msgid "Edit users group"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:11
+msgid "UsersGroups"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:50
+msgid "Members"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:58
+msgid "Choosen group members"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:61
+msgid "Remove all elements"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:75
+msgid "Available members"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_group_edit.html:79
+msgid "Add all elements"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:5
+msgid "Users groups administration"
+msgstr ""
+
+#: rhodecode/templates/admin/users_groups/users_groups.html:32
+msgid "group name"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:21 rhodecode/templates/base/base.html:316
+#: rhodecode/templates/base/base.html:318 rhodecode/templates/base/base.html:320
+msgid "Home"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:25 rhodecode/templates/base/base.html:325
+#: rhodecode/templates/base/base.html:327 rhodecode/templates/base/base.html:329
+#: rhodecode/templates/journal/journal.html:4
+#: rhodecode/templates/journal/journal.html:17
+#: rhodecode/templates/journal/public_journal.html:4
+msgid "Journal"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:71
+msgid "Submit a bug"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:111
+msgid "Switch repository"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:113
+msgid "Products"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:119
+msgid "loading..."
+msgstr ""
+
+#: rhodecode/templates/base/base.html:185 rhodecode/templates/base/base.html:187
+#: rhodecode/templates/base/base.html:189
+#: rhodecode/templates/summary/summary.html:4
+msgid "Summary"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:201 rhodecode/templates/base/base.html:203
+#: rhodecode/templates/base/base.html:205
+#: rhodecode/templates/changelog/changelog.html:6
+#: rhodecode/templates/changelog/changelog.html:14
+msgid "Changelog"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:210 rhodecode/templates/base/base.html:212
+#: rhodecode/templates/base/base.html:214
+msgid "Switch to"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:218
+#: rhodecode/templates/branches/branches.html:13
+msgid "branches"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:225
+#: rhodecode/templates/branches/branches_data.html:32
+msgid "There are no branches yet"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:230
+#: rhodecode/templates/shortlog/shortlog_data.html:10
+#: rhodecode/templates/tags/tags.html:14
+msgid "tags"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:237
+#: rhodecode/templates/tags/tags_data.html:32
+msgid "There are no tags yet"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:244 rhodecode/templates/base/base.html:246
+#: rhodecode/templates/base/base.html:248 rhodecode/templates/files/files.html:4
+msgid "Files"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:253 rhodecode/templates/base/base.html:257
+#: rhodecode/templates/files/files_annotate.html:40
+#: rhodecode/templates/files/files_source.html:11
+msgid "Options"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:262 rhodecode/templates/base/base.html:264
+#: rhodecode/templates/base/base.html:281
+msgid "settings"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:268
+msgid "search"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:275
+msgid "journal"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:277
+msgid "users"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:278
+msgid "users groups"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:279
+msgid "permissions"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:293 rhodecode/templates/base/base.html:295
+msgid "Followers"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:301 rhodecode/templates/base/base.html:303
+msgid "Forks"
+msgstr ""
+
+#: rhodecode/templates/base/base.html:334 rhodecode/templates/base/base.html:336
+#: rhodecode/templates/base/base.html:338 rhodecode/templates/search/search.html:4
+#: rhodecode/templates/search/search.html:24
+#: rhodecode/templates/search/search.html:46
+msgid "Search"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:80
+#: rhodecode/templates/journal/journal.html:48
+#: rhodecode/templates/summary/summary.html:36
+msgid "Stop following this repository"
+msgstr ""
+
+#: rhodecode/templates/base/root.html:90
+#: rhodecode/templates/summary/summary.html:40
+msgid "Start following this repository"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:4
+#: rhodecode/templates/tags/tags_data.html:4
+msgid "date"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:6
+#: rhodecode/templates/shortlog/shortlog_data.html:7
+#: rhodecode/templates/tags/tags_data.html:6
+msgid "author"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:8
+#: rhodecode/templates/shortlog/shortlog_data.html:11
+#: rhodecode/templates/tags/tags_data.html:8
+msgid "links"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:24
+#: rhodecode/templates/shortlog/shortlog_data.html:39
+#: rhodecode/templates/tags/tags_data.html:24
+msgid "changeset"
+msgstr ""
+
+#: rhodecode/templates/branches/branches_data.html:26
+#: rhodecode/templates/files/files.html:12
+#: rhodecode/templates/shortlog/shortlog_data.html:41
+#: rhodecode/templates/summary/summary.html:225
+#: rhodecode/templates/tags/tags_data.html:26
+msgid "files"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:14
+msgid "showing "
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:14
+msgid "out of"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:37
+msgid "Show"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:40
+msgid "set"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:52
+#: rhodecode/templates/changeset/changeset.html:42
+#: rhodecode/templates/summary/summary.html:613
+msgid "commit"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:66
+#: rhodecode/templates/changeset/changeset.html:55
+msgid "removed"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:67
+#: rhodecode/templates/changeset/changeset.html:56
+msgid "changed"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:68
+#: rhodecode/templates/changeset/changeset.html:57
+msgid "added"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:70
+#: rhodecode/templates/changelog/changelog.html:71
+#: rhodecode/templates/changelog/changelog.html:72
+#: rhodecode/templates/changeset/changeset.html:59
+#: rhodecode/templates/changeset/changeset.html:60
+#: rhodecode/templates/changeset/changeset.html:61
+#, python-format
+msgid "affected %s files"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:77
+#: rhodecode/templates/changeset/changeset.html:66
+msgid "merge"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:82
+#: rhodecode/templates/changeset/changeset.html:72
+msgid "Parent"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:87
+#: rhodecode/templates/changeset/changeset.html:77
+msgid "No parents"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:92
+#: rhodecode/templates/changeset/changeset.html:80
+#: rhodecode/templates/files/files.html:29
+#: rhodecode/templates/files/files_annotate.html:25
+#: rhodecode/templates/shortlog/shortlog_data.html:9
+msgid "branch"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:96
+#: rhodecode/templates/changeset/changeset.html:83
+msgid "tag"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:132
+msgid "Show selected changes __S -> __E"
+msgstr ""
+
+#: rhodecode/templates/changelog/changelog.html:163
+#: rhodecode/templates/shortlog/shortlog_data.html:65
+msgid "There are no changes yet"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:6
+#: rhodecode/templates/changeset/changeset.html:14
+#: rhodecode/templates/changeset/changeset.html:31
+msgid "Changeset"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:32
+#: rhodecode/templates/changeset/changeset.html:121
+#: rhodecode/templates/changeset/changeset_range.html:78
+#: rhodecode/templates/files/file_diff.html:32
+msgid "raw diff"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:34
+#: rhodecode/templates/changeset/changeset.html:123
+#: rhodecode/templates/changeset/changeset_range.html:80
+#: rhodecode/templates/files/file_diff.html:34
+msgid "download diff"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:90
+#, python-format
+msgid "%s files affected with %s additions and %s deletions."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:101
+msgid "Changeset was to big and was cut off..."
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:119
+#: rhodecode/templates/changeset/changeset_range.html:76
+#: rhodecode/templates/files/file_diff.html:30
+msgid "diff"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset.html:132
+#: rhodecode/templates/changeset/changeset_range.html:89
+msgid "No changes in this file"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:30
+msgid "Compare View"
+msgstr ""
+
+#: rhodecode/templates/changeset/changeset_range.html:52
+msgid "Files affected"
+msgstr ""
+
+#: rhodecode/templates/errors/error_document.html:44
+#, python-format
+msgid "You will be redirected to %s in %s seconds"
+msgstr ""
+
+#: rhodecode/templates/files/file_diff.html:4
+#: rhodecode/templates/files/file_diff.html:12
+msgid "File diff"
+msgstr ""
+
+#: rhodecode/templates/files/file_diff.html:40
+msgid "No changes"
+msgstr ""
+
+#: rhodecode/templates/files/files.html:37
+#: rhodecode/templates/files/files_annotate.html:31
+msgid "Location"
+msgstr ""
+
+#: rhodecode/templates/files/files.html:46
+msgid "Go back"
+msgstr ""
+
+#: rhodecode/templates/files/files.html:47
+msgid "No files at given path"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:4
+msgid "File annotate"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:12
+msgid "annotate"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:33
+#: rhodecode/templates/files/files_browser.html:47
+#: rhodecode/templates/files/files_source.html:2
+msgid "Revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:36
+#: rhodecode/templates/files/files_browser.html:45
+#: rhodecode/templates/files/files_source.html:7
+msgid "Size"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:38
+#: rhodecode/templates/files/files_browser.html:46
+#: rhodecode/templates/files/files_source.html:9
+msgid "Mimetype"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:41
+msgid "show source"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:43
+#: rhodecode/templates/files/files_annotate.html:72
+#: rhodecode/templates/files/files_source.html:14
+#: rhodecode/templates/files/files_source.html:45
+msgid "show as raw"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:45
+#: rhodecode/templates/files/files_source.html:16
+msgid "download as raw"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:48
+#: rhodecode/templates/files/files_source.html:19
+msgid "History"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:67
+#: rhodecode/templates/files/files_source.html:40
+#, python-format
+msgid "Binary file (%s)"
+msgstr ""
+
+#: rhodecode/templates/files/files_annotate.html:72
+#: rhodecode/templates/files/files_source.html:45
+msgid "File is to big to display"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:13
+msgid "view"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:14
+msgid "previous revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:16
+msgid "next revision"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:23
+msgid "follow current branch"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:48
+msgid "Last modified"
+msgstr ""
+
+#: rhodecode/templates/files/files_browser.html:49
+msgid "Last commiter"
+msgstr ""
+
+#: rhodecode/templates/files/files_source.html:12
+msgid "show annotation"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:34
+msgid "Following"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:41
+msgid "following user"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:41
+msgid "user"
+msgstr ""
+
+#: rhodecode/templates/journal/journal.html:65
+msgid "You are not following any users or repositories"
+msgstr ""
+
+#: rhodecode/templates/journal/journal_data.html:52
+msgid "No entries yet"
+msgstr ""
+
+#: rhodecode/templates/journal/public_journal.html:17
+msgid "Public Journal"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:7
+#: rhodecode/templates/search/search.html:26
+msgid "in repository: "
+msgstr ""
+
+#: rhodecode/templates/search/search.html:9
+#: rhodecode/templates/search/search.html:28
+msgid "in all repositories"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:42
+msgid "Search term"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:54
+msgid "Search in"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:57
+msgid "File contents"
+msgstr ""
+
+#: rhodecode/templates/search/search.html:59
+msgid "File names"
+msgstr ""
+
+#: rhodecode/templates/search/search_content.html:20
+#: rhodecode/templates/search/search_path.html:15
+msgid "Permission denied"
+msgstr ""
+
+#: rhodecode/templates/settings/repo_fork.html:5
+msgid "Fork"
+msgstr ""
+
+#: rhodecode/templates/settings/repo_fork.html:31
+msgid "Fork name"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog.html:5
+#: rhodecode/templates/summary/summary.html:670
+msgid "Shortlog"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog.html:14
+msgid "shortlog"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:5
+msgid "commit message"
+msgstr ""
+
+#: rhodecode/templates/shortlog/shortlog_data.html:6
+msgid "age"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:12
+msgid "summary"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:79
+msgid "remote clone"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:123
+msgid "by"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:130
+msgid "Clone url"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:139
+msgid "Trending source files"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:148
+msgid "Download"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:152
+msgid "There are no downloads yet"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:154
+msgid "Downloads are disabled for this repository"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:156
+#: rhodecode/templates/summary/summary.html:309
+msgid "enable"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:164
+#: rhodecode/templates/summary/summary.html:287
+#, python-format
+msgid "Download %s as %s"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:175
+msgid "Feeds"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:248
+#: rhodecode/templates/summary/summary.html:688
+#: rhodecode/templates/summary/summary.html:699
+msgid "show more"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:301
+msgid "Commit activity by day / author"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:313
+msgid "Loaded in"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:607
+msgid "commits"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:608
+msgid "files added"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:609
+msgid "files changed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:610
+msgid "files removed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:614
+msgid "file added"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:615
+msgid "file changed"
+msgstr ""
+
+#: rhodecode/templates/summary/summary.html:616
+msgid "file removed"
+msgstr ""
+
--- a/rhodecode/lib/base.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/base.py	Tue Apr 26 14:03:00 2011 +0200
@@ -62,7 +62,8 @@
 
             c.rhodecode_repo, dbrepo = self.scm_model.get(c.repo_name,
                                                           retval='repo')
-            if c.rhodecode_repo:
+
+            if c.rhodecode_repo is not None:
                 c.repository_followers = self.scm_model.get_followers(c.repo_name)
                 c.repository_forks = self.scm_model.get_forks(c.repo_name)
             else:
--- a/rhodecode/lib/celerylib/__init__.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/celerylib/__init__.py	Tue Apr 26 14:03:00 2011 +0200
@@ -48,6 +48,7 @@
 except KeyError:
     CELERY_ON = False
 
+
 class ResultWrapper(object):
     def __init__(self, task):
         self.task = task
@@ -56,12 +57,14 @@
     def result(self):
         return self.task
 
+
 def run_task(task, *args, **kwargs):
     if CELERY_ON:
         try:
             t = task.apply_async(args=args, kwargs=kwargs)
             log.info('running task %s:%s', t.task_id, task)
             return t
+
         except socket.error, e:
             if  e.errno == 111:
                 log.debug('Unable to connect to celeryd. Sync execution')
@@ -76,14 +79,20 @@
     return ResultWrapper(task(*args, **kwargs))
 
 
+def __get_lockkey(func, *fargs, **fkwargs):
+    params = list(fargs)
+    params.extend(['%s-%s' % ar for ar in fkwargs.items()])
+
+    func_name = str(func.__name__) if hasattr(func, '__name__') else str(func)
+
+    lockkey = 'task_%s' % \
+        md5(func_name + '-' + '-'.join(map(str, params))).hexdigest()
+    return lockkey
+
+
 def locked_task(func):
     def __wrapper(func, *fargs, **fkwargs):
-        params = list(fargs)
-        params.extend(['%s-%s' % ar for ar in fkwargs.items()])
-
-        lockkey = 'task_%s' % \
-            md5(str(func.__name__) + '-' + \
-                '-'.join(map(str, params))).hexdigest()
+        lockkey = __get_lockkey(func, *fargs, **fkwargs)
         log.info('running task with lockkey %s', lockkey)
         try:
             l = DaemonLock(lockkey)
--- a/rhodecode/lib/celerylib/tasks.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/celerylib/tasks.py	Tue Apr 26 14:03:00 2011 +0200
@@ -37,13 +37,14 @@
 from pylons import config
 from pylons.i18n.translation import _
 
-from rhodecode.lib.celerylib import run_task, locked_task, str2bool
+from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
+    __get_lockkey, LockHeld, DaemonLock
 from rhodecode.lib.helpers import person
 from rhodecode.lib.smtp_mailer import SmtpMailer
 from rhodecode.lib.utils import OrderedDict, add_cache
 from rhodecode.model import init_model
 from rhodecode.model import meta
-from rhodecode.model.db import RhodeCodeUi
+from rhodecode.model.db import RhodeCodeUi, Statistics, Repository
 
 from vcs.backends import get_repo
 
@@ -125,146 +126,162 @@
 
 
 @task(ignore_result=True)
-@locked_task
 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
     try:
         log = get_commits_stats.get_logger()
     except:
         log = logging.getLogger(__name__)
 
-    from rhodecode.model.db import Statistics, Repository
+    lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
+                            ts_max_y)
+    log.info('running task with lockkey %s', lockkey)
+    try:
+        lock = DaemonLock(lockkey)
 
-    #for js data compatibilty
-    akc = lambda k: person(k).replace('"', "")
+        #for js data compatibilty cleans the key for person from '
+        akc = lambda k: person(k).replace('"', "")
 
-    co_day_auth_aggr = {}
-    commits_by_day_aggregate = {}
-    repos_path = get_repos_path()
-    p = os.path.join(repos_path, repo_name)
-    repo = get_repo(p)
+        co_day_auth_aggr = {}
+        commits_by_day_aggregate = {}
+        repos_path = get_repos_path()
+        p = os.path.join(repos_path, repo_name)
+        repo = get_repo(p)
+        repo_size = len(repo.revisions)
+        #return if repo have no revisions
+        if repo_size < 1:
+            lock.release()
+            return True
 
-    skip_date_limit = True
-    parse_limit = int(config['app_conf'].get('commit_parse_limit'))
-    last_rev = 0
-    last_cs = None
-    timegetter = itemgetter('time')
+        skip_date_limit = True
+        parse_limit = int(config['app_conf'].get('commit_parse_limit'))
+        last_rev = 0
+        last_cs = None
+        timegetter = itemgetter('time')
 
-    sa = get_session()
+        sa = get_session()
 
-    dbrepo = sa.query(Repository)\
-        .filter(Repository.repo_name == repo_name).scalar()
-    cur_stats = sa.query(Statistics)\
-        .filter(Statistics.repository == dbrepo).scalar()
+        dbrepo = sa.query(Repository)\
+            .filter(Repository.repo_name == repo_name).scalar()
+        cur_stats = sa.query(Statistics)\
+            .filter(Statistics.repository == dbrepo).scalar()
 
-    if cur_stats is not None:
-        last_rev = cur_stats.stat_on_revision
+        if cur_stats is not None:
+            last_rev = cur_stats.stat_on_revision
 
-    #return if repo is empty
-    if not repo.revisions:
-        return True
+        if last_rev == repo.get_changeset().revision and repo_size > 1:
+            #pass silently without any work if we're not on first revision or
+            #current state of parsing revision(from db marker) is the
+            #last revision
+            lock.release()
+            return True
 
-    if last_rev == repo.get_changeset().revision and len(repo.revisions) > 1:
-        #pass silently without any work if we're not on first revision or
-        #current state of parsing revision(from db marker) is the last revision
-        return True
+        if cur_stats:
+            commits_by_day_aggregate = OrderedDict(json.loads(
+                                        cur_stats.commit_activity_combined))
+            co_day_auth_aggr = json.loads(cur_stats.commit_activity)
 
-    if cur_stats:
-        commits_by_day_aggregate = OrderedDict(
-                                       json.loads(
-                                        cur_stats.commit_activity_combined))
-        co_day_auth_aggr = json.loads(cur_stats.commit_activity)
+        log.debug('starting parsing %s', parse_limit)
+        lmktime = mktime
+
+        last_rev = last_rev + 1 if last_rev > 0 else last_rev
 
-    log.debug('starting parsing %s', parse_limit)
-    lmktime = mktime
-
-    last_rev = last_rev + 1 if last_rev > 0 else last_rev
+        for cs in repo[last_rev:last_rev + parse_limit]:
+            last_cs = cs  # remember last parsed changeset
+            k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
+                          cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
 
-    for cs in repo[last_rev:last_rev + parse_limit]:
-        last_cs = cs  # remember last parsed changeset
-        k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
-                      cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
+            if akc(cs.author) in co_day_auth_aggr:
+                try:
+                    l = [timegetter(x) for x in
+                         co_day_auth_aggr[akc(cs.author)]['data']]
+                    time_pos = l.index(k)
+                except ValueError:
+                    time_pos = False
+
+                if time_pos >= 0 and time_pos is not False:
+
+                    datadict = \
+                        co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
 
-        if akc(cs.author) in co_day_auth_aggr:
-            try:
-                l = [timegetter(x) for x in
-                     co_day_auth_aggr[akc(cs.author)]['data']]
-                time_pos = l.index(k)
-            except ValueError:
-                time_pos = False
+                    datadict["commits"] += 1
+                    datadict["added"] += len(cs.added)
+                    datadict["changed"] += len(cs.changed)
+                    datadict["removed"] += len(cs.removed)
+
+                else:
+                    if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
 
-            if time_pos >= 0 and time_pos is not False:
-
-                datadict = co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
-
-                datadict["commits"] += 1
-                datadict["added"] += len(cs.added)
-                datadict["changed"] += len(cs.changed)
-                datadict["removed"] += len(cs.removed)
+                        datadict = {"time": k,
+                                    "commits": 1,
+                                    "added": len(cs.added),
+                                    "changed": len(cs.changed),
+                                    "removed": len(cs.removed),
+                                   }
+                        co_day_auth_aggr[akc(cs.author)]['data']\
+                            .append(datadict)
 
             else:
                 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
-
-                    datadict = {"time": k,
-                                "commits": 1,
-                                "added": len(cs.added),
-                                "changed": len(cs.changed),
-                                "removed": len(cs.removed),
-                               }
-                    co_day_auth_aggr[akc(cs.author)]['data']\
-                        .append(datadict)
+                    co_day_auth_aggr[akc(cs.author)] = {
+                                        "label": akc(cs.author),
+                                        "data": [{"time":k,
+                                                 "commits":1,
+                                                 "added":len(cs.added),
+                                                 "changed":len(cs.changed),
+                                                 "removed":len(cs.removed),
+                                                 }],
+                                        "schema": ["commits"],
+                                        }
 
-        else:
-            if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
-                co_day_auth_aggr[akc(cs.author)] = {
-                                    "label": akc(cs.author),
-                                    "data": [{"time":k,
-                                             "commits":1,
-                                             "added":len(cs.added),
-                                             "changed":len(cs.changed),
-                                             "removed":len(cs.removed),
-                                             }],
-                                    "schema": ["commits"],
-                                    }
+            #gather all data by day
+            if k in commits_by_day_aggregate:
+                commits_by_day_aggregate[k] += 1
+            else:
+                commits_by_day_aggregate[k] = 1
 
-        #gather all data by day
-        if k in commits_by_day_aggregate:
-            commits_by_day_aggregate[k] += 1
-        else:
-            commits_by_day_aggregate[k] = 1
+        overview_data = sorted(commits_by_day_aggregate.items(),
+                               key=itemgetter(0))
+
+        if not co_day_auth_aggr:
+            co_day_auth_aggr[akc(repo.contact)] = {
+                "label": akc(repo.contact),
+                "data": [0, 1],
+                "schema": ["commits"],
+            }
 
-    overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
-    if not co_day_auth_aggr:
-        co_day_auth_aggr[akc(repo.contact)] = {
-            "label": akc(repo.contact),
-            "data": [0, 1],
-            "schema": ["commits"],
-        }
+        stats = cur_stats if cur_stats else Statistics()
+        stats.commit_activity = json.dumps(co_day_auth_aggr)
+        stats.commit_activity_combined = json.dumps(overview_data)
 
-    stats = cur_stats if cur_stats else Statistics()
-    stats.commit_activity = json.dumps(co_day_auth_aggr)
-    stats.commit_activity_combined = json.dumps(overview_data)
+        log.debug('last revison %s', last_rev)
+        leftovers = len(repo.revisions[last_rev:])
+        log.debug('revisions to parse %s', leftovers)
 
-    log.debug('last revison %s', last_rev)
-    leftovers = len(repo.revisions[last_rev:])
-    log.debug('revisions to parse %s', leftovers)
+        if last_rev == 0 or leftovers < parse_limit:
+            log.debug('getting code trending stats')
+            stats.languages = json.dumps(__get_codes_stats(repo_name))
 
-    if last_rev == 0 or leftovers < parse_limit:
-        log.debug('getting code trending stats')
-        stats.languages = json.dumps(__get_codes_stats(repo_name))
+        try:
+            stats.repository = dbrepo
+            stats.stat_on_revision = last_cs.revision if last_cs else 0
+            sa.add(stats)
+            sa.commit()
+        except:
+            log.error(traceback.format_exc())
+            sa.rollback()
+            lock.release()
+            return False
 
-    try:
-        stats.repository = dbrepo
-        stats.stat_on_revision = last_cs.revision if last_cs else 0
-        sa.add(stats)
-        sa.commit()
-    except:
-        log.error(traceback.format_exc())
-        sa.rollback()
-        return False
-    if len(repo.revisions) > 1:
-        run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
+        #final release
+        lock.release()
 
-    return True
+        #execute another task if celery is enabled
+        if len(repo.revisions) > 1 and CELERY_ON:
+            run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
+        return True
+    except LockHeld:
+        log.info('LockHeld')
+        return 'Task with key %s already running' % lockkey
 
 
 @task(ignore_result=True)
@@ -313,7 +330,6 @@
     """
     Sends an email with defined parameters from the .ini files.
 
-
     :param recipients: list of recipients, it this is empty the defined email
         address from field 'email_to' is used instead
     :param subject: subject of the mail
@@ -351,14 +367,14 @@
 
 @task(ignore_result=True)
 def create_repo_fork(form_data, cur_user):
+    from rhodecode.model.repo import RepoModel
+    from vcs import get_backend
+
     try:
         log = create_repo_fork.get_logger()
     except:
         log = logging.getLogger(__name__)
 
-    from rhodecode.model.repo import RepoModel
-    from vcs import get_backend
-
     repo_model = RepoModel(get_session())
     repo_model.create(form_data, cur_user, just_db=True, fork=True)
     repo_name = form_data['repo_name']
--- a/rhodecode/lib/db_manage.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/db_manage.py	Tue Apr 26 14:03:00 2011 +0200
@@ -36,13 +36,14 @@
 from rhodecode.lib.auth import get_crypt_password, generate_api_key
 from rhodecode.lib.utils import ask_ok
 from rhodecode.model import init_model
-from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
-    UserToPerm, DbMigrateVersion
+from rhodecode.model.db import User, Permission, RhodeCodeUi, \
+    RhodeCodeSettings, UserToPerm, DbMigrateVersion
 
 from sqlalchemy.engine import create_engine
 
 log = logging.getLogger(__name__)
 
+
 class DbManage(object):
     def __init__(self, log_sql, dbconf, root, tests=False):
         self.dbname = dbconf.split('/')[-1]
@@ -76,8 +77,6 @@
         meta.Base.metadata.create_all(checkfirst=checkfirst)
         log.info('Created tables for %s', self.dbname)
 
-
-
     def set_db_version(self):
         try:
             ver = DbMigrateVersion()
@@ -91,7 +90,6 @@
             raise
         log.info('db version set to: %s', __dbversion__)
 
-
     def upgrade(self):
         """Upgrades given database schema to given revision following
         all needed steps, to perform the upgrade
@@ -170,8 +168,6 @@
             print ('performing upgrade step %s' % step)
             callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
 
-
-
     def fix_repo_paths(self):
         """Fixes a old rhodecode version path into new one without a '*'
         """
@@ -225,9 +221,9 @@
         if not self.tests:
             import getpass
 
-
             def get_password():
-                password = getpass.getpass('Specify admin password (min 6 chars):')
+                password = getpass.getpass('Specify admin password '
+                                           '(min 6 chars):')
                 confirm = getpass.getpass('Confirm password:')
 
                 if password != confirm:
@@ -252,9 +248,12 @@
             self.create_user(username, password, email, True)
         else:
             log.info('creating admin and regular test users')
-            self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
-            self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
-            self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
+            self.create_user('test_admin', 'test12',
+                             'test_admin@mail.com', True)
+            self.create_user('test_regular', 'test12',
+                             'test_regular@mail.com', False)
+            self.create_user('test_regular2', 'test12',
+                             'test_regular2@mail.com', False)
 
     def create_ui_settings(self):
         """Creates ui settings, fills out hooks
@@ -308,7 +307,6 @@
             self.sa.rollback()
             raise
 
-
     def create_ldap_options(self):
         """Creates ldap settings"""
 
@@ -321,7 +319,6 @@
                         ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
                         ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
 
-
                 setting = RhodeCodeSettings(k, v)
                 self.sa.add(setting)
             self.sa.commit()
@@ -353,14 +350,12 @@
             log.error('No write permission to given path: %s [%s/3]',
                       path, retries)
 
-
         if retries == 0:
             sys.exit()
         if path_ok is False:
             retries -= 1
             return self.config_prompt(test_repo_path, retries)
 
-
         return path
 
     def create_settings(self, path):
@@ -393,12 +388,10 @@
         paths.ui_key = '/'
         paths.ui_value = path
 
-
         hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
         hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
         hgsettings3 = RhodeCodeSettings('ga_code', '')
 
-
         try:
             self.sa.add(web1)
             self.sa.add(web2)
@@ -467,8 +460,13 @@
                  ('hg.create.repository', 'Repository create'),
                  ('hg.create.none', 'Repository creation disabled'),
                  ('hg.register.none', 'Register disabled'),
-                 ('hg.register.manual_activate', 'Register new user with RhodeCode without manual activation'),
-                 ('hg.register.auto_activate', 'Register new user with RhodeCode without auto activation'),
+                 ('hg.register.manual_activate', 'Register new user with '
+                                                 'RhodeCode without manual'
+                                                 'activation'),
+
+                 ('hg.register.auto_activate', 'Register new user with '
+                                               'RhodeCode without auto '
+                                               'activation'),
                 ]
 
         for p in perms:
--- a/rhodecode/lib/dbmigrate/versions/003_version_1_2_0.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/dbmigrate/versions/003_version_1_2_0.py	Tue Apr 26 14:03:00 2011 +0200
@@ -10,7 +10,6 @@
 from rhodecode.lib.dbmigrate.migrate.changeset import *
 
 from rhodecode.model.meta import Base
-from rhodecode.model.db import BaseModel
 
 log = logging.getLogger(__name__)
 
@@ -44,12 +43,17 @@
     UsersGroupMember().__table__.create()
 
     #==========================================================================
+    # Add table `users_group_repo_to_perm`
+    #==========================================================================
+    from rhodecode.model.db import UsersGroupRepoToPerm
+    UsersGroupRepoToPerm().__table__.create()
+
+    #==========================================================================
     # Add table `users_group_to_perm`
     #==========================================================================
     from rhodecode.model.db import UsersGroupToPerm
     UsersGroupToPerm().__table__.create()
 
-
     #==========================================================================
     # Upgrade of `users` table
     #==========================================================================
--- a/rhodecode/lib/helpers.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/helpers.py	Tue Apr 26 14:03:00 2011 +0200
@@ -622,7 +622,8 @@
         suf = ''
         if len(nodes) > 30:
             suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
-        return literal(pref + '<br/> '.join([safe_unicode(x.path) for x in nodes[:30]]) + suf)
+        return literal(pref + '<br/> '.join([safe_unicode(x.path)
+                                             for x in nodes[:30]]) + suf)
     else:
         return ': ' + _('No Files')
 
@@ -635,6 +636,56 @@
         return repo_name
     else:
         def make_link(group):
-            return link_to(group.group_name, url('repos_group', id=group.group_id))
+            return link_to(group.group_name, url('repos_group',
+                                                 id=group.group_id))
         return literal(' &raquo; '.join(map(make_link, groups)) + \
                        " &raquo; " + repo_name)
+
+
+def fancy_file_stats(stats):
+    a, d, t = stats[0], stats[1], stats[0] + stats[1]
+    width = 100
+    unit = float(width) / (t or 1)
+
+    a_p = max(9, unit * a) if a > 0 else 0# needs > 9% to be visible
+    d_p = max(9, unit * d) if d > 0 else 0 # needs > 9% to be visible
+    p_sum = a_p + d_p
+
+    if p_sum > width:
+        #adjust the percentage to be == 100% since we adjusted to 9
+        if a_p > d_p:
+            a_p = a_p - (p_sum - width)
+        else:
+            d_p = d_p - (p_sum - width)
+
+    a_v = a if a > 0 else ''
+    d_v = d if d > 0 else ''
+
+
+    def cgen(l_type):
+        mapping = {'tr':'top-right-rounded-corner',
+                   'tl':'top-left-rounded-corner',
+                   'br':'bottom-right-rounded-corner',
+                   'bl':'bottom-left-rounded-corner'}
+        map_getter = lambda x:mapping[x]
+
+        if l_type == 'a' and d_v:
+            #case when added and deleted are present
+            return ' '.join(map(map_getter, ['tl', 'bl']))
+
+        if l_type == 'a' and not d_v:
+            return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
+
+        if l_type == 'd' and a_v:
+            return ' '.join(map(map_getter, ['tr', 'br']))
+
+        if l_type == 'd' and not a_v:
+            return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
+
+
+
+    d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
+                                                                 a_p, a_v)
+    d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
+                                                                   d_p, d_v)
+    return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
--- a/rhodecode/lib/middleware/https_fixup.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/middleware/https_fixup.py	Tue Apr 26 14:03:00 2011 +0200
@@ -25,7 +25,9 @@
 
 from rhodecode.lib import str2bool
 
+
 class HttpsFixup(object):
+
     def __init__(self, app, config):
         self.application = app
         self.config = config
@@ -34,9 +36,9 @@
         self.__fixup(environ)
         return self.application(environ, start_response)
 
-
     def __fixup(self, environ):
-        """Function to fixup the environ as needed. In order to use this
+        """
+        Function to fixup the environ as needed. In order to use this
         middleware you should set this header inside your
         proxy ie. nginx, apache etc.
         """
--- a/rhodecode/lib/middleware/simplegit.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/middleware/simplegit.py	Tue Apr 26 14:03:00 2011 +0200
@@ -30,13 +30,15 @@
 
 from dulwich import server as dulserver
 
+
 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
 
     def handle(self):
         write = lambda x: self.proto.write_sideband(1, x)
 
-        graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
-            self.repo.get_peeled)
+        graph_walker = dulserver.ProtocolGraphWalker(self,
+                                                     self.repo.object_store,
+                                                     self.repo.get_peeled)
         objects_iter = self.repo.fetch_objects(
           graph_walker.determine_wants, graph_walker, self.progress,
           get_tagged=self.get_tagged)
@@ -46,8 +48,8 @@
             return
 
         self.progress("counting objects: %d, done.\n" % len(objects_iter))
-        dulserver.write_pack_data(dulserver.ProtocolFile(None, write), objects_iter,
-                        len(objects_iter))
+        dulserver.write_pack_data(dulserver.ProtocolFile(None, write),
+                                  objects_iter, len(objects_iter))
         messages = []
         messages.append('thank you for using rhodecode')
 
@@ -75,6 +77,7 @@
 
 log = logging.getLogger(__name__)
 
+
 def is_git(environ):
     """Returns True if request's target is git server.
     ``HTTP_USER_AGENT`` would then have git client version given.
@@ -86,6 +89,7 @@
         return True
     return False
 
+
 class SimpleGit(object):
 
     def __init__(self, application, config):
@@ -123,16 +127,17 @@
         #======================================================================
         # CHECK ANONYMOUS PERMISSION
         #======================================================================
-        if self.action in ['pull', 'push'] or self.action:
+        if self.action in ['pull', 'push']:
             anonymous_user = self.__get_user('default')
             self.username = anonymous_user.username
-            anonymous_perm = self.__check_permission(self.action, anonymous_user ,
-                                           self.repo_name)
+            anonymous_perm = self.__check_permission(self.action,
+                                                     anonymous_user,
+                                                     self.repo_name)
 
             if anonymous_perm is not True or anonymous_user.active is False:
                 if anonymous_perm is not True:
-                    log.debug('Not enough credentials to access this repository'
-                              'as anonymous user')
+                    log.debug('Not enough credentials to access this '
+                              'repository as anonymous user')
                 if anonymous_user.active is False:
                     log.debug('Anonymous access is disabled, running '
                               'authentication')
@@ -142,7 +147,8 @@
                 #==============================================================
 
                 if not REMOTE_USER(environ):
-                    self.authenticate.realm = str(self.config['rhodecode_realm'])
+                    self.authenticate.realm = str(
+                                                self.config['rhodecode_realm'])
                     result = self.authenticate(environ)
                     if isinstance(result, str):
                         AUTH_TYPE.update(environ, 'basic')
@@ -150,31 +156,31 @@
                     else:
                         return result.wsgi_application(environ, start_response)
 
-
                 #==============================================================
                 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
                 # BASIC AUTH
                 #==============================================================
 
-                if self.action in ['pull', 'push']  or self.action:
+                if self.action in ['pull', 'push']:
                     username = REMOTE_USER(environ)
                     try:
                         user = self.__get_user(username)
                         self.username = user.username
                     except:
                         log.error(traceback.format_exc())
-                        return HTTPInternalServerError()(environ, start_response)
+                        return HTTPInternalServerError()(environ,
+                                                         start_response)
 
                     #check permissions for this repository
-                    perm = self.__check_permission(self.action, user, self.repo_name)
+                    perm = self.__check_permission(self.action, user,
+                                                   self.repo_name)
                     if perm is not True:
-                        print 'not allowed'
                         return HTTPForbidden()(environ, start_response)
 
-        self.extras = {'ip':self.ipaddr,
-                       'username':self.username,
-                       'action':self.action,
-                       'repository':self.repo_name}
+        self.extras = {'ip': self.ipaddr,
+                       'username': self.username,
+                       'action': self.action,
+                       'repository': self.repo_name}
 
         #===================================================================
         # GIT REQUEST HANDLING
@@ -193,15 +199,12 @@
         #invalidate cache on push
         if self.action == 'push':
             self.__invalidate_cache(self.repo_name)
-            messages = []
-            messages.append('thank you for using rhodecode')
-            return app(environ, start_response)
-        else:
-            return app(environ, start_response)
 
+        return app(environ, start_response)
 
     def __make_app(self):
-        backend = dulserver.DictBackend({'/' + self.repo_name: Repo(self.repo_path)})
+        _d = {'/' + self.repo_name: Repo(self.repo_path)}
+        backend = dulserver.DictBackend(_d)
         gitserve = HTTPGitApplication(backend)
 
         return gitserve
@@ -216,21 +219,20 @@
         """
         if action == 'push':
             if not HasPermissionAnyMiddleware('repository.write',
-                                              'repository.admin')\
-                                                (user, repo_name):
+                                              'repository.admin')(user,
+                                                                  repo_name):
                 return False
 
         else:
             #any other action need at least read permission
             if not HasPermissionAnyMiddleware('repository.read',
                                               'repository.write',
-                                              'repository.admin')\
-                                                (user, repo_name):
+                                              'repository.admin')(user,
+                                                                  repo_name):
                 return False
 
         return True
 
-
     def __get_repository(self, environ):
         """Get's repository name out of PATH_INFO header
 
@@ -246,7 +248,6 @@
         repo_name = repo_name.split('/')[0]
         return repo_name
 
-
     def __get_user(self, username):
         return UserModel().get_by_username(username, cache=True)
 
@@ -262,7 +263,8 @@
                        'git-upload-pack': 'pull',
                        }
 
-            return mapping.get(service_cmd, service_cmd if service_cmd else 'other')
+            return mapping.get(service_cmd,
+                               service_cmd if service_cmd else 'other')
         else:
             return 'other'
 
--- a/rhodecode/lib/middleware/simplehg.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/lib/middleware/simplehg.py	Tue Apr 26 14:03:00 2011 +0200
@@ -44,6 +44,7 @@
 
 log = logging.getLogger(__name__)
 
+
 def is_mercurial(environ):
     """Returns True if request's target is mercurial server - header
     ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
@@ -53,6 +54,7 @@
         return True
     return False
 
+
 class SimpleHg(object):
 
     def __init__(self, application, config):
@@ -93,13 +95,14 @@
         if self.action in ['pull', 'push']:
             anonymous_user = self.__get_user('default')
             self.username = anonymous_user.username
-            anonymous_perm = self.__check_permission(self.action, anonymous_user ,
-                                           self.repo_name)
+            anonymous_perm = self.__check_permission(self.action,
+                                                     anonymous_user,
+                                                     self.repo_name)
 
             if anonymous_perm is not True or anonymous_user.active is False:
                 if anonymous_perm is not True:
-                    log.debug('Not enough credentials to access this repository'
-                              'as anonymous user')
+                    log.debug('Not enough credentials to access this '
+                              'repository as anonymous user')
                 if anonymous_user.active is False:
                     log.debug('Anonymous access is disabled, running '
                               'authentication')
@@ -109,7 +112,8 @@
                 #==============================================================
 
                 if not REMOTE_USER(environ):
-                    self.authenticate.realm = str(self.config['rhodecode_realm'])
+                    self.authenticate.realm = str(
+                                                self.config['rhodecode_realm'])
                     result = self.authenticate(environ)
                     if isinstance(result, str):
                         AUTH_TYPE.update(environ, 'basic')
@@ -117,7 +121,6 @@
                     else:
                         return result.wsgi_application(environ, start_response)
 
-
                 #==============================================================
                 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
                 # BASIC AUTH
@@ -130,22 +133,24 @@
                         self.username = user.username
                     except:
                         log.error(traceback.format_exc())
-                        return HTTPInternalServerError()(environ, start_response)
+                        return HTTPInternalServerError()(environ,
+                                                         start_response)
 
                     #check permissions for this repository
-                    perm = self.__check_permission(self.action, user, self.repo_name)
+                    perm = self.__check_permission(self.action, user,
+                                                   self.repo_name)
                     if perm is not True:
                         return HTTPForbidden()(environ, start_response)
 
-        self.extras = {'ip':self.ipaddr,
-                       'username':self.username,
-                       'action':self.action,
-                       'repository':self.repo_name}
+        self.extras = {'ip': self.ipaddr,
+                       'username': self.username,
+                       'action': self.action,
+                       'repository': self.repo_name}
 
-        #===================================================================
+        #======================================================================
         # MERCURIAL REQUEST HANDLING
-        #===================================================================
-        environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
+        #======================================================================
+        environ['PATH_INFO'] = '/'  # since we wrap into hgweb, reset the path
         self.baseui = make_ui('db')
         self.basepath = self.config['base_path']
         self.repo_path = os.path.join(self.basepath, self.repo_name)
@@ -168,18 +173,18 @@
 
         return app(environ, start_response)
 
-
     def __make_app(self):
-        """Make an wsgi application using hgweb, and my generated baseui
-        instance
         """
-
-        hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
-        return  self.__load_web_settings(hgserve, self.extras)
+        Make an wsgi application using hgweb, and inject generated baseui
+        instance, additionally inject some extras into ui object
+        """
+        self.__inject_extras(self.baseui, self.extras)
+        return hgweb(str(self.repo_path), baseui=self.baseui)
 
 
     def __check_permission(self, action, user, repo_name):
-        """Checks permissions using action (push/pull) user and repository
+        """
+        Checks permissions using action (push/pull) user and repository
         name
 
         :param action: push or pull action
@@ -188,23 +193,23 @@
         """
         if action == 'push':
             if not HasPermissionAnyMiddleware('repository.write',
-                                              'repository.admin')\
-                                                (user, repo_name):
+                                              'repository.admin')(user,
+                                                                  repo_name):
                 return False
 
         else:
             #any other action need at least read permission
             if not HasPermissionAnyMiddleware('repository.read',
                                               'repository.write',
-                                              'repository.admin')\
-                                                (user, repo_name):
+                                              'repository.admin')(user,
+                                                                  repo_name):
                 return False
 
         return True
 
-
     def __get_repository(self, environ):
-        """Get's repository name out of PATH_INFO header
+        """
+        Get's repository name out of PATH_INFO header
 
         :param environ: environ where PATH_INFO is stored
         """
@@ -222,7 +227,8 @@
         return UserModel().get_by_username(username, cache=True)
 
     def __get_action(self, environ):
-        """Maps mercurial request commands into a clone,pull or push command.
+        """
+        Maps mercurial request commands into a clone,pull or push command.
         This should always return a valid command string
 
         :param environ:
@@ -236,7 +242,7 @@
         for qry in environ['QUERY_STRING'].split('&'):
             if qry.startswith('cmd'):
                 cmd = qry.split('=')[-1]
-                if mapping.has_key(cmd):
+                if cmd in mapping:
                     return mapping[cmd]
                 else:
                     return 'pull'
@@ -247,17 +253,14 @@
         push requests"""
         invalidate_cache('get_repo_cached_%s' % repo_name)
 
-
-    def __load_web_settings(self, hgserve, extras={}):
-        #set the global ui for hgserve instance passed
-        hgserve.repo.ui = self.baseui
+    def __inject_extras(self, baseui, extras={}):
 
         hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
 
         #inject some additional parameters that will be available in ui
         #for hooks
         for k, v in extras.items():
-            hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
+            baseui.setconfig('rhodecode_extras', k, v)
 
         repoui = make_ui('file', hgrc, False)
 
@@ -265,6 +268,4 @@
             #overwrite our ui instance with the section from hgrc file
             for section in ui_sections:
                 for k, v in repoui.configitems(section):
-                    hgserve.repo.ui.setconfig(section, k, v)
-
-        return hgserve
+                    baseui.repo.ui.setconfig(section, k, v)
--- a/rhodecode/model/__init__.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/__init__.py	Tue Apr 26 14:03:00 2011 +0200
@@ -47,16 +47,19 @@
 
 log = logging.getLogger(__name__)
 
+
 def init_model(engine):
-    """Initializes db session, bind the engine with the metadata,
-    Call this before using any of the tables or classes in the model, preferably
-    once in application start
+    """
+    Initializes db session, bind the engine with the metadata,
+    Call this before using any of the tables or classes in the model,
+    preferably once in application start
 
     :param engine: engine to bind to
     """
     log.info("initializing db for %s", engine)
     meta.Base.metadata.bind = engine
 
+
 class BaseModel(object):
     """Base Model for all RhodeCode models, it adds sql alchemy session
     into instance of model
--- a/rhodecode/model/caching_query.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/caching_query.py	Tue Apr 26 14:03:00 2011 +0200
@@ -18,11 +18,13 @@
 Beaker constructs.
 
 """
+import beaker
 from beaker.exceptions import BeakerException
+
 from sqlalchemy.orm.interfaces import MapperOption
 from sqlalchemy.orm.query import Query
 from sqlalchemy.sql import visitors
-import beaker
+
 
 class CachingQuery(Query):
     """A Query subclass which optionally loads full results from a Beaker
@@ -74,7 +76,8 @@
 
         """
         if hasattr(self, '_cache_parameters'):
-            return self.get_value(createfunc=lambda: list(Query.__iter__(self)))
+            return self.get_value(createfunc=lambda:
+                                  list(Query.__iter__(self)))
         else:
             return Query.__iter__(self)
 
@@ -103,11 +106,13 @@
         cache, cache_key = _get_cache_parameters(self)
         cache.put(cache_key, value)
 
+
 def query_callable(manager):
     def query(*arg, **kw):
         return CachingQuery(manager, *arg, **kw)
     return query
 
+
 def get_cache_region(name, region):
     if region not in beaker.cache.cache_regions:
         raise BeakerException('Cache region `%s` not configured '
@@ -115,6 +120,7 @@
     kw = beaker.cache.cache_regions[region]
     return beaker.cache.Cache._get_cache(name, kw)
 
+
 def _get_cache_parameters(query):
     """For a query with cache_region and cache_namespace configured,
     return the correspoinding Cache instance and cache key, based
@@ -122,7 +128,8 @@
 
     """
     if not hasattr(query, '_cache_parameters'):
-        raise ValueError("This Query does not have caching parameters configured.")
+        raise ValueError("This Query does not have caching "
+                         "parameters configured.")
 
     region, namespace, cache_key = query._cache_parameters
 
@@ -142,6 +149,7 @@
 
     return cache, cache_key
 
+
 def _namespace_from_query(namespace, query):
     # cache namespace - the token handed in by the
     # option + class we're querying against
@@ -152,6 +160,7 @@
 
     return namespace
 
+
 def _set_cache_parameters(query, region, namespace, cache_key):
 
     if hasattr(query, '_cache_parameters'):
@@ -162,6 +171,7 @@
                     )
     query._cache_parameters = region, namespace, cache_key
 
+
 class FromCache(MapperOption):
     """Specifies that a Query should load results from a cache."""
 
@@ -191,7 +201,9 @@
     def process_query(self, query):
         """Process a Query during normal loading operation."""
 
-        _set_cache_parameters(query, self.region, self.namespace, self.cache_key)
+        _set_cache_parameters(query, self.region, self.namespace,
+                              self.cache_key)
+
 
 class RelationshipCache(MapperOption):
     """Specifies that a Query as called within a "lazy load"
@@ -217,7 +229,7 @@
         self.region = region
         self.namespace = namespace
         self._relationship_options = {
-            (attribute.property.parent.class_, attribute.property.key) : self
+            (attribute.property.parent.class_, attribute.property.key): self
         }
 
     def process_query_conditionally(self, query):
@@ -232,7 +244,8 @@
 
             for cls in mapper.class_.__mro__:
                 if (cls, key) in self._relationship_options:
-                    relationship_option = self._relationship_options[(cls, key)]
+                    relationship_option = \
+                        self._relationship_options[(cls, key)]
                     _set_cache_parameters(
                             query,
                             relationship_option.region,
@@ -261,6 +274,7 @@
 
     """
     v = []
+
     def visit_bindparam(bind):
         value = query._params.get(bind.key, bind.value)
 
@@ -272,5 +286,5 @@
 
         v.append(value)
     if query._criterion is not None:
-        visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam})
+        visitors.traverse(query._criterion, {}, {'bindparam': visit_bindparam})
     return v
--- a/rhodecode/model/db.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/db.py	Tue Apr 26 14:03:00 2011 +0200
@@ -33,7 +33,9 @@
 from sqlalchemy.orm import relationship, backref
 from sqlalchemy.orm.interfaces import MapperExtension
 
+from rhodecode.lib import str2bool
 from rhodecode.model.meta import Base, Session
+from rhodecode.model.caching_query import FromCache
 
 log = logging.getLogger(__name__)
 
@@ -61,6 +63,35 @@
         return "<%s('%s:%s')>" % (self.__class__.__name__,
                                   self.app_settings_name, self.app_settings_value)
 
+
+    @classmethod
+    def get_app_settings(cls, cache=False):
+
+        ret = Session.query(cls)
+
+        if cache:
+            ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
+
+        if not ret:
+            raise Exception('Could not get application settings !')
+        settings = {}
+        for each in ret:
+            settings['rhodecode_' + each.app_settings_name] = \
+                each.app_settings_value
+
+        return settings
+
+    @classmethod
+    def get_ldap_settings(cls, cache=False):
+        ret = Session.query(cls)\
+                .filter(cls.app_settings_name.startswith('ldap_'))\
+                .all()
+        fd = {}
+        for row in ret:
+            fd.update({row.app_settings_name:str2bool(row.app_settings_value)})
+        return fd
+
+
 class RhodeCodeUi(Base):
     __tablename__ = 'rhodecode_ui'
     __table_args__ = {'useexisting':True}
@@ -158,6 +189,20 @@
 
     members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
 
+
+    @classmethod
+    def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
+        if case_insensitive:
+            gr = Session.query(cls)\
+            .filter(cls.users_group_name.ilike(group_name))
+        else:
+            gr = Session.query(UsersGroup)\
+                .filter(UsersGroup.users_group_name == group_name)
+        if cache:
+            gr = gr.options(FromCache("sql_cache_short",
+                                          "get_user_%s" % group_name))
+        return gr.scalar()
+
 class UsersGroupMember(Base):
     __tablename__ = 'users_groups_members'
     __table_args__ = {'useexisting':True}
@@ -195,7 +240,7 @@
     fork = relationship('Repository', remote_side=repo_id)
     group = relationship('Group')
     repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
-    users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
+    users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
     stats = relationship('Statistics', cascade='all', uselist=False)
 
     followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
@@ -285,6 +330,10 @@
         return "<%s('%s:%s')>" % (self.__class__.__name__,
                                   self.permission_id, self.permission_name)
 
+    @classmethod
+    def get_by_key(cls, key):
+        return Session.query(cls).filter(cls.permission_name == key).scalar()
+
 class RepoToPerm(Base):
     __tablename__ = 'repo_to_perm'
     __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
@@ -307,9 +356,43 @@
     user = relationship('User')
     permission = relationship('Permission')
 
+    @classmethod
+    def has_perm(cls, user_id, perm):
+        if not isinstance(perm, Permission):
+            raise Exception('perm needs to be an instance of Permission class')
 
-class UsersGroupToPerm(Base):
-    __tablename__ = 'users_group_to_perm'
+        return Session.query(cls).filter(cls.user_id == user_id)\
+            .filter(cls.permission == perm).scalar() is not None
+
+    @classmethod
+    def grant_perm(cls, user_id, perm):
+        if not isinstance(perm, Permission):
+            raise Exception('perm needs to be an instance of Permission class')
+
+        new = cls()
+        new.user_id = user_id
+        new.permission = perm
+        try:
+            Session.add(new)
+            Session.commit()
+        except:
+            Session.rollback()
+
+
+    @classmethod
+    def revoke_perm(cls, user_id, perm):
+        if not isinstance(perm, Permission):
+            raise Exception('perm needs to be an instance of Permission class')
+
+        try:
+            Session.query(cls).filter(cls.user_id == user_id)\
+                .filter(cls.permission == perm).delete()
+            Session.commit()
+        except:
+            Session.rollback()
+
+class UsersGroupRepoToPerm(Base):
+    __tablename__ = 'users_group_repo_to_perm'
     __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
     users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
     users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
@@ -320,6 +403,55 @@
     permission = relationship('Permission')
     repository = relationship('Repository')
 
+
+class UsersGroupToPerm(Base):
+    __tablename__ = 'users_group_to_perm'
+    users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
+    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+    permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+    users_group = relationship('UsersGroup')
+    permission = relationship('Permission')
+
+
+    @classmethod
+    def has_perm(cls, users_group_id, perm):
+        if not isinstance(perm, Permission):
+            raise Exception('perm needs to be an instance of Permission class')
+
+        return Session.query(cls).filter(cls.users_group_id ==
+                                         users_group_id)\
+                                         .filter(cls.permission == perm)\
+                                         .scalar() is not None
+
+    @classmethod
+    def grant_perm(cls, users_group_id, perm):
+        if not isinstance(perm, Permission):
+            raise Exception('perm needs to be an instance of Permission class')
+
+        new = cls()
+        new.users_group_id = users_group_id
+        new.permission = perm
+        try:
+            Session.add(new)
+            Session.commit()
+        except:
+            Session.rollback()
+
+
+    @classmethod
+    def revoke_perm(cls, users_group_id, perm):
+        if not isinstance(perm, Permission):
+            raise Exception('perm needs to be an instance of Permission class')
+
+        try:
+            Session.query(cls).filter(cls.users_group_id == users_group_id)\
+                .filter(cls.permission == perm).delete()
+            Session.commit()
+        except:
+            Session.rollback()
+
+
 class GroupToPerm(Base):
     __tablename__ = 'group_to_perm'
     __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
@@ -355,12 +487,19 @@
     user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
     follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
     follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
+    follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
 
     user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
 
     follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
     follows_repository = relationship('Repository', order_by='Repository.repo_name')
 
+
+
+    @classmethod
+    def get_repo_followers(cls, repo_id):
+        return Session.query(cls).filter(cls.follows_repo_id == repo_id)
+
 class CacheInvalidation(Base):
     __tablename__ = 'cache_invalidation'
     __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
--- a/rhodecode/model/forms.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/forms.py	Tue Apr 26 14:03:00 2011 +0200
@@ -37,7 +37,6 @@
 from rhodecode.model import meta
 from rhodecode.model.user import UserModel
 from rhodecode.model.repo import RepoModel
-from rhodecode.model.users_group import UsersGroupModel
 from rhodecode.model.db import User, UsersGroup
 from rhodecode import BACKENDS
 
@@ -47,9 +46,9 @@
 class State_obj(object):
     _ = staticmethod(_)
 
-#===============================================================================
+#==============================================================================
 # VALIDATORS
-#===============================================================================
+#==============================================================================
 class ValidAuthToken(formencode.validators.FancyValidator):
     messages = {'invalid_token':_('Token mismatch')}
 
@@ -73,23 +72,19 @@
             if old_un != value or not edit:
                 if UserModel().get_by_username(value, cache=False,
                                                case_insensitive=True):
-                    raise formencode.Invalid(_('This username already exists') ,
-                                             value, state)
-
+                    raise formencode.Invalid(_('This username already '
+                                               'exists') , value, state)
 
             if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
                 raise formencode.Invalid(_('Username may only contain '
-                                           'alphanumeric characters underscores, '
-                                           'periods or dashes and must begin with '
-                                           'alphanumeric character'),
-                                      value, state)
-
-
+                                           'alphanumeric characters '
+                                           'underscores, periods or dashes '
+                                           'and must begin with alphanumeric '
+                                           'character'), value, state)
 
     return _ValidUsername
 
 
-
 def ValidUsersGroup(edit, old_data):
 
     class _ValidUsersGroup(formencode.validators.FancyValidator):
@@ -100,22 +95,23 @@
             #check if group is unique
             old_ugname = None
             if edit:
-                old_ugname = UsersGroupModel()\
-                    .get(old_data.get('users_group_id')).users_group_name
+                old_ugname = UsersGroup.get(
+                            old_data.get('users_group_id')).users_group_name
 
             if old_ugname != value or not edit:
-                if UsersGroupModel().get_by_groupname(value, cache=False,
+                if UsersGroup.get_by_group_name(value, cache=False,
                                                case_insensitive=True):
-                    raise formencode.Invalid(_('This users group already exists') ,
-                                             value, state)
+                    raise formencode.Invalid(_('This users group '
+                                               'already exists') , value,
+                                             state)
 
 
             if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
                 raise formencode.Invalid(_('Group name may only contain '
-                                           'alphanumeric characters underscores, '
-                                           'periods or dashes and must begin with '
-                                           'alphanumeric character'),
-                                      value, state)
+                                           'alphanumeric characters '
+                                           'underscores, periods or dashes '
+                                           'and must begin with alphanumeric '
+                                           'character'), value, state)
 
     return _ValidUsersGroup
 
@@ -226,13 +222,40 @@
 
     return _ValidRepoName
 
+def ValidCloneUri():
+    from mercurial.httprepo import httprepository, httpsrepository
+    from rhodecode.lib.utils import make_ui
+
+    class _ValidCloneUri(formencode.validators.FancyValidator):
+        def to_python(self, value, state):
+            if not value:
+                pass
+            elif value.startswith('https'):
+                try:
+                    httpsrepository(make_ui('db'), value).capabilities()
+                except:
+                    raise formencode.Invalid(_('invalid clone url'), value,
+                                             state)
+            elif value.startswith('http'):
+                try:
+                    httprepository(make_ui('db'), value).capabilities()
+                except:
+                    raise formencode.Invalid(_('invalid clone url'), value,
+                                             state)
+            else:
+                raise formencode.Invalid(_('Invalid clone url, provide a '
+                                           'valid clone http\s url'), value,
+                                         state)
+
+    return _ValidCloneUri
+
 def ValidForkType(old_data):
     class _ValidForkType(formencode.validators.FancyValidator):
 
         def to_python(self, value, state):
             if old_data['repo_type'] != value:
-                raise formencode.Invalid(_('Fork have to be the same type as original'),
-                                         value, state)
+                raise formencode.Invalid(_('Fork have to be the same '
+                                           'type as original'), value, state)
             return value
     return _ValidForkType
 
@@ -457,7 +480,8 @@
         filter_extra_fields = False
         repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
                         ValidRepoName(edit, old_data))
-        clone_uri = UnicodeString(strip=True, min=1, not_empty=False)
+        clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
+                        ValidCloneUri()())
         repo_group = OneOf(repo_groups, hideList=True)
         repo_type = OneOf(supported_backends)
         description = UnicodeString(strip=True, min=1, not_empty=True)
--- a/rhodecode/model/meta.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/meta.py	Tue Apr 26 14:03:00 2011 +0200
@@ -19,6 +19,7 @@
                 )
           )
 
+
 class BaseModel(object):
     """Base Model for all classess
 
--- a/rhodecode/model/permission.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/permission.py	Tue Apr 26 14:03:00 2011 +0200
@@ -66,8 +66,10 @@
 
     def update(self, form_result):
         perm_user = self.sa.query(User)\
-                .filter(User.username == form_result['perm_user_name']).scalar()
-        u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
+                .filter(User.username ==
+                        form_result['perm_user_name']).scalar()
+        u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
+                                               perm_user).all()
         if len(u2p) != 3:
             raise Exception('Defined: %s should be 3  permissions for default'
                             ' user. This should not happen please verify'
@@ -104,7 +106,6 @@
                 perm_user.active = bool(form_result['anonymous'])
                 self.sa.add(perm_user)
 
-
             self.sa.commit()
         except (DatabaseError,):
             log.error(traceback.format_exc())
--- a/rhodecode/model/repo.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/repo.py	Tue Apr 26 14:03:00 2011 +0200
@@ -36,13 +36,12 @@
 from rhodecode.model import BaseModel
 from rhodecode.model.caching_query import FromCache
 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
-    Statistics, UsersGroup, UsersGroupToPerm, RhodeCodeUi
+    Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi
 from rhodecode.model.user import UserModel
-from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
-
 
 log = logging.getLogger(__name__)
 
+
 class RepoModel(BaseModel):
 
     @LazyProperty
@@ -62,7 +61,6 @@
                                           "get_repo_%s" % repo_id))
         return repo.scalar()
 
-
     def get_by_repo_name(self, repo_name, cache=False):
         repo = self.sa.query(Repository)\
             .filter(Repository.repo_name == repo_name)
@@ -72,7 +70,6 @@
                                           "get_repo_%s" % repo_name))
         return repo.scalar()
 
-
     def get_full(self, repo_name, cache=False, invalidate=False):
         repo = self.sa.query(Repository)\
             .options(joinedload(Repository.fork))\
@@ -95,7 +92,6 @@
                 make_transient(attr)
         return ret
 
-
     def get_users_js(self):
 
         users = self.sa.query(User).filter(User.active == True).all()
@@ -105,7 +101,6 @@
                                         for u in users])
         return users_array
 
-
     def get_users_groups_js(self):
         users_groups = self.sa.query(UsersGroup)\
             .filter(UsersGroup.users_group_active == True).all()
@@ -122,29 +117,30 @@
         try:
             cur_repo = self.get_by_repo_name(repo_name, cache=False)
             user_model = UserModel(self.sa)
-            users_group_model = UsersGroupModel(self.sa)
 
             #update permissions
             for member, perm, member_type in form_data['perms_updates']:
                 if member_type == 'user':
                     r2p = self.sa.query(RepoToPerm)\
-                            .filter(RepoToPerm.user == user_model.get_by_username(member))\
+                            .filter(RepoToPerm.user == user_model.
+                                    get_by_username(member))\
                             .filter(RepoToPerm.repository == cur_repo)\
                             .one()
 
                     r2p.permission = self.sa.query(Permission)\
-                                        .filter(Permission.permission_name == perm)\
-                                        .scalar()
+                                        .filter(Permission.permission_name ==
+                                                perm).scalar()
                     self.sa.add(r2p)
                 else:
-                    g2p = self.sa.query(UsersGroupToPerm)\
-                            .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
-                            .filter(UsersGroupToPerm.repository == cur_repo)\
-                            .one()
+                    g2p = self.sa.query(UsersGroupRepoToPerm)\
+                            .filter(UsersGroupRepoToPerm.users_group ==
+                                    UsersGroup.get_by_group_name(member))\
+                            .filter(UsersGroupRepoToPerm.repository ==
+                                    cur_repo).one()
 
                     g2p.permission = self.sa.query(Permission)\
-                                        .filter(Permission.permission_name == perm)\
-                                        .scalar()
+                                        .filter(Permission.permission_name ==
+                                                perm).scalar()
                     self.sa.add(g2p)
 
             #set new permissions
@@ -155,17 +151,19 @@
                     r2p.user = user_model.get_by_username(member)
 
                     r2p.permission = self.sa.query(Permission)\
-                                        .filter(Permission.permission_name == perm)\
-                                        .scalar()
+                                        .filter(Permission.
+                                                permission_name == perm)\
+                                                .scalar()
                     self.sa.add(r2p)
                 else:
-                    g2p = UsersGroupToPerm()
+                    g2p = UsersGroupRepoToPerm()
                     g2p.repository = cur_repo
-                    g2p.users_group = users_group_model.get_by_groupname(member)
+                    g2p.users_group = UsersGroup.get_by_group_name(member)
 
                     g2p.permission = self.sa.query(Permission)\
-                                        .filter(Permission.permission_name == perm)\
-                                        .scalar()
+                                        .filter(Permission.
+                                                permission_name == perm)\
+                                                .scalar()
                     self.sa.add(g2p)
 
             #update current repo
@@ -276,10 +274,10 @@
 
     def delete_perm_users_group(self, form_data, repo_name):
         try:
-            self.sa.query(UsersGroupToPerm)\
-                .filter(UsersGroupToPerm.repository \
+            self.sa.query(UsersGroupRepoToPerm)\
+                .filter(UsersGroupRepoToPerm.repository \
                         == self.get_by_repo_name(repo_name))\
-                .filter(UsersGroupToPerm.users_group_id \
+                .filter(UsersGroupRepoToPerm.users_group_id \
                         == form_data['users_group_id']).delete()
             self.sa.commit()
         except:
@@ -298,7 +296,6 @@
             self.sa.rollback()
             raise
 
-
     def __create_repo(self, repo_name, alias, clone_uri=False):
         """
         makes repository on filesystem
--- a/rhodecode/model/scm.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/scm.py	Tue Apr 26 14:03:00 2011 +0200
@@ -140,7 +140,9 @@
                 repo, dbrepo = r_dbr
 
                 if repo is None or dbrepo is None:
-                    log.error('Repository %s looks somehow corrupted', r_name)
+                    log.error('Repository "%s" looks somehow corrupted '
+                              'fs-repo:%s,db-repo:%s both values should be '
+                              'present', r_name, repo, dbrepo)
                     continue
                 last_change = repo.last_change
                 tip = h.get_changeset_safe(repo, 'tip')
@@ -342,22 +344,18 @@
         return f is not None
 
     def get_followers(self, repo_id):
-        if isinstance(repo_id, int):
-            return self.sa.query(UserFollowing)\
-                    .filter(UserFollowing.follows_repo_id == repo_id).count()
-        else:
-            return self.sa.query(UserFollowing)\
-                    .filter(UserFollowing.follows_repository \
-                            == RepoModel().get_by_repo_name(repo_id)).count()
+        if not isinstance(repo_id, int):
+            repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
+
+        return self.sa.query(UserFollowing)\
+                .filter(UserFollowing.follows_repo_id == repo_id).count()
 
     def get_forks(self, repo_id):
-        if isinstance(repo_id, int):
-            return self.sa.query(Repository)\
+        if not isinstance(repo_id, int):
+            repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
+
+        return self.sa.query(Repository)\
                 .filter(Repository.fork_id == repo_id).count()
-        else:
-            return self.sa.query(Repository)\
-                .filter(Repository.fork \
-                        == RepoModel().get_by_repo_name(repo_id)).count()
 
     def pull_changes(self, repo_name, username):
         repo, dbrepo = self.get(repo_name, retval='all')
--- a/rhodecode/model/user.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/user.py	Tue Apr 26 14:03:00 2011 +0200
@@ -31,18 +31,21 @@
 from rhodecode.model import BaseModel
 from rhodecode.model.caching_query import FromCache
 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
-    UserToPerm, UsersGroupToPerm, UsersGroupMember
-from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
+    UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
+from rhodecode.lib.exceptions import DefaultUserException, \
+    UserOwnsReposException
 
 from sqlalchemy.exc import DatabaseError
 from rhodecode.lib import generate_api_key
+from sqlalchemy.orm import joinedload
 
 log = logging.getLogger(__name__)
 
-PERM_WEIGHTS = {'repository.none':0,
-                'repository.read':1,
-                'repository.write':3,
-                'repository.admin':3}
+PERM_WEIGHTS = {'repository.none': 0,
+                'repository.read': 1,
+                'repository.write': 3,
+                'repository.admin': 3}
+
 
 class UserModel(BaseModel):
 
@@ -53,7 +56,6 @@
                                           "get_user_%s" % user_id))
         return user.get(user_id)
 
-
     def get_by_username(self, username, cache=False, case_insensitive=False):
 
         if case_insensitive:
@@ -66,7 +68,6 @@
                                           "get_user_%s" % username))
         return user.scalar()
 
-
     def get_by_api_key(self, api_key, cache=False):
 
         user = self.sa.query(User)\
@@ -104,7 +105,8 @@
         if self.get_by_username(username, case_insensitive=True) is None:
             try:
                 new_user = User()
-                new_user.username = username.lower() # add ldap account always lowercase
+                # add ldap account always lowercase
+                new_user.username = username.lower()
                 new_user.password = get_crypt_password(password)
                 new_user.api_key = generate_api_key(username)
                 new_user.email = attrs['email']
@@ -113,7 +115,6 @@
                 new_user.name = attrs['name']
                 new_user.lastname = attrs['lastname']
 
-
                 self.sa.add(new_user)
                 self.sa.commit()
                 return True
@@ -216,7 +217,6 @@
         from rhodecode.lib.celerylib import tasks, run_task
         run_task(tasks.reset_user_password, data['email'])
 
-
     def fill_data(self, auth_user, user_id=None, api_key=None):
         """
         Fetches auth_user by user_id,or api_key if present.
@@ -248,11 +248,11 @@
 
         return auth_user
 
-
     def fill_perms(self, user):
-        """Fills user permission attribute with permissions taken from database
+        """
+        Fills user permission attribute with permissions taken from database
         works for permissions given for repositories, and for permissions that
-        as part of beeing group member
+        are granted to groups
 
         :param user: user instance to fill his perms
         """
@@ -260,83 +260,124 @@
         user.permissions['repositories'] = {}
         user.permissions['global'] = set()
 
-        #===========================================================================
+        #======================================================================
         # fetch default permissions
-        #===========================================================================
+        #======================================================================
         default_user = self.get_by_username('default', cache=True)
 
         default_perms = self.sa.query(RepoToPerm, Repository, Permission)\
-            .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
-            .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
+            .join((Repository, RepoToPerm.repository_id ==
+                   Repository.repo_id))\
+            .join((Permission, RepoToPerm.permission_id ==
+                   Permission.permission_id))\
             .filter(RepoToPerm.user == default_user).all()
 
         if user.is_admin:
-            #=======================================================================
+            #==================================================================
             # #admin have all default rights set to admin
-            #=======================================================================
+            #==================================================================
             user.permissions['global'].add('hg.admin')
 
             for perm in default_perms:
                 p = 'repository.admin'
-                user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
+                user.permissions['repositories'][perm.RepoToPerm.
+                                                 repository.repo_name] = p
 
         else:
-            #=======================================================================
+            #==================================================================
             # set default permissions
-            #=======================================================================
+            #==================================================================
+            uid = user.user_id
 
             #default global
             default_global_perms = self.sa.query(UserToPerm)\
-                .filter(UserToPerm.user == self.sa.query(User)\
-                       .filter(User.username == 'default').one())
+                .filter(UserToPerm.user == default_user)
 
             for perm in default_global_perms:
                 user.permissions['global'].add(perm.permission.permission_name)
 
             #default for repositories
             for perm in default_perms:
-                if perm.Repository.private and not perm.Repository.user_id == user.user_id:
+                if perm.Repository.private and not (perm.Repository.user_id ==
+                                                    uid):
                     #diself.sable defaults for private repos,
                     p = 'repository.none'
-                elif perm.Repository.user_id == user.user_id:
+                elif perm.Repository.user_id == uid:
                     #set admin if owner
                     p = 'repository.admin'
                 else:
                     p = perm.Permission.permission_name
 
-                user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
+                user.permissions['repositories'][perm.RepoToPerm.
+                                                 repository.repo_name] = p
 
-            #=======================================================================
+            #==================================================================
             # overwrite default with user permissions if any
-            #=======================================================================
-            user_perms = self.sa.query(RepoToPerm, Permission, Repository)\
-                .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
-                .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
-                .filter(RepoToPerm.user_id == user.user_id).all()
+            #==================================================================
+
+            #user global
+            user_perms = self.sa.query(UserToPerm)\
+                    .options(joinedload(UserToPerm.permission))\
+                    .filter(UserToPerm.user_id == uid).all()
 
             for perm in user_perms:
-                if perm.Repository.user_id == user.user_id:#set admin if owner
+                user.permissions['global'].add(perm.permission.
+                                               permission_name)
+
+            #user repositories
+            user_repo_perms = self.sa.query(RepoToPerm, Permission,
+                                            Repository)\
+                .join((Repository, RepoToPerm.repository_id ==
+                       Repository.repo_id))\
+                .join((Permission, RepoToPerm.permission_id ==
+                       Permission.permission_id))\
+                .filter(RepoToPerm.user_id == uid).all()
+
+            for perm in user_repo_perms:
+                # set admin if owner
+                if perm.Repository.user_id == uid:
                     p = 'repository.admin'
                 else:
                     p = perm.Permission.permission_name
-                user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
+                user.permissions['repositories'][perm.RepoToPerm.
+                                                 repository.repo_name] = p
 
-
-            #=======================================================================
+            #==================================================================
             # check if user is part of groups for this repository and fill in
             # (or replace with higher) permissions
-            #=======================================================================
-            user_perms_from_users_groups = self.sa.query(UsersGroupToPerm, Permission, Repository,)\
-                .join((Repository, UsersGroupToPerm.repository_id == Repository.repo_id))\
-                .join((Permission, UsersGroupToPerm.permission_id == Permission.permission_id))\
-                .join((UsersGroupMember, UsersGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
-                .filter(UsersGroupMember.user_id == user.user_id).all()
+            #==================================================================
+
+            #users group global
+            user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
+                .options(joinedload(UsersGroupToPerm.permission))\
+                .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
+                       UsersGroupMember.users_group_id))\
+                .filter(UsersGroupMember.user_id == uid).all()
 
             for perm in user_perms_from_users_groups:
+                user.permissions['global'].add(perm.permission.permission_name)
+
+            #users group repositories
+            user_repo_perms_from_users_groups = self.sa.query(
+                                                UsersGroupRepoToPerm,
+                                                Permission, Repository,)\
+                .join((Repository, UsersGroupRepoToPerm.repository_id ==
+                       Repository.repo_id))\
+                .join((Permission, UsersGroupRepoToPerm.permission_id ==
+                       Permission.permission_id))\
+                .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
+                       UsersGroupMember.users_group_id))\
+                .filter(UsersGroupMember.user_id == uid).all()
+
+            for perm in user_repo_perms_from_users_groups:
                 p = perm.Permission.permission_name
-                cur_perm = user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name]
-                #overwrite permission only if it's greater than permission given from other sources
+                cur_perm = user.permissions['repositories'][perm.
+                                                    UsersGroupRepoToPerm.
+                                                    repository.repo_name]
+                #overwrite permission only if it's greater than permission
+                # given from other sources
                 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
-                    user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name] = p
+                    user.permissions['repositories'][perm.UsersGroupRepoToPerm.
+                                                     repository.repo_name] = p
 
         return user
--- a/rhodecode/model/users_group.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/model/users_group.py	Tue Apr 26 14:03:00 2011 +0200
@@ -32,8 +32,6 @@
 from rhodecode.model.caching_query import FromCache
 from rhodecode.model.db import UsersGroup, UsersGroupMember
 
-from sqlalchemy.exc import DatabaseError
-
 log = logging.getLogger(__name__)
 
 
@@ -43,24 +41,9 @@
         users_group = self.sa.query(UsersGroup)
         if cache:
             users_group = users_group.options(FromCache("sql_cache_short",
-                                          "get_users_group_%s" % users_group_id))
+                                    "get_users_group_%s" % users_group_id))
         return users_group.get(users_group_id)
 
-
-    def get_by_groupname(self, users_group_name, cache=False,
-                         case_insensitive=False):
-
-        if case_insensitive:
-            user = self.sa.query(UsersGroup)\
-            .filter(UsersGroup.users_group_name.ilike(users_group_name))
-        else:
-            user = self.sa.query(UsersGroup)\
-                .filter(UsersGroup.users_group_name == users_group_name)
-        if cache:
-            user = user.options(FromCache("sql_cache_short",
-                                          "get_user_%s" % users_group_name))
-        return user.scalar()
-
     def create(self, form_data):
         try:
             new_users_group = UsersGroup()
@@ -86,8 +69,9 @@
                     members_list = []
                     if v:
                         for u_id in set(v):
-                            members_list.append(UsersGroupMember(users_group_id,
-                                                             u_id))
+                            members_list.append(UsersGroupMember(
+                                                            users_group_id,
+                                                            u_id))
                     setattr(users_group, 'members', members_list)
                 setattr(users_group, k, v)
 
--- a/rhodecode/public/css/style.css	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/public/css/style.css	Tue Apr 26 14:03:00 2011 +0200
@@ -389,6 +389,12 @@
 overflow-x:hidden;
 overflow-y:auto;
 }
+#header #header-inner #quick ul.repo_switcher li.qfilter_rs {
+float:none;
+margin:0;
+border-bottom:2px solid #003367;
+}
+
 
 #header #header-inner #quick .repo_switcher_type{
 position:absolute;
@@ -829,7 +835,7 @@
 }
 
 #content div.box div.form div.fields div.field div.label {
-left:80px;
+left:70px;
 width:auto;
 position:absolute;
 margin:0;
@@ -1546,6 +1552,24 @@
 font-weight: bold;
 }
 
+.cs_files .node{
+float: left;
+}
+.cs_files .changes{
+float: right;
+}
+.cs_files .changes .added{
+background-color: #BBFFBB;
+float: left;
+text-align: center;
+font-size: 90%; 
+}
+.cs_files .changes .deleted{
+background-color: #FF8888;
+float: left;
+text-align: center;
+font-size: 90%;
+}
 .cs_files .cs_added {
 background:url("../images/icons/page_white_add.png") no-repeat scroll 3px;
 height:16px;
--- a/rhodecode/templates/admin/users/user_edit.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/admin/users/user_edit.html	Tue Apr 26 14:03:00 2011 +0200
@@ -24,7 +24,7 @@
         ${self.breadcrumbs()}       
     </div>
     <!-- end box / title -->
-    ${h.form(url('user', id=c.user.user_id),method='put')}
+    ${h.form(url('update_user', id=c.user.user_id),method='put')}
     <div class="form">
         <div class="field">
            <div class="gravatar_box">
@@ -126,7 +126,7 @@
     <div class="title">
         <h5>${_('Permissions')}</h5>       
     </div>
-    ${h.form(url('user', id=c.user.user_id),method='put')}
+    ${h.form(url('user_perm', id=c.user.user_id),method='put')}
     <div class="form">
         <!-- fields -->
         <div class="fields">
@@ -135,7 +135,7 @@
                     <label for="">${_('Create repositories')}:</label>
                 </div>
                 <div class="checkboxes">
-                    ${h.checkbox('create',value=True)}
+                    ${h.checkbox('create_repo_perm',value=True)}
                 </div>
              </div>
             <div class="buttons">
--- a/rhodecode/templates/admin/users/users.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/admin/users/users.html	Tue Apr 26 14:03:00 2011 +0200
@@ -51,7 +51,7 @@
                     <td>${h.bool2icon(user.admin)}</td>
                     <td>${h.bool2icon(bool(user.ldap_dn))}</td>
                     <td>
-                        ${h.form(url('user', id=user.user_id),method='delete')}
+                        ${h.form(url('delete_user', id=user.user_id),method='delete')}
                             ${h.submit('remove_','delete',id="remove_user_%s" % user.user_id,
                             class_="delete_icon action_button",onclick="return confirm('Confirm to delete this user');")}
                         ${h.end_form()}
--- a/rhodecode/templates/admin/users_groups/users_group_edit.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/admin/users_groups/users_group_edit.html	Tue Apr 26 14:03:00 2011 +0200
@@ -247,7 +247,7 @@
     <div class="title">
         <h5>${_('Permissions')}</h5>       
     </div>
-    ${h.form(url('user', id=''),method='put')}
+    ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
     <div class="form">
         <!-- fields -->
         <div class="fields">
@@ -256,7 +256,7 @@
                     <label for="">${_('Create repositories')}:</label>
                 </div>
                 <div class="checkboxes">
-                    ${h.checkbox('create',value=True)}
+                    ${h.checkbox('create_repo_perm',value=True)}
                 </div>
              </div>
             <div class="buttons">
--- a/rhodecode/templates/base/base.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/base/base.html	Tue Apr 26 14:03:00 2011 +0200
@@ -121,12 +121,55 @@
 					</ul>
 					<script type="text/javascript">
 					   YUE.on('repo_switcher','mouseover',function(){
+						      function qfilter(){
+						         var S = YAHOO.util.Selector;
+						         
+						         var q_filter = YUD.get('q_filter_rs');
+						         var F = YAHOO.namespace('q_filter_rs'); 
+						         
+						         YUE.on(q_filter,'click',function(){
+						            q_filter.value = '';
+						         });
+						    
+						         F.filterTimeout = null;
+						         
+						         F.updateFilter  = function() { 
+						            // Reset timeout 
+						            F.filterTimeout = null;
+						            
+						            var obsolete = [];
+						            var nodes = S.query('ul#repo_switcher_list li a.repo_name');
+						            var req = YUD.get('q_filter_rs').value;
+						            for (n in nodes){
+						                YUD.setStyle(nodes[n].parentNode,'display','')
+						            }
+						            if (req){
+						                for (n in nodes){
+						                    console.log(n);
+						                    if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
+						                        obsolete.push(nodes[n]); 
+						                    }
+						                }
+						                if(obsolete){
+						                    for (n in obsolete){
+						                        YUD.setStyle(obsolete[n].parentNode,'display','none');
+						                    }
+						                }
+						            }
+						         }
+						         
+						         YUE.on(q_filter,'keyup',function(e){
+						             clearTimeout(F.filterTimeout); 
+						             setTimeout(F.updateFilter,600); 
+						         });
+						}
 						   var loaded = YUD.hasClass('repo_switcher','loaded');
 						   if(!loaded){
 							   YUD.addClass('repo_switcher','loaded');
 							   YAHOO.util.Connect.asyncRequest('GET',"${h.url('repo_switcher')}",{
 								   success:function(o){
 								      YUD.get('repo_switcher_list').innerHTML = o.responseText;
+								      qfilter();
 								   },
 								   failure:function(o){
 									   YUD.removeClass('repo_switcher','loaded');   
@@ -247,7 +290,7 @@
                 </li>
                 
                 <li>
-                    <a title="${_('Followers')}" href="#">
+                    <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
                     <span class="icon_short">
                         <img src="${h.url("/images/icons/heart.png")}" alt="${_('Followers')}" />
                     </span>
--- a/rhodecode/templates/changeset/changeset.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/changeset/changeset.html	Tue Apr 26 14:03:00 2011 +0200
@@ -86,10 +86,16 @@
 		         </span>                                                                 
 	                </div>              
 	        </div>
-	        <span style="font-size:1.1em;font-weight: bold">${_('Files affected (%s)' % len(c.changeset.affected_files))}</span>
+	        <span style="font-size:1.1em;font-weight: bold">
+	        ${_('%s files affected with %s additions and %s deletions.') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
+	        </span>
 	        <div class="cs_files">
-	                %for change,filenode,diff,cs1,cs2 in c.changes:
-	                    <div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.repo_name_slug('C%s' % h.safe_unicode(filenode.path))))}</div>
+	                %for change,filenode,diff,cs1,cs2,stat in c.changes:
+	                    <div class="cs_${change}">
+		                    <div class="node">${h.link_to(h.safe_unicode(filenode.path),
+		                                        h.url.current(anchor=h.repo_name_slug('C%s' % h.safe_unicode(filenode.path))))}</div>
+		                    <div class="changes">${h.fancy_file_stats(stat)}</div>
+	                    </div>
 	                %endfor
 	                % if c.cut_off:
 	                  ${_('Changeset was to big and was cut off...')}
@@ -99,7 +105,7 @@
 	    
     </div>
     	
-	%for change,filenode,diff,cs1,cs2 in c.changes:
+	%for change,filenode,diff,cs1,cs2,stat in c.changes:
 		%if change !='removed':
 		<div style="clear:both;height:10px"></div>
 		<div class="diffblock">
--- a/rhodecode/templates/changeset/changeset_range.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/changeset/changeset_range.html	Tue Apr 26 14:03:00 2011 +0200
@@ -53,7 +53,7 @@
 	        <div class="cs_files">
 	               %for cs in c.cs_ranges:
 	                   <div class="cur_cs">r${cs}</div>
-	                %for change,filenode,diff,cs1,cs2 in c.changes[cs.raw_id]:
+	                %for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
 	                    <div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.repo_name_slug('C%s-%s' % (cs.short_id,h.safe_unicode(filenode.path)))))}</div>
 	                %endfor
 	               %endfor 
@@ -62,7 +62,7 @@
 	    
     </div>
    %for cs in c.cs_ranges:    	
-	%for change,filenode,diff,cs1,cs2 in c.changes[cs.raw_id]:
+	%for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
 		%if change !='removed':
 		<div style="clear:both;height:10px"></div>
 		<div class="diffblock">
--- a/rhodecode/templates/files/file_diff.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/files/file_diff.html	Tue Apr 26 14:03:00 2011 +0200
@@ -38,6 +38,9 @@
 			<div class="code-body">
 		 			%if c.no_changes:
 		            	${_('No changes')}
+		            %elif c.big_diff:
+		                ${_('Diff is to big to display')} ${h.link_to(_('raw diff'),
+                           h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='raw'))}
 		            %else:        
 						${c.cur_diff|n}
 		            %endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/templates/followers/followers.html	Tue Apr 26 14:03:00 2011 +0200
@@ -0,0 +1,32 @@
+## -*- coding: utf-8 -*-
+<%inherit file="/base/base.html"/>
+
+<%def name="title()">
+    ${c.repo_name} ${_('Followers')} - ${c.rhodecode_name}
+</%def>
+
+<%def name="breadcrumbs_links()">
+    ${h.link_to(u'Home',h.url('/'))}
+    &raquo; 
+    ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
+    &raquo;
+    ${_('followers')}
+</%def>
+
+<%def name="page_nav()">
+    ${self.menu('followers')}
+</%def>
+<%def name="main()">
+<div class="box">
+    <!-- box / title -->
+    <div class="title">
+        ${self.breadcrumbs()}
+    </div>
+    <!-- end box / title -->
+    <div class="table">
+        <div id="followers">
+            ${c.followers_data}
+        </div>   
+    </div>
+</div>    
+</%def> 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/templates/followers/followers_data.html	Tue Apr 26 14:03:00 2011 +0200
@@ -0,0 +1,36 @@
+## -*- coding: utf-8 -*-
+
+% for f in c.followers_pager:
+    <div>
+        <div class="follower_user">
+            <div class="gravatar">
+                <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
+            </div>
+            <span style="font-size: 20px"> <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname})</span>
+        </div>
+        <div style="clear:both;padding-top: 10px"></div>
+        <div class="follower_date">${_('Started following')} - 
+        <span class="tooltip" title="${f.follows_from}"> ${h.age(f.follows_from)}</span></div>
+        <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
+    </div>                
+% endfor 
+
+<div class="pagination-wh pagination-left">
+<script type="text/javascript">
+  var data_div = 'followers';
+  YAHOO.util.Event.onDOMReady(function(){
+    YAHOO.util.Event.addListener(
+    		YUD.getElementsByClassName('pager_link'),"click",
+    		function(){
+            YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');
+            });
+    });
+</script>
+
+${c.followers_pager.pager('$link_previous ~2~ $link_next',     
+onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
+success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
+YUE.on(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
+        YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});       
+YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
+</div>
\ No newline at end of file
--- a/rhodecode/templates/repo_switcher_list.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/repo_switcher_list.html	Tue Apr 26 14:03:00 2011 +0200
@@ -1,15 +1,23 @@
-## -*- coding: utf-8 -*-    
+## -*- coding: utf-8 -*-
+
+<li class="qfilter_rs">
+<input type="text" 
+style="border:0"        
+value="quick filter..." 
+name="filter" size="15" id="q_filter_rs" />
+</li>
+    
 %for repo in c.repos_list:
      
       %if repo['dbrepo']['private']:
          <li>
              <img src="${h.url("/images/icons/lock.png")}" alt="${_('Private repository')}" class="repo_switcher_type"/>
-             ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="%s" % repo['dbrepo']['repo_type'])}
+             ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
           </li>
       %else:
          <li>
              <img src="${h.url("/images/icons/lock_open.png")}" alt="${_('Public repository')}" class="repo_switcher_type" />
-             ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="%s" % repo['dbrepo']['repo_type'])}
+             ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
          </li>
       %endif  
 %endfor
\ No newline at end of file
--- a/rhodecode/templates/summary/summary.html	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/templates/summary/summary.html	Tue Apr 26 14:03:00 2011 +0200
@@ -130,7 +130,7 @@
 			      <label>${_('Clone url')}:</label>
 			  </div>
 			  <div class="input-short">
-			      <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
+			      <input type="text" id="clone_url" readonly="readonly" value="${c.rhodecode_repo.alias} clone ${c.clone_repo_url}" size="70"/>
 			  </div>
 			 </div>
 			 
--- a/rhodecode/tests/test_hg_operations.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/rhodecode/tests/test_hg_operations.py	Tue Apr 26 14:03:00 2011 +0200
@@ -14,6 +14,7 @@
 import shutil
 import logging
 from os.path import join as jn
+from os.path import dirname as dn
 
 from tempfile import _RandomNameSequence
 from subprocess import Popen, PIPE
@@ -31,7 +32,8 @@
 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
 from rhodecode.config.environment import load_environment
 
-conf = appconfig('config:development.ini', relative_to='./../../')
+rel_path = dn(dn(dn(os.path.abspath(__file__))))
+conf = appconfig('config:development.ini', relative_to=rel_path)
 load_environment(conf.global_conf, conf.local_conf)
 
 add_cache(conf)
--- a/setup.py	Tue Apr 26 14:02:53 2011 +0200
+++ b/setup.py	Tue Apr 26 14:03:00 2011 +0200
@@ -21,7 +21,7 @@
         "celery>=2.2.5",
         "babel",
         "python-dateutil>=1.5.0,<2.0.0",
-        "dulwich>=0.7.0"
+        "dulwich>=0.7.1"
     ]
 
 classifiers = ['Development Status :: 4 - Beta',