Changeset - 05b212954275
[Not reviewed]
default
0 4 3
Marcin Kuzminski - 15 years ago 2010-06-30 15:35:10
marcin@python-works.com
Implemented owner settings, as separete posibility to edit repositry by non administrative owner of repository
7 files changed with 377 insertions and 1 deletions:
0 comments (0 inline, 0 general)
pylons_app/config/routing.py
Show inline comments
 
@@ -112,7 +112,15 @@ def make_map(config):
 
    map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
 
                controller='files', action='annotate', revision='tip', f_path='',
 
                conditions=dict(function=check_repo))    
 
    map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
 
                controller='files', action='archivefile', revision='tip',
 
                conditions=dict(function=check_repo))
 
    map.connect('repo_settings_update', '/{repo_name:.*}/settings',
 
                controller='settings', action="update",
 
                conditions=dict(method=["PUT"], function=check_repo))
 
    map.connect('repo_settings_home', '/{repo_name:.*}/settings',
 
                controller='settings', action='index',
 
                conditions=dict(function=check_repo))
 

	
 
    
 
    return map
pylons_app/controllers/settings.py
Show inline comments
 
new file 100644
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# settings controller for pylons
 
# 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 June 30, 2010
 
settings controller for pylons
 
@author: marcink
 
"""
 
from formencode import htmlfill
 
from pylons import tmpl_context as c, request, url
 
from pylons.controllers.util import redirect
 
from pylons.i18n.translation import _
 
from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.lib.utils import invalidate_cache
 
from pylons_app.model.forms import RepoSettingsForm
 
from pylons_app.model.repo_model import RepoModel
 
import formencode
 
import logging
 
import pylons_app.lib.helpers as h
 
log = logging.getLogger(__name__)
 

	
 
class SettingsController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionAllDecorator('repository.admin')           
 
    def __before__(self):
 
        super(SettingsController, self).__before__()
 
        
 
    def index(self, repo_name):
 
        repo_model = RepoModel()
 
        c.repo_info = repo = repo_model.get(repo_name)
 
        if not repo:
 
            h.flash(_('%s repository is not mapped to db perhaps' 
 
                      ' it was created or renamed from the filesystem'
 
                      ' please run the application again'
 
                      ' in order to rescan repositories') % repo_name,
 
                      category='error')
 
        
 
            return redirect(url('repos'))        
 
        defaults = c.repo_info.__dict__
 
        defaults.update({'user':c.repo_info.user.username})
 
        c.users_array = repo_model.get_users_js()
 
        
 
        for p in c.repo_info.repo2perm:
 
            defaults.update({'perm_%s' % p.user.username: 
 
                             p.permission.permission_name})
 
            
 
        return htmlfill.render(
 
            render('settings/repo_settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )  
 

	
 
    def update(self, repo_name):
 
        print request.POST
 
        print 'x' * 110
 
        repo_model = RepoModel()
 
        _form = RepoSettingsForm(edit=True)()
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            repo_model.update(repo_name, form_result)
 
            invalidate_cache('cached_repo_list')
 
            h.flash(_('Repository %s updated succesfully' % repo_name),
 
                    category='success')
 
                           
 
        except formencode.Invalid as errors:
 
            c.repo_info = repo_model.get(repo_name)
 
            c.users_array = repo_model.get_users_js()
 
            errors.value.update({'user':c.repo_info.user.username})
 
            c.form_errors = errors.error_dict
 
            return htmlfill.render(
 
                 render('admin/repos/repo_edit.html'),
 
                defaults=errors.value,
 
                encoding="UTF-8")
 
        except Exception:
 
            h.flash(_('error occured during update of repository %s') \
 
                    % form_result['repo_name'], category='error')
 
                    
 
        return redirect(url('repo_settings_home', repo_name=repo_name))
pylons_app/model/forms.py
Show inline comments
 
@@ -180,12 +180,20 @@ class ValidPerms(formencode.validators.F
 
            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
 
@@ -237,6 +245,21 @@ def RepoForm(edit=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/templates/base/base.html
Show inline comments
 
@@ -104,13 +104,13 @@ def is_current(selected):
 
	            <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li>
 
				<li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li>            
 
	            <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li>
 
	            <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li>
 
	            <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li>
 
				%if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
 
					<li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name))}</li>
 
					<li ${is_current('settings')}>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name))}</li>
 
				%endif					        
 
	        </ul>
 
		%else:
 
		##Root menu
 
			<ul class="page-nav">
 
				<li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li>
pylons_app/templates/settings/repo_settings.html
Show inline comments
 
new file 100644
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Repository settings')}
 
</%def>
 
<%def name="breadcrumbs()">
 
    ${h.link_to(u'Home',h.url('/'))}
 
    / 
 
    ${h.link_to(c.repo_name,h.url('shortlog_home',repo_name=c.repo_name))}
 
    /
 
    ${_('settings')}
 
</%def>
 
<%def name="page_nav()">
 
	${self.menu('settings')}     
 
</%def>
 
<%def name="main()">
 
	<h2 class="no-link no-border">${_('Settings')}</h2>
 
	<div>
 
        ${h.form(url('repo_settings_update', 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>${_('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)}>
 
	        					<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)})">
 
				                  		<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 postData = '_method=delete&user_id='+user_id; 
 
												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">
 
        							${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>   
pylons_app/templates/shortlog/shortlog.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Repository managment')}
 
</%def>
 
<%def name="breadcrumbs()">
pylons_app/tests/functional/test_settings.py
Show inline comments
 
new file 100644
 
from pylons_app.tests import *
 

	
 
class TestSettingsController(TestController):
 

	
 
    def test_index(self):
 
        response = self.app.get(url(controller='settings', action='index'))
 
        # Test response...
0 comments (0 inline, 0 general)