datatracker/ietf/secr/drafts/forms.py
Henrik Levkowetz 9fd5d104c7 Merged in [15111] from rjsparks@nostrum.com:
Removed the \'replaces\' functionality from /secr/drafts. Addresses part of #2513.
 - Legacy-Id: 15117
Note: SVN reference [15111] has been migrated to Git commit c3241bddac
2018-05-03 20:09:08 +00:00

372 lines
16 KiB
Python

import datetime
import re
import os
from django import forms
from ietf.doc.models import Document, State
from ietf.name.models import IntendedStdLevelName
from ietf.group.models import Group
from ietf.person.models import Person, Email
from ietf.person.fields import SearchableEmailField
from ietf.secr.groups.forms import get_person
# ---------------------------------------------
# Select Choices
# ---------------------------------------------
WITHDRAW_CHOICES = (('ietf','Withdraw by IETF'),('author','Withdraw by Author'))
# ---------------------------------------------
# Custom Fields
# ---------------------------------------------
class DocumentField(forms.FileField):
'''A validating document upload field'''
def __init__(self, unique=False, *args, **kwargs):
self.extension = kwargs.pop('extension')
self.filename = kwargs.pop('filename')
self.rev = kwargs.pop('rev')
super(DocumentField, self).__init__(*args, **kwargs)
def clean(self, data, initial=None):
file = super(DocumentField, self).clean(data,initial)
if file:
# validate general file format
m = re.search(r'.*-\d{2}\.(txt|pdf|ps|xml)', file.name)
if not m:
raise forms.ValidationError('File name must be in the form base-NN.[txt|pdf|ps|xml]')
# ensure file extension is correct
base,ext = os.path.splitext(file.name)
if ext != self.extension:
raise forms.ValidationError('Incorrect file extension: %s' % ext)
# if this isn't a brand new submission we need to do some extra validations
if self.filename:
# validate filename
if base[:-3] != self.filename:
raise forms.ValidationError, "Filename: %s doesn't match Draft filename." % base[:-3]
# validate revision
next_revision = str(int(self.rev)+1).zfill(2)
if base[-2:] != next_revision:
raise forms.ValidationError, "Expected revision # %s" % (next_revision)
return file
class GroupModelChoiceField(forms.ModelChoiceField):
'''
Custom ModelChoiceField sets queryset to include all active workgroups and the
individual submission group, none. Displays group acronyms as choices. Call it without the
queryset argument, for example:
group = GroupModelChoiceField(required=True)
'''
def __init__(self, *args, **kwargs):
kwargs['queryset'] = Group.objects.filter(type__in=('wg','individ'),state__in=('bof','proposed','active')).order_by('acronym')
super(GroupModelChoiceField, self).__init__(*args, **kwargs)
def label_from_instance(self, obj):
return obj.acronym
class AliasModelChoiceField(forms.ModelChoiceField):
'''
Custom ModelChoiceField, just uses Alias name in the select choices as opposed to the
more confusing alias -> doc format used by DocAlias.__unicode__
'''
def label_from_instance(self, obj):
return obj.name
# ---------------------------------------------
# Forms
# ---------------------------------------------
class AddModelForm(forms.ModelForm):
start_date = forms.DateField()
group = GroupModelChoiceField(required=True,help_text='Use group "none" for Individual Submissions.')
class Meta:
model = Document
fields = ('title','group','stream','start_date','pages','abstract','internal_comments')
# use this method to set attrs which keeps other meta info from model.
def __init__(self, *args, **kwargs):
super(AddModelForm, self).__init__(*args, **kwargs)
self.fields['title'].label='Document Name'
self.fields['title'].widget=forms.Textarea()
self.fields['start_date'].initial=datetime.date.today
self.fields['pages'].label='Number of Pages'
self.fields['internal_comments'].label='Comments'
class AuthorForm(forms.Form):
'''
The generic javascript for populating the email list based on the name selected expects to
see an id_email field
'''
person = forms.CharField(max_length=50,widget=forms.TextInput(attrs={'class':'name-autocomplete'}),help_text="To see a list of people type the first name, or last name, or both.")
email = forms.CharField(widget=forms.Select(),help_text="Select an email.")
affiliation = forms.CharField(max_length=100, required=False, help_text="Affiliation")
country = forms.CharField(max_length=255, required=False, help_text="Country")
# check for id within parenthesis to ensure name was selected from the list
def clean_person(self):
person = self.cleaned_data.get('person', '')
m = re.search(r'(\d+)', person)
if person and not m:
raise forms.ValidationError("You must select an entry from the list!")
# return person object
return get_person(person)
# check that email exists and return the Email object
def clean_email(self):
email = self.cleaned_data['email']
try:
obj = Email.objects.get(address=email)
except Email.ObjectDoesNoExist:
raise forms.ValidationError("Email address not found!")
# return email object
return obj
class EditModelForm(forms.ModelForm):
#expiration_date = forms.DateField(required=False)
state = forms.ModelChoiceField(queryset=State.objects.filter(type='draft'),empty_label=None)
iesg_state = forms.ModelChoiceField(queryset=State.objects.filter(type='draft-iesg'),required=False)
group = GroupModelChoiceField(required=True)
review_by_rfc_editor = forms.BooleanField(required=False)
shepherd = SearchableEmailField(required=False, only_users=True)
class Meta:
model = Document
fields = ('title','group','ad','shepherd','notify','stream','review_by_rfc_editor','name','rev','pages','intended_std_level','std_level','abstract','internal_comments')
# use this method to set attrs which keeps other meta info from model.
def __init__(self, *args, **kwargs):
super(EditModelForm, self).__init__(*args, **kwargs)
self.fields['ad'].queryset = Person.objects.filter(role__name='ad').distinct()
self.fields['title'].label='Document Name'
self.fields['title'].widget=forms.Textarea()
self.fields['rev'].widget.attrs['size'] = 2
self.fields['abstract'].widget.attrs['cols'] = 72
self.initial['state'] = self.instance.get_state().pk
if self.instance.get_state('draft-iesg'):
self.initial['iesg_state'] = self.instance.get_state('draft-iesg').pk
# setup special fields
if self.instance:
# setup replaced
self.fields['review_by_rfc_editor'].initial = bool(self.instance.tags.filter(slug='rfc-rev'))
def save(self, commit=False):
m = super(EditModelForm, self).save(commit=False)
state = self.cleaned_data['state']
iesg_state = self.cleaned_data['iesg_state']
if 'state' in self.changed_data:
m.set_state(state)
# note we're not sending notices here, is this desired
if 'iesg_state' in self.changed_data:
if iesg_state == None:
m.unset_state('draft-iesg')
else:
m.set_state(iesg_state)
if 'review_by_rfc_editor' in self.changed_data:
if self.cleaned_data.get('review_by_rfc_editor',''):
m.tags.add('rfc-rev')
else:
m.tags.remove('rfc-rev')
# handle replaced by
return m
# field must contain filename of existing draft
def clean_replaced_by(self):
name = self.cleaned_data.get('replaced_by', '')
if name and not Document.objects.filter(name=name):
raise forms.ValidationError("ERROR: Draft does not exist")
return name
def clean(self):
super(EditModelForm, self).clean()
cleaned_data = self.cleaned_data
"""
expiration_date = cleaned_data.get('expiration_date','')
status = cleaned_data.get('status','')
replaced = cleaned_data.get('replaced',False)
replaced_by = cleaned_data.get('replaced_by','')
replaced_status_object = IDStatus.objects.get(status_id=5)
expired_status_object = IDStatus.objects.get(status_id=2)
# this condition seems to be valid
#if expiration_date and status != expired_status_object:
# raise forms.ValidationError('Expiration Date set but status is %s' % (status))
if status == expired_status_object and not expiration_date:
raise forms.ValidationError('Status is Expired but Expirated Date is not set')
if replaced and status != replaced_status_object:
raise forms.ValidationError('You have checked Replaced but status is %s' % (status))
if replaced and not replaced_by:
raise forms.ValidationError('You have checked Replaced but Replaced By field is empty')
"""
return cleaned_data
class EmailForm(forms.Form):
# max_lengths come from db limits, cc is not limited
action = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False)
expiration_date = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False)
withdraw_type = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False)
replaced = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False)
replaced_by = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False)
filename = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False)
to = forms.CharField(max_length=255)
cc = forms.CharField(required=False)
subject = forms.CharField(max_length=255)
body = forms.CharField(widget=forms.Textarea(), strip=False)
def __init__(self, *args, **kwargs):
if 'hidden' in kwargs:
self.hidden = kwargs.pop('hidden')
else:
self.hidden = False
super(EmailForm, self).__init__(*args, **kwargs)
if self.hidden:
for key in self.fields.keys():
self.fields[key].widget = forms.HiddenInput()
class ExtendForm(forms.Form):
action = forms.CharField(max_length=255, widget=forms.HiddenInput(),initial='extend')
expiration_date = forms.DateField()
class BaseRevisionModelForm(forms.ModelForm):
class Meta:
model = Document
fields = ('title','pages','abstract')
class RevisionModelForm(forms.ModelForm):
class Meta:
model = Document
fields = ('title','pages','abstract')
# use this method to set attrs which keeps other meta info from model.
def __init__(self, *args, **kwargs):
super(RevisionModelForm, self).__init__(*args, **kwargs)
self.fields['title'].label='Document Name'
self.fields['title'].widget=forms.Textarea()
self.fields['pages'].label='Number of Pages'
# class RfcModelForm(forms.ModelForm):
# rfc_number = forms.IntegerField()
# rfc_published_date = forms.DateField(initial=datetime.datetime.now)
# group = GroupModelChoiceField(required=True)
#
# class Meta:
# model = Document
# fields = ('title','group','pages','std_level','internal_comments')
#
# # use this method to set attrs which keeps other meta info from model.
# def __init__(self, *args, **kwargs):
# super(RfcModelForm, self).__init__(*args, **kwargs)
# self.fields['title'].widget = forms.Textarea()
# self.fields['std_level'].required = True
#
# def save(self, force_insert=False, force_update=False, commit=False):
# obj = super(RfcModelForm, self).save(commit=False)
#
# # create DocAlias
# DocAlias.objects.create(document=self.instance,name="rfc%d" % self.cleaned_data['rfc_number'])
#
# return obj
#
# def clean_rfc_number(self):
# rfc_number = self.cleaned_data['rfc_number']
# if DocAlias.objects.filter(name='rfc' + str(rfc_number)):
# raise forms.ValidationError("RFC %d already exists" % rfc_number)
# return rfc_number
# class RfcObsoletesForm(forms.Form):
# relation = forms.ModelChoiceField(queryset=DocRelationshipName.objects.filter(slug__in=('updates','obs')),required=False)
# rfc = forms.IntegerField(required=False)
#
# # ensure that RFC exists
# def clean_rfc(self):
# rfc = self.cleaned_data.get('rfc','')
# if rfc:
# if not Document.objects.filter(docalias__name="rfc%s" % rfc):
# raise forms.ValidationError("RFC does not exist")
# return rfc
#
# def clean(self):
# super(RfcObsoletesForm, self).clean()
# cleaned_data = self.cleaned_data
# relation = cleaned_data.get('relation','')
# rfc = cleaned_data.get('rfc','')
# if (relation and not rfc) or (rfc and not relation):
# raise forms.ValidationError('You must select a relation and enter RFC #')
# return cleaned_data
class SearchForm(forms.Form):
intended_std_level = forms.ModelChoiceField(queryset=IntendedStdLevelName.objects,label="Intended Status",required=False)
document_title = forms.CharField(max_length=80,label='Document Title',required=False)
group = forms.CharField(max_length=12,required=False)
filename = forms.CharField(max_length=80,required=False)
state = forms.ModelChoiceField(queryset=State.objects.filter(type='draft'),required=False)
revision_date_start = forms.DateField(label='Revision Date (start)',required=False)
revision_date_end = forms.DateField(label='Revision Date (end)',required=False)
class UploadForm(forms.Form):
txt = DocumentField(label=u'.txt format', required=True,extension='.txt',filename=None,rev=None)
xml = DocumentField(label=u'.xml format', required=False,extension='.xml',filename=None,rev=None)
pdf = DocumentField(label=u'.pdf format', required=False,extension='.pdf',filename=None,rev=None)
ps = DocumentField(label=u'.ps format', required=False,extension='.ps',filename=None,rev=None)
def __init__(self, *args, **kwargs):
if 'draft' in kwargs:
self.draft = kwargs.pop('draft')
else:
self.draft = None
super(UploadForm, self).__init__(*args, **kwargs)
if self.draft:
for field in self.fields.itervalues():
field.filename = self.draft.name
field.rev = self.draft.rev
def clean(self):
# Checks that all files have the same base
if any(self.errors):
# Don't bother validating unless each field is valid on its own
return
txt = self.cleaned_data['txt']
xml = self.cleaned_data['xml']
pdf = self.cleaned_data['pdf']
ps = self.cleaned_data['ps']
# we only need to do these validations for new drafts
if not self.draft:
names = []
for file in (txt,xml,pdf,ps):
if file:
base = os.path.splitext(file.name)[0]
if base not in names:
names.append(base)
if len(names) > 1:
raise forms.ValidationError, "All files must have the same base name"
# ensure that the basename is unique
base = os.path.splitext(txt.name)[0]
if Document.objects.filter(name=base[:-3]):
raise forms.ValidationError, "This document filename already exists: %s" % base[:-3]
# ensure that rev is 00
if base[-2:] != '00':
raise forms.ValidationError, "New Drafts must start with 00 revision number."
return self.cleaned_data
class WithdrawForm(forms.Form):
withdraw_type = forms.CharField(widget=forms.Select(choices=WITHDRAW_CHOICES),help_text='Select which type of withdraw to perform.')