blob: 1236ff16fd4e6e93d29a5d2aaaa89305d51557b4 [file] [log] [blame]
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Custom fields and widgets for Gerrit.
This requires Django 0.97.pre.
"""
### Imports ###
import string
import logging
import django.forms.widgets
import django.forms.fields
from django import forms
from django.utils import encoding
from django.utils import safestring
from django.forms import util
from django.utils import html
from django.utils import simplejson
from google.appengine.ext import db
import models
def person_to_dict(v):
entry = {}
if isinstance(v, models.Account):
entry["type"] = "user"
entry["key"] = "user/" + v.email
entry["email"] = v.email
entry["real_name"] = v.real_name
entry["sort_key"] = "2/" + unicode(v.user)
elif isinstance(v, models.AccountGroup):
entry["type"] = "group"
entry["key"] = "group/" + str(v.key())
entry["name"] = v.name
entry["sort_key"] = "1/" + unicode(v.name)
else:
raise AssertionError("bad value: " + str(v))
return entry
def people_to_dicts(value):
data = []
for v in value:
if isinstance(v, list):
data.extend(people_to_dicts(v))
elif v:
data.append(person_to_dict(v))
data.sort(lambda a,b: cmp(a["sort_key"], b["sort_key"]))
return data
### User/Group Field ###
class UserGroupWidget(django.forms.widgets.Widget):
"""The widget that is used with UserGroupField."""
def __init__(self, allow_users=True, allow_groups=True, attrs=None):
self.attrs = {'cols': '40', 'rows': '10'}
self.allow_users = allow_users
self.allow_groups = allow_groups
if attrs:
self.attrs.update(attrs)
def render(self, name, value, attrs=None):
if value is None:
value = []
return safestring.mark_safe(
u"""
<div id="%(name)s_mom"></div>
<script>
UserGroupField_insertField(document.getElementById('%(name)s_mom'),
'%(name)s', %(allow_users)s, %(allow_groups)s, %(initial)s);
</script>
""" % { "name":name,
"initial":self._render_initial_js(value),
"allow_users": ("true" if self.allow_users else "false"),
"allow_groups": ("true" if self.allow_groups else "false"),
})
def _render_initial_js(self, value):
data = people_to_dicts(value)
return "[%s]" % ','.join(map(simplejson.dumps, data))
def value_from_datadict(self, data, files, name):
return data.getlist(name + "_keys")
class UserGroupField(django.forms.fields.Field):
"""A Field that picks a list of users and groups."""
def __init__(self, *args, **kwargs):
self.allow_users = kwargs.pop("allow_users", True)
self.allow_groups = kwargs.pop("allow_groups", True)
self.widget = UserGroupWidget(self.allow_users, self.allow_groups)
super(UserGroupField, self).__init__(*args, **kwargs)
def clean(self, data, initial=None):
def get_correct_model(key):
(type,id) = key.split("/", 1)
if id:
try:
if type == "user":
return models.Account.get_account_for_email(id)
elif type == "group":
return models.AccountGroup.get(id)
except db.BadKeyError, v:
pass
raise forms.ValidationError("invalid key")
keys = data
result = [get_correct_model(key) for key in keys]
super(UserGroupField, self).clean(initial or result)
return result
@classmethod
def get_users(cls, cleaned):
"""Returns the users, given the cleaned data from the form.
e.g.
model_obj.usrs = fields.UserGroupField.get_users(form.cleaned_data['field'])
"""
return [x.user for x in cleaned if isinstance(x, models.Account)]
@classmethod
def get_groups(cls, cleaned):
"""Returns the groups, given the cleaned data from the form.
e.g.
groups = fields.UserGroupField.get_users(form.cleaned_data['field'])
"""
return [x for x in cleaned if isinstance(x, models.AccountGroup)]
@classmethod
def get_group_keys(cls, cleaned):
"""Returns keys for the groups, given the cleaned data from the form.
e.g.
groups = fields.UserGroupField.get_users(form.cleaned_data['field'])
"""
return [x.key() for x in cleaned if isinstance(x, models.AccountGroup)]
@classmethod
def get_user_and_group_keys(cls, cleaned):
"""Returns the users and the groups for the cleaned data from the form.
e.g.
(model_obj.users,model_obj.groups
) = fields.UserGroupField.get_user_and_group_keys(
form.cleaned_data['field'])
"""
return (UserGroupField.get_users(cleaned),
UserGroupField.get_group_keys(cleaned))
@classmethod
def field_value_for_keys(cls, users=[], groups=[]):
"""Return the value suitable for this field from a list keys.
e.g.
form_initial_values['field'] = fields.UserGroupField.field_value_for_keys(
users, group_keys)
"""
return ([models.AccountGroup.get(k) for k in groups]
+ [models.Account.get_account_for_user(u) for u in users])
### Approvers Field ###
class ApproversWidget(django.forms.widgets.Widget):
"""The widget for ApproversField"""
def __init__(self, allow_users=True, allow_groups=True, attrs=None,
approvers=None, verifiers=None):
self.attrs = {'cols': '40', 'rows': '10'}
if attrs:
self.attrs.update(attrs)
self.approvers = approvers or UserGroupWidget();
self.verifiers = verifiers or UserGroupWidget();
def render(self, name, value, attrs=None):
if value is None:
value = []
styles = self.attrs.get("styles", {})
return safestring.mark_safe(
u"""
<div id="%(name)s_mom"></div>
<script>
ApproversField_insertField('%(name)s_mom', '%(name)s', %(initial)s,
%(styles)s);
</script>
""" % {
"name": name,
"initial": self._render_initial_js(name, value),
"styles": simplejson.dumps(styles),
})
def _render_initial_js(self, name, value):
data = []
index = 0
for v in value:
# BEGIN DEBUGGING
key = "initial_%d" % index
files = v["files"]
bad_files = v.get("bad_files", [])
approvers = v["approvers"]
verifiers = v["verifiers"]
# END DEBUGGING
entry = {}
entry["key"] = "initial_%d" % index
entry["files"] = encoding.force_unicode("\n".join(files))
entry["bad_files"] = map(encoding.force_unicode, bad_files)
entry["approvers"] = people_to_dicts(approvers)
entry["verifiers"] = people_to_dicts(verifiers)
data.append(entry)
index = index + 1
rows = []
for entry in data:
rows.append(simplejson.dumps(entry))
return "[%s]" % ','.join(rows)
def value_from_datadict(self, data, files, name):
result = []
keys = data.getlist(name + "_keys")
for key in keys:
field_key = "%s_%s" % (name, key)
files = filter(string.strip, data.get(field_key + "_files").splitlines())
bad_files = []
for f in files:
if not models.ApprovalRight.validate_file(f):
err = True
bad_files.append(f)
approvers = self.approvers.value_from_datadict(data, files,
field_key + "_approvers")
verifiers = self.verifiers.value_from_datadict(data, files,
field_key + "_verifiers")
result.append({
"key": key,
"files": files,
"bad_files": bad_files,
"approvers": approvers,
"verifiers": verifiers,
})
return result
class ApproversField(django.forms.fields.Field):
"""A Field to pick which users/groups can edit which field"""
approvers = UserGroupField();
verifiers = UserGroupField();
widget = ApproversWidget(approvers=approvers.widget,
verifiers=verifiers.widget, attrs={"styles": {
"approval": "approval"
}})
def __init__(self, *args, **kwargs):
super(ApproversField, self).__init__(*args, **kwargs)
def clean(self, data, initial=None):
result = []
err = False
for d in data:
files = d["files"]
if len(d["bad_files"]) > 0:
err = True
approvers = self.approvers.clean(d["approvers"])
verifiers = self.verifiers.clean(d["verifiers"])
result.append({"files": files, "approvers": approvers,
"verifiers": verifiers})
if False:
for r in result:
logging.info("clean: files=" + str(r["files"]))
logging.info(" approvers=" + str(r["approvers"]))
logging.info(" verifiers=" + str(r["verifiers"]))
super(ApproversField, self).clean(initial or result)
if err:
raise forms.ValidationError("invalid files")
return result
### Project field ###
class ProjectSelectWidget(django.forms.widgets.Widget):
"""A widget that lets a user pick a set of projects."""
def __init__(self, attrs=None):
super(ProjectSelectWidget, self).__init__(attrs)
if attrs:
self.attrs.update(attrs)
def render(self, name, value, attrs=None):
if value is None:
value = []
project_list = [{'name': p.name, 'key': str(p.key())}
for p in models.Project.get_all_projects()]
return safestring.mark_safe(
u"""
<div id="%(name)s_mom"></div>
<script>
ProjectField_insertField(document.getElementById('%(name)s_mom'),
'%(name)s', %(project_list)s, %(initial)s);
</script>
""" % { "name": name,
"project_list": self._render_js_list(project_list),
"initial": self._render_js_list([str(v) for v in value]),
})
def _render_js_list(self, value):
return "[%s]" % ','.join(map(simplejson.dumps, value))
def value_from_datadict(self, data, files, name):
return set([v.strip() for v in data.getlist(name) if len(v.strip()) > 0])
class ProjectSelectField(django.forms.fields.Field):
"""A Field that lets a user pick a set of projects."""
def __init__(self, *args, **kwargs):
self.widget = ProjectSelectWidget()
super(ProjectSelectField, self).__init__(*args, **kwargs)
def clean(self, data, initial=None):
objects = models.Project.get(data)
result = [o.key() for o in objects if o]
super(ProjectSelectField, self).clean(initial or result)
return result