Commits

Marcin Kuzminski committed dc2584b Merge

merged beta into default branch

Comments (0)

Files changed (86)

 *.egg
 
 syntax: regexp
+^rcextensions
 ^build
 ^docs/build/
 ^docs/_build/
 however RhodeCode can be run as standalone hosted application on your own server.
 It is open source and donation ware and focuses more on providing a customized, 
 self administered interface for Mercurial_ and GIT_  repositories. 
-RhodeCode is powered by a vcs_ library that Lukasz Balcerzak and I created to 
-handle multiple different version control systems.
+RhodeCode works on *nix systems and Windows it is powered by a vcs_ library 
+that Lukasz Balcerzak and Marcin Kuzminski created to handle multiple 
+different version control systems.
 
-RhodeCode uses `Semantic Versioning <http://semver.org/>`_
+RhodeCode uses `PEP386 versioning http://www.python.org/dev/peps/pep-0386/`_
 
 Installation
 ------------
 - Intelligent cache with invalidation after push or project change, provides 
   high performance and always up to date data.
 - Rss / atom feeds, gravatar support, download sources as zip/tar/gz
-- Async tasks for speed and performance using celery_ (works without them too)  
+- Optional async tasks for speed and performance using celery_  
 - Backup scripts can do backup of whole app and send it over scp to desired 
   location 
 - Based on pylons / sqlalchemy / sqlite / whoosh / vcs
 ## all running rhodecode instances. Leave empty if you don't use it
 instance_id = 
 
+## alternative return HTTP header for failed authentication. Default HTTP
+## response is 401 HTTPUnauthorized. Currently HG clients have troubles with 
+## handling that. Set this variable to 403 to return HTTPForbidden
+auth_ret_code =
+
 ####################################
 ###        CELERY CONFIG        ####
 ####################################
 
 beaker.session.type = file
 beaker.session.key = rhodecode
+# secure cookie requires AES python libraries
 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
 #beaker.session.validate_key = 9712sds2212c--zxc123
 beaker.session.timeout = 36000
 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.convert_unicode = true
+sqlalchemy.db1.convert_unicode = true
 
 ################################
 ### LOGGING CONFIGURATION   ####
 ################################
 [loggers]
-keys = root, routes, rhodecode, sqlalchemy, beaker, templates
+keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
 
 [handlers]
 keys = console, console_sql
 qualname = sqlalchemy.engine
 propagate = 0
 
+[logger_whoosh_indexer]
+level = DEBUG
+handlers = 
+qualname = whoosh_indexer
+propagate = 1
+
 ##############
 ## HANDLERS ##
 ##############
 All clients are required to send JSON-RPC spec JSON data::
 
     {   
-        "id:<id>,
+        "id:"<id>",
         "api_key":"<api_key>",
         "method":"<method_name>",
         "args":{"<arg_key>":"<arg_val>"}
 RhodeCode API will return always a JSON-RPC response::
 
     {   
-        "id":<id>,
-        "result": "<result>",
-        "error": null
+        "id":<id>, # matching id sent by request
+        "result": "<result>"|null, # JSON formatted result, null if any errors
+        "error": "null"|<error_message> # JSON formatted error (if any)
     }
 
 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "pull"
     args :    {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_user"
     args :    { 
                 "email" :    "<email>",
                 "active" :   "<bool>",
                 "admin" :    "<bool>",
-                "ldap_dn" :  "<ldap_dn>"
+                "ldap_dn" :  "<ldap_dn>",
+                "last_login": "<last_login>",
+                "permissions": {
+                    "global": ["hg.create.repository",
+                               "repository.read",
+                               "hg.register.manual_activate"],
+                    "repositories": {"repo1": "repository.none"},
+                    "repositories_groups": {"Group1": "group.read"}
+                 },
             }
 
     error:  null
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_users"
     args :    { }
                 "email" :    "<email>",
                 "active" :   "<bool>",
                 "admin" :    "<bool>",
-                "ldap_dn" :  "<ldap_dn>"
+                "ldap_dn" :  "<ldap_dn>",
+                "last_login": "<last_login>",
               },
             ]
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "create_user"
     args :    {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "update_user"
     args :    {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_users_group"
     args :    {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_users_groups"
     args :    { }
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "create_users_group"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "add_user_users_group"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "remove_user_from_users_group"
     args:     {
 get_repo
 --------
 
-Gets an existing repository by it's name or repository_id. This command can 
+Gets an existing repository by it's name or repository_id. Members will return
+either users_group or user associated to that repository. This command can 
 be executed only using api_key belonging to user with admin rights.
 
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_repo"
     args:     {
                 "type" :        "<type>",
                 "description" : "<description>",
                 "members" :     [
-                                  { "id" :         "<userid>",
+                                  { 
+                                    "type": "user",
+                                    "id" :         "<userid>",
                                     "username" :   "<username>",
                                     "firstname":   "<firstname>",
                                     "lastname" :   "<lastname>",
                                     "permission" : "repository.(read|write|admin)"
                                   },
-                                  {
+                                  { 
+                                    "type": "users_group",
                                     "id" :       "<usersgroupid>",
                                     "name" :     "<usersgroupname>",
                                     "active":    "<bool>",
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_repos"
     args:     { }
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "get_repo_nodes"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "create_repo"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "delete_repo"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "grant_user_permission"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method  : "revoke_user_permission"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method :  "grant_users_group_permission"
     args:     {
 
 INPUT::
 
+    id : <id_for_response>
     api_key : "<api_key>"
     method  : "revoke_users_group_permission"
     args:     {

docs/changelog.rst

 =========
 
 
+1.3.4 (**2012-03-28**)
+----------------------
 
+news
+++++
+
+- Whoosh logging is now controlled by the .ini files logging setup
+- added clone-url into edit form on /settings page
+- added help text into repo add/edit forms
+- created rcextensions module with additional mappings (ref #322) and
+  post push/pull/create repo hooks callbacks
+- implemented #377 Users view for his own permissions on account page
+- #399 added inheritance of permissions for users group on repos groups
+- #401 repository group is automatically pre-selected when adding repos 
+  inside a repository group
+- added alternative HTTP 403 response when client failed to authenticate. Helps 
+  solving issues with Mercurial and LDAP
+- #402 removed group prefix from repository name when listing repositories 
+  inside a group
+- added gravatars into permission view and permissions autocomplete
+- #347 when running multiple RhodeCode instances, properly invalidates cache 
+  for all registered servers
+
+fixes
++++++
+
+- fixed #390 cache invalidation problems on repos inside group
+- fixed #385 clone by ID url was loosing proxy prefix in URL
+- fixed some unicode problems with waitress
+- fixed issue with escaping < and > in changeset commits
+- fixed error occurring during recursive group creation in API 
+  create_repo function
+- fixed #393 py2.5 fixes for routes url generator
+- fixed #397 Private repository groups shows up before login
+- fixed #396 fixed problems with revoking users in nested groups
+- fixed mysql unicode issues + specified InnoDB as default engine with 
+  utf8 charset
+- #406 trim long branch/tag names in changelog to not break UI
+  
 1.3.3 (**2012-03-02**)
 ----------------------
 
    usage/git_support
    usage/statistics
    usage/backup
-   
+   usage/debugging
+
 **Develop**
 
 .. toctree::
 
 
 Next, you need to create the databases used by RhodeCode. I recommend that you
-use sqlite (default) or postgresql. If you choose a database other than the
+use postgresql or sqlite (default). If you choose a database other than the
 default ensure you properly adjust the db url in your production.ini
-configuration file to use this other database. Create the databases by running
+configuration file to use this other database. RhodeCode currently supports
+postgresql, sqlite and mysql databases. Create the database by running
 the following command::
 
     paster setup-app production.ini
 - In the admin panel you can toggle ldap, anonymous, permissions settings. As
   well as edit more advanced options on users and repositories
 
-Try copying your own mercurial repository into the "root" directory you are
-using, then from within the RhodeCode web application choose Admin >
-repositories. Then choose Add New Repository. Add the repository you copied 
-into the root. Test that you can browse your repository from within RhodeCode 
-and then try cloning your repository from RhodeCode with::
+Optionally users can create `rcextensions` package that extends RhodeCode
+functionality. To do this simply execute::
 
-    hg clone http://127.0.0.1:5000/<repository name>
+    paster make-rcext production.ini
 
-where *repository name* is replaced by the name of your repository.
+This will create `rcextensions` package in the same place that your `ini` file
+lives. With `rcextensions` it's possible to add additional mapping for whoosh, 
+stats and add additional code into the push/pull/create repo hooks. For example
+for sending signals to build-bots such as jenkins.
+Please see the `__init__.py` file inside `rcextensions` package 
+for more details.
+
 
 Using RhodeCode with SSH
 ------------------------
 and will always recheck the settings of the application, if there are no new 
 options that need to be set.
 
+.. note::
+   If you're using Celery, make sure you restart all instances of it after
+   upgrade.
 
 .. _virtualenv: http://pypi.python.org/pypi/virtualenv  
 .. _python: http://www.python.org/

docs/usage/debugging.rst

+.. _debugging:
+
+===================
+Debugging RhodeCode
+===================
+
+If you encountered problems with RhodeCode here are some instructions how to
+possibly debug them.
+
+** First make sure you're using the latest version available.**
+
+enable detailed debug
+---------------------
+
+RhodeCode uses standard python logging modules to log it's output.
+By default only loggers with INFO level are displayed. To enable full output
+change `level = DEBUG` for all logging handlers in currently used .ini file. 
+This change will allow to see much more detailed output in the logfile or
+console. This generally helps a lot to track issues.
+
+
+enable interactive debug mode
+-----------------------------
+
+To enable interactive debug mode simply comment out `set debug = false` in
+.ini file, this will trigger and interactive debugger each time there an
+error in browser, or send a http link if error occured in the backend. This
+is a great tool for fast debugging as you get a handy python console right
+in the web view. ** NEVER ENABLE THIS ON PRODUCTION ** the interactive console
+can be a serious security threat to you system.

docs/usage/general.rst

 on errors the mails will have a detailed traceback of error.
 
 
+Mails are also sent for code comments. If someone comments on a changeset
+mail is sent to all participants, the person who commited the changeset 
+(if present in RhodeCode), and to all people mentioned with @mention system.
+
+
 Trending source files
 ---------------------
 

init.d/rhodecode-daemon4

+#!/bin/bash
+###########################################
+#### THIS IS AN ARCH LINUX RC.D SCRIPT ####
+###########################################
+
+. /etc/rc.conf
+. /etc/rc.d/functions
+
+DAEMON=rhodecode
+APP_HOMEDIR="/srv"
+APP_PATH="$APP_HOMEDIR/$DAEMON"
+CONF_NAME="production.ini"
+LOG_FILE="/var/log/$DAEMON.log"
+PID_FILE="/run/daemons/$DAEMON"
+APPL=/usr/bin/paster
+RUN_AS="*****"
+
+ARGS="serve --daemon \
+--user=$RUN_AS \
+--group=$RUN_AS \
+--pid-file=$PID_FILE \
+--log-file=$LOG_FILE \
+$APP_PATH/$CONF_NAME"
+
+[ -r /etc/conf.d/$DAEMON ] && . /etc/conf.d/$DAEMON
+
+if [[ -r $PID_FILE ]]; then
+    read -r PID < "$PID_FILE"
+    if [[ $PID && ! -d /proc/$PID ]]; then
+        unset PID
+        rm_daemon $DAEMON
+    fi
+fi
+
+case "$1" in
+start)
+    stat_busy "Starting $DAEMON"
+    export HOME=$APP_PATH
+    [ -z "$PID" ] && $APPL $ARGS &>/dev/null
+    if [ $? = 0 ]; then
+        add_daemon $DAEMON
+        stat_done
+    else
+        stat_fail
+        exit 1
+    fi
+    ;;
+stop)
+    stat_busy "Stopping $DAEMON"
+    [ -n "$PID" ] && kill $PID &>/dev/null 
+    if [ $? = 0 ]; then
+        rm_daemon $DAEMON
+        stat_done
+    else
+        stat_fail
+        exit 1
+    fi
+    ;;
+restart)
+    $0 stop
+    sleep 1
+    $0 start
+    ;;
+status)
+    stat_busy "Checking $name status";
+    ck_status $name
+    ;;
+*)
+    echo "usage: $0 {start|stop|restart|status}"
+esac
 ## all running rhodecode instances. Leave empty if you don't use it
 instance_id = 
 
+## alternative return HTTP header for failed authentication. Default HTTP
+## response is 401 HTTPUnauthorized. Currently HG clients have troubles with 
+## handling that. Set this variable to 403 to return HTTPForbidden
+auth_ret_code =
+
 ####################################
 ###        CELERY CONFIG        ####
 ####################################
 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.convert_unicode = true
+sqlalchemy.db1.convert_unicode = true
 
 ################################
 ### LOGGING CONFIGURATION   ####
 ################################
 [loggers]
-keys = root, routes, rhodecode, sqlalchemy, beaker, templates
+keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
 
 [handlers]
 keys = console, console_sql
 qualname = sqlalchemy.engine
 propagate = 0
 
+[logger_whoosh_indexer]
+level = DEBUG
+handlers = 
+qualname = whoosh_indexer
+propagate = 1
+
 ##############
 ## HANDLERS ##
 ##############
 Pylons==1.0.0
 Beaker==1.6.3
-WebHelpers>=1.2
+WebHelpers==1.3
 formencode==1.2.4
-SQLAlchemy==0.7.4
-Mako==0.5.0
+SQLAlchemy==0.7.6
+Mako==0.6.2
 pygments>=1.4
 whoosh>=2.3.0,<2.4
 celery>=2.2.5,<2.3
 babel
 python-dateutil>=1.5.0,<2.0.0
-dulwich>=0.8.0,<0.9.0
+https://github.com/jelmer/dulwich/tarball/master
 webob==1.0.8
 markdown==2.1.1
 docutils==0.8.1
 py-bcrypt
-mercurial>=2.1,<2.2
+mercurial>=2.1,<2.2

rhodecode/__init__.py

     ~~~~~~~~~~~~~~~~~~
 
     RhodeCode, a web based repository management based on pylons
-    versioning implementation: http://semver.org/
+    versioning implementation: http://www.python.org/dev/peps/pep-0386/
 
     :created_on: Apr 9, 2010
     :author: marcink
 import sys
 import platform
 
-VERSION = (1, 3, 3)
-__version__ = '.'.join((str(each) for each in VERSION[:4]))
+VERSION = (1, 3, 4)
+
+try:
+    from rhodecode.lib import get_current_revision
+    _rev = get_current_revision()
+    if _rev:
+        VERSION += ('dev%s' % _rev[0],)
+except ImportError:
+    pass
+
+__version__ = ('.'.join((str(each) for each in VERSION[:3])) +
+               '.'.join(VERSION[3:]))
 __dbversion__ = 5  # defines current db version for migrations
 __platform__ = platform.system()
 __license__ = 'GPLv3'
 requirements = [
     "Pylons==1.0.0",
     "Beaker==1.6.3",
-    "WebHelpers>=1.2",
+    "WebHelpers==1.3",
     "formencode==1.2.4",
-    "SQLAlchemy==0.7.4",
-    "Mako==0.5.0",
+    "SQLAlchemy==0.7.6",
+    "Mako==0.6.2",
     "pygments>=1.4",
     "whoosh>=2.3.0,<2.4",
     "celery>=2.2.5,<2.3",
     "babel",
     "python-dateutil>=1.5.0,<2.0.0",
-    "dulwich>=0.8.0,<0.9.0",
+    "dulwich>=0.8.4,<0.9.0",
     "webob==1.0.8",
     "markdown==2.1.1",
     "docutils==0.8.1",
     requirements.append("mercurial>=2.1,<2.2")
 
 
-try:
-    from rhodecode.lib import get_current_revision
-    _rev = get_current_revision(quiet=True)
-except ImportError:
-    # this is needed when doing some setup.py operations
-    _rev = False
-
-if len(VERSION) > 3 and _rev:
-    __version__ += ' [rev:%s]' % _rev[0]
-
-
 def get_version():
     """Returns shorter version (digit parts only) as string."""
 
 
 # link to config for pylons
 CONFIG = {}
+
+# Linked module for extensions
+EXTENSIONS = {}

rhodecode/config/conf.py

+# -*- coding: utf-8 -*-
+"""
+    package.rhodecode.config.conf
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Various config settings for RhodeCode
+
+    :created_on: Mar 7, 2012
+    :author: marcink
+    :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
+    :license: <name>, see LICENSE_FILE for more details.
+"""
+from rhodecode import EXTENSIONS
+
+from rhodecode.lib.utils2 import __get_lem
+
+
+# language map is also used by whoosh indexer, which for those specified
+# extensions will index it's content
+LANGUAGES_EXTENSIONS_MAP = __get_lem()
+
+#==============================================================================
+# WHOOSH INDEX EXTENSIONS
+#==============================================================================
+# EXTENSIONS WE WANT TO INDEX CONTENT OFF USING WHOOSH
+INDEX_EXTENSIONS = LANGUAGES_EXTENSIONS_MAP.keys()
+
+# list of readme files to search in file tree and display in summary
+# attached weights defines the search  order lower is first
+ALL_READMES = [
+    ('readme', 0), ('README', 0), ('Readme', 0),
+    ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
+    ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
+    ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
+    ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
+]
+
+# extension together with weights to search lower is first
+RST_EXTS = [
+    ('', 0), ('.rst', 1), ('.rest', 1),
+    ('.RST', 2), ('.REST', 2),
+    ('.txt', 3), ('.TXT', 3)
+]
+
+MARKDOWN_EXTS = [
+    ('.md', 1), ('.MD', 1),
+    ('.mkdn', 2), ('.MKDN', 2),
+    ('.mdown', 3), ('.MDOWN', 3),
+    ('.markdown', 4), ('.MARKDOWN', 4)
+]
+
+PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
+
+ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
+
+DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
+
+DATE_FORMAT = "%Y-%m-%d"

rhodecode/config/deployment.ini_tmpl

 ## all running rhodecode instances. Leave empty if you don't use it
 instance_id = 
 
+## alternative return HTTP header for failed authentication. Default HTTP
+## response is 401 HTTPUnauthorized. Currently HG clients have troubles with 
+## handling that. Set this variable to 403 to return HTTPForbidden
+auth_ret_code =
+
 ####################################
 ###        CELERY CONFIG        ####
 ####################################
 
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.convert_unicode = true
+sqlalchemy.db1.convert_unicode = true
 
 ################################
 ### LOGGING CONFIGURATION   ####
 ################################
 [loggers]
-keys = root, routes, rhodecode, sqlalchemy, beaker, templates
+keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
 
 [handlers]
 keys = console, console_sql
 qualname = sqlalchemy.engine
 propagate = 0
 
+[logger_whoosh_indexer]
+level = DEBUG
+handlers = 
+qualname = whoosh_indexer
+propagate = 1
+
 ##############
 ## HANDLERS ##
 ##############

rhodecode/config/environment.py

 
 import os
 import logging
+import rhodecode
 
 from mako.lookup import TemplateLookup
 from pylons.configuration import PylonsConfig
 from pylons.error import handle_mako_error
 
-import rhodecode
+# don't remove this import it does magic for celery
+from rhodecode.lib import celerypylons
+
 import rhodecode.lib.app_globals as app_globals
-import rhodecode.lib.helpers
 
 from rhodecode.config.routing import make_map
-# don't remove this import it does magic for celery
-from rhodecode.lib import celerypylons, str2bool
-from rhodecode.lib import engine_from_config
+
+from rhodecode.lib import helpers
 from rhodecode.lib.auth import set_available_permissions
-from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config
+from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config,\
+    load_rcextensions
+from rhodecode.lib.utils2 import engine_from_config, str2bool
 from rhodecode.model import init_model
 from rhodecode.model.scm import ScmModel
 
 
 
 def load_environment(global_conf, app_conf, initial=False):
-    """Configure the Pylons environment via the ``pylons.config``
+    """
+    Configure the Pylons environment via the ``pylons.config``
     object
     """
     config = PylonsConfig()
 
     # Pylons paths
     root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-    paths = dict(root=root,
-                 controllers=os.path.join(root, 'controllers'),
-                 static_files=os.path.join(root, 'public'),
-                 templates=[os.path.join(root, 'templates')])
+    paths = dict(
+        root=root,
+        controllers=os.path.join(root, 'controllers'),
+        static_files=os.path.join(root, 'public'),
+        templates=[os.path.join(root, 'templates')]
+    )
 
     # Initialize config with the basic options
     config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
 
     config['routes.map'] = make_map(config)
     config['pylons.app_globals'] = app_globals.Globals(config)
-    config['pylons.h'] = rhodecode.lib.helpers
+    config['pylons.h'] = helpers
     rhodecode.CONFIG = config
+
+    load_rcextensions(root_path=config['here'])
+
     # Setup cache object as early as possible
     import pylons
     pylons.cache._push_object(config['pylons.app_globals'].cache)

rhodecode/config/rcextensions/__init__.py

+# Additional mappings that are not present in the pygments lexers
+# used for building stats
+# format is {'ext':'Name'} eg. {'py':'Python'}
+# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
+# build by pygments
+EXTRA_MAPPINGS = {}
+
+#==============================================================================
+# WHOOSH INDEX EXTENSIONS
+#==============================================================================
+# if INDEX_EXTENSIONS is [] it'll use pygments lexers extensions by default.
+# To set your own just add to this list extensions to index with content
+INDEX_EXTENSIONS = []
+
+# additional extensions for indexing besides the default from pygments
+# those get's added to INDEX_EXTENSIONS
+EXTRA_INDEX_EXTENSIONS = []
+
+
+#==============================================================================
+# POST CREATE REPOSITORY HOOK
+#==============================================================================
+# this function will be executed after each repository is created
+def _crhook(*args, **kwargs):
+    """
+    Post create repository HOOK
+    kwargs available:
+     :param repo_name:
+     :param repo_type:
+     :param description:
+     :param private:
+     :param created_on:
+     :param enable_downloads:
+     :param repo_id:
+     :param user_id:
+     :param enable_statistics:
+     :param clone_uri:
+     :param fork_id:
+     :param group_id:
+     :param created_by:
+    """
+    return 0
+CREATE_REPO_HOOK = _crhook
+
+
+#==============================================================================
+# POST PUSH HOOK
+#==============================================================================
+
+# this function will be executed after each push it's runned after the build-in
+# hook that rhodecode uses for logging pushes
+def _pushhook(*args, **kwargs):
+    """
+    Post push hook
+    kwargs available:
+
+      :param username: name of user who pushed
+      :param ip: ip of who pushed
+      :param action: pull
+      :param repository: repository name
+      :param pushed_revs: generator of pushed revisions
+    """
+    return 0
+PUSH_HOOK = _pushhook
+
+
+#==============================================================================
+# POST PULL HOOK
+#==============================================================================
+
+# this function will be executed after each push it's runned after the build-in
+# hook that rhodecode uses for logging pushes
+def _pullhook(*args, **kwargs):
+    """
+    Post pull hook
+    kwargs available::
+
+      :param username: name of user who pulled
+      :param ip: ip of who pushed
+      :param action: pull
+      :param repository: repository name
+    """
+    return 0
+PULL_HOOK = _pullhook

rhodecode/config/rcextensions/make_rcextensions.py

+# -*- coding: utf-8 -*-
+"""
+    rhodecode.config.rcextensions.make_rcextensions
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Whoosh indexing module for RhodeCode
+
+    :created_on: Mar 6, 2012
+    :author: marcink
+    :copyright: (C) 2010-2012 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 pkg_resources
+import traceback
+import logging
+from os.path import dirname as dn, join as jn
+
+#to get the rhodecode import
+sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
+
+from rhodecode.lib.utils import BasePasterCommand, Command, ask_ok
+
+log = logging.getLogger(__name__)
+
+
+class MakeRcExt(BasePasterCommand):
+
+    max_args = 1
+    min_args = 1
+
+    usage = "CONFIG_FILE"
+    summary = "Creates additional extensions for rhodecode"
+    group_name = "RhodeCode"
+    takes_config_file = -1
+    parser = Command.standard_parser(verbose=True)
+
+    def command(self):
+        logging.config.fileConfig(self.path_to_ini_file)
+        from pylons import config
+
+        def _make_file(ext_file):
+            bdir = os.path.split(ext_file)[0]
+            if not os.path.isdir(bdir):
+                os.makedirs(bdir)
+            with open(ext_file, 'wb') as f:
+                f.write(tmpl)
+                log.info('Writen new extensions file to %s' % ext_file)
+
+        here = config['here']
+        tmpl = pkg_resources.resource_string(
+            'rhodecode', jn('config', 'rcextensions', '__init__.py')
+        )
+        ext_file = jn(here, 'rcextensions', '__init__.py')
+        if os.path.exists(ext_file):
+            msg = ('Extension file already exists, do you want '
+                   'to overwrite it ? [y/n]')
+            if ask_ok(msg):
+                _make_file(ext_file)
+            else:
+                log.info('nothing done...')
+        else:
+            _make_file(ext_file)
+
+    def update_parser(self):
+        pass

rhodecode/controllers/admin/repos.py

 
         :param repo_name:
         """
-
         try:
             RepoModel().revoke_user_permission(repo=repo_name,
                                                user=request.POST['user_id'])

rhodecode/controllers/admin/users.py

         user_model = UserModel()
         try:
             user_model.delete(id)
+            Session.commit()
             h.flash(_('successfully deleted user'), category='success')
-            Session.commit()
         except (UserOwnsReposException, DefaultUserException), e:
-            h.flash(str(e), category='warning')
+            h.flash(e, category='warning')
         except Exception:
+            log.error(traceback.format_exc())
             h.flash(_('An error occurred during deletion of user'),
                     category='error')
         return redirect(url('users'))

rhodecode/controllers/admin/users_groups.py

 from pylons.controllers.util import abort, redirect
 from pylons.i18n.translation import _
 
+from rhodecode.lib import helpers as h
 from rhodecode.lib.exceptions import UsersGroupsAssignedException
-from rhodecode.lib import helpers as h, safe_unicode
+from rhodecode.lib.utils2 import safe_unicode
 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 from rhodecode.lib.base import BaseController, render
 

rhodecode/controllers/api/__init__.py

         try:
             return json.dumps(response)
         except TypeError, e:
-            log.debug('Error encoding response: %s' % e)
+            log.error('API FAILED. Error encoding response: %s' % e)
             return json.dumps(
                 dict(
-                    self._req_id,
+                    id=self._req_id,
                     result=None,
                     error="Error encoding response"
                 )

rhodecode/controllers/api/api.py

 
 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
 from rhodecode.lib.auth import HasPermissionAllDecorator, \
-    HasPermissionAnyDecorator, PasswordGenerator
+    HasPermissionAnyDecorator, PasswordGenerator, AuthUser
 
 from rhodecode.model.meta import Session
 from rhodecode.model.scm import ScmModel
-from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
+from rhodecode.model.db import User, UsersGroup, Repository
 from rhodecode.model.repo import RepoModel
 from rhodecode.model.user import UserModel
 from rhodecode.model.users_group import UsersGroupModel
-from rhodecode.model.repos_group import ReposGroupModel
-
+from rhodecode.lib.utils import map_groups
 
 log = logging.getLogger(__name__)
 
             email=user.email,
             active=user.active,
             admin=user.admin,
-            ldap_dn=user.ldap_dn
+            ldap_dn=user.ldap_dn,
+            last_login=user.last_login,
+            permissions=AuthUser(user_id=user.user_id).permissions
         )
 
     @HasPermissionAllDecorator('hg.admin')
                     email=user.email,
                     active=user.active,
                     admin=user.admin,
-                    ldap_dn=user.ldap_dn
+                    ldap_dn=user.ldap_dn,
+                    last_login=user.last_login,
                 )
             )
         return result
     @HasPermissionAllDecorator('hg.admin')
     def add_user_to_users_group(self, apiuser, group_name, username):
         """"
-        Add a user to a group
+        Add a user to a users group
 
         :param apiuser:
         :param group_name:
             user = user.user
             members.append(
                 dict(
-                    type_="user",
+                    type="user",
                     id=user.user_id,
                     username=user.username,
                     firstname=user.name,
             users_group = users_group.users_group
             members.append(
                 dict(
-                    type_="users_group",
+                    type="users_group",
                     id=users_group.users_group_id,
                     name=users_group.users_group_name,
                     active=users_group.users_group_active,
             if Repository.get_by_repo_name(repo_name):
                 raise JSONRPCError("repo %s already exist" % repo_name)
 
-            groups = repo_name.split('/')
+            groups = repo_name.split(Repository.url_sep())
             real_name = groups[-1]
-            groups = groups[:-1]
-            parent_id = None
-            for g in groups:
-                group = RepoGroup.get_by_group_name(g)
-                if not group:
-                    group = ReposGroupModel().create(g, '', parent_id)
-                parent_id = group.group_id
+            # create structure of groups
+            group = map_groups(repo_name)
 
             repo = RepoModel().create(
                 dict(
                     description=description,
                     private=private,
                     repo_type=repo_type,
-                    repo_group=parent_id,
+                    repo_group=group.group_id if group else None,
                     clone_uri=clone_uri
                 ),
                 owner

rhodecode/controllers/branches.py

 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib.base import BaseRepoController, render
 from rhodecode.lib.compat import OrderedDict
-from rhodecode.lib import safe_unicode
+from rhodecode.lib.utils2 import safe_unicode
 log = logging.getLogger(__name__)
 
 

rhodecode/controllers/changeset.py

 log = logging.getLogger(__name__)
 
 
-def anchor_url(revision, path):
+def _update_with_GET(params, GET):
+    for k in ['diff1', 'diff2', 'diff']:
+        params[k] += GET.getall(k)
+
+
+def anchor_url(revision, path, GET):
     fid = h.FID(revision, path)
-    return h.url.current(anchor=fid, **dict(request.GET))
+    return h.url.current(anchor=fid, **dict(GET))
 
 
 def get_ignore_ws(fid, GET):
-    ig_ws_global = request.GET.get('ignorews')
+    ig_ws_global = GET.get('ignorews')
     ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
     if ig_ws:
         try:
     return ig_ws_global
 
 
-def _ignorews_url(fileid=None):
-
+def _ignorews_url(GET, fileid=None):
+    fileid = str(fileid) if fileid else None
     params = defaultdict(list)
+    _update_with_GET(params, GET)
     lbl = _('show white space')
-    ig_ws = get_ignore_ws(fileid, request.GET)
-    ln_ctx = get_line_ctx(fileid, request.GET)
+    ig_ws = get_ignore_ws(fileid, GET)
+    ln_ctx = get_line_ctx(fileid, GET)
     # global option
     if fileid is None:
         if ig_ws is None:
 
 
 def get_line_ctx(fid, GET):
-    ln_ctx_global = request.GET.get('context')
+    ln_ctx_global = GET.get('context')
     ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
 
     if ln_ctx:
         return
 
 
-def _context_url(fileid=None):
+def _context_url(GET, fileid=None):
     """
     Generates url for context lines
 
     :param fileid:
     """
-    ig_ws = get_ignore_ws(fileid, request.GET)
-    ln_ctx = (get_line_ctx(fileid, request.GET) or 3) * 2
+
+    fileid = str(fileid) if fileid else None
+    ig_ws = get_ignore_ws(fileid, GET)
+    ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
 
     params = defaultdict(list)
+    _update_with_GET(params, GET)
 
     # global option
     if fileid is None:
         c.anchor_url = anchor_url
         c.ignorews_url = _ignorews_url
         c.context_url = _context_url
-
+        limit_off = request.GET.get('fulldiff')
         #get ranges of revisions if preset
         rev_range = revision.split('...')[:2]
         enable_comments = True
                 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
                 lim = self.cut_off_limit
                 if cumulative_diff > self.cut_off_limit:
-                    lim = -1
+                    lim = -1 if limit_off is None else None
                 size, cs1, cs2, diff, st = wrapped_diff(
                     filenode_old=None,
                     filenode_new=node,
                 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
                 lim = self.cut_off_limit
                 if cumulative_diff > self.cut_off_limit:
-                    lim = -1
+                    lim = -1 if limit_off is None else None
                 size, cs1, cs2, diff, st = wrapped_diff(
                     filenode_old=filenode_old,
                     filenode_new=node,

rhodecode/controllers/feed.py

 from pylons import url, response, tmpl_context as c
 from pylons.i18n.translation import _
 
-from rhodecode.lib import safe_unicode
+from rhodecode.lib.utils2 import safe_unicode
 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib.base import BaseRepoController
 

rhodecode/controllers/files.py

 from pylons.controllers.util import redirect
 from pylons.decorators import jsonify
 
-from rhodecode.lib.vcs.conf import settings
-from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
-    EmptyRepositoryError, ImproperArchiveTypeError, VCSError, \
-    NodeAlreadyExistsError
-from rhodecode.lib.vcs.nodes import FileNode
+from rhodecode.lib import diffs
+from rhodecode.lib import helpers as h
 
 from rhodecode.lib.compat import OrderedDict
-from rhodecode.lib import convert_line_endings, detect_mode, safe_str
+from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib.base import BaseRepoController, render
 from rhodecode.lib.utils import EmptyChangeset
-from rhodecode.lib import diffs
-import rhodecode.lib.helpers as h
+from rhodecode.lib.vcs.conf import settings
+from rhodecode.lib.vcs.exceptions import RepositoryError, \
+    ChangesetDoesNotExistError, EmptyRepositoryError, \
+    ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
+from rhodecode.lib.vcs.nodes import FileNode
+
 from rhodecode.model.repo import RepoModel
+from rhodecode.model.scm import ScmModel
+
 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
     _context_url, get_line_ctx, get_ignore_ws
-from rhodecode.lib.diffs import wrapped_diff
-from rhodecode.model.scm import ScmModel
+
 
 log = logging.getLogger(__name__)
 
             ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
 
             lim = request.GET.get('fulldiff') or self.cut_off_limit
-            _, cs1, cs2, diff, st = wrapped_diff(filenode_old=node1,
+            _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
                                          filenode_new=node2,
                                          cut_off_limit=lim,
                                          ignore_whitespace=ign_whitespace_lcl,

rhodecode/controllers/home.py

     def index(self):
         c.repos_list = self.scm_model.get_repos()
         c.groups = self.scm_model.get_repos_groups()
-
+        c.group = None
         return render('/index.html')
 
     def repo_switcher(self):

rhodecode/controllers/summary.py

 import traceback
 import calendar
 import logging
+import urllib
 from time import mktime
 from datetime import timedelta, date
 from urlparse import urlparse
 
 from beaker.cache import cache_region, region_invalidate
 
+from rhodecode.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
 from rhodecode.model.db import Statistics, CacheInvalidation
-from rhodecode.lib import ALL_READMES, ALL_EXTS
+from rhodecode.lib.utils2 import safe_unicode
 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib.base import BaseRepoController, render
 from rhodecode.lib.utils import EmptyChangeset
 from rhodecode.lib.markup_renderer import MarkupRenderer
 from rhodecode.lib.celerylib import run_task
-from rhodecode.lib.celerylib.tasks import get_commits_stats, \
-    LANGUAGES_EXTENSIONS_MAP
+from rhodecode.lib.celerylib.tasks import get_commits_stats
 from rhodecode.lib.helpers import RepoPage
 from rhodecode.lib.compat import json, OrderedDict
 
 
         uri_tmpl = config.get('clone_uri', default_clone_uri)
         uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
-
+        decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
         uri_dict = {
            'user': username,
            'pass': password,
            'scheme': parsed_url.scheme,
            'netloc': parsed_url.netloc,
-           'path': parsed_url.path
+           'path': decoded_path
         }
+
         uri = uri_tmpl % uri_dict
         # generate another clone url by id
-        uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id})
+        uri_dict.update(
+         {'path': decoded_path.replace(repo_name, '_%s' % c.dbrepo.repo_id)}
+        )
         uri_id = uri_tmpl % uri_dict
 
         c.clone_repo_url = uri
         c.clone_repo_url_id = uri_id
         c.repo_tags = OrderedDict()
-        for name, hash in c.rhodecode_repo.tags.items()[:10]:
+        for name, hash_ in c.rhodecode_repo.tags.items()[:10]:
             try:
-                c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
+                c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash_)
             except ChangesetError:
-                c.repo_tags[name] = EmptyChangeset(hash)
+                c.repo_tags[name] = EmptyChangeset(hash_)
 
         c.repo_branches = OrderedDict()
-        for name, hash in c.rhodecode_repo.branches.items()[:10]:
+        for name, hash_ in c.rhodecode_repo.branches.items()[:10]:
             try:
-                c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
+                c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash_)
             except ChangesetError:
-                c.repo_branches[name] = EmptyChangeset(hash)
+                c.repo_branches[name] = EmptyChangeset(hash_)
 
         td = date.today() + timedelta(days=1)
         td_1m = td - timedelta(days=calendar.mdays[td.month])
         if c.enable_downloads:
             c.download_options = self._get_download_links(c.rhodecode_repo)
 
-        c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
+        c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_db_repo)
         return render('summary/summary.html')
 
     def __get_readme_data(self, repo):
 
             return readme_data, readme_file
 
-        key = repo.name + '_README'
+        key = repo.repo_name + '_README'
         inv = CacheInvalidation.invalidate(key)
         if inv is not None:
             region_invalidate(_get_readme_from_cache, None, key)

rhodecode/lib/__init__.py

-# -*- coding: utf-8 -*-
-"""
-    rhodecode.lib.__init__
-    ~~~~~~~~~~~~~~~~~~~~~~~
-
-    Some simple helper functions
-
-    :created_on: Jan 5, 2011
-    :author: marcink
-    :copyright: (C) 2011-2012 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 re
-from rhodecode.lib.vcs.utils.lazy import LazyProperty
-
-
-def __get_lem():
-    from pygments import lexers
-    from string import lower
-    from collections import defaultdict
-
-    d = defaultdict(lambda: [])
-
-    def __clean(s):
-        s = s.lstrip('*')
-        s = s.lstrip('.')
-
-        if s.find('[') != -1:
-            exts = []
-            start, stop = s.find('['), s.find(']')
-
-            for suffix in s[start + 1:stop]:
-                exts.append(s[:s.find('[')] + suffix)
-            return map(lower, exts)
-        else:
-            return map(lower, [s])
-
-    for lx, t in sorted(lexers.LEXERS.items()):
-        m = map(__clean, t[-2])
-        if m:
-            m = reduce(lambda x, y: x + y, m)
-            for ext in m:
-                desc = lx.replace('Lexer', '')
-                d[ext].append(desc)
-
-    return dict(d)
-
-# language map is also used by whoosh indexer, which for those specified
-# extensions will index it's content
-LANGUAGES_EXTENSIONS_MAP = __get_lem()
-
-# Additional mappings that are not present in the pygments lexers
-# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
-ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
-
-LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
-
-# list of readme files to search in file tree and display in summary
-# attached weights defines the search  order lower is first
-ALL_READMES = [
-    ('readme', 0), ('README', 0), ('Readme', 0),
-    ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
-    ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
-    ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
-    ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
-]
-
-# extension together with weights to search lower is first
-RST_EXTS = [
-    ('', 0), ('.rst', 1), ('.rest', 1),
-    ('.RST', 2), ('.REST', 2),
-    ('.txt', 3), ('.TXT', 3)
-]
-
-MARKDOWN_EXTS = [
-    ('.md', 1), ('.MD', 1),
-    ('.mkdn', 2), ('.MKDN', 2),
-    ('.mdown', 3), ('.MDOWN', 3),
-    ('.markdown', 4), ('.MARKDOWN', 4)
-]
-
-PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
-
-ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
-
-
-def str2bool(_str):
-    """
-    returs True/False value from given string, it tries to translate the
-    string into boolean
-
-    :param _str: string value to translate into boolean
-    :rtype: boolean
-    :returns: boolean from given string
-    """
-    if _str is None:
-        return False
-    if _str in (True, False):
-        return _str
-    _str = str(_str).strip().lower()
-    return _str in ('t', 'true', 'y', 'yes', 'on', '1')
-
-
-def convert_line_endings(line, mode):
-    """
-    Converts a given line  "line end" accordingly to given mode
-
-    Available modes are::
-        0 - Unix
-        1 - Mac
-        2 - DOS
-
-    :param line: given line to convert
-    :param mode: mode to convert to
-    :rtype: str
-    :return: converted line according to mode
-    """
-    from string import replace
-
-    if mode == 0:
-            line = replace(line, '\r\n', '\n')
-            line = replace(line, '\r', '\n')
-    elif mode == 1:
-            line = replace(line, '\r\n', '\r')
-            line = replace(line, '\n', '\r')
-    elif mode == 2:
-            line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
-    return line
-
-
-def detect_mode(line, default):
-    """
-    Detects line break for given line, if line break couldn't be found
-    given default value is returned
-
-    :param line: str line
-    :param default: default
-    :rtype: int
-    :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
-    """
-    if line.endswith('\r\n'):
-        return 2
-    elif line.endswith('\n'):
-        return 0
-    elif line.endswith('\r'):
-        return 1
-    else:
-        return default
-
-
-def generate_api_key(username, salt=None):
-    """
-    Generates unique API key for given username, if salt is not given
-    it'll be generated from some random string
-
-    :param username: username as string
-    :param salt: salt to hash generate KEY
-    :rtype: str
-    :returns: sha1 hash from username+salt
-    """
-    from tempfile import _RandomNameSequence
-    import hashlib
-
-    if salt is None:
-        salt = _RandomNameSequence().next()
-
-    return hashlib.sha1(username + salt).hexdigest()
-
-
-def safe_unicode(str_, from_encoding=None):
-    """
-    safe unicode function. Does few trick to turn str_ into unicode
-
-    In case of UnicodeDecode error we try to return it with encoding detected
-    by chardet library if it fails fallback to unicode with errors replaced
-
-    :param str_: string to decode
-    :rtype: unicode
-    :returns: unicode object
-    """
-    if isinstance(str_, unicode):
-        return str_
-
-    if not from_encoding:
-        import rhodecode
-        DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
-        from_encoding = DEFAULT_ENCODING
-
-    try:
-        return unicode(str_)
-    except UnicodeDecodeError:
-        pass
-
-    try:
-        return unicode(str_, from_encoding)
-    except UnicodeDecodeError:
-        pass
-
-    try:
-        import chardet
-        encoding = chardet.detect(str_)['encoding']
-        if encoding is None:
-            raise Exception()
-        return str_.decode(encoding)
-    except (ImportError, UnicodeDecodeError, Exception):
-        return unicode(str_, from_encoding, 'replace')
-
-
-def safe_str(unicode_, to_encoding=None):
-    """
-    safe str function. Does few trick to turn unicode_ into string
-
-    In case of UnicodeEncodeError we try to return it with encoding detected
-    by chardet library if it fails fallback to string with errors replaced
-
-    :param unicode_: unicode to encode
-    :rtype: str
-    :returns: str object
-    """
-
-    # if it's not basestr cast to str
-    if not isinstance(unicode_, basestring):
-        return str(unicode_)
-
-    if isinstance(unicode_, str):
-        return unicode_
-
-    if not to_encoding:
-        import rhodecode
-        DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
-        to_encoding = DEFAULT_ENCODING
-
-    try:
-        return unicode_.encode(to_encoding)
-    except UnicodeEncodeError:
-        pass
-
-    try:
-        import chardet
-        encoding = chardet.detect(unicode_)['encoding']
-        print encoding
-        if encoding is None:
-            raise UnicodeEncodeError()
-
-        return unicode_.encode(encoding)
-    except (ImportError, UnicodeEncodeError):
-        return unicode_.encode(to_encoding, 'replace')
-
-    return safe_str
-
-
-def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
-    """
-    Custom engine_from_config functions that makes sure we use NullPool for
-    file based sqlite databases. This prevents errors on sqlite. This only
-    applies to sqlalchemy versions < 0.7.0
-
-    """
-    import sqlalchemy
-    from sqlalchemy import engine_from_config as efc
-    import logging
-
-    if int(sqlalchemy.__version__.split('.')[1]) < 7:
-
-        # This solution should work for sqlalchemy < 0.7.0, and should use
-        # proxy=TimerProxy() for execution time profiling
-
-        from sqlalchemy.pool import NullPool
-        url = configuration[prefix + 'url']
-
-        if url.startswith('sqlite'):
-            kwargs.update({'poolclass': NullPool})
-        return efc(configuration, prefix, **kwargs)
-    else:
-        import time
-        from sqlalchemy import event
-        from sqlalchemy.engine import Engine
-
-        log = logging.getLogger('sqlalchemy.engine')
-        BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
-        engine = efc(configuration, prefix, **kwargs)
-
-        def color_sql(sql):
-            COLOR_SEQ = "\033[1;%dm"
-            COLOR_SQL = YELLOW
-            normal = '\x1b[0m'
-            return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
-
-        if configuration['debug']:
-            #attach events only for debug configuration
-
-            def before_cursor_execute(conn, cursor, statement,
-                                    parameters, context, executemany):
-                context._query_start_time = time.time()
-                log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
-
-
-            def after_cursor_execute(conn, cursor, statement,
-                                    parameters, context, executemany):
-                total = time.time() - context._query_start_time
-                log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
-
-            event.listen(engine, "before_cursor_execute",
-                         before_cursor_execute)
-            event.listen(engine, "after_cursor_execute",
-                         after_cursor_execute)
-
-    return engine
-
-
-def age(curdate):
-    """
-    turns a datetime into an age string.
-
-    :param curdate: datetime object
-    :rtype: unicode
-    :returns: unicode words describing age
-    """
-
-    from datetime import datetime
-    from webhelpers.date import time_ago_in_words
-
-    _ = lambda s: s
-
-    if not curdate:
-        return ''
-
-    agescales = [(_(u"year"), 3600 * 24 * 365),
-                 (_(u"month"), 3600 * 24 * 30),
-                 (_(u"day"), 3600 * 24),
-                 (_(u"hour"), 3600),
-                 (_(u"minute"), 60),
-                 (_(u"second"), 1), ]
-
-    age = datetime.now() - curdate
-    age_seconds = (age.days * agescales[2][1]) + age.seconds
-    pos = 1
-    for scale in agescales:
-        if scale[1] <= age_seconds:
-            if pos == 6:
-                pos = 5
-            return '%s %s' % (time_ago_in_words(curdate,
-                                                agescales[pos][0]), _('ago'))
-        pos += 1
-
-    return _(u'just now')
-
-
-def uri_filter(uri):
-    """
-    Removes user:password from given url string
-
-    :param uri:
-    :rtype: unicode
-    :returns: filtered list of strings
-    """
-    if not uri:
-        return ''
-
-    proto = ''
-
-    for pat in ('https://', 'http://'):
-        if uri.startswith(pat):
-            uri = uri[len(pat):]
-            proto = pat
-            break
-
-    # remove passwords and username
-    uri = uri[uri.find('@') + 1:]
-
-    # get the port
-    cred_pos = uri.find(':')
-    if cred_pos == -1:
-        host, port = uri, None
-    else:
-        host, port = uri[:cred_pos], uri[cred_pos + 1:]
-
-    return filter(None, [proto, host, port])
-
-
-def credentials_filter(uri):
-    """
-    Returns a url with removed credentials
-
-    :param uri:
-    """
-
-    uri = uri_filter(uri)
-    #check if we have port
-    if len(uri) > 2 and uri[2]:
-        uri[2] = ':' + uri[2]
-
-    return ''.join(uri)
-
-
-def get_changeset_safe(repo, rev):
-    """
-    Safe version of get_changeset if this changeset doesn't exists for a
-    repo it returns a Dummy one instead
-
-    :param repo:
-    :param rev:
-    """
-    from rhodecode.lib.vcs.backends.base import BaseRepository
-    from rhodecode.lib.vcs.exceptions import RepositoryError
-    if not isinstance(repo, BaseRepository):
-        raise Exception('You must pass an Repository '
-                        'object as first argument got %s', type(repo))
-
-    try:
-        cs = repo.get_changeset(rev)
-    except RepositoryError:
-        from rhodecode.lib.utils import EmptyChangeset
-        cs = EmptyChangeset(requested_revision=rev)
-    return cs
 
 
 def get_current_revision(quiet=False):
             print ("Cannot retrieve rhodecode's revision. Original error "
                    "was: %s" % err)
         return None
-
-
-def extract_mentioned_users(s):
-    """
-    Returns unique usernames from given string s that have @mention
-
-    :param s: string to get mentions
-    """
-    usrs = {}
-    for username in re.findall(r'(?:^@|\s@)(\w+)', s):
-        usrs[username] = username
-
-    return sorted(usrs.keys())

rhodecode/lib/auth.py

 if __platform__ in PLATFORM_OTHERS:
     import bcrypt
 
-from rhodecode.lib import str2bool, safe_unicode
+from rhodecode.lib.utils2 import str2bool, safe_unicode
 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
 from rhodecode.lib.auth_ldap import AuthLdap
         self.user = cls.rhodecode_user
         self.user_perms = self.user.permissions
         log.debug('checking %s permissions %s for %s %s',
-           self.__class__.__name__, self.required_perms, cls,
-               self.user)
+           self.__class__.__name__, self.required_perms, cls, self.user)
 
         if self.check_permissions():
             log.debug('Permission granted for %s %s' % (cls, self.user))
             user_perms = set([self.user_perms['repositories'][repo_name]])
         except KeyError:
             return False
+
         if self.required_perms.intersection(user_perms):
             return True
         return False
 
         for perm in perms:
             if perm not in available_perms:
-                raise Exception("'%s' permission in not defined" % perm)
+                raise Exception("'%s' permission is not defined" % perm)
         self.required_perms = set(perms)
         self.user_perms = None
-        self.granted_for = ''
         self.repo_name = None
+        self.group_name = None
 
     def __call__(self, check_Location=''):