Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Pootle-2.0.0/local_apps/pootle_app/views/language/admin_permissions.py
blob: 672748bbbe6ccd0b51cb3efa83a219997a3d8d2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/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('<tr><td>%(id)s%(username)s%(profiles)s</td><td>%(permissions)s</td><td>%(delete)s</td></tr>' % 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 "<tr><th>%s</th><th>%s</th><th>%s</th></tr>%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))