diff options
Diffstat (limited to 'webapp/codereview/fields.py')
-rw-r--r-- | webapp/codereview/fields.py | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/webapp/codereview/fields.py b/webapp/codereview/fields.py new file mode 100644 index 0000000000..1236ff16fd --- /dev/null +++ b/webapp/codereview/fields.py @@ -0,0 +1,337 @@ +# 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 + + |