Changeset - 716911af91e1
[Not reviewed]
beta
0 10 0
Marcin Kuzminski - 15 years ago 2011-03-06 00:06:28
marcin@python-works.com
Added api_key into user, api key get's generated again after password change
updated ini files
10 files changed with 84 insertions and 150 deletions:
0 comments (0 inline, 0 general)
development.ini
Show inline comments
 
################################################################################
 
################################################################################
 
# RhodeCode - Pylons environment configuration                                 #
 
#                                                                              # 
 
# The %(here)s variable will be replaced with the parent directory of this file#
 
################################################################################
 

	
 
[DEFAULT]
 
debug = true
 
################################################################################
 
## Uncomment and replace with the address which should receive                ## 
 
## any error reports after application crash                                  ##
 
## Additionally those settings will be used by RhodeCode mailing system       ##
 
################################################################################
 
#email_to = admin@localhost
 
#error_email_from = paste_error@localhost
 
#app_email_from = rhodecode-noreply@localhost
 
#error_message =
 

	
 
#smtp_server = mail.server.com
 
#smtp_username = 
 
#smtp_password = 
 
#smtp_port = 
 
#smtp_use_tls = false
 
#smtp_use_ssl = true
 

	
 
[server:main]
 
##nr of threads to spawn
 
threadpool_workers = 5
 

	
 
##max request before thread respawn
 
threadpool_max_requests = 6
 

	
 
##option to use threads of process
 
use_threadpool = false
 
use_threadpool = true
 

	
 
use = egg:Paste#http
 
host = 0.0.0.0
 
port = 5000
 

	
 
[app:main]
 
use = egg:rhodecode
 
full_stack = true
 
static_files = true
 
lang=en
 
cache_dir = %(here)s/data
 
index_dir = %(here)s/data/index
 
app_instance_uuid = develop
 
cut_off_limit = 256000
 
force_https = false
 
commit_parse_limit = 25
 
use_gravatar = true
 

	
 
####################################
 
###        CELERY CONFIG        ####
 
####################################
 
use_celery = false
 
broker.host = localhost
 
broker.vhost = rabbitmqhost
 
broker.port = 5672
 
broker.user = rabbitmq
 
broker.password = qweqwe
 

	
 
celery.imports = rhodecode.lib.celerylib.tasks
 

	
 
celery.result.backend = amqp
 
celery.result.dburi = amqp://
 
celery.result.serialier = json
 

	
 
#celery.send.task.error.emails = true
 
#celery.amqp.task.result.expires = 18000
 

	
 
celeryd.concurrency = 2
 
#celeryd.log.file = celeryd.log
 
celeryd.log.level = debug
 
celeryd.max.tasks.per.child = 1
 

	
 
#tasks will never be sent to the queue, but executed locally instead.
 
celery.always.eager = false
 

	
 
####################################
 
###         BEAKER CACHE        ####
 
####################################
 
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.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.long_term.type=memory
 
beaker.cache.long_term.expire=36000
 

	
 
beaker.cache.sql_cache_short.type=memory
 
beaker.cache.sql_cache_short.expire=10
 

	
 
beaker.cache.sql_cache_med.type=memory
 
beaker.cache.sql_cache_med.expire=360
 

	
 
beaker.cache.sql_cache_long.type=file
 
beaker.cache.sql_cache_long.expire=3600
 

	
 
####################################
 
###       BEAKER SESSION        ####
 
####################################
 
## Type of storage used for the session, current types are 
 
## dbm, file, memcached, database, and memory. 
 
## The storage uses the Container API 
 
##that is also used by the cache system.
 
beaker.session.type = file
 

	
 
beaker.session.key = rhodecode
 
beaker.session.secret = g654dcno0-9873jhgfreyu
 
beaker.session.timeout = 36000
 

	
 
##auto save the session to not to use .save()
 
beaker.session.auto = False
 

	
 
##true exire at browser close
 
#beaker.session.cookie_expires = 3600
 

	
 
    
 
################################################################################
 
## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*  ##
 
## Debug mode will enable the interactive debugging tool, allowing ANYONE to  ##
 
## execute malicious code after an exception is raised.                       ##
 
################################################################################
 
#set debug = false
 

	
 
##################################
 
###       LOGVIEW CONFIG       ###
 
##################################
 
logview.sqlalchemy = #faa
 
logview.pylons.templating = #bfb
 
logview.pylons.util = #eee
 

	
 
#########################################################
 
### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG    ###
 
#########################################################
 
#sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
 
sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 
#sqlalchemy.db1.echo = False
 
#sqlalchemy.db1.pool_recycle = 3600
 
sqlalchemy.convert_unicode = true
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, rhodecode, sqlalchemy,beaker,templates
 

	
 
[handlers]
 
keys = console
 

	
 
[formatters]
 
keys = generic,color_formatter
 

	
 
#############
 
## LOGGERS ##
 
#############
 
[logger_root]
 
level = NOTSET
 
handlers = console
 

	
 
[logger_routes]
 
level = DEBUG
 
handlers = console
 
qualname = routes.middleware
 
# "level = DEBUG" logs the route matched and routing variables.
 
propagate = 0
 

	
 
[logger_beaker]
 
level = ERROR
 
handlers = console
 
qualname = beaker.container
 
propagate = 0
 

	
 
[logger_templates]
 
level = INFO
 
handlers = console
 
qualname = pylons.templating
 
propagate = 0
 

	
 
[logger_rhodecode]
 
level = DEBUG
 
handlers = console
 
qualname = rhodecode
 
propagate = 0
 

	
 
[logger_sqlalchemy]
 
level = ERROR
 
handlers = console
 
qualname = sqlalchemy.engine
 
propagate = 0
 

	
 
##############
 
## HANDLERS ##
 
##############
 

	
 
[handler_console]
 
class = StreamHandler
 
args = (sys.stderr,)
 
level = NOTSET
 
formatter = color_formatter
 

	
 
################
 
## FORMATTERS ##
 
################
 

	
 
[formatter_generic]
 
format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 
datefmt = %Y-%m-%d %H:%M:%S
 

	
 
[formatter_color_formatter]
 
class=rhodecode.lib.colored_formatter.ColorFormatter
 
format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 
datefmt = %Y-%m-%d %H:%M:%S
 
\ No newline at end of file
production.ini
Show inline comments
 
################################################################################
 
################################################################################
 
# RhodeCode - Pylons environment configuration                                 #
 
#                                                                              # 
 
# The %(here)s variable will be replaced with the parent directory of this file#
 
################################################################################
 

	
 
[DEFAULT]
 
debug = true
 
################################################################################
 
## Uncomment and replace with the address which should receive                ## 
 
## any error reports after application crash                                  ##
 
## Additionally those settings will be used by RhodeCode mailing system       ##
 
################################################################################
 
#email_to = admin@localhost
 
#error_email_from = paste_error@localhost
 
#app_email_from = rhodecode-noreply@localhost
 
#error_message =
 

	
 
#smtp_server = mail.server.com
 
#smtp_username = 
 
#smtp_password = 
 
#smtp_port = 
 
#smtp_use_tls = false
 
#smtp_use_ssl = true
 

	
 
[server:main]
 
##nr of threads to spawn
 
threadpool_workers = 5
 

	
 
##max request before thread respawn
 
threadpool_max_requests = 2
 
threadpool_max_requests = 6
 

	
 
##option to use threads of process
 
use_threadpool = true
 

	
 
use = egg:Paste#http
 
host = 127.0.0.1
 
port = 8001
 

	
 
[app:main]
 
use = egg:rhodecode
 
full_stack = true
 
static_files = false
 
lang=en
 
cache_dir = %(here)s/data
 
index_dir = %(here)s/data/index
 
cut_off_limit = 256000
 
force_https = false
 
commit_parse_limit = 250
 
commit_parse_limit = 25
 
use_gravatar = true
 

	
 
####################################
 
###        CELERY CONFIG        ####
 
####################################
 
use_celery = false
 
broker.host = localhost
 
broker.vhost = rabbitmqhost
 
broker.port = 5672
 
broker.user = rabbitmq
 
broker.password = qweqwe
 

	
 
celery.imports = rhodecode.lib.celerylib.tasks
 

	
 
celery.result.backend = amqp
 
celery.result.dburi = amqp://
 
celery.result.serialier = json
 

	
 
#celery.send.task.error.emails = true
 
#celery.amqp.task.result.expires = 18000
 

	
 
celeryd.concurrency = 2
 
#celeryd.log.file = celeryd.log
 
celeryd.log.level = debug
 
celeryd.max.tasks.per.child = 1
 

	
 
#tasks will never be sent to the queue, but executed locally instead.
 
celery.always.eager = false
 

	
 
####################################
 
###         BEAKER CACHE        ####
 
####################################
 
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.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.long_term.type=memory
 
beaker.cache.long_term.expire=36000
 

	
 

	
 
beaker.cache.sql_cache_short.type=memory
 
beaker.cache.sql_cache_short.expire=10
 

	
 
beaker.cache.sql_cache_med.type=memory
 
beaker.cache.sql_cache_med.expire=360
 

	
 
beaker.cache.sql_cache_long.type=file
 
beaker.cache.sql_cache_long.expire=3600
 

	
 
####################################
 
###       BEAKER SESSION        ####
 
####################################
 
## Type of storage used for the session, current types are 
 
## dbm, file, memcached, database, and memory. 
 
## The storage uses the Container API 
 
##that is also used by the cache system.
 
beaker.session.type = file
 

	
 
beaker.session.key = rhodecode
 
beaker.session.secret = g654dcno0-9873jhgfreyu
 
beaker.session.timeout = 36000
 

	
 
##auto save the session to not to use .save()
 
beaker.session.auto = False
 

	
 
##true exire at browser close
 
#beaker.session.cookie_expires = 3600
 

	
 
    
 
################################################################################
 
## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*  ##
 
## Debug mode will enable the interactive debugging tool, allowing ANYONE to  ##
 
## execute malicious code after an exception is raised.                       ##
 
################################################################################
 
set debug = false
 

	
 
##################################
 
###       LOGVIEW CONFIG       ###
 
##################################
 
logview.sqlalchemy = #faa
 
logview.pylons.templating = #bfb
 
logview.pylons.util = #eee
 

	
 
#########################################################
 
### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG    ###
 
#########################################################
 
sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
 
#sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
 
sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
 
#sqlalchemy.db1.echo = False
 
#sqlalchemy.db1.pool_recycle = 3600
 
sqlalchemy.convert_unicode = true
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, rhodecode, sqlalchemy,beaker,templates
 

	
 
[handlers]
 
keys = console
 

	
 
[formatters]
 
keys = generic,color_formatter
 

	
 
#############
 
## LOGGERS ##
 
#############
 
[logger_root]
 
level = INFO
 
handlers = console
 

	
 
[logger_routes]
 
level = INFO
 
handlers = console
 
qualname = routes.middleware
 
# "level = DEBUG" logs the route matched and routing variables.
 
propagate = 0
 

	
 
[logger_beaker]
 
level = ERROR
 
handlers = console
 
qualname = beaker.container
 
propagate = 0
 

	
 
[logger_templates]
 
level = INFO
 
handlers = console
 
qualname = pylons.templating
 
propagate = 0
 

	
 
[logger_rhodecode]
 
level = DEBUG
 
handlers = console
 
qualname = rhodecode
 
propagate = 0
 

	
 
[logger_sqlalchemy]
 
level = ERROR
 
handlers = console
 
qualname = sqlalchemy.engine
 
propagate = 0
 

	
 
##############
 
## HANDLERS ##
 
##############
 

	
 
[handler_console]
 
class = StreamHandler
 
args = (sys.stderr,)
 
level = NOTSET
 
formatter = color_formatter
 

	
 
################
 
## FORMATTERS ##
 
################
 

	
 
[formatter_generic]
 
format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 
datefmt = %Y-%m-%d %H:%M:%S
 

	
 
[formatter_color_formatter]
 
class=rhodecode.lib.colored_formatter.ColorFormatter
 
format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 
datefmt = %Y-%m-%d %H:%M:%S
 
\ No newline at end of file
rhodecode/lib/__init__.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.__init__
 
    ~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Some simple helper functions
 
    
 
    :created_on: Jan 5, 2011
 
    :author: marcink
 
    :copyright: (C) 2009-2010 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; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# 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, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 

	
 
def str2bool(v):
 
    return v.lower() in ["yes", "true", "t", "1"] if v else None
 

	
 
def generate_api_key(username, salt=None):
 
    from tempfile import _RandomNameSequence
 
    import hashlib
 

	
 
    if salt is None:
 
        salt = _RandomNameSequence().next()
 

	
 
    return hashlib.sha1(username + salt).hexdigest()
rhodecode/lib/auth.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.auth
 
    ~~~~~~~~~~~~~~~~~~
 
    
 
    authentication and permission libraries
 
    
 
    :created_on: Apr 4, 2010
 
    :copyright: (c) 2010 by marcink.
 
    :license: LICENSE_NAME, see LICENSE_FILE 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; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# 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, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 

	
 
import bcrypt
 
import random
 
import logging
 
import traceback
 

	
 
import hashlib
 
from tempfile import _RandomNameSequence
 
from decorator import decorator
 

	
 
from pylons import config, session, url, request
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
 
from rhodecode.lib.utils import get_repo_slug
 
from rhodecode.lib.auth_ldap import AuthLdap
 

	
 
from rhodecode.model import meta
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
 
    UserToPerm, UsersGroupToPerm, UsersGroupMember
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
PERM_WEIGHTS = {'repository.none':0,
 
                'repository.read':1,
 
                'repository.write':3,
 
                'repository.admin':3}
 

	
 

	
 
class PasswordGenerator(object):
 
    """This is a simple class for generating password from
 
        different sets of characters
 
        usage:
 
        passwd_gen = PasswordGenerator()
 
        #print 8-letter password containing only big and small letters of alphabet
 
        print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)        
 
    """
 
    ALPHABETS_NUM = r'''1234567890'''#[0]
 
    ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
 
    ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
 
    ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''    #[3]
 
    ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
 
    ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
 
    ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
 
    ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
 
    ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
 

	
 
    def __init__(self, passwd=''):
 
        self.passwd = passwd
 

	
 
    def gen_password(self, len, type):
 
        self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
 
        return self.passwd
 

	
 

	
 
def get_crypt_password(password):
 
    """Cryptographic function used for password hashing based on pybcrypt
 
    
 
    :param password: password to hash
 
    """
 
    return bcrypt.hashpw(password, bcrypt.gensalt(10))
 

	
 
def generate_api_key(username, salt=None):
 
    if salt is None:
 
        salt = _RandomNameSequence().next()
 

	
 
    return hashlib.sha1(username + salt).hexdigest()
 

	
 
def check_password(password, hashed):
 
    return bcrypt.hashpw(password, hashed) == hashed
 

	
 
def authfunc(environ, username, password):
 
    """Dummy authentication function used in Mercurial/Git/ and access control,
 
    
 
    :param environ: needed only for using in Basic auth
 
    """
 
    return authenticate(username, password)
 

	
 

	
 
def authenticate(username, password):
 
    """Authentication function used for access control,
 
    firstly checks for db authentication then if ldap is enabled for ldap
 
    authentication, also creates ldap user if not in database
 
    
 
    :param username: username
 
    :param password: password
 
    """
 
    user_model = UserModel()
 
    user = user_model.get_by_username(username, cache=False)
 

	
 
    log.debug('Authenticating user using RhodeCode account')
 
    if user is not None and not user.ldap_dn:
 
        if user.active:
 

	
 
            if user.username == 'default' and user.active:
 
                log.info('user %s authenticated correctly as anonymous user',
 
                         username)
 
                return True
 

	
 
            elif user.username == username and check_password(password, user.password):
 
                log.info('user %s authenticated correctly', username)
 
                return True
 
        else:
 
            log.warning('user %s is disabled', username)
 

	
 
    else:
 
        log.debug('Regular authentication failed')
 
        user_obj = user_model.get_by_username(username, cache=False,
 
                                            case_insensitive=True)
 

	
 
        if user_obj is not None and not user_obj.ldap_dn:
 
            log.debug('this user already exists as non ldap')
 
            return False
 

	
 
        from rhodecode.model.settings import SettingsModel
 
        ldap_settings = SettingsModel().get_ldap_settings()
 

	
 
        #======================================================================
 
        # FALLBACK TO LDAP AUTH IF ENABLE                
 
        #======================================================================
 
        if ldap_settings.get('ldap_active', False):
 
            log.debug("Authenticating user using ldap")
 
            kwargs = {
 
                  'server':ldap_settings.get('ldap_host', ''),
 
                  'base_dn':ldap_settings.get('ldap_base_dn', ''),
 
                  'port':ldap_settings.get('ldap_port'),
 
                  'bind_dn':ldap_settings.get('ldap_dn_user'),
 
                  'bind_pass':ldap_settings.get('ldap_dn_pass'),
 
                  'use_ldaps':ldap_settings.get('ldap_ldaps'),
 
                  'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
 
                  'ldap_filter':ldap_settings.get('ldap_filter'),
 
                  'search_scope':ldap_settings.get('ldap_search_scope'),
 
                  'attr_login':ldap_settings.get('ldap_attr_login'),
 
                  'ldap_version':3,
 
                  }
 
            log.debug('Checking for ldap authentication')
 
            try:
 
                aldap = AuthLdap(**kwargs)
 
                (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
 
                log.debug('Got ldap DN response %s', user_dn)
 

	
 
                user_attrs = {
 
                    'name'     : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
 
                    'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
 
                    'email'    : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
 
                    }
 

	
 
                if user_model.create_ldap(username, password, user_dn, user_attrs):
 
                    log.info('created new ldap user %s', username)
 

	
 
                return True
 
            except (LdapUsernameError, LdapPasswordError,):
 
                pass
 
            except (Exception,):
 
                log.error(traceback.format_exc())
 
                pass
 
    return False
 

	
 
class  AuthUser(object):
 
    """A simple object that handles a mercurial username for authentication
 
    """
 

	
 
    def __init__(self):
 
        self.username = 'None'
 
        self.name = ''
 
        self.lastname = ''
 
        self.email = ''
 
        self.user_id = None
 
        self.is_authenticated = False
 
        self.is_admin = False
 
        self.permissions = {}
 

	
 
    def __repr__(self):
 
        return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
 

	
 
def set_available_permissions(config):
 
    """This function will propagate pylons globals with all available defined
 
    permission given in db. We don't want 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: current pylons config instance
 
    
 
    """
 
    log.info('getting information about all available permissions')
 
    try:
 
        sa = meta.Session()
 
        all_perms = sa.query(Permission).all()
 
    except:
 
        pass
 
    finally:
 
        meta.Session.remove()
 

	
 
    config['available_permissions'] = [x.permission_name for x in all_perms]
 

	
 
def fill_perms(user):
 
    """Fills user permission attribute with permissions taken from database
 
    works for permissions given for repositories, and for permissions that
 
    as part of beeing group member
 
    
 
    :param user: user instance to fill his perms
 
    """
 

	
 
    sa = meta.Session()
 
    user.permissions['repositories'] = {}
 
    user.permissions['global'] = set()
 

	
 
    #===========================================================================
 
    # fetch default permissions
 
    #===========================================================================
 
    default_user = UserModel().get_by_username('default', cache=True)
 

	
 
    default_perms = sa.query(RepoToPerm, Repository, Permission)\
 
        .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
 
        .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
 
        .filter(RepoToPerm.user == default_user).all()
 

	
 
    if user.is_admin:
 
        #=======================================================================
 
        # #admin have all default rights set to admin        
 
        #=======================================================================
 
        user.permissions['global'].add('hg.admin')
 

	
 
        for perm in default_perms:
 
            p = 'repository.admin'
 
            user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
 

	
 
    else:
 
        #=======================================================================
 
        # set default permissions
 
        #=======================================================================
 

	
 
        #default global
 
        default_global_perms = sa.query(UserToPerm)\
 
            .filter(UserToPerm.user == sa.query(User)\
 
                   .filter(User.username == 'default').one())
 

	
 
        for perm in default_global_perms:
 
            user.permissions['global'].add(perm.permission.permission_name)
 

	
 
        #default for repositories
 
        for perm in default_perms:
 
            if perm.Repository.private and not perm.Repository.user_id == user.user_id:
 
                #disable defaults for private repos,
 
                p = 'repository.none'
 
            elif perm.Repository.user_id == user.user_id:
 
                #set admin if owner
 
                p = 'repository.admin'
 
            else:
 
                p = perm.Permission.permission_name
 

	
 
            user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
 

	
 
        #=======================================================================
 
        # overwrite default with user permissions if any
 
        #=======================================================================
 
        user_perms = sa.query(RepoToPerm, Permission, Repository)\
 
            .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
 
            .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
 
            .filter(RepoToPerm.user_id == user.user_id).all()
rhodecode/lib/celerylib/tasks.py
Show inline comments
 
@@ -56,353 +56,354 @@ try:
 
except ImportError:
 
    #python 2.5 compatibility
 
    import simplejson as json
 

	
 
__all__ = ['whoosh_index', 'get_commits_stats',
 
           'reset_user_password', 'send_email']
 

	
 
CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
 

	
 
def get_session():
 
    if CELERY_ON:
 
        engine = engine_from_config(config, 'sqlalchemy.db1.')
 
        init_model(engine)
 
    sa = meta.Session()
 
    return sa
 

	
 
def get_repos_path():
 
    sa = get_session()
 
    q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
 
    return q.ui_value
 

	
 
@task(ignore_result=True)
 
@locked_task
 
def whoosh_index(repo_location, full_index):
 
    #log = whoosh_index.get_logger()
 
    from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
 
    index_location = config['index_dir']
 
    WhooshIndexingDaemon(index_location=index_location,
 
                         repo_location=repo_location, sa=get_session())\
 
                         .run(full_index=full_index)
 

	
 
@task(ignore_result=True)
 
@locked_task
 
def get_commits_stats(repo_name, ts_min_y, ts_max_y):
 
    try:
 
        log = get_commits_stats.get_logger()
 
    except:
 
        log = logging.getLogger(__name__)
 

	
 
    from rhodecode.model.db import Statistics, Repository
 

	
 
    #for js data compatibilty
 
    author_key_cleaner = lambda k: person(k).replace('"', "")
 

	
 
    commits_by_day_author_aggregate = {}
 
    commits_by_day_aggregate = {}
 
    repos_path = get_repos_path()
 
    p = os.path.join(repos_path, repo_name)
 
    repo = get_repo(p)
 

	
 
    skip_date_limit = True
 
    parse_limit = int(config['app_conf'].get('commit_parse_limit'))
 
    last_rev = 0
 
    last_cs = None
 
    timegetter = itemgetter('time')
 

	
 
    sa = get_session()
 

	
 
    dbrepo = sa.query(Repository)\
 
        .filter(Repository.repo_name == repo_name).scalar()
 
    cur_stats = sa.query(Statistics)\
 
        .filter(Statistics.repository == dbrepo).scalar()
 

	
 
    if cur_stats is not None:
 
        last_rev = cur_stats.stat_on_revision
 

	
 
    #return if repo is empty
 
    if not repo.revisions:
 
        return True
 

	
 
    if last_rev == repo.get_changeset().revision and len(repo.revisions) > 1:
 
        #pass silently without any work if we're not on first revision or 
 
        #current state of parsing revision(from db marker) is the last revision
 
        return True
 

	
 
    if cur_stats:
 
        commits_by_day_aggregate = OrderedDict(
 
                                       json.loads(
 
                                        cur_stats.commit_activity_combined))
 
        commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
 

	
 
    log.debug('starting parsing %s', parse_limit)
 
    lmktime = mktime
 

	
 
    last_rev = last_rev + 1 if last_rev > 0 else last_rev
 

	
 
    for cs in repo[last_rev:last_rev + parse_limit]:
 
        last_cs = cs #remember last parsed changeset
 
        k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
 
                      cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
 

	
 
        if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
 
            try:
 
                l = [timegetter(x) for x in commits_by_day_author_aggregate\
 
                        [author_key_cleaner(cs.author)]['data']]
 
                time_pos = l.index(k)
 
            except ValueError:
 
                time_pos = False
 

	
 
            if time_pos >= 0 and time_pos is not False:
 

	
 
                datadict = commits_by_day_author_aggregate\
 
                    [author_key_cleaner(cs.author)]['data'][time_pos]
 

	
 
                datadict["commits"] += 1
 
                datadict["added"] += len(cs.added)
 
                datadict["changed"] += len(cs.changed)
 
                datadict["removed"] += len(cs.removed)
 

	
 
            else:
 
                if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
 

	
 
                    datadict = {"time":k,
 
                                "commits":1,
 
                                "added":len(cs.added),
 
                                "changed":len(cs.changed),
 
                                "removed":len(cs.removed),
 
                               }
 
                    commits_by_day_author_aggregate\
 
                        [author_key_cleaner(cs.author)]['data'].append(datadict)
 

	
 
        else:
 
            if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
 
                commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
 
                                    "label":author_key_cleaner(cs.author),
 
                                    "data":[{"time":k,
 
                                             "commits":1,
 
                                             "added":len(cs.added),
 
                                             "changed":len(cs.changed),
 
                                             "removed":len(cs.removed),
 
                                             }],
 
                                    "schema":["commits"],
 
                                    }
 

	
 
        #gather all data by day
 
        if commits_by_day_aggregate.has_key(k):
 
            commits_by_day_aggregate[k] += 1
 
        else:
 
            commits_by_day_aggregate[k] = 1
 

	
 
    overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
 
    if not commits_by_day_author_aggregate:
 
        commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
 
            "label":author_key_cleaner(repo.contact),
 
            "data":[0, 1],
 
            "schema":["commits"],
 
        }
 

	
 
    stats = cur_stats if cur_stats else Statistics()
 
    stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
 
    stats.commit_activity_combined = json.dumps(overview_data)
 

	
 
    log.debug('last revison %s', last_rev)
 
    leftovers = len(repo.revisions[last_rev:])
 
    log.debug('revisions to parse %s', leftovers)
 

	
 
    if last_rev == 0 or leftovers < parse_limit:
 
        log.debug('getting code trending stats')
 
        stats.languages = json.dumps(__get_codes_stats(repo_name))
 

	
 
    try:
 
        stats.repository = dbrepo
 
        stats.stat_on_revision = last_cs.revision if last_cs else 0
 
        sa.add(stats)
 
        sa.commit()
 
    except:
 
        log.error(traceback.format_exc())
 
        sa.rollback()
 
        return False
 
    if len(repo.revisions) > 1:
 
        run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
 

	
 
    return True
 

	
 
@task(ignore_result=True)
 
def reset_user_password(user_email):
 
    try:
 
        log = reset_user_password.get_logger()
 
    except:
 
        log = logging.getLogger(__name__)
 

	
 
    from rhodecode.lib import auth
 
    from rhodecode.model.db import User
 

	
 
    try:
 
        try:
 
            sa = get_session()
 
            user = sa.query(User).filter(User.email == user_email).scalar()
 
            new_passwd = auth.PasswordGenerator().gen_password(8,
 
                             auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
 
            if user:
 
                user.password = auth.get_crypt_password(new_passwd)
 
                user.api_key = auth.generate_api_key(user.username)
 
                sa.add(user)
 
                sa.commit()
 
                log.info('change password for %s', user_email)
 
            if new_passwd is None:
 
                raise Exception('unable to generate new password')
 

	
 
        except:
 
            log.error(traceback.format_exc())
 
            sa.rollback()
 

	
 
        run_task(send_email, user_email,
 
                 "Your new rhodecode password",
 
                 'Your new rhodecode password:%s' % (new_passwd))
 
        log.info('send new password mail to %s', user_email)
 

	
 

	
 
    except:
 
        log.error('Failed to update user password')
 
        log.error(traceback.format_exc())
 

	
 
    return True
 

	
 
@task(ignore_result=True)
 
def send_email(recipients, subject, body):
 
    """
 
    Sends an email with defined parameters from the .ini files.
 
    
 
    
 
    :param recipients: list of recipients, it this is empty the defined email
 
        address from field 'email_to' is used instead
 
    :param subject: subject of the mail
 
    :param body: body of the mail
 
    """
 
    try:
 
        log = send_email.get_logger()
 
    except:
 
        log = logging.getLogger(__name__)
 

	
 
    email_config = config
 

	
 
    if not recipients:
 
        recipients = [email_config.get('email_to')]
 

	
 
    mail_from = email_config.get('app_email_from')
 
    user = email_config.get('smtp_username')
 
    passwd = email_config.get('smtp_password')
 
    mail_server = email_config.get('smtp_server')
 
    mail_port = email_config.get('smtp_port')
 
    tls = str2bool(email_config.get('smtp_use_tls'))
 
    ssl = str2bool(email_config.get('smtp_use_ssl'))
 

	
 
    try:
 
        m = SmtpMailer(mail_from, user, passwd, mail_server,
 
                       mail_port, ssl, tls)
 
        m.send(recipients, subject, body)
 
    except:
 
        log.error('Mail sending failed')
 
        log.error(traceback.format_exc())
 
        return False
 
    return True
 

	
 
@task(ignore_result=True)
 
def create_repo_fork(form_data, cur_user):
 
    try:
 
        log = create_repo_fork.get_logger()
 
    except:
 
        log = logging.getLogger(__name__)
 

	
 
    from rhodecode.model.repo import RepoModel
 
    from vcs import get_backend
 

	
 
    repo_model = RepoModel(get_session())
 
    repo_model.create(form_data, cur_user, just_db=True, fork=True)
 
    repo_name = form_data['repo_name']
 
    repos_path = get_repos_path()
 
    repo_path = os.path.join(repos_path, repo_name)
 
    repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
 
    alias = form_data['repo_type']
 

	
 
    log.info('creating repo fork %s as %s', repo_name, repo_path)
 
    backend = get_backend(alias)
 
    backend(str(repo_fork_path), create=True, src_url=str(repo_path))
 

	
 
def __get_codes_stats(repo_name):
 
    LANGUAGES_EXTENSIONS_MAP = {'scm': 'Scheme', 'asmx': 'VbNetAspx', 'Rout':
 
    'RConsole', 'rest': 'Rst', 'abap': 'ABAP', 'go': 'Go', 'phtml': 'HtmlPhp',
 
    'ns2': 'Newspeak', 'xml': 'EvoqueXml', 'sh-session': 'BashSession', 'ads':
 
    'Ada', 'clj': 'Clojure', 'll': 'Llvm', 'ebuild': 'Bash', 'adb': 'Ada',
 
    'ada': 'Ada', 'c++-objdump': 'CppObjdump', 'aspx':
 
    'VbNetAspx', 'ksh': 'Bash', 'coffee': 'CoffeeScript', 'vert': 'GLShader',
 
    'Makefile.*': 'Makefile', 'di': 'D', 'dpatch': 'DarcsPatch', 'rake':
 
    'Ruby', 'moo': 'MOOCode', 'erl-sh': 'ErlangShell', 'geo': 'GLShader',
 
    'pov': 'Povray', 'bas': 'VbNet', 'bat': 'Batch', 'd': 'D', 'lisp':
 
    'CommonLisp', 'h': 'C', 'rbx': 'Ruby', 'tcl': 'Tcl', 'c++': 'Cpp', 'md':
 
    'MiniD', '.vimrc': 'Vim', 'xsd': 'Xml', 'ml': 'Ocaml', 'el': 'CommonLisp',
 
    'befunge': 'Befunge', 'xsl': 'Xslt', 'pyx': 'Cython', 'cfm':
 
    'ColdfusionHtml', 'evoque': 'Evoque', 'cfg': 'Ini', 'htm': 'Html',
 
    'Makefile': 'Makefile', 'cfc': 'ColdfusionHtml', 'tex': 'Tex', 'cs':
 
    'CSharp', 'mxml': 'Mxml', 'patch': 'Diff', 'apache.conf': 'ApacheConf',
 
    'scala': 'Scala', 'applescript': 'AppleScript', 'GNUmakefile': 'Makefile',
 
    'c-objdump': 'CObjdump', 'lua': 'Lua', 'apache2.conf': 'ApacheConf', 'rb':
 
    'Ruby', 'gemspec': 'Ruby', 'rl': 'RagelObjectiveC', 'vala': 'Vala', 'tmpl':
 
    'Cheetah', 'bf': 'Brainfuck', 'plt': 'Gnuplot', 'G': 'AntlrRuby', 'xslt':
 
    'Xslt', 'flxh': 'Felix', 'asax': 'VbNetAspx', 'Rakefile': 'Ruby', 'S': 'S',
 
    'wsdl': 'Xml', 'js': 'Javascript', 'autodelegate': 'Myghty', 'properties':
 
    'Ini', 'bash': 'Bash', 'c': 'C', 'g': 'AntlrRuby', 'r3': 'Rebol', 's':
 
    'Gas', 'ashx': 'VbNetAspx', 'cxx': 'Cpp', 'boo': 'Boo', 'prolog': 'Prolog',
 
    'sqlite3-console': 'SqliteConsole', 'cl': 'CommonLisp', 'cc': 'Cpp', 'pot':
 
    'Gettext', 'vim': 'Vim', 'pxi': 'Cython', 'yaml': 'Yaml', 'SConstruct':
 
    'Python', 'diff': 'Diff', 'txt': 'Text', 'cw': 'Redcode', 'pxd': 'Cython',
 
    'plot': 'Gnuplot', 'java': 'Java', 'hrl': 'Erlang', 'py': 'Python',
 
    'makefile': 'Makefile', 'squid.conf': 'SquidConf', 'asm': 'Nasm', 'toc':
 
    'Tex', 'kid': 'Genshi', 'rhtml': 'Rhtml', 'po': 'Gettext', 'pl': 'Prolog',
 
    'pm': 'Perl', 'hx': 'Haxe', 'ascx': 'VbNetAspx', 'ooc': 'Ooc', 'asy':
 
    'Asymptote', 'hs': 'Haskell', 'SConscript': 'Python', 'pytb':
 
    'PythonTraceback', 'myt': 'Myghty', 'hh': 'Cpp', 'R': 'S', 'aux': 'Tex',
 
    'rst': 'Rst', 'cpp-objdump': 'CppObjdump', 'lgt': 'Logtalk', 'rss': 'Xml',
 
    'flx': 'Felix', 'b': 'Brainfuck', 'f': 'Fortran', 'rbw': 'Ruby',
 
    '.htaccess': 'ApacheConf', 'cxx-objdump': 'CppObjdump', 'j': 'ObjectiveJ',
 
    'mll': 'Ocaml', 'yml': 'Yaml', 'mu': 'MuPAD', 'r': 'Rebol', 'ASM': 'Nasm',
 
    'erl': 'Erlang', 'mly': 'Ocaml', 'mo': 'Modelica', 'def': 'Modula2', 'ini':
 
    'Ini', 'control': 'DebianControl', 'vb': 'VbNet', 'vapi': 'Vala', 'pro':
 
    'Prolog', 'spt': 'Cheetah', 'mli': 'Ocaml', 'as': 'ActionScript3', 'cmd':
 
    'Batch', 'cpp': 'Cpp', 'io': 'Io', 'tac': 'Python', 'haml': 'Haml', 'rkt':
 
    'Racket', 'st':'Smalltalk', 'inc': 'Povray', 'pas': 'Delphi', 'cmake':
 
    'CMake', 'csh':'Tcsh', 'hpp': 'Cpp', 'feature': 'Gherkin', 'html': 'Html',
 
    'php':'Php', 'php3':'Php', 'php4':'Php', 'php5':'Php', 'xhtml': 'Html',
 
    'hxx': 'Cpp', 'eclass': 'Bash', 'css': 'Css',
 
    'frag': 'GLShader', 'd-objdump': 'DObjdump', 'weechatlog': 'IrcLogs',
 
    'tcsh': 'Tcsh', 'objdump': 'Objdump', 'pyw': 'Python', 'h++': 'Cpp',
 
    'py3tb': 'Python3Traceback', 'jsp': 'Jsp', 'sql': 'Sql', 'mak': 'Makefile',
 
    'php': 'Php', 'mao': 'Mako', 'man': 'Groff', 'dylan': 'Dylan', 'sass':
 
    'Sass', 'cfml': 'ColdfusionHtml', 'darcspatch': 'DarcsPatch', 'tpl':
 
    'Smarty', 'm': 'ObjectiveC', 'f90': 'Fortran', 'mod': 'Modula2', 'sh':
 
    'Bash', 'lhs': 'LiterateHaskell', 'sources.list': 'SourcesList', 'axd':
 
    'VbNetAspx', 'sc': 'Python'}
 

	
 
    repos_path = get_repos_path()
 
    p = os.path.join(repos_path, repo_name)
 
    repo = get_repo(p)
 
    tip = repo.get_changeset()
 
    code_stats = {}
 

	
 
    def aggregate(cs):
 
        for f in cs[2]:
 
            ext = f.extension
 
            key = LANGUAGES_EXTENSIONS_MAP.get(ext, ext)
 
            key = key or ext
 
            if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
 
                if code_stats.has_key(key):
 
                    code_stats[key] += 1
 
                else:
 
                    code_stats[key] = 1
 

	
 
    map(aggregate, tip.walk('/'))
 

	
 
    return code_stats or {}
 

	
 

	
 

	
 

	
rhodecode/lib/db_manage.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.db_manage
 
    ~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Database creation, and setup module for RhodeCode. Used for creation
 
    of database as well as for migration operations
 
    
 
    :created_on: Apr 10, 2010
 
    :author: marcink
 
    :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>    
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software; you can redistribute it and/or
 
# modify it under the terms of the GNU General Public License
 
# as published by the Free Software Foundation; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# 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, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 

	
 
import os
 
import sys
 
import uuid
 
import logging
 
from os.path import dirname as dn, join as jn
 

	
 
from rhodecode import __dbversion__
 
from rhodecode.model import meta
 

	
 
from rhodecode.lib.auth import get_crypt_password
 
from rhodecode.lib.auth import get_crypt_password, generate_api_key
 
from rhodecode.lib.utils import ask_ok
 
from rhodecode.model import init_model
 
from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
 
    UserToPerm, DbMigrateVersion
 

	
 
from sqlalchemy.engine import create_engine
 

	
 
log = logging.getLogger(__name__)
 

	
 
class DbManage(object):
 
    def __init__(self, log_sql, dbconf, root, tests=False):
 
        self.dbname = dbconf.split('/')[-1]
 
        self.tests = tests
 
        self.root = root
 
        self.dburi = dbconf
 
        self.log_sql = log_sql
 
        self.db_exists = False
 
        self.init_db()
 

	
 
    def init_db(self):
 
        engine = create_engine(self.dburi, echo=self.log_sql)
 
        init_model(engine)
 
        self.sa = meta.Session()
 

	
 
    def check_for_db(self, override):
 
        db_path = jn(self.root, self.dbname)
 
        if self.dburi.startswith('sqlite'):
 
            log.info('checking for existing db in %s', db_path)
 
            if os.path.isfile(db_path):
 

	
 
                self.db_exists = True
 
                if not override:
 
                    raise Exception('database already exists')
 
            return 'sqlite'
 
        if self.dburi.startswith('postgresql'):
 
            self.db_exists = True
 
            return 'postgresql'
 

	
 

	
 
    def create_tables(self, override=False):
 
        """Create a auth database
 
        """
 

	
 
        db_type = self.check_for_db(override)
 
        if self.db_exists:
 
            log.info("database exist and it's going to be destroyed")
 
            if self.tests:
 
                destroy = True
 
            else:
 
                destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
 
            if not destroy:
 
                sys.exit()
 
            if self.db_exists and destroy:
 
                if db_type == 'sqlite':
 
                    os.remove(jn(self.root, self.dbname))
 
                if db_type == 'postgresql':
 
                    meta.Base.metadata.drop_all()
 

	
 
        checkfirst = not override
 
        meta.Base.metadata.create_all(checkfirst=checkfirst)
 
        log.info('Created tables for %s', self.dbname)
 

	
 

	
 

	
 
    def set_db_version(self):
 
        try:
 
            ver = DbMigrateVersion()
 
            ver.version = __dbversion__
 
            ver.repository_id = 'rhodecode_db_migrations'
 
            ver.repository_path = 'versions'
 
            self.sa.add(ver)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 
        log.info('db version set to: %s', __dbversion__)
 

	
 

	
 
    def upgrade(self):
 
        """Upgrades given database schema to given revision following 
 
        all needed steps, to perform the upgrade
 
        
 
        """
 

	
 
        from rhodecode.lib.dbmigrate.migrate.versioning import api
 
        from rhodecode.lib.dbmigrate.migrate.exceptions import \
 
            DatabaseNotControlledError
 

	
 
        upgrade = ask_ok('You are about to perform database upgrade, make '
 
                         'sure You backed up your database before. '
 
                         'Continue ? [y/n]')
 
        if not upgrade:
 
            sys.exit('Nothing done')
 

	
 
        repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
 
                             'rhodecode/lib/dbmigrate')
 
        db_uri = self.dburi
 

	
 
        try:
 
            curr_version = api.db_version(db_uri, repository_path)
 
            msg = ('Found current database under version'
 
                 ' control with version %s' % curr_version)
 

	
 
        except (RuntimeError, DatabaseNotControlledError), e:
 
            curr_version = 1
 
            msg = ('Current database is not under version control. Setting'
 
                   ' as version %s' % curr_version)
 
            api.version_control(db_uri, repository_path, curr_version)
 

	
 
        print (msg)
 

	
 
        if curr_version == __dbversion__:
 
            sys.exit('This database is already at the newest version')
 

	
 
        #======================================================================
 
        # UPGRADE STEPS
 
        #======================================================================
 
        class UpgradeSteps(object):
 
            """Those steps follow schema versions so for example schema 
 
            for example schema with seq 002 == step_2 and so on.
 
            """
 

	
 
            def __init__(self, klass):
 
                self.klass = klass
 

	
 
            def step_0(self):
 
                #step 0 is the schema upgrade, and than follow proper upgrades
 
                print ('attempting to do database upgrade to version %s' \
 
                                % __dbversion__)
 
                api.upgrade(db_uri, repository_path, __dbversion__)
 
                print ('Schema upgrade completed')
 

	
 
            def step_1(self):
 
                pass
 

	
 
            def step_2(self):
 
                print ('Patching repo paths for newer version of RhodeCode')
 
                self.klass.fix_repo_paths()
 

	
 
                print ('Patching default user of RhodeCode')
 
                self.klass.fix_default_user()
 

	
 
                log.info('Changing ui settings')
 
                self.klass.create_ui_settings()
 

	
 
            def step_3(self):
 
                print ('Adding additional settings into RhodeCode db')
 
                self.klass.fix_settings()
 

	
 
        upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
 

	
 
        #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
 
        for step in upgrade_steps:
 
            print ('performing upgrade step %s' % step)
 
            callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
 

	
 

	
 

	
 
    def fix_repo_paths(self):
 
        """Fixes a old rhodecode version path into new one without a '*'
 
        """
 

	
 
        paths = self.sa.query(RhodeCodeUi)\
 
                .filter(RhodeCodeUi.ui_key == '/')\
 
                .scalar()
 

	
 
        paths.ui_value = paths.ui_value.replace('*', '')
 

	
 
        try:
 
            self.sa.add(paths)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 
    def fix_default_user(self):
 
        """Fixes a old default user with some 'nicer' default values,
 
        used mostly for anonymous access
 
        """
 
        def_user = self.sa.query(User)\
 
                .filter(User.username == 'default')\
 
                .one()
 

	
 
        def_user.name = 'Anonymous'
 
        def_user.lastname = 'User'
 
        def_user.email = 'anonymous@rhodecode.org'
 

	
 
        try:
 
            self.sa.add(def_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
@@ -253,282 +253,284 @@ class DbManage(object):
 
                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 = 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:
 
            log.info('creating admin and regular test users')
 
            self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
 
            self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
 
            self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
 

	
 
    def create_ui_settings(self):
 
        """Creates ui settings, fills out hooks
 
        and disables dotencode
 
        
 
        """
 
        #HOOKS
 
        hooks1_key = 'changegroup.update'
 
        hooks1_ = self.sa.query(RhodeCodeUi)\
 
            .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
 

	
 
        hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
 
        hooks1.ui_section = 'hooks'
 
        hooks1.ui_key = hooks1_key
 
        hooks1.ui_value = 'hg update >&2'
 
        hooks1.ui_active = False
 

	
 
        hooks2_key = 'changegroup.repo_size'
 
        hooks2_ = self.sa.query(RhodeCodeUi)\
 
            .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
 

	
 
        hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
 
        hooks2.ui_section = 'hooks'
 
        hooks2.ui_key = hooks2_key
 
        hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
 

	
 
        hooks3 = RhodeCodeUi()
 
        hooks3.ui_section = 'hooks'
 
        hooks3.ui_key = 'pretxnchangegroup.push_logger'
 
        hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
 

	
 
        hooks4 = RhodeCodeUi()
 
        hooks4.ui_section = 'hooks'
 
        hooks4.ui_key = 'preoutgoing.pull_logger'
 
        hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
 

	
 
        #For mercurial 1.7 set backward comapatibility with format
 
        dotencode_disable = RhodeCodeUi()
 
        dotencode_disable.ui_section = 'format'
 
        dotencode_disable.ui_key = 'dotencode'
 
        dotencode_disable.ui_value = 'false'
 

	
 
        try:
 
            self.sa.add(hooks1)
 
            self.sa.add(hooks2)
 
            self.sa.add(hooks3)
 
            self.sa.add(hooks4)
 
            self.sa.add(dotencode_disable)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 

	
 
    def create_ldap_options(self):
 
        """Creates ldap settings"""
 

	
 
        try:
 
            for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
 
                      'ldap_tls_reqcert', 'ldap_dn_user', 'ldap_dn_pass',
 
                      'ldap_base_dn', 'ldap_filter', 'ldap_search_scope',
 
                      'ldap_attr_login', 'ldap_attr_firstname', 'ldap_attr_lastname',
 
                      'ldap_attr_email']:
 

	
 
                setting = RhodeCodeSettings(k, '')
 
                self.sa.add(setting)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 
    def config_prompt(self, test_repo_path='', retries=3):
 
        if retries == 3:
 
            log.info('Setting up repositories config')
 

	
 
        if not self.tests and not test_repo_path:
 
            path = raw_input('Specify valid full path to your repositories'
 
                        ' you can change this later in application settings:')
 
        else:
 
            path = test_repo_path
 
        path_ok = True
 

	
 
        #check proper dir
 
        if not os.path.isdir(path):
 
            path_ok = False
 
            log.error('Entered path is not a valid directory: %s [%s/3]',
 
                      path, retries)
 

	
 
        #check write access
 
        if not os.access(path, os.W_OK):
 
            path_ok = False
 

	
 
            log.error('No write permission to given path: %s [%s/3]',
 
                      path, retries)
 

	
 

	
 
        if retries == 0:
 
            sys.exit()
 
        if path_ok is False:
 
            retries -= 1
 
            return self.config_prompt(test_repo_path, retries)
 

	
 

	
 
        return path
 

	
 
    def create_settings(self, path):
 

	
 
        self.create_ui_settings()
 

	
 
        #HG UI OPTIONS
 
        web1 = RhodeCodeUi()
 
        web1.ui_section = 'web'
 
        web1.ui_key = 'push_ssl'
 
        web1.ui_value = 'false'
 

	
 
        web2 = RhodeCodeUi()
 
        web2.ui_section = 'web'
 
        web2.ui_key = 'allow_archive'
 
        web2.ui_value = 'gz zip bz2'
 

	
 
        web3 = RhodeCodeUi()
 
        web3.ui_section = 'web'
 
        web3.ui_key = 'allow_push'
 
        web3.ui_value = '*'
 

	
 
        web4 = RhodeCodeUi()
 
        web4.ui_section = 'web'
 
        web4.ui_key = 'baseurl'
 
        web4.ui_value = '/'
 

	
 
        paths = RhodeCodeUi()
 
        paths.ui_section = 'paths'
 
        paths.ui_key = '/'
 
        paths.ui_value = path
 

	
 

	
 
        hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
 
        hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
 
        hgsettings3 = RhodeCodeSettings('ga_code', '')
 

	
 

	
 
        try:
 
            self.sa.add(web1)
 
            self.sa.add(web2)
 
            self.sa.add(web3)
 
            self.sa.add(web4)
 
            self.sa.add(paths)
 
            self.sa.add(hgsettings1)
 
            self.sa.add(hgsettings2)
 
            self.sa.add(hgsettings3)
 

	
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 
        self.create_ldap_options()
 

	
 
        log.info('created ui config')
 

	
 
    def create_user(self, username, password, email='', admin=False):
 
        log.info('creating administrator user %s', username)
 
        new_user = User()
 
        new_user.username = username
 
        new_user.password = get_crypt_password(password)
 
        new_user.api_key = generate_api_key(username)
 
        new_user.name = 'RhodeCode'
 
        new_user.lastname = 'Admin'
 
        new_user.email = email
 
        new_user.admin = admin
 
        new_user.active = True
 

	
 
        try:
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 
    def create_default_user(self):
 
        log.info('creating default user')
 
        #create default user for handling default permissions.
 
        def_user = User()
 
        def_user.username = 'default'
 
        def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
 
        def_user.api_key = generate_api_key('default')
 
        def_user.name = 'Anonymous'
 
        def_user.lastname = 'User'
 
        def_user.email = 'anonymous@rhodecode.org'
 
        def_user.admin = False
 
        def_user.active = False
 
        try:
 
            self.sa.add(def_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
 
    def create_permissions(self):
 
        #module.(access|create|change|delete)_[name]
 
        #module.(read|write|owner)
 
        perms = [('repository.none', 'Repository no access'),
 
                 ('repository.read', 'Repository read access'),
 
                 ('repository.write', 'Repository write access'),
 
                 ('repository.admin', 'Repository admin access'),
 
                 ('hg.admin', 'Hg Administrator'),
 
                 ('hg.create.repository', 'Repository create'),
 
                 ('hg.create.none', 'Repository creation disabled'),
 
                 ('hg.register.none', 'Register disabled'),
 
                 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
 
                 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
 
                 ('hg.register.manual_activate', 'Register new user with RhodeCode without manual activation'),
 
                 ('hg.register.auto_activate', 'Register new user with RhodeCode without auto activation'),
 
                ]
 

	
 
        for p in perms:
 
            new_perm = Permission()
 
            new_perm.permission_name = p[0]
 
            new_perm.permission_longname = p[1]
 
            try:
 
                self.sa.add(new_perm)
 
                self.sa.commit()
 
            except:
 
                self.sa.rollback()
 
                raise
 

	
 
    def populate_default_permissions(self):
 
        log.info('creating default user permissions')
 

	
 
        default_user = self.sa.query(User)\
 
        .filter(User.username == 'default').scalar()
 

	
 
        reg_perm = UserToPerm()
 
        reg_perm.user = default_user
 
        reg_perm.permission = self.sa.query(Permission)\
 
        .filter(Permission.permission_name == 'hg.register.manual_activate')\
 
        .scalar()
 

	
 
        create_repo_perm = UserToPerm()
 
        create_repo_perm.user = default_user
 
        create_repo_perm.permission = self.sa.query(Permission)\
 
        .filter(Permission.permission_name == 'hg.create.repository')\
 
        .scalar()
 

	
 
        default_repo_perm = UserToPerm()
 
        default_repo_perm.user = default_user
 
        default_repo_perm.permission = self.sa.query(Permission)\
 
        .filter(Permission.permission_name == 'repository.read')\
 
        .scalar()
 

	
 
        try:
 
            self.sa.add(reg_perm)
 
            self.sa.add(create_repo_perm)
 
            self.sa.add(default_repo_perm)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 

	
rhodecode/model/user.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.model.user
 
    ~~~~~~~~~~~~~~~~~~~~
 

	
 
    users model for RhodeCode
 
    
 
    :created_on: Apr 9, 2010
 
    :author: marcink
 
    :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>    
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software; you can redistribute it and/or
 
# modify it under the terms of the GNU General Public License
 
# as published by the Free Software Foundation; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# 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, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 

	
 
import logging
 
import traceback
 

	
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.model import BaseModel
 
from rhodecode.model.caching_query import FromCache
 
from rhodecode.model.db import User
 

	
 
from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
 

	
 
from sqlalchemy.exc import DatabaseError
 
from rhodecode.lib import generate_api_key
 

	
 
log = logging.getLogger(__name__)
 

	
 
class UserModel(BaseModel):
 

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

	
 

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

	
 
        if case_insensitive:
 
            user = self.sa.query(User).filter(User.username.ilike(username))
 
        else:
 
            user = self.sa.query(User)\
 
                .filter(User.username == username)
 
        if cache:
 
            user = user.options(FromCache("sql_cache_short",
 
                                          "get_user_%s" % username))
 
        return user.scalar()
 

	
 
    def create(self, form_data):
 
        try:
 
            new_user = User()
 
            for k, v in form_data.items():
 
                setattr(new_user, k, v)
 

	
 
            new_user.api_key = generate_api_key(form_data['username'])
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 

	
 
    def create_ldap(self, username, password, user_dn, attrs):
 
        """
 
        Checks if user is in database, if not creates this user marked
 
        as ldap user
 
        :param username:
 
        :param password:
 
        :param user_dn:
 
        :param attrs:
 
        """
 
        from rhodecode.lib.auth import get_crypt_password
 
        log.debug('Checking for such ldap account in RhodeCode database')
 
        if self.get_by_username(username, case_insensitive=True) is None:
 
            try:
 
                new_user = User()
 
                new_user.username = username.lower() # add ldap account always lowercase
 
                new_user.password = get_crypt_password(password)
 
                new_user.api_key = generate_api_key(username)
 
                new_user.email = attrs['email']
 
                new_user.active = True
 
                new_user.ldap_dn = user_dn
 
                new_user.name = attrs['name']
 
                new_user.lastname = attrs['lastname']
 

	
 

	
 
                self.sa.add(new_user)
 
                self.sa.commit()
 
                return True
 
            except (DatabaseError,):
 
                log.error(traceback.format_exc())
 
                self.sa.rollback()
 
                raise
 
        log.debug('this %s user exists skipping creation of ldap account',
 
                  username)
 
        return False
 

	
 
    def create_registration(self, form_data):
 
        from rhodecode.lib.celerylib import tasks, run_task
 
        try:
 
            new_user = User()
 
            for k, v in form_data.items():
 
                if k != 'admin':
 
                    setattr(new_user, k, v)
 

	
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
            body = ('New user registration\n'
 
                    'username: %s\n'
 
                    'email: %s\n')
 
            body = body % (form_data['username'], form_data['email'])
 

	
 
            run_task(tasks.send_email, None,
 
                     _('[RhodeCode] New User registration'),
 
                     body)
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 

	
 
    def update(self, user_id, form_data):
 
        try:
 
            new_user = self.get(user_id, cache=False)
 
            if new_user.username == 'default':
 
            user = self.get(user_id, cache=False)
 
            if user.username == 'default':
 
                raise DefaultUserException(
 
                                _("You can't Edit this user since it's"
 
                                  " crucial for entire application"))
 

	
 
            for k, v in form_data.items():
 
                if k == 'new_password' and v != '':
 
                    new_user.password = v
 
                    user.password = v
 
                    user.api_key = generate_api_key(user.username)
 
                else:
 
                    setattr(new_user, k, v)
 
                    setattr(user, k, v)
 

	
 
            self.sa.add(new_user)
 
            self.sa.add(user)
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 

	
 
    def update_my_account(self, user_id, form_data):
 
        try:
 
            new_user = self.get(user_id, cache=False)
 
            if new_user.username == 'default':
 
            user = self.get(user_id, cache=False)
 
            if user.username == 'default':
 
                raise DefaultUserException(
 
                                _("You can't Edit this user since it's"
 
                                  " crucial for entire application"))
 
            for k, v in form_data.items():
 
                if k == 'new_password' and v != '':
 
                    new_user.password = v
 
                    user.password = v
 
                    user.api_key = generate_api_key(user.username)
 
                else:
 
                    if k not in ['admin', 'active']:
 
                        setattr(new_user, k, v)
 
                        setattr(user, k, v)
 

	
 
            self.sa.add(new_user)
 
            self.sa.add(user)
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 

	
 
    def delete(self, user_id):
 
        try:
 
            user = self.get(user_id, cache=False)
 
            if user.username == 'default':
 
                raise DefaultUserException(
 
                                _("You can't remove this user since it's"
 
                                  " crucial for entire application"))
 
            if user.repositories:
 
                raise UserOwnsReposException(_('This user still owns %s '
 
                                               'repositories and cannot be '
 
                                               'removed. Switch owners or '
 
                                               'remove those repositories') \
 
                                               % user.repositories)
 
            self.sa.delete(user)
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 

	
 
    def reset_password(self, data):
 
        from rhodecode.lib.celerylib import tasks, run_task
 
        run_task(tasks.reset_user_password, data['email'])
 

	
 

	
 
    def fill_data(self, user):
 
        """
 
        Fills user data with those from database and log out user if not 
 
        present in database
 
        :param user:
 
        """
 

	
 
        if not hasattr(user, 'user_id') or user.user_id is None:
 
            raise Exception('passed in user has to have the user_id attribute')
 

	
 

	
 
        log.debug('filling auth user data')
 
        try:
 
            dbuser = self.get(user.user_id)
 
            user.username = dbuser.username
 
            user.is_admin = dbuser.admin
 
            user.name = dbuser.name
 
            user.lastname = dbuser.lastname
 
            user.email = dbuser.email
 
        except:
 
            log.error(traceback.format_exc())
 
            user.is_authenticated = False
 

	
 
        return user
rhodecode/public/css/style.css
Show inline comments
 
@@ -669,385 +669,385 @@ padding:4px 0;
 
 
#content div.box ul.disc,#content div.box ul.circle {
 
margin:10px 24px 10px 38px;
 
}
 
 
#content div.box ul.square {
 
margin:10px 24px 10px 40px;
 
}
 
 
#content div.box img.left {
 
border:none;
 
float:left;
 
margin:10px 10px 10px 0;
 
}
 
 
#content div.box img.right {
 
border:none;
 
float:right;
 
margin:10px 0 10px 10px;
 
}
 
 
#content div.box div.messages {
 
clear:both;
 
overflow:hidden;
 
margin:0 20px;
 
padding:0;
 
}
 
 
#content div.box div.message {
 
clear:both;
 
overflow:hidden;
 
margin:0;
 
padding:10px 0;
 
}
 
 
#content div.box div.message a {
 
font-weight:400 !important;
 
}
 
 
#content div.box div.message div.image {
 
float:left;
 
margin:9px 0 0 5px;
 
padding:6px;
 
}
 
 
#content div.box div.message div.image img {
 
vertical-align:middle;
 
margin:0;
 
}
 
 
#content div.box div.message div.text {
 
float:left;
 
margin:0;
 
padding:9px 6px;
 
}
 
 
#content div.box div.message div.dismiss a {
 
height:16px;
 
width:16px;
 
display:block;
 
background:url("../images/icons/cross.png") no-repeat;
 
margin:15px 14px 0 0;
 
padding:0;
 
}
 
 
#content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6 {
 
border:none;
 
margin:0;
 
padding:0;
 
}
 
 
#content div.box div.message div.text span {
 
height:1%;
 
display:block;
 
margin:0;
 
padding:5px 0 0;
 
}
 
 
#content div.box div.message-error {
 
height:1%;
 
clear:both;
 
overflow:hidden;
 
background:#FBE3E4;
 
border:1px solid #FBC2C4;
 
color:#860006;
 
}
 
 
#content div.box div.message-error h6 {
 
color:#860006;
 
}
 
 
#content div.box div.message-warning {
 
height:1%;
 
clear:both;
 
overflow:hidden;
 
background:#FFF6BF;
 
border:1px solid #FFD324;
 
color:#5f5200;
 
}
 
 
#content div.box div.message-warning h6 {
 
color:#5f5200;
 
}
 
 
#content div.box div.message-notice {
 
height:1%;
 
clear:both;
 
overflow:hidden;
 
background:#8FBDE0;
 
border:1px solid #6BACDE;
 
color:#003863;
 
}
 
 
#content div.box div.message-notice h6 {
 
color:#003863;
 
}
 
 
#content div.box div.message-success {
 
height:1%;
 
clear:both;
 
overflow:hidden;
 
background:#E6EFC2;
 
border:1px solid #C6D880;
 
color:#4e6100;
 
}
 
 
#content div.box div.message-success h6 {
 
color:#4e6100;
 
}
 
 
#content div.box div.form div.fields div.field {
 
height:1%;
 
border-bottom:1px solid #DDD;
 
clear:both;
 
margin:0;
 
padding:10px 0;
 
}
 
 
#content div.box div.form div.fields div.field-first {
 
padding:0 0 10px;
 
}
 
 
#content div.box div.form div.fields div.field-noborder {
 
border-bottom:0 !important;
 
}
 
 
#content div.box div.form div.fields div.field span.error-message {
 
height:1%;
 
display:inline-block;
 
color:red;
 
margin:8px 0 0 4px;
 
padding:0;
 
}
 
 
#content div.box div.form div.fields div.field span.success {
 
height:1%;
 
display:block;
 
color:#316309;
 
margin:8px 0 0;
 
padding:0;
 
}
 
 
#content div.box div.form div.fields div.field div.label {
 
left:80px;
 
width:auto;
 
position:absolute;
 
margin:0;
 
padding:8px 0 0 5px;
 
}
 
 
#content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label {
 
clear:both;
 
overflow:hidden;
 
left:0;
 
width:auto;
 
position:relative;
 
margin:0;
 
padding:0 0 8px;
 
}
 
 
#content div.box div.form div.fields div.field div.label-select {
 
padding:5px 0 0 5px;
 
}
 
 
#content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select {
 
padding:0 0 8px;
 
}
 
 
#content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea {
 
padding:0 0 8px !important;
 
}
 
 
#content div.box div.form div.fields div.field div.label label {
 
#content div.box div.form div.fields div.field div.label label, div.label label{
 
color:#393939;
 
font-weight:700;
 
}
 
 
#content div.box div.form div.fields div.field div.input {
 
margin:0 0 0 200px;
 
}
 
#content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input {
 
margin:0 0 0 0px;
 
}
 
 
#content div.box div.form div.fields div.field div.input input {
 
background:#FFF;
 
border-top:1px solid #b3b3b3;
 
border-left:1px solid #b3b3b3;
 
border-right:1px solid #eaeaea;
 
border-bottom:1px solid #eaeaea;
 
color:#000;
 
font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
 
font-size:11px;
 
margin:0;
 
padding:7px 7px 6px;
 
}
 
 
 
 
#content div.box div.form div.fields div.field div.input input.small {
 
width:30%;
 
}
 
 
#content div.box div.form div.fields div.field div.input input.medium {
 
width:55%;
 
}
 
 
#content div.box div.form div.fields div.field div.input input.large {
 
width:85%;
 
}
 
 
#content div.box div.form div.fields div.field div.input input.date {
 
width:177px;
 
}
 
 
#content div.box div.form div.fields div.field div.input input.button {
 
background:#D4D0C8;
 
border-top:1px solid #FFF;
 
border-left:1px solid #FFF;
 
border-right:1px solid #404040;
 
border-bottom:1px solid #404040;
 
color:#000;
 
margin:0;
 
padding:4px 8px;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea {
 
border-top:1px solid #b3b3b3;
 
border-left:1px solid #b3b3b3;
 
border-right:1px solid #eaeaea;
 
border-bottom:1px solid #eaeaea;
 
margin:0 0 0 200px;
 
padding:10px;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea-editor {
 
border:1px solid #ddd;
 
padding:0;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea textarea {
 
width:100%;
 
height:220px;
 
overflow:hidden;
 
background:#FFF;
 
color:#000;
 
font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
 
font-size:11px;
 
outline:none;
 
border-width:0;
 
margin:0;
 
padding:0;
 
}
 
 
#content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea {
 
width:100%;
 
height:100px;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea table {
 
width:100%;
 
border:none;
 
margin:0;
 
padding:0;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea table td {
 
background:#DDD;
 
border:none;
 
padding:0;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea table td table {
 
width:auto;
 
border:none;
 
margin:0;
 
padding:0;
 
}
 
 
#content div.box div.form div.fields div.field div.textarea table td table td {
 
font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
 
font-size:11px;
 
padding:5px 5px 5px 0;
 
}
 
 
#content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus {
 
background:#f6f6f6;
 
border-color:#666;
 
}
 
 
div.form div.fields div.field div.button {
 
margin:0;
 
padding:0 0 0 8px;
 
}
 
 
div.form div.fields div.field div.highlight .ui-button {
 
background:#4e85bb url("../images/button_highlight.png") repeat-x;
 
border-top:1px solid #5c91a4;
 
border-left:1px solid #2a6f89;
 
border-right:1px solid #2b7089;
 
border-bottom:1px solid #1a6480;
 
color:#FFF;
 
margin:0;
 
padding:6px 12px;
 
}
 
 
div.form div.fields div.field div.highlight .ui-state-hover {
 
background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
 
border-top:1px solid #78acbf;
 
border-left:1px solid #34819e;
 
border-right:1px solid #35829f;
 
border-bottom:1px solid #257897;
 
color:#FFF;
 
margin:0;
 
padding:6px 12px;
 
}
 
 
#content div.box div.form div.fields div.buttons div.highlight input.ui-button {
 
background:#4e85bb url("../images/button_highlight.png") repeat-x;
 
border-top:1px solid #5c91a4;
 
border-left:1px solid #2a6f89;
 
border-right:1px solid #2b7089;
 
border-bottom:1px solid #1a6480;
 
color:#fff;
 
margin:0;
 
padding:6px 12px;
 
}
 
 
#content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover {
 
background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
 
border-top:1px solid #78acbf;
 
border-left:1px solid #34819e;
 
border-right:1px solid #35829f;
 
border-bottom:1px solid #257897;
 
color:#fff;
 
margin:0;
 
padding:6px 12px;
 
}
 
 
#content div.box table {
 
width:100%;
 
border-collapse:collapse;
 
margin:0;
 
padding:0;
 
}
 
 
#content div.box table th {
 
background:#eee;
 
border-bottom:1px solid #ddd;
 
padding:5px 0px 5px 5px;
 
}
 
 
#content div.box table th.left {
 
text-align:left;
 
}
 
 
#content div.box table th.right {
 
text-align:right;
 
}
 
 
#content div.box table th.center {
 
text-align:center;
 
}
 
 
#content div.box table th.selected {
rhodecode/templates/admin/users/user_edit.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(_('Admin'),h.url('admin_home'))} 
 
    &raquo; 
 
    ${h.link_to(_('Users'),h.url('users'))} 
 
    &raquo;
 
    ${_('edit')} "${c.user.username}"
 
</%def>
 

	
 
<%def name="page_nav()">
 
	${self.menu('admin')}
 
</%def>
 

	
 
<%def name="main()">
 
<div class="box box-left">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}       
 
    </div>
 
    <!-- end box / title -->
 
    ${h.form(url('user', id=c.user.user_id),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 
        <div class="field">
 
           <div class="gravatar_box">
 
               <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
 
               <p>
 
               <strong>Change your avatar at <a href="http://gravatar.com">gravatar.com</a></strong><br/> 
 
               ${_('Using')} ${c.user.email}
 
               </p>
 
           </div>
 
        </div>
 
        <div class="field">
 
            <div class="label">
 
                <label>${_('API key')}</label> ${c.user.api_key}
 
            </div>
 
        </div>
 
                 
 
        <div class="fields">
 
             <div class="field">
 
                <div class="gravatar_box">
 
              		<div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
 
              		<p>
 
              		<strong>Change your avatar at <a href="http://gravatar.com">gravatar.com</a></strong><br/> 
 
              		${_('Using')} ${c.user.email}
 
              		</p>
 
            	</div>
 
             </div>        
 
        
 
             <div class="field">
 
                <div class="label">
 
                    <label for="username">${_('Username')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('username',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="ldap_dn">${_('LDAP DN')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('ldap_dn',class_='small')}
 
                    ${h.text('ldap_dn',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="new_password">${_('New password')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.password('new_password',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="name">${_('First Name')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('name',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="lastname">${_('Last Name')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('lastname',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label">
 
                    <label for="email">${_('Email')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('email',class_='medium')}
 
                </div>
 
             </div>
 
            
 
             <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="active">${_('Active')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('active',value=True)}
 
                </div>
 
             </div>
 
             
 
             <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="admin">${_('Admin')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('admin',value=True)}
 
                </div>
 
             </div>             
 
            <div class="buttons">
 
              ${h.submit('save','Save',class_="ui-button")}
 
              ${h.reset('reset','Reset',class_="ui-button")}
 
            </div>             
 
    	</div>    
 
    </div>
 
    ${h.end_form()}
 
</div>
 
<div class="box box-right">
 
    <!-- box / title -->
 
    <div class="title">
 
        <h5>${_('Permissions')}</h5>       
 
    </div>
 
	<form id="map_form" method="post" action="{%url update_permissions %}">
 
	<div class="form">
 
	  <div class="fields">
 
	  
 
	  
 
	  
 
		<table> 
 
		        <tr>
 
		            <td class="label">${_('Permissions')}:</td>
 
		            <td>
 
		                <div>
 
		                    <div style="float:left">
 
		                    <div class="text">${_('Granted permissions')}</div>
 
		                        ${h.select('granted_permissions',[],c.granted_permissions,multiple=True,size=8,style="min-width:210px")}
 
		                    </div>
 
		                    <div style="float:left;width:20px;padding-top:50px">
 
		                        <img alt="add" id="add_element" 
 
		                            style="padding:2px;cursor:pointer" 
 
		                            src="${h.url("/images/icons/arrow_left.png")}">
 
		                        <br />
 
		                        <img alt="remove" id="remove_element" 
 
		                            style="padding:2px;cursor:pointer" 
 
		                            src="${h.url("/images/icons/arrow_right.png")}">
 
		                    </div>
 
		                    <div style="float:left">
 
		                        <div class="text">${_('Available permissions')}</div>
 
		                         ${h.select('available_permissions',[],c.available_permissions,multiple=True,size=8,style="min-width:210px")}
 
		                    </div>
 
		                </div>
 
		            </td>           
 
		        </tr>       
 

	
 
		</table>
 
        <div class="buttons">
 
         ${h.submit('Save','Save',class_="ui-button")}    
 
        </div>
 
	  </div>
 
	</div>
 
	</form>
 
    
 
    
 
<script type="text/javascript">
 
    YAHOO.util.Event.onDOMReady(function(){
 
     
 
            var D = YAHOO.util.Dom;
 
            var E = YAHOO.util.Event;
 
        
 
            //temp container for storage.
 
            var cache = new Array();
 
            var c =  D.get('id_granted_permissions');
 
            
 
            //get only selected options for further fullfilment
 
            for(var i = 0;node =c.options[i];i++){
 
                if(node.selected){
 
                    //push selected to my temp storage left overs :)
 
                    cache.push(node);
 
                }   
 
            }
 
            
 
            //clear select
 
            c.options.length = 0;
 

	
 
            //fill it with remembered options
 
            for(var i = 0;node = cache[i];i++){
 
                c.options[i]=new Option(node.text, node.value, false, false);
 
            }
 
            
 
            function target_callback(e){
 
                window.location='/admin/t4?g='+e.target.value;
 
            }
 

	
 
            function prompts_action_callback(e){
 
                
 
                var choosen = D.get('id_granted_permissions');  
 
                var availible = D.get('id_available_permissions');
 
        
 
                if (this.id=='add_element'){
 
                    for(var i=0; node = availible.options[i];i++){
 
                        if(node.selected){
 
                            choosen.appendChild(new Option(node.text, node.value, false, false));
 
                        }
 
                    }
 
                }
 
                else if (this.id=='remove_element'){
 

	
 
                    //temp container for storage.
 
                    cache = new Array();
 
                    
 
                    for(var i = 0;node = choosen.options[i];i++){
 
                        if(!node.selected){
 
                            //push left overs :)
 
                            cache.push(node);
 
                        }   
 
                    }
 
                    //clear select
 
                    choosen.options.length = 0;
 
                    for(var i = 0;node = cache[i];i++){
 
                        choosen.options[i]=new Option(node.text, node.value, false, false);
 
                    }               
 
                }                   
 
                else{
 
                    
 
                }
 
            }
 
            
 
            E.addListener('id_groups','change',target_callback);
 
    
 
            E.addListener(['add_element','remove_element'],'click',prompts_action_callback)
 

	
 
            E.addListener('map_form','submit',function(){
 
                var choosen = D.get('id_granted_permissions');
 
                for (var i = 0; i < choosen.options.length; i++) {
 
                    choosen.options[i].selected = 'selected';
 
                }
 
            })  
 
        });
 
</script>    
 
    ${h.form(url('user', id=c.user.user_id),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 
        <div class="fields">
 
             <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="">${_('Create repositories')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('create',value=True)}
 
                </div>
 
             </div>
 
            <div class="buttons">
 
              ${h.submit('save','Save',class_="ui-button")}
 
              ${h.reset('reset','Reset',class_="ui-button")}
 
            </div>             
 
        </div>    
 
    </div>
 
    ${h.end_form()}    
 
</div>
 
</%def>
rhodecode/templates/admin/users/user_edit_my_account.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${_('My Account')}
 
</%def>
 

	
 
<%def name="page_nav()">
 
	${self.menu('admin')}
 
</%def>
 

	
 
<%def name="main()">
 

	
 
<div class="box box-left">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}       
 
    </div>
 
    <!-- end box / title -->
 
    <div>
 
    ${h.form(url('admin_settings_my_account_update'),method='put')}
 
	    <div class="form">
 
	    
 
             <div class="field">
 
                <div class="gravatar_box">
 
                    <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
 
                    <p>
 
                    <strong>Change your avatar at <a href="http://gravatar.com">gravatar.com</a></strong><br/> 
 
                    ${_('Using')} ${c.user.email}
 
                    </p>
 
                </div>
 
             </div>   	    
 
	    
 
	        <div class="field">
 
	            <div class="label">
 
	                <label>${_('API key')}</label> ${c.user.api_key}
 
	            </div>
 
	        </div>	    
 
	        <div class="fields">
 
	             <div class="field">
 
	                <div class="label">
 
	                    <label for="username">${_('Username')}:</label>
 
	                </div>
 
	                <div class="input">
 
	                    ${h.text('username',class_="medium")}
 
	                </div>
 
	             </div>
 
	            
 
	             <div class="field">
 
	                <div class="label">
 
	                    <label for="new_password">${_('New password')}:</label>
 
	                </div>
 
	                <div class="input">
 
	                    ${h.password('new_password',class_="medium")}
 
	                </div>
 
	             </div>
 
	            
 
	             <div class="field">
 
	                <div class="label">
 
	                    <label for="name">${_('First Name')}:</label>
 
	                </div>
 
	                <div class="input">
 
	                    ${h.text('name',class_="medium")}
 
	                </div>
 
	             </div>
 
	            
 
	             <div class="field">
 
	                <div class="label">
 
	                    <label for="lastname">${_('Last Name')}:</label>
 
	                </div>
 
	                <div class="input">
 
	                    ${h.text('lastname',class_="medium")}
 
	                </div>
 
	             </div>
 
	            
 
	             <div class="field">
 
	                <div class="label">
 
	                    <label for="email">${_('Email')}:</label>
 
	                </div>
 
	                <div class="input">
 
	                    ${h.text('email',class_="medium")}
 
	                </div>
 
	             </div>
 
	            
 
	            <div class="buttons">
 
	              ${h.submit('save','Save',class_="ui-button")}
 
	              ${h.reset('reset','Reset',class_="ui-button")}
 
	            </div>             
 
	    	</div>
 
	    </div>    	
 
    ${h.end_form()}
 
    </div>
 
</div>    
 

	
 
<div class="box box-right">
 
    <!-- box / title -->
 
    <div class="title">
 
        <h5>${_('My repositories')} 
 
        <input class="top-right-rounded-corner top-left-rounded-corner bottom-left-rounded-corner bottom-right-rounded-corner" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
 
        </h5>
 
         %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
         <ul class="links">
 
           <li>
 
             <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
 
           </li>          
 
         </ul>           
 
         %endif        
 
    </div>
 
    <!-- end box / title -->
 
    <div class="table">
 
	    <table>
 
	    <thead>
 
            <tr>
 
            <th class="left">${_('Name')}</th>
 
            <th class="left">${_('revision')}</th>
 
            <th colspan="2" class="left">${_('action')}</th>            
 
	    </thead>
 
	     <tbody>
 
	     %if c.user_repos:
 
		     %for repo in c.user_repos:
 
		        <tr>
 
		            <td>
 
                     %if repo['dbrepo']['repo_type'] =='hg':
 
                       <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
 
                     %elif repo['dbrepo']['repo_type'] =='git':
 
                       <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
 
                     %else:
 
                       
 
                     %endif 		            
 
		             %if repo['dbrepo']['private']:
 
		                <img class="icon" alt="${_('private')}" src="${h.url("/images/icons/lock.png")}"/>
 
		             %else:
 
		                <img class="icon" alt="${_('public')}" src="${h.url("/images/icons/lock_open.png")}"/>
 
		             %endif
 
		                                             
 
		            ${h.link_to(repo['repo'].name, h.url('summary_home',repo_name=repo['repo'].name),class_="repo_name")}
 
		            %if repo['dbrepo_fork']:
 
		            	<a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}">
 
		            	<img class="icon" alt="${_('public')}"
 
		            	title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}" 
 
		            	src="${h.url("/images/icons/arrow_divide.png")}"/></a>
 
		            %endif		            
 
		            </td> 
 
		            <td><span class="tooltip" title="${repo['repo'].last_change}">${("r%s:%s") % (h.get_changeset_safe(repo['repo'],'tip').revision,h.short_id(h.get_changeset_safe(repo['repo'],'tip').raw_id))}</span></td>
 
		            <td><a href="${h.url('repo_settings_home',repo_name=repo['repo'].name)}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url("/images/icons/application_form_edit.png")}"/></a></td>
 
		            <td>
 
	                  ${h.form(url('repo_settings_delete', repo_name=repo['repo'].name),method='delete')}
 
	                    ${h.submit('remove_%s' % repo['repo'].name,'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
 
	                  ${h.end_form()}	            
 
		            </td>
 
		        </tr>
 
		     %endfor
 
	     %else:
 
	     	${_('No repositories yet')} 
 
	     	%if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
	     		${h.link_to(_('create one now'),h.url('admin_settings_create_repository'))}
 
	     	%endif
 
	     %endif
 
	     </tbody>
 
	     </table>
 
    </div>
 
    
 
</div>
 
    <script type="text/javascript">
 
     var D = YAHOO.util.Dom;
 
     var E = YAHOO.util.Event;
 
     var S = YAHOO.util.Selector;
 
     
 
     var q_filter = D.get('q_filter');
 
     var F = YAHOO.namespace('q_filter'); 
 
     
 
     E.on(q_filter,'click',function(){
 
        q_filter.value = '';
 
     });
 

	
 
     F.filterTimeout = null;
 
     
 
     F.updateFilter  = function() { 
 
        // Reset timeout 
 
        F.filterTimeout = null;
 
        
 
        var obsolete = [];
 
        var nodes = S.query('div.table tr td a.repo_name');
 
        var req = D.get('q_filter').value;
 
        for (n in nodes){
 
            D.setStyle(nodes[n].parentNode.parentNode,'display','')
 
        }
 
        if (req){
 
            for (n in nodes){
 
                if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
 
                    obsolete.push(nodes[n]); 
 
                }
 
            }
 
            if(obsolete){
 
                for (n in obsolete){
 
                    D.setStyle(obsolete[n].parentNode.parentNode,'display','none');
 
                }
 
            }
 
        }
 
     }
 
     
 
     E.on(q_filter,'keyup',function(e){
 
         clearTimeout(F.filterTimeout); 
 
         setTimeout(F.updateFilter,600); 
 
     });
 
     
 
    </script>
 
</%def>  
 
\ No newline at end of file
0 comments (0 inline, 0 general)