Revamp and clean up submit models:

- Rename IdSubmissionDetail to Submission
- Rename various submission fields to correspond to the conventions in
  the new schema
- Use a name model for the states instead of IdSubmissionStatus
- Drop the TempIdAuthor model which is based on splitting up author
  names
- Add a simple textual SubmissionEvent for tracking events in the
  lifetime of a submission
- Delete a bunch of obsolete fields
- Make sure all submission have an access key so we can depend on it
- Add state for when approval is needed from previous authors

A couple of migrations take care of transforming the
IdSubmissionDetail and moving data over/cleaning it up.

Also revamp the submit view code:

- Make form code do validation/cleaning only so there's a clear
  separation of concerns
- Reduce uses of inheritance that made the code hard to follow -
  forms now don't inherit from each other, views don't call each other
  but instead reuse common utilities, templates share CSS/utilities
  instead of relying on inheritance
- Move email rendering/sending to separate file
- Drop the in-grown terminology use (auto post vs. manual posts)
- Make the status page explain who is emailed for what purpose
- Add history table with recorded events
- Make the status page handle its post actions by itself instead of
  duplicating most of the setup logic in a number of simple views
- Fix a couple of minor bugs and handle some edge cases better
- Expand tests with a couple of more cases

Possibly the submit tool could still use more help text added to
explain the process, ideally what's explained in the tool instructions
page should be inlined or self-evident.
 - Legacy-Id: 6714
This commit is contained in:
Ole Laursen 2013-11-15 13:30:32 +00:00
parent fd01ddd215
commit ecf68dbb05
53 changed files with 3809 additions and 2027 deletions

View file

@ -31,7 +31,7 @@ from django.conf import settings
from ietf.utils.path import path as Path
from ietf.submit.models import IdSubmissionDetail
from ietf.submit.models import Submission
from ietf.doc.models import Document
@ -56,11 +56,11 @@ from_email = settings.IDSUBMIT_FROM_EMAIL
if "<" in from_email:
from_email = from_email.split("<")[1].split(">")[0]
submission = IdSubmissionDetail.objects.filter(filename=draft).order_by('-pk')[0]
submission = Submission.objects.filter(name=draft).order_by('-pk')[0]
document = Document.objects.get(name=draft)
emails = [ author.address for author in document.authors.all() ]
file = Path(settings.INTERNET_DRAFT_PATH) / ("%s-%s.txt"%(draft, submission.revision))
file = Path(settings.INTERNET_DRAFT_PATH) / ("%s-%s.txt"%(draft, submission.rev))
upload_time = time.localtime(file.mtime)
timestr1 = time.strftime("%b %d %H:%M", upload_time)
@ -87,4 +87,4 @@ for log in logfiles:
for qi in queue_ids:
if qi in line:
sys.stdout.write(line)

View file

@ -713,6 +713,101 @@
"desc": ""
}
},
{
"pk": "uploaded",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 1,
"next_states": [
"auth",
"aut-appr",
"grp-appr",
"manual",
"cancel"
],
"used": true,
"name": "Uploaded",
"desc": ""
}
},
{
"pk": "auth",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 2,
"next_states": [
"cancel",
"posted"
],
"used": true,
"name": "Awaiting Submitter Authentication",
"desc": ""
}
},
{
"pk": "aut-appr",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 3,
"next_states": [
"cancel",
"posted"
],
"used": true,
"name": "Awaiting Approval from Previous Version Authors'",
"desc": ""
}
},
{
"pk": "grp-appr",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 4,
"next_states": [
"cancel",
"posted"
],
"used": true,
"name": "Awaiting Initial Version Approval",
"desc": ""
}
},
{
"pk": "manual",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 5,
"next_states": [
"cancel",
"posted"
],
"used": true,
"name": "Awaiting Manual Post",
"desc": ""
}
},
{
"pk": "cancel",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 6,
"next_states": [],
"used": true,
"name": "Cancelled",
"desc": ""
}
},
{
"pk": "posted",
"model": "name.draftsubmissionstatename",
"fields": {
"order": 7,
"next_states": [],
"used": true,
"name": "Posted",
"desc": ""
}
},
{
"pk": "comment",
"model": "name.feedbacktype",

View file

@ -0,0 +1,214 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'DraftSubmissionStateName'
db.create_table('name_draftsubmissionstatename', (
('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
))
db.send_create_signal('name', ['DraftSubmissionStateName'])
# Adding M2M table for field next_states on 'DraftSubmissionStateName'
db.create_table('name_draftsubmissionstatename_next_states', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('from_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False)),
('to_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False))
))
db.create_unique('name_draftsubmissionstatename_next_states', ['from_draftsubmissionstatename_id', 'to_draftsubmissionstatename_id'])
def backwards(self, orm):
# Deleting model 'DraftSubmissionStateName'
db.delete_table('name_draftsubmissionstatename')
# Removing M2M table for field next_states on 'DraftSubmissionStateName'
db.delete_table('name_draftsubmissionstatename_next_states')
models = {
'name.ballotpositionname': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.constraintname': {
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.dbtemplatetypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docremindertypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.draftsubmissionstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['name.DraftSubmissionStateName']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.feedbacktype': {
'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupmilestonestatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.liaisonstatementpurposename': {
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.meetingtypename': {
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.nomineepositionstate': {
'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.rolename': {
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.sessionstatusname': {
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.timeslottypename': {
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['name']

View file

@ -0,0 +1,204 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
uploaded, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="uploaded", order=1, name="Uploaded")
submitter, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="auth", order=2, name="Awaiting Submitter Authentication")
author_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="aut-appr", order=3, name="Awaiting Approval from Previous Version Authors'")
group_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="grp-appr", order=4, name="Awaiting Initial Version Approval")
manual_post, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="manual", order=5, name="Awaiting Manual Post")
cancel, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="cancel", order=6, name="Cancelled")
posted, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="posted", order=7, name="Posted")
uploaded.next_states = [submitter, author_approval, group_approval, manual_post, cancel]
submitter.next_states = [cancel, posted]
author_approval.next_states = [cancel, posted]
group_approval.next_states = [cancel, posted]
manual_post.next_states = [cancel, posted]
def backwards(self, orm):
"Write your backwards methods here."
models = {
'name.ballotpositionname': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.constraintname': {
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.dbtemplatetypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docremindertypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.draftsubmissionstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['name.DraftSubmissionStateName']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.feedbacktype': {
'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupmilestonestatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.liaisonstatementpurposename': {
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.meetingtypename': {
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.nomineepositionstate': {
'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.rolename': {
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.sessionstatusname': {
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.timeslottypename': {
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['name']

View file

@ -66,3 +66,8 @@ class FeedbackType(NameModel):
"""Type of feedback: questionnaires, nominations, comments"""
class DBTemplateTypeName(NameModel):
"""reStructuredText, Plain, Django"""
class DraftSubmissionStateName(NameModel):
"""Uploaded, Awaiting Submitter Authentication, Awaiting Approval from
Previous Version Authors, Awaiting Initial Version Approval, Awaiting
Manual Post, Cancelled, Posted"""
next_states = models.ManyToManyField('DraftSubmissionStateName', related_name="previous_states", blank=True)

View file

@ -17,7 +17,7 @@ from ietf.meeting.models import Meeting
from ietf.name.models import StreamName
from ietf.doc.models import Document, DocumentAuthor
from ietf.doc.utils import augment_with_start_time
from ietf.submit.models import IdSubmissionDetail, Preapproval
from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName, SubmissionEvent
from ietf.utils.draft import Draft
from ietf.secr.proceedings.proc_utils import get_progress_stats
from ietf.secr.sreq.views import get_meeting
@ -107,7 +107,7 @@ def process_files(request,draft):
the basename, revision number and a list of file types. Basename and revision
are assumed to be the same for all because this is part of the validation process.
It also creates the IdSubmissionDetail record WITHOUT saving, and places it in the
It also creates the Submission record WITHOUT saving, and places it in the
session for saving in the final action step.
'''
files = request.FILES
@ -124,29 +124,37 @@ def process_files(request,draft):
wrapper = Draft(file.read(),file.name)
handle_uploaded_file(file)
# create IdSubmissionDetail record, leaved unsaved
idsub = IdSubmissionDetail(
id_document_name=draft.title,
filename=basename,
revision=revision,
txt_page_count=draft.pages,
filesize=txt_size,
creation_date=wrapper.get_creation_date(),
# create Submission record, leaved unsaved
idsub = Submission(
name=basename,
title=draft.title,
rev=revision,
pages=draft.pages,
file_size=txt_size,
document_date=wrapper.get_creation_date(),
submission_date=datetime.date.today(),
idnits_message='idnits bypassed by manual posting',
temp_id_document_tag=None,
group_acronym_id=draft.group.id,
group_id=draft.group.id,
remote_ip=request.META['REMOTE_ADDR'],
first_two_pages=''.join(wrapper.pages[:2]),
status_id=-2,
state=DraftSubmissionStateName.objects.get(slug="posted"),
abstract=draft.abstract,
file_type=','.join(file_type_list),
man_posted_date=datetime.date.today(),
man_posted_by=request.user.get_profile())
file_types=','.join(file_type_list),
)
request.session['idsub'] = idsub
return (filename,revision,file_type_list)
def post_submission(request):
submission = request.session['idsub']
submission.save()
SubmissionEvent.objects.create(
submission=submission,
by=request.user.person,
desc="Submitted and posted manually")
def promote_files(draft, types):
'''
This function takes one argument, a draft object. It then moves the draft files from
@ -308,8 +316,8 @@ def do_revision(draft, request):
promote_files(new_draft, request.session['file_type'])
# save the submission record
request.session['idsub'].save()
post_submission(request)
# send announcement if we are in IESG process
if new_draft.get_state('draft-iesg'):
announcement_from_form(request.session['email'],by=request.user.get_profile())
@ -356,8 +364,8 @@ def do_update(draft,request):
promote_files(new_draft, request.session['file_type'])
# save the submission record
request.session['idsub'].save()
post_submission(request)
# send announcement
announcement_from_form(request.session['email'],by=request.user.get_profile())
@ -581,7 +589,7 @@ def add(request):
promote_files(draft, file_type_list)
# save the submission record
request.session['idsub'].save()
post_submission(request)
request.session['action'] = 'add'

View file

@ -296,12 +296,6 @@ LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool <lsmt@' + IETF_DOMAI
LIAISON_ATTACH_PATH = '/a/www/ietf-datatracker/documents/LIAISON/'
LIAISON_ATTACH_URL = '/documents/LIAISON/'
# ID Submission Tool settings
IDSUBMIT_FROM_EMAIL = 'IETF I-D Submission Tool <idsubmission@ietf.org>'
IDSUBMIT_TO_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_FROM_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_LIST_EMAIL = 'i-d-announce@ietf.org'
# NomCom Tool settings
ROLODEX_URL = ""
NOMCOM_PUBLIC_KEYS_DIR = '/a/www/nomcom/public_keys/'
@ -312,8 +306,13 @@ DAYS_TO_EXPIRE_NOMINATION_LINK = ''
DEFAULT_FEEDBACK_TYPE = 'offtopic'
NOMINEE_FEEDBACK_TYPES = ['comment', 'questio', 'nomina']
# Days from meeting to cut off dates on submit
FIRST_CUTOFF_DAYS = 19
# ID Submission Tool settings
IDSUBMIT_FROM_EMAIL = 'IETF I-D Submission Tool <idsubmission@ietf.org>'
IDSUBMIT_TO_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_FROM_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_LIST_EMAIL = 'i-d-announce@ietf.org'
FIRST_CUTOFF_DAYS = 19 # Days from meeting to cut off dates on submit
SECOND_CUTOFF_DAYS = 12
CUTOFF_HOUR = 00 # midnight UTC
SUBMISSION_START_DAYS = -90
@ -327,18 +326,16 @@ IDSUBMIT_STAGING_PATH = '/a/www/www6s/staging/'
IDSUBMIT_STAGING_URL = 'http://www.ietf.org/staging/'
IDSUBMIT_IDNITS_BINARY = '/a/www/ietf-datatracker/scripts/idnits'
MAX_PLAIN_DRAFT_SIZE = 6291456 # Max size of the txt draft in bytes
IDSUBMIT_MAX_PLAIN_DRAFT_SIZE = 6291456 # Max size of the txt draft in bytes
# DOS THRESHOLDS PER DAY (Sizes are in MB)
MAX_SAME_DRAFT_NAME = 20
MAX_SAME_DRAFT_NAME_SIZE = 50
MAX_SAME_SUBMITTER = 50
MAX_SAME_SUBMITTER_SIZE = 150
MAX_SAME_WG_DRAFT = 150
MAX_SAME_WG_DRAFT_SIZE = 450
MAX_DAILY_SUBMISSION = 1000
MAX_DAILY_SUBMISSION_SIZE = 2000
# End of ID Submission Tool settings
IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME = 20
IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME_SIZE = 50 # in MB
IDSUBMIT_MAX_DAILY_SAME_SUBMITTER = 50
IDSUBMIT_MAX_DAILY_SAME_SUBMITTER_SIZE = 150 # in MB
IDSUBMIT_MAX_DAILY_SAME_GROUP = 150
IDSUBMIT_MAX_DAILY_SAME_GROUP_SIZE = 450 # in MB
IDSUBMIT_MAX_DAILY_SUBMISSIONS = 1000
IDSUBMIT_MAX_DAILY_SUBMISSIONS_SIZE = 2000 # in MB
# Account settings
DAYS_TO_EXPIRE_REGISTRATION_LINK = 3

View file

@ -4,37 +4,29 @@ from django.utils.safestring import mark_safe
from ietf.submit.models import *
class IdSubmissionStatusAdmin(admin.ModelAdmin):
pass
admin.site.register(IdSubmissionStatus, IdSubmissionStatusAdmin)
class IdSubmissionDetailAdmin(admin.ModelAdmin):
list_display = ['submission_id', 'draft_link', 'status_link', 'submission_date', 'last_updated_date',]
ordering = [ '-submission_date' ]
search_fields = ['filename', ]
raw_id_fields = ['group_acronym']
class SubmissionAdmin(admin.ModelAdmin):
list_display = ['id', 'draft_link', 'status_link', 'submission_date',]
ordering = [ '-id' ]
search_fields = ['name', ]
raw_id_fields = ['group']
def status_link(self, instance):
url = urlreverse('draft_status_by_hash',
kwargs=dict(submission_id=instance.submission_id,
submission_hash=instance.get_hash()))
return '<a href="%s">%s</a>' % (url, instance.status)
url = urlreverse('submit_submission_status_by_hash',
kwargs=dict(submission_id=instance.pk,
access_key=instance.access_key))
return '<a href="%s">%s</a>' % (url, instance.state)
status_link.allow_tags = True
def draft_link(self, instance):
if instance.status_id in (-1, -2):
return '<a href="http://www.ietf.org/id/%s-%s.txt">%s</a>' % (instance.filename, instance.revision, instance.filename)
if instance.state_id == "posted":
return '<a href="http://www.ietf.org/id/%s-%s.txt">%s</a>' % (instance.name, instance.rev, instance.name)
else:
return instance.filename
return instance.name
draft_link.allow_tags = True
admin.site.register(IdSubmissionDetail, IdSubmissionDetailAdmin)
admin.site.register(Submission, SubmissionAdmin)
class PreapprovalAdmin(admin.ModelAdmin):
pass
admin.site.register(Preapproval, PreapprovalAdmin)
class TempIdAuthorsAdmin(admin.ModelAdmin):
ordering = ["-id"]
admin.site.register(TempIdAuthors, TempIdAuthorsAdmin)

View file

@ -1,102 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0">
<object pk="-4" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Cancelled</field>
</object>
<object pk="-3" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Dead</field>
</object>
<object pk="-2" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Posted by the Secretariat</field>
</object>
<object pk="-1" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Posted</field>
</object>
<object pk="0" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Ready To Post</field>
</object>
<object pk="1" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Uploaded</field>
</object>
<object pk="2" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">ID NITS Passed</field>
</object>
<object pk="3" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Initial Version Approval Required</field>
</object>
<object pk="4" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Submitter Authentication Required</field>
</object>
<object pk="5" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Manual Post Requested</field>
</object>
<object pk="6" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">External Meta-Data Required</field>
</object>
<object pk="7" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Internal Database Has Been Updated</field>
</object>
<object pk="8" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">ID Announcement Scheduled</field>
</object>
<object pk="9" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">ID Tracker Notification Scheduled</field>
</object>
<object pk="10" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Initial Version Approval Requested</field>
</object>
<object pk="101" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Plain text version does not exist</field>
</object>
<object pk="102" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">File size is larger than 20 MB</field>
</object>
<object pk="103" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Duplicate Internet-Draft submission is currently in process.</field>
</object>
<object pk="104" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Simultaneous submission from the same IP address</field>
</object>
<object pk="105" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Auth key does not match</field>
</object>
<object pk="106" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - No such Internet-Draft is currently in process</field>
</object>
<object pk="107" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Draft is not in an appropriate status for the requested page</field>
</object>
<object pk="108" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Unknown Request</field>
</object>
<object pk="109" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Invalid Email Address</field>
</object>
<object pk="110" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Direct Access is prohibited</field>
</object>
<object pk="201" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Invalid version number</field>
</object>
<object pk="202" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - Invalid filename</field>
</object>
<object pk="203" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error - The document failed idnits verification</field>
</object>
<object pk="204" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Creation Date must be within 3 days of the submission date.</field>
</object>
<object pk="205" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Error Not a valid submitter</field>
</object>
<object pk="206" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Incorrect Meta-Data</field>
</object>
<object pk="111" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">The document does not contain a legitimate filename that start with draft-*.</field>
</object>
<object pk="11" model="submit.idsubmissionstatus">
<field type="CharField" name="status_value">Initial Version Approved</field>
</object>
</django-objects>

View file

@ -1,14 +1,9 @@
import hashlib
import random
import os
import subprocess
import datetime
from django import forms
from django.core.validators import email_re
from django.forms.formsets import formset_factory
from django.conf import settings
from django.contrib.sites.models import Site
from django.template.loader import render_to_string
from django.utils.html import mark_safe
from django.core.urlresolvers import reverse as urlreverse
@ -17,44 +12,36 @@ import debug
from ietf.group.models import Group, Role
from ietf.doc.models import Document
from ietf.meeting.models import Meeting
from ietf.submit.models import IdSubmissionDetail, TempIdAuthors, Preapproval
from ietf.submit.utils import MANUAL_POST_REQUESTED, UPLOADED, AWAITING_AUTHENTICATION, POSTED, POSTED_BY_SECRETARIAT, submission_confirmation_email_list
from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName
from ietf.submit.utils import validate_submission_rev, validate_submission_document_date
from ietf.submit.parsers.pdf_parser import PDFParser
from ietf.submit.parsers.plain_parser import PlainParser
from ietf.submit.parsers.ps_parser import PSParser
from ietf.submit.parsers.xml_parser import XMLParser
from ietf.utils.mail import send_mail
from ietf.utils.draft import Draft
from ietf.utils.pipe import pipe
from ietf.utils.log import log
class UploadForm(forms.Form):
txt = forms.FileField(label=u'.txt format', required=True)
xml = forms.FileField(label=u'.xml format', required=False)
pdf = forms.FileField(label=u'.pdf format', required=False)
ps = forms.FileField(label=u'.ps format', required=False)
fieldsets = [('Upload a draft', ('txt', 'xml', 'pdf', 'ps'))]
class Media:
css = {'all': ("/css/liaisons.css", )}
def __init__(self, *args, **kwargs):
self.request=kwargs.pop('request', None)
self.remote_ip=self.request.META.get('REMOTE_ADDR', None)
def __init__(self, request, *args, **kwargs):
super(UploadForm, self).__init__(*args, **kwargs)
self.in_first_cut_off = False
self.idnits_message = None
self.shutdown = False
self.draft = None
self.filesize = None
self.group = None
self.file_type = []
self.read_dates()
def read_dates(self):
self.remote_ip = request.META.get('REMOTE_ADDR', None)
self.in_first_cut_off = False
self.cutoff_warning = ""
self.shutdown = False
self.set_cutoff_warnings()
self.group = None
self.parsed_draft = None
def set_cutoff_warnings(self):
now = datetime.datetime.utcnow()
first_cut_off = Meeting.get_first_cut_off()
second_cut_off = Meeting.get_second_cut_off()
@ -74,166 +61,100 @@ class UploadForm(forms.Form):
self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s.<br>The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday)
self.shutdown = True
def __unicode__(self):
return self.as_div()
def clean_file(self, field_name, parser_class):
f = self.cleaned_data[field_name]
if not f:
return f
def as_div(self):
return render_to_string('submit/submitform.html', {'form': self})
parsed_info = parser_class(f).critical_parse()
if parsed_info.errors:
raise forms.ValidationError(parsed_info.errors)
return f
def get_fieldsets(self):
if not self.fieldsets:
yield dict(name=None, fields=self)
else:
for fieldset, fields in self.fieldsets:
fieldset_dict = dict(name=fieldset, fields=[])
for field_name in fields:
if field_name in self.fields.keyOrder:
fieldset_dict['fields'].append(self[field_name])
if not fieldset_dict['fields']:
# if there is no fields in this fieldset, we continue to next fieldset
continue
yield fieldset_dict
def clean_txt(self):
txt_file = self.cleaned_data['txt']
if not txt_file:
return txt_file
parsed_info = PlainParser(txt_file).critical_parse()
if parsed_info.errors:
raise forms.ValidationError(parsed_info.errors)
self.filesize=txt_file.size
return txt_file
return self.clean_file("txt", PlainParser)
def clean_pdf(self):
pdf_file = self.cleaned_data['pdf']
if not pdf_file:
return pdf_file
parsed_info = PDFParser(pdf_file).critical_parse()
if parsed_info.errors:
raise forms.ValidationError(parsed_info.errors)
return pdf_file
return self.clean_file("pdf", PDFParser)
def clean_ps(self):
ps_file = self.cleaned_data['ps']
if not ps_file:
return ps_file
parsed_info = PSParser(ps_file).critical_parse()
if parsed_info.errors:
raise forms.ValidationError(parsed_info.errors)
return ps_file
return self.clean_file("ps", PSParser)
def clean_xml(self):
xml_file = self.cleaned_data['xml']
if not xml_file:
return xml_file
parsed_info = XMLParser(xml_file).critical_parse()
if parsed_info.errors:
raise forms.ValidationError(parsed_info.errors)
return xml_file
return self.clean_file("xml", XMLParser)
def clean(self):
if self.shutdown:
raise forms.ValidationError('The tool is shut down')
self.check_paths()
if self.cleaned_data.get('txt', None):
self.get_draft()
self.group=self.get_working_group()
self.check_previous_submission()
if self.draft.revision == '00' and self.in_first_cut_off:
# sanity check that paths exist (for development servers)
for s in ("IDSUBMIT_STAGING_PATH", "IDSUBMIT_IDNITS_BINARY",
"IDSUBMIT_REPOSITORY_PATH", "INTERNET_DRAFT_ARCHIVE_DIR"):
if not os.path.exists(getattr(settings, s)):
raise forms.ValidationError('%s defined in settings.py does not exist' % s)
if self.cleaned_data.get('txt'):
# try to parse it
txt_file = self.cleaned_data['txt']
txt_file.seek(0)
self.parsed_draft = Draft(txt_file.read(), txt_file.name)
txt_file.seek(0)
if not self.parsed_draft.filename:
raise forms.ValidationError("Draft parser could not extract a valid draft name from the .txt file")
# check group
self.group = self.deduce_group()
# check existing
existing = Submission.objects.filter(name=self.parsed_draft.filename, rev=self.parsed_draft.revision).exclude(state__in=("posted", "cancel"))
if existing:
raise forms.ValidationError(mark_safe('Submission with same name and revision is currently being processed. <a href="%s">Check the status here</a>' % urlreverse("submit_submission_status", kwargs={ 'submission_id': existing[0].pk })))
# cut-off
if self.parsed_draft.revision == '00' and self.in_first_cut_off:
raise forms.ValidationError(mark_safe(self.cutoff_warning))
self.check_tresholds()
# check thresholds
today = datetime.date.today()
self.check_submissions_tresholds(
"for the draft %s" % self.parsed_draft.filename,
dict(name=self.parsed_draft.filename, rev=self.parsed_draft.revision, submission_date=today),
settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME, settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME_SIZE,
)
self.check_submissions_tresholds(
"for the same submitter",
dict(remote_ip=self.remote_ip, submission_date=today),
settings.IDSUBMIT_MAX_DAILY_SAME_SUBMITTER, settings.IDSUBMIT_MAX_DAILY_SAME_SUBMITTER_SIZE,
)
if self.group:
self.check_submissions_tresholds(
"for the group \"%s\"" % (self.group.acronym),
dict(group=self.group, submission_date=today),
settings.IDSUBMIT_MAX_DAILY_SAME_GROUP, settings.IDSUBMIT_MAX_DAILY_SAME_GROUP_SIZE,
)
self.check_submissions_tresholds(
"across all submitters",
dict(submission_date=today),
settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS, settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS_SIZE,
)
return super(UploadForm, self).clean()
def check_tresholds(self):
filename = self.draft.filename
revision = self.draft.revision
remote_ip = self.remote_ip
today = datetime.date.today()
def check_submissions_tresholds(self, which, filter_kwargs, max_amount, max_size):
submissions = Submission.objects.filter(**filter_kwargs)
# Same draft by name
same_name = IdSubmissionDetail.objects.filter(filename=filename, revision=revision, submission_date=today)
if same_name.count() > settings.MAX_SAME_DRAFT_NAME:
raise forms.ValidationError('The same I-D cannot be submitted more than %s times a day' % settings.MAX_SAME_DRAFT_NAME)
if sum(i.filesize for i in same_name) > settings.MAX_SAME_DRAFT_NAME_SIZE * 1048576:
raise forms.ValidationError('The same I-D submission cannot exceed more than %s MByte a day' % settings.MAX_SAME_DRAFT_NAME_SIZE)
if len(submissions) > max_amount:
raise forms.ValidationError("Max submissions %s has been reached for today (maximum is %s submissions)." % (which, max_amount))
if sum(s.file_size for s in submissions) > max_size * 1024 * 1024:
raise forms.ValidationError("Max uploaded amount %s has been reached for today (maximum is %s MB)." % (which, max_size))
# Total from same ip
same_ip = IdSubmissionDetail.objects.filter(remote_ip=remote_ip, submission_date=today)
if same_ip.count() > settings.MAX_SAME_SUBMITTER:
raise forms.ValidationError('The same submitter cannot submit more than %s I-Ds a day' % settings.MAX_SAME_SUBMITTER)
if sum(i.filesize for i in same_ip) > settings.MAX_SAME_SUBMITTER_SIZE * 1048576:
raise forms.ValidationError('The same submitter cannot exceed more than %s MByte a day' % settings.MAX_SAME_SUBMITTER_SIZE)
# Total in same group
if self.group:
same_group = IdSubmissionDetail.objects.filter(group_acronym=self.group, submission_date=today)
if same_group.count() > settings.MAX_SAME_WG_DRAFT:
raise forms.ValidationError('The same working group I-Ds cannot be submitted more than %s times a day' % settings.MAX_SAME_WG_DRAFT)
if sum(i.filesize for i in same_group) > settings.MAX_SAME_WG_DRAFT_SIZE * 1048576:
raise forms.ValidationError('Total size of same working group I-Ds cannot exceed %s MByte a day' % settings.MAX_SAME_WG_DRAFT_SIZE)
# Total drafts for today
total_today = IdSubmissionDetail.objects.filter(submission_date=today)
if total_today.count() > settings.MAX_DAILY_SUBMISSION:
raise forms.ValidationError('The total number of today\'s submission has reached the maximum number of submission per day')
if sum(i.filesize for i in total_today) > settings.MAX_DAILY_SUBMISSION_SIZE * 1048576:
raise forms.ValidationError('The total size of today\'s submission has reached the maximum size of submission per day')
def check_paths(self):
self.staging_path = getattr(settings, 'IDSUBMIT_STAGING_PATH', None)
self.idnits = getattr(settings, 'IDSUBMIT_IDNITS_BINARY', None)
if not self.staging_path:
raise forms.ValidationError('IDSUBMIT_STAGING_PATH not defined in settings.py')
if not os.path.exists(self.staging_path):
raise forms.ValidationError('IDSUBMIT_STAGING_PATH defined in settings.py does not exist')
if not self.idnits:
raise forms.ValidationError('IDSUBMIT_IDNITS_BINARY not defined in settings.py')
if not os.path.exists(self.idnits):
raise forms.ValidationError('IDSUBMIT_IDNITS_BINARY defined in settings.py does not exist')
def check_previous_submission(self):
filename = self.draft.filename
revision = self.draft.revision
existing = IdSubmissionDetail.objects.filter(filename=filename, revision=revision,
status__pk__gte=0, status__pk__lt=100)
if existing:
raise forms.ValidationError(mark_safe('Duplicate Internet-Draft submission is currently in process. <a href="/submit/status/%s/">Check it here</a>' % existing[0].pk))
def get_draft(self):
if self.draft:
return self.draft
txt_file = self.cleaned_data['txt']
txt_file.seek(0)
self.draft = Draft(txt_file.read(), txt_file.name)
txt_file.seek(0)
return self.draft
def save(self):
for ext in ['txt', 'pdf', 'xml', 'ps']:
fd = self.cleaned_data[ext]
if not fd:
continue
self.file_type.append('.%s' % ext)
filename = os.path.join(self.staging_path, '%s-%s.%s' % (self.draft.filename, self.draft.revision, ext))
destination = open(filename, 'wb+')
for chunk in fd.chunks():
destination.write(chunk)
destination.close()
self.check_idnits()
return self.save_draft_info(self.draft)
def check_idnits(self):
filepath = os.path.join(self.staging_path, '%s-%s.txt' % (self.draft.filename, self.draft.revision))
#p = subprocess.Popen([self.idnits, '--submitcheck', '--nitcount', filepath], stdout=subprocess.PIPE)
cmd = "%s --submitcheck --nitcount %s" % (self.idnits, filepath)
code, out, err = pipe(cmd)
if code != 0:
log("idnits error: %s:\n Error %s: %s" %(cmd, code, err))
self.idnits_message = out
def get_working_group(self):
name = self.draft.filename
def deduce_group(self):
"""Figure out group from name or previously submitted draft, returns None if individual."""
name = self.parsed_draft.filename
existing_draft = Document.objects.filter(name=name, type="draft")
if existing_draft:
group = existing_draft[0].group
@ -245,11 +166,11 @@ class UploadForm(forms.Form):
if name.startswith('draft-ietf-') or name.startswith("draft-irtf-"):
components = name.split("-")
if len(components) < 3:
raise forms.ValidationError("The draft name \"%s\" is missing a third part, please rename it")
raise forms.ValidationError(u"The draft name \"%s\" is missing a third part, please rename it" % name)
if components[1] == "ietf":
group_type = "wg"
else:
elif components[1] == "irtf":
group_type = "rg"
# first check groups with dashes
@ -261,109 +182,51 @@ class UploadForm(forms.Form):
return Group.objects.get(acronym=components[2], type=group_type)
except Group.DoesNotExist:
raise forms.ValidationError('There is no active group with acronym \'%s\', please rename your draft' % components[2])
elif name.startswith("draft-iab-"):
return Group.objects.get(acronym="iab")
else:
return None
def save_draft_info(self, draft):
detail = IdSubmissionDetail.objects.create(
id_document_name=draft.get_title(),
filename=draft.filename,
revision=draft.revision,
txt_page_count=draft.get_pagecount(),
filesize=self.filesize,
creation_date=draft.get_creation_date(),
submission_date=datetime.date.today(),
idnits_message=self.idnits_message,
temp_id_document_tag=-1,
group_acronym=self.group,
remote_ip=self.remote_ip,
first_two_pages=''.join(draft.pages[:2]),
status_id=UPLOADED,
abstract=draft.get_abstract(),
file_type=','.join(self.file_type),
)
for order, author in enumerate(draft.get_author_list(), start=1):
full_name, first_name, middle_initial, last_name, name_suffix, email, company = author
# save full name
TempIdAuthors.objects.create(
id_document_tag=-1,
first_name=full_name.strip(),
email_address=(email or "").strip(),
author_order=order,
submission=detail)
return detail
class AutoPostForm(forms.Form):
class NameEmailForm(forms.Form):
"""For validating supplied submitter and author information."""
name = forms.CharField(required=True)
email = forms.EmailField(label=u'Email address', required=True)
email = forms.EmailField(label=u'Email address')
def __init__(self, *args, **kwargs):
self.draft = kwargs.pop('draft', None)
self.validation = kwargs.pop('validation', None)
self.replaces = kwargs.pop('replaces', None)
super(AutoPostForm, self).__init__(*args, **kwargs)
email_required = kwargs.pop("email_required", True)
super(NameEmailForm, self).__init__(*args, **kwargs)
def get_author_buttons(self):
buttons = []
for i in self.validation.authors:
buttons.append('<input type="button" data-name="%(name)s" data-email="%(email)s" value="%(name)s" />'
% dict(name=i.get_full_name(),
email=i.email()[1] or ''))
return "".join(buttons)
self.fields["email"].required = email_required
self.fields["name"].widget.attrs["class"] = "name"
self.fields["email"].widget.attrs["class"] = "email"
def save(self, request):
self.save_submitter_info()
self.save_new_draft_info()
self.send_confirmation_mail(request)
def clean_name(self):
return self.cleaned_data["name"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
def send_confirmation_mail(self, request):
subject = 'Confirmation for Auto-Post of I-D %s' % self.draft.filename
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = submission_confirmation_email_list(self.draft)
def clean_email(self):
return self.cleaned_data["email"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_confirm', kwargs=dict(submission_id=self.draft.submission_id, auth_key=self.draft.auth_key))
status_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash', kwargs=dict(submission_id=self.draft.submission_id, submission_hash=self.draft.get_hash()))
send_mail(request, to_email, from_email, subject, 'submit/confirm_autopost.txt',
{ 'draft': self.draft, 'confirm_url': confirm_url, 'status_url': status_url })
def cleaned_line(self):
line = self.cleaned_data["name"]
email = self.cleaned_data.get("email")
if email:
line += u" <%s>" % email
return line
def save_submitter_info(self):
return TempIdAuthors.objects.create(
id_document_tag=self.draft.temp_id_document_tag,
first_name=self.cleaned_data['name'],
email_address=self.cleaned_data['email'],
author_order=0,
submission=self.draft,
)
class EditSubmissionForm(forms.ModelForm):
title = forms.CharField(required=True, max_length=255)
rev = forms.CharField(label=u'Revision', max_length=2, required=True)
document_date = forms.DateField(required=True)
pages = forms.IntegerField(required=True)
abstract = forms.CharField(widget=forms.Textarea, required=True)
def save_new_draft_info(self):
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
self.draft.auth_key = hashlib.sha1(salt+self.cleaned_data['email']).hexdigest()
self.draft.status_id = AWAITING_AUTHENTICATION
self.draft.save()
note = forms.CharField(label=mark_safe(u'Comment to<br/> the Secretariat'), widget=forms.Textarea, required=False)
class MetaDataForm(AutoPostForm):
title = forms.CharField(label=u'Title', required=True)
version = forms.CharField(label=u'Version', required=True)
creation_date = forms.DateField(label=u'Creation date', required=True)
pages = forms.IntegerField(label=u'Pages', required=True)
abstract = forms.CharField(label=u'Abstract', widget=forms.Textarea, required=True)
name = forms.CharField(required=True)
email = forms.EmailField(label=u'Email address', required=True)
comments = forms.CharField(label=u'Comments to the secretariat', widget=forms.Textarea, required=False)
fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'name', 'email', 'comments']
def __init__(self, *args, **kwargs):
super(MetaDataForm, self).__init__(*args, **kwargs)
self.set_initials()
self.authors = self.get_initial_authors()
class Meta:
model = Submission
fields = ['title', 'rev', 'document_date', 'pages', 'abstract', 'note']
def get_initial_authors(self):
authors=[]
@ -379,117 +242,33 @@ class MetaDataForm(AutoPostForm):
if email and not email_re.search(email):
author['errors']['email'] = 'Enter a valid e-mail address'
if name or email:
author.update({'get_full_name': name,
'email': (name, email),
author.update({'name': name,
'email': email,
'index': index,
})
authors.append(author)
authors.sort(key=lambda x: x['index'])
return authors
def set_initials(self):
self.fields['pages'].initial=self.draft.txt_page_count
self.fields['creation_date'].initial=self.draft.creation_date
self.fields['version'].initial=self.draft.revision
self.fields['abstract'].initial=self.draft.abstract
self.fields['title'].initial=self.draft.id_document_name
def clean_rev(self):
rev = self.cleaned_data["rev"]
def clean_creation_date(self):
creation_date = self.cleaned_data.get('creation_date', None)
if not creation_date:
return None
submit_date = self.draft.submission_date
if (creation_date + datetime.timedelta(days=3) < submit_date or
creation_date - datetime.timedelta(days=3) > submit_date):
raise forms.ValidationError('Creation Date must be within 3 days of submission date')
return creation_date
if len(rev) == 1:
rev = "0" + rev
def clean_version(self):
version = self.cleaned_data.get('version', None)
if not version:
return None
if len(version) > 2:
raise forms.ValidationError('Version field is not in NN format')
try:
version_int = int(version)
except ValueError:
raise forms.ValidationError('Version field is not in NN format')
if version_int > 99 or version_int < 0:
raise forms.ValidationError('Version must be set between 00 and 99')
existing_revisions = [int(i.rev) for i in Document.objects.filter(name=self.draft.filename)]
expected = 0
if existing_revisions:
expected = max(existing_revisions) + 1
if version_int != expected:
raise forms.ValidationError('Invalid Version Number (Version %02d is expected)' % expected)
return version
error = validate_submission_rev(self.instance.name, rev)
if error:
raise forms.ValidationError(error)
def clean(self):
if bool([i for i in self.authors if i['errors']]):
raise forms.ValidationError('Please fix errors in author list')
return super(MetaDataForm, self).clean()
return rev
def get_authors(self):
if not self.is_bound:
return self.validation.get_authors()
else:
return self.authors
def move_docs(self, draft, revision):
old_revision = draft.revision
for ext in draft.file_type.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (draft.filename, old_revision, ext))
dest = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (draft.filename, revision, ext))
os.rename(source, dest)
def save_new_draft_info(self):
draft = self.draft
draft.id_document_name = self.cleaned_data['title']
if draft.revision != self.cleaned_data['version']:
self.move_docs(draft, self.cleaned_data['version'])
draft.revision = self.cleaned_data['version']
draft.creation_date = self.cleaned_data['creation_date']
draft.txt_page_count = self.cleaned_data['pages']
draft.abstract = self.cleaned_data['abstract']
draft.comment_to_sec = self.cleaned_data['comments']
draft.status_id = MANUAL_POST_REQUESTED
draft.save()
# sync authors
draft.tempidauthors_set.all().delete()
self.save_submitter_info() # submitter is author 0
for i, author in enumerate(self.authors):
TempIdAuthors.objects.create(
id_document_tag=draft.temp_id_document_tag,
first_name=author["get_full_name"], # save full name
email_address=author["email"][1],
author_order=i + 1,
submission=draft)
def save(self, request):
self.save_new_draft_info()
self.send_mail_to_secretariat(request)
def send_mail_to_secretariat(self, request):
subject = 'Manual Post Requested for %s' % self.draft.filename
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = settings.IDSUBMIT_TO_EMAIL
cc = [self.cleaned_data['email']]
cc += [i['email'][1] for i in self.authors]
if self.draft.group_acronym:
cc += [r.email.address for r in Role.objects.filter(group=self.draft.group_acronym, name="chair").select_related("email")]
cc = list(set(cc))
submitter = self.draft.tempidauthors_set.get(author_order=0)
send_mail(request, to_email, from_email, subject, 'submit/manual_post_mail.txt', {
'form': self,
'draft': self.draft,
'url': settings.IDTRACKER_BASE_URL + urlreverse('draft_status', kwargs=dict(submission_id=self.draft.submission_id)),
'submitter': submitter
},
cc=cc)
def clean_document_date(self):
document_date = self.cleaned_data['document_date']
error = validate_submission_document_date(self.instance.submission_date, document_date)
if error:
raise forms.ValidationError(error)
return document_date
class PreapprovalForm(forms.Form):
name = forms.CharField(max_length=255, required=True, label="Pre-approved name", initial="draft-ietf-")
@ -515,7 +294,7 @@ class PreapprovalForm(forms.Form):
if Preapproval.objects.filter(name=n):
raise forms.ValidationError("Pre-approval for this name already exists.")
if IdSubmissionDetail.objects.filter(status__in=[POSTED, POSTED_BY_SECRETARIAT ], filename=n):
if Submission.objects.filter(state="posted", name=n):
raise forms.ValidationError("A draft with this name has already been submitted and accepted. A pre-approval would not make any difference.")
return n

View file

@ -1,34 +0,0 @@
#!/usr/bin/python
# boiler plate
import os, sys
ietf_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../ietf'))
sys.path.insert(0, ietf_path)
from django.core.management import setup_environ
import settings
setup_environ(settings)
# script
from django.core.serializers import serialize
from django.db.models import Q
def output(name, qs):
try:
f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.xml" % name), 'w')
f.write(serialize("xml", qs, indent=4))
f.close()
except:
from django.db import connection
from pprint import pprint
pprint(connection.queries)
raise
# pick all name models directly out of the module
names = []
from ietf.submit.models import IdSubmissionStatus
output("idsubmissionstatus", IdSubmissionStatus.objects.all())

152
ietf/submit/mail.py Normal file
View file

@ -0,0 +1,152 @@
from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
from django.contrib.sites.models import Site
from django.template.loader import render_to_string
from ietf.utils.mail import send_mail, send_mail_message
from ietf.doc.models import Document
from ietf.person.models import Person
from ietf.group.models import Role
from ietf.message.models import Message
def submission_confirmation_email_list(submission):
try:
doc = Document.objects.get(name=submission.name)
email_list = [i.author.formatted_email() for i in doc.documentauthor_set.all()]
except Document.DoesNotExist:
email_list = [u"%s <%s>" % (author["name"], author["email"])
for author in submission.authors_parsed() if author["email"]]
if submission.submitter_parsed()["email"] and submission.submitter not in email_list:
email_list.append(submission.submitter)
return email_list
def send_submission_confirmation(request, submission):
subject = 'Confirm submission of I-D %s' % submission.name
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = submission_confirmation_email_list(submission)
confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_key=submission.auth_key))
status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_key=submission.access_key))
send_mail(request, to_email, from_email, subject, 'submit/confirm_submission.txt', {
'submission': submission,
'confirm_url': confirm_url,
'status_url': status_url,
})
return to_email
def send_full_url(request, submission):
subject = 'Full URL for managing submission of draft %s' % submission.name
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = submission_confirmation_email_list(submission)
url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash',
kwargs=dict(submission_id=submission.pk,
access_key=submission.access_key))
send_mail(request, to_email, from_email, subject, 'submit/full_url.txt', {
'submission': submission,
'url': url,
})
return to_email
def send_approval_request_to_group(request, submission):
subject = 'New draft waiting for approval: %s' % submission.name
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = [r.formatted_email() for r in Role.objects.filter(group=submission.group, name="chair").select_related("email", "person")]
if not to_email:
return to_email
send_mail(request, to_email, from_email, subject, 'submit/approval_request.txt', {
'submission': submission,
'domain': Site.objects.get_current().domain,
})
return to_email
def send_manual_post_request(request, submission, errors):
subject = u'Manual Post Requested for %s' % submission.name
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = settings.IDSUBMIT_TO_EMAIL
cc = [submission.submitter]
cc += [u"%s <%s>" % (author["name"], author["email"])
for author in submission.authors_parsed() if author["email"]]
if submission.group:
cc += [r.formatted_email() for r in Role.objects.filter(group=submission.group, name="chair").select_related("email", "person")]
cc = list(set(cc))
send_mail(request, to_email, from_email, subject, 'submit/manual_post_request.txt', {
'submission': submission,
'url': settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status', kwargs=dict(submission_id=submission.pk)),
'errors': errors,
}, cc=cc)
def announce_to_lists(request, submission):
m = Message()
m.by = Person.objects.get(name="(System)")
if request.user.is_authenticated():
try:
m.by = request.user.get_profile()
except Person.DoesNotExist:
pass
m.subject = 'I-D Action: %s-%s.txt' % (submission.name, submission.rev)
m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL
if submission.group and submission.group.list_email:
m.cc = submission.group.list_email
m.body = render_to_string('submit/announce_to_lists.txt',
dict(submission=submission,
settings=settings))
m.save()
m.related_docs.add(Document.objects.get(name=submission.name))
send_mail_message(request, m)
def announce_new_version(request, submission, draft, state_change_msg):
to_email = []
if draft.notify:
to_email.append(draft.notify)
if draft.ad:
to_email.append(draft.ad.role_email("ad").address)
if draft.stream_id == "iab":
to_email.append("IAB Stream <iab-stream@iab.org>")
elif draft.stream_id == "ise":
to_email.append("Independent Submission Editor <rfc-ise@rfc-editor.org>")
elif draft.stream_id == "irtf":
to_email.append("IRSG <irsg@irtf.org>")
# if it has been sent to the RFC Editor, keep them in the loop
if draft.get_state_slug("draft-iesg") in ("ann", "rfcqueue"):
to_email.append("RFC Editor <rfc-editor@rfc-editor.org>")
active_ballot = draft.active_ballot()
if active_ballot:
for ad, pos in active_ballot.active_ad_positions().iteritems():
if pos and pos.pos_id == "discuss":
to_email.append(ad.role_email("ad").address)
if to_email:
subject = 'New Version Notification - %s-%s.txt' % (submission.name, submission.rev)
from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt',
{'submission': submission,
'msg': state_change_msg})
def announce_to_authors(request, submission):
authors = [u"%s <%s>" % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]]
to_email = list(set(submission_confirmation_email_list(submission) + authors))
from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
subject = 'New Version Notification for %s-%s.txt' % (submission.name, submission.rev)
if submission.group:
group = submission.group.acronym
elif submission.name.startswith('draft-iesg'):
group = 'IESG'
else:
group = 'Individual Submission'
send_mail(request, to_email, from_email, subject, 'submit/announce_to_authors.txt',
{'submission': submission,
'group': group})

View file

@ -0,0 +1,297 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
nullable_string_fields = [
"id_document_name",
"comment_to_sec",
"filename",
"revision",
"replaces",
"file_type",
"abstract",
"idnits_message",
"first_two_pages",
"remote_ip",
"auth_key",
"submission_hash",
]
for f in nullable_string_fields:
orm.IdSubmissionDetail.objects.filter(**{ f: None}).update(**{ f: ""})
def backwards(self, orm):
pass
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'related': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'reversely_related_document_set'", 'blank': 'True', 'through': "orm['doc.RelatedDocument']", 'to': "orm['doc.DocAlias']"}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'group.ietfwg': {
'Meta': {'object_name': 'IETFWG', 'db_table': "'group_group'", '_ormbases': ['group.Group'], 'proxy': 'True'}
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
'submit.idsubmissiondetail': {
'Meta': {'object_name': 'IdSubmissionDetail'},
'abstract': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'comment_to_sec': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'file_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'filename': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'filesize': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'first_two_pages': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'group_acronym': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id_document_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'idnits_failed': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'idnits_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'invalid_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'last_updated_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'last_updated_time': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}),
'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'revision': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}),
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}),
'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'submission_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'submission_hash': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'submission_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'temp_id_document_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'txt_page_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'warning_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'wg_submission': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
},
'submit.idsubmissionstatus': {
'Meta': {'object_name': 'IdSubmissionStatus'},
'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'submit.preapproval': {
'Meta': {'object_name': 'Preapproval'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'submit.tempidauthors': {
'Meta': {'object_name': 'TempIdAuthors'},
'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'id_document_tag': ('django.db.models.fields.IntegerField', [], {}),
'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionDetail']"})
}
}
complete_apps = ['submit']

View file

@ -0,0 +1,327 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
depends_on = (
("name", "0017_populate_draftsubmissionstate"),
)
def forwards(self, orm):
db.rename_table('submit_idsubmissiondetail', 'submit_submission')
db.rename_column('submit_submission', 'submission_id', "id")
db.rename_column('submit_submission', 'id_document_name', "title")
db.alter_column('submit_submission', 'title', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True))
db.rename_column('submit_submission', 'creation_date', "document_date")
db.rename_column('submit_submission', 'group_acronym_id', 'group_id')
db.rename_column('submit_submission', 'txt_page_count', 'pages')
db.rename_column('submit_submission', 'comment_to_sec', 'note')
db.alter_column('submit_submission', 'note', self.gf('django.db.models.fields.TextField')(default='', blank=True))
db.rename_column('submit_submission', 'filename', 'name')
db.alter_column('submit_submission', 'name', self.gf('django.db.models.fields.CharField')(db_index=True, default='', max_length=255, blank=True))
db.rename_column('submit_submission', 'filesize', 'file_size')
db.rename_column('submit_submission', 'revision', 'rev')
db.alter_column('submit_submission', 'rev', self.gf('django.db.models.fields.CharField')(default='', max_length=3, blank=True))
db.delete_column('submit_submission', 'idnits_failed')
db.delete_column('submit_submission', 'invalid_version')
db.delete_column('submit_submission', 'temp_id_document_tag')
db.delete_column('submit_submission', 'error_message')
db.delete_column('submit_submission', 'wg_submission')
db.delete_column('submit_submission', 'warning_message')
db.delete_column('submit_submission', 'last_updated_date')
db.delete_column('submit_submission', 'last_updated_time')
db.add_column('submit_submission', 'state', self.gf('django.db.models.fields.related.ForeignKey')(default='uploaded', to=orm['name.DraftSubmissionStateName']))
db.add_column('submit_submission', 'authors', self.gf('django.db.models.fields.TextField')(default=""))
db.add_column('submit_submission', 'submitter', self.gf('django.db.models.fields.CharField')(max_length=255, default=""))
db.alter_column('submit_submission', 'submission_date', self.gf('django.db.models.fields.DateField')())
db.alter_column('submit_submission', 'replaces', self.gf('django.db.models.fields.CharField')(default='', max_length=255))
db.rename_column('submit_submission', 'file_type', 'file_types')
db.alter_column('submit_submission', 'file_types', self.gf('django.db.models.fields.CharField')(default='', max_length=50))
db.alter_column('submit_submission', 'abstract', self.gf('django.db.models.fields.TextField')(default=''))
db.alter_column('submit_submission', 'idnits_message', self.gf('django.db.models.fields.TextField')(default=''))
db.alter_column('submit_submission', 'first_two_pages', self.gf('django.db.models.fields.TextField')(default=''))
db.alter_column('submit_submission', 'remote_ip', self.gf('django.db.models.fields.CharField')(default='', max_length=100))
db.alter_column('submit_submission', 'auth_key', self.gf('django.db.models.fields.CharField')(default='', max_length=255))
db.rename_column('submit_submission', 'submission_hash', "access_key")
db.alter_column('submit_submission', 'access_key', self.gf('django.db.models.fields.CharField')(default='', max_length=255))
db.create_table('submit_submissionevent', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('submission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.Submission'])),
('time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
('by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'], null=True, blank=True)),
('desc', self.gf('django.db.models.fields.TextField')()),
))
db.send_create_signal('submit', ['SubmissionEvent'])
def backwards(self, orm):
pass
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.draftsubmissionstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
'submit.submission': {
'Meta': {'object_name': 'Submission'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
'first_two_pages': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'default': "'uploaded'", 'to': "orm['name.DraftSubmissionStateName']"}),
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}),
'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'submit.submissionevent': {
'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'desc': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'submit.idsubmissionstatus': {
'Meta': {'object_name': 'IdSubmissionStatus'},
'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'submit.preapproval': {
'Meta': {'object_name': 'Preapproval'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'submit.tempidauthors': {
'Meta': {'object_name': 'TempIdAuthors'},
'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'id_document_tag': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"})
}
}
complete_apps = ['submit']

View file

@ -0,0 +1,392 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
import hashlib, random, time
from django.conf import settings
state_map = dict((n.slug, n) for n in orm["name.DraftSubmissionStateName"].objects.all())
status_map = {
-4: "cancel",
-2: "posted", # Secretariat
-1: "posted",
1: "uploaded",
4: "auth",
5: "manual",
10: "grp-appr",
}
from django.core.validators import validate_email, ValidationError
for d in orm.Submission.objects.all().iterator():
if not d.name:
# get rid of a few mishaps that seem to have been
# accepted without a name
d.delete()
continue
# map state
state = state_map[status_map.get(d.status_id, "cancel")]
# map authors
authors = []
submitter = ""
submitter_email = ""
for a in d.tempidauthors_set.order_by("author_order"):
parts = (a.first_name or '', a.middle_initial or '', a.last_name or '', a.name_suffix or '')
name = u" ".join(x.strip() for x in parts if x.strip())
email = a.email_address
orig = email
# clean
name = name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
email = email.replace("Email:", "").replace("E-mail:", "").replace("mailto:", "").replace(">", "").replace("<", "").replace("\n", "").replace(" ", "").rstrip(",").lstrip(":").strip(".").rstrip(";").rstrip("-").rstrip("\"").lstrip("\"").rstrip("@")
if email:
line = u"%s <%s>" % (name, email)
else:
line = name
if a.author_order == 0:
submitter = line
submitter_email = email
else:
authors.append(line)
# make sure we always have a key
access_key = d.access_key
if not access_key:
access_key = hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random()) + str(d.name.encode("utf-8"))).hexdigest()[:32]
# fill in submission event
submitter_person = None
if d.submitter_tag:
try:
submitter_person = orm["person.Person"].objects.get(id=d.submitter_tag)
except models.ObjectDoesNotExist:
pass
if submitter_email:
try:
submitter_person = orm["person.Person"].objects.get(email__address=submitter_email)
except models.ObjectDoesNotExist:
pass
if submitter_person and not submitter:
submitter = submitter_person.name
if submitter_person:
orm.SubmissionEvent.objects.get_or_create(
submission=d,
time=d.submission_date,
by=submitter_person,
desc="Uploaded submission",
)
# fill in manual post events
if d.status_id == -2 and d.man_posted_by:
if d.man_posted_by == "Amy Vezza":
d.man_posted_by = "Amy K. Vezza"
try:
by = orm["person.Person"].objects.get(name=d.man_posted_by)
orm.SubmissionEvent.objects.get_or_create(
submission=d,
time=d.man_posted_date or d.submission_date,
by=by,
desc="Posted submission manually",
)
except models.ObjectDoesNotExist:
pass
# update the new revision doc events that are set to
# "(System)" with our newly discovered submitter
if submitter_person and d.state_id == "posted":
orm["doc.NewRevisionDocEvent"].objects.filter(doc=d.name, rev=d.rev, type="new_revision", by=0).update(by=submitter_person)
# update the submission itself
orm.Submission.objects.filter(pk=d.pk).update(
submitter=submitter,
state=state,
authors="\n".join(authors),
access_key=access_key,
)
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.draftsubmissionstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
'submit.submission': {
'Meta': {'object_name': 'Submission'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
'first_two_pages': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'default': "'uploaded'", 'to': "orm['name.DraftSubmissionStateName']"}),
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}),
'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'submit.submissionevent': {
'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'desc': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'submit.idsubmissionstatus': {
'Meta': {'object_name': 'IdSubmissionStatus'},
'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'submit.preapproval': {
'Meta': {'object_name': 'Preapproval'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'submit.tempidauthors': {
'Meta': {'object_name': 'TempIdAuthors'},
'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'id_document_tag': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"})
}
}
complete_apps = ['submit']

View file

@ -0,0 +1,301 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting model 'TempIdAuthors'
db.delete_table('submit_tempidauthors')
# Deleting model 'IdSubmissionStatus'
db.delete_table('submit_idsubmissionstatus')
# Deleting field 'Submission.man_posted_date'
db.delete_column('submit_submission', 'man_posted_date')
# Deleting field 'Submission.status'
db.delete_column('submit_submission', 'status_id')
# Deleting field 'Submission.submitter_tag'
db.delete_column('submit_submission', 'submitter_tag')
# Deleting field 'Submission.sub_email_priority'
db.delete_column('submit_submission', 'sub_email_priority')
# Deleting field 'Submission.man_posted_by'
db.delete_column('submit_submission', 'man_posted_by')
def backwards(self, orm):
# Adding model 'TempIdAuthors'
db.create_table('submit_tempidauthors', (
('last_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('last_modified_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
('id_document_tag', self.gf('django.db.models.fields.IntegerField')(default=-1)),
('last_modified_time', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
('email_address', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('middle_initial', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('first_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('submission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.Submission'])),
('name_suffix', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('author_order', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
))
db.send_create_signal('submit', ['TempIdAuthors'])
# Adding model 'IdSubmissionStatus'
db.create_table('submit_idsubmissionstatus', (
('status_value', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('status_id', self.gf('django.db.models.fields.IntegerField')(primary_key=True)),
))
db.send_create_signal('submit', ['IdSubmissionStatus'])
# Adding field 'Submission.man_posted_date'
db.add_column('submit_submission', 'man_posted_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False)
# Adding field 'Submission.status'
db.add_column('submit_submission', 'status', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.IdSubmissionStatus'], null=True, db_column='status_id', blank=True), keep_default=False)
# Adding field 'Submission.submitter_tag'
db.add_column('submit_submission', 'submitter_tag', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
# Adding field 'Submission.sub_email_priority'
db.add_column('submit_submission', 'sub_email_priority', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
# Adding field 'Submission.man_posted_by'
db.add_column('submit_submission', 'man_posted_by', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False)
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.draftsubmissionstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['name.DraftSubmissionStateName']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
'submit.preapproval': {
'Meta': {'object_name': 'Preapproval'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'submit.submission': {
'Meta': {'object_name': 'Submission'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
'first_two_pages': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DraftSubmissionStateName']"}),
'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'submit.submissionevent': {
'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'desc': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
}
}
complete_apps = ['submit']

View file

@ -5,64 +5,73 @@ from django.db import models
from ietf.person.models import Person
from ietf.group.models import Group
from ietf.name.models import DraftSubmissionStateName
from ietf.utils.uniquekey import generate_unique_key
class IdSubmissionStatus(models.Model):
status_id = models.IntegerField(primary_key=True)
status_value = models.CharField(blank=True, max_length=255)
def parse_email_line(line):
"""Split line on the form 'Some Name <email@example.com>'"""
m = re.match("([^<]+) <([^>]+)>$", line)
if m:
return dict(name=m.group(1), email=m.group(2))
else:
return dict(name=line, email="")
class Submission(models.Model):
state = models.ForeignKey(DraftSubmissionStateName)
remote_ip = models.CharField(max_length=100, blank=True)
access_key = models.CharField(max_length=255, default=generate_unique_key)
auth_key = models.CharField(max_length=255, blank=True)
# draft metadata
name = models.CharField(max_length=255, db_index=True)
group = models.ForeignKey(Group, null=True, blank=True)
title = models.CharField(max_length=255, blank=True)
abstract = models.TextField(blank=True)
rev = models.CharField(max_length=3, blank=True)
pages = models.IntegerField(null=True, blank=True)
authors = models.TextField(blank=True, help_text="List of author names and emails, one author per line, e.g. \"John Doe &lt;john@example.org&gt;\"")
note = models.TextField(blank=True)
replaces = models.CharField(max_length=255, blank=True)
first_two_pages = models.TextField(blank=True)
file_types = models.CharField(max_length=50, blank=True)
file_size = models.IntegerField(null=True, blank=True)
document_date = models.DateField(null=True, blank=True)
submission_date = models.DateField(default=datetime.date.today)
submitter = models.CharField(max_length=255, blank=True, help_text="Name and email of submitter, e.g. \"John Doe &lt;john@example.org&gt;\"")
idnits_message = models.TextField(blank=True)
def __unicode__(self):
return self.status_value
return u"%s-%s" % (self.name, self.rev)
class IdSubmissionDetail(models.Model):
submission_id = models.AutoField(primary_key=True)
temp_id_document_tag = models.IntegerField(null=True, blank=True)
status = models.ForeignKey(IdSubmissionStatus, db_column='status_id', null=True, blank=True)
last_updated_date = models.DateField(null=True, blank=True)
last_updated_time = models.CharField(null=True, blank=True, max_length=25)
id_document_name = models.CharField(null=True, blank=True, max_length=255)
group_acronym = models.ForeignKey(Group, null=True, blank=True)
filename = models.CharField(null=True, blank=True, max_length=255, db_index=True)
creation_date = models.DateField(null=True, blank=True)
submission_date = models.DateField(null=True, blank=True)
remote_ip = models.CharField(null=True, blank=True, max_length=100)
revision = models.CharField(null=True, blank=True, max_length=3)
submitter_tag = models.IntegerField(null=True, blank=True)
auth_key = models.CharField(null=True, blank=True, max_length=255)
idnits_message = models.TextField(null=True, blank=True)
file_type = models.CharField(null=True, blank=True, max_length=50)
comment_to_sec = models.TextField(null=True, blank=True)
abstract = models.TextField(null=True, blank=True)
txt_page_count = models.IntegerField(null=True, blank=True)
error_message = models.CharField(null=True, blank=True, max_length=255)
warning_message = models.TextField(null=True, blank=True)
wg_submission = models.IntegerField(null=True, blank=True)
filesize = models.IntegerField(null=True, blank=True)
man_posted_date = models.DateField(null=True, blank=True)
man_posted_by = models.CharField(null=True, blank=True, max_length=255)
first_two_pages = models.TextField(null=True, blank=True)
sub_email_priority = models.IntegerField(null=True, blank=True)
invalid_version = models.IntegerField(null=True, blank=True)
idnits_failed = models.IntegerField(null=True, blank=True)
submission_hash = models.CharField(null=True, blank=True, max_length=255)
replaces = models.CharField(null=True, blank=True, max_length=255)
def authors_parsed(self):
res = []
for line in self.authors.replace("\r", "").split("\n"):
line = line.strip()
if line:
res.append(parse_email_line(line))
return res
def submitter_parsed(self):
return parse_email_line(self.submitter)
class SubmissionEvent(models.Model):
submission = models.ForeignKey(Submission)
time = models.DateTimeField(default=datetime.datetime.now)
by = models.ForeignKey(Person, null=True, blank=True)
desc = models.TextField()
def __unicode__(self):
return u"%s-%s" % (self.filename, self.revision)
return u"%s %s by %s at %s" % (self.submission.name, self.desc, self.by.plain_name() if self.by else "(unknown)", self.time)
def create_hash(self):
self.submission_hash = hashlib.md5(settings.SECRET_KEY + self.filename).hexdigest()
class Meta:
ordering = ("-time", "-id")
def get_hash(self):
if not self.submission_hash:
self.create_hash()
self.save()
return self.submission_hash
def create_submission_hash(sender, instance, **kwargs):
instance.create_hash()
models.signals.pre_save.connect(create_submission_hash, sender=IdSubmissionDetail)
class Preapproval(models.Model):
"""Pre-approved draft submission name."""
@ -72,25 +81,3 @@ class Preapproval(models.Model):
def __unicode__(self):
return self.name
class TempIdAuthors(models.Model):
id_document_tag = models.IntegerField()
first_name = models.CharField(blank=True, max_length=255) # with new schema, this contains the full name while the other name fields are empty to avoid loss of information
last_name = models.CharField(blank=True, max_length=255)
email_address = models.CharField(blank=True, max_length=255)
last_modified_date = models.DateField(null=True, blank=True)
last_modified_time = models.CharField(blank=True, max_length=100)
author_order = models.IntegerField(null=True, blank=True)
submission = models.ForeignKey(IdSubmissionDetail)
middle_initial = models.CharField(blank=True, max_length=255, null=True)
name_suffix = models.CharField(blank=True, max_length=255, null=True)
def email(self):
return (self.get_full_name(), self.email_address)
def get_full_name(self):
parts = (self.first_name or '', self.middle_initial or '', self.last_name or '', self.name_suffix or '')
return u" ".join(x.strip() for x in parts if x.strip())
def __unicode__(self):
return u"%s <%s>" % self.email()

View file

@ -1,26 +1,28 @@
import re
CUTOFF_HOUR = 17
class MetaDataDraft(object):
revision = None
filename = None
class MetaData(object):
rev = None
name = None
group = None
filesize = None
file_size = None
first_two_pages = None
page_count = None
pages = None
submission_date = None
creation_date = None
document_date = None
authors = None
class ParseInfo(object):
"""Collect errors from a parse"""
def __init__(self):
self.errors = []
# warnings are currently unused by the parsers
self.warnings = {}
self.metadraft = MetaDataDraft()
# the metadata fields are currently unused, i.e. the plain
# text parser fills in some fields but they are not used
# anywhere (instead the draft parser is used for .txt and the
# other file types have no actual parsing at the moment)
self.metadata = MetaData()
def add_error(self, error_str):
self.errors.append(error_str)

View file

@ -17,14 +17,14 @@ class PlainParser(FileParser):
super(PlainParser, self).critical_parse()
self.parse_max_size()
self.parse_file_charset()
self.parse_filename()
self.parse_name()
return self.parsed_info
def parse_max_size(self):
if self.fd.size > settings.MAX_PLAIN_DRAFT_SIZE:
self.parsed_info.add_error('File size is larger than %s' % filesizeformat(settings.MAX_PLAIN_DRAFT_SIZE))
self.parsed_info.metadraft.filesize = self.fd.size
self.parsed_info.metadraft.submission_date = datetime.date.today()
if self.fd.size > settings.IDSUBMIT_MAX_PLAIN_DRAFT_SIZE:
self.parsed_info.add_error('File size is larger than %s' % filesizeformat(settings.IDSUBMIT_MAX_PLAIN_DRAFT_SIZE))
self.parsed_info.metadata.file_size = self.fd.size
self.parsed_info.metadata.submission_date = datetime.date.today()
def parse_file_charset(self):
import magic
@ -40,9 +40,9 @@ class PlainParser(FileParser):
magic.magic_load(m.cookie, None)
filetype = m.from_buffer(content)
if not 'ascii' in filetype:
self.parsed_info.add_error('A plain text document must be submitted.')
self.parsed_info.add_error('A plain text ASCII document must be submitted.')
def parse_filename(self):
def parse_name(self):
self.fd.file.seek(0)
draftre = re.compile('(draft-\S+)')
revisionre = re.compile('.*-(\d+)$')
@ -53,24 +53,24 @@ class PlainParser(FileParser):
match = draftre.search(line)
if not match:
continue
filename = match.group(1)
filename = re.sub('^[^\w]+', '', filename)
filename = re.sub('[^\w]+$', '', filename)
filename = re.sub('\.txt$', '', filename)
extra_chars = re.sub('[0-9a-z\-]', '', filename)
name = match.group(1)
name = re.sub('^[^\w]+', '', name)
name = re.sub('[^\w]+$', '', name)
name = re.sub('\.txt$', '', name)
extra_chars = re.sub('[0-9a-z\-]', '', name)
if extra_chars:
if len(extra_chars) == 1:
self.parsed_info.add_error((u'The filename contains a disallowed character: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) +
self.parsed_info.add_error((u'The name contains a disallowed character: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) +
u'(see http://www.ietf.org/id-info/guidelines.html#naming for details).')
else:
self.parsed_info.add_error((u'The filename contains disallowed characters: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) +
self.parsed_info.add_error((u'The name contains disallowed characters: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) +
u'(see http://www.ietf.org/id-info/guidelines.html#naming for details).')
match_revision = revisionre.match(filename)
match_revision = revisionre.match(name)
if match_revision:
self.parsed_info.metadraft.revision = match_revision.group(1)
self.parsed_info.metadata.rev = match_revision.group(1)
else:
self.parsed_info.add_error(u'The filename found on the first page of the document does not contain a revision: "%s"' % (filename,))
filename = re.sub('-\d+$', '', filename)
self.parsed_info.metadraft.filename = filename
self.parsed_info.add_error(u'The name found on the first page of the document does not contain a revision: "%s"' % (name,))
name = re.sub('-\d+$', '', name)
self.parsed_info.metadata.name = name
return
self.parsed_info.add_error('The first page of the document does not contain a legitimate filename that start with draft-*')
self.parsed_info.add_error('The first page of the document does not contain a legitimate name that start with draft-*')

View file

@ -4,40 +4,37 @@ from django import template
from django.conf import settings
from django.utils.html import mark_safe, escape
from ietf.submit.utils import POSTED, POSTED_BY_SECRETARIAT
register = template.Library()
@register.inclusion_tag('submit/submission_files.html', takes_context=True)
def show_submission_files(context, submission):
result = []
for ext in submission.file_type.split(','):
for ext in submission.file_types.split(','):
exists = False
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
if os.path.exists(source):
exists = True
elif submission.status_id in [POSTED, POSTED_BY_SECRETARIAT]:
elif submission.state_id == "posted":
continue
result.append({'name': '[%s version ]' % ext[1:].capitalize(),
result.append({'name': '[%s version]' % ext[1:].upper(),
'exists': exists,
'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.filename, submission.revision, ext)})
'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.name, submission.rev, ext)})
return {'files': result}
@register.filter
def two_pages_decorated_with_validation(value, validation):
pages = value.first_two_pages or ''
if not 'revision' in validation.warnings.keys():
return mark_safe('<pre class="twopages" style="display: none;">%s</pre>' % escape(pages))
result = '<pre class="twopages" style="display: none;">\n'
def two_pages_decorated_with_errors(submission, errors):
pages = submission.first_two_pages or ''
if 'rev' not in errors.keys():
return mark_safe('<pre class="twopages">%s</pre>' % escape(pages))
result = '<pre class="twopages">\n'
for line in pages.split('\n'):
if line.find('%s-%s' % (value.filename, value.revision)) > -1:
result += '</pre><pre class="twopages" style="display: none; background: red;">'
if line.find('%s-%s' % (submission.name, submission.rev)) > -1:
result += '</pre><pre class="twopages" style="background: red;">'
result += escape(line)
result += '\n'
result += '</pre><pre class="twopages" style="display: none;">\n'
result += '</pre><pre class="twopages">\n'
else:
result += escape(line)
result += '\n'

View file

@ -1,11 +1,11 @@
Informational Test Name
Informational Author Name
Internet-Draft Test Center Inc.
Intended status: Informational %(date)s
Expires: %(expire)s
Testing tests
%(filename)s
%(name)s
Abstract
@ -94,12 +94,13 @@ Internet-Draft Testing tests %(month_year)s
Authors' Addresses
Test Name
Author Name
Test Center Inc.
Some way 42
Some Where, NY
42 Some Road
Some Where 12345
US
Email: testname@example.com
Email: author@example.com

View file

@ -17,11 +17,9 @@ from ietf.utils.mail import outbox
from ietf.person.models import Person, Email
from ietf.group.models import Group, Role
from ietf.doc.models import *
from ietf.submit.models import IdSubmissionDetail, Preapproval
from ietf.submit.models import Submission, Preapproval
class SubmitTests(django.test.TestCase):
fixtures = ['idsubmissionstatus']
def setUp(self):
self.staging_dir = os.path.abspath("tmp-submit-staging-dir")
os.mkdir(self.staging_dir)
@ -40,17 +38,7 @@ class SubmitTests(django.test.TestCase):
shutil.rmtree(self.repository_dir)
shutil.rmtree(self.archive_dir)
def do_submission(self, name, rev):
# break early in case of missing configuration
self.assertTrue(os.path.exists(settings.IDSUBMIT_IDNITS_BINARY))
# get
url = urlreverse('submit_index')
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('input[type=file][name=txt]')), 1)
def submission_txt_file(self, name, rev):
# construct appropriate text draft
f = open(os.path.join(settings.BASE_DIR, "submit", "test_submission.txt"))
template = f.read()
@ -61,58 +49,86 @@ class SubmitTests(django.test.TestCase):
expire=(datetime.date.today() + datetime.timedelta(days=100)).strftime("%Y-%m-%d"),
year=datetime.date.today().strftime("%Y"),
month_year=datetime.date.today().strftime("%B, %Y"),
filename="%s-%s" % (name, rev),
name="%s-%s" % (name, rev),
)
test_file = StringIO(str(submission_text))
test_file.name = "somename.txt"
txt_file = StringIO(str(submission_text))
txt_file.name = "somename.txt"
return txt_file
def do_submission(self, name, rev):
# break early in case of missing configuration
self.assertTrue(os.path.exists(settings.IDSUBMIT_IDNITS_BINARY))
# get
url = urlreverse('submit_upload_submission')
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('input[type=file][name=txt]')), 1)
# submit
txt_file = self.submission_txt_file(name, rev)
r = self.client.post(url,
dict(txt=test_file))
dict(txt=txt_file))
self.assertEquals(r.status_code, 302)
supply_submitter_url = r["Location"]
status_url = r["Location"]
self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
self.assertEquals(IdSubmissionDetail.objects.filter(filename=name).count(), 1)
submission = IdSubmissionDetail.objects.get(filename=name)
self.assertEquals(submission.tempidauthors_set.count(), 1)
self.assertEquals(Submission.objects.filter(name=name).count(), 1)
submission = Submission.objects.get(name=name)
self.assertTrue(re.search('\s+Summary:\s+0\s+errors|No nits found', submission.idnits_message))
author = submission.tempidauthors_set.all()[0]
self.assertEquals(author.first_name, "Test Name")
self.assertEquals(len(submission.authors_parsed()), 1)
author = submission.authors_parsed()[0]
self.assertEquals(author["name"], "Author Name")
self.assertEquals(author["email"], "author@example.com")
return supply_submitter_url
return status_url
def supply_submitter(self, name, supply_submitter_url):
def supply_submitter(self, name, status_url, submitter_name, submitter_email):
# check the page
r = self.client.get(supply_submitter_url)
r = self.client.get(status_url)
q = PyQuery(r.content)
self.assertEquals(len(q('input[type=submit][name=autopost]')), 1)
post_button = q('input[type=submit][value*="Post"]')
self.assertEquals(len(post_button), 1)
action = post_button.parents("form").find('input[type=hidden][name="action"]').val()
# post submitter info
r = self.client.post(supply_submitter_url,
dict(autopost="1",
name="Test Name",
email="testname@example.com",
))
# submitter is saved as author order 0
submission = IdSubmissionDetail.objects.get(filename=name)
self.assertEquals(submission.tempidauthors_set.count(), 2)
self.assertEquals(submission.tempidauthors_set.get(author_order=0).first_name, "Test Name")
r = self.client.post(status_url, {
"action": action,
"submitter-name": submitter_name,
"submitter-email": submitter_email,
})
submission = Submission.objects.get(name=name)
self.assertEquals(submission.submitter, u"%s <%s>" % (submitter_name, submitter_email))
return r
def test_submit_new(self):
def extract_confirm_url(self, confirm_email):
# dig out confirm_email link
msg = confirm_email.get_payload(decode=True)
line_start = "http"
confirm_url = None
for line in msg.split("\n"):
if line.strip().startswith(line_start):
confirm_url = line.strip()
self.assertTrue(confirm_url)
return confirm_url
def test_submit_new_wg(self):
# submit new -> supply submitter info -> approve
draft = make_test_data()
name = "draft-ietf-mars-testing-tests"
rev = "00"
supply_submitter_url = self.do_submission(name, rev)
status_url = self.do_submission(name, rev)
# supply submitter info, then draft should be in and ready for approval
mailbox_before = len(outbox)
r = self.supply_submitter(name, supply_submitter_url)
r = self.supply_submitter(name, status_url, "Author Name", "author@example.com")
self.assertEquals(r.status_code, 302)
status_url = r["Location"]
@ -126,13 +142,14 @@ class SubmitTests(django.test.TestCase):
r = self.client.get(status_url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
approve_submit = q('input[type=submit][value*="Approve"]')
self.assertEquals(len(approve_submit), 1)
approve_button = q('input[type=submit][value*="Approve"]')
self.assertEquals(len(approve_button), 1)
action = approve_button.parents("form").find('input[type=hidden][name="action"]').val()
# approve submission
mailbox_before = len(outbox)
approve_url = approve_submit.parents("form").attr("action")
r = self.client.post(approve_url, dict())
r = self.client.post(status_url, dict(action=action))
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(docalias__name=name)
@ -140,7 +157,7 @@ class SubmitTests(django.test.TestCase):
new_revision = draft.latest_event()
self.assertEquals(draft.group.acronym, "mars")
self.assertEquals(new_revision.type, "new_revision")
self.assertEquals(new_revision.by.name, "Test Name")
self.assertEquals(new_revision.by.name, "Author Name")
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
self.assertTrue(os.path.exists(os.path.join(self.repository_dir, u"%s-%s.txt" % (name, rev))))
self.assertEquals(draft.type_id, "draft")
@ -148,18 +165,19 @@ class SubmitTests(django.test.TestCase):
self.assertTrue(draft.expires >= datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE - 1))
self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id).slug, "wg-doc")
self.assertEquals(draft.authors.count(), 1)
self.assertEquals(draft.authors.all()[0].get_name(), "Test Name")
self.assertEquals(draft.authors.all()[0].address, "testname@example.com")
self.assertEquals(draft.authors.all()[0].get_name(), "Author Name")
self.assertEquals(draft.authors.all()[0].address, "author@example.com")
self.assertEquals(len(outbox), mailbox_before + 2)
self.assertTrue((u"I-D Action: %s" % name) in outbox[-2]["Subject"])
self.assertTrue("Test Name" in unicode(outbox[-2]))
self.assertTrue("Author Name" in unicode(outbox[-2]))
self.assertTrue("New Version Notification" in outbox[-1]["Subject"])
self.assertTrue(name in unicode(outbox[-1]))
self.assertTrue("mars" in unicode(outbox[-1]))
def test_submit_existing(self):
# submit new revision of existing -> supply submitter info -> confirm
# submit new revision of existing -> supply submitter info -> prev authors confirm
draft = make_test_data()
prev_author = draft.documentauthor_set.all()[0]
# pretend IANA reviewed it
draft.set_state(State.objects.get(used=True, type="draft-iana-review", slug="not-ok"))
@ -188,45 +206,43 @@ class SubmitTests(django.test.TestCase):
with open(os.path.join(self.repository_dir, "%s-%s.txt" % (name, old_rev)), 'w') as f:
f.write("a" * 2000)
supply_submitter_url = self.do_submission(name, rev)
status_url = self.do_submission(name, rev)
# supply submitter info, then we get a confirmation email
# supply submitter info, then previous authors get a confirmation email
mailbox_before = len(outbox)
r = self.supply_submitter(name, supply_submitter_url)
r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com")
self.assertEquals(r.status_code, 302)
status_url = r["Location"]
r = self.client.get(status_url)
self.assertEquals(r.status_code, 200)
self.assertTrue("Your submission is pending email authentication" in r.content)
self.assertTrue("The submission is pending approval by the authors" in r.content)
self.assertEquals(len(outbox), mailbox_before + 1)
confirmation = outbox[-1]
self.assertTrue("Confirmation for" in confirmation["Subject"])
self.assertTrue(name in confirmation["Subject"])
confirm_email = outbox[-1]
self.assertTrue("Confirm submission" in confirm_email["Subject"])
self.assertTrue(name in confirm_email["Subject"])
self.assertTrue(prev_author.author.address in confirm_email["To"])
# submitter and new author can't confirm
self.assertTrue("author@example.com" not in confirm_email["To"])
self.assertTrue("submitter@example.com" not in confirm_email["To"])
# dig out confirmation link
msg = confirmation.get_payload(decode=True)
line_start = "Confirmation URL:"
self.assertTrue(line_start in msg)
confirm_url = None
for line in msg.split("\n"):
if line.startswith(line_start):
confirm_url = line[len(line_start):].strip()
confirm_url = self.extract_confirm_url(confirm_email)
# go to confirm page
r = self.client.get(confirm_url)
q = PyQuery(r.content)
self.assertEquals(len(q('input[type=submit][value=Auto-Post]')), 1)
self.assertEquals(len(q('input[type=submit][value*="Confirm"]')), 1)
# confirm
mailbox_before = len(outbox)
r = self.client.post(confirm_url)
self.assertEquals(r.status_code, 200)
self.assertTrue('Authorization key accepted' in r.content)
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(docalias__name=name)
self.assertEquals(draft.rev, rev)
self.assertEquals(draft.group.acronym, name.split("-")[2])
self.assertEquals(draft.docevent_set.all()[1].type, "new_revision")
self.assertEquals(draft.docevent_set.all()[1].by.name, "Test Name")
self.assertEquals(draft.docevent_set.all()[1].by.name, "Submitter Name")
self.assertTrue(not os.path.exists(os.path.join(self.repository_dir, "%s-%s.txt" % (name, old_rev))))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "%s-%s.txt" % (name, old_rev))))
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
@ -236,12 +252,12 @@ class SubmitTests(django.test.TestCase):
self.assertEquals(draft.get_state_slug("draft-stream-%s" % draft.stream_id), "wg-doc")
self.assertEquals(draft.get_state_slug("draft-iana-review"), "changed")
self.assertEquals(draft.authors.count(), 1)
self.assertEquals(draft.authors.all()[0].get_name(), "Test Name")
self.assertEquals(draft.authors.all()[0].address, "testname@example.com")
self.assertEquals(draft.authors.all()[0].get_name(), "Author Name")
self.assertEquals(draft.authors.all()[0].address, "author@example.com")
self.assertEquals(len(outbox), mailbox_before + 3)
self.assertTrue((u"I-D Action: %s" % name) in outbox[-3]["Subject"])
self.assertTrue((u"I-D Action: %s" % name) in draft.message_set.order_by("-time")[0].subject)
self.assertTrue("Test Name" in unicode(outbox[-3]))
self.assertTrue("Author Name" in unicode(outbox[-3]))
self.assertTrue("New Version Notification" in outbox[-2]["Subject"])
self.assertTrue(name in unicode(outbox[-2]))
self.assertTrue("mars" in unicode(outbox[-2]))
@ -251,6 +267,51 @@ class SubmitTests(django.test.TestCase):
self.assertTrue(name in unicode(outbox[-1]))
self.assertTrue("mars" in unicode(outbox[-1]))
def test_submit_new_individual(self):
# submit new -> supply submitter info -> confirm
draft = make_test_data()
name = "draft-authorname-testing-tests"
rev = "00"
status_url = self.do_submission(name, rev)
# supply submitter info, then draft should be be ready for email auth
mailbox_before = len(outbox)
r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com")
self.assertEquals(r.status_code, 302)
status_url = r["Location"]
r = self.client.get(status_url)
self.assertEquals(r.status_code, 200)
self.assertTrue("The submission is pending email authentication" in r.content)
self.assertEquals(len(outbox), mailbox_before + 1)
confirm_email = outbox[-1]
self.assertTrue("Confirm submission" in confirm_email["Subject"])
self.assertTrue(name in confirm_email["Subject"])
# both submitter and author get email
self.assertTrue("author@example.com" in confirm_email["To"])
self.assertTrue("submitter@example.com" in confirm_email["To"])
confirm_url = self.extract_confirm_url(outbox[-1])
# go to confirm page
r = self.client.get(confirm_url)
q = PyQuery(r.content)
self.assertEquals(len(q('input[type=submit][value*="Confirm"]')), 1)
# confirm
mailbox_before = len(outbox)
r = self.client.post(confirm_url)
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(docalias__name=name)
self.assertEquals(draft.rev, rev)
new_revision = draft.latest_event()
self.assertEquals(new_revision.type, "new_revision")
self.assertEquals(new_revision.by.name, "Submitter Name")
def test_submit_new_wg_with_dash(self):
draft = make_test_data()
@ -260,7 +321,7 @@ class SubmitTests(django.test.TestCase):
self.do_submission(name, "00")
self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.acronym, group.acronym)
self.assertEquals(Submission.objects.get(name=name).group.acronym, group.acronym)
def test_submit_new_irtf(self):
draft = make_test_data()
@ -271,8 +332,8 @@ class SubmitTests(django.test.TestCase):
self.do_submission(name, "00")
self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.acronym, group.acronym)
self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.type_id, group.type_id)
self.assertEquals(Submission.objects.get(name=name).group.acronym, group.acronym)
self.assertEquals(Submission.objects.get(name=name).group.type_id, group.type_id)
def test_submit_new_iab(self):
draft = make_test_data()
@ -281,7 +342,7 @@ class SubmitTests(django.test.TestCase):
self.do_submission(name, "00")
self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.acronym, "iab")
self.assertEquals(Submission.objects.get(name=name).group.acronym, "iab")
def test_cancel_submission(self):
# submit -> cancel
@ -290,38 +351,41 @@ class SubmitTests(django.test.TestCase):
name = "draft-ietf-mars-testing-tests"
rev = "00"
supply_submitter_url = self.do_submission(name, rev)
status_url = self.do_submission(name, rev)
# check we got cancel button
r = self.client.get(supply_submitter_url)
r = self.client.get(status_url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
cancel_submission = q('input[type=submit][value*="Cancel"]')
self.assertEquals(len(cancel_submission), 1)
cancel_button = q('input[type=submit][value*="Cancel"]')
self.assertEquals(len(cancel_button), 1)
cancel_url = cancel_submission.parents("form").attr("action")
action = cancel_button.parents("form").find("input[type=hidden][name=\"action\"]").val()
# cancel
r = self.client.post(cancel_url)
r = self.client.post(status_url, dict(action=action))
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
def test_edit_submission(self):
def test_edit_submission_and_force_post(self):
# submit -> edit
draft = make_test_data()
name = "draft-ietf-mars-testing-tests"
rev = "00"
supply_submitter_url = self.do_submission(name, rev)
status_url = self.do_submission(name, rev)
# check we got edit button
r = self.client.get(supply_submitter_url)
r = self.client.get(status_url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('input[type=submit][value*="Adjust"]')), 1)
adjust_button = q('input[type=submit][value*="Adjust"]')
self.assertEquals(len(adjust_button), 1)
action = adjust_button.parents("form").find('input[type=hidden][name="action"]').val()
# go to edit, we do this by posting, slightly weird
r = self.client.post(supply_submitter_url)
r = self.client.post(status_url, dict(action=action))
self.assertEquals(r.status_code, 302)
edit_url = r['Location']
@ -329,48 +393,67 @@ class SubmitTests(django.test.TestCase):
r = self.client.get(edit_url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('input[name=title]')), 1)
self.assertEquals(len(q('input[name=edit-title]')), 1)
# edit
mailbox_before = len(outbox)
creation_date = datetime.date.today() - datetime.timedelta(days=-3)
r = self.client.post(edit_url,
dict(title="some title",
version="00",
creation_date=creation_date.strftime("%Y-%m-%d"),
abstract="some abstract",
pages="123",
name="Some Random Test Person",
email="random@example.com",
comments="no comments",
name_0="Person 1",
email_0="person1@example.com",
name_1="Person 2",
email_1="person2@example.com",
))
document_date = datetime.date.today() - datetime.timedelta(days=-3)
r = self.client.post(edit_url, {
"edit-title": "some title",
"edit-rev": "00",
"edit-document_date": document_date.strftime("%Y-%m-%d"),
"edit-abstract": "some abstract",
"edit-pages": "123",
"submitter-name": "Some Random Test Person",
"submitter-email": "random@example.com",
"edit-note": "no comments",
"authors-0-name": "Person 1",
"authors-0-email": "person1@example.com",
"authors-1-name": "Person 2",
"authors-1-email": "person2@example.com",
"authors-prefix": ["authors-", "authors-0", "authors-1"],
})
self.assertEquals(r.status_code, 302)
submission = IdSubmissionDetail.objects.get(filename=name)
self.assertEquals(submission.id_document_name, "some title")
self.assertEquals(submission.creation_date, creation_date)
submission = Submission.objects.get(name=name)
self.assertEquals(submission.title, "some title")
self.assertEquals(submission.document_date, document_date)
self.assertEquals(submission.abstract, "some abstract")
self.assertEquals(submission.txt_page_count, 123)
self.assertEquals(submission.comment_to_sec, "no comments")
self.assertEquals(submission.pages, 123)
self.assertEquals(submission.note, "no comments")
self.assertEquals(submission.submitter, "Some Random Test Person <random@example.com>")
self.assertEquals(submission.state_id, "manual")
authors = submission.tempidauthors_set
self.assertEquals(authors.count(), 3)
# first one is submitter
self.assertEquals(authors.get(author_order=0).first_name, "Some Random Test Person")
self.assertEquals(authors.get(author_order=0).email_address, "random@example.com")
self.assertEquals(authors.get(author_order=1).first_name, "Person 1")
self.assertEquals(authors.get(author_order=1).email_address, "person1@example.com")
self.assertEquals(authors.get(author_order=2).first_name, "Person 2")
self.assertEquals(authors.get(author_order=2).email_address, "person2@example.com")
authors = submission.authors_parsed()
self.assertEquals(len(authors), 2)
self.assertEquals(authors[0]["name"], "Person 1")
self.assertEquals(authors[0]["email"], "person1@example.com")
self.assertEquals(authors[1]["name"], "Person 2")
self.assertEquals(authors[1]["email"], "person2@example.com")
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertTrue("Manual Post Requested" in outbox[-1]["Subject"])
self.assertTrue(name in outbox[-1]["Subject"])
# as Secretariat, we should see the force post button
self.client.login(remote_user="secretary")
r = self.client.get(status_url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
post_button = q('input[type=submit][value*="Force"]')
self.assertEquals(len(post_button), 1)
action = post_button.parents("form").find('input[type=hidden][name="action"]').val()
# force post
mailbox_before = len(outbox)
r = self.client.post(status_url, dict(action=action))
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(docalias__name=name)
self.assertEquals(draft.rev, rev)
def test_request_full_url(self):
# submit -> request full URL to be sent
draft = make_test_data()
@ -380,8 +463,8 @@ class SubmitTests(django.test.TestCase):
self.do_submission(name, rev)
submission = IdSubmissionDetail.objects.get(filename=name)
url = urlreverse('draft_status', kwargs=dict(submission_id=submission.submission_id))
submission = Submission.objects.get(name=name)
url = urlreverse('submit_submission_status', kwargs=dict(submission_id=submission.pk))
# check we got request full URL button
r = self.client.get(url)
@ -390,41 +473,79 @@ class SubmitTests(django.test.TestCase):
request_button = q('input[type=submit][value*="Request full access"]')
self.assertEquals(len(request_button), 1)
request_url = request_button.parents("form").attr("action")
# request URL to be sent
mailbox_before = len(outbox)
r = self.client.post(request_url)
action = request_button.parents("form").find("input[type=hidden][name=\"action\"]").val()
r = self.client.post(url, dict(action=action))
self.assertEquals(r.status_code, 200)
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertTrue("Full URL for managing submission" in outbox[-1]["Subject"])
self.assertTrue(name in outbox[-1]["Subject"])
class ApprovalsTests(django.test.TestCase):
fixtures = ['idsubmissionstatus']
def test_submit_all_file_types(self):
draft = make_test_data()
name = "draft-ietf-mars-testing-tests"
rev = "00"
txt_file = self.submission_txt_file(name, rev)
# the checks for other file types are currently embarrassingly
# dumb, so don't bother constructing proper XML/PS/PDF draft
# files
xml_file = StringIO('<?xml version="1.0" encoding="utf-8"?>\n<draft>This is XML</draft>')
xml_file.name = "somename.xml"
pdf_file = StringIO('%PDF-1.5\nThis is PDF')
pdf_file.name = "somename.pdf"
ps_file = StringIO('%!PS-Adobe-2.0\nThis is PostScript')
ps_file.name = "somename.ps"
r = self.client.post(urlreverse('submit_upload_submission'), dict(
txt=txt_file,
xml=xml_file,
pdf=pdf_file,
ps=ps_file,
))
self.assertEquals(r.status_code, 302)
self.assertEquals(Submission.objects.filter(name=name).count(), 1)
self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
self.assertTrue(name in open(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))).read())
self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.xml" % (name, rev))))
self.assertTrue('This is XML' in open(os.path.join(self.staging_dir, u"%s-%s.xml" % (name, rev))).read())
self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.pdf" % (name, rev))))
self.assertTrue('This is PDF' in open(os.path.join(self.staging_dir, u"%s-%s.pdf" % (name, rev))).read())
self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.ps" % (name, rev))))
self.assertTrue('This is PostScript' in open(os.path.join(self.staging_dir, u"%s-%s.ps" % (name, rev))).read())
class ApprovalsTests(django.test.TestCase):
def test_approvals(self):
make_test_data()
url = urlreverse('submit_approvals')
self.client.login(remote_user="marschairman")
from ietf.submit.views import POSTED, INITIAL_VERSION_APPROVAL_REQUESTED
Preapproval.objects.create(name="draft-ietf-mars-foo", by=Person.objects.get(user__username="marschairman"))
Preapproval.objects.create(name="draft-ietf-mars-baz", by=Person.objects.get(user__username="marschairman"))
IdSubmissionDetail.objects.create(filename="draft-ietf-mars-foo",
group_acronym_id=Group.objects.get(acronym="mars").pk,
Submission.objects.create(name="draft-ietf-mars-foo",
group_id=Group.objects.get(acronym="mars").pk,
submission_date=datetime.date.today(),
revision="00",
status_id=POSTED)
IdSubmissionDetail.objects.create(filename="draft-ietf-mars-bar",
group_acronym_id=Group.objects.get(acronym="mars").pk,
rev="00",
state_id="posted",
access_key="abc")
Submission.objects.create(name="draft-ietf-mars-bar",
group_id=Group.objects.get(acronym="mars").pk,
submission_date=datetime.date.today(),
revision="00",
status_id=INITIAL_VERSION_APPROVAL_REQUESTED)
rev="00",
state_id="grp-appr",
access_key="def")
# get
r = self.client.get(url)

View file

@ -2,32 +2,17 @@ from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('ietf.submit.views',
url(r'^$', 'submit_index', name='submit_index'),
url(r'^status/$', 'submit_status', name='submit_status'),
url(r'^status/(?P<submission_id>\d+)/$', 'draft_status', name='draft_status'),
url(r'^status/(?P<submission_id>\d+)/edit/$', 'draft_edit', name='draft_edit'),
url(r'^status/(?P<submission_id>\d+)/confirm/(?P<auth_key>[a-f\d]+)/$', 'draft_confirm', name='draft_confirm'),
url(r'^status/(?P<submission_id>\d+)/cancel/$', 'draft_cancel', name='draft_cancel'),
url(r'^status/(?P<submission_id>\d+)/approve/$', 'draft_approve', name='draft_approve'),
url(r'^status/(?P<submission_id>\d+)/force/$', 'draft_force', name='draft_force'),
url(r'^status/(?P<submission_id>\d+)/request/$', 'full_url_request', name='full_url_request'),
url(r'^status/(?P<submission_id>\d+)/(?P<submission_hash>[a-f\d]+)/$', 'draft_status', name='draft_status_by_hash'),
url(r'^status/(?P<submission_id>\d+)/(?P<submission_hash>[a-f\d]+)/cancel/$', 'draft_cancel', name='draft_cancel_by_hash'),
url(r'^status/(?P<submission_id>\d+)/(?P<submission_hash>[a-f\d]+)/edit/$', 'draft_edit', name='draft_edit_by_hash'),
url(r'^$', 'upload_submission', name='submit_upload_submission'),
url(r'^status/$', 'search_submission', name='submit_search_submission'),
url(r'^status/(?P<submission_id>\d+)/$', 'submission_status', name='submit_submission_status'),
url(r'^status/(?P<submission_id>\d+)/edit/$', 'edit_submission', name='submit_edit_submission'),
url(r'^status/(?P<submission_id>\d+)/confirm/(?P<auth_key>[a-f\d]+)/$', 'confirm_submission', name='submit_confirm_submission'),
url(r'^status/(?P<submission_id>\d+)/(?P<access_key>[a-f\d]*)/$', 'submission_status', name='submit_submission_status_by_hash'),
url(r'^status/(?P<submission_id>\d+)/(?P<access_key>[a-f\d]+)/edit/$', 'edit_submission', name='submit_edit_submission_by_hash'),
url(r'^note-well/$', 'note_well', name='submit_note_well'),
url(r'^tool-instructions/$', 'tool_instructions', name='submit_tool_instructions'),
url(r'^approvals/$', 'approvals', name='submit_approvals'),
url(r'^approvals/addpreapproval/$', 'add_preapproval', name='submit_add_preapproval'),
url(r'^approvals/cancelpreapproval/(?P<preapproval_id>[a-f\d]+)/$', 'cancel_preapproval', name='submit_cancel_preapproval'),
)
urlpatterns += patterns('django.views.generic.simple',
url(r'^note-well/$', 'direct_to_template',
{'template': 'submit/note_well.html',
'extra_context': {'selected': 'notewell'}
},
name='submit_note_well'),
url(r'^tool-instructions/$', 'direct_to_template',
{'template': 'submit/tool_instructions.html',
'extra_context': {'selected': 'instructions'}
},
name='submit_tool_instructions'),
)

View file

@ -12,57 +12,129 @@ from ietf.utils.log import log
from ietf.utils import unaccent
from ietf.ietfauth.utils import has_role
from ietf.submit.models import TempIdAuthors, IdSubmissionDetail, Preapproval
from ietf.submit.models import Submission, SubmissionEvent, Preapproval, DraftSubmissionStateName
from ietf.doc.models import *
from ietf.person.models import Person, Alias, Email
from ietf.doc.utils import add_state_change_event
from ietf.message.models import Message
from ietf.utils.pipe import pipe
from ietf.utils.log import log
from ietf.submit.mail import announce_to_lists, announce_new_version, announce_to_authors
# Some useful states
UPLOADED = 1
AWAITING_AUTHENTICATION = 4
MANUAL_POST_REQUESTED = 5
POSTED = -1
POSTED_BY_SECRETARIAT = -2
CANCELLED = -4
INITIAL_VERSION_APPROVAL_REQUESTED = 10
def check_idnits(path):
#p = subprocess.Popen([self.idnits, '--submitcheck', '--nitcount', path], stdout=subprocess.PIPE)
cmd = "%s --submitcheck --nitcount %s" % (settings.IDSUBMIT_IDNITS_BINARY, path)
code, out, err = pipe(cmd)
if code != 0:
log("idnits error: %s:\n Error %s: %s" %( cmd, code, err))
return out
def found_idnits(idnits_message):
if not idnits_message:
return False
success_re = re.compile('\s+Summary:\s+0\s+|No nits found')
if success_re.search(idnits_message):
return True
return False
def validate_submission(submission):
errors = {}
if submission.state_id not in ("cancel", "posted"):
for ext in submission.file_types.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
if not os.path.exists(source):
errors['files'] = '"%s" was not found in the staging area.<br />We recommend you that you cancel this submission and upload your files again.' % os.path.basename(source)
break
if not submission.title:
errors['title'] = 'Title is empty or was not found'
if submission.group and submission.group.state_id != "active":
errors['group'] = 'Group exists but is not an active group'
if not submission.abstract:
errors['abstract'] = 'Abstract is empty or was not found'
if not submission.authors_parsed():
errors['authors'] = 'No authors found'
# revision
if submission.state_id != "posted":
error = validate_submission_rev(submission.name, submission.rev)
if error:
errors['rev'] = error
# draft date
error = validate_submission_document_date(submission.submission_date, submission.document_date)
if error:
errors['document_date'] = error
return errors
def validate_submission_rev(name, rev):
if not rev:
return 'Revision not found'
try:
rev = int(rev)
except ValueError:
return 'Revision must be a number'
else:
if not (0 <= rev <= 99):
return 'Revision must be between 00 and 99'
expected = 0
existing_revs = [int(i.rev) for i in Document.objects.filter(name=name)]
if existing_revs:
expected = max(existing_revs) + 1
if rev != expected:
return 'Invalid revision (revision %02d is expected)' % expected
return None
def validate_submission_document_date(submission_date, document_date):
if not document_date:
return 'Document date is empty or not in a proper format'
elif abs(submission_date - document_date) > datetime.timedelta(days=3):
return 'Document date must be within 3 days of submission date'
return None
def create_submission_event(request, submission, desc):
by = None
if request and request.user.is_authenticated():
try:
by = request.user.person
except Person.DoesNotExist:
pass
SubmissionEvent.objects.create(submission=submission, by=by, desc=desc)
def request_full_url(request, submission):
subject = 'Full URL for managing submission of draft %s' % submission.filename
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = submission_confirmation_email_list(submission)
url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash',
kwargs=dict(submission_id=submission.submission_id,
submission_hash=submission.get_hash()))
send_mail(request, to_email, from_email, subject, 'submit/request_full_url.txt',
{'submission': submission,
'url': url})
def perform_post(request, submission):
def post_submission(request, submission):
system = Person.objects.get(name="(System)")
try:
draft = Document.objects.get(name=submission.filename)
draft = Document.objects.get(name=submission.name)
save_document_in_history(draft)
except Document.DoesNotExist:
draft = Document(name=submission.filename)
draft = Document(name=submission.name)
draft.intended_std_level = None
prev_rev = draft.rev
draft.type_id = "draft"
draft.time = datetime.datetime.now()
draft.title = submission.id_document_name
group = submission.group_acronym or Group.objects.get(type="individ")
draft.title = submission.title
group = submission.group or Group.objects.get(type="individ")
if not (group.type_id == "individ" and draft.group and draft.group.type_id == "area"):
# don't overwrite an assigned area if it's still an individual
# submission
draft.group_id = group.pk
draft.rev = submission.revision
draft.pages = submission.txt_page_count
draft.rev = submission.rev
draft.pages = submission.pages
draft.abstract = submission.abstract
was_rfc = draft.get_state_slug() == "rfc"
@ -81,14 +153,14 @@ def perform_post(request, submission):
draft.expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE)
draft.save()
a = submission.tempidauthors_set.filter(author_order=0)
if a:
submitter = ensure_person_email_info_exists(a[0]).person
submitter_parsed = submission.submitter_parsed()
if submitter_parsed["name"] and submitter_parsed["email"]:
submitter = ensure_person_email_info_exists(submitter_parsed["name"], submitter_parsed["email"]).person
else:
submitter = system
draft.set_state(State.objects.get(used=True, type="draft", slug="active"))
DocAlias.objects.get_or_create(name=submission.filename, document=draft)
DocAlias.objects.get_or_create(name=submission.name, document=draft)
update_authors(draft, submission)
@ -128,8 +200,8 @@ def perform_post(request, submission):
state_change_msg = e.desc
move_docs(submission)
submission.status_id = POSTED
move_files_to_repository(submission)
submission.state = DraftSubmissionStateName.objects.get(slug="posted")
announce_to_lists(request, submission)
announce_new_version(request, submission, draft, state_change_msg)
@ -137,116 +209,33 @@ def perform_post(request, submission):
submission.save()
def submission_confirmation_email_list(submission):
try:
doc = Document.objects.get(name=submission.filename)
email_list = [i.author.formatted_email() for i in doc.documentauthor_set.all()]
except Document.DoesNotExist:
email_list = [u'%s <%s>' % i.email() for i in submission.tempidauthors_set.all()]
return email_list
def announce_to_lists(request, submission):
authors = []
for i in submission.tempidauthors_set.order_by('author_order'):
if not i.author_order:
continue
authors.append(i.get_full_name())
m = Message()
m.by = Person.objects.get(name="(System)")
if request.user.is_authenticated():
try:
m.by = request.user.get_profile()
except Person.DoesNotExist:
pass
m.subject = 'I-D Action: %s-%s.txt' % (submission.filename, submission.revision)
m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL
if submission.group_acronym and submission.group_acronym.list_email:
m.cc = submission.group_acronym.list_email
m.body = render_to_string('submit/announce_to_lists.txt', dict(submission=submission,
authors=authors,
settings=settings,))
m.save()
m.related_docs.add(Document.objects.get(name=submission.filename))
send_mail_message(request, m)
def announce_new_version(request, submission, draft, state_change_msg):
to_email = []
if draft.notify:
to_email.append(draft.notify)
if draft.ad:
to_email.append(draft.ad.role_email("ad").address)
if draft.stream_id == "iab":
to_email.append("IAB Stream <iab-stream@iab.org>")
elif draft.stream_id == "ise":
to_email.append("Independent Submission Editor <rfc-ise@rfc-editor.org>")
elif draft.stream_id == "irtf":
to_email.append("IRSG <irsg@irtf.org>")
# if it has been sent to the RFC Editor, keep them in the loop
if draft.get_state_slug("draft-iesg") in ("ann", "rfcqueue"):
to_email.append("RFC Editor <rfc-editor@rfc-editor.org>")
active_ballot = draft.active_ballot()
if active_ballot:
for ad, pos in active_ballot.active_ad_positions().iteritems():
if pos and pos.pos_id == "discuss":
to_email.append(ad.role_email("ad").address)
if to_email:
subject = 'New Version Notification - %s-%s.txt' % (submission.filename, submission.revision)
from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt',
{'submission': submission,
'msg': state_change_msg})
def announce_to_authors(request, submission):
authors = submission.tempidauthors_set.all()
to_email = list(set(submission_confirmation_email_list(submission) + [u'%s <%s>' % i.email() for i in authors]))
from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
subject = 'New Version Notification for %s-%s.txt' % (submission.filename, submission.revision)
if submission.group_acronym:
wg = submission.group_acronym.acronym
elif submission.filename.startswith('draft-iesg'):
wg = 'IESG'
else:
wg = 'Individual Submission'
send_mail(request, to_email, from_email, subject, 'submit/announce_to_authors.txt',
{'submission': submission,
'submitter': authors[0].get_full_name(),
'wg': wg})
def get_person_from_author(author):
persons = None
def get_person_from_name_email(name, email):
# try email
if author.email_address:
persons = Person.objects.filter(email__address=author.email_address).distinct()
if email:
persons = Person.objects.filter(email__address=email).distinct()
if len(persons) == 1:
return persons[0]
else:
persons = Person.objects.none()
if not persons:
persons = Person.objects.all()
# try full name
p = persons.filter(alias__name=author.get_full_name()).distinct()
p = persons.filter(alias__name=name).distinct()
if p:
return p[0]
return None
def ensure_person_email_info_exists(author):
person = get_person_from_author(author)
def ensure_person_email_info_exists(name, email):
person = get_person_from_name_email(name, email)
# make sure we got a person
# make sure we have a person
if not person:
person = Person()
person.name = author.get_full_name()
person.name = name
person.ascii = unaccent.asciify(person.name)
person.save()
@ -254,9 +243,9 @@ def ensure_person_email_info_exists(author):
if person.name != person.ascii:
Alias.objects.create(name=ascii, person=person)
# make sure we got an email address
if author.email_address:
addr = author.email_address.lower()
# make sure we have an email address
if email:
addr = email.lower()
else:
# we're in trouble, use a fake one
addr = u"unknown-email-%s" % person.name.replace(" ", "-")
@ -277,12 +266,10 @@ def ensure_person_email_info_exists(author):
return email
def update_authors(draft, submission):
# order 0 is submitter
authors = []
for author in submission.tempidauthors_set.exclude(author_order=0).order_by('author_order'):
email = ensure_person_email_info_exists(author)
for order, author in enumerate(submission.authors_parsed()):
email = ensure_person_email_info_exists(author["name"], author["email"])
a = DocumentAuthor.objects.filter(document=draft, author=email)
if a:
@ -290,17 +277,29 @@ def update_authors(draft, submission):
else:
a = DocumentAuthor(document=draft, author=email)
a.order = author.author_order
a.order = order
a.save()
authors.append(email)
draft.documentauthor_set.exclude(author__in=authors).delete()
def move_docs(submission):
for ext in submission.file_type.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
dest = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
def cancel_submission(submission):
submission.state = DraftSubmissionStateName.objects.get(slug="cancel")
submission.save()
remove_submission_files(submission)
def rename_submission_files(submission, prev_rev, new_rev):
for ext in submission.file_types.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, prev_rev, ext))
dest = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, new_rev, ext))
os.rename(source, dest)
def move_files_to_repository(submission):
for ext in submission.file_types.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
dest = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
if os.path.exists(source):
os.rename(source, dest)
else:
@ -309,28 +308,28 @@ def move_docs(submission):
else:
raise ValueError("Intended to move '%s' to '%s', but found source and destination missing.")
def remove_docs(submission):
for ext in submission.file_type.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
def remove_submission_files(submission):
for ext in submission.file_types.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
if os.path.exists(source):
os.unlink(source)
def get_approvable_submissions(user):
def approvable_submissions_for_user(user):
if not user.is_authenticated():
return []
res = IdSubmissionDetail.objects.filter(status=INITIAL_VERSION_APPROVAL_REQUESTED).order_by('-submission_date')
res = Submission.objects.filter(state="grp-appr").order_by('-submission_date')
if has_role(user, "Secretariat"):
return res
# those we can reach as chair
return res.filter(group_acronym__role__name="chair", group_acronym__role__person__user=user)
return res.filter(group__role__name="chair", group__role__person__user=user)
def get_preapprovals(user):
def preapprovals_for_user(user):
if not user.is_authenticated():
return []
posted = IdSubmissionDetail.objects.distinct().filter(status__in=[POSTED, POSTED_BY_SECRETARIAT]).values_list('filename', flat=True)
posted = Submission.objects.distinct().filter(state="posted").values_list('name', flat=True)
res = Preapproval.objects.exclude(name__in=posted).order_by("-time").select_related('by')
if has_role(user, "Secretariat"):
return res
@ -341,125 +340,13 @@ def get_preapprovals(user):
return res
def get_recently_approved(user, since):
def recently_approved_by_user(user, since):
if not user.is_authenticated():
return []
res = IdSubmissionDetail.objects.distinct().filter(status__in=[POSTED, POSTED_BY_SECRETARIAT], submission_date__gte=since, revision="00").order_by('-submission_date')
res = Submission.objects.distinct().filter(state="posted", submission_date__gte=since, rev="00").order_by('-submission_date')
if has_role(user, "Secretariat"):
return res
# those we can reach as chair
return res.filter(group_acronym__role__name="chair", group_acronym__role__person__user=user)
class DraftValidation(object):
def __init__(self, draft):
self.draft = draft
self.warnings = {}
self.passes_idnits = self.passes_idnits()
self.wg = self.get_working_group()
self.authors = self.get_authors()
self.submitter = self.get_submitter()
def passes_idnits(self):
passes_idnits = self.check_idnits_success(self.draft.idnits_message)
return passes_idnits
def get_working_group(self):
if self.draft.group_acronym and self.draft.group_acronym.type_id == "individ":
return None
return self.draft.group_acronym
def check_idnits_success(self, idnits_message):
if not idnits_message:
return False
success_re = re.compile('\s+Summary:\s+0\s+|No nits found')
if success_re.search(idnits_message):
return True
return False
def is_valid_attr(self, key):
if key in self.warnings.keys():
return False
return True
def is_valid(self):
self.validate_metadata()
return not bool(self.warnings.keys()) and self.passes_idnits
def validate_metadata(self):
self.validate_revision()
self.validate_title()
self.validate_authors()
self.validate_abstract()
self.validate_creation_date()
self.validate_wg()
self.validate_files()
def validate_files(self):
if self.draft.status_id in [POSTED, POSTED_BY_SECRETARIAT]:
return
for ext in self.draft.file_type.split(','):
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (self.draft.filename, self.draft.revision, ext))
if not os.path.exists(source):
self.add_warning('document_files', '"%s" were not found in the staging area.<br />We recommend you that you cancel this submission and upload your files again.' % os.path.basename(source))
break
def validate_title(self):
if not self.draft.id_document_name:
self.add_warning('title', 'Title is empty or was not found')
def validate_wg(self):
if self.wg and self.wg.state_id != "active":
self.add_warning('group', 'Group exists but is not an active group')
def validate_abstract(self):
if not self.draft.abstract:
self.add_warning('abstract', 'Abstract is empty or was not found')
def add_warning(self, key, value):
self.warnings.update({key: value})
def validate_revision(self):
if self.draft.status_id in [POSTED, POSTED_BY_SECRETARIAT]:
return
revision = self.draft.revision
existing_revisions = [int(i.rev) for i in Document.objects.filter(name=self.draft.filename)]
expected = 0
if existing_revisions:
expected = max(existing_revisions) + 1
try:
if int(revision) != expected:
self.add_warning('revision', 'Invalid Version Number (Version %02d is expected)' % expected)
except ValueError:
self.add_warning('revision', 'Revision not found')
def validate_authors(self):
if not self.authors:
self.add_warning('authors', 'No authors found')
return
def validate_creation_date(self):
date = self.draft.creation_date
if not date:
self.add_warning('creation_date', 'Creation Date field is empty or the creation date is not in a proper format')
return
submit_date = self.draft.submission_date
if (date + datetime.timedelta(days=3) < submit_date or
date - datetime.timedelta(days=3) > submit_date):
self.add_warning('creation_date', 'Creation Date must be within 3 days of submission date')
def get_authors(self):
return self.draft.tempidauthors_set.exclude(author_order=0).order_by('author_order')
def get_submitter(self):
submitter = self.draft.tempidauthors_set.filter(author_order=0)
if submitter:
return submitter[0]
# elif self.draft.submitter_tag:
# try:
# return PersonOrOrgInfo.objects.get(pk=self.draft.submitter_tag)
# except PersonOrOrgInfo.DoesNotExist:
# return False
return None
return res.filter(group__role__name="chair", group__role__person__user=user)

View file

@ -1,32 +1,97 @@
# Copyright The IETF Trust 2007, All Rights Reserved
import datetime
import os
from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
from django.core.validators import validate_email, ValidationError
from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden, HttpResponseNotAllowed
from django.shortcuts import get_object_or_404
from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from ietf.doc.models import Document
from ietf.group.models import Group, Role
from ietf.utils.mail import send_mail
from ietf.ietfauth.utils import has_role, role_required
from ietf.submit.models import IdSubmissionDetail, Preapproval
from ietf.submit.forms import UploadForm, AutoPostForm, MetaDataForm, PreapprovalForm
from ietf.submit.utils import UPLOADED, AWAITING_AUTHENTICATION, MANUAL_POST_REQUESTED, CANCELLED, POSTED, INITIAL_VERSION_APPROVAL_REQUESTED
from ietf.submit.utils import get_approvable_submissions, get_preapprovals, get_recently_approved, perform_post, remove_docs, request_full_url
from ietf.submit.utils import DraftValidation
from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName
from ietf.submit.forms import UploadForm, NameEmailForm, EditSubmissionForm, PreapprovalForm
from ietf.submit.utils import approvable_submissions_for_user, preapprovals_for_user, recently_approved_by_user
from ietf.submit.utils import check_idnits, found_idnits, validate_submission, create_submission_event
from ietf.submit.utils import post_submission, cancel_submission, rename_submission_files
from ietf.submit.mail import send_full_url, send_approval_request_to_group, send_submission_confirmation, submission_confirmation_email_list, send_manual_post_request
from ietf.utils.uniquekey import generate_unique_key
def submit_index(request):
def upload_submission(request):
if request.method == 'POST':
try:
form = UploadForm(request=request, data=request.POST, files=request.FILES)
form = UploadForm(request, data=request.POST, files=request.FILES)
if form.is_valid():
submit = form.save()
return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': submit.submission_id, 'submission_hash': submit.get_hash()}))
except IOError, e:
if "Client read error" in str(e): # The server got an IOError when trying to read POST data
# save files
file_types = []
for ext in ['txt', 'pdf', 'xml', 'ps']:
f = form.cleaned_data[ext]
if not f:
continue
file_types.append('.%s' % ext)
draft = form.parsed_draft
name = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.%s' % (draft.filename, draft.revision, ext))
with open(name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
# check idnits
text_path = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (draft.filename, draft.revision))
idnits_message = check_idnits(text_path)
# extract author lines
authors = []
for author in draft.get_author_list():
full_name, first_name, middle_initial, last_name, name_suffix, email, company = author
line = full_name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
email = (email or "").strip()
if email:
try:
validate_email(email)
except ValidationError:
email = ""
if email:
line += u" <%s>" % email
authors.append(line)
# save submission
submission = Submission.objects.create(
state=DraftSubmissionStateName.objects.get(slug="uploaded"),
remote_ip=form.remote_ip,
name=draft.filename,
group=form.group,
title=draft.get_title(),
abstract=draft.get_abstract(),
rev=draft.revision,
pages=draft.get_pagecount(),
authors="\n".join(authors),
note="",
first_two_pages=''.join(draft.pages[:2]),
file_size=form.cleaned_data['txt'].size,
file_types=','.join(file_types),
submission_date=datetime.date.today(),
document_date=draft.get_creation_date(),
replaces="",
idnits_message=idnits_message,
)
create_submission_event(request, submission, desc="Uploaded submission")
return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_key=submission.access_key)
except IOError as e:
if "read error" in str(e): # The server got an IOError when trying to read POST data
form = UploadForm(request=request)
form._errors = {}
form._errors["__all__"] = form.error_class(["There was a failure receiving the complete form data -- please try again."])
@ -34,241 +99,289 @@ def submit_index(request):
raise
else:
form = UploadForm(request=request)
return render_to_response('submit/submit_index.html',
return render_to_response('submit/upload_submission.html',
{'selected': 'index',
'form': form},
context_instance=RequestContext(request))
def note_well(request):
return render_to_response('submit/note_well.html', {'selected': 'notewell'},
context_instance=RequestContext(request))
def submit_status(request):
def tool_instructions(request):
return render_to_response('submit/tool_instructions.html', {'selected': 'instructions'},
context_instance=RequestContext(request))
def search_submission(request):
error = None
filename = None
name = None
if request.method == 'POST':
filename = request.POST.get('filename', '')
detail = IdSubmissionDetail.objects.filter(filename=filename).order_by('-pk')
if detail:
return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': detail[0].submission_id}))
error = 'No valid history found for %s' % filename
return render_to_response('submit/submit_status.html',
name = request.POST.get('name', '')
submission = Submission.objects.filter(name=name).order_by('-pk')
if submission:
return HttpResponseRedirect(urlreverse(submission_status, None, kwargs={'submission_id': submission[0].pk}))
error = 'No valid submission found for %s' % name
return render_to_response('submit/search_submission.html',
{'selected': 'status',
'error': error,
'filename': filename},
'name': name},
context_instance=RequestContext(request))
def can_edit_submission(request, submission, access_key):
key_matched = access_key and submission.access_key == access_key
return key_matched or has_role(request.user, "Secretariat")
def _can_approve(user, detail):
if detail.status_id != INITIAL_VERSION_APPROVAL_REQUESTED or not detail.group_acronym:
return None
if detail.group_acronym.has_role(user, "chair") or has_role(user, "Secretariat"):
return True
return False
def _can_force_post(user, detail):
if detail.status_id not in [MANUAL_POST_REQUESTED,
AWAITING_AUTHENTICATION, INITIAL_VERSION_APPROVAL_REQUESTED]:
return None
if has_role(user, "Secretariat"):
return True
return False
def _can_cancel(user, detail, submission_hash):
if detail.status_id in [CANCELLED, POSTED]:
return None
if has_role(user, "Secretariat"):
return True
if submission_hash and detail.get_hash() == submission_hash:
return True
return False
def _can_edit(user, detail, submission_hash):
if detail.status_id != UPLOADED:
return None
if has_role(user, "Secretariat"):
return True
if submission_hash and detail.get_hash() == submission_hash:
return True
return False
def draft_status(request, submission_id, submission_hash=None, message=None):
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
if submission_hash and not detail.get_hash() == submission_hash:
def submission_status(request, submission_id, access_key=None, message=None):
submission = get_object_or_404(Submission, pk=submission_id)
if access_key and submission.access_key != access_key:
raise Http404
validation = DraftValidation(detail)
is_valid = validation.is_valid()
status = None
allow_edit = _can_edit(request.user, detail, submission_hash)
can_force_post = _can_force_post(request.user, detail)
can_approve = _can_approve(request.user, detail)
can_cancel = _can_cancel(request.user, detail, submission_hash)
if detail.status_id != UPLOADED:
if detail.status_id == CANCELLED:
message = ('error', 'This submission has been cancelled, modification is no longer possible')
status = detail.status
allow_edit = None
if detail.group_acronym and detail.revision == '00':
replaces = "Replaces draft"
else:
replaces = None
if request.method == 'POST' and allow_edit:
if request.POST.get('autopost', False):
auto_post_form = AutoPostForm(draft=detail, validation=validation, replaces=replaces, data=request.POST)
if auto_post_form.is_valid():
try:
preapproval = Preapproval.objects.get(name=detail.filename)
except Preapproval.DoesNotExist:
preapproval = None
errors = validate_submission(submission)
passes_idnits = found_idnits(submission.idnits_message)
if detail.revision == '00' and detail.group_acronym and detail.group_acronym.type_id == "wg" and not preapproval:
detail.status_id = INITIAL_VERSION_APPROVAL_REQUESTED
detail.save()
key_matched = access_key and submission.access_key == access_key
is_secretariat = has_role(request.user, "Secretariat")
is_chair = submission.group and submission.group.has_role(request.user, "chair")
submitter = auto_post_form.save_submitter_info()
subject = 'New draft waiting for approval: %s' % detail.filename
from_email = settings.IDSUBMIT_FROM_EMAIL
to_email = [r.formatted_email() for r in Role.objects.filter(group=detail.group_acronym, name="chair").select_related("email", "person")]
if to_email:
authors = detail.tempidauthors_set.exclude(author_order=0).order_by('author_order')
send_mail(request, to_email, from_email, subject, 'submit/submission_approval.txt',
{'submitter': submitter, 'authors': authors,
'draft': detail, 'domain': Site.objects.get_current().domain})
return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': detail.submission_id}))
else:
auto_post_form.save(request)
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
validation = DraftValidation(detail)
is_valid = validation.is_valid()
status = detail.status
can_force_post = _can_force_post(request.user, detail)
can_approve = _can_approve(request.user, detail)
can_cancel = _can_cancel(request.user, detail, submission_hash)
allow_edit = None
message = ('success', 'Your submission is pending email authentication. An email has been sent you with instructions.')
else:
submission_hash = detail.get_hash()
if submission_hash:
return HttpResponseRedirect(urlreverse('draft_edit_by_hash', None, kwargs={'submission_id': detail.submission_id, 'submission_hash': submission_hash}))
else:
return HttpResponseRedirect(urlreverse(draft_edit, None, kwargs={'submission_id': detail.submission_id }))
else:
auto_post_form = AutoPostForm(draft=detail, validation=validation, replaces=replaces)
can_edit = can_edit_submission(request, submission, access_key) and submission.state_id == "uploaded"
can_cancel = (key_matched or is_secretariat) and submission.state.next_states.filter(slug="cancel")
can_group_approve = (is_secretariat or is_chair) and submission.state_id == "grp-appr"
can_force_post = is_secretariat and submission.state.next_states.filter(slug="posted")
show_send_full_url = not key_matched and not is_secretariat and submission.state_id not in ("cancel", "posted")
show_notify_button = False
if allow_edit == False or can_cancel == False:
show_notify_button = True
if submission_hash is None and has_role(request.user, "Secretariat"):
submission_hash = detail.get_hash() # we'll need this when rendering the cancel button in the form
return render_to_response('submit/draft_status.html',
{'selected': 'status',
'detail': detail,
'validation': validation,
'auto_post_form': auto_post_form,
'is_valid': is_valid,
'status': status,
'message': message,
'allow_edit': allow_edit,
'can_force_post': can_force_post,
'can_approve': can_approve,
'can_cancel': can_cancel,
'submission_hash': submission_hash,
'show_notify_button': show_notify_button,
},
context_instance=RequestContext(request))
confirmation_list = submission_confirmation_email_list(submission)
try:
preapproval = Preapproval.objects.get(name=submission.name)
except Preapproval.DoesNotExist:
preapproval = None
requires_group_approval = submission.rev == '00' and submission.group and submission.group.type_id in ("wg", "rg") and not preapproval
requires_prev_authors_approval = Document.objects.filter(name=submission.name)
if submission.state_id == "cancel":
message = ('error', 'This submission has been cancelled, modification is no longer possible.')
elif submission.state_id == "auth":
message = ('success', u'The submission is pending email authentication. An email has been sent to: %s' % ",".join(confirmation_list))
elif submission.state_id == "grp-appr":
message = ('success', 'The submission is pending approval by the group chairs.')
elif submission.state_id == "aut-appr":
message = ('success', 'The submission is pending approval by the authors of the previous version. An email has been sent to: %s' % ",".join(confirmation_list))
def draft_cancel(request, submission_id, submission_hash=None):
if request.method!='POST':
return HttpResponseNotAllowed(['POST'])
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
can_cancel = _can_cancel(request.user, detail, submission_hash)
if not can_cancel:
if can_cancel == None:
raise Http404
return HttpResponseForbidden('You have no permission to perform this action')
detail.status_id = CANCELLED
detail.save()
remove_docs(detail)
return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': submission_id}))
submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
def draft_edit(request, submission_id, submission_hash=None):
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
can_edit = _can_edit(request.user, detail, submission_hash)
if not can_edit:
if can_edit == None:
raise Http404
return HttpResponseForbidden('You have no permission to perform this action')
validation = DraftValidation(detail)
validation.validate_wg()
if request.method == 'POST':
form = MetaDataForm(draft=detail, validation=validation, data=request.POST)
if form.is_valid():
form.save(request)
return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': detail.submission_id}))
else:
form = MetaDataForm(draft=detail, validation=validation)
return render_to_response('submit/draft_edit.html',
action = request.POST.get('action')
if action == "autopost" and submission.state_id == "uploaded":
if not can_edit:
return HttpResponseForbidden("You do not have permission to perfom this action")
submitter_form = NameEmailForm(request.POST, prefix="submitter")
if submitter_form.is_valid():
submission.submitter = submitter_form.cleaned_line()
if requires_group_approval:
submission.state = DraftSubmissionStateName.objects.get(slug="grp-appr")
submission.save()
sent_to = send_approval_request_to_group(request, submission)
desc = "sent approval email to group chairs: %s" % u", ".join(sent_to)
else:
submission.auth_key = generate_unique_key()
if requires_prev_authors_approval:
submission.state = DraftSubmissionStateName.objects.get(slug="aut-appr")
else:
submission.state = DraftSubmissionStateName.objects.get(slug="auth")
submission.save()
sent_to = send_submission_confirmation(request, submission)
if submission.state_id == "aut-appr":
desc = u"sent confirmation email to previous authors: %s" % u", ".join(sent_to)
else:
desc = u"sent confirmation email to submitter and authors: %s" % u", ".join(sent_to)
create_submission_event(request, submission, u"Set submitter to \"%s\" and %s" % (submission.submitter, desc))
return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_key=access_key)
elif action == "edit" and submission.state_id == "uploaded":
if access_key:
return redirect("submit_edit_submission_by_hash", submission_id=submission.pk, access_key=access_key)
else:
return redirect("submit_edit_submission", submission_id=submission.pk)
elif action == "sendfullurl" and submission.state_id not in ("cancel", "posted"):
sent_to = send_full_url(request, submission)
message = ('success', u'An email has been sent with the full access URL to: %s' % u",".join(confirmation_list))
create_submission_event(request, submission, u"Sent full access URL to: %s" % u", ".join(sent_to))
elif action == "cancel" and submission.state.next_states.filter(slug="cancel"):
if not can_cancel:
return HttpResponseForbidden('You do not have permission to perform this action')
cancel_submission(submission)
create_submission_event(request, submission, "Cancelled submission")
return redirect("submit_submission_status", submission_id=submission_id)
elif action == "approve" and submission.state_id == "grp-appr":
if not can_group_approve:
return HttpResponseForbidden('You do not have permission to perform this action')
post_submission(request, submission)
create_submission_event(request, submission, "Approved and posted submission")
return redirect("doc_view", name=submission.name)
elif action == "forcepost" and submission.state.next_states.filter(slug="posted"):
if not can_force_post:
return HttpResponseForbidden('You do not have permission to perform this action')
post_submission(request, submission)
if submission.state_id == "manual":
desc = "Posted submission manually"
else:
desc = "Forced post of submission"
create_submission_event(request, submission, desc)
return redirect("doc_view", name=submission.name)
else:
# something went wrong, turn this into a GET and let the user deal with it
return HttpResponseRedirect("")
return render_to_response('submit/submission_status.html',
{'selected': 'status',
'detail': detail,
'validation': validation,
'form': form,
'settings': settings
'submission': submission,
'errors': errors,
'passes_idnits': passes_idnits,
'submitter_form': submitter_form,
'message': message,
'can_edit': can_edit,
'can_force_post': can_force_post,
'can_group_approve': can_group_approve,
'can_cancel': can_cancel,
'show_send_full_url': show_send_full_url,
'requires_group_approval': requires_group_approval,
'requires_prev_authors_approval': requires_prev_authors_approval,
'confirmation_list': confirmation_list,
},
context_instance=RequestContext(request))
def draft_confirm(request, submission_id, auth_key):
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
message = None
if auth_key != detail.auth_key:
message = ('error', 'Incorrect authorization key')
elif detail.status_id != AWAITING_AUTHENTICATION:
message = ('error', 'The submission can not be autoposted because it is in state: %s' % detail.status.status_value)
else:
if request.method=='POST':
message = ('success', 'Authorization key accepted. Auto-Post complete')
perform_post(request, detail)
def edit_submission(request, submission_id, access_key=None):
submission = get_object_or_404(Submission, pk=submission_id, state="uploaded")
if not can_edit_submission(request.user, submission, access_key):
return HttpResponseForbidden('You do not have permission to access this page')
errors = validate_submission(submission)
form_errors = False
# we split the form handling into multiple forms, one for the
# submission itself, one for the submitter, and a list of forms
# for the authors
empty_author_form = NameEmailForm(email_required=False)
if request.method == 'POST':
# get a backup submission now, the model form may change some
# fields during validation
prev_submission = Submission.objects.get(pk=submission.pk)
edit_form = EditSubmissionForm(request.POST, instance=submission, prefix="edit")
submitter_form = NameEmailForm(request.POST, prefix="submitter")
author_forms = [ NameEmailForm(request.POST, email_required=False, prefix=prefix)
for prefix in request.POST.getlist("authors-prefix")
if prefix != "authors-" ]
# trigger validation of all forms
validations = [edit_form.is_valid(), submitter_form.is_valid()] + [ f.is_valid() for f in author_forms ]
if all(validations):
submission.submitter = submitter_form.cleaned_line()
submission.authors = "\n".join(f.cleaned_line() for f in author_forms)
edit_form.save(commit=False) # transfer changes
if submission.rev != prev_submission.rev:
rename_submission_files(submission, prev_submission.rev, submission.rev)
submission.state = DraftSubmissionStateName.objects.get(slug="manual")
submission.save()
send_manual_post_request(request, submission, errors)
changed_fields = [
submission._meta.get_field(f).verbose_name
for f in list(edit_form.fields.keys()) + ["submitter", "authors"]
if getattr(submission, f) != getattr(prev_submission, f)
]
if changed_fields:
desc = u"Edited %s and sent request for manual post" % u", ".join(changed_fields)
else:
desc = "Sent request for manual post"
create_submission_event(request, submission, desc)
return redirect("submit_submission_status", submission_id=submission.pk)
else:
return render_to_response('submit/last_confirmation_step.html',
{'detail': detail, },
context_instance=RequestContext(request))
return draft_status(request, submission_id, message=message)
form_errors = True
else:
edit_form = EditSubmissionForm(instance=submission, prefix="edit")
submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
author_forms = [ NameEmailForm(initial=author, email_required=False, prefix="authors-%s" % i)
for i, author in enumerate(submission.authors_parsed()) ]
return render_to_response('submit/edit_submission.html',
{'selected': 'status',
'submission': submission,
'edit_form': edit_form,
'submitter_form': submitter_form,
'author_forms': author_forms,
'empty_author_form': empty_author_form,
'errors': errors,
'form_errors': form_errors,
},
context_instance=RequestContext(request))
def draft_approve(request, submission_id, check_function=_can_approve):
if request.method!='POST':
return HttpResponseNotAllowed(['POST'])
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
can_perform = check_function(request.user, detail)
if not can_perform:
if can_perform == None:
raise Http404
return HttpResponseForbidden('You have no permission to perform this action')
perform_post(request, detail)
return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': submission_id}))
def confirm_submission(request, submission_id, auth_key):
submission = get_object_or_404(Submission, pk=submission_id)
if request.method == 'POST' and submission.state_id in ("auth", "aut-appr") and auth_key == submission.auth_key:
post_submission(request, submission)
def draft_force(request, submission_id):
if request.method!='POST':
return HttpResponseNotAllowed(['POST'])
return draft_approve(request, submission_id, check_function=_can_force_post)
create_submission_event(request, submission, "Confirmed and posted submission")
return redirect("doc_view", name=submission.name)
return render_to_response('submit/confirm_submission.html', {
'submission': submission,
'auth_key': auth_key,
}, context_instance=RequestContext(request))
def full_url_request(request, submission_id):
if request.method!='POST':
return HttpResponseNotAllowed(['POST'])
detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
request_full_url(request, detail)
message = ('success', 'An email has been sent to draft authors to inform them of the full access url')
return draft_status(request, submission_id, message=message)
def approvals(request):
approvals = get_approvable_submissions(request.user)
preapprovals = get_preapprovals(request.user)
approvals = approvable_submissions_for_user(request.user)
preapprovals = preapprovals_for_user(request.user)
days = 30
recently_approved = get_recently_approved(request.user, datetime.date.today() - datetime.timedelta(days=days))
recently_approved = recently_approved_by_user(request.user, datetime.date.today() - datetime.timedelta(days=days))
return render_to_response('submit/approvals.html',
{'selected': 'approvals',
@ -309,7 +422,7 @@ def add_preapproval(request):
def cancel_preapproval(request, preapproval_id):
preapproval = get_object_or_404(Preapproval, pk=preapproval_id)
if preapproval not in get_preapprovals(request.user):
if preapproval not in preapprovals_for_user(request.user):
raise HttpResponseForbidden("You do not have permission to cancel this preapproval.")
if request.method == "POST" and request.POST.get("action", "") == "cancel":

View file

@ -88,7 +88,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<a style="padding: 0;" href="{% url ietf.group.views_stream.streams %}irtf/">IRTF</a>
<a style="padding: 0;" href="{% url ietf.group.views_stream.streams %}ise/">ISE</a>
</li>
<li><a href="{% url submit_index %}">Submit a draft</a></li>
<li><a href="{% url submit_upload_submission %}">Submit a draft</a></li>
{% if user|has_role:"WG Chair" %}
<li><a href="{% url submit_approvals %}">Approve a draft</a></li>
{% endif %}

View file

@ -1,15 +1,15 @@
{% autoescape off %}
A new version (-{{ submission.revision }}) has been submitted for {{ submission.filename }}:
http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt
A new version (-{{ submission.rev }}) has been submitted for {{ submission.name }}:
http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt
{% if msg %}
{{ msg|striptags }}
{% endif %}
The IETF datatracker page for this Internet-Draft is:
https://datatracker.ietf.org/doc/{{ submission.filename }}/
https://datatracker.ietf.org/doc/{{ submission.name }}/
Diff from previous version:
http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }}
http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }}
Please note that it may take a couple of minutes from the time of submission
until the htmlized version and diff are available at tools.ietf.org.

View file

@ -1,23 +1,23 @@
{% autoescape off %}
A new version of I-D, {{ submission.filename }}-{{ submission.revision }}.txt
has been successfully submitted by {{ submitter }} and posted to the
A new version of I-D, {{ submission.name }}-{{ submission.rev }}.txt
has been successfully submitted by {{ submission.submitter_parsed.name }} and posted to the
IETF repository.
Filename: {{ submission.filename }}
Revision: {{ submission.revision }}
Title: {{ submission.id_document_name }}
Creation date: {{ submission.creation_date|date:"Y-m-d" }}
Group: {{ wg }}
Number of pages: {{ submission.txt_page_count }}
URL: http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt
Status: http://datatracker.ietf.org/doc/{{ submission.filename }}
Htmlized: http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }}
{% ifnotequal submission.revision "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }}{% endifnotequal %}
Name: {{ submission.name }}
Revision: {{ submission.rev }}
Title: {{ submission.title }}
Document date: {{ submission.document_date|date:"Y-m-d" }}
Group: {{ group }}
Pages: {{ submission.pages }}
URL: http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt
Status: https://datatracker.ietf.org/doc/{{ submission.name }}/
Htmlized: http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }}
{% if submission.rev != "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }}{% endif %}
Abstract:
{{ submission.abstract }}
{{ submission.comment_to_sec|default:"" }}
{{ submission.note|default:"" }}
Please note that it may take a couple of minutes from the time of submission
until the htmlized version and diff are available at tools.ietf.org.

View file

@ -1,25 +1,25 @@
{% autoescape off %}
A New Internet-Draft is available from the on-line Internet-Drafts directories.
{% if submission.group_acronym %} This draft is a work item of the {{ submission.group_acronym.name }} Working Group of the IETF.{% endif %}
{% if submission.group %} This draft is a work item of the {{ submission.group.name }} Working Group of the IETF.{% endif %}
Title : {{ submission.id_document_name }}
Author(s) : {% for author in authors %}{{ author }}{% if not forloop.last %}
Title : {{ submission.title }}
Author{{ submission.authors_parsed|pluralize:" ,s" }} : {% for author in submission.authors_parsed %}{{ author.name }}{% if not forloop.last %}
{% endif %}{% endfor %}
Filename : {{ submission.filename }}-{{ submission.revision }}.txt
Pages : {{ submission.txt_page_count }}
Filename : {{ submission.name }}-{{ submission.rev }}.txt
Pages : {{ submission.pages }}
Date : {{ submission.submission_date|date:"Y-m-d" }}
Abstract:
{{ submission.abstract }}
The IETF datatracker status page for this draft is:
https://datatracker.ietf.org/doc/{{ submission.filename }}
https://datatracker.ietf.org/doc/{{ submission.name }}/
There's also a htmlized version available at:
http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }}
{% if submission.revision != "00" %}
http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }}
{% if submission.rev != "00" %}
A diff from the previous version is available at:
http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.filename }}-{{ submission.revision }}
http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.name }}-{{ submission.rev }}
{% endif %}
Please note that it may take a couple of minutes from the time of submission

View file

@ -0,0 +1,33 @@
{% autoescape off %}
Hi,
Chair approval is needed for posting of {{ submission.name }}-{{ submission.rev }}.
To approve the draft, go to this URL (note: you need to login to be able to approve):
https://{{ domain }}/submit/status/{{ submission.pk }}/{{ submission.access_key }}/
File name : {{ submission.name }}
Revision : {{ submission.rev }}
Submission date : {{ submission.submission_date }}
Group : {{ submission.group|default:"Individual Submission" }}
Title : {{ submission.title }}
Document date : {{ submission.document_date }}
Pages : {{ submission.pages }}
File size : {{ submission.file_size|filesizeformat }}
Submitter : {{ submission.submitter }}
Abstract : {{ submission.abstract }}
Authors:
{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
{% endfor %}
{% endautoescape %}
Best regards,
The IETF Secretariat
through the draft submission service

View file

@ -22,16 +22,16 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; }
<p>You don't have any submissions to approve.</p>
{% else %}
<table cellspacing="0" class="approvals">
<tr>
<th>Submission</th>
<th>Submitted</th>
</tr>
{% for d in approvals %}
<tr>
<td><a href="{% url draft_status d.pk %}">{{ d.filename }}-{{ d.revision }}</a></td>
<td>{{ d.submission_date }}</td>
</tr>
{% endfor %}
<tr>
<th>Submission</th>
<th>Submitted</th>
</tr>
{% for s in approvals %}
<tr>
<td><a href="{% url submit_submission_status_by_hash submission_id=s.pk access_key=s.access_key %}">{{ s.name }}-{{ s.rev }}</a></td>
<td>{{ s.submission_date }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
@ -46,19 +46,19 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; }
{% else %}
<table cellspacing="0" class="preapprovals">
<tr>
<th>Draft name</th>
<th>Pre-approved</th>
<th>By</th>
</tr>
{% for p in preapprovals %}
<tr>
<td>{{ p.name }}</td>
<td>{{ p.time|date:"Y-m-d" }}</td>
<td>{{ p.by }}</td>
<td><a class="cancel" href="{% url submit_cancel_preapproval preapproval_id=p.id %}">cancel pre-approval</a></td>
</tr>
{% endfor %}
<tr>
<th>Draft name</th>
<th>Pre-approved</th>
<th>By</th>
</tr>
{% for p in preapprovals %}
<tr>
<td>{{ p.name }}</td>
<td>{{ p.time|date:"Y-m-d" }}</td>
<td>{{ p.by }}</td>
<td><a class="cancel" href="{% url submit_cancel_preapproval preapproval_id=p.id %}">cancel pre-approval</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
@ -69,18 +69,19 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; }
{% else %}
<table cellspacing="0" class="recently-approved">
<tr>
<th>Draft</th>
<th>Submitted</th>
</tr>
{% for d in recently_approved %}
<tr>
<td><a href="{% url doc_view d.filename %}">{{ d.filename }}</a></td>
<td>{{ d.submission_date }}</td>
</tr>
{% endfor %}
<tr>
<th>Draft</th>
<th>Submitted</th>
</tr>
{% for d in recently_approved %}
<tr>
<td><a href="{% url doc_view d.name %}">{{ d.name }}</a></td>
<td>{{ d.submission_date }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% else %}
<h2>Submission approvals</h2>
<p>

View file

@ -0,0 +1,56 @@
{% extends "submit/submit_base.html" %}
{% block title %}Confirm submission of {{ submission.name }}{% endblock %}
{% block morecss %}
{{ block.super }}
p.error { color: red; font-weight: bold; font-size: 1.5em; }
{% endblock %}
{% block submit_content %}
<h2>Confirm submission of {{ submission.name }}</h2>
{% if submission.state_id != "auth" and submission.state_id != "aut-appr" %}
{% if submission.state_id == "posted" %}
<p>The submission has already been posted. See the <a href="{% url doc_view name=submission.name %}">draft here</a>.</p>
{% else %}
<p>The submission is not in a state where it can be confirmed.</p>
<p>Go to the <a href="{% url submit_submission_status submission_id=submission.pk %}">status page</a>
to see what has happened to it.</p>
{% endif %}
{% else %}
{% if auth_key != submission.auth_key %}
<p class="error">Incorrect authorization key.</p>
<p>Double-check the link you followed. If everything fails, you can go to
the <a href="{% url submit_submission_status submission_id=submission.pk %}">status page</a>,
cancel the submission and try again.</p>
{% else %}
<p>Authorization key accepted.</p>
<p>Please press the button below to finish posting of
<strong>{{ submission.name }}-{{ submission.rev }}</strong></p>
<form method="post">
<input type="submit" value="Confirm submission and post draft" />
</form>
{% endif %}
{% endif %}
{% endblock %}
{% block scripts %}
jQuery(function () {
jQuery("form").submit(function() {
if (this.submittedAlready)
return false;
else
this.submittedAlready = true;
});
});
{% endblock %}

View file

@ -2,12 +2,13 @@
Hi,
The IETF datatracker draft submission service has received your draft
{{ draft.filename }}-{{ draft.revision }}, and requires a
{{ submission.name }}-{{ submission.rev }}, and requires a
confirmation step in order to be able to complete the posting of
the draft.
Please follow this link to the page where you can confirm the posting:
Confirmation URL: {{ confirm_url|safe }}
{{ confirm_url }}
Best regards,

View file

@ -1,169 +0,0 @@
{% extends "submit/draft_status.html" %}
{% load submit_tags %}
{% block title %}Adjust Meta-Data{% endblock %}
{% block morecss %}
{{ block.super }}
table.metadata-table #id_title, table.metadata-table #id_abstract, table.metadata-table #id_comments { width: 500px; }
table.metadata-table tr.warning th, table.metadata-table tr.warning td { background-color: #ffeebb; }
table.ietf-table tr { vertical-align: top; }
table.ietf-table tr.error { background-color: #ffeebb; border-top: 1px dashed red; border-bottom: 1px dashed red;}
table.ietf-table span.field-error { display: block; color: red; }
{% endblock %}
{% block pagehead %}
{{ block.super }}
<script type="text/javascript" src="/js/draft-submit.js"></script>
<script type="text/javascript">
(function ($) {
$.fn.AuthorList = function() {
return this.each(function () {
var table = $(this);
var makeEditable = function() {
table.find('tbody tr').each(function(index, value) {
var tr = $(this);
tr.find('td').each(function() {
var td = $(this);
var text = td.find('.fieldValue');
var name = td.attr('name');
if (tr.hasClass('non_editable')) {
td.prepend('<input style="display: none;" type="text" name="' + name + '_' + index + '" value="' + text.text() + '" />');
} else {
td.prepend('<input type="text" name="' + name + '_' + index + '" value="' + text.text() + '" />');
text.html('');
}
});
});
};
var addNewEntry = function() {
table.append(table.find('tbody tr').last().clone());
var new_tr = table.find('tbody tr').last();
new_tr.toggleClass('evenrow').toggleClass('oddrow');
new_tr.removeClass('error').find('.field-error').remove();
new_tr.find('input').each(function() {
var name = $(this).attr('name');
var splitted = name.split('_');
splitted.reverse();
name = name.replace(splitted[0], (parseInt(splitted[0]) + 1).toString(10));
$(this).attr('name', name);
$(this).val('');
});
};
var bindTriggers = function() {
$('.new_author').click(addNewEntry);
};
var initialize = function() {
makeEditable();
bindTriggers();
};
initialize();
});
};
$(document).ready(function () {
$('table.author_list').AuthorList();
});
})(jQuery);
</script>
{% endblock %}
{% block submit_content %}
<h2>Adjust External Meta-Data</h2>
<div id="idnits_results" style="visibility:hidden;">
<div class="hd">
<span style="display: none;" id="twopages_title">First two pages</span>
</div>
<div class="bd">
<div id="stream_dialog_body" style="padding: 0em 5em; height: 400px; overflow: auto;">
<pre class="twopages" style="display: none;">{{ detail.first_two_pages }}</pre>
</div>
</div>
</div>
<table class="metadata-table">
<tr><th>Document</th><td>{{ detail.filename }} <a class="twopages_trigger" href="#">[View first two pages]</a>
{% show_submission_files detail %}
</td></tr>
<tr><th>Submission date</th><td>{{ detail.submission_date }}</td></tr>
<tr{% if validation.warnings.group %} class="warning"{% endif %}><th>Group</th><td>{{ validation.wg|default:"Individual Submission" }}
{% if validation.warnings.group %}
<div class="warn_message">The secretariat will be notified that the working group is not active</div>
{% endif %}
</td></tr>
<tr><th>File size</th><td>{{ detail.filesize|filesizeformat }}</td></tr>
</table>
<h3>Adjust data</h3>
{% if form.errors %}
<div class="metadata-errors">
Please fix the following errors.
</div>
{% endif %}
<form method="post" action="">
<table class="metadata-table">
<tr{% if form.errors.title %} class="warning"{% endif %}><th>Title</th><td>{{ form.title }}{{ form.errors.title }}</td></tr>
<tr{% if form.errors.version %} class="warning"{% endif %}><th>Version</th><td>{{ form.version }}{{ form.errors.version }}</td></tr>
<tr{% if form.errors.creation_date %} class="warning"{% endif %}><th>Creation date</th><td>{{ form.creation_date }}{{ form.errors.creation_date }}</td></tr>
<tr{% if form.errors.abstract %} class="warning"{% endif %}><th>Abstract</th><td>{{ form.abstract }}{{ form.errors.abstract }}</td></tr>
<tr{% if form.errors.pages %} class="warning"{% endif %}><th>Pages</th><td>{{ form.pages }}{{ form.errors.pages }}</td></tr>
<tr><th>Submitter</th>
<td>
If you are one of the authors, then please click a button by your name to automatically fill in the submitter's information as requested below. Otherwise, Please manually enter your information.<br />
{{ form.get_author_buttons|safe }}
</td></tr>
{% if settings.USE_DB_REDESIGN_PROXY_CLASSES %}
<tr{% if form.errors.name %} class="warning"{% endif %}><th class="author">Name</th><td>{{ form.name }}{{ form.errors.name }}</td></tr>
{% else %}
<tr{% if form.errors.first_name %} class="warning"{% endif %}><th class="author">First name</th><td>{{ form.first_name }}{{ form.errors.first_name }}</td></tr>
<tr{% if form.errors.last_name %} class="warning"{% endif %}><th class="author">Last name</th><td>{{ form.last_name }}{{ form.errors.last_name }}</td></tr>
{% endif %}
<tr{% if form.errors.email %} class="warning"{% endif %}><th class="author">Email address</th><td>{{ form.email }}{{ form.errors.email }}</td></tr>
<tr{% if form.errors.comments %} class="warning"{% endif %}><th>Comments to the secretariat</th><td>{{ form.comments }}{{ form.errors.comments }}</td></tr>
</table>
<h3>Authors</h3>
<table class="author_list ietf-table" style="width: 100%;">
<thead>
<tr>
{% if settings.USE_DB_REDESIGN_PROXY_CLASSES %}
<th>Name</th>
{% else %}
<th>First name</th>
<th>Last name</th>
{% endif %}
<th>Email address</th>
</tr>
</thead>
<tbody>
{% for author in form.get_authors %}
<tr class="editable {% cycle oddrow,evenrow %}{% if author.errors %} error{% endif %}">
{% if settings.USE_DB_REDESIGN_PROXY_CLASSES %}
<td name="name"><span class="fieldValue">{{ author.get_full_name|default:"" }}</span><span class="field-error">{{ author.errors.name }}</span></td>
{% else %}
<td name="first_name"><span class="fieldValue">{{ author.first_name|default:"" }}</span><span class="field-error">{{ author.errors.first_name }}</span></td>
<td name="last_name"><span class="fieldValue">{{ author.last_name|default:"" }}</span><span class="field-error">{{ author.errors.last_name }}</span></td>
{% endif %}
<td name="email"><span class="fieldValue">{{ author.email.1|default:"" }}</span><span class="field-error">{{ author.errors.email }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
<div style="text-align: right; margin-bottom: 1em;">
<input type="button" value="Add another author" class="new_author" />
</div>
<input type="submit" value="Submit for manual posting" />
</form>
<p>
The IETF is an organized activity of the <a href="http://www.isoc.org">Internet Society</a>
<br>Please send problem reports to <a href="mailto:ietf-action@ietf.org">ietf-action@ietf.org</a>.
</p>
{% endblock %}

View file

@ -1,257 +0,0 @@
{% extends "submit/submit_base.html" %}
{% load submit_tags %}
{% block title %}Submission status{% endblock %}
{% block morecss %}
{{ block.super }}
div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; }
div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; }
div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; }
table.metadata-table th { white-space: nowrap; font-weight: bold; }
table.metadata-table #id_first_name, table.metadata-table #id_last_name { width: 200px; }
table.metadata-table #id_email { width: 400px; }
table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; }
table.metadata-table th.author { text-align: right; }
table.metadata-table tr { vertical-align: top; }
table.metadata-table tr.warning td, table.metadata-table tr.warning th { background-color: #ffaaaa; }
table.metadata-table div.warn_message { color: red; }
table.metadata-table ul.errorlist { color: red; padding: 0px; margin: 0px; list-style-type: none; }
pre.twopages { margin: 0px; }
{% endblock morecss %}
{% block pagehead %}
<script type="text/javascript" src="/js/lib/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/js/draft-submit.js"></script>
{% if can_cancel %}
<script type="text/javascript">
function confirmCancelation(){
{% if is_valid %}if (!confirm("Cancel this submission?")) return false;{% endif %}
return true;
}
</script>
{% endif %}
<script type="text/javascript">
(function ($) {
$(document).ready(function () {
var handleClose = function() {
idnitsDialog.hide();
};
var buttons = [{text:"Close", handler:handleClose, isDefault:true}];
var kl = [new YAHOO.util.KeyListener(document, {keys:27}, handleClose)]
var idnitsDialog = new YAHOO.widget.Dialog("idnits_results", {
visible:false, draggable:false, close:true, modal:true,
width:"860px", fixedcenter:true, constraintoviewport:true,
buttons: buttons, keylisteners:kl});
idnitsDialog.render();
var showIdnits = function() {
$('#idnits_results').show();
$('#idnits_title').show();
$('#twopages_title').hide();
$('pre.idnits_message').show();
$('pre.twopages').hide();
idnitsDialog.show();
return false;
}
var showTwoPages = function() {
$('#idnits_results').show();
$('#idnits_title').hide();
$('#twopages_title').show();
$('pre.idnits_message').hide();
$('pre.twopages').show();
idnitsDialog.show();
return false;
}
$('a.idnits_trigger').click(showIdnits);
$('a.twopages_trigger').click(showTwoPages);
});
})(jQuery);
</script>
{% endblock %}
{% block submit_content %}
{% if status %}
<h2>Status of the submission: {{ status.status_value }}</h2>
{% endif %}
{% if message %}
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
{% endif %}
{% if auto_post_form.errors %}
<div class="info-message-error">Please fix errors in the form below</div>
{% endif %}
<h2>Check Page</h2>
<p>
{% if validation.passes_idnits %}
Your draft has been verified to meet IDNITS requirements.
{% else %}
Your draft has NOT been verified to meet IDNITS requirements.
{% endif %}
<a class="idnits_trigger" href="#">(View IDNITS Results)</a>
</p>
<div id="idnits_results" style="visibility:hidden; display: none;">
<div class="hd">
<span style="display: none;" id="idnits_title">Idnits results</span>
<span style="display: none;" id="twopages_title">First two pages</span>
</div>
<div class="bd">
<div id="stream_dialog_body" style="padding: 0em 5em; height: 400px; overflow: auto;">
<pre class="idnits_message" style="display: none;">{{ detail.idnits_message }}</pre>
{{ detail|two_pages_decorated_with_validation:validation }}
</div>
</div>
</div>
<h2>Meta-Data from the Draft</h2>
{% if validation.warnings %}
<div class="metadata-errors">
<h3>Meta-Data errors found</h3>
<p>
Please make sure that your Internet-Draft includes all of the required meta-data in the proper format.
</p>
<p>
If your Internet-Draft *does* include all of the required meta-data in the proper format, and if
the error(s) identified above are due to the failure of the tool to extract the meta-data correctly,
then please use the 'Adjust Meta-Data' button below, which will take you to the 'Adjust Screen' where
you can correct the improperly extracted meta-data. You will then be able to submit your Internet-Draft
to the Secretariat for manual posting.
</p>
<p>
If your Internet-Draft *does not* include all of the required meta-data in the proper format, then
please cancel this submission, update your Internet-Draft, and resubmit it.
</p>
<p>
<strong>NOTE:</strong> The Secretariat will NOT add any meta-data to your Internet-Draft or edit the meta-data. An
Internet-Draft that does not include all of the required meta-data in the proper format WILL be
returned to the submitter.
</p>
</div>
{% endif %}
<table class="metadata-table">
<tr{% if validation.warnings.document_files %} class="warning"{% endif %}>
<th>Document</th>
<td>
{% ifequal status.status_value "Posted" %}<a href="http://www.ietf.org/id/{{ detail.filename }}-{{detail.revision}}.txt">{{ detail.filename }}</a>{% else %}{{ detail.filename }}{% endifequal %}
<br/><a class="twopages_trigger" href="#">[View first two pages]</a>
{% show_submission_files detail %}
<div class="warn_message">{{ validation.warnings.document_files|safe }}</div>
</td>
</tr>
<tr{% if validation.warnings.revision %} class="warning"{% endif %}><th>Revision</th><td>{{ detail.revision }}<div class="warn_message">{{ validation.warnings.revision }}{% if validation.warnings.revision %}<br /><a class="twopages_trigger" href="#">[View error]</a>{% endif %}</div></td></tr>
<tr{% if validation.warnings.group %} class="warning"{% endif %}><th>Group</th><td>{{ validation.wg|default:"Individual Submission" }}<div class="warn_message">{{ validation.warnings.group }}</div></td></tr>
<tr{% if validation.warnings.creation_date %} class="warning"{% endif %}><th>Document date</th><td>{{ detail.creation_date }}<div class="warn_message">{{ validation.warnings.creation_date }}</div></td></tr>
<tr><th>Submission date</th><td>{{ detail.submission_date }}</td></tr>
<tr{% if validation.warnings.title %} class="warning"{% endif %}><th>Title</th><td>{{ detail.id_document_name|default:"" }}<div class="warn_message">{{ validation.warnings.title }}</div></td></tr>
<tr{% if validation.warnings.authors %} class="warning"{% endif %}><th colspan="2">Author information</th></tr>
{% if validation.warnings.authors %}
<tr class="warning"><td colspan="2"><div class="warn_message">{{ validation.warnings.authors|safe }}</div></td></tr>
{% endif %}
{% if validation.authors %}
{% for author in validation.authors %}
<tr{% if validation.warnings.authors %} class="warning"{% endif %}><th class="author">Author {{ forloop.counter }}</th><td>{{ author.get_full_name }} {% if author.email.1 %}&lt;{{ author.email.1 }}&gt;{% endif %}</td></tr>
{% endfor %}
{% endif %}
<tr{% if validation.warnings.abstract %} class="warning"{% endif %}><th>Abstract</th><td>{{ detail.abstract|linebreaksbr }}<div class="warn_message">{{ validation.warnings.abstract }}</div></td></tr>
<tr{% if validation.warnings.pages %} class="warning"{% endif %}><th>Pages</th><td>{{ detail.txt_page_count }}<div class="warn_message">{{ validation.warnings.pages }}</div></td></tr>
<tr><th>File size</th><td>{{ detail.filesize|filesizeformat }}</td></tr>
</table>
{% if allow_edit %}
<form method="post" action="">
<input type="submit" value="Adjust Meta-Data" value="adjust" /> (Leads to manual post by the Secretariat)
</form>
{% if is_valid %}
<h2>Please edit the following meta-data before proceeding to Auto-Post</h2>
<p>
If you are one of the authors of this document, then please click the button with your name on it to automatically fill in the submitter information as requested below. Otherwise, please manually enter your information.
</p>
<form method="post" action="">
{{ auto_post_form.get_author_buttons|safe }}
<table class="metadata-table">
{{ auto_post_form }}
</table>
<input type="submit" value="Post" name="autopost" />
</form>
{% endif %}
{% else %}
{% if validation.submitter %}
<h3>Submitter information</h3>
<table class="metadata-table">
<tr><th>Name</th><td>{{ validation.submitter.first_name }}</td></tr>
<tr><th>Email address</th><td>{{ validation.submitter.email_address|default:validation.submitter.email.1 }}</td></tr>
</table>
{% endif %}
{% endif %}
{% if can_cancel %}
<h2>Cancel submission</h2>
<p>
<form method="post" onsubmit="return confirmCancelation();" action="{% url draft_cancel_by_hash detail.submission_id submission_hash %}">
<input type="submit" value="Cancel Submission" /><br>
</form>
This submission will be canceled, and its uploaded document(s) permanently deleted.
</p>
{% endif %}
{% if can_approve %}
<p>
<form method="post" action="/submit/status/{{ detail.submission_id }}/approve/">
<input type="submit" value="Approve this submission" />
</form>
</p>
{% endif %}
{% if can_force_post %}
<p>
<form method="post" action="/submit/status/{{ detail.submission_id }}/force/">
<input type="submit" value="Force post" />
</form>
</p>
{% endif %}
{% if show_notify_button %}
<div class="metadata-errors">
<p>
You are not allowed to modify or cancel this submission. You only can modify or cancel this submission from the same URL you were redirected to after the submission.
</p>
<p>
If you are the submitter check your browser history to find this url. You can share it with any person you need.
</p>
<p>
If you are one of the authors you can request the URL from wich you can modify or cancel this submission by clicking the next button. An email will be sent to the draft authors and to the submitter (if the submitter's email is available).
</p>
<form method="post" action="{% url full_url_request detail.submission_id %}">
<input type="submit" value="Request full access URL" />
</form>
</div>
{% endif %}
<p>
The IETF is an organized activity of the <a href="http://www.isoc.org">Internet Society</a>
<br>Please send problem reports to <a href="mailto:ietf-action@ietf.org">ietf-action@ietf.org</a>.
</p>
{% endblock %}
{% block scripts %}
jQuery(function () {
jQuery("form").submit(function() {
if (this.submittedAlready)
return false;
else
this.submittedAlready = true;
});
});
{% endblock %}

View file

@ -0,0 +1,114 @@
{% extends "submit/submit_base.html" %}
{% load submit_tags %}
{% block title %}Adjust Meta-Data of Submitted {{ submission.name }}{% endblock %}
{% block morecss %}
{{ block.super }}
table.metadata-table tr th { padding-right: 2em; }
table.metadata-table #id_edit-title, table.metadata-table #id_edit-abstract, table.metadata-table #id_edit-note { width: 40em; }
table.authors td { vertical-align: top; }
table.authors tr.empty { display: none; }
input.add-author { margin-left: 42em; }
div.manual-posting { margin-top: 2em; }
{% endblock %}
{% block submit_content %}
<h2>Adjust Meta-Data of Submitted {{ submission.name }}</h2>
<div class="twopages-popup" style="display:none">
<div class="content">
<pre>{{ submission|two_pages_decorated_with_errors:errors }}</pre>
</div>
<div class="actions">
<a href="" class="button close">Close</a>
</div>
</div>
<table class="metadata-table">
<tr>
<th>Document</th>
<td>{{ submission.name }} <a class="twopages-trigger" href="">[View first two pages]</a>
{% show_submission_files submission %}
</td>
</tr>
<tr><th>Submission date</th><td>{{ submission.submission_date }}</td></tr>
<tr{% if errors.group %} class="error"{% endif %}>
<th>Group</th>
<td>{{ submission.group|default:"Individual Submission" }}
{% if errors.group %}<div class="error-msg">{{ errors.group }} (note: the Secretariat will be notified of this)</div>{% endif %}
</td>
</tr>
<tr><th>File size</th><td>{{ submission.file_size|filesizeformat }}</td></tr>
</table>
<h3>Adjust meta-data</h3>
{% if form_errors %}
<div class="metadata-errors">
Please fix the following errors.
</div>
{% endif %}
<form method="post">
<table class="metadata-table">
{% for field in edit_form %}
{% if field.name != "note" %}
<tr{% if field.errors %} class="error"{% endif %}><th>{{ field.label_tag }}</th><td>{{ field }}{{ field.errors }}</td></tr>
{% endif %}
{% endfor %}
{% include "submit/submitter_form.html" %}
{% with edit_form.note as field %}
<tr{% if field.errors %} class="error"{% endif %}><th>{{ field.label_tag }}</th><td>{{ field }}{{ field.errors }}</td></tr>
{% endwith %}
</table>
<h3>Authors</h3>
<table class="authors metadata-table">
<thead>
<tr>
<th>Name</th>
<th>Email address</th>
</tr>
</thead>
<tbody>
<tr class="empty">
<td>{{ empty_author_form.name }} <input type="hidden" name="authors-prefix" value="authors-" /></td>
<td>{{ empty_author_form.email }}</td>
</tr>
{% for form in author_forms %}
<tr class="{% if form.errors %} error{% endif %}">
<td>{{ form.name }} {{ form.name.errors }} <input type="hidden" name="authors-prefix" value="{{ form.prefix }}" /></td>
<td>{{ form.email }} {{ form.email.errors }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div>
<input type="button" value="Add another author" class="add-author" />
</div>
<div class="manual-posting">
<input type="submit" value="Submit for manual posting" />
</div>
</form>
{% include "submit/problem-reports-footer.html" %}
{% endblock %}
{% block js %}
{{ block.super }}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/draft-submit.js"></script>
{% endblock %}

View file

@ -2,7 +2,7 @@
Hi,
The datatracker has received a request to send out the link to the URL where you
can confirm the submission of your draft {{ submission.filename }}-{{ submission.revision }}.
can confirm the submission of your draft {{ submission.name }}-{{ submission.rev }}.
Please follow this link to get full access to the submission page:
{{ url|safe }}

View file

@ -1,26 +0,0 @@
{% extends "submit/submit_base.html" %}
{% block title %}Confirm Auto-Post{% endblock %}
{% block submit_content %}
<h2>Confirm auto-post</h2>
<p>
Authorization key accepted. Please press the button below to finish Auto-Post of <strong>{{ detail.filename }}-{{ detail.revision }}</strong>
</p>
<form action="" method="post">
<input type="submit" value="Auto-Post" />
</form>
{% endblock %}
{% block scripts %}
jQuery(function () {
jQuery("form").submit(function() {
if (this.submittedAlready)
return false;
else
this.submittedAlready = true;
});
});
{% endblock %}

View file

@ -1,31 +0,0 @@
{% autoescape off %}
Hi,
Manual posting has been requested for the following Internet-Draft:
I-D Submission Tool URL:
{{ url }}
File name : {{ draft.filename }}
Version : {{ draft.revision }}
Submission date : {{ draft.submission_date }}
Group : {{ draft.group_acronym|default:"Individual Submission" }} {% if form.validation.warnings.group %}*Please note that this group is not an active one*{% endif %}
Title : {{ draft.id_document_name }}
Document date : {{ draft.creation_date }}
Pages : {{ draft.txt_page_count }}
File size : {{ draft.filesize|filesizeformat }}
Submitter : {{ submitter.get_full_name }} <{{ submitter.email.1 }}>
Abstract : {{ draft.abstract }}
Authors:
{% for author in form.get_authors %} {{ author.get_full_name }} <{{ author.email.1 }}>
{% endfor %}
Comments to the secretariat:
{{ draft.comment_to_sec }}
{% endautoescape %}

View file

@ -0,0 +1,31 @@
{% autoescape off %}
Hi,
Manual posting has been requested for the following Internet-Draft:
I-D Submission Tool URL:
{{ url }}
File name : {{ submission.name }}
Revision : {{ submission.rev }}
Submission date : {{ submission.submission_date }}
Group : {{ submission.group|default:"Individual Submission" }} {% if errors.group %}*Please note: {{ errors.group }}*{% endif %}
Title : {{ submission.title }}
Document date : {{ submission.document_date }}
Pages : {{ submission.pages }}
File size : {{ submission.file_size|filesizeformat }}
Submitter : {{ submission.submitter }}
Abstract : {{ submission.abstract }}
Authors:
{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
{% endfor %}
Comment to the secretariat:
{{ submission.note }}
{% endautoescape %}

View file

@ -0,0 +1,4 @@
<p class="problem-reports-footer">
The IETF is an organized activity of the <a href="http://www.isoc.org">Internet Society</a><br>
Please send problem reports to <a href="mailto:ietf-action@ietf.org">ietf-action@ietf.org</a>.
</p>

View file

@ -0,0 +1,18 @@
{% extends "submit/submit_base.html" %}
{% block title %}Submission status{% endblock %}
{% block submit_content %}
<p>Please enter the name of the Internet-Draft you wish to view
submission status for:</p>
<form method="post">
{% if error %}<div style="color: red;">{{ error }}</div>{% endif %}
<input type="text" name="name" value="{{ name|default:"draft-" }}" />
<input type="submit" value="See status" />
</form>
{% include "submit/problem-reports-footer.html" %}
{% endblock %}

View file

@ -1,33 +0,0 @@
{% autoescape off %}
Hi,
WG chair approval is needed for posting of {{ draft.filename }}-{{ draft.revision }}.
To approve the draft, go to this URL (note: you need to login to be able to approve):
https://{{ domain }}/submit/status/{{ draft.submission_id }}/{{ draft.submission_hash }}/
File name : {{ draft.filename }}
Version : {{ draft.revision }}
Submission date : {{ draft.submission_date }}
Group : {{ draft.group_acronym|default:"Individual Submission" }}
Title : {{ draft.id_document_name }}
Document date : {{ draft.creation_date }}
Pages : {{ draft.txt_page_count }}
File size : {{ draft.filesize|filesizeformat }}
Submitter : {{ submitter.get_full_name }} <{{ submitter.email_address }}>
Abstract : {{ draft.abstract }}
Authors:
{% for author in authors %} {{ author.get_full_name }} <{{ author.email.1 }}>
{% endfor %}
{% endautoescape %}
Best regards,
The IETF Secretariat
through the draft submission service

View file

@ -0,0 +1,275 @@
{% extends "submit/submit_base.html" %}
{% load ietf_filters submit_tags %}
{% block title %}Status of submission of {{ submission.name }}-{{ submission.rev }}{% endblock %}
{% block submit_content %}
{% if submission.state_id != "uploaded" %}
<h2>Status of the submission: {{ submission.state.name }}</h2>
{% endif %}
{% if message %}
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
{% endif %}
{% if submitter_form.errors %}
<div class="info-message-error">Please fix errors in the form below</div>
{% endif %}
<h2>IDNITS</h2>
<p>
{% if passes_idnits %}
Your draft has been verified to meet IDNITS requirements.
{% else %}
Your draft has NOT been verified to meet IDNITS requirements.
{% endif %}
<a class="idnits-trigger" href="">(View IDNITS Results)</a>
</p>
<div class="idnits-popup" style="display:none">
<div class="content">
<pre>{{ submission.idnits_message }}</pre>
</div>
<div class="actions">
<a href="" class="button close">Close</a>
</div>
</div>
<div class="twopages-popup" style="display:none">
<div class="content">
<pre>{{ submission|two_pages_decorated_with_errors:errors }}</pre>
</div>
<div class="actions">
<a href="" class="button close">Close</a>
</div>
</div>
<h2>Meta-Data from the Submission</h2>
{% if errors %}
<div class="metadata-errors">
<h3>Meta-Data errors found</h3>
<p>Please make sure that your Internet-Draft includes all of the required meta-data in the proper format.</p>
<p>If your Internet-Draft *does* include all of the required meta-data in the proper format, and if
the error(s) identified above are due to the failure of the tool to extract the meta-data correctly,
then please use the 'Adjust Meta-Data' button below, which will take you to the 'Adjust Screen' where
you can correct the improperly extracted meta-data. You will then be able to submit your Internet-Draft
to the Secretariat for manual posting.</p>
<p>If your Internet-Draft *does not* include all of the required meta-data in the proper format, then
please cancel this submission, update your Internet-Draft, and resubmit it.</p>
<p><strong>NOTE:</strong> The Secretariat will NOT add any
meta-data to your Internet-Draft or edit the meta-data. An
Internet-Draft that does not include all of the required meta-data in
the proper format WILL be returned to the submitter.</p>
</div>
{% endif %}
<table class="metadata-table">
<tr{% if errors.files %} class="error"{% endif %}>
<th>Document</th>
<td>
{% if submission.state_id == "posted" %}<a href="http://www.ietf.org/id/{{ submission.name }}-{{submission.rev}}.txt">{{ submission.name }}</a>{% else %}{{ submission.name }}{% endif %}
<div><a class="twopages-trigger" href="">[View first two pages]</a></div>
{% show_submission_files submission %}
{% if errors.files %}<div class="error-msg">{{ errors.files|safe }}</div>{% endif %}
</td>
</tr>
<tr{% if errors.rev %} class="error"{% endif %}>
<th>Revision</th>
<td>{{ submission.rev }}
{% if errors.rev %}
<div class="error-msg">
{{ errors.rev }}
<br /><a class="twopages-trigger" href="">[View error]</a>
</div>
{% endif %}
</td>
</tr>
<tr{% if errors.group %} class="error"{% endif %}>
<th>Group</th>
<td>{{ submission.group|default:"Individual Submission" }}
{% if errors.group %}<div class="error-msg">{{ errors.group }}</div>{% endif %}
</td>
</tr>
<tr{% if errors.document_date %} class="error"{% endif %}>
<th>Document date</th>
<td>{{ submission.document_date }}
{% if errors.document_date %}<div class="error-msg">{{ errors.document_date }}</div>{% endif %}
</td>
</tr>
<tr>
<th>Submission date</th>
<td>{{ submission.submission_date }}</td>
</tr>
<tr{% if errors.title %} class="error"{% endif %}>
<th>Title</th>
<td>{{ submission.title|default:"" }}
{% if errors.title %}<div class="error-msg">{{ errors.title }}</div>{% endif %}
</td>
</tr>
<tr{% if errors.authors %} class="error"{% endif %}>
<th>Authors</th>
<td>{% with submission.authors_parsed as authors %}{{ authors|length }} author{{ authors|pluralize }}{% endwith %}
{% if errors.authors %}<div class="error-msg">{{ errors.authors|safe }}</div>{% endif %}
</td>
</tr>
{% for author in submission.authors_parsed %}
<tr{% if errors.authors %} class="error"{% endif %}>
<th class="author">Author {{ forloop.counter }}</th>
<td>{{ author.name }} {% if author.email %}&lt;{{ author.email }}&gt;{% endif %}</td>
</tr>
{% endfor %}
<tr{% if errors.abstract %} class="error"{% endif %}>
<th>Abstract</th>
<td>{{ submission.abstract|linebreaksbr }}
{% if errors.abstract %}<div class="error-msg">{{ errors.abstract }}</div>{% endif %}
</td>
</tr>
<tr{% if errors.pages %} class="error"{% endif %}>
<th>Pages</th>
<td>{{ submission.pages }}
{% if errors.pages %}<div class="error-msg">{{ errors.pages }}</div>{% endif %}
</td>
</tr>
<tr>
<th>File size</th>
<td>{{ submission.file_size|filesizeformat }}</td>
</tr>
</table>
{% if can_edit %}
<form method="post">
<input type="hidden" name="action" value="edit" />
<input type="submit" value="Adjust Meta-Data" value="adjust" /> (Leads to manual post by the Secretariat)
</form>
{% if passes_idnits and not errors %}
<h2>Please edit the following meta-data before posting</h2>
<p></p>
<form method="post">
<table class="metadata-table">
{% include "submit/submitter_form.html" %}
</table>
<input type="hidden" name="action" value="autopost" />
<input type="submit" value="Post submission" />
{% if requires_group_approval %}
(Notifies group chairs to get approval)
{% else %}
{% if requires_prev_authors_approval %}
(Notifies authors of previous revision of draft to get approval)
{% else %}
(Notifies submitter and authors for confirmation)
{% endif %}
{% endif %}
</form>
{% endif %}
{% else %}
{% if submission.submitter %}
<h3>Submitter information</h3>
<table class="metadata-table">
<tr><th>Name</th><td>{{ submission.submitter_parsed.name }}</td></tr>
<tr><th>Email address</th><td>{{ submission.submitter_parsed.email }}</td></tr>
</table>
{% endif %}
{% endif %}
{% if can_cancel %}
<h2>Cancel submission</h2>
<p>
Cancel submission and delete the uploaded file{{ submission.file_types|split:","|pluralize }} permanently:
<form id="cancel-submission" method="post">
<input type="hidden" name="action" value="cancel" />
<input type="submit" value="Cancel submission" />
</form>
</p>
{% endif %}
{% if can_group_approve %}
<h2>Approve submission</h2>
<p>
<form method="post">
<input type="hidden" name="action" value="approve" />
<input type="submit" value="Approve this submission" />
</form>
</p>
{% endif %}
{% if can_force_post %}
<p>
<form method="post">
<input type="hidden" name="action" value="forcepost" />
<input type="submit" value="Force post of submission" />
</form>
</p>
{% endif %}
{% if show_send_full_url %}
<div class="metadata-errors">
<p>You are not allowed to modify or cancel this submission. You can
only modify or cancel this submission from the same URL you were
redirected to after the submission.</p>
<p>If you are the submitter check your browser history to find this
URL. You can share it with any person you need.</p>
<p>If you are one of the authors you can request the URL from wich
you can modify or cancel this submission by clicking the next
button. An email will then be sent to the authors and submitter
(if submitter email was entered): {{ confirmation_list|join:", " }}.</p>
<form method="post">
<input type="hidden" name="action" value="sendfullurl" />
<input type="submit" value="Request full access URL" />
</form>
</div>
{% endif %}
<h2>History</h2>
<table class="ietf-table history">
<tr><th>Date</th><th>By</th><th>Text</th></tr>
{% for e in submission.submissionevent_set.all %}
<tr class="{% cycle oddrow,evenrow %}">
<td>{{ e.time|date:"Y-m-d" }}</td>
<td>{{ e.by|default:"" }}</td>
<td>{{ e.desc }}
</td>
</tr>
{% endfor %}
</table>
{% include "submit/problem-reports-footer.html" %}
{% endblock %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/draft-submit.js"></script>
{% endblock %}

View file

@ -1,34 +1,22 @@
{% extends "base.html" %}
{% block morecss %}
.ietf-navset {
background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px;
color:white;
border:1px solid black;
padding:4px;
}
.ietf-navset .selected { font-weight:bold; padding: 0 3px; }
.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; }
.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; }
{% block pagehead %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="/css/submit.css"></link>
{% endblock %}
{% block content %}
<h1>IETF Internet-Draft Submission </h1>
<div class="ietf-navset">
{% if selected == "index" %}<span class="selected">Upload</span>{% else %}<a href="{% url submit_index %}">Upload</a>{% endif %} |
{% if selected == "status" %}<span class="selected">Status</span>{% else %}<a href="{% url submit_status %}">Status</a>{% endif %} |
{% if selected == "index" %}<span class="selected">Upload</span>{% else %}<a href="{% url submit_upload_submission %}">Upload</a>{% endif %} |
{% if selected == "status" %}<span class="selected">Status</span>{% else %}<a href="{% url submit_search_submission %}">Status</a>{% endif %} |
{% if selected == "instructions" %}<span class="selected">Tool Instructions</span>{% else %}<a href="{% url submit_tool_instructions %}">Tool Instructions</a>{% endif %} |
{% if selected == "notewell" %}<span class="selected">NOTE WELL</span>{% else %}<a href="{% url submit_note_well %}">NOTE WELL</a>{% endif %} |
{% if selected == "approvals" %}<span class="selected">Approvals</span>{% else %}<a href="{% url submit_approvals %}">Approvals</a>{% endif %}
</div>
{% if form.cutoff_warning %}
<div class="cutoff-warning">
{{ form.cutoff_warning|safe }}
</div>
{% endif %}
{% block submit_content %}
{% endblock %}
{% endblock %}

View file

@ -1,22 +0,0 @@
{% extends "submit/submit_base.html" %}
{% block title %}Upload{% endblock %}
{% block pagehead %}
{{ form.media }}
{% endblock %}
{% block submit_content %}
<p>This page is used to submit IETF Internet-Drafts to the Internet-Draft repository. The list of current Internet-Drafts can be accessed at <a href="http://www.ietf.org/ietf/1id-abstracts.txt">http://www.ietf.org/ietf/1id-abstracts.txt</a></p>
<p>Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts.</p>
<p>Internet-Drafts are draft documents, and are valid for a maximum of six months. They may be updated, replaced, or obsoleted by other documents at any time.</p>
{% if not form.shutdown %}
<p>If you run into problems when submitting an Internet-Draft using this and the following pages, you may alternatively submit your draft by email to <a href="mailto:internet-drafts@ietf.org">internet-drafts@ietf.org</a>. However, be advised that manual processing always takes additional time.</p>
{{ form }}
{% endif %}
<p>
The IETF is an organized activity of the <a href="http://www.isoc.org">Internet Society</a>
<br>Please send problem reports to <a href="mailto:ietf-action@ietf.org">ietf-action@ietf.org</a>.
</p>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends "submit/submit_base.html" %}
{% block title %}Submission status{% endblock %}
{% block pagehead %}
{{ form.media }}
{% endblock %}
{% block submit_content %}
<p>
Please enter the filename of the Internet-Draft you wish to view submission status for:
</p>
<form method="post" action="">
{% if error %}<div style="color: red;">{{ error }}</div>{% endif %}
<input type="text" name="filename" value="{{ filename|default:"draft-" }}" />
<input type="submit" value="Find status" />
</form>
<p>
<strong>
Note that the status page only displays the status of an Internet-Draft with a posting still in progress or an Internet-Draft that has been successfully posted.</strong>
</p>
<p>
The IETF is an organized activity of the <a href="http://www.isoc.org">Internet Society</a>
<br>Please send problem reports to <a href="mailto:ietf-action@ietf.org">ietf-action@ietf.org</a>.
</p>
{% endblock %}

View file

@ -1,53 +0,0 @@
{% load i18n %}
<form class="submitform liaisonform" method="post" action="" enctype="multipart/form-data">
<div class="formconfig" style="display: none;">
{% block formconfig %}
<span class="info_update_url">{% url get_info %}</span>
{% endblock %}
</div>
<div class="baseform{% if form.edit %} baseformedit{% endif %}">
{% if form.errors %}
<div class="formErrors">
Please correct the errors below.
</div>
{{ form.non_field_errors }}
{% endif %}
{% for fieldset in form.get_fieldsets %}
{% if fieldset.name %}
<div class="fieldset">
<h2>{{ fieldset.name }}</h2>
{% endif %}
{% for field in fieldset.fields %}
<div id="baseform-fieldname-{{ field.html_name }}"
class="{% if field.errors %}fieldError {% endif %}field BaseFormStringWidget{% if field.field.column_style %} {{ field.field.column_style }}{% endif %}">
<label for="id_{{ field.html_name }}">{{ field.label }}
{% if field.field.required %}
<span class="fieldRequired" title="Required">*</span>
{% endif %}
</label>
<div class="fieldWidget">
<div id="{{ field.html_name }}_help" class="formHelp"> {{ field.help_text }}</div>
{{ field }}
{{ field.errors }}
</div>
<div class="endfield"></div>
</div>
{% endfor %}
{% if fieldset.name %}
</div>
{% endif %}
{% endfor %}
</div>
{% if not form.shutdown %}
<div class="submitrow">
<input type="submit" value="Upload" name="upload" />
</div>
{% endif %}
</form>

View file

@ -0,0 +1,17 @@
<tr>
<th>Submitter</th>
<td class="author-button-help">
If you are one of the authors, please click the button below
with your name on it to automatically fill in the
submitter information. Otherwise,
please manually enter your name and email address.<br /><br />
{% for author in submission.authors_parsed %}
<input type="button" class="author" data-name="{{ author.name }}" data-email="{{ author.email }}" value="{{ author.name }}" />
{% endfor %}
</td>
</tr>
{% for field in submitter_form %}
<tr{% if field.errors %} class="error"{% endif %}><th>{{ field.label_tag }}</th><td>{{ field }}{{ field.errors }}</td></tr>
{% endfor %}

View file

@ -3,7 +3,7 @@
{% block submit_content %}
<h2>I-D Submission Tool Instructions</h2>
<h3>Tool URL: <a href="{% url submit_index %}">http://datatracker.ietf.org/{% url submit_index %}</a></h3>
<h3>Tool URL: <a href="{% url submit_upload_submission %}">https://datatracker.ietf.org{% url submit_upload_submission %}</a></h3>
This page will explain the purpose and content of each screen in the I-D Submission Tool, and the actions that result by clicking the form buttons on each screen.<br>
The specification for this tool can be found in <a href="http://www.ietf.org/rfc/rfc4228.txt?number=4228">RFC 4228</a>.
@ -98,7 +98,7 @@ This is the screen where a user can adjust any meta-data that could have been in
<b>Status Screen</b>
<p>
The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by filename.
The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by name.
</p>
<u>Form buttons and resulting actions:</u><br>
<table border="0">

View file

@ -0,0 +1,63 @@
{% extends "submit/submit_base.html" %}
{% block title %}Upload{% endblock %}
{% block morecss %}
{{ block.super }}
form.upload-form h3 { margin: 0; color: #fff; background-color: #2647a0; padding: 0.3em 0.8em; }
form.upload-form table { padding: 0.3em 0.8em; }
form.upload-form .required { color: #f00; }
form.upload-form td { padding-right: 5em; padding-top: 0.4em; padding-bottom: 0.4em; }
form.upload-form .ietf-box { margin-bottom: 1em; }
{% endblock %}
{% block submit_content %}
{% if form.cutoff_warning %}
<div class="cutoff-warning">
{{ form.cutoff_warning|safe }}
</div>
{% endif %}
<p>This page is used to submit IETF Internet-Drafts to the
Internet-Draft repository. The list of current Internet-Drafts can be
accessed at<a href="http://www.ietf.org/ietf/1id-abstracts.txt">http://www.ietf.org/ietf/1id-abstracts.txt</a></p>
<p>Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF), its areas, and its working groups. Note that other
groups may also distribute working documents as Internet-Drafts.</p>
<p>Internet-Drafts are draft documents, and are valid for a maximum of
six months. They may be updated, replaced, or obsoleted by other
documents at any time.</p>
{% if not form.shutdown %}
<p>If you run into problems when submitting an Internet-Draft
using this and the following pages, you may alternatively submit
your draft by email to
<a href="mailto:internet-drafts@ietf.org">internet-drafts@ietf.org</a>.
However, be advised that manual processing always takes additional time.</p>
<form class="upload-form" method="post" enctype="multipart/form-data">
<div class="ietf-box">
<h3>Upload a draft</h3>
{{ form.non_field_errors }}
<table>
{% for field in form %}
<tr>
<td>{{ field.label_tag }} {% if field.field.required %}<span class="required">*</span>{% endif %}</td>
<td>{{ field }} {{ field.errors }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="upload-submit">
<input type="submit" value="Upload" name="upload" />
</div>
</form>
{% endif %}
{% include "submit/problem-reports-footer.html" %}
{% endblock %}

31
static/css/submit.css Normal file
View file

@ -0,0 +1,31 @@
.ietf-navset {
background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px;
color:white;
border:1px solid black;
padding:4px;
}
.ietf-navset .selected { font-weight:bold; padding: 0 3px; }
.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; }
.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; }
.problem-reports-footer { font-style: italic; margin-top: 2em; }
div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; max-width: 50em; }
div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; }
div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; }
table.metadata-table th { white-space: nowrap; font-weight: bold; }
table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; }
table.metadata-table th.author { text-align: right; }
table.metadata-table tr { vertical-align: top; }
table.metadata-table tr.error td, table.metadata-table tr.error th { background-color: #ffaaaa; }
table.metadata-table div.error-msg { color: red; }
table.metadata-table td.author-button-help { max-width: 40em; }
table.metadata-table input.name, table.metadata-table input.email { width: 25em; }
pre.twopages { margin: 0px; }
.idnits-popup .content,
.twopages-popup .content { background-color: #fff; width: 55em; height: 30em; overflow: auto; padding: 1em; }
a.idnits-trigger, a.twopages-trigger, a.idnits-trigger:visited, a.twopages-trigger:visited { color: #000; }
table.history { max-width: 50em; }

View file

@ -1,12 +1,60 @@
$(function (){
jQuery(function (){
// fill in submitter info when an author button is clicked
$("input[type=button]").click(function () {
var name = $(this).data("name");
if (name == null) // backwards compatibility
return;
var email = $(this).data("email");
jQuery("input[type=button].author").click(function () {
var name = jQuery(this).data("name");
var email = jQuery(this).data("email");
$(this).parents("form").find("input[name=name]").val(name || "");
$(this).parents("form").find("input[name=email]").val(email || "");
jQuery(this).parents("form").find("input[name=submitter-name]").val(name || "");
jQuery(this).parents("form").find("input[name=submitter-email]").val(email || "");
});
jQuery("form").submit(function() {
if (this.submittedAlready)
return false;
else {
this.submittedAlready = true;
return true;
}
});
jQuery("form#cancel-submission").submit(function () {
return confirm("Cancel this submission?");
});
jQuery(".idnits-trigger").click(function (e) {
e.preventDefault();
var popup = jQuery(".idnits-popup").clone().show();
showModalBox(popup);
});
jQuery(".twopages-trigger").click(function (e) {
e.preventDefault();
var popup = jQuery(".twopages-popup").clone().show();
showModalBox(popup);
});
jQuery("form .add-author").click(function (e) {
e.preventDefault();
var table = jQuery("table.authors tbody");
var row = table.find("tr.empty").clone();
row.removeClass("empty");
var prefixInput = row.find('input[name=authors-prefix]');
// figure out a prefix
var i = 0, prefix;
do {
++i;
prefix = prefixInput.val() + i;
}
while (table.find('input[name=authors-prefix][value="' + prefix +'"]').length > 0);
prefixInput.val(prefix);
row.find('input').not(prefixInput).each(function () {
this.name = prefix + "-" + this.name;
});
table.append(row);
});
});