GitHub Viewer
# Patchwork - automated patch tracking system
# Copyright (C) 2008 Jeremy Kerr
#
# SPDX-License-Identifier: GPL-2.0-or-later
from django.contrib.auth.models import User
from django import forms
from django.forms import renderers
from django.db.models import Q
from django.db.utils import ProgrammingError
from django.template.backends import django as django_template_backend
from patchwork.models import Bundle
from patchwork.models import Patch
from patchwork.models import State
from patchwork.models import UserProfile
class PatchworkTableRenderer(renderers.EngineMixin, renderers.BaseRenderer):
backend = django_template_backend.DjangoTemplates
form_template_name = 'django/forms/table.html'
formset_template_name = 'django/forms/formsets/table.html'
class RegistrationForm(forms.Form):
first_name = forms.CharField(max_length=30, required=False)
last_name = forms.CharField(max_length=30, required=False)
username = forms.RegexField(
regex=r'^\w+$',
max_length=30,
label='Username',
)
email = forms.EmailField(max_length=100, label='Email address')
password = forms.CharField(widget=forms.PasswordInput(), label='Password')
def clean_username(self):
value = self.cleaned_data['username']
try:
User.objects.get(username__iexact=value)
except User.DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError(
'This username is already taken. Please choose another.'
)
def clean_email(self):
value = self.cleaned_data['email']
try:
user = User.objects.get(email__iexact=value)
except User.DoesNotExist:
return self.cleaned_data['email']
raise forms.ValidationError(
'This email address is already in use '
'for the account "%s".\n' % user.username
)
def clean(self):
return self.cleaned_data
class BundleForm(forms.ModelForm):
name = forms.RegexField(
regex=r'^[^/]+$',
min_length=1,
max_length=50,
required=False,
error_messages={'invalid': "Bundle names can't contain slashes"},
widget=forms.TextInput(
attrs={'class': 'create-bundle', 'placeholder': 'Bundle name'}
),
)
class Meta:
model = Bundle
fields = ['name', 'public']
class CreateBundleForm(BundleForm):
def clean_name(self):
name = self.cleaned_data['name']
if not name:
raise forms.ValidationError(
'No bundle name was specified', code='invalid'
)
count = Bundle.objects.filter(
owner=self.instance.owner, name=name
).count()
if count > 0:
raise forms.ValidationError(
'A bundle called %(name)s already exists',
code='invalid',
params={'name': name},
)
return name
class Meta:
model = Bundle
fields = ['name']
class DeleteBundleForm(forms.Form):
name = 'deletebundleform'
form_name = forms.CharField(initial=name, widget=forms.HiddenInput)
bundle_id = forms.IntegerField(widget=forms.HiddenInput)
class EmailForm(forms.Form):
email = forms.EmailField(max_length=200)
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['items_per_page', 'show_ids']
labels = {'show_ids': 'Show Patch IDs:'}
def _get_delegate_qs(project, instance=None):
if instance and not project:
project = instance.project
if not project:
raise ValueError('Expected a project')
q = Q(
profile__in=UserProfile.objects.filter(maintainer_projects=project)
.values('pk')
.query
)
if instance and instance.delegate:
q = q | Q(username=instance.delegate)
return User.objects.complex_filter(q)
class PatchForm(forms.ModelForm):
def __init__(self, instance=None, project=None, *args, **kwargs):
super(PatchForm, self).__init__(instance=instance, *args, **kwargs)
self.fields['delegate'] = forms.ModelChoiceField(
queryset=_get_delegate_qs(project, instance),
widget=forms.Select(attrs={'class': 'change-property-delegate'}),
required=False,
)
class Meta:
model = Patch
fields = ['state', 'archived', 'delegate']
widgets = {
'state': forms.Select(attrs={'class': 'change-property-state'}),
'archived': forms.CheckboxInput(
attrs={'class': 'archive-patch-check'}
),
}
class OptionalModelChoiceField(forms.ModelChoiceField):
no_change_choice = ('*', 'No change')
to_field_name = None
def __init__(self, *args, placeholder, className, **kwargs):
self.no_change_choice = ('*', placeholder)
self.widget = forms.Select(attrs={'class': className})
super(OptionalModelChoiceField, self).__init__(
initial=self.no_change_choice[0], *args, **kwargs
)
def _get_choices(self):
# _get_choices queries the database, which can fail if the db
# hasn't been initialised yet. catch that and give an empty
# set of choices for now.
try:
choices = list(
super(OptionalModelChoiceField, self)._get_choices()
)
except ProgrammingError:
choices = []
choices.append(self.no_change_choice)
return choices
choices = property(_get_choices, forms.ChoiceField.choices.fset)
def is_no_change(self, value):
return value == self.no_change_choice[0]
def clean(self, value):
if value == self.no_change_choice[0]:
return value
return super(OptionalModelChoiceField, self).clean(value)
class OptionalBooleanField(forms.TypedChoiceField):
def __init__(self, className, *args, **kwargs):
self.widget = forms.Select(attrs={'class': className})
super(OptionalBooleanField, self).__init__(*args, **kwargs)
def is_no_change(self, value):
return value == self.empty_value
class MultiplePatchForm(forms.Form):
action = 'update'
archived = OptionalBooleanField(
className='archive-patch-select',
choices=[
('*', 'No change'),
('True', 'Archive'),
('False', 'Unarchive'),
],
coerce=lambda x: x == 'True',
empty_value='*',
label='Archived',
)
def __init__(self, project, *args, **kwargs):
super(MultiplePatchForm, self).__init__(*args, **kwargs)
self.fields['delegate'] = OptionalModelChoiceField(
queryset=_get_delegate_qs(project=project),
placeholder='Delegate to',
className='change-property-delegate',
label='Delegate to',
required=False,
)
self.fields['state'] = OptionalModelChoiceField(
queryset=State.objects.all(),
placeholder='Change state',
className='change-property-state',
label='Change state',
)
def save(self, instance, commit=True):
opts = instance.__class__._meta
if self.errors:
raise ValueError(
'The %s could not be changed because the data '
"didn't validate." % opts.object_name
)
data = self.cleaned_data
# Update the instance
for f in opts.fields:
if f.name not in data:
continue
field = self.fields.get(f.name, None)
if not field:
continue
if field.is_no_change(data[f.name]):
continue
setattr(instance, f.name, data[f.name])
if commit:
instance.save()
return instance