#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2009 Zuza Software Foundation # # This file is part of Pootle. # # Pootle is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # Pootle 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 Pootle; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from django import forms from django.forms.formsets import formset_factory, BaseFormSet from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe from django.shortcuts import render_to_response from django.template import RequestContext from pootle_app.models.profile import PootleProfile from pootle_app.models.permissions import get_pootle_permissions, PermissionSet, get_matching_permissions from pootle_app.views.language import navbar_dict from pootle_app.views.language import search_forms from pootle_app.views.admin import util class PermissionSetForm(forms.Form): """A PermissionSetForm represents a PermissionSet to the user. This form will be used in a formset, PermissionSetFormSet. This explains some of the more perculiar code in here. In t""" id = forms.IntegerField(required=False, widget=forms.HiddenInput) profiles = forms.ChoiceField(required=False) permissions = forms.MultipleChoiceField(required=False, widget=forms.SelectMultiple) delete = forms.BooleanField(required=False) def __init__(self, *args, **kwargs): super(PermissionSetForm, self).__init__(*args, **kwargs) # permission_choices = [(codename, _(permission.name)) for codename, permission in get_pootle_permissions().iteritems()] # Used to render the correct choices self['permissions'].field.widget.choices = permission_choices # Used during validation to ensure that valid choices were selected self['permissions'].field.choices = permission_choices profile_choices = self.initial['profile_data'] # Used to render the correct choices self['profiles'].field.widget.choices = profile_choices # Used during validation to ensure that valid choices were selected self['profiles'].field.choices = profile_choices # Remove 'profile_data' lest we have to deal with weird an # unexpected complaints from Django's data validation # machinery. del self.initial['profile_data'] def as_table(self): params = {'id': self['id'].as_widget(), 'permissions': self['permissions'].as_widget()} if not self.is_new_user(): params.update({ 'username': self.initial['username'], 'profiles': self['profiles'].as_hidden(), 'delete': self['delete'].as_widget()}) else: params.update({ 'delete': self['delete'].as_hidden(),}) if 'new' in self.initial: params.update({ 'username': '', 'profiles': self['profiles'].as_widget() }) else: params.update({ 'username': self.initial['username'], 'profiles': self['profiles'].as_hidden() }) return mark_safe('%(id)s%(username)s%(profiles)s%(permissions)s%(delete)s' % params) def is_new_user(self): return self.initial['id'] is None #def clean(self): # if self.is_new_user() and len(self.changed_data) > 0: # return self['profiles'] != u'None' # else: # return True class BasePermissionSetFormSet(BaseFormSet): def as_table(self): return "%s%s%s%s" % ( _("Username"), _("Permissions"), _("Delete"), super(BasePermissionSetFormSet, self).as_table()) # See the comments in PermissionSetForm We don't want the # formset_factory to create empty extra forms. If extra > 0, then the # associated forms will not receive any initial data (i.e. the data # passed by the 'initial' parameter when creating a formset; for # example, # PermissionSetFormSet(initial=get_permission_data(translation_project))). # This is problematic for us, since we need to pass the choices to be # used in the dropdown boxes and selection boxes to the forms. The way # that I chose to do this was to pass the choice data in via the # initial parameter. This is then used in PermissionSetForm.__init__ # to set the choices of the choice widgets. In other words, we want # extra==0, so that we can pass data to each form in the formset. PermissionSetFormSet = formset_factory(PermissionSetForm, BasePermissionSetFormSet, extra=0) def get_id(permission_set, profile_dict): if permission_set.profile in profile_dict: return permission_set.id else: return None def get_permission_data(directory): # Get all the PermissionSet objects associated with the current directory permission_sets = PermissionSet.objects.filter(directory=directory) profile_permission_sets = dict((permission_set.profile, permission_set) for permission_set in permission_sets) # Get all profile objects which do not have PermissionSet objects # in the current 'translation_project' pointing to them. profiles_without_permissions = [profile for profile in PootleProfile.objects.all().order_by('user__username') if profile not in profile_permission_sets] # Build a list of initial data to be fed to a formset. Each entry # here corresponds to an actual PermissionSet. Thus, we set 'id' # to that of the PermissionSet in question. 'permissions' is a # list of Permission codenames which have been enabled for this # PermissionSet. # # profiles and profile_data are used to display a dropdown list of # users without PermissionSet objects for the current # 'translation_project'. Thus, they are used in the form which # creates PermissionSet objects for new users. So we don't care # about them for forms corresponding to existing PermissionSet # objects. We include them so that Django's validation machinery # won't complain that their values changed (this can quite # possibly be removed without having Django complain, but that's # for someone else to try). permission_data = [{'id': get_id(permission_set, profile_permission_sets), 'permissions': [permission.codename for permission in permission_set.positive_permissions.all()], 'username': permission_set.profile.user.username, 'profiles': permission_set.profile.id, 'profile_data': [(permission_set.profile.id, permission_set.profile.user.username)]} for permission_set in permission_sets] # If there are any profiles which do not have PermissionSet # objects associated with the current 'translation_project', then # we want to display a form to make it possible to add them... if len(profiles_without_permissions) > 0: # Get the profile object for the user 'default' default_profile = PootleProfile.objects.get(user__username='default') default_permissions = get_matching_permissions(default_profile, directory) # The form to add a new profile doesn't yet correspond to a # PermissionSet object. Thus, 'id' can't have a valid # PermissionSet id value. # # The selected 'permissions' should match the list of default # permissions permission_data.append({'id': None, 'permissions': [permission for permission in default_permissions], 'username': '', 'profiles': None, 'profile_data': [(None, '')] + [(profile.id, profile.user.username) for profile in profiles_without_permissions], 'new': True}) return permission_data def process_update(request, directory): def find_updated_forms(formset): deleted_forms = [] changed_forms = [] for form in formset.forms: # If the user toggled the 'delete' checkbox, we'll roast # the corresponding PermissionSet if form.cleaned_data['delete']: deleted_forms.append(form) # Otherwise, if the form contains any changed data, we'll # have to update and save it. elif len(form.changed_data) > 0: changed_forms.append(form) return deleted_forms, changed_forms def get_permission_set(form): if form.is_new_user() and form.cleaned_data['profiles'].isdigit(): permission_set = PermissionSet(profile_id=int(form.cleaned_data['profiles']), directory=directory) permission_set.save() return permission_set elif form.cleaned_data['id'] is not None: return PermissionSet.objects.get(pk=form.cleaned_data['id']) if request.method == 'POST': permission_set_formset = PermissionSetFormSet(data=request.POST, initial=get_permission_data(directory)) pootle_permissions = get_pootle_permissions() # Check whether there are any validation errors in the form # that the user submitted... if permission_set_formset.is_valid(): deleted_forms, changed_forms = find_updated_forms(permission_set_formset) for form in deleted_forms: get_permission_set(form).delete() for form in changed_forms: permission_set = get_permission_set(form) if permission_set is None: continue # pootle_permissions is a (permission codename -> # PermissionSet) dict. We get the permission codenames # from form['permissions'].data. permission_set.positive_permissions = [pootle_permissions[codename] for codename in form.cleaned_data['permissions']] permission_set.save() return PermissionSetFormSet(initial=get_permission_data(directory)) else: # If the form validation failed, we'll return the old # form, which will automatically print validation errors # to the user when it's output again. return permission_set_formset else: return PermissionSetFormSet(initial=get_permission_data(directory)) @util.has_permission('administrate') def view(request, translation_project): language = translation_project.language project = translation_project.project permission_set_formset = process_update(request, translation_project.directory) if translation_project.file_style == "gnu": filestyle_text = _("This is a GNU-style project (files named per language code).") else: filestyle_text = _("This is a standard style project (one directory per language).") template_vars = { "project": project, "language": language, "filestyle_text": filestyle_text, "permissions_title": _("User Permissions"), "username_title": _("Username"), "permission_set_formset": permission_set_formset, "adduser_text": _("(select to add user)"), "search": search_forms.get_search_form(request), "navitems": [navbar_dict.make_directory_navbar_dict(request, translation_project.directory)], "feed_path": translation_project.directory.pootle_path[1:], } return render_to_response("language/tp_admin_permissions.html", template_vars, context_instance=RequestContext(request))