Changeset - cec5cbc956c0
[Not reviewed]
default
0 5 0
Marcin Kuzminski - 15 years ago 2010-07-01 18:10:43
marcin@python-works.com
Repository managment permissions, fixed found bugs updated js, added extra checks for doubled users and non active ones
5 files changed with 16 insertions and 13 deletions:
0 comments (0 inline, 0 general)
pylons_app/model/db.py
Show inline comments
 
from pylons_app.model.meta import Base
 
from sqlalchemy.orm import relation, backref
 
from sqlalchemy import *
 
from vcs.utils.lazy import LazyProperty
 

	
 
class User(Base): 
 
    __tablename__ = 'users'
 
    __table_args__ = {'useexisting':True}
 
    user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
 
    admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
 
    name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
 
    
 
    user_log = relation('UserLog')
 
    
 
    @LazyProperty
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.name, self.lastname, self.email)
 
        
 
    def __repr__(self):
 
        return "<User('%s:%s')>" % (self.user_id, self.username)
 
      
 
class UserLog(Base): 
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'useexisting':True}
 
    user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 
 
    repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
 
    action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
 
    
 
    user = relation('User')
 
    
 
class Repository(Base):
 
    __tablename__ = 'repositories'
 
    __table_args__ = {'useexisting':True}
 
    repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
 
    private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
 
    description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    
 
    user = relation('User')
 
    repo2perm = relation('Repo2Perm', cascade='all')
 
    
 
class Permission(Base):
 
    __tablename__ = 'permissions'
 
    __table_args__ = {'useexisting':True}
 
    permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    
 
    def __repr__(self):
 
        return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
 

	
 
class Repo2Perm(Base):
 
    __tablename__ = 'repo_to_perm'
 
    __table_args__ = (UniqueConstraint('user_id', 'permission_id', 'repository'), {'useexisting':True})
 
    __table_args__ = (UniqueConstraint('user_id', 'repository'), {'useexisting':True})
 
    repo2perm_id = Column("repo2perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
 
    repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None) 
 
    
 
    user = relation('User')
 
    permission = relation('Permission')
 
    
pylons_app/model/forms.py
Show inline comments
 
""" this is forms validation classes
 
http://formencode.org/module-formencode.validators.html
 
for list off all availible validators
 

	
 
we can create our own validators
 

	
 
The table below outlines the options which can be used in a schema in addition to the validators themselves
 
pre_validators          []     These validators will be applied before the schema
 
chained_validators      []     These validators will be applied after the schema
 
allow_extra_fields      False     If True, then it is not an error when keys that aren't associated with a validator are present
 
filter_extra_fields     False     If True, then keys that aren't associated with a validator are removed
 
if_key_missing          NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
 
ignore_key_missing      False     If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already    
 
  
 
  
 
<name> = formencode.validators.<name of validator>
 
<name> must equal form name
 
list=[1,2,3,4,5]
 
for SELECT use formencode.All(OneOf(list), Int())
 
    
 
"""
 
from formencode import All
 
from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
 
    Email, Bool, StringBoolean
 
from pylons import session
 
from pylons.i18n.translation import _
 
from pylons_app.lib.auth import get_crypt_password
 
import pylons_app.lib.helpers as h
 
from pylons_app.model import meta
 
from pylons_app.model.db import User, Repository
 
from sqlalchemy.exc import OperationalError
 
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
 
from webhelpers.pylonslib.secure_form import authentication_token
 
import datetime
 
import formencode
 
import logging
 
log = logging.getLogger(__name__)
 

	
 

	
 
#this is needed to translate the messages using _() in validators
 
class State_obj(object):
 
    _ = staticmethod(_)
 
    
 
#===============================================================================
 
# VALIDATORS
 
#===============================================================================
 
class ValidAuthToken(formencode.validators.FancyValidator):
 
    messages = {'invalid_token':_('Token mismatch')}
 

	
 
    def validate_python(self, value, state):
 

	
 
        if value != authentication_token():
 
            raise formencode.Invalid(self.message('invalid_token', state,
 
                                            search_number=value), value, state)
 
class ValidUsername(formencode.validators.FancyValidator):
 

	
 
    def validate_python(self, value, state):
 
        if value in ['default', 'new_user']:
 
            raise formencode.Invalid(_('Invalid username'), value, state)
 
    
 
class ValidPassword(formencode.validators.FancyValidator):
 
    
 
    def to_python(self, value, state):
 
        return get_crypt_password(value)
 
        
 
class ValidAuth(formencode.validators.FancyValidator):
 
    messages = {
 
            'invalid_password':_('invalid password'),
 
            'invalid_login':_('invalid user name'),
 
            'disabled_account':_('Your acccount is disabled')
 
            
 
            }
 
    #error mapping
 
    e_dict = {'username':messages['invalid_login'],
 
              'password':messages['invalid_password']}
 
    e_dict_disable = {'username':messages['disabled_account']}
 
    
 
    def validate_python(self, value, state):
 
        sa = meta.Session
 
        crypted_passwd = get_crypt_password(value['password'])
 
        username = value['username']
 
        try:
 
            user = sa.query(User).filter(User.username == username).one()
 
        except (NoResultFound, MultipleResultsFound, OperationalError) as e:
 
            log.error(e)
 
            user = None
 
            raise formencode.Invalid(self.message('invalid_password',
 
                                     state=State_obj), value, state,
 
                                     error_dict=self.e_dict)            
 
        if user:
 
            if user.active:
 
                if user.username == username and user.password == crypted_passwd:
 
                    from pylons_app.lib.auth import AuthUser
 
                    auth_user = AuthUser()
 
                    auth_user.username = username
 
                    auth_user.is_authenticated = True
 
                    auth_user.is_admin = user.admin
 
                    auth_user.user_id = user.user_id
 
                    session['hg_app_user'] = auth_user
 
                    session.save()
 
                    log.info('user %s is now authenticated', username)
 
                    
 
                    try:
 
                        user.last_login = datetime.datetime.now()
 
                        sa.add(user)
 
                        sa.commit()                        
 
                    except (OperationalError) as e:
 
                        log.error(e)
 
                        sa.rollback()
 
                    
 
                    return value
 
                else:
 
                    log.warning('user %s not authenticated', username)
 
                    raise formencode.Invalid(self.message('invalid_password',
 
                                             state=State_obj), value, state,
 
                                             error_dict=self.e_dict)
 
            else:
 
                log.warning('user %s is disabled', username)
 
                raise formencode.Invalid(self.message('disabled_account',
 
                                         state=State_obj),
 
                                         value, state,
 
                                         error_dict=self.e_dict_disable)
 
            
 
            
 
class ValidRepoUser(formencode.validators.FancyValidator):
 
            
 
    def to_python(self, value, state):
 
        sa = meta.Session
 
        try:
 
            self.user_db = sa.query(User).filter(User.username == value).one()
 
            self.user_db = sa.query(User)\
 
                .filter(User.active == True)\
 
                .filter(User.username == value).one()
 
        except Exception:
 
            raise formencode.Invalid(_('This username is not valid'),
 
                                     value, state)
 
        return self.user_db.user_id
 

	
 
def ValidRepoName(edit=False):    
 
    class _ValidRepoName(formencode.validators.FancyValidator):
 
            
 
        def to_python(self, value, state):
 
            slug = h.repo_name_slug(value)
 
            if slug in ['_admin']:
 
                raise formencode.Invalid(_('This repository name is disallowed'),
 
                                         value, state)
 
            sa = meta.Session
 
            if sa.query(Repository).get(slug) and not edit:
 
                raise formencode.Invalid(_('This repository already exists'),
 
                                         value, state)
 
                        
 
            return slug 
 
    return _ValidRepoName
 

	
 
class ValidPerms(formencode.validators.FancyValidator):
 
    messages = {'perm_new_user_name':_('This username is not valid')}
 
    
 
    def to_python(self, value, state):
 
        perms_update = []
 
        perms_new = []
 
        #build a list of permission to update and new permission to create
 
        for k, v in value.items():
 
            if k.startswith('perm_'):
 
                if  k.startswith('perm_new_user'):
 
                    new_perm = value.get('perm_new_user', False)
 
                    new_user = value.get('perm_new_user_name', False)
 
                    if new_user and new_perm:
 
                        if (new_user, new_perm) not in perms_new:
 
                            perms_new.append((new_user, new_perm))
 
                else:
 
                    usr = k[5:]                    
 
                    if usr == 'default':
 
                        if value['private']:
 
                            #set none for default when updating to private repo
 
                            v = 'repository.none'
 
                    perms_update.append((usr, v))
 
        value['perms_updates'] = perms_update
 
        value['perms_new'] = perms_new
 
        sa = meta.Session
 
        for k, v in perms_new:
 
            try:
 
                self.user_db = sa.query(User).filter(User.username == k).one()
 
                self.user_db = sa.query(User)\
 
                    .filter(User.active == True)\
 
                    .filter(User.username == k).one()
 
            except Exception:
 
                msg = self.message('perm_new_user_name',
 
                                     state=State_obj)
 
                raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})            
 
        return value
 
    
 
class ValidSettings(formencode.validators.FancyValidator):
 
    
 
    def to_python(self, value, state):
 
        #settings  form can't edit user
 
        if value.has_key('user'):
 
            del['value']['user']
 
        
 
        return value                
 
#===============================================================================
 
# FORMS        
 
#===============================================================================
 
class LoginForm(formencode.Schema):
 
    allow_extra_fields = True
 
    filter_extra_fields = True
 
    username = UnicodeString(
 
                             strip=True,
 
                             min=3,
 
                             not_empty=True,
 
                             messages={
 
                                       'empty':_('Please enter a login'),
 
                                       'tooShort':_('Enter a value %(min)i characters long or more')}
 
                            )
 

	
 
    password = UnicodeString(
 
                            strip=True,
 
                            min=3,
 
                            not_empty=True,
 
                            messages={
 
                                      'empty':_('Please enter a password'),
 
                                      'tooShort':_('Enter a value %(min)i characters long or more')}
 
                                )
 

	
 

	
 
    #chained validators have access to all data
 
    chained_validators = [ValidAuth]
 
    
 
def UserForm(edit=False):
 
    class _UserForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername)
 
        if edit:
 
            new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
 
        else:
 
            password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
 
        active = StringBoolean(if_missing=False)
 
        name = UnicodeString(strip=True, min=3, not_empty=True)
 
        lastname = UnicodeString(strip=True, min=3, not_empty=True)
 
        email = Email(not_empty=True)
 
        
 
    return _UserForm
 

	
 
def RepoForm(edit=False):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
 
        description = UnicodeString(strip=True, min=3, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 
        
 
        if edit:
 
            user = All(Int(not_empty=True), ValidRepoUser)
 
        
 
        chained_validators = [ValidPerms]
 
    return _RepoForm
 

	
 
def RepoSettingsForm(edit=False):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
 
        description = UnicodeString(strip=True, min=3, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 
        
 
        chained_validators = [ValidPerms, ValidSettings]
 
    return _RepoForm
 

	
 

	
 

	
 

	
pylons_app/model/repo_model.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# model for handling repositories actions
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
# 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.
 

	
 
"""
 
Created on Jun 5, 2010
 
model for handling repositories actions
 
@author: marcink
 
"""
 
from pylons_app.model.meta import Session
 
from pylons_app.model.db import Repository, Repo2Perm, User, Permission
 
import shutil
 
import os
 
from datetime import datetime
 
from pylons_app.lib.utils import check_repo
 
from pylons import app_globals as g
 
import traceback
 
import logging
 
log = logging.getLogger(__name__)
 

	
 
class RepoModel(object):
 
    
 
    def __init__(self):
 
        self.sa = Session()
 
    
 
    def get(self, id):
 
        return self.sa.query(Repository).get(id)
 
        
 
    def get_users_js(self):
 
        
 
        users = self.sa.query(User).all()
 
        users = self.sa.query(User).filter(User.active == True).all()
 
        u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
 
        users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
 
                                                    u.lastname, u.username) 
 
                                        for u in users])
 
        return users_array        
 
        
 
    
 
    def update(self, repo_id, form_data):
 
        try:
 
            if repo_id != form_data['repo_name']:
 
                self.__rename_repo(repo_id, form_data['repo_name'])
 
            cur_repo = self.sa.query(Repository).get(repo_id)
 
            for k, v in form_data.items():
 
                if k == 'user':
 
                    cur_repo.user_id = v
 
                else:
 
                    setattr(cur_repo, k, v)
 
            
 
            #update permissions
 
            for username, perm in form_data['perms_updates']:
 
                r2p = self.sa.query(Repo2Perm)\
 
                        .filter(Repo2Perm.user == self.sa.query(User)\
 
                                .filter(User.username == username).one())\
 
                        .filter(Repo2Perm.repository == repo_id).one()
 
                
 
                r2p.permission_id = self.sa.query(Permission).filter(
 
                                                Permission.permission_name == 
 
                                                perm).one().permission_id
 
                self.sa.add(r2p)
 
            
 
            for username, perm in form_data['perms_new']:
 
                r2p = Repo2Perm()
 
                r2p.repository = repo_id
 
                r2p.user = self.sa.query(User)\
 
                                .filter(User.username == username).one()
 
                
 
                r2p.permission_id = self.sa.query(Permission).filter(
 
                                                Permission.permission_name == 
 
                                                perm).one().permission_id
 
                self.sa.add(r2p)
 
                                    
 
            self.sa.add(cur_repo)
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise    
 
    
 
    def create(self, form_data, cur_user, just_db=False):
 
        try:
 
            repo_name = form_data['repo_name']
 
            new_repo = Repository()
 
            for k, v in form_data.items():
 
                setattr(new_repo, k, v)
 
                
 
            new_repo.user_id = cur_user.user_id
 
            self.sa.add(new_repo)
 

	
 
            #create default permission
 
            repo2perm = Repo2Perm()
 
            default_perm = 'repository.none' if form_data['private'] \
 
                                                        else 'repository.read'
 
            repo2perm.permission_id = self.sa.query(Permission)\
 
                    .filter(Permission.permission_name == default_perm)\
 
                    .one().permission_id
 
                        
 
            repo2perm.repository = repo_name
 
            repo2perm.user_id = self.sa.query(User)\
 
                    .filter(User.username == 'default').one().user_id 
 
            
 
            self.sa.add(repo2perm)
 
            self.sa.commit()
 
            if not just_db:
 
                self.__create_repo(repo_name)
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise    
 
                     
 
    def delete(self, repo):
 
        try:
 
            self.sa.delete(repo)
 
            self.sa.commit()
 
            self.__delete_repo(repo.repo_name)
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 
    def delete_perm_user(self, form_data, repo_name):
 
        try:
 
            r2p = self.sa.query(Repo2Perm).filter(Repo2Perm.repository == repo_name)\
 
            .filter(Repo2Perm.user_id == form_data['user_id']).delete()
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            self.sa.rollback()
 
            raise
 
           
 
    def __create_repo(self, repo_name):        
 
        repo_path = os.path.join(g.base_path, repo_name)
 
        if check_repo(repo_name, g.base_path):
 
            log.info('creating repo %s in %s', repo_name, repo_path)
 
            from vcs.backends.hg import MercurialRepository
 
            MercurialRepository(repo_path, create=True)
 

	
 
    def __rename_repo(self, old, new):
 
        log.info('renaming repoo from %s to %s', old, new)
 
        old_path = os.path.join(g.base_path, old)
 
        new_path = os.path.join(g.base_path, new)
 
        shutil.move(old_path, new_path)
 
    
 
    def __delete_repo(self, name):
 
        rm_path = os.path.join(g.base_path, name)
 
        log.info("Removing %s", rm_path)
 
        #disable hg 
 
        shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
 
        #disable repo
 
        shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
 
                                          % (datetime.today(), name)))
pylons_app/public/css/monoblue_custom.css
Show inline comments
 
/*** Initial Settings ***/
 
#mainhtml {
 
	margin: 15px 50px;
 
	background: #DBD4C6;
 
	font-family: sans-serif;
 
}
 

	
 
#mainhtml .breadcrumbs a:HOVER {
 
	text-decoration: underline;
 
}
 

	
 
a {
 
	color: #556CB5;
 
	text-decoration: none;
 
}
 

	
 
a:HOVER {
 
	text-decoration: underline;
 
}
 

	
 
/*** end of Initial Settings ***/ 
 

	
 
/*** ***/
 
.table_disp {
 
	border-left: 0px solid #666666;
 
	border-bottom: 1px solid #666666;
 
	border-right: 1px solid #666666;
 
	padding: 0px;
 
	margin: 0px;
 
	border-spacing: 0px;
 
}
 

	
 
.table_disp .header {
 
	border-top: 1px solid #666666;
 
	background-color: #556CB5;
 
	font-weight: bold;
 
	color: white;
 
	vertical-align: middle;
 
	padding: 3px 5px;
 
	text-align: left;
 
	font-size: 0.9em;
 
}
 

	
 
.table_disp .header td {
 
	padding: 4px;
 
	vertical-align: middle;
 
	border-top: 1px solid #AAAAAA;
 
	border-bottom: 2px solid #666666;
 
}
 

	
 
.table_disp td {
 
	border-left: 1px solid #AAAAAA;
 
	padding-left: 4px;
 
	padding-right: 4px;
 
}
 

	
 
table tr.parity0:hover,table tr.parity1:hover {
 
	background: #D5E1E6;
 
}
 

	
 
table tr.parity0 {
 
	background: #EAEAE9;
 
}
 

	
 
table tr.parity1 {
 
	background: #FFFFFF;
 
}
 

	
 
/*** ***/ 
 

	
 
/** COMMON SETTINGS **/
 
.add_icon {
 
	background: url("/images/icons/add.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
.edit_icon {
 
	background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
.delete_icon {
 
	background: url("/images/icons/delete.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
.action_button {
 
	border: 0px;
 
	display: block;
 
}
 

	
 
.action_button:hover {
 
	border: 0px;
 
	font-style: italic;
 
	cursor: pointer;
 
}
 

	
 
.flash_msg ul {
 
	margin: 0;
 
	padding: 25px 0px 0px 0px;
 
}
 

	
 
.error_msg {
 
	background-color: #FFCFCF;
 
	background-image: url("/images/icons/error_msg.png");
 
	border: 1px solid #FF9595;
 
	color: #CC3300;
 
}
 

	
 
.warning_msg {
 
	background-color: #FFFBCC;
 
	background-image: url("/images/icons/warning_msg.png");
 
	border: 1px solid #FFF35E;
 
	color: #C69E00;
 
}
 

	
 
.success_msg {
 
	background-color: #D5FFCF;
 
	background-image: url("/images/icons/success_msg.png");
 
	border: 1px solid #97FF88;
 
	color: #009900;
 
}
 

	
 
.notice_msg {
 
	background-color: #DCE3FF;
 
	background-image: url("/images/icons/notice_msg.png");
 
	border: 1px solid #93A8FF;
 
	color: #556CB5;
 
}
 

	
 
.success_msg,.error_msg,.notice_msg,.warning_msg {
 
	background-position: 10px center;
 
	background-repeat: no-repeat;
 
	font-size: 12px;
 
	font-weight: bold;
 
	min-height: 14px;
 
	line-height: 14px;
 
	margin-bottom: 0px;
 
	margin-top: 0px;
 
	padding: 3px 10px 3px 40px;
 
	display: block;
 
	overflow: auto;
 
}
 

	
 
#msg_close {
 
	background: transparent url("icons/cross_grey_small.png") no-repeat
 
		scroll 0 0;
 
	cursor: pointer;
 
	height: 16px;
 
	position: absolute;
 
	right: 5px;
 
	top: 5px;
 
	width: 16px;
 
}
 

	
 
.error-message {
 
	color: #CC3300;
 
}
 

	
 
/**** TOOLTIP ****/
 
.yui-overlay,.yui-panel-container {
 
	visibility: hidden;
 
	position: absolute;
 
	z-index: 2;
 
}
 

	
 
.yui-tt {
 
	visibility: hidden;
 
	position: absolute;
 
	color: #666666;
 
	background-color: #FFFFFF;
 
	font-family: arial, helvetica, verdana, sans-serif;
 
	padding: 8px;
 
	border: 2px solid #556CB5;
 
	font: 100% sans-serif;
 
	width: auto;
 
	opacity: 1.0;
 
}
 

	
 
.yui-tt-shadow {
 
	display: none;
 
}
 

	
 
/** END TOOLTIP **/ 
 

	
 
/** AUTOCOMPLETE **/ 
 

	
 
.ac{
 
	vertical-align: top;
 

	
 
}
 
.ac .match {
 
    font-weight:bold;
 
}
 

	
 
.ac .yui-ac {
 
	position: relative;
 
	font-family: arial;
 
	font-size: 100%;
 
}
 

	
 
.ac #perm_ac{
 
.ac .perm_ac{
 
	width:15em;
 
}
 
/* styles for input field */
 
.ac .yui-ac-input {
 
	position: absolute;
 
	width: 100%;
 
}
 

	
 
/* styles for results container */
 
.ac .yui-ac-container {
 
	position: absolute;
 
	top: 1.6em;
 
	width: 100%;
 
}
 

	
 
/* styles for header/body/footer wrapper within container */
 
.ac .yui-ac-content {
 
	position: absolute;
 
	width: 100%;
 
	border: 1px solid #808080;
 
	background: #fff;
 
	overflow: hidden;
 
	z-index: 9050;
 
}
 

	
 
/* styles for container shadow */
 
.ac .yui-ac-shadow {
 
	position: absolute;
 
	margin: .3em;
 
	width: 100%;
 
	background: #000;
 
	-moz-opacity: 0.10;
 
	opacity: .10;
 
	filter: alpha(opacity = 10);
 
	z-index: 9049;
 
}
 

	
 
/* styles for results list */
 
.ac .yui-ac-content ul {
 
	margin: 0;
 
	padding: 0;
 
	width: 100%;
 
}
 

	
 
/* styles for result item */
 
.ac .yui-ac-content li {
 
	margin: 0;
 
	padding: 2px 5px;
 
	cursor: default;
 
	white-space: nowrap;
 
}
 

	
 
/* styles for prehighlighted result item */
 
.ac .yui-ac-content li.yui-ac-prehighlight {
 
	background: #B3D4FF;
 
}
 

	
 
/* styles for highlighted result item */
 
.ac .yui-ac-content li.yui-ac-highlight {
 
	background: #556CB5;
 
	color: #FFF;
 
}
 

	
 
/** END AUTOCOMPLETE **/
 
div#main {
 
	padding: 5px;
 
}
 

	
 
div#container {
 
	background: #FFFFFF;
 
	position: relative;
 
	color: #666;
 
}
 

	
 
div.page-header {
 
	padding: 50px 20px 0;
 
	background: #556cb5 top left repeat-x;
 
	position: relative;
 
}
 

	
 
div.page-header h1 {
 
	margin: 10px 0 30px;
 
	font-size: 1.8em;
 
	font-weight: bold;
 
	font-family: sans-serif;
 
	letter-spacing: 1px;
 
	color: #FFFFFF;
 
}
 

	
 
div.page-header h1 a {
 
	font-weight: bold;
 
	color: #FFFFFF;
 
}
 

	
 
div.page-header a {
 
	text-decoration: none;
 
}
 

	
 
div.page-header form {
 
	position: absolute;
 
	margin-bottom: 2px;
 
	bottom: 0;
 
	right: 20px;
 
}
 

	
 
div.page-header form label {
 
	color: #DDD;
 
}
 

	
 
div.page-header form input {
 
	padding: 2px;
 
	border: solid 1px #DDD;
 
}
 

	
 
div.page-header form dl {
 
	overflow: hidden;
 
}
 

	
 
div.page-header form dl dt {
 
	font-size: 1.2em;
 
}
 

	
 
div.page-header form dl dt,div.page-header form dl dd {
 
	margin: 0 0 0 5px;
 
	float: left;
 
	height: 24px;
 
	line-height: 20px;
 
}
 

	
 
ul.page-nav {
 
	margin: 10px 0 0 0;
 
	list-style-type: none;
 
	overflow: hidden;
 
	width: 800px;
 
	padding: 0;
 
}
 

	
 
ul.page-nav li {
 
	margin: 0 4px 0 0;
 
	float: left;
 
	height: 24px;
 
	font-size: 1.1em;
 
	line-height: 24px;
 
	text-align: center;
 
	background: #556CB5;
 
}
 

	
 
ul.page-nav li.current {
 
	background: #FFF;
 
	padding-right: 5px;
 
	padding-left: 5px;
 
}
 

	
 
ul.page-nav li.current a {
 
	color: #556CB5;
 
}
 

	
 
ul.page-nav li a {
 
	height: 24px;
 
	color: #FFF;
 
	padding-right: 5px;
 
	padding-left: 5px;
 
	display: block;
 
	text-decoration: none;
 
	font-weight: bold;
 
}
 

	
 
ul.page-nav li.logout a {
 
	color: #FDAC9D;
 
}
 

	
 
ul.page-nav li a:hover {
 
	background: #FFF;
 
	color: #556CB5;
 
}
 

	
 
ul.submenu {
 
	margin: 5px 0px -20px 0px;
 
	list-style-type: none;
 
}
 

	
 
ul.submenu li {
 
	margin: 0 10px 0 0;
 
	font-size: 0.9em;
 
	font-weight: bold;
 
	display: inline;
 
}
 

	
 
ul.submenu .repos {
 
	background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
ul.submenu .users {
 
	background: url("/images/icons/user_edit.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
ul.submenu .permissions {
 
	background: url("/images/icons/folder_key.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 
ul.submenu .settings {
 
	background: url("/images/icons/cog.png") no-repeat scroll 3px;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
ul.submenu .current_submenu {
 
	border-bottom: 2px solid #556CB5;
 
}
 

	
 
h2 {
 
	margin: 20px 0 10px;
 
	height: 30px;
 
	line-height: 30px;
 
	text-indent: 20px;
 
	background: #FFF;
 
	font-size: 1.2em;
 
	border-top: dotted 1px #D5E1E6;
 
	font-weight: bold;
 
	color: #556CB5;
 
}
 

	
 
h2.no-link {
 
	color: #006699;
 
}
 

	
 
h2.no-border {
 
	color: #FFF;
 
	background: #556CB5;
 
	border: 0;
 
}
 

	
 
h2 a {
 
	font-weight: bold;
 
	color: #006699;
 
}
 

	
 
div.page-path {
 
	text-align: right;
 
	padding: 20px 30px 10px 0;
 
	border: solid #d9d8d1;
 
	border-width: 0px 0px 1px;
 
	font-size: 1.2em;
 
}
 

	
 
div.page-footer {
 
	margin: 50px 0 0;
 
	position: relative;
 
	text-align: center;
 
	font-weight: bold;
 
	font-size: 90%;
 
}
 

	
 
div.page-footer p {
 
	position: relative;
 
	left: 20px;
 
	bottom: 5px;
 
	font-size: 1.2em;
 
}
 

	
 
ul.rss-logo {
 
	position: absolute;
 
	top: -10px;
 
	right: 20px;
 
	height: 20px;
 
	list-style-type: none;
 
}
 

	
 
ul.rss-logo li {
 
	display: inline;
 
}
 

	
 
ul.rss-logo li a {
 
	padding: 3px 6px;
 
	line-height: 10px;
 
	border: 1px solid;
 
	border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
 
	color: #ffffff;
 
	background-color: #ff6600;
 
	font-weight: bold;
 
	font-family: sans-serif;
 
	font-size: 10px;
 
	text-align: center;
 
	text-decoration: none;
 
}
 

	
 
div.rss-logo li a:hover {
 
	background-color: #ee5500;
 
}
 

	
 
p.normal {
 
	margin: 20px 0 20px 30px;
 
	font-size: 1.2em;
 
}
 

	
 
span.logtags span {
 
	background-repeat: no-repeat;
 
	height: 16px;
 
	padding-left: 20px;
 
	padding-top: 0px;
 
	text-align: left;
 
	font-weight: bold;
 
}
 

	
 
span.logtags span.tagtag {
 
	background-image: url("/images/icons/tag_green.png");
 
}
 

	
 
span.logtags span.branchtag {
 
	background-image: url("/images/icons/arrow_branch.png");
 
	color: #628F53;
 
}
 

	
 
span.logtags span.inbranchtag {
 
	background-image: url("/images/icons/arrow_branch.png");
 
}
 

	
 
div.diff pre {
 
	margin: 10px 0 0 0;
 
}
 

	
 
div.diff pre span {
 
	font-family: monospace;
 
	white-space: pre;
 
	font-size: 1.2em;
 
	padding: 3px 0;
 
}
 

	
 
td.source {
 
	white-space: pre;
 
	font-family: monospace;
 
	margin: 10px 30px 0;
 
	font-size: 1.2em;
 
	font-family: monospace;
 
}
 

	
 
div.source div.parity0,div.source div.parity1 {
 
	padding: 1px;
 
	font-size: 1.2em;
 
}
 

	
 
div.source div.parity0 {
 
	background: #F1F6F7;
 
}
 

	
 
div.source div.parity1 {
 
	background: #FFFFFF;
 
}
 

	
 
div.parity0:hover,div.parity1:hover {
 
	background: #D5E1E6;
 
}
 

	
 
.linenr {
 
	color: #999;
 
	text-align: right;
 
}
 

	
 
.lineno {
 
	text-align: right;
 
}
 

	
 
.lineno a {
 
	color: #999;
 
}
 

	
 
td.linenr {
 
	width: 60px;
 
}
 

	
 
div#powered-by {
pylons_app/templates/admin/repos/repo_edit.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Repositories administration')}
 
</%def>
 
<%def name="breadcrumbs()">
 
	${h.link_to(u'Admin',h.url('admin_home'))}
 
	 /  
 
	 ${_('Repos')}
 
</%def>
 
<%def name="page_nav()">
 
	${self.menu('admin')}
 
	${self.submenu('repos')}
 
</%def>
 
<%def name="main()">
 
	<div>
 
        <h2>${_('Repositories')} - ${_('edit')} "${c.repo_name}"</h2>
 
        ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
 
        <table>
 
        	<tr>
 
        		<td>${_('Name')}</td>
 
        		<td>${h.text('repo_name',size="28")}</td>
 
        		<td>${self.get_form_error('repo_name')}</td>
 
        	</tr>
 
        	<tr>
 
        		<td>${_('Description')}</td>
 
        		<td>${h.textarea('description',cols=32,rows=5)}</td>
 
        		<td>${self.get_form_error('description')}</td>
 
        	</tr>
 
        	<tr>
 
        		<td>${_('Private')}</td>
 
        		<td>${h.checkbox('private',value="True")}</td>
 
        		<td>${self.get_form_error('private')}</td>
 
        	</tr>
 
        	<tr>
 
        		<td>${_('Owner')}</td>
 
				<td class='ac'>
 
					<div id="perm_ac">
 
					<div class="perm_ac">
 
						${h.text('user',class_='yui-ac-input')}
 
						<div id="owner_container"></div>
 
					</div>
 
				</td>        		
 
        		<td>${self.get_form_error('user')}</td>
 
        	</tr>
 
        	<tr>
 
        		<td>${_('Permissions')}</td>
 
        		<td>
 
        			<table>
 
        				<tr>
 
        					<td>${_('none')}</td>
 
        					<td>${_('read')}</td>
 
        					<td>${_('write')}</td>
 
        					<td>${_('admin')}</td>
 
        					<td>${_('user')}</td>
 
        				</tr>
 
        				
 
        				%for r2p in c.repo_info.repo2perm:
 
        					%if r2p.user.username =='default' and c.repo_info.private:
 
        						<tr>
 
									<td colspan="4">
 
										<span style="font-size: 0.8em">${_('disabled for private repository')}</span></td>
 
									<td>${r2p.user.username}</td>
 
								</tr>
 
							%else:
 
	        				<tr id=${id(r2p.user.username)}>
 
	        				<tr id="id${id(r2p.user.username)}">
 
	        					<td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
 
	        					<td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
 
	        					<td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
 
	        					<td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
 
	        					<td>${r2p.user.username}</td>
 
	        					<td>
 
	        					  %if r2p.user.username !='default':
 
				                  	<span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},${id(r2p.user.username)})">
 
				                  	<span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
 
				                  		<script type="text/javascript">
 
											function ajaxAction(user_id,field_id){
 
												var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
 
												var callback = { success:function(o){
 
																YAHOO.util.Dom.get(String(field_id)).innerHTML = '<td colspan="6"></td>';
 
															 }};
 
												var tr = YAHOO.util.Dom.get(String(field_id));
 
												tr.parentNode.removeChild(tr);}};
 
												var postData = '_method=delete&user_id='+user_id; 
 
												var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); 
 
						                	};
 
												var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
 
										</script>       	
 
				                  	</span>
 
				                  %endif					
 
	        					</td>
 
	        				</tr>
 
	        				%endif
 
						%endfor
 
						<%
 
							if not hasattr(c,'form_errors'):
 
								d = 'display:none;'
 
							else:
 
								d=''
 
						%>
 

	
 
        				<tr id="add_perm_input" style="${d}">
 
        					<td>${h.radio('perm_new_user','repository.none')}</td>
 
        					<td>${h.radio('perm_new_user','repository.read')}</td>
 
        					<td>${h.radio('perm_new_user','repository.write')}</td>
 
        					<td>${h.radio('perm_new_user','repository.admin')}</td>
 
        					<td class='ac'>
 
        						<div id="perm_ac">
 
        						<div class="perm_ac" id="perm_ac">
 
        							${h.text('perm_new_user_name',class_='yui-ac-input')}
 
									<div id="perm_container"></div>
 
        						</div>
 
        					</td>
 
        					<td>${self.get_form_error('perm_new_user_name')}</td>     					
 
        				</tr>
 
        				<tr>
 
        					<td colspan="4">
 
        						<span id="add_perm" class="add_icon" style="cursor: pointer;">
 
        						${_('Add another user')}
 
        						</span>
 
        					</td>
 
        				</tr>
 
        			</table>
 
        		</td>
 
        		
 
        	</tr>
 
        	<tr>
 
        		<td></td>
 
        		<td>${h.submit('update','update')}</td>
 
        	</tr>
 
        	        	        	
 
        </table>
 
        ${h.end_form()}
 
        <script type="text/javascript">
 
        	YAHOO.util.Event.onDOMReady(function(){
 
				var D = YAHOO.util.Dom;
 
				YAHOO.util.Event.addListener('add_perm','click',function(){
 
					D.setStyle('add_perm_input','display','');
 
					D.setStyle('add_perm','opacity','0.6');
 
					D.setStyle('add_perm','cursor','default');
 
				});
 
            });
 
        </script>
 
		<script type="text/javascript">    
 
		YAHOO.example.FnMultipleFields = function(){
 
		    var myContacts = ${c.users_array|n}
 
		    
 
		    // Define a custom search function for the DataSource
 
		    var matchNames = function(sQuery) {
 
		        // Case insensitive matching
 
		        var query = sQuery.toLowerCase(),
 
		            contact,
 
		            i=0,
 
		            l=myContacts.length,
 
		            matches = [];
 
		        
 
		        // Match against each name of each contact
 
		        for(; i<l; i++) {
 
		            contact = myContacts[i];
 
		            if((contact.fname.toLowerCase().indexOf(query) > -1) ||
 
		                (contact.lname.toLowerCase().indexOf(query) > -1) ||
 
		                (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
 
		                matches[matches.length] = contact;
 
		            }
 
		        }
 
		
 
		        return matches;
 
		    };
 
		
 
		    // Use a FunctionDataSource
 
		    var oDS = new YAHOO.util.FunctionDataSource(matchNames);
 
		    oDS.responseSchema = {
 
		        fields: ["id", "fname", "lname", "nname"]
 
		    }
 
		
 
		    // Instantiate AutoComplete for perms
 
		    var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
 
		    oAC_perms.useShadow = false;
 
		    oAC_perms.resultTypeList = false;
 
		    
 
		    // Instantiate AutoComplete for owner
 
		 	var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
 
		 	oAC_owner.useShadow = false;
 
		 	oAC_owner.resultTypeList = false;
 
		    
 
		    
 
		    // Custom formatter to highlight the matching letters
 
		    var custom_formatter = function(oResultData, sQuery, sResultMatch) {
 
		        var query = sQuery.toLowerCase(),
 
		            fname = oResultData.fname,
 
		            lname = oResultData.lname,
 
		            nname = oResultData.nname || "", // Guard against null value
 
		            query = sQuery.toLowerCase(),
 
		            fnameMatchIndex = fname.toLowerCase().indexOf(query),
 
		            lnameMatchIndex = lname.toLowerCase().indexOf(query),
 
		            nnameMatchIndex = nname.toLowerCase().indexOf(query),
 
		            displayfname, displaylname, displaynname;
 
		            
 
		        if(fnameMatchIndex > -1) {
 
		            displayfname = highlightMatch(fname, query, fnameMatchIndex);
 
		        }
 
		        else {
 
		            displayfname = fname;
 
		        }
 
		
 
		        if(lnameMatchIndex > -1) {
 
		            displaylname = highlightMatch(lname, query, lnameMatchIndex);
 
		        }
 
		        else {
 
		            displaylname = lname;
 
		        }
 
		
 
		        if(nnameMatchIndex > -1) {
 
		            displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
 
		        }
 
		        else {
 
		            displaynname = nname ? "(" + nname + ")" : "";
 
		        }
 
		
 
		        return displayfname + " " + displaylname + " " + displaynname;
 
		        
 
		    };
 
		    oAC_perms.formatResult = custom_formatter; 
 
		    oAC_owner.formatResult = custom_formatter;
 
		    			    
 
		    // Helper function for the formatter
 
		    var highlightMatch = function(full, snippet, matchindex) {
 
		        return full.substring(0, matchindex) + 
 
		                "<span class='match'>" + 
 
		                full.substr(matchindex, snippet.length) + 
 
		                "</span>" +
 
		                full.substring(matchindex + snippet.length);
 
		    };
 
		
 
		    var myHandler = function(sType, aArgs) {
 
		        var myAC = aArgs[0]; // reference back to the AC instance
 
		        var elLI = aArgs[1]; // reference to the selected LI element
 
		        var oData = aArgs[2]; // object literal of selected item's result data
 
		        myAC.getInputEl().value = oData.nname;
 
		    };
 

	
 
		    oAC_perms.itemSelectEvent.subscribe(myHandler);
 
		    oAC_owner.itemSelectEvent.subscribe(myHandler);
 
		    
 
		    return {
 
		        oDS: oDS,
 
		        oAC_perms: oAC_perms,
 
		        oAC_owner: oAC_owner, 
 
		    };
 
		}();
 
		    
 
		</script>        
 
    </div>
 
</%def>   
0 comments (0 inline, 0 general)