Mercurial > kallithea
changeset 320:05b212954275
Implemented owner settings, as separete posibility to edit repositry by non administrative owner of repository
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Wed, 30 Jun 2010 15:35:10 +0200 |
parents | c12f4d19c950 |
children | 81e18dc718c7 |
files | pylons_app/config/routing.py pylons_app/controllers/settings.py pylons_app/model/forms.py pylons_app/templates/base/base.html pylons_app/templates/settings/repo_settings.html pylons_app/templates/shortlog/shortlog.html pylons_app/tests/functional/test_settings.py |
diffstat | 7 files changed, 378 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/pylons_app/config/routing.py Tue Jun 29 20:45:35 2010 +0200 +++ b/pylons_app/config/routing.py Wed Jun 30 15:35:10 2010 +0200 @@ -115,4 +115,12 @@ 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pylons_app/controllers/settings.py Wed Jun 30 15:35:10 2010 +0200 @@ -0,0 +1,97 @@ +#!/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))
--- a/pylons_app/model/forms.py Tue Jun 29 20:45:35 2010 +0200 +++ b/pylons_app/model/forms.py Wed Jun 30 15:35:10 2010 +0200 @@ -182,7 +182,15 @@ 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 #=============================================================================== @@ -240,3 +248,18 @@ 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 + + + +
--- a/pylons_app/templates/base/base.html Tue Jun 29 20:45:35 2010 +0200 +++ b/pylons_app/templates/base/base.html Wed Jun 30 15:35:10 2010 +0200 @@ -107,7 +107,7 @@ <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:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pylons_app/templates/settings/repo_settings.html Wed Jun 30 15:35:10 2010 +0200 @@ -0,0 +1,240 @@ +## -*- 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>
--- a/pylons_app/templates/shortlog/shortlog.html Tue Jun 29 20:45:35 2010 +0200 +++ b/pylons_app/templates/shortlog/shortlog.html Wed Jun 30 15:35:10 2010 +0200 @@ -1,3 +1,4 @@ +## -*- coding: utf-8 -*- <%inherit file="/base/base.html"/> <%def name="title()">
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pylons_app/tests/functional/test_settings.py Wed Jun 30 15:35:10 2010 +0200 @@ -0,0 +1,7 @@ +from pylons_app.tests import * + +class TestSettingsController(TestController): + + def test_index(self): + response = self.app.get(url(controller='settings', action='index')) + # Test response...