changeset 612:79457e03ef68

Merge with b0a411f5ec7056bc0b32c8263538b308c33b82f6
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 18 Oct 2010 15:29:56 +0200
parents edf8567be8ed (diff) b0a411f5ec70 (current diff)
children 956952fea94c
files development.ini production.ini rhodecode/config/deployment.ini_tmpl rhodecode/controllers/changelog.py rhodecode/lib/auth.py rhodecode/model/meta.py
diffstat 38 files changed, 318 insertions(+), 526 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Tue Oct 12 16:49:28 2010 +0200
+++ b/.hgtags	Mon Oct 18 15:29:56 2010 +0200
@@ -14,5 +14,4 @@
 ca41d544dbdfd2f81bd0304168492a26276aadb6 v0.8.3
 2fa16ec5822da0c6fade3dd1ed9b6c0655e5dbbf v0.8.4
 16ba57d8fe2317c49dbd422afd07ab497687aa02 v0.8.5
-2cd05c5803ade0f491d697fe75ce417138fe2295 v1.0.0rc1
-2ec9b6c8cacee06a773713f455a516d622241a25 v1.0.0rc2
+53128b6b9a4ddb6ee9554cbb83a082a6d1316b42 v1.0.0rc4
--- a/development.ini	Tue Oct 12 16:49:28 2010 +0200
+++ b/development.ini	Mon Oct 18 15:29:56 2010 +0200
@@ -50,14 +50,16 @@
 beaker.cache.data_dir=/%(here)s/data/cache/data
 beaker.cache.lock_dir=/%(here)s/data/cache/lock
 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
-beaker.cache.long_term.type=memory
-beaker.cache.long_term.expire=36000
+
+beaker.cache.super_short_term.type=memory
+beaker.cache.super_short_term.expire=10
 
 beaker.cache.short_term.type=memory
 beaker.cache.short_term.expire=60
 
-beaker.cache.super_short_term.type=memory
-beaker.cache.super_short_term.expire=10
+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=5
--- a/docs/changelog.rst	Tue Oct 12 16:49:28 2010 +0200
+++ b/docs/changelog.rst	Mon Oct 18 15:29:56 2010 +0200
@@ -3,12 +3,20 @@
 Changelog
 =========
 
+1.0.0rc4  (**2010-10-12**)
 
-1.0.0rc3 (**tip**)
+- fixed python2.5 missing simplejson imports (thanks to Jens Bäckman)
+- removed cache_manager settings from sqlalchemy meta
+- added sqlalchemy cache settings to ini files
+- validated password length and added second try of failure on paster setup-app
+- fixed setup database destroy prompt even when there was no db
+
+
+1.0.0rc3 (**2010-10-11**)
 
 - fixed i18n during installation.
 
-1.0.0rc2 (**tip**)
+1.0.0rc2 (**2010-10-11**)
 
 - Disabled dirsize in file browser, it's causing nasty bug when dir renames 
   occure. After vcs is fixed it'll be put back again.
--- a/docs/setup.rst	Tue Oct 12 16:49:28 2010 +0200
+++ b/docs/setup.rst	Mon Oct 18 15:29:56 2010 +0200
@@ -17,7 +17,7 @@
 
 ::
 
- paster setup-app production.ini` 
+ paster setup-app production.ini
 
 - This command will create all needed tables and an admin account. 
   When asked for a path You can either use a new location of one with already 
@@ -26,7 +26,7 @@
 - Remember that the given path for mercurial_ repositories must be write 
   accessible for the application. It's very important since RhodeCode web interface
   will work even without such an access but, when trying to do a push it'll 
-  eventually faile with permission denied errors. 
+  eventually fail with permission denied errors. 
 - Run 
 
 ::
@@ -35,15 +35,12 @@
  
 - This command runs the rhodecode server the app should be available at the 
   127.0.0.1:5000. This ip and port is configurable via the production.ini 
-  file  created in previos step
+  file  created in previous step
 - Use admin account you created to login.
 - Default permissions on each repository is read, and owner is admin. So 
-  remember to update these.
-
-- All needed configs are inside rhodecode sources ie. celeryconfig.py, 
-  development.ini, production.ini You can configure the email, ports, loggers, 
-  workers from there.
+  remember to update these if needed.
   
+    
 Setting up Whoosh
 -----------------
 
@@ -51,7 +48,7 @@
 
 ::
  
- python /var/www/rhodecode/rhodecode/lib/indexers/daemon.py incremental <put_here_path_to_repos>
+ python /var/www/rhodecode/<rhodecode_installation_path>/lib/indexers/daemon.py incremental <put_here_path_to_repos>
   
 When using incremental mode whoosh will check last modification date of each file
 and add it to reindex if newer file is available. Also indexing daemon checks
@@ -69,7 +66,7 @@
     listen          80;
     server_name     hg.myserver.com;
     access_log      /var/log/nginx/rhodecode.access.log;
-    error_log      /var/log/nginx/rhodecode.error.log;
+    error_log       /var/log/nginx/rhodecode.error.log;
     location / {
             root /var/www/rhodecode/rhodecode/public/;
             if (!-f $request_filename){
@@ -81,7 +78,7 @@
     }
  }  
   
-Here's the proxy.conf. It's tunned so it'll not timeout on long
+Here's the proxy.conf. It's tuned so it'll not timeout on long
 pushes and also on large pushes::
 
     proxy_redirect              off;
@@ -111,7 +108,7 @@
     lang=en
     cache_dir = %(here)s/data
 
-To not have the statics served by the application.
+To not have the statics served by the application. And improve speed.
 
 
 Other configuration files
--- a/production.ini	Tue Oct 12 16:49:28 2010 +0200
+++ b/production.ini	Mon Oct 18 15:29:56 2010 +0200
@@ -50,14 +50,16 @@
 beaker.cache.data_dir=/%(here)s/data/cache/data
 beaker.cache.lock_dir=/%(here)s/data/cache/lock
 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
-beaker.cache.long_term.type=memory
-beaker.cache.long_term.expire=36000
+
+beaker.cache.super_short_term.type=memory
+beaker.cache.super_short_term.expire=10
 
 beaker.cache.short_term.type=memory
 beaker.cache.short_term.expire=60
 
-beaker.cache.super_short_term.type=memory
-beaker.cache.super_short_term.expire=10
+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=5
--- a/rhodecode/__init__.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/__init__.py	Mon Oct 18 15:29:56 2010 +0200
@@ -24,7 +24,7 @@
 @author: marcink
 """
 
-VERSION = (1, 0, 0, 'rc3')
+VERSION = (1, 0, 0, 'rc4')
 
 __version__ = '.'.join((str(each) for each in VERSION[:4]))
 
--- a/rhodecode/config/deployment.ini_tmpl	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/config/deployment.ini_tmpl	Mon Oct 18 15:29:56 2010 +0200
@@ -51,14 +51,15 @@
 beaker.cache.data_dir=/%(here)s/data/cache/data
 beaker.cache.lock_dir=/%(here)s/data/cache/lock
 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
-beaker.cache.long_term.type=memory
-beaker.cache.long_term.expire=36000
+
+beaker.cache.super_short_term.type=memory
+beaker.cache.super_short_term.expire=10
 
 beaker.cache.short_term.type=memory
 beaker.cache.short_term.expire=60
 
-beaker.cache.super_short_term.type=memory
-beaker.cache.super_short_term.expire=10
+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=5
--- a/rhodecode/config/routing.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/config/routing.py	Mon Oct 18 15:29:56 2010 +0200
@@ -1,4 +1,5 @@
-"""Routes configuration
+"""
+Routes configuration
 
 The more specific and detailed routes should be defined first so they
 may take precedent over the more generic routes. For more information
@@ -15,24 +16,28 @@
     map.minimization = False
     map.explicit = False
 
+    def check_repo(environ, match_dict):
+        """
+        check for valid repository for proper 404 handling
+        :param environ:
+        :param match_dict:
+        """
+        repo_name = match_dict.get('repo_name')
+        return not cr(repo_name, config['base_path'])
+
     # The ErrorController route (handles 404/500 error pages); it should
     # likely stay at the top, ensuring it can always be resolved
     map.connect('/error/{action}', controller='error')
     map.connect('/error/{action}/{id}', controller='error')
 
+    #==========================================================================
     # CUSTOM ROUTES HERE
+    #==========================================================================
+
+    #MAIN PAGE
     map.connect('hg_home', '/', controller='hg', action='index')
-    
-    def check_repo(environ, match_dict):
-        """
-        check for valid repository for proper 404 handling
-        @param environ:
-        @param match_dict:
-        """
-        repo_name = match_dict.get('repo_name')
-        return not cr(repo_name, config['base_path'])
- 
-    #REST REPO MAP
+
+    #ADMIN REPOSITORY REST ROUTES
     with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
         m.connect("repos", "/repos",
              action="create", conditions=dict(method=["POST"]))
@@ -67,11 +72,14 @@
         m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
              action="delete_perm_user", conditions=dict(method=["DELETE"],
                                                         function=check_repo))
-        
+
+    #ADMIN USER REST ROUTES
     map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
+
+    #ADMIN PERMISSIONS REST ROUTES
     map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
-    
-    #REST SETTINGS MAP
+
+    #ADMIN SETTINGS REST ROUTES
     with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
         m.connect("admin_settings", "/settings",
              action="create", conditions=dict(method=["POST"]))
@@ -101,8 +109,8 @@
              action="my_account_update", conditions=dict(method=["PUT"]))
         m.connect("admin_settings_create_repository", "/create_repository",
              action="create_repository", conditions=dict(method=["GET"]))
-    
-    #ADMIN
+
+    #ADMIN MAIN PAGES
     with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
         m.connect('admin_home', '', action='index')#main page
         m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
@@ -110,13 +118,13 @@
     #SEARCH
     map.connect('search', '/_admin/search', controller='search',)
     map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
-    
+
     #LOGIN/LOGOUT/REGISTER/SIGN IN
     map.connect('login_home', '/_admin/login', controller='login')
     map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
     map.connect('register', '/_admin/register', controller='login', action='register')
     map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
-        
+
     #FEEDS
     map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
                 controller='feed', action='rss',
@@ -124,9 +132,9 @@
     map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
                 controller='feed', action='atom',
                 conditions=dict(function=check_repo))
-    
-    
-    #OTHERS
+
+
+    #REPOSITORY ROUTES
     map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
                 controller='changeset', revision='tip',
                 conditions=dict(function=check_repo))
@@ -142,7 +150,7 @@
     map.connect('tags_home', '/{repo_name:.*}/tags',
                 controller='tags', conditions=dict(function=check_repo))
     map.connect('changelog_home', '/{repo_name:.*}/changelog',
-                controller='changelog', conditions=dict(function=check_repo))    
+                controller='changelog', conditions=dict(function=check_repo))
     map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
                 controller='files', revision='tip', f_path='',
                 conditions=dict(function=check_repo))
@@ -157,10 +165,10 @@
                 conditions=dict(function=check_repo))
     map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
                 controller='files', action='annotate', revision='tip', f_path='',
-                conditions=dict(function=check_repo))    
+                conditions=dict(function=check_repo))
     map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
                 controller='files', action='archivefile', revision='tip',
-                conditions=dict(function=check_repo))   
+                conditions=dict(function=check_repo))
     map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
                 controller='settings', action="delete",
                 conditions=dict(method=["DELETE"], function=check_repo))
@@ -177,5 +185,5 @@
     map.connect('repo_fork_home', '/{repo_name:.*}/fork',
                 controller='settings', action='fork',
                 conditions=dict(function=check_repo))
-        
+
     return map
--- a/rhodecode/controllers/admin/repos.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/controllers/admin/repos.py	Mon Oct 18 15:29:56 2010 +0200
@@ -193,7 +193,7 @@
     def delete_perm_user(self, repo_name):
         """
         DELETE an existing repository permission user
-        @param repo_name:
+        :param repo_name:
         """
         
         try:
--- a/rhodecode/controllers/changelog.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/controllers/changelog.py	Mon Oct 18 15:29:56 2010 +0200
@@ -22,6 +22,7 @@
 changelog controller for pylons
 @author: marcink
 """
+
 try:
     import json
 except ImportError:
--- a/rhodecode/lib/auth.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/auth.py	Mon Oct 18 15:29:56 2010 +0200
@@ -67,7 +67,7 @@
     
 def get_crypt_password(password):
     """Cryptographic function used for password hashing based on sha1
-    @param password: password to hash
+    :param password: password to hash
     """    
     return bcrypt.hashpw(password, bcrypt.gensalt(10))
 
@@ -121,7 +121,7 @@
     permission given in db. We don't wannt to check each time from db for new 
     permissions since adding a new permission also requires application restart
     ie. to decorate new views with the newly created permission
-    @param config:
+    :param config:
     """
     log.info('getting information about all available permissions')
     try:
@@ -139,7 +139,7 @@
     """
     Fills user data with those from database and log out user if not present
     in database
-    @param user:
+    :param user:
     """
     sa = meta.Session
     dbuser = sa.query(User).options(FromCache('sql_cache_short',
@@ -159,7 +159,7 @@
 def fill_perms(user):
     """
     Fills user permission attribute with permissions taken from database
-    @param user:
+    :param user:
     """
     
     sa = meta.Session
@@ -234,7 +234,7 @@
 def get_user(session):
     """
     Gets user from session, and wraps permissions into user
-    @param session:
+    :param session:
     """
     user = session.get('rhodecode_user', AuthUser())
     if user.is_authenticated:
--- a/rhodecode/lib/celerylib/tasks.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/celerylib/tasks.py	Mon Oct 18 15:29:56 2010 +0200
@@ -308,9 +308,10 @@
                     'yaws']
     repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
     repo = MercurialRepository(repos_path + repo_name)
-
+    tip = repo.get_changeset()
+    
     code_stats = {}
-    for topnode, dirs, files in repo.walk('/', 'tip'):
+    for topnode, dirs, files in tip.walk('/'):
         for f in files:
             k = f.mimetype
             if f.extension in LANGUAGES_EXTENSIONS:
--- a/rhodecode/lib/db_manage.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/db_manage.py	Mon Oct 18 15:29:56 2010 +0200
@@ -56,7 +56,6 @@
         log.info('checking for existing db in %s', db_path)
         if os.path.isfile(db_path):
             self.db_exists = True
-            log.info('database exist')
             if not override:
                 raise Exception('database already exists')
 
@@ -65,7 +64,7 @@
         Create a auth database
         """
         self.check_for_db(override)
-        if override:
+        if self.db_exists:
             log.info("database exist and it's going to be destroyed")
             if self.tests:
                 destroy = True
@@ -79,15 +78,33 @@
         meta.Base.metadata.create_all(checkfirst=checkfirst)
         log.info('Created tables for %s', self.dbname)
     
-    def admin_prompt(self):
+    def admin_prompt(self, second=False):
         if not self.tests:
             import getpass
+            
+            
+            def get_password():
+                password = getpass.getpass('Specify admin password (min 6 chars):')
+                confirm = getpass.getpass('Confirm password:')
+            
+                if password != confirm:
+                    log.error('passwords mismatch')
+                    return False
+                if len(password) < 6:
+                    log.error('password is to short use at least 6 characters')
+                    return False
+                                
+                return password
+            
             username = raw_input('Specify admin username:')
-            password = getpass.getpass('Specify admin password:')
-            confirm = getpass.getpass('Confirm password:')
-            if password != confirm:
-                log.error('passwords mismatch')
-                sys.exit()
+            
+            password = get_password()
+            if not password:
+                #second try
+                password = get_password()
+                if not password:
+                    sys.exit()
+                
             email = raw_input('Specify admin email:')
             self.create_user(username, password, email, True)
         else:
--- a/rhodecode/lib/helpers.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/helpers.py	Mon Oct 18 15:29:56 2010 +0200
@@ -28,8 +28,8 @@
 class _Link(object):
     '''
     Make a url based on label and url with help of url_for
-    @param label:name of link    if not defined url is used
-    @param url: the url for link
+    :param label:name of link    if not defined url is used
+    :param url: the url for link
     '''
 
     def __call__(self, label='', *url_, **urlargs):
@@ -52,8 +52,8 @@
 def recursive_replace(str, replace=' '):
     """
     Recursive replace of given sign to just one instance
-    @param str: given string
-    @param replace:char to find and replace multiple instances
+    :param str: given string
+    :param replace:char to find and replace multiple instances
         
     Examples::
     >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
@@ -72,7 +72,7 @@
         """
         Special function just to wrap our text into nice formatted autowrapped
         text
-        @param tooltip_title:
+        :param tooltip_title:
         """
         
         return wrap_paragraphs(escape(tooltip_title), trim_at)\
@@ -226,7 +226,7 @@
 def pygmentize(filenode, **kwargs):
     """
     pygmentize function using pygments
-    @param filenode:
+    :param filenode:
     """
     return literal(code_highlight(filenode.content,
                                   filenode.lexer, CodeHtmlFormatter(**kwargs)))
@@ -234,7 +234,7 @@
 def pygmentize_annotation(filenode, **kwargs):
     """
     pygmentize function for annotation
-    @param filenode:
+    :param filenode:
     """
     
     color_dict = {}
--- a/rhodecode/lib/hooks.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/hooks.py	Mon Oct 18 15:29:56 2010 +0200
@@ -53,9 +53,9 @@
 def user_action_mapper(ui, repo, hooktype=None, **kwargs):
     """
     Maps user last push action to new changeset id, from mercurial
-    @param ui:
-    @param repo:
-    @param hooktype:
+    :param ui:
+    :param repo:
+    :param hooktype:
     """
     
     try:
--- a/rhodecode/lib/indexers/__init__.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/indexers/__init__.py	Mon Oct 18 15:29:56 2010 +0200
@@ -115,8 +115,8 @@
         Smart function that implements chunking the content
         but not overlap chunks so it doesn't highlight the same
         close occurrences twice.
-        @param matcher:
-        @param size:
+        :param matcher:
+        :param size:
         """
         memory = [(0, 0)]
         for span in self.matcher.spans():
--- a/rhodecode/lib/indexers/daemon.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/indexers/daemon.py	Mon Oct 18 15:29:56 2010 +0200
@@ -86,8 +86,9 @@
         based on repository walk function
         """
         index_paths_ = set()
+        tip = repo.get_changeset()
         try:
-            for topnode, dirs, files in repo.walk('/', 'tip'):
+            for topnode, dirs, files in tip.walk('/'):
                 for f in files:
                     index_paths_.add(jn(repo.path, f.path))
                 for dir in dirs:
--- a/rhodecode/lib/middleware/simplehg.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/middleware/simplehg.py	Mon Oct 18 15:29:56 2010 +0200
@@ -39,7 +39,7 @@
 import logging
 import os
 import traceback
- 
+
 log = logging.getLogger(__name__)
 
 class SimpleHg(object):
@@ -49,7 +49,7 @@
         self.config = config
         #authenticate this mercurial request using 
         self.authenticate = AuthBasicAuthenticator('', authfunc)
-        
+
     def __call__(self, environ, start_response):
         if not is_mercurial(environ):
             return self.application(environ, start_response)
@@ -66,7 +66,7 @@
                 REMOTE_USER.update(environ, result)
             else:
                 return result.wsgi_application(environ, start_response)
-        
+
         try:
             repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
             if repo_name.endswith('/'):
@@ -74,7 +74,7 @@
         except:
             log.error(traceback.format_exc())
             return HTTPInternalServerError()(environ, start_response)
-        
+
         #===================================================================
         # CHECK PERMISSIONS FOR THIS REQUEST
         #===================================================================
@@ -86,25 +86,29 @@
             except:
                 log.error(traceback.format_exc())
                 return HTTPInternalServerError()(environ, start_response)
+
             #check permissions for this repository
-            if action == 'pull':
+            if action == 'push':
+                if not HasPermissionAnyMiddleware('repository.write',
+                                                  'repository.admin')\
+                                                    (user, repo_name):
+                    return HTTPForbidden()(environ, start_response)
+
+            else:
+                #any other action need at least read permission
                 if not HasPermissionAnyMiddleware('repository.read',
                                                   'repository.write',
                                                   'repository.admin')\
                                                     (user, repo_name):
                     return HTTPForbidden()(environ, start_response)
-            if action == 'push':
-                if not HasPermissionAnyMiddleware('repository.write',
-                                                  'repository.admin')\
-                                                    (user, repo_name):
-                    return HTTPForbidden()(environ, start_response)
-            
-            #log action    
-            proxy_key = 'HTTP_X_REAL_IP'
-            def_key = 'REMOTE_ADDR'
-            ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
-            self.__log_user_action(user, action, repo_name, ipaddr)            
-        
+
+            #log action
+            if action in ('push', 'pull', 'clone'):
+                proxy_key = 'HTTP_X_REAL_IP'
+                def_key = 'REMOTE_ADDR'
+                ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
+                self.__log_user_action(user, action, repo_name, ipaddr)
+
         #===================================================================
         # MERCURIAL REQUEST HANDLING
         #===================================================================
@@ -124,16 +128,16 @@
         except Exception:
             log.error(traceback.format_exc())
             return HTTPInternalServerError()(environ, start_response)
-        
+
         #invalidate cache on push
         if action == 'push':
             self.__invalidate_cache(repo_name)
             messages = []
             messages.append('thank you for using rhodecode')
-        
+
             return self.msg_wrapper(app, environ, start_response, messages)
         else:
-            return app(environ, start_response)           
+            return app(environ, start_response)
 
 
     def msg_wrapper(self, app, environ, start_response, messages=[]):
@@ -141,9 +145,9 @@
         Wrapper for custom messages that come out of mercurial respond messages
         is a list of messages that the user will see at the end of response 
         from merurial protocol actions that involves remote answers
-        @param app:
-        @param environ:
-        @param start_response:
+        :param app:
+        :param environ:
+        :param start_response:
         """
         def custom_messages(msg_list):
             for msg in msg_list:
@@ -154,68 +158,70 @@
     def __make_app(self):
         hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
         return  self.__load_web_settings(hgserve)
-    
+
     def __get_environ_user(self, environ):
         return environ.get('REMOTE_USER')
-    
+
     def __get_user(self, username):
         return get_user_cached(username)
-        
+
     def __get_action(self, environ):
         """
-        Maps mercurial request commands into a pull or push command.
-        @param environ:
+        Maps mercurial request commands into a clone,pull or push command.
+        This should always return a valid command string
+        :param environ:
         """
         mapping = {'changegroup': 'pull',
                    'changegroupsubset': 'pull',
                    'stream_out': 'pull',
-                   'listkeys': 'pull',
+                   #'listkeys': 'pull',
                    'unbundle': 'push',
                    'pushkey': 'push', }
-        
         for qry in environ['QUERY_STRING'].split('&'):
             if qry.startswith('cmd'):
                 cmd = qry.split('=')[-1]
                 if mapping.has_key(cmd):
                     return mapping[cmd]
-    
+                else:
+                    return cmd
+
     def __log_user_action(self, user, action, repo, ipaddr):
         action_logger(user, action, repo, ipaddr)
-        
+
     def __invalidate_cache(self, repo_name):
         """we know that some change was made to repositories and we should
         invalidate the cache to see the changes right away but only for
         push requests"""
         invalidate_cache('cached_repo_list')
         invalidate_cache('full_changelog', repo_name)
-           
-                   
+
+
     def __load_web_settings(self, hgserve):
-        #set the global ui for hgserve
+        #set the global ui for hgserve instance passed
         hgserve.repo.ui = self.baseui
-        
+
         hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
         repoui = make_ui('file', hgrc, False)
-        
-        
+
+
         if repoui:
             #overwrite our ui instance with the section from hgrc file
             for section in ui_sections:
                 for k, v in repoui.configitems(section):
                     hgserve.repo.ui.setconfig(section, k, v)
-            
+
         return hgserve
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- a/rhodecode/lib/pidlock.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/pidlock.py	Mon Oct 18 15:29:56 2010 +0200
@@ -109,8 +109,8 @@
     def makelock(self, lockname, pidfile):
         """
         this function will make an actual lock
-        @param lockname: acctual pid of file
-        @param pidfile: the file to write the pid in
+        :param lockname: acctual pid of file
+        :param pidfile: the file to write the pid in
         """
         if self.debug:
             print 'creating a file %s and pid: %s' % (pidfile, lockname)
--- a/rhodecode/lib/smtp_mailer.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/smtp_mailer.py	Mon Oct 18 15:29:56 2010 +0200
@@ -108,7 +108,7 @@
         '''
         Get content based on type, if content is a string do open first
         else just read because it's a probably open file object
-        @param msg_file:
+        :param msg_file:
         '''
         if isinstance(msg_file, str):
             return open(msg_file, "rb").read()
--- a/rhodecode/lib/utils.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/lib/utils.py	Mon Oct 18 15:29:56 2010 +0200
@@ -36,7 +36,7 @@
 log = logging.getLogger(__name__)
 
 
-def get_repo_slug(request):    
+def get_repo_slug(request):
     return request.environ['pylons.routes_dict'].get('repo_name')
 
 def is_mercurial(environ):
@@ -49,14 +49,26 @@
         return True
     return False
 
+def is_git(environ):
+    """
+    Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
+    then have git client version given.
+    
+    :param environ:
+    """
+    http_user_agent = environ.get('HTTP_USER_AGENT')
+    if http_user_agent.startswith('git'):
+        return True
+    return False
+
 def action_logger(user, action, repo, ipaddr, sa=None):
     """
     Action logger for various action made by users
     """
-    
+
     if not sa:
-        sa = meta.Session 
-        
+        sa = meta.Session
+
     try:
         if hasattr(user, 'user_id'):
             user_id = user.user_id
@@ -64,7 +76,7 @@
             user_id = sa.query(User).filter(User.username == user).one()
         else:
             raise Exception('You have to provide user object or username')
-       
+
         repo_name = repo.lstrip('/')
         user_log = UserLog()
         user_log.user_id = user_id
@@ -82,7 +94,7 @@
         raise
         sa.rollback()
         log.error('could not log user action:%s', str(e))
-                
+
 def check_repo_dir(paths):
     repos_path = paths[0][1].split('/')
     if repos_path[-1] in ['*', '**']:
@@ -122,7 +134,7 @@
         retries = retries - 1
         if retries < 0: raise IOError
         print complaint
-        
+
 @cache_region('super_short_term', 'cached_hg_ui')
 def get_hg_ui_cached():
     try:
@@ -139,13 +151,13 @@
         ret = sa.query(RhodeCodeSettings).all()
     finally:
         meta.Session.remove()
-        
+
     if not ret:
         raise Exception('Could not get application settings !')
     settings = {}
     for each in ret:
-        settings['rhodecode_' + each.app_settings_name] = each.app_settings_value    
-    
+        settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
+
     return settings
 
 def get_hg_ui_settings():
@@ -154,7 +166,7 @@
         ret = sa.query(RhodeCodeUi).all()
     finally:
         meta.Session.remove()
-        
+
     if not ret:
         raise Exception('Could not get application ui settings !')
     settings = {}
@@ -163,15 +175,15 @@
         v = each.ui_value
         if k == '/':
             k = 'root_path'
-        
+
         if k.find('.') != -1:
             k = k.replace('.', '_')
-        
+
         if each.ui_section == 'hooks':
             v = each.ui_active
-        
-        settings[each.ui_section + '_' + k] = v  
-    
+
+        settings[each.ui_section + '_' + k] = v
+
     return settings
 
 #propagated from mercurial documentation
@@ -185,15 +197,15 @@
                 'paths', 'profiling',
                 'server', 'trusted',
                 'ui', 'web', ]
-        
-def make_ui(read_from='file', path=None, checkpaths=True):        
+
+def make_ui(read_from='file', path=None, checkpaths=True):
     """
     A function that will read python rc files or database
     and make an mercurial ui object from read options
     
-    @param path: path to mercurial config file
-    @param checkpaths: check the path
-    @param read_from: read from 'file' or 'db'
+    :param path: path to mercurial config file
+    :param checkpaths: check the path
+    :param read_from: read from 'file' or 'db'
     """
 
     baseui = ui.ui()
@@ -209,52 +221,52 @@
             for k, v in cfg.items(section):
                 baseui.setconfig(section, k, v)
                 log.debug('settings ui from file[%s]%s:%s', section, k, v)
-        if checkpaths:check_repo_dir(cfg.items('paths'))                
-              
-        
+        if checkpaths:check_repo_dir(cfg.items('paths'))
+
+
     elif read_from == 'db':
         hg_ui = get_hg_ui_cached()
         for ui_ in hg_ui:
             if ui_.ui_active:
                 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
                 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
-        
-    
+
+
     return baseui
 
 
 def set_rhodecode_config(config):
     hgsettings = get_hg_settings()
-    
+
     for k, v in hgsettings.items():
         config[k] = v
 
 def invalidate_cache(name, *args):
     """Invalidates given name cache"""
-    
+
     from beaker.cache import region_invalidate
     log.info('INVALIDATING CACHE FOR %s', name)
-    
+
     """propagate our arguments to make sure invalidation works. First
     argument has to be the name of cached func name give to cache decorator
     without that the invalidation would not work"""
     tmp = [name]
     tmp.extend(args)
     args = tuple(tmp)
-    
+
     if name == 'cached_repo_list':
         from rhodecode.model.hg_model import _get_repos_cached
         region_invalidate(_get_repos_cached, None, *args)
-        
+
     if name == 'full_changelog':
         from rhodecode.model.hg_model import _full_changelog_cached
         region_invalidate(_full_changelog_cached, None, *args)
-        
+
 class EmptyChangeset(BaseChangeset):
     """
     An dummy empty changeset.
     """
-    
+
     revision = -1
     message = ''
     author = ''
@@ -266,35 +278,35 @@
         representation.
         """
         return '0' * 40
-    
+
     @LazyProperty
     def short_id(self):
         return self.raw_id[:12]
 
     def get_file_changeset(self, path):
         return self
-    
+
     def get_file_content(self, path):
         return u''
-    
+
     def get_file_size(self, path):
         return 0
-    
+
 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
     """
     maps all found repositories into db
     """
     from rhodecode.model.repo_model import RepoModel
-    
+
     sa = meta.Session
     user = sa.query(User).filter(User.admin == True).first()
-    
+
     rm = RepoModel()
-    
+
     for name, repo in initial_repo_list.items():
         if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
             log.info('repository %s not found creating default', name)
-                
+
             form_data = {
                          'repo_name':name,
                          'description':repo.description if repo.description != 'unknown' else \
@@ -311,7 +323,7 @@
                 sa.delete(repo)
                 sa.commit()
 
-    
+
     meta.Session.remove()
 
 from UserDict import DictMixin
@@ -421,25 +433,25 @@
 #===============================================================================
 def create_test_index(repo_location, full_index):
     """Makes default test index
-    @param repo_location:
-    @param full_index:
+    :param repo_location:
+    :param full_index:
     """
     from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
     from rhodecode.lib.pidlock import DaemonLock, LockHeld
     from rhodecode.lib.indexers import IDX_LOCATION
     import shutil
-    
+
     if os.path.exists(IDX_LOCATION):
         shutil.rmtree(IDX_LOCATION)
-         
+
     try:
         l = DaemonLock()
         WhooshIndexingDaemon(repo_location=repo_location)\
             .run(full_index=full_index)
         l.release()
     except LockHeld:
-        pass    
-    
+        pass
+
 def create_test_env(repos_test_path, config):
     """Makes a fresh database and 
     install test repository into tmp dir
@@ -448,7 +460,7 @@
     import tarfile
     import shutil
     from os.path import dirname as dn, join as jn, abspath
-    
+
     log = logging.getLogger('TestEnvCreator')
     # create logger
     log.setLevel(logging.DEBUG)
@@ -456,20 +468,20 @@
     # create console handler and set level to debug
     ch = logging.StreamHandler()
     ch.setLevel(logging.DEBUG)
-    
+
     # create formatter
     formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
-    
+
     # add formatter to ch
     ch.setFormatter(formatter)
-    
+
     # add ch to logger
     log.addHandler(ch)
-    
+
     #PART ONE create db
     dbname = config['sqlalchemy.db1.url'].split('/')[-1]
     log.debug('making test db %s', dbname)
-    
+
     dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
                         tests=True)
     dbmanage.create_tables(override=True)
@@ -478,12 +490,12 @@
     dbmanage.admin_prompt()
     dbmanage.create_permissions()
     dbmanage.populate_default_permissions()
-    
+
     #PART TWO make test repo
     log.debug('making test vcs repo')
     if os.path.isdir('/tmp/vcs_test'):
         shutil.rmtree('/tmp/vcs_test')
-        
+
     cur_dir = dn(dn(abspath(__file__)))
     tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
     tar.extractall('/tmp')
--- a/rhodecode/model/meta.py	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/model/meta.py	Mon Oct 18 15:29:56 2010 +0200
@@ -3,9 +3,6 @@
 from sqlalchemy.orm import scoped_session, sessionmaker
 from rhodecode.model import caching_query
 from beaker import cache
-import os
-from os.path import join as jn, dirname as dn, abspath
-import time
 
 # Beaker CacheManager.  A home base for cache configurations.
 cache_manager = cache.CacheManager()
@@ -25,9 +22,5 @@
 #For another db...
 #Base2 = declarative_base()
 
-#===============================================================================
-# CACHE OPTIONS
-#===============================================================================
-#Configured globally in .ini files
 #to use cache use this in query
 #.options(FromCache("sqlalchemy_cache_type", "cachekey"))
--- a/rhodecode/public/css/style.css	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/public/css/style.css	Mon Oct 18 15:29:56 2010 +0200
@@ -139,30 +139,6 @@
 padding:0;
 }
 
-div.color a.blue {
-background:#376ea6;
-}
-
-div.color a.green {
-background:#85924b;
-}
-
-div.color a.brown {
-background:#9b6e42;
-}
-
-div.color a.purple {
-background:#88528b;
-}
-
-div.color a.red {
-background:#bd3220;
-}
-
-div.color a.greyblue {
-background:#566e86;
-}
-
 div.options {
 clear:both;
 overflow:hidden;
@@ -180,6 +156,35 @@
 padding:3px 8px;
 }
 
+.top-left-rounded-corner {
+-webkit-border-top-left-radius: 8px;
+-khtml-border-radius-topleft: 8px; 
+-moz-border-radius-topleft: 8px;
+border-top-left-radius: 8px;
+}
+
+.top-right-rounded-corner {
+-webkit-border-top-right-radius: 8px;
+-khtml-border-radius-topright: 8px;    
+-moz-border-radius-topright: 8px;
+border-top-right-radius: 8px;
+}
+
+.bottom-left-rounded-corner {
+-webkit-border-bottom-left-radius: 8px;
+-khtml-border-radius-bottomleft: 8px;  
+-moz-border-radius-bottomleft: 8px;
+border-bottom-left-radius: 8px;
+}
+
+.bottom-right-rounded-corner {
+-webkit-border-bottom-right-radius: 8px;
+-khtml-border-radius-bottomright: 8px; 
+-moz-border-radius-bottomright: 8px;
+border-bottom-right-radius: 8px;
+}
+
+
 #header {
 margin:0;
 padding:0 30px;
@@ -357,7 +362,6 @@
 max-height:275px;
 overflow-x:hidden;
 overflow-y:auto;
-white-space:nowrap;
 }
 
 #header #header-inner #quick li ul li {
@@ -376,22 +380,11 @@
 padding:7px 9px;
 }
 
-#header #header-inner #quick li ul li a.childs {
-width:167px;
-background:#FFF url("../../resources/images/plus.png") no-repeat 8px 9px;
-margin:0;
-padding:7px 9px 7px 24px;
-}
-
 #header #header-inner #quick li ul li a:hover {
 color:#000;
 background:#FFF;
 }
 
-#header #header-inner #quick li ul li a.childs:hover {
-background:#FFF url("../../resources/images/minus.png") no-repeat 8px 9px;
-}
-
 #header #header-inner #quick ul ul {
 top:auto;
 }
@@ -488,184 +481,12 @@
 padding:12px 9px 7px 24px;
 }
 
-#header #header-inner div.corner {
-height:6px;
-width:6px;
-position:absolute;
-background:url("../../images/header_inner_corners.png") no-repeat;
-}
-
 #content #left {
 left:0;
 width:280px;
 position:absolute;
 }
 
-#content #left #menu {
-clear:both;
-overflow:hidden;
-margin:5px 10px 0 60px;
-padding:0;
-}
-
-#content #left #menu h6 {
-clear:both;
-overflow:hidden;
-background:#dfdfdf url("../images/menu.png") repeat-x;
-color:#6e6e6e;
-margin:5px 0 0;
-padding:0;
-}
-
-#content #left #menu h6 a {
-height:1%;
-display:block;
-clear:both;
-overflow:hidden;
-background:url("../images/menu_l.png") no-repeat top left;
-color:#6e6e6e;
-text-decoration:none;
-margin:0;
-padding:0;
-}
-
-#content #left #menu h6 span {
-height:1%;
-display:block;
-background:url("../images/menu_r.png") no-repeat top right;
-margin:0;
-padding:9px 10px 10px;
-}
-
-#content #left #menu h6.selected {
-color:#FFF;
-background:#00376e url("../../images/menu_selected.png") repeat-x;
-}
-
-#content #left #menu h6.selected a {
-color:#fff;
-background:url("../../images/menu_l_selected.png") no-repeat top left;
-}
-
-#content #left #menu h6.selected span {
-background:url("../../images/menu_r_selected.png") no-repeat top right;
-}
-
-#content #left #menu ul {
-background:#376ea6;
-margin:0;
-padding:0;
-}
-
-#content #left #menu li {
-clear:both;
-overflow:hidden;
-list-style:none;
-color:#fff;
-border-top:1px solid #4377ab;
-border-bottom:1px solid #326395;
-margin:0;
-padding:0;
-}
-
-#content #left #menu li a {
-height:1%;
-display:block;
-float:left;
-color:#fff;
-text-decoration:none;
-background:url("../../images/menu_arrow.png") no-repeat 0 9px;
-margin:0 0 0 6px;
-padding:8px 0 8px 18px;
-}
-
-#content #left #menu li a:hover {
-color:#b9dcff;
-}
-
-#content #left #menu li.collapsible {
-background:url("../../images/menu_border.png") no-repeat top left;
-}
-
-#content #left #menu li.collapsible a {
-height:1%;
-display:block;
-background:transparent;
-float:left;
-font-weight:700;
-margin:0 0 0 6px;
-padding:8px 0;
-}
-
-#content #left #menu li.collapsible a.plus {
-height:10px;
-width:10px;
-display:block;
-float:left;
-background:url("../images/menu_plus.png") no-repeat 5px 10px;
-border:none;
-margin:0;
-padding:8px 0 9px 24px;
-}
-
-#content #left #menu li.collapsible a.minus {
-height:10px;
-width:10px;
-display:block;
-float:left;
-background:url("../images/menu_minus.png") no-repeat 5px 10px;
-border:none;
-border-bottom:1px solid #326395;
-margin:0;
-padding:8px 0 9px 24px;
-}
-
-#content #left #menu li ul {
-border-left:18px solid #326395;
-margin:0;
-padding:0;
-}
-
-#content #left #menu li ul li {
-clear:both;
-overflow:hidden;
-list-style:none;
-color:#fff;
-background:url("../../images/menu_arrow.png") no-repeat 10px 9px;
-border-top:1px solid #4377ab;
-border-bottom:1px solid #326395;
-margin:0;
-padding:0;
-}
-
-#content #left #menu li.collapsible ul li a {
-font-weight:400;
-}
-
-#content #left #menu li.last {
-border-bottom:none;
-}
-
-#content #left #date-picker {
-clear:both;
-overflow:hidden;
-margin:10px 10px 0 60px;
-padding:0;
-}
-
-#content #left #date-picker .ui-datepicker {
-width:auto;
-clear:both;
-overflow:hidden;
-background:#FFF;
-border:1px solid #d1d1d1;
-padding:0;
-}
-
-#content #left #date-picker .ui-datepicker .ui-datepicker-header {
-padding:5px 0;
-}
-
 #content #right {
 margin:0 60px 10px 290px;
 }
@@ -1036,10 +857,6 @@
 padding:4px 8px;
 }
 
-#content div.box div.form div.fields div.field div.input img.ui-datepicker-trigger {
-margin:0 0 0 6px;
-}
-
 #content div.box div.form div.fields div.field div.input a.ui-input-file {
 width:28px;
 height:28px;
@@ -1054,10 +871,6 @@
 padding:0;
 }
 
-#content div.box div.form div.fields div.field div.input a:hover.ui-input-file {
-background:#e5e3e3 url("../images/button_browse_selected.png") no-repeat;
-}
-
 #content div.box div.form div.fields div.field div.textarea {
 border-top:1px solid #b3b3b3;
 border-left:1px solid #b3b3b3;
@@ -1091,24 +904,6 @@
 height:100px;
 }
 
-#content div.box div.form div.fields div.field div.textarea textarea.error {
-background-color:#FBE3E4;
-background-image:url("../../../resources/images/icons/exclamation.png");
-background-repeat:no-repeat;
-background-position:3px 3px;
-border:1px solid #FBC2C4;
-padding:3px 10px 10px 23px;
-}
-
-#content div.box div.form div.fields div.field div.textarea textarea.success {
-background-color:#E6EFC2;
-background-image:url("../../../resources/images/icons/accept.png");
-background-repeat:no-repeat;
-background-position:3px 3px;
-border:1px solid #C6D880;
-padding:3px 10px 10px 23px;
-}
-
 #content div.box div.form div.fields div.field div.textarea table {
 width:100%;
 border:none;
@@ -1441,11 +1236,14 @@
 padding:0 30px;
 }
 
-#footer p {
-background:none repeat scroll 0 0 #2F2F2F;
+#footer div#footer-inner {
+background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
+}
+
+#footer div#footer-inner p {
+padding:15px 25px 15px 0;
 color:#FFF;
 font-weight:700;
-padding:15px 25px 15px 0;
 }
 
 #login div.title {
@@ -1458,13 +1256,6 @@
 padding:0;
 }
 
-#login div.title div.corner {
-height:6px;
-width:6px;
-position:absolute;
-background:url("../../images/login_corners.png") no-repeat;
-}
-
 #login div.inner {
 width:380px;
 background:#FFF url("../images/login.png") no-repeat top left;
@@ -1522,13 +1313,6 @@
 padding:0;
 }
 
-#register div.title div.corner {
-height:6px;
-width:6px;
-position:absolute;
-background:url("../images/login_corners.png") no-repeat;
-}
-
 #register div.inner {
 width:380px;
 background:#FFF;
@@ -1589,11 +1373,7 @@
 }
 
 .trending_language {
--moz-border-radius-bottomright:4px;
--moz-border-radius-topright:4px;
-border-bottom-right-radius:4px 4px;
-border-top-right-radius:4px 4px;
-background-color:#369;
+background-color:#003367;
 color:#FFF;
 display:block;
 min-width:20px;
@@ -1649,7 +1429,7 @@
 }
 
 .cs_files .cs_added {
-background:url("/images/icons/page_white_add.png") no-repeat scroll 3px;
+background:url("../images/icons/page_white_add.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 margin-top:7px;
@@ -1657,7 +1437,7 @@
 }
 
 .cs_files .cs_changed {
-background:url("/images/icons/page_white_edit.png") no-repeat scroll 3px;
+background:url("../images/icons/page_white_edit.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 margin-top:7px;
@@ -1665,7 +1445,7 @@
 }
 
 .cs_files .cs_removed {
-background:url("/images/icons/page_white_delete.png") no-repeat scroll 3px;
+background:url("../images/icons/page_white_delete.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 margin-top:7px;
@@ -1816,14 +1596,14 @@
 }
 
 table.code-browser .browser-file {
-background:url("/images/icons/document_16.png") no-repeat scroll 3px;
+background:url("../images/icons/document_16.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 text-align:left;
 }
 
 table.code-browser .browser-dir {
-background:url("/images/icons/folder_16.png") no-repeat scroll 3px;
+background:url("../images/icons/folder_16.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 text-align:left;
@@ -1974,7 +1754,7 @@
 }
 
 .add_icon {
-background:url("/images/icons/add.png") no-repeat scroll 3px;
+background:url("../images/icons/add.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 padding-top:1px;
@@ -1982,7 +1762,7 @@
 }
 
 .edit_icon {
-background:url("/images/icons/folder_edit.png") no-repeat scroll 3px;
+background:url("../images/icons/folder_edit.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 padding-top:1px;
@@ -1990,7 +1770,7 @@
 }
 
 .delete_icon {
-background:url("/images/icons/delete.png") no-repeat scroll 3px;
+background:url("../images/icons/delete.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 padding-top:1px;
@@ -1998,7 +1778,7 @@
 }
 
 .rss_icon {
-background:url("/images/icons/rss_16.png") no-repeat scroll 3px;
+background:url("../images/icons/rss_16.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 padding-top:1px;
@@ -2006,7 +1786,7 @@
 }
 
 .atom_icon {
-background:url("/images/icons/atom.png") no-repeat scroll 3px;
+background:url("../images/icons/atom.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 padding-top:1px;
@@ -2014,7 +1794,7 @@
 }
 
 .archive_icon {
-background:url("/images/icons/compress.png") no-repeat scroll 3px;
+background:url("../images/icons/compress.png") no-repeat scroll 3px;
 height:16px;
 padding-left:20px;
 text-align:left;
@@ -2153,7 +1933,7 @@
 min-height:100%;
 clear:both;
 overflow:hidden;
-padding:10px 30px;
+padding:14px 30px;
 }
 
 #content div.box div.title div.search {
@@ -2227,28 +2007,6 @@
 display:block;
 }
 
-#header #header-inner div.tl,#login div.title div.tl,#register div.title div.tl {
-top:0;
-left:0;
-background-position:0 0;
-}
-
-#header #header-inner div.tr,#login div.title div.tr,#register div.title div.tr {
-top:0;
-right:0;
-background-position:-6px 0;
-}
-
-#content #left #date-picker .ui-datepicker .ui-datepicker-prev,#content #left #date-picker .ui-datepicker .ui-datepicker-prev-hover {
-top:5px;
-left:4px;
-}
-
-#content #left #date-picker .ui-datepicker .ui-datepicker-next,#content #left #date-picker .ui-datepicker .ui-datepicker-next-hover {
-top:5px;
-right:4px;
-}
-
 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a {
 background:url("../../images/title_tab_selected.png") no-repeat bottom center;
 color:#bfe3ff;
@@ -2318,10 +2076,6 @@
 border:1px solid #666;
 }
 
-#content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus span.ui-icon,#content div.box div.action a.ui-selectmenu-focus span.ui-icon {
-background-image:url(../images/ui/ui-icons_222222_256x240.png);
-}
-
 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio {
 clear:both;
 overflow:hidden;
Binary file rhodecode/public/images/header_inner_corners.png has changed
Binary file rhodecode/public/images/login_corners.png has changed
Binary file rhodecode/public/images/menu.png has changed
Binary file rhodecode/public/images/menu_border.png has changed
Binary file rhodecode/public/images/menu_l.png has changed
Binary file rhodecode/public/images/menu_l_selected.png has changed
Binary file rhodecode/public/images/menu_r.png has changed
Binary file rhodecode/public/images/menu_r_selected.png has changed
Binary file rhodecode/public/images/menu_selected.png has changed
--- a/rhodecode/templates/base/base.html	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/templates/base/base.html	Mon Oct 18 15:29:56 2010 +0200
@@ -28,17 +28,15 @@
             <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
         </ul>
         <!-- end user -->
-        <div id="header-inner">
+        <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
             <!-- logo -->
             <div id="logo">
                 <h1><a href="${h.url('hg_home')}">${c.rhodecode_name}</a></h1>
             </div>
             <!-- end logo -->
-            <!-- quick menu -->
+            <!-- menu -->
             ${self.page_nav()}
-            <!-- end quick -->
-            <div class="corner tl"></div>
-            <div class="corner tr"></div>
+            <!-- quick -->
         </div>
     </div>     
     <!-- end header -->
@@ -63,8 +61,10 @@
 
 	<!-- footer -->
 	<div id="footer">
+	   <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
 	    <p>RhodeCode ${c.rhodecode_version} &copy; 2010 by Marcin Kuzminski</p>
-        <script type="text/javascript">${h.tooltip.activate()}</script>	    
+	   </div>
+        <script type="text/javascript">${h.tooltip.activate()}</script>
 	</div>
 	<!-- end footer -->
 </body>
--- a/rhodecode/templates/login.html	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/templates/login.html	Mon Oct 18 15:29:56 2010 +0200
@@ -8,20 +8,14 @@
         <meta name="robots" content="index, nofollow"/>
             
         <!-- stylesheets -->
-        <link rel="stylesheet" type="text/css" href="/css/reset.css" />
         <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
-        <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
-
-        <!-- scripts -->
 
     </head>
     <body>
 <div id="login">
             <!-- login -->
-            <div class="title">
+            <div class="title top-left-rounded-corner top-right-rounded-corner">
                 <h5>${_('Sign In to rhodecode')}</h5>
-                <div class="corner tl"></div>
-                <div class="corner tr"></div>
             </div>
             <div class="inner">            
                 ${h.form(h.url.current(came_from=c.came_from))}
--- a/rhodecode/templates/password_reset.html	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/templates/password_reset.html	Mon Oct 18 15:29:56 2010 +0200
@@ -8,20 +8,14 @@
         <meta name="robots" content="index, nofollow"/>
             
         <!-- stylesheets -->
-        <link rel="stylesheet" type="text/css" href="/css/reset.css" />
         <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
-        <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
-
-        <!-- scripts -->
 
     </head>
     <body>
 		<div id="register">
 			
-			<div class="title">
+			<div class="title top-left-rounded-corner top-right-rounded-corner">
 				<h5>${_('Reset You password to rhodecode')}</h5>
-                <div class="corner tl"></div>
-                <div class="corner tr"></div>				
 			</div>
 			<div class="inner">
 			    ${h.form(url('password_reset'))}
--- a/rhodecode/templates/register.html	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/templates/register.html	Mon Oct 18 15:29:56 2010 +0200
@@ -8,20 +8,14 @@
         <meta name="robots" content="index, nofollow"/>
             
         <!-- stylesheets -->
-        <link rel="stylesheet" type="text/css" href="/css/reset.css" />
         <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
-        <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
-
-        <!-- scripts -->
 
     </head>
     <body>
 		<div id="register">
 			
-			<div class="title">
+			<div class="title top-left-rounded-corner top-right-rounded-corner">
 				<h5>${_('Sign Up to rhodecode')}</h5>
-                <div class="corner tl"></div>
-                <div class="corner tr"></div>				
 			</div>
 			<div class="inner">
 			    ${h.form(url('register'))}
--- a/rhodecode/templates/summary/summary.html	Tue Oct 12 16:49:28 2010 +0200
+++ b/rhodecode/templates/summary/summary.html	Mon Oct 18 15:29:56 2010 +0200
@@ -139,7 +139,7 @@
 			  		    var trending_language = document.createElement('div');
 			  		    trending_language.title = k;
 			  		    trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
-			  		    trending_language.setAttribute("class", 'trending_language');
+			  		    trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
 			  		    trending_language.style.width=percentage+"%";
 						td2.appendChild(trending_language);
 						
--- a/setup.cfg	Tue Oct 12 16:49:28 2010 +0200
+++ b/setup.cfg	Mon Oct 18 15:29:56 2010 +0200
@@ -1,5 +1,5 @@
 [egg_info]
-tag_build = rc3
+tag_build = rc4
 tag_svn_revision = true
 
 [easy_install]
@@ -32,3 +32,11 @@
 input_file = rhodecode/i18n/rhodecode.pot
 output_dir = rhodecode/i18n
 previous = true
+
+[build_sphinx]
+source-dir = docs/
+build-dir  = docs/_build
+all_files  = 1
+
+[upload_sphinx]
+upload-dir = docs/_build/html
\ No newline at end of file