| # 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 |
| |
| |