338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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
 | 
						|
 | 
						|
 |