changeset 1564:752b0a7b7679

Merge with beta
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 17 Oct 2011 01:01:18 +0200
parents a05d16fc1d87 (current diff) e774fc919e76 (diff)
children ba2205d093dc
files CONTRIBUTORS docs/changelog.rst docs/setup.rst rhodecode/__init__.py rhodecode/config/middleware.py rhodecode/config/routing.py rhodecode/controllers/admin/permissions.py rhodecode/controllers/admin/repos.py rhodecode/controllers/admin/settings.py rhodecode/controllers/login.py rhodecode/lib/__init__.py rhodecode/lib/auth.py rhodecode/lib/backup_manager.py rhodecode/lib/celerylib/__init__.py rhodecode/lib/celerylib/tasks.py rhodecode/lib/exceptions.py rhodecode/lib/helpers.py rhodecode/lib/indexers/__init__.py rhodecode/lib/middleware/simplegit.py rhodecode/lib/middleware/simplehg.py rhodecode/lib/smtp_mailer.py rhodecode/lib/utils.py rhodecode/model/db.py rhodecode/model/forms.py rhodecode/model/repo.py rhodecode/model/scm.py rhodecode/public/css/style.css rhodecode/templates/files/files_browser.html rhodecode/templates/summary/summary.html rhodecode/tests/functional/test_changelog.py setup.py test.ini
diffstat 47 files changed, 1034 insertions(+), 356 deletions(-) [+]
line wrap: on
line diff
--- a/CONTRIBUTORS	Sat Oct 08 16:26:29 2011 +0200
+++ b/CONTRIBUTORS	Mon Oct 17 01:01:18 2011 +0200
@@ -12,4 +12,4 @@
     Augosto Hermann <augusto.herrmann@planejamento.gov.br>    
     Ankit Solanki <ankit.solanki@gmail.com>    
     Liad Shani <liadff@gmail.com>
-    
\ No newline at end of file
+    Les Peabody <lpeabody@gmail.com>
--- a/docs/changelog.rst	Sat Oct 08 16:26:29 2011 +0200
+++ b/docs/changelog.rst	Mon Oct 17 01:01:18 2011 +0200
@@ -3,6 +3,30 @@
 Changelog
 =========
 
+
+1.2.2 (**2011-10-17**)
+======================
+
+news
+----
+
+- #226 repo groups are available by path instead of numerical id
+ 
+fixes
+-----
+
+- #259 Groups with the same name but with different parent group
+- #260 Put repo in group, then move group to another group -> repo becomes unavailable
+- #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
+- #265 ldap save fails sometimes on converting attributes to booleans, 
+  added getter and setter into model that will prevent from this on db model level
+- fixed problems with timestamps issues #251 and #213
+- fixes #266 Rhodecode allows to create repo with the same name and in 
+  the same parent as group
+- fixes #245 Rescan of the repositories on Windows
+- fixes #248 cannot edit repos inside a group on windows
+- fixes #219 forking problems on windows
+
 1.2.1 (**2011-10-08**)
 ======================
 
@@ -17,11 +41,9 @@
 - gui fixes
 - fixed logger
 
-
 1.2.0 (**2011-10-07**)
 ======================
 
-
 news
 ----
 
--- a/docs/setup.rst	Sat Oct 08 16:26:29 2011 +0200
+++ b/docs/setup.rst	Mon Oct 17 01:01:18 2011 +0200
@@ -443,8 +443,8 @@
 In order to not have the statics served by the application. This improves speed.
 
 
-Apache virtual host example
----------------------------
+Apache virtual host reverse proxy example
+-----------------------------------------
 
 Here is a sample configuration file for apache using proxy::
 
@@ -503,6 +503,31 @@
 Apache's WSGI config
 --------------------
 
+Alternatively, RhodeCode can be set up with Apache under mod_wsgi. For
+that, you'll need to:
+
+- Install mod_wsgi. If using a Debian-based distro, you can install
+  the package libapache2-mod-wsgi::
+
+    aptitude install libapache2-mod-wsgi
+
+- Enable mod_wsgi::
+
+    a2enmod wsgi
+
+- Create a wsgi dispatch script, like the one below. Make sure you
+  check the paths correctly point to where you installed RhodeCode
+  and its Python Virtual Environment.
+- Enable the WSGIScriptAlias directive for the wsgi dispatch script,
+  as in the following example. Once again, check the paths are
+  correctly specified.
+
+Here is a sample excerpt from an Apache Virtual Host configuration file::
+
+    WSGIDaemonProcess pylons user=www-data group=www-data processes=1 \
+        threads=4 \
+        python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
+    WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
 
 Example wsgi dispatch script::
 
@@ -512,6 +537,9 @@
     
     # sometimes it's needed to set the curent dir
     os.chdir('/home/web/rhodecode/') 
+
+    import site
+    site.addsitedir("/home/web/rhodecode/pyenv/lib/python2.6/site-packages")
     
     from paste.deploy import loadapp
     from paste.script.util.logging_config import fileConfig
@@ -519,6 +547,10 @@
     fileConfig('/home/web/rhodecode/production.ini')
     application = loadapp('config:/home/web/rhodecode/production.ini')
 
+Note: when using mod_wsgi you'll need to install the same version of
+Mercurial that's inside RhodeCode's virtualenv also on the system's Python
+environment.
+
 
 Other configuration files
 -------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/init.d/rhodecode-daemon3	Mon Oct 17 01:01:18 2011 +0200
@@ -0,0 +1,132 @@
+#!/bin/sh
+########################################
+#### THIS IS A REDHAT INIT.D SCRIPT ####
+########################################
+
+##################################################
+#
+# RhodeCode server startup script
+# Recommended default-startup: 2 3 4 5
+# Recommended default-stop: 0 1 6
+#
+##################################################
+
+
+APP_NAME="rhodecode"
+# the location of your app
+# since this is a web app, it should go in /var/www
+APP_PATH="/var/www/$APP_NAME"
+
+CONF_NAME="production.ini"
+
+# write to wherever the PID should be stored, just ensure
+# that the user you run paster as has the appropriate permissions
+# same goes for the log file
+PID_PATH="/var/run/rhodecode/pid"
+LOG_PATH="/var/log/rhodecode/rhodecode.log"
+
+# replace this with the path to the virtual environment you
+# made for RhodeCode
+PYTHON_PATH="/opt/python_virtualenvironments/rhodecode-venv"
+
+RUN_AS="rhodecode"
+
+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"
+
+DESC="rhodecode-server"
+LOCK_FILE="/var/lock/subsys/$APP_NAME"
+
+# source CentOS init functions
+. /etc/init.d/functions
+
+RETVAL=0
+
+remove_pid () {
+  rm -f ${PID_PATH}
+  rmdir `dirname ${PID_PATH}`
+}
+
+ensure_pid_dir () {
+  PID_DIR=`dirname ${PID_PATH}`
+  if [ ! -d ${PID_DIR} ] ; then
+    mkdir -p ${PID_DIR}
+    chown -R ${RUN_AS}:${RUN_AS} ${PID_DIR}
+    chmod 755 ${PID_DIR}
+  fi
+}
+
+start_rhodecode () {
+    ensure_pid_dir
+    PYTHON_EGG_CACHE="/tmp" daemon --pidfile $PID_PATH \
+        --user $RUN_AS "$DAEMON $DAEMON_OPTS"
+    RETVAL=$?
+    [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+    return $RETVAL
+}
+
+stop_rhodecode () {
+    if [ -e $LOCK_FILE ]; then
+      killproc -p $PID_PATH
+      RETVAL=$?
+      rm -f $LOCK_FILE
+      rm -f $PID_PATH
+    else
+      RETVAL=1
+    fi
+    return $RETVAL
+}
+
+status_rhodecode() {
+  if [ -e $LOCK_FILE ]; then
+    # exit with non-zero to indicate failure
+    RETVAL=1
+  else
+    RETVAL=0
+  fi
+  return $RETVAL
+}
+
+restart_rhodecode () {
+    stop_rhodecode
+    start_rhodecode
+    RETVAL=$?
+}
+
+case "$1" in
+  start)
+    echo -n $"Starting $DESC: "
+    start_rhodecode
+    echo
+    ;;
+  stop)
+    echo -n $"Stopping $DESC: "
+    stop_rhodecode
+    echo
+    ;;
+  status)
+    status_rhodecode
+    RETVAL=$?
+    if [ ! $RETVAL -eq 0 ]; then
+      echo "RhodeCode server is running..."
+    else
+      echo "RhodeCode server is stopped."
+    fi
+    ;;
+  restart)
+    echo -n $"Restarting $DESC: "
+    restart_rhodecode
+    echo
+    ;;
+  *)
+    echo $"Usage: $0 {start|stop|restart|status}"
+    RETVAL=1
+    ;;
+esac
+
+exit $RETVAL
\ No newline at end of file
--- a/rhodecode/__init__.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/__init__.py	Mon Oct 17 01:01:18 2011 +0200
@@ -25,7 +25,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import platform
 
-VERSION = (1, 2, 1)
+VERSION = (1, 2, 2)
 __version__ = '.'.join((str(each) for each in VERSION[:4]))
 __dbversion__ = 3 #defines current db version for migrations
 __platform__ = platform.system()
@@ -35,7 +35,7 @@
 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
 
 try:
-    from rhodecode.lib.utils import get_current_revision
+    from rhodecode.lib import get_current_revision
     _rev = get_current_revision()
 except ImportError:
     #this is needed when doing some setup.py operations
--- a/rhodecode/config/routing.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/config/routing.py	Mon Oct 17 01:01:18 2011 +0200
@@ -19,10 +19,10 @@
                  always_scan=config['debug'])
     rmap.minimization = False
     rmap.explicit = False
-    
+
     from rhodecode.lib.utils import is_valid_repo
     from rhodecode.lib.utils import is_valid_repos_group
-    
+
     def check_repo(environ, match_dict):
         """
         check for valid repository for proper 404 handling
@@ -30,7 +30,7 @@
         :param environ:
         :param match_dict:
         """
-         
+
         repo_name = match_dict.get('repo_name')
         return is_valid_repo(repo_name, config['base_path'])
 
@@ -42,7 +42,7 @@
         :param match_dict:
         """
         repos_group_name = match_dict.get('group_name')
-        
+
         return is_valid_repos_group(repos_group_name, config['base_path'])
 
 
@@ -333,13 +333,13 @@
     # REPOSITORY ROUTES
     #==========================================================================
     rmap.connect('summary_home', '/{repo_name:.*}',
-                controller='summary', 
+                controller='summary',
                 conditions=dict(function=check_repo))
-    
-#    rmap.connect('repo_group_home', '/{group_name:.*}',
-#                controller='admin/repos_groups',action="show_by_name", 
-#                conditions=dict(function=check_group))
-    
+
+    rmap.connect('repos_group_home', '/{group_name:.*}',
+                controller='admin/repos_groups', action="show_by_name",
+                conditions=dict(function=check_group))
+
     rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
                 controller='changeset', revision='tip',
                 conditions=dict(function=check_repo))
--- a/rhodecode/controllers/admin/permissions.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/controllers/admin/permissions.py	Mon Oct 17 01:01:18 2011 +0200
@@ -144,7 +144,7 @@
         c.create_choices = self.create_choices
 
         if id == 'default':
-            default_user = User.by_username('default')
+            default_user = User.get_by_username('default')
             defaults = {'_method': 'put',
                         'anonymous': default_user.active}
 
--- a/rhodecode/controllers/admin/repos.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/controllers/admin/repos.py	Mon Oct 17 01:01:18 2011 +0200
@@ -64,20 +64,10 @@
         super(ReposController, self).__before__()
 
     def __load_defaults(self):
+        c.repo_groups = Group.groups_choices()
+        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
+        
         repo_model = RepoModel()
-
-        c.repo_groups = [('', '')]
-        parents_link = lambda k: h.literal('&raquo;'.join(
-                                    map(lambda k: k.group_name,
-                                        k.parents + [k])
-                                    )
-                                )
-
-        c.repo_groups.extend([(x.group_id, parents_link(x)) for \
-                                            x in self.sa.query(Group).all()])
-        c.repo_groups = sorted(c.repo_groups,
-                               key=lambda t: t[1].split('&raquo;')[0])
-        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
         c.users_array = repo_model.get_users_js()
         c.users_groups_array = repo_model.get_users_groups_js()
 
@@ -89,8 +79,8 @@
         """
         self.__load_defaults()
 
-        c.repo_info = db_repo = Repository.by_repo_name(repo_name)
-        repo = scm_repo = db_repo.scm_instance
+        c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
+        repo = db_repo.scm_instance
 
         if c.repo_info is None:
             h.flash(_('%s repository is not mapped to db perhaps'
@@ -101,7 +91,7 @@
 
             return redirect(url('repos'))
 
-        c.default_user_id = User.by_username('default').user_id
+        c.default_user_id = User.get_by_username('default').user_id
         c.in_public_journal = self.sa.query(UserFollowing)\
             .filter(UserFollowing.user_id == c.default_user_id)\
             .filter(UserFollowing.follows_repository == c.repo_info).scalar()
@@ -234,11 +224,11 @@
                          repo_groups=c.repo_groups_choices)()
         try:
             form_result = _form.to_python(dict(request.POST))
-            repo_model.update(repo_name, form_result)
+            repo = repo_model.update(repo_name, form_result)
             invalidate_cache('get_repo_cached_%s' % repo_name)
             h.flash(_('Repository %s updated successfully' % repo_name),
                     category='success')
-            changed_name = form_result['repo_name_full']
+            changed_name = repo.repo_name
             action_logger(self.rhodecode_user, 'admin_updated_repo',
                               changed_name, '', self.sa)
 
@@ -381,8 +371,8 @@
         token = get_token()
         if cur_token == token:
             try:
-                repo_id = Repository.by_repo_name(repo_name).repo_id
-                user_id = User.by_username('default').user_id
+                repo_id = Repository.get_by_repo_name(repo_name).repo_id
+                user_id = User.get_by_username('default').user_id
                 self.scm_model.toggle_following_repo(repo_id, user_id)
                 h.flash(_('Updated repository visibility in public journal'),
                         category='success')
--- a/rhodecode/controllers/admin/repos_groups.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/controllers/admin/repos_groups.py	Mon Oct 17 01:01:18 2011 +0200
@@ -9,9 +9,10 @@
 from pylons.controllers.util import abort, redirect
 from pylons.i18n.translation import _
 
+from sqlalchemy.exc import IntegrityError
+
 from rhodecode.lib import helpers as h
-from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
-    HasPermissionAnyDecorator
+from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator
 from rhodecode.lib.base import BaseController, render
 from rhodecode.model.db import Group
 from rhodecode.model.repos_group import ReposGroupModel
@@ -31,19 +32,7 @@
         super(ReposGroupsController, self).__before__()
 
     def __load_defaults(self):
-
-        c.repo_groups = [('', '')]
-        parents_link = lambda k: h.literal('&raquo;'.join(
-                                    map(lambda k: k.group_name,
-                                        k.parents + [k])
-                                    )
-                                )
-
-        c.repo_groups.extend([(x.group_id, parents_link(x)) for \
-                                            x in self.sa.query(Group).all()])
-
-        c.repo_groups = sorted(c.repo_groups,
-                               key=lambda t: t[1].split('&raquo;')[0])
+        c.repo_groups = Group.groups_choices()
         c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 
     def __load_data(self, group_id):
@@ -58,6 +47,8 @@
 
         data = repo_group.get_dict()
 
+        data['group_name'] = repo_group.name
+
         return data
 
     @HasPermissionAnyDecorator('hg.admin')
@@ -169,13 +160,28 @@
             repos_group_model.delete(id)
             h.flash(_('removed repos group %s' % gr.group_name), category='success')
             #TODO: in future action_logger(, '', '', '', self.sa)
+        except IntegrityError, e:
+            if e.message.find('groups_group_parent_id_fkey'):
+                log.error(traceback.format_exc())
+                h.flash(_('Cannot delete this group it still contains '
+                          'subgroups'),
+                        category='warning')
+            else:
+                log.error(traceback.format_exc())
+                h.flash(_('error occurred during deletion of repos '
+                          'group %s' % gr.group_name), category='error')
+
         except Exception:
             log.error(traceback.format_exc())
-            h.flash(_('error occurred during deletion of repos group %s' % gr.group_name),
-                    category='error')
+            h.flash(_('error occurred during deletion of repos '
+                      'group %s' % gr.group_name), category='error')
 
         return redirect(url('repos_groups'))
 
+    def show_by_name(self, group_name):
+        id_ = Group.get_by_group_name(group_name).group_id
+        return self.show(id_)
+
     def show(self, id, format='html'):
         """GET /repos_groups/id: Show a specific item"""
         # url('repos_group', id=ID)
--- a/rhodecode/controllers/admin/settings.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/controllers/admin/settings.py	Mon Oct 17 01:01:18 2011 +0200
@@ -366,17 +366,7 @@
     def create_repository(self):
         """GET /_admin/create_repository: Form to create a new item"""
 
-        c.repo_groups = [('', '')]
-        parents_link = lambda k: h.literal('&raquo;'.join(
-                                    map(lambda k: k.group_name,
-                                        k.parents + [k])
-                                    )
-                                )
-
-        c.repo_groups.extend([(x.group_id, parents_link(x)) for \
-                                            x in self.sa.query(Group).all()])
-        c.repo_groups = sorted(c.repo_groups,
-                               key=lambda t: t[1].split('&raquo;')[0])
+        c.repo_groups = Group.groups_choices()
         c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 
         new_repo = request.GET.get('repo', '')
--- a/rhodecode/controllers/login.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/controllers/login.py	Mon Oct 17 01:01:18 2011 +0200
@@ -64,8 +64,7 @@
                 c.form_result = login_form.to_python(dict(request.POST))
                 #form checks for username/password, now we're authenticated
                 username = c.form_result['username']
-                user = User.by_username(username,
-                                                   case_insensitive=True)
+                user = User.get_by_username(username, case_insensitive=True)
                 auth_user = AuthUser(user.user_id)
                 auth_user.set_authenticated()
                 session['rhodecode_user'] = auth_user
@@ -95,8 +94,7 @@
     def register(self):
         user_model = UserModel()
         c.auto_active = False
-        for perm in user_model.get_by_username('default',
-                                               cache=False).user_perms:
+        for perm in User.get_by_username('default').user_perms:
             if perm.permission.permission_name == 'hg.register.auto_activate':
                 c.auto_active = True
                 break
--- a/rhodecode/lib/__init__.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/__init__.py	Mon Oct 17 01:01:18 2011 +0200
@@ -23,6 +23,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import os
+
 def __get_lem():
     from pygments import lexers
     from string import lower
@@ -379,3 +381,24 @@
         from rhodecode.lib.utils import EmptyChangeset
         cs = EmptyChangeset(requested_revision=rev)
     return cs
+
+
+def get_current_revision():
+    """
+    Returns tuple of (number, id) from repository containing this package
+    or None if repository could not be found.
+    """
+
+    try:
+        from vcs import get_repo
+        from vcs.utils.helpers import get_scm
+        from vcs.exceptions import RepositoryError, VCSError
+        repopath = os.path.join(os.path.dirname(__file__), '..', '..')
+        scm = get_scm(repopath)[0]
+        repo = get_repo(path=repopath, alias=scm)
+        tip = repo.get_changeset()
+        return (tip.revision, tip.short_id)
+    except (ImportError, RepositoryError, VCSError), err:
+        print ("Cannot retrieve rhodecode's revision. Original error "
+               "was: %s" % err)
+        return None
--- a/rhodecode/lib/auth.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/auth.py	Mon Oct 17 01:01:18 2011 +0200
@@ -6,8 +6,8 @@
     authentication and permission libraries
 
     :created_on: Apr 4, 2010
-    :copyright: (c) 2010 by marcink.
-    :license: LICENSE_NAME, see LICENSE_FILE for more details.
+    :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
@@ -48,7 +48,7 @@
 
 from rhodecode.model import meta
 from rhodecode.model.user import UserModel
-from rhodecode.model.db import Permission, RhodeCodeSettings
+from rhodecode.model.db import Permission, RhodeCodeSettings, User
 
 log = logging.getLogger(__name__)
 
@@ -151,7 +151,7 @@
     """
 
     user_model = UserModel()
-    user = user_model.get_by_username(username, cache=False)
+    user = User.get_by_username(username)
 
     log.debug('Authenticating user using RhodeCode account')
     if user is not None and not user.ldap_dn:
@@ -170,8 +170,7 @@
 
     else:
         log.debug('Regular authentication failed')
-        user_obj = user_model.get_by_username(username, cache=False,
-                                            case_insensitive=True)
+        user_obj = User.get_by_username(username, case_insensitive=True)
 
         if user_obj is not None and not user_obj.ldap_dn:
             log.debug('this user already exists as non ldap')
@@ -252,7 +251,7 @@
 
     def propagate_data(self):
         user_model = UserModel()
-        self.anonymous_user = user_model.get_by_username('default', cache=True)
+        self.anonymous_user = User.get_by_username('default')
         if self._api_key and self._api_key != self.anonymous_user.api_key:
             #try go get user by api key
             log.debug('Auth User lookup by API KEY %s', self._api_key)
--- a/rhodecode/lib/backup_manager.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/backup_manager.py	Mon Oct 17 01:01:18 2011 +0200
@@ -7,8 +7,8 @@
     repositories and send it to backup server using RSA key via ssh.
 
     :created_on: Feb 28, 2010
-    :copyright: (c) 2010 by marcink.
-    :license: LICENSE_NAME, see LICENSE_FILE for more details.
+    :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
--- a/rhodecode/lib/base.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/base.py	Mon Oct 17 01:01:18 2011 +0200
@@ -67,7 +67,7 @@
         super(BaseRepoController, self).__before__()
         if c.repo_name:
 
-            c.rhodecode_db_repo = Repository.by_repo_name(c.repo_name)
+            c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
             c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
 
             if c.rhodecode_repo is None:
--- a/rhodecode/lib/celerylib/__init__.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/celerylib/__init__.py	Mon Oct 17 01:01:18 2011 +0200
@@ -94,11 +94,11 @@
 def locked_task(func):
     def __wrapper(func, *fargs, **fkwargs):
         lockkey = __get_lockkey(func, *fargs, **fkwargs)
-        lockkey_path = dn(dn(dn(os.path.abspath(__file__))))
+        lockkey_path = config['here']
 
         log.info('running task with lockkey %s', lockkey)
         try:
-            l = DaemonLock(jn(lockkey_path, lockkey))
+            l = DaemonLock(file_=jn(lockkey_path, lockkey))
             ret = func(*fargs, **fkwargs)
             l.release()
             return ret
--- a/rhodecode/lib/celerylib/tasks.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/celerylib/tasks.py	Mon Oct 17 01:01:18 2011 +0200
@@ -97,10 +97,11 @@
 
     lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
                             ts_max_y)
-    lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
+    lockkey_path = config['here']
+
     log.info('running task with lockkey %s', lockkey)
     try:
-        lock = l = DaemonLock(jn(lockkey_path, lockkey))
+        lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
 
         #for js data compatibilty cleans the key for person from '
         akc = lambda k: person(k).replace('"', "")
--- a/rhodecode/lib/compat.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/compat.py	Mon Oct 17 01:01:18 2011 +0200
@@ -24,6 +24,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import os
+from rhodecode import __platform__, PLATFORM_WIN
+
 #==============================================================================
 # json
 #==============================================================================
@@ -358,3 +361,19 @@
 # OrderedSet
 #==============================================================================
 from sqlalchemy.util import OrderedSet
+
+
+#==============================================================================
+# kill FUNCTIONS
+#==============================================================================
+if __platform__ in PLATFORM_WIN:
+    import ctypes
+
+    def kill(pid, sig):
+        """kill function for Win32"""
+        kernel32 = ctypes.windll.kernel32
+        handle = kernel32.OpenProcess(1, 0, pid)
+        return (0 != kernel32.TerminateProcess(handle, 0))
+
+else:
+    kill = os.kill
--- a/rhodecode/lib/exceptions.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/exceptions.py	Mon Oct 17 01:01:18 2011 +0200
@@ -6,8 +6,8 @@
     Set of custom exceptions used in RhodeCode
 
     :created_on: Nov 17, 2010
-    :copyright: (c) 2010 by marcink.
-    :license: LICENSE_NAME, see LICENSE_FILE for more details.
+    :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
--- a/rhodecode/lib/helpers.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/helpers.py	Mon Oct 17 01:01:18 2011 +0200
@@ -598,12 +598,11 @@
         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.name, url('repos_group_home',
+                                           group_name=group.group_name))
         return literal(' &raquo; '.join(map(make_link, groups)) + \
                        " &raquo; " + repo_name)
 
-
 def fancy_file_stats(stats):
     """
     Displays a fancy two colored bar for number of added/deleted
--- a/rhodecode/lib/indexers/__init__.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/indexers/__init__.py	Mon Oct 17 01:01:18 2011 +0200
@@ -101,7 +101,7 @@
         from rhodecode.lib.pidlock import LockHeld, DaemonLock
         from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
         try:
-            l = DaemonLock(file=jn(dn(dn(index_location)), 'make_index.lock'))
+            l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
             WhooshIndexingDaemon(index_location=index_location,
                                  repo_location=repo_location,
                                  repo_list=repo_list)\
--- a/rhodecode/lib/middleware/simplegit.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/middleware/simplegit.py	Mon Oct 17 01:01:18 2011 +0200
@@ -262,7 +262,7 @@
         return repo_name
 
     def __get_user(self, username):
-        return User.by_username(username)
+        return User.get_by_username(username)
 
     def __get_action(self, environ):
         """Maps git request commands into a pull or push command.
--- a/rhodecode/lib/middleware/simplehg.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/middleware/simplehg.py	Mon Oct 17 01:01:18 2011 +0200
@@ -76,7 +76,7 @@
 
         # skip passing error to error controller
         environ['pylons.status_code_redirect'] = True
-                
+
         #======================================================================
         # EXTRACT REPOSITORY NAME FROM ENV
         #======================================================================
@@ -90,12 +90,13 @@
         # GET ACTION PULL or PUSH
         #======================================================================
         action = self.__get_action(environ)
-        
+
         #======================================================================
         # CHECK ANONYMOUS PERMISSION
         #======================================================================
         if action in ['pull', 'push']:
             anonymous_user = self.__get_user('default')
+
             username = anonymous_user.username
             anonymous_perm = self.__check_permission(action,
                                                      anonymous_user,
@@ -152,13 +153,13 @@
         #======================================================================
         # MERCURIAL REQUEST HANDLING
         #======================================================================
-        
+
         repo_path = safe_str(os.path.join(self.basepath, repo_name))
         log.debug('Repository path is %s' % repo_path)
-        
+
         baseui = make_ui('db')
         self.__inject_extras(repo_path, baseui, extras)
-        
+
 
         # quick check if that dir exists...
         if is_valid_repo(repo_name, self.basepath) is False:
@@ -228,7 +229,7 @@
         return repo_name
 
     def __get_user(self, username):
-        return User.by_username(username)
+        return User.get_by_username(username)
 
     def __get_action(self, environ):
         """
@@ -257,7 +258,7 @@
         push requests"""
         invalidate_cache('get_repo_cached_%s' % repo_name)
 
-    def __inject_extras(self,repo_path, baseui, extras={}):
+    def __inject_extras(self, repo_path, baseui, extras={}):
         """
         Injects some extra params into baseui instance
         
--- a/rhodecode/lib/pidlock.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/pidlock.py	Mon Oct 17 01:01:18 2011 +0200
@@ -6,20 +6,7 @@
 from warnings import warn
 from multiprocessing.util import Finalize
 
-from rhodecode import __platform__, PLATFORM_WIN
-
-if __platform__ in PLATFORM_WIN:
-    import ctypes
-
-    def kill(pid, sig):
-        """kill function for Win32"""
-        kernel32 = ctypes.windll.kernel32
-        handle = kernel32.OpenProcess(1, 0, pid)
-        return (0 != kernel32.TerminateProcess(handle, 0))
-
-else:
-    kill = os.kill
-
+from rhodecode.lib.compat import kill
 
 class LockHeld(Exception):
     pass
@@ -29,17 +16,17 @@
     """daemon locking
     USAGE:
     try:
-        l = DaemonLock(desc='test lock')
+        l = DaemonLock(file_='/path/tolockfile',desc='test lock')
         main()
         l.release()
     except LockHeld:
         sys.exit(1)
     """
 
-    def __init__(self, file=None, callbackfn=None,
+    def __init__(self, file_=None, callbackfn=None,
                  desc='daemon lock', debug=False):
 
-        self.pidfile = file if file else os.path.join(
+        self.pidfile = file_ if file_ else os.path.join(
                                                     os.path.dirname(__file__),
                                                     'running.lock')
         self.callbackfn = callbackfn
--- a/rhodecode/lib/smtp_mailer.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/smtp_mailer.py	Mon Oct 17 01:01:18 2011 +0200
@@ -6,9 +6,21 @@
     Simple smtp mailer used in RhodeCode
 
     :created_on: Sep 13, 2010
-    :copyright: (c) 2011 by marcink.
-    :license: LICENSE_NAME, see LICENSE_FILE for more details.
+    :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
 import smtplib
--- a/rhodecode/lib/utils.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/lib/utils.py	Mon Oct 17 01:01:18 2011 +0200
@@ -112,7 +112,7 @@
         if hasattr(user, 'user_id'):
             user_obj = user
         elif isinstance(user, basestring):
-            user_obj = User.by_username(user)
+            user_obj = User.get_by_username(user)
         else:
             raise Exception('You have to provide user object or username')
 
@@ -360,16 +360,19 @@
 
     parent = None
     group = None
-    for lvl, group_name in enumerate(groups[:-1]):
+
+    # last element is repo in nested groups structure
+    groups = groups[:-1]
+
+    for lvl, group_name in enumerate(groups):
+        group_name = '/'.join(groups[:lvl] + [group_name])
         group = sa.query(Group).filter(Group.group_name == group_name).scalar()
 
         if group is None:
             group = Group(group_name, parent)
             sa.add(group)
             sa.commit()
-
         parent = group
-
     return group
 
 
@@ -386,8 +389,13 @@
     rm = RepoModel()
     user = sa.query(User).filter(User.admin == True).first()
     added = []
+    # fixup groups paths to new format on the fly
+    # TODO: remove this in future
+    for g in Group.query().all():
+        g.group_name = g.get_new_name(g.name)
+        sa.add(g)    
     for name, repo in initial_repo_list.items():
-        group = map_groups(name.split(os.sep))
+        group = map_groups(name.split(Repository.url_sep()))
         if not rm.get_by_repo_name(name, cache=False):
             log.info('repository %s not found creating default', name)
             added.append(name)
@@ -442,26 +450,6 @@
             beaker.cache.cache_regions[region] = region_settings
 
 
-def get_current_revision():
-    """Returns tuple of (number, id) from repository containing this package
-    or None if repository could not be found.
-    """
-
-    try:
-        from vcs import get_repo
-        from vcs.utils.helpers import get_scm
-        from vcs.exceptions import RepositoryError, VCSError
-        repopath = os.path.join(os.path.dirname(__file__), '..', '..')
-        scm = get_scm(repopath)[0]
-        repo = get_repo(path=repopath, alias=scm)
-        tip = repo.get_changeset()
-        return (tip.revision, tip.short_id)
-    except (ImportError, RepositoryError, VCSError), err:
-        logging.debug("Cannot retrieve rhodecode's revision. Original error "
-                      "was: %s" % err)
-        return None
-
-
 #==============================================================================
 # TEST FUNCTIONS AND CREATORS
 #==============================================================================
@@ -483,7 +471,7 @@
         os.makedirs(index_location)
 
     try:
-        l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
+        l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
         WhooshIndexingDaemon(index_location=index_location,
                              repo_location=repo_location)\
             .run(full_index=full_index)
--- a/rhodecode/model/db.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/model/db.py	Mon Oct 17 01:01:18 2011 +0200
@@ -31,9 +31,10 @@
 
 from sqlalchemy import *
 from sqlalchemy.exc import DatabaseError
-from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
+from sqlalchemy.ext.hybrid import hybrid_property
+from sqlalchemy.orm import relationship, backref, joinedload, class_mapper, \
+    validates
 from sqlalchemy.orm.interfaces import MapperExtension
-
 from beaker.cache import cache_region, region_invalidate
 
 from vcs import get_backend
@@ -42,13 +43,14 @@
 from vcs.utils.lazy import LazyProperty
 
 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
-    generate_api_key
+    generate_api_key, safe_unicode
 from rhodecode.lib.exceptions import UsersGroupsAssignedException
 from rhodecode.lib.compat import json
 
 from rhodecode.model.meta import Base, Session
 from rhodecode.model.caching_query import FromCache
 
+
 log = logging.getLogger(__name__)
 
 #==============================================================================
@@ -126,7 +128,8 @@
 
     @classmethod
     def get(cls, id_):
-        return Session.query(cls).get(id_)
+        if id_:
+            return Session.query(cls).get(id_)
 
     @classmethod
     def delete(cls, id_):
@@ -140,12 +143,34 @@
     __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
     app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
     app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
-    app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
     def __init__(self, k='', v=''):
         self.app_settings_name = k
         self.app_settings_value = v
 
+
+    @validates('_app_settings_value')
+    def validate_settings_value(self, key, val):
+        assert type(val) == unicode
+        return val
+
+    @hybrid_property
+    def app_settings_value(self):
+        v = self._app_settings_value
+        if v == 'ldap_active':
+            v = str2bool(v)
+        return v 
+
+    @app_settings_value.setter
+    def app_settings_value(self,val):
+        """
+        Setter that will always make sure we use unicode in app_settings_value
+        
+        :param val:
+        """
+        self._app_settings_value = safe_unicode(val)
+
     def __repr__(self):
         return "<%s('%s:%s')>" % (self.__class__.__name__,
                                   self.app_settings_name, self.app_settings_value)
@@ -176,14 +201,11 @@
     @classmethod
     def get_ldap_settings(cls, cache=False):
         ret = Session.query(cls)\
-                .filter(cls.app_settings_name.startswith('ldap_'))\
-                .all()
+                .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
             fd.update({row.app_settings_name:row.app_settings_value})
 
-        fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
-
         return fd
 
 
@@ -281,17 +303,16 @@
             return self.__class__.__name__
 
     @classmethod
-    def by_username(cls, username, case_insensitive=False):
+    def get_by_username(cls, username, case_insensitive=False):
         if case_insensitive:
-            return Session.query(cls).filter(cls.username.like(username)).one()
+            return Session.query(cls).filter(cls.username.ilike(username)).scalar()
         else:
-            return Session.query(cls).filter(cls.username == username).one()
+            return Session.query(cls).filter(cls.username == username).scalar()
 
     @classmethod
     def get_by_api_key(cls, api_key):
         return Session.query(cls).filter(cls.api_key == api_key).one()
 
-
     def update_lastlogin(self):
         """Update user lastlogin"""
 
@@ -487,12 +508,16 @@
                                   self.repo_id, self.repo_name)
 
     @classmethod
-    def by_repo_name(cls, repo_name):
+    def url_sep(cls):
+        return '/'
+    
+    @classmethod
+    def get_by_repo_name(cls, repo_name):
         q = Session.query(cls).filter(cls.repo_name == repo_name)
 
         q = q.options(joinedload(Repository.fork))\
-            .options(joinedload(Repository.user))\
-            .options(joinedload(Repository.group))\
+                .options(joinedload(Repository.user))\
+                .options(joinedload(Repository.group))\
 
         return q.one()
 
@@ -507,13 +532,14 @@
         
         :param cls:
         """
-        q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
+        q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == 
+                                              cls.url_sep())
         q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
 
     @property
     def just_name(self):
-        return self.repo_name.split(os.sep)[-1]
+        return self.repo_name.split(Repository.url_sep())[-1]
 
     @property
     def groups_with_parents(self):
@@ -542,7 +568,8 @@
         Returns base full path for that repository means where it actually
         exists on a filesystem
         """
-        q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
+        q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == 
+                                              Repository.url_sep())
         q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
 
@@ -552,9 +579,18 @@
         # we need to split the name by / since this is how we store the
         # names in the database, but that eventually needs to be converted
         # into a valid system path
-        p += self.repo_name.split('/')
+        p += self.repo_name.split(Repository.url_sep())
         return os.path.join(*p)
 
+    def get_new_name(self, repo_name):
+        """
+        returns new full repository name based on assigned group and new new
+        
+        :param group_name:
+        """
+        path_prefix = self.group.full_path_splitted if self.group else []
+        return Repository.url_sep().join(path_prefix + [repo_name])
+
     @property
     def _ui(self):
         """
@@ -719,9 +755,26 @@
                                   self.group_name)
 
     @classmethod
+    def groups_choices(cls):
+        from webhelpers.html import literal as _literal
+        repo_groups = [('', '')]
+        sep = ' &raquo; '
+        _name = lambda k: _literal(sep.join(k))
+
+        repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
+                              for x in cls.query().all()])
+        
+        repo_groups = sorted(repo_groups,key=lambda t: t[1].split(sep)[0])        
+        return repo_groups
+    
+    @classmethod
     def url_sep(cls):
         return '/'
 
+    @classmethod
+    def get_by_group_name(cls, group_name):
+        return cls.query().filter(cls.group_name == group_name).scalar()
+
     @property
     def parents(self):
         parents_recursion_limit = 5
@@ -751,9 +804,16 @@
         return Session.query(Group).filter(Group.parent_group == self)
 
     @property
+    def name(self):
+        return self.group_name.split(Group.url_sep())[-1]
+
+    @property
     def full_path(self):
-        return Group.url_sep().join([g.group_name for g in self.parents] +
-                        [self.group_name])
+        return self.group_name
+
+    @property
+    def full_path_splitted(self):
+        return self.group_name.split(Group.url_sep())
 
     @property
     def repositories(self):
@@ -772,6 +832,17 @@
 
         return cnt + children_count(self)
 
+
+    def get_new_name(self, group_name):
+        """
+        returns new full group name based on parent and new name
+        
+        :param group_name:
+        """
+        path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
+        return Group.url_sep().join(path_prefix + [group_name])
+
+
 class Permission(Base, BaseModel):
     __tablename__ = 'permissions'
     __table_args__ = {'extend_existing':True}
--- a/rhodecode/model/forms.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/model/forms.py	Mon Oct 17 01:01:18 2011 +0200
@@ -32,6 +32,7 @@
 from pylons.i18n.translation import _
 from webhelpers.pylonslib.secure_form import authentication_token
 
+from rhodecode.config.routing import ADMIN_PREFIX
 from rhodecode.lib.utils import repo_name_slug
 from rhodecode.lib.auth import authenticate, get_crypt_password
 from rhodecode.lib.exceptions import LdapImportError
@@ -70,8 +71,7 @@
                 old_un = UserModel().get(old_data.get('user_id')).username
 
             if old_un != value or not edit:
-                if UserModel().get_by_username(value, cache=False,
-                                               case_insensitive=True):
+                if User.get_by_username(value, case_insensitive=True):
                     raise formencode.Invalid(_('This username already '
                                                'exists') , value, state)
 
@@ -122,7 +122,7 @@
         def validate_python(self, value, state):
             #TODO WRITE VALIDATIONS
             group_name = value.get('group_name')
-            group_parent_id = int(value.get('group_parent_id') or - 1)
+            group_parent_id = int(value.get('group_parent_id') or -1)
 
             # slugify repo group just in case :)
             slug = repo_name_slug(group_name)
@@ -206,7 +206,7 @@
     def validate_python(self, value, state):
         password = value['password']
         username = value['username']
-        user = UserModel().get_by_username(username)
+        user = User.get_by_username(username)
 
         if authenticate(username, password):
             return value
@@ -241,7 +241,7 @@
             repo_name = value.get('repo_name')
 
             slug = repo_name_slug(repo_name)
-            if slug in ['_admin', '']:
+            if slug in [ADMIN_PREFIX, '']:
                 e_dict = {'repo_name': _('This repository name is disallowed')}
                 raise formencode.Invalid('', value, state, error_dict=e_dict)
 
@@ -250,7 +250,7 @@
                 gr = Group.get(value.get('repo_group'))
                 group_path = gr.full_path
                 # value needs to be aware of group name in order to check
-                # db key This is an actuall just the name to store in the
+                # db key This is an actual just the name to store in the
                 # database
                 repo_name_full = group_path + Group.url_sep() + repo_name
             else:
@@ -259,25 +259,32 @@
 
 
             value['repo_name_full'] = repo_name_full
-            if old_data.get('repo_name') != repo_name_full or not edit:
+            rename = old_data.get('repo_name') != repo_name_full
+            create = not edit
+            if  rename or create:
 
                 if group_path != '':
                     if RepoModel().get_by_repo_name(repo_name_full,):
                         e_dict = {'repo_name':_('This repository already '
-                                                'exists in group "%s"') %
+                                                'exists in a group "%s"') %
                                   gr.group_name}
                         raise formencode.Invalid('', value, state,
                                                  error_dict=e_dict)
+                elif Group.get_by_group_name(repo_name_full):
+                        e_dict = {'repo_name':_('There is a group with this'
+                                                ' name already "%s"') %
+                                  repo_name_full}
+                        raise formencode.Invalid('', value, state,
+                                                 error_dict=e_dict)
 
-                else:
-                    if RepoModel().get_by_repo_name(repo_name_full):
+                elif RepoModel().get_by_repo_name(repo_name_full):
                         e_dict = {'repo_name':_('This repository '
                                                 'already exists')}
                         raise formencode.Invalid('', value, state,
                                                  error_dict=e_dict)
+
             return value
 
-
     return _ValidRepoName
 
 def ValidForkName():
@@ -287,7 +294,7 @@
             repo_name = value.get('fork_name')
 
             slug = repo_name_slug(repo_name)
-            if slug in ['_admin', '']:
+            if slug in [ADMIN_PREFIX, '']:
                 e_dict = {'repo_name': _('This repository name is disallowed')}
                 raise formencode.Invalid('', value, state, error_dict=e_dict)
 
--- a/rhodecode/model/repo.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/model/repo.py	Mon Oct 17 01:01:18 2011 +0200
@@ -98,11 +98,11 @@
         try:
             cur_repo = self.get_by_repo_name(repo_name, cache=False)
 
-            #update permissions
+            # 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.by_username(member))\
+                            .filter(RepoToPerm.user == User.get_by_username(member))\
                             .filter(RepoToPerm.repository == cur_repo)\
                             .one()
 
@@ -122,12 +122,12 @@
                                                 perm).scalar()
                     self.sa.add(g2p)
 
-            #set new permissions
+            # set new permissions
             for member, perm, member_type in form_data['perms_new']:
                 if member_type == 'user':
                     r2p = RepoToPerm()
                     r2p.repository = cur_repo
-                    r2p.user = User.by_username(member)
+                    r2p.user = User.get_by_username(member)
 
                     r2p.permission = self.sa.query(Permission)\
                                         .filter(Permission.
@@ -144,26 +144,29 @@
                                                 .scalar()
                     self.sa.add(g2p)
 
-            #update current repo
+            # update current repo
             for k, v in form_data.items():
                 if k == 'user':
-                    cur_repo.user = User.by_username(v)
+                    cur_repo.user = User.get_by_username(v)
                 elif k == 'repo_name':
-                    cur_repo.repo_name = form_data['repo_name_full']
+                    pass
                 elif k == 'repo_group':
                     cur_repo.group_id = v
 
                 else:
                     setattr(cur_repo, k, v)
 
+            new_name = cur_repo.get_new_name(form_data['repo_name'])
+            cur_repo.repo_name = new_name
+
             self.sa.add(cur_repo)
 
-            if repo_name != form_data['repo_name_full']:
+            if repo_name != new_name:
                 # rename repository
-                self.__rename_repo(old=repo_name,
-                                   new=form_data['repo_name_full'])
+                self.__rename_repo(old=repo_name, new=new_name)
 
             self.sa.commit()
+            return cur_repo
         except:
             log.error(traceback.format_exc())
             self.sa.rollback()
@@ -208,8 +211,7 @@
             #create default permission
             repo_to_perm = RepoToPerm()
             default = 'repository.read'
-            for p in UserModel(self.sa).get_by_username('default',
-                                                    cache=False).user_perms:
+            for p in User.get_by_username('default').user_perms:
                 if p.permission.permission_name.startswith('repository.'):
                     default = p.permission.permission_name
                     break
@@ -221,8 +223,7 @@
                     .one().permission_id
 
             repo_to_perm.repository = new_repo
-            repo_to_perm.user_id = UserModel(self.sa)\
-                .get_by_username('default', cache=False).user_id
+            repo_to_perm.user_id = User.get_by_username('default').user_id
 
             self.sa.add(repo_to_perm)
 
@@ -237,7 +238,7 @@
             from rhodecode.model.scm import ScmModel
             ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
                                              cur_user.user_id)
-
+            return new_repo
         except:
             log.error(traceback.format_exc())
             self.sa.rollback()
@@ -304,7 +305,7 @@
         :param parent_id:
         :param clone_uri:
         """
-        from rhodecode.lib.utils import is_valid_repo
+        from rhodecode.lib.utils import is_valid_repo,is_valid_repos_group
 
         if new_parent_id:
             paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
@@ -315,12 +316,20 @@
         repo_path = os.path.join(*map(lambda x:safe_str(x),
                                 [self.repos_path, new_parent_path, repo_name]))
 
-        if is_valid_repo(repo_path, self.repos_path) is False:
-            log.info('creating repo %s in %s @ %s', repo_name, repo_path,
-                     clone_uri)
-            backend = get_backend(alias)
+        
+        # check if this path is not a repository
+        if is_valid_repo(repo_path, self.repos_path):
+            raise Exception('This path %s is a valid repository' % repo_path)
 
-            backend(repo_path, create=True, src_url=clone_uri)
+        # check if this path is a group
+        if is_valid_repos_group(repo_path, self.repos_path):
+            raise Exception('This path %s is a valid group' % repo_path)
+                
+        log.info('creating repo %s in %s @ %s', repo_name, repo_path,
+                 clone_uri)
+        backend = get_backend(alias)
+
+        backend(repo_path, create=True, src_url=clone_uri)
 
 
     def __rename_repo(self, old, new):
--- a/rhodecode/model/repos_group.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/model/repos_group.py	Mon Oct 17 01:01:18 2011 +0200
@@ -50,7 +50,7 @@
         q = RhodeCodeUi.get_by_key('/').one()
         return q.ui_value
 
-    def __create_group(self, group_name, parent_id):
+    def __create_group(self, group_name):
         """
         makes repositories group on filesystem
 
@@ -58,44 +58,30 @@
         :param parent_id:
         """
 
-        if parent_id:
-            paths = Group.get(parent_id).full_path.split(Group.url_sep())
-            parent_path = os.sep.join(paths)
-        else:
-            parent_path = ''
-
-        create_path = os.path.join(self.repos_path, parent_path, group_name)
+        create_path = os.path.join(self.repos_path, group_name)
         log.debug('creating new group in %s', create_path)
 
         if os.path.isdir(create_path):
             raise Exception('That directory already exists !')
 
-
         os.makedirs(create_path)
 
-
-    def __rename_group(self, old, old_parent_id, new, new_parent_id):
+    def __rename_group(self, old, new):
         """
         Renames a group on filesystem
         
         :param group_name:
         """
+
+        if old == new:
+            log.debug('skipping group rename')
+            return
+
         log.debug('renaming repos group from %s to %s', old, new)
 
-        if new_parent_id:
-            paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
-            new_parent_path = os.sep.join(paths)
-        else:
-            new_parent_path = ''
 
-        if old_parent_id:
-            paths = Group.get(old_parent_id).full_path.split(Group.url_sep())
-            old_parent_path = os.sep.join(paths)
-        else:
-            old_parent_path = ''
-
-        old_path = os.path.join(self.repos_path, old_parent_path, old)
-        new_path = os.path.join(self.repos_path, new_parent_path, new)
+        old_path = os.path.join(self.repos_path, old)
+        new_path = os.path.join(self.repos_path, new)
 
         log.debug('renaming repos paths from %s to %s', old_path, new_path)
 
@@ -114,22 +100,23 @@
         paths = os.sep.join(paths)
 
         rm_path = os.path.join(self.repos_path, paths)
-        os.rmdir(rm_path)
+        if os.path.isdir(rm_path):
+            # delete only if that path really exists
+            os.rmdir(rm_path)
 
     def create(self, form_data):
         try:
             new_repos_group = Group()
-            new_repos_group.group_name = form_data['group_name']
-            new_repos_group.group_description = \
-                form_data['group_description']
-            new_repos_group.group_parent_id = form_data['group_parent_id']
+            new_repos_group.group_description = form_data['group_description']
+            new_repos_group.parent_group = Group.get(form_data['group_parent_id'])
+            new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name'])
 
             self.sa.add(new_repos_group)
 
-            self.__create_group(form_data['group_name'],
-                                form_data['group_parent_id'])
+            self.__create_group(new_repos_group.group_name)
 
             self.sa.commit()
+            return new_repos_group
         except:
             log.error(traceback.format_exc())
             self.sa.rollback()
@@ -139,23 +126,27 @@
 
         try:
             repos_group = Group.get(repos_group_id)
-            old_name = repos_group.group_name
-            old_parent_id = repos_group.group_parent_id
+            old_path = repos_group.full_path
 
-            repos_group.group_name = form_data['group_name']
-            repos_group.group_description = \
-                form_data['group_description']
-            repos_group.group_parent_id = form_data['group_parent_id']
+            #change properties
+            repos_group.group_description = form_data['group_description']
+            repos_group.parent_group = Group.get(form_data['group_parent_id'])
+            repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
+
+            new_path = repos_group.full_path
 
             self.sa.add(repos_group)
 
-            if old_name != form_data['group_name'] or (old_parent_id !=
-                                                form_data['group_parent_id']):
-                self.__rename_group(old=old_name, old_parent_id=old_parent_id,
-                                    new=form_data['group_name'],
-                                    new_parent_id=form_data['group_parent_id'])
+            self.__rename_group(old_path, new_path)
+
+            # we need to get all repositories from this new group and 
+            # rename them accordingly to new group path
+            for r in repos_group.repositories:
+                r.repo_name = r.get_new_name(r.just_name)
+                self.sa.add(r)
 
             self.sa.commit()
+            return repos_group
         except:
             log.error(traceback.format_exc())
             self.sa.rollback()
--- a/rhodecode/model/scm.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/model/scm.py	Mon Oct 17 01:01:18 2011 +0200
@@ -41,9 +41,8 @@
 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
     action_logger, EmptyChangeset
 from rhodecode.model import BaseModel
-from rhodecode.model.user import UserModel
 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
-    UserFollowing, UserLog
+    UserFollowing, UserLog, User
 
 log = logging.getLogger(__name__)
 
@@ -148,10 +147,15 @@
         repos_list = {}
 
         for name, path in get_filesystem_repos(repos_path, recursive=True):
+            
+            # name need to be decomposed and put back together using the /
+            # since this is internal storage separator for rhodecode
+            name = Repository.url_sep().join(name.split(os.sep))
+            
             try:
                 if name in repos_list:
                     raise RepositoryError('Duplicate repository name %s '
-                                    'found in %s' % (name, path))
+                                          'found in %s' % (name, path))
                 else:
 
                     klass = get_backend(path[0])
@@ -283,7 +287,7 @@
         return f is not None
 
     def is_following_user(self, username, user_id, cache=False):
-        u = UserModel(self.sa).get_by_username(username)
+        u = User.get_by_username(username)
 
         f = self.sa.query(UserFollowing)\
             .filter(UserFollowing.follows_user == u)\
@@ -293,24 +297,24 @@
 
     def get_followers(self, repo_id):
         if not isinstance(repo_id, int):
-            repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
+            repo_id = getattr(Repository.get_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 not isinstance(repo_id, int):
-            repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
+            repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
 
         return self.sa.query(Repository)\
                 .filter(Repository.fork_id == repo_id).count()
 
     def pull_changes(self, repo_name, username):
-        dbrepo = Repository.by_repo_name(repo_name)
+        dbrepo = Repository.get_by_repo_name(repo_name)
         clone_uri = dbrepo.clone_uri
         if not clone_uri:
             raise Exception("This repository doesn't have a clone uri")
-        
+
         repo = dbrepo.scm_instance
         try:
             extras = {'ip': '',
@@ -363,12 +367,12 @@
             from vcs.backends.git import GitInMemoryChangeset as IMC
         # decoding here will force that we have proper encoded values
         # in any other case this will throw exceptions and deny commit
-        
-        if isinstance(content,(basestring,)):
+
+        if isinstance(content, (basestring,)):
             content = safe_str(content)
-        elif isinstance(content,file):
+        elif isinstance(content, file):
             content = content.read()
-            
+
         message = safe_str(message)
         path = safe_str(f_path)
         author = safe_str(author)
--- a/rhodecode/public/css/style.css	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/public/css/style.css	Mon Oct 17 01:01:18 2011 +0200
@@ -246,12 +246,13 @@
 }
 
 #header #header-inner {
-height:40px;
+min-height:40px;
 clear:both;
 position:relative;
 background:#003367 url("../images/header_inner.png") repeat-x;
 margin:0;
 padding:0;
+display:block;
 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
 -webkit-border-radius: 4px 4px 4px 4px;
 -khtml-border-radius: 4px 4px 4px 4px; 
@@ -272,7 +273,10 @@
 #header #header-inner #home a:hover {
 background-position:0 -40px;
 }
-
+#header #header-inner #logo {
+    float: left;
+    position: absolute;	
+}
 #header #header-inner #logo h1 {
 color:#FFF;
 font-size:18px;
@@ -348,7 +352,7 @@
 left:0;
 border-left:none;
 border-right:1px solid #2e5c89;
-padding:8px 8px 4px;
+padding:8px 6px 4px;
 }
 
 #header #header-inner #quick li span.icon_short {
@@ -356,7 +360,10 @@
 left:0;
 border-left:none;
 border-right:1px solid #2e5c89;
-padding:9px 4px 4px;
+padding:8px 6px 4px;
+}
+#header #header-inner #quick li span.icon img, #header #header-inner #quick li span.icon_short img {
+	margin: 0px -2px 0px 0px;
 }
 
 #header #header-inner #quick li a:hover {
@@ -564,6 +571,14 @@
 }
 
 
+.groups_breadcrumbs a {
+	color: #fff;
+}
+.groups_breadcrumbs a:hover {
+    color: #bfe3ff;
+    text-decoration: none;
+}
+
 .quick_repo_menu{
 	background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
 	cursor: pointer;
@@ -1895,19 +1910,6 @@
     padding-left: 5px;
 }
 
-div.browserblock .search_activate #filter_activate,div.browserblock .add_node a{
-	vertical-align: sub;
-	border: 1px solid;
-	padding:2px;
-	-webkit-border-radius: 4px 4px 4px 4px;
-	-khtml-border-radius: 4px 4px 4px 4px; 
-	-moz-border-radius: 4px 4px 4px 4px;
-	border-radius: 4px 4px 4px 4px;
-	background: url("../images/button.png") repeat-x scroll 0 0 #E5E3E3;
-	border-color: #DDDDDD #DDDDDD #C6C6C6 #C6C6C6;
-	color: #515151;
-}
-
 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover{
     text-decoration: none !important;    
 }
@@ -2371,8 +2373,10 @@
 border:1px solid #316293;
 }
 
-
-input.ui-button-small {
+.ui-button-small a:hover {
+	
+}
+input.ui-button-small,.ui-button-small {
 background:#e5e3e3 url("../images/button.png") repeat-x !important;
 border-top:1px solid #DDD !important;
 border-left:1px solid #c6c6c6 !important;
@@ -2387,17 +2391,19 @@
 border-radius: 4px 4px 4px 4px !important;
 box-shadow: 0 1px 0 #ececec !important;
 cursor: pointer !important;
-}
-
-input.ui-button-small:hover {
+padding:0px 2px 1px 2px;
+}
+
+input.ui-button-small:hover,.ui-button-small:hover {
 background:#b4b4b4 url("../images/button_selected.png") repeat-x !important;
 border-top:1px solid #ccc !important;
 border-left:1px solid #bebebe !important;
 border-right:1px solid #b1b1b1 !important;
-border-bottom:1px solid #afafaf !important;	
-}
-
-input.ui-button-small-blue {
+border-bottom:1px solid #afafaf !important;
+text-decoration: none;
+}
+
+input.ui-button-small-blue,.ui-button-small-blue {
 background:#4e85bb url("../images/button_highlight.png") repeat-x;
 border-top:1px solid #5c91a4;
 border-left:1px solid #2a6f89;
@@ -2410,6 +2416,7 @@
 border-radius: 4px 4px 4px 4px;
 box-shadow: 0 1px 0 #ececec;
 cursor: pointer;
+padding:0px 2px 1px 2px;
 }
 
 input.ui-button-small-blue:hover {
--- a/rhodecode/templates/admin/repos_groups/repos_groups.html	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/templates/admin/repos_groups/repos_groups.html	Mon Oct 17 01:01:18 2011 +0200
@@ -5,12 +5,14 @@
 </%def>
 
 <%def name="breadcrumbs()">
+    <span class="groups_breadcrumbs">
     ${_('Groups')} 
     %if c.group.parent_group:
-        &raquo; ${h.link_to(c.group.parent_group.group_name,
-        h.url('repos_group',id=c.group.parent_group.group_id))}
+        &raquo; ${h.link_to(c.group.parent_group.name,
+        h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
     %endif
-    &raquo; "${c.group.group_name}" ${_('with')} 
+    &raquo; "${c.group.name}" ${_('with')}
+    </span> 
 </%def>
 
 <%def name="page_nav()">
--- a/rhodecode/templates/admin/repos_groups/repos_groups_edit.html	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/templates/admin/repos_groups/repos_groups_edit.html	Mon Oct 17 01:01:18 2011 +0200
@@ -2,14 +2,14 @@
 <%inherit file="/base/base.html"/>
 
 <%def name="title()">
-    ${_('Edit repos group')} ${c.repos_group.group_name} - ${c.rhodecode_name}
+    ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name}
 </%def>
 <%def name="breadcrumbs_links()">
     ${h.link_to(_('Admin'),h.url('admin_home'))} 
     &raquo; 
     ${h.link_to(_('Repos groups'),h.url('repos_groups'))} 
     &raquo;
-    ${_('edit repos group')} "${c.repos_group.group_name}"
+    ${_('edit repos group')} "${c.repos_group.name}"
 </%def>
 
 <%def name="page_nav()">
--- a/rhodecode/templates/admin/repos_groups/repos_groups_show.html	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/templates/admin/repos_groups/repos_groups_show.html	Mon Oct 17 01:01:18 2011 +0200
@@ -44,14 +44,14 @@
                       <td>
                           <div style="white-space: nowrap">
                           <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
-                          ${h.link_to(h.literal(' &raquo; '.join([g.group_name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
+                          ${h.link_to(h.literal(' &raquo; '.join([g.name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
                           </div>
                       </td>
                       <td>${gr.group_description}</td>
                       <td><b>${gr.repositories.count()}</b></td>
 		               <td>
 		                 ${h.form(url('repos_group', id=gr.group_id),method='delete')}
-		                   ${h.submit('remove_%s' % gr.group_name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
+		                   ${h.submit('remove_%s' % gr.name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
 		                 ${h.end_form()}
 		               </td>                      
                   </tr>
--- a/rhodecode/templates/files/files_browser.html	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/templates/files/files_browser.html	Mon Oct 17 01:01:18 2011 +0200
@@ -11,9 +11,9 @@
 			${h.form(h.url.current())}
 			<div class="info_box">
 	          <span class="rev">${_('view')}@rev</span> 
-	          <a class="rev" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
+	          <a class="ui-button-small" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
 	          ${h.text('at_rev',value=c.changeset.revision,size=5)}
-	          <a class="rev" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
+	          <a class="ui-button-small" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
 	          ## ${h.submit('view',_('view'),class_="ui-button-small")}
 		    </div>           
 			${h.end_form()}
@@ -24,11 +24,11 @@
 	    </div>
         <div class="browser-search">
               <div id="search_activate_id" class="search_activate">
-                  <a id="filter_activate" href="#">${_('search file list')}</a>
+                  <a class="ui-button-small" id="filter_activate" href="#">${_('search file list')}</a>
               </div>
               % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):            
-                    <div  id="add_node_id" class="add_node">
-                        <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
+                    <div id="add_node_id" class="add_node">
+                        <a class="ui-button-small" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
                     </div>
               % endif               
         <div>
--- a/rhodecode/templates/index_base.html	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/templates/index_base.html	Mon Oct 17 01:01:18 2011 +0200
@@ -35,7 +35,7 @@
                       <td>
                           <div style="white-space: nowrap">
                           <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
-                          ${h.link_to(gr.group_name,url('repos_group',id=gr.group_id))}
+                          ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
                           </div>
                       </td>
                       <td>${gr.group_description}</td>
--- a/rhodecode/templates/summary/summary.html	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/templates/summary/summary.html	Mon Oct 17 01:01:18 2011 +0200
@@ -45,17 +45,17 @@
                  
                  ##REPO TYPE
 		         %if c.dbrepo.repo_type =='hg':
-		           <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
+		           <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
 		         %endif
 		         %if c.dbrepo.repo_type =='git':
-		           <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
+		           <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
 		         %endif 
                             
                  ##PUBLIC/PRIVATE     			  
 	             %if c.dbrepo.private:
-	                <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
+	                <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
 	             %else:
-	                <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
+	                <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
 	             %endif
 	             
 	              ##REPO NAME
@@ -67,7 +67,7 @@
 	            	<a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
 	            	<img class="icon" alt="${_('public')}"
 	            	title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" 
-	            	src="${h.url("/images/icons/arrow_divide.png")}"/>
+	            	src="${h.url('/images/icons/arrow_divide.png')}"/>
 	            	${_('Fork of')} ${c.dbrepo.fork.repo_name}
 	            	</a>
 	            	</div>
@@ -78,7 +78,7 @@
                     <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
                     <img class="icon" alt="${_('remote clone')}"
                     title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" 
-                    src="${h.url("/images/icons/connect.png")}"/>
+                    src="${h.url('/images/icons/connect.png')}"/>
                     ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
                     </a>
                     </div>					
@@ -151,7 +151,7 @@
 		        %elif c.enable_downloads is False:
 		          ${_('Downloads are disabled for this repository')}
                     %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
-                        [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
+                        ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
                     %endif  		          
 		        %else:
 			        ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
@@ -317,7 +317,7 @@
          %if c.no_data:
            ${c.no_data_msg}
            %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
-                [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
+                ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
            %endif         
            
         %else:
--- a/rhodecode/tests/functional/test_changelog.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/tests/functional/test_changelog.py	Mon Oct 17 01:01:18 2011 +0200
@@ -13,7 +13,7 @@
                         """name="5e204e7583b9" type="checkbox" value="1" />"""
                         in response.body)
         self.assertTrue("""<span>commit 154: 5e204e7583b9@2010-08-10 """
-                        """01:18:46</span>""" in response.body)
+                        """02:18:46</span>""" in response.body)
         self.assertTrue("""Small update at simplevcs app""" in response.body)
 
 
@@ -43,7 +43,7 @@
                         """name="46ad32a4f974" type="checkbox" value="1" />"""
                         in response.body)
         self.assertTrue("""<span>commit 64: 46ad32a4f974@2010-04-20"""
-                        """ 00:33:21</span>"""in response.body)
+                        """ 01:33:21</span>"""in response.body)
 
         self.assertTrue("""<span id="46ad32a4f974e45472a898c6b0acb600320"""
                         """579b1" class="changed_total tooltip" """
--- a/rhodecode/tests/functional/test_login.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/tests/functional/test_login.py	Mon Oct 17 01:01:18 2011 +0200
@@ -145,8 +145,8 @@
                                              'lastname':'test'})
 
         self.assertEqual(response.status , '200 OK')
-        assert 'An email address must contain a single @' in response.body
-        assert 'This username already exists' in response.body
+        self.assertTrue('An email address must contain a single @' in response.body)
+        self.assertTrue('This username already exists' in response.body)
 
 
 
@@ -160,7 +160,7 @@
                                              'lastname':'test'})
 
         self.assertEqual(response.status , '200 OK')
-        assert 'Invalid characters in password' in response.body
+        self.assertTrue('Invalid characters in password' in response.body)
 
 
     def test_register_password_mismatch(self):
@@ -246,7 +246,7 @@
 
         # GOOD KEY
 
-        key = User.by_username(username).api_key
+        key = User.get_by_username(username).api_key
 
         response = self.app.get(url(controller='login',
                                     action='password_reset_confirmation',
--- a/rhodecode/tests/functional/test_summary.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/tests/functional/test_summary.py	Mon Oct 17 01:01:18 2011 +0200
@@ -41,7 +41,7 @@
 
 
     def _enable_stats(self):
-        r = Repository.by_repo_name(HG_REPO)
+        r = Repository.get_by_repo_name(HG_REPO)
         r.enable_statistics = True
         self.sa.add(r)
         self.sa.commit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/tests/test_concurency.py	Mon Oct 17 01:01:18 2011 +0200
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+"""
+    rhodecode.tests.test_hg_operations
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Test suite for making push/pull operations
+
+    :created_on: Dec 30, 2010
+    :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 os
+import sys
+import shutil
+import logging
+from os.path import join as jn
+from os.path import dirname as dn
+
+from tempfile import _RandomNameSequence
+from subprocess import Popen, PIPE
+
+from paste.deploy import appconfig
+from pylons import config
+from sqlalchemy import engine_from_config
+
+from rhodecode.lib.utils import add_cache
+from rhodecode.model import init_model
+from rhodecode.model import meta
+from rhodecode.model.db import User, Repository
+from rhodecode.lib.auth import get_crypt_password
+
+from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
+from rhodecode.config.environment import load_environment
+
+rel_path = dn(dn(dn(os.path.abspath(__file__))))
+conf = appconfig('config:development.ini', relative_to=rel_path)
+load_environment(conf.global_conf, conf.local_conf)
+
+add_cache(conf)
+
+USER = 'test_admin'
+PASS = 'test12'
+HOST = '127.0.0.1:5000'
+DEBUG = True
+log = logging.getLogger(__name__)
+
+
+class Command(object):
+
+    def __init__(self, cwd):
+        self.cwd = cwd
+
+    def execute(self, cmd, *args):
+        """Runs command on the system with given ``args``.
+        """
+
+        command = cmd + ' ' + ' '.join(args)
+        log.debug('Executing %s' % command)
+        if DEBUG:
+            print command
+        p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
+        stdout, stderr = p.communicate()
+        if DEBUG:
+            print stdout, stderr
+        return stdout, stderr
+
+def get_session():
+    engine = engine_from_config(conf, 'sqlalchemy.db1.')
+    init_model(engine)
+    sa = meta.Session()
+    return sa
+
+
+def create_test_user(force=True):
+    print 'creating test user'
+    sa = get_session()
+
+    user = sa.query(User).filter(User.username == USER).scalar()
+
+    if force and user is not None:
+        print 'removing current user'
+        for repo in sa.query(Repository).filter(Repository.user == user).all():
+            sa.delete(repo)
+        sa.delete(user)
+        sa.commit()
+
+    if user is None or force:
+        print 'creating new one'
+        new_usr = User()
+        new_usr.username = USER
+        new_usr.password = get_crypt_password(PASS)
+        new_usr.email = 'mail@mail.com'
+        new_usr.name = 'test'
+        new_usr.lastname = 'lasttestname'
+        new_usr.active = True
+        new_usr.admin = True
+        sa.add(new_usr)
+        sa.commit()
+
+    print 'done'
+
+
+def create_test_repo(force=True):
+    print 'creating test repo'
+    from rhodecode.model.repo import RepoModel
+    sa = get_session()
+
+    user = sa.query(User).filter(User.username == USER).scalar()
+    if user is None:
+        raise Exception('user not found')
+
+
+    repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
+
+    if repo is None:
+        print 'repo not found creating'
+
+        form_data = {'repo_name':HG_REPO,
+                     'repo_type':'hg',
+                     'private':False,
+                     'clone_uri':'' }
+        rm = RepoModel(sa)
+        rm.base_path = '/home/hg'
+        rm.create(form_data, user)
+
+    print 'done'
+
+def set_anonymous_access(enable=True):
+    sa = get_session()
+    user = sa.query(User).filter(User.username == 'default').one()
+    user.active = enable
+    sa.add(user)
+    sa.commit()
+
+def get_anonymous_access():
+    sa = get_session()
+    return sa.query(User).filter(User.username == 'default').one().active
+
+
+#==============================================================================
+# TESTS
+#==============================================================================
+def test_clone_with_credentials(no_errors=False, repo=HG_REPO):
+    cwd = path = jn(TESTS_TMP_PATH, repo)
+
+
+    try:
+        shutil.rmtree(path, ignore_errors=True)
+        os.makedirs(path)
+        #print 'made dirs %s' % jn(path)
+    except OSError:
+        raise
+
+
+    clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
+                  {'user':USER,
+                   'pass':PASS,
+                   'host':HOST,
+                   'cloned_repo':repo,
+                   'dest':path + _RandomNameSequence().next()}
+
+    stdout, stderr = Command(cwd).execute('hg clone', clone_url)
+
+    if no_errors is False:
+        assert """adding file changes""" in stdout, 'no messages about cloning'
+        assert """abort""" not in stderr , 'got error from clone'
+
+if __name__ == '__main__':
+    try:
+        create_test_user(force=False)
+
+        for i in range(int(sys.argv[2])):
+            test_clone_with_credentials(repo=sys.argv[1])
+
+    except Exception, e:
+        sys.exit('stop on %s' % e)
--- a/rhodecode/tests/test_hg_operations.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/tests/test_hg_operations.py	Mon Oct 17 01:01:18 2011 +0200
@@ -6,9 +6,21 @@
     Test suite for making push/pull operations
 
     :created_on: Dec 30, 2010
-    :copyright: (c) 2010 by marcink.
-    :license: LICENSE_NAME, see LICENSE_FILE for more details.
+    :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 os
 import time
@@ -29,7 +41,7 @@
 from rhodecode.lib.utils import add_cache
 from rhodecode.model import init_model
 from rhodecode.model import meta
-from rhodecode.model.db import User, Repository
+from rhodecode.model.db import User, Repository, UserLog
 from rhodecode.lib.auth import get_crypt_password
 
 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
@@ -44,8 +56,8 @@
 USER = 'test_admin'
 PASS = 'test12'
 HOST = '127.0.0.1:5000'
-DEBUG = bool(int(sys.argv[1]))
-print 'DEBUG:',DEBUG
+DEBUG = True if sys.argv[1:] else False
+print 'DEBUG:', DEBUG
 log = logging.getLogger(__name__)
 
 
@@ -70,15 +82,17 @@
 
 
 def test_wrapp(func):
-    
-    def __wrapp(*args,**kwargs):
-        print '###%s###' %func.__name__
+
+    def __wrapp(*args, **kwargs):
+        print '>>>%s' % func.__name__
         try:
-            res = func(*args,**kwargs)
-        except:
-            print '--%s failed--' % func.__name__
-            return
-        print 'ok'
+            res = func(*args, **kwargs)
+        except Exception, e:
+            print ('###############\n-'
+                   '--%s failed %s--\n'
+                   '###############\n' % (func.__name__, e))
+            sys.exit()
+        print '++OK++'
         return res
     return __wrapp
 
@@ -90,20 +104,20 @@
 
 
 def create_test_user(force=True):
-    print 'creating test user'
+    print '\tcreating test user'
     sa = get_session()
 
     user = sa.query(User).filter(User.username == USER).scalar()
 
     if force and user is not None:
-        print 'removing current user'
+        print '\tremoving current user'
         for repo in sa.query(Repository).filter(Repository.user == user).all():
             sa.delete(repo)
         sa.delete(user)
         sa.commit()
 
     if user is None or force:
-        print 'creating new one'
+        print '\tcreating new one'
         new_usr = User()
         new_usr.username = USER
         new_usr.password = get_crypt_password(PASS)
@@ -115,7 +129,7 @@
         sa.add(new_usr)
         sa.commit()
 
-    print 'done'
+    print '\tdone'
 
 
 def create_test_repo(force=True):
@@ -130,7 +144,7 @@
     repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
 
     if repo is None:
-        print 'repo not found creating'
+        print '\trepo not found creating'
 
         form_data = {'repo_name':HG_REPO,
                      'repo_type':'hg',
@@ -144,12 +158,13 @@
 def set_anonymous_access(enable=True):
     sa = get_session()
     user = sa.query(User).filter(User.username == 'default').one()
+    sa.expire(user)
     user.active = enable
     sa.add(user)
     sa.commit()
     sa.remove()
-    
-    print 'anonymous access is now:',enable
+    import time;time.sleep(3)
+    print '\tanonymous access is now:', enable
 
 
 def get_anonymous_access():
@@ -173,13 +188,13 @@
     except OSError:
         raise
 
-    print 'checking if anonymous access is enabled'
+    print '\tchecking if anonymous access is enabled'
     anonymous_access = get_anonymous_access()
     if anonymous_access:
-        print 'enabled, disabling it '
+        print '\tenabled, disabling it '
         set_anonymous_access(enable=False)
         time.sleep(1)
-        
+
     clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
                   {'user':USER,
                    'pass':PASS,
@@ -206,13 +221,13 @@
         raise
 
 
-    print 'checking if anonymous access is enabled'
+    print '\tchecking if anonymous access is enabled'
     anonymous_access = get_anonymous_access()
     if not anonymous_access:
-        print 'not enabled, enabling it '
+        print '\tnot enabled, enabling it '
         set_anonymous_access(enable=True)
         time.sleep(1)
-        
+
     clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
                   {'user':USER,
                    'pass':PASS,
@@ -227,7 +242,7 @@
 
     #disable if it was enabled
     if not anonymous_access:
-        print 'disabling anonymous access'
+        print '\tdisabling anonymous access'
         set_anonymous_access(enable=False)
 
 @test_wrapp
@@ -241,12 +256,12 @@
     except OSError:
         raise
 
-    print 'checking if anonymous access is enabled'
+    print '\tchecking if anonymous access is enabled'
     anonymous_access = get_anonymous_access()
     if anonymous_access:
-        print 'enabled, disabling it '
+        print '\tenabled, disabling it '
         set_anonymous_access(enable=False)
-        
+
     clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
                   {'user':USER + 'error',
                    'pass':PASS,
@@ -257,7 +272,7 @@
     stdout, stderr = Command(cwd).execute('hg clone', clone_url)
 
     if not """abort: authorization failed"""  in stderr:
-        raise Exception('Failure')    
+        raise Exception('Failure')
 
 @test_wrapp
 def test_pull():
@@ -335,7 +350,7 @@
     try:
         shutil.rmtree(path, ignore_errors=True)
         os.makedirs(path)
-        print 'made dirs %s' % jn(path)
+        print '\tmade dirs %s' % jn(path)
     except OSError:
         raise
 
@@ -361,19 +376,37 @@
     if not """abort: HTTP Error 403: Forbidden"""  in stderr:
         raise Exception('Failure')
 
+@test_wrapp
+def get_logs():
+    sa = get_session()
+    return len(sa.query(UserLog).all())
+
+@test_wrapp
+def test_logs(initial):
+    sa = get_session()
+    logs = sa.query(UserLog).all()
+    operations = 7
+    if initial + operations != len(logs):
+        raise Exception("missing number of logs %s vs %s" % (initial, len(logs)))
+
 
 if __name__ == '__main__':
     create_test_user(force=False)
     create_test_repo()
-    
+
+    initial_logs = get_logs()
+
 #    test_push_modify_file()
     test_clone_with_credentials()
     test_clone_wrong_credentials()
 
 
     test_push_new_file(commits=2, with_clone=True)
-#
+
+    test_clone_anonymous()
     test_push_wrong_path()
-    
-    test_clone_anonymous()
-    test_push_wrong_credentials()
\ No newline at end of file
+
+
+    test_push_wrong_credentials()
+
+    test_logs(initial_logs)
--- a/rhodecode/tests/test_libs.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/tests/test_libs.py	Mon Oct 17 01:01:18 2011 +0200
@@ -7,9 +7,21 @@
     Package for testing various lib/helper functions in rhodecode
     
     :created_on: Jun 9, 2011
-    :copyright: (c) 2011 by marcink.
-    :license: LICENSE_NAME, see LICENSE_FILE for more details.
+    :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/>.
 
 
 
--- a/rhodecode/tests/test_models.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/rhodecode/tests/test_models.py	Mon Oct 17 01:01:18 2011 +0200
@@ -1,2 +1,153 @@
+import os
 import unittest
 from rhodecode.tests import *
+
+from rhodecode.model.repos_group import ReposGroupModel
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.db import Group, User
+from sqlalchemy.exc import IntegrityError
+
+class TestReposGroups(unittest.TestCase):
+
+    def setUp(self):
+        self.g1 = self.__make_group('test1', skip_if_exists=True)
+        self.g2 = self.__make_group('test2', skip_if_exists=True)
+        self.g3 = self.__make_group('test3', skip_if_exists=True)
+
+    def tearDown(self):
+        print 'out'
+
+    def __check_path(self, *path):
+        path = [TESTS_TMP_PATH] + list(path)
+        path = os.path.join(*path)
+        return os.path.isdir(path)
+
+    def _check_folders(self):
+        print os.listdir(TESTS_TMP_PATH)
+
+    def __make_group(self, path, desc='desc', parent_id=None,
+                     skip_if_exists=False):
+
+        gr = Group.get_by_group_name(path)
+        if gr and skip_if_exists:
+            return gr
+
+        form_data = dict(group_name=path,
+                         group_description=desc,
+                         group_parent_id=parent_id)
+        gr = ReposGroupModel().create(form_data)
+        return gr
+
+    def __delete_group(self, id_):
+        ReposGroupModel().delete(id_)
+
+
+    def __update_group(self, id_, path, desc='desc', parent_id=None):
+        form_data = dict(group_name=path,
+                         group_description=desc,
+                         group_parent_id=parent_id)
+
+        gr = ReposGroupModel().update(id_, form_data)
+        return gr
+
+    def test_create_group(self):
+        g = self.__make_group('newGroup')
+        self.assertEqual(g.full_path, 'newGroup')
+
+        self.assertTrue(self.__check_path('newGroup'))
+
+
+    def test_create_same_name_group(self):
+        self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
+
+
+    def test_same_subgroup(self):
+        sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
+        self.assertEqual(sg1.parent_group, self.g1)
+        self.assertEqual(sg1.full_path, 'test1/sub1')
+        self.assertTrue(self.__check_path('test1', 'sub1'))
+
+        ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
+        self.assertEqual(ssg1.parent_group, sg1)
+        self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
+        self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
+
+
+    def test_remove_group(self):
+        sg1 = self.__make_group('deleteme')
+        self.__delete_group(sg1.group_id)
+
+        self.assertEqual(Group.get(sg1.group_id), None)
+        self.assertFalse(self.__check_path('deteteme'))
+
+        sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
+        self.__delete_group(sg1.group_id)
+
+        self.assertEqual(Group.get(sg1.group_id), None)
+        self.assertFalse(self.__check_path('test1', 'deteteme'))
+
+
+    def test_rename_single_group(self):
+        sg1 = self.__make_group('initial')
+
+        new_sg1 = self.__update_group(sg1.group_id, 'after')
+        self.assertTrue(self.__check_path('after'))
+        self.assertEqual(Group.get_by_group_name('initial'), None)
+
+
+    def test_update_group_parent(self):
+
+        sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
+
+        new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
+        self.assertTrue(self.__check_path('test1', 'after'))
+        self.assertEqual(Group.get_by_group_name('test1/initial'), None)
+
+
+        new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
+        self.assertTrue(self.__check_path('test3', 'after'))
+        self.assertEqual(Group.get_by_group_name('test3/initial'), None)
+
+
+        new_sg1 = self.__update_group(sg1.group_id, 'hello')
+        self.assertTrue(self.__check_path('hello'))
+
+        self.assertEqual(Group.get_by_group_name('hello'), new_sg1)
+
+
+
+    def test_subgrouping_with_repo(self):
+
+        g1 = self.__make_group('g1')
+        g2 = self.__make_group('g2')
+
+        # create new repo
+        form_data = dict(repo_name='john',
+                         repo_name_full='john',
+                         fork_name=None,
+                         description=None,
+                         repo_group=None,
+                         private=False,
+                         repo_type='hg',
+                         clone_uri=None)
+        cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
+        r = RepoModel().create(form_data, cur_user)
+
+        self.assertEqual(r.repo_name, 'john')
+
+        # put repo into group
+        form_data = form_data
+        form_data['repo_group'] = g1.group_id
+        form_data['perms_new'] = []
+        form_data['perms_updates'] = []
+        RepoModel().update(r.repo_name, form_data)
+        self.assertEqual(r.repo_name, 'g1/john')
+
+
+        self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
+        self.assertTrue(self.__check_path('g2', 'g1'))
+
+        # test repo
+        self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
+
+
--- a/setup.py	Sat Oct 08 16:26:29 2011 +0200
+++ b/setup.py	Mon Oct 17 01:01:18 2011 +0200
@@ -11,10 +11,11 @@
 
 requirements = [
         "Pylons==1.0.0",
+        "Beaker==1.5.4",
         "WebHelpers>=1.2",
         "formencode==1.2.4",
-        "SQLAlchemy>=0.7.2,<0.8",
-        "Mako>=0.4.2",
+        "SQLAlchemy==0.7.3",
+        "Mako==0.5.0",
         "pygments>=1.4",
         "mercurial>=1.9.3,<2.0",
         "whoosh<1.8",
@@ -22,7 +23,7 @@
         "babel",
         "python-dateutil>=1.5.0,<2.0.0",
         "dulwich>=0.8.0",
-        "vcs>=0.2.1",
+        "vcs==0.2.2",
         "webob==1.0.8"    
     ]
 
@@ -33,7 +34,7 @@
                'Environment :: Web Environment',
                'Framework :: Pylons',
                'Intended Audience :: Developers',
-               'License :: OSI Approved :: BSD License',
+               'License :: OSI Approved :: GNU General Public License (GPL)',
                'Operating System :: OS Independent',
                'Programming Language :: Python',
                'Programming Language :: Python :: 2.5',
--- a/test.ini	Sat Oct 08 16:26:29 2011 +0200
+++ b/test.ini	Mon Oct 17 01:01:18 2011 +0200
@@ -23,6 +23,7 @@
 #smtp_password = 
 #smtp_port = 
 #smtp_use_tls = false
+#smtp_use_ssl = true
 
 [server:main]
 ##nr of threads to spawn
@@ -49,6 +50,7 @@
 cut_off_limit = 256000
 force_https = false
 commit_parse_limit = 25
+use_gravatar = true
 
 ####################################
 ###        CELERY CONFIG        ####
@@ -93,7 +95,6 @@
 beaker.cache.long_term.type=memory
 beaker.cache.long_term.expire=36000
 
-
 beaker.cache.sql_cache_short.type=memory
 beaker.cache.sql_cache_short.expire=10
 
@@ -150,13 +151,13 @@
 ### LOGGING CONFIGURATION   ####
 ################################
 [loggers]
-keys = root, routes, rhodecode, sqlalchemy,beaker,templates
+keys = root, routes, rhodecode, sqlalchemy, beaker, templates
 
 [handlers]
 keys = console
 
 [formatters]
-keys = generic,color_formatter
+keys = generic, color_formatter
 
 #############
 ## LOGGERS ##
@@ -167,9 +168,10 @@
 
 [logger_routes]
 level = ERROR
-handlers = console
+handlers = 
 qualname = routes.middleware
 # "level = DEBUG" logs the route matched and routing variables.
+propagate = 1
 
 [logger_beaker]
 level = DEBUG
@@ -185,9 +187,9 @@
 
 [logger_rhodecode]
 level = ERROR
-handlers = console
+handlers = 
 qualname = rhodecode
-propagate = 0
+propagate = 1
 
 [logger_sqlalchemy]
 level = ERROR
@@ -203,7 +205,7 @@
 class = StreamHandler
 args = (sys.stderr,)
 level = NOTSET
-formatter = color_formatter
+formatter = generic
 
 ################
 ## FORMATTERS ##