Merged in branch/iola/people-cleanup @ [8471] from olau@iola.dk, which

enhances a number of pages in the datatracker where forms contain fields
  where a person should be chosen, such as for instance when choosing the
  shepherd of a document, so that they uniformly display choices which show
  both email address and name.

  Furthermore, changes have been made so as to make the email address
  uniformly act as reference to the person record.  

  Autocompletion is now consistently provided when looking for the email
  address or name of the person to choose.  

  This solves a number of issues where it has been difficult to choose the
  correct Person/Email combination, and where the correct email address to use
  for an association has been unavailable previously.
 - Legacy-Id: 8472
Note: SVN reference [8471] has been migrated to Git commit d62f2343e8
This commit is contained in:
Henrik Levkowetz 2014-10-25 21:05:50 +00:00
commit 704589b4c7
47 changed files with 1657 additions and 285 deletions

View file

@ -15,7 +15,7 @@ ietfdb (5.7.0) ietf; urgency=medium
correct Person/Email combination, and where the correct email address to use
for an association has been unavailable previously.
-- Henrik Levkowetz <henrik@levkowetz.com> 25 Oct 2014 13:53:37 -0700
-- Henrik Levkowetz <henrik@levkowetz.com> 25 Oct 2014 13:49:49 -0200
ietfdb (5.6.5) ietf; urgency=medium

View file

@ -89,7 +89,7 @@ class ShepherdRule(RuleManager):
description = 'All I-Ds with a particular document shepherd'
def get_documents(self):
return Document.objects.filter(type='draft', states__slug='active').filter(shepherd__name__icontains=self.value).distinct()
return Document.objects.filter(type='draft', states__slug='active').filter(shepherd__person__name__icontains=self.value).distinct()
# class ReferenceToRFCRule(RuleManager):

View file

@ -32,9 +32,9 @@ def email_stream_changed(request, doc, old_stream, new_stream, text=""):
# These use comprehension to deal with conditions when there might be more than one chair listed for a stream
if old_stream:
to.extend([x.person.formatted_email() for x in Role.objects.filter(group__acronym=old_stream.slug,name='chair')])
to.extend([r.formatted_email() for r in Role.objects.filter(group__acronym=old_stream.slug, name='chair')])
if new_stream:
to.extend([x.person.formatted_email() for x in Role.objects.filter(group__acronym=new_stream.slug,name='chair')])
to.extend([r.formatted_email() for r in Role.objects.filter(group__acronym=new_stream.slug, name='chair')])
if not to:
return
@ -443,10 +443,10 @@ def stream_state_email_recipients(doc, extra_recipients=[]):
res.append(email.formatted_email())
persons.add(email.person)
for p in extra_recipients:
if not p in persons:
res.append(p.formatted_email())
persons.add(p)
for e in extra_recipients:
if e.person not in persons:
res.append(e.formatted_email())
persons.add(e.person)
return res

View file

@ -0,0 +1,380 @@
# -*- coding: utf-8 -*-
from south.db import db
from south.v2 import SchemaMigration
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Document.shepherd_email'
db.add_column(u'doc_document', 'shepherd_email',
self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='shepherd_document_set', null=True, to=orm['person.Email']),
keep_default=False)
# Adding field 'DocHistory.shepherd_email'
db.add_column(u'doc_dochistory', 'shepherd_email',
self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='shepherd_dochistory_set', null=True, to=orm['person.Email']),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Document.shepherd_email'
db.delete_column(u'doc_document', 'shepherd_email_id')
# Deleting field 'DocHistory.shepherd_email'
db.delete_column(u'doc_dochistory', 'shepherd_email_id')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
u'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'}),
u'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'})
},
u'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': [u'doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.BallotType']"}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
u'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': [u'doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': u"orm['name.BallotPositionName']"})
},
u'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'doc.consensusdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': [u'doc.DocEvent']},
'consensus': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
u'doc.deletedevent': {
'Meta': {'object_name': 'DeletedEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'json': ('django.db.models.fields.TextField', [], {}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
u'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'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'})
},
u'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': u"orm['doc.Document']"}),
'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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'}),
'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', [], {'to': u"orm['doc.DocAlias']", 'symmetrical': 'False', 'through': u"orm['doc.RelatedDocHistory']", '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_dochistory_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
'shepherd_email': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
u'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
u'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocEvent']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocReminderTypeName']"})
},
u'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': u"orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['person.Person']"}),
'shepherd_email': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
u'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
u'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
u'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': u"orm['doc.DocAlias']"})
},
u'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocAlias']"})
},
u'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'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': u"orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'doc.statedocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.State']", 'null': 'True', 'blank': 'True'}),
'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"})
},
u'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'})
},
u'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
u'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': u"orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
u'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'}),
u'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': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']

View file

@ -0,0 +1,377 @@
# -*- coding: utf-8 -*-
from south.v2 import DataMigration
class Migration(DataMigration):
def forwards(self, orm):
# figure out person -> email mapping
shepherds = set(orm.Document.objects.values_list("shepherd", flat=True).distinct())
shepherds.update(orm.DocHistory.objects.values_list("shepherd", flat=True).distinct())
for person_id in shepherds:
emails = orm['person.Email'].objects.filter(person=person_id).order_by("-active", "-time")[:1]
if not emails:
print "ERROR: no emails for", person_id
email = emails[0]
orm.Document.objects.filter(shepherd=person_id, shepherd_email=None).update(shepherd_email=email)
orm.DocHistory.objects.filter(shepherd=person_id, shepherd_email=None).update(shepherd_email=email)
def backwards(self, orm):
pass
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
u'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'}),
u'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'})
},
u'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': [u'doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.BallotType']"}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
u'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': [u'doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': u"orm['name.BallotPositionName']"})
},
u'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'doc.consensusdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': [u'doc.DocEvent']},
'consensus': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
u'doc.deletedevent': {
'Meta': {'object_name': 'DeletedEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'json': ('django.db.models.fields.TextField', [], {}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
u'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'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'})
},
u'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': u"orm['doc.Document']"}),
'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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'}),
'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', [], {'to': u"orm['doc.DocAlias']", 'symmetrical': 'False', 'through': u"orm['doc.RelatedDocHistory']", '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_dochistory_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
'shepherd_email': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
u'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
u'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocEvent']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocReminderTypeName']"})
},
u'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': u"orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['person.Person']"}),
'shepherd_email': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
u'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
u'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
u'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': u"orm['doc.DocAlias']"})
},
u'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocAlias']"})
},
u'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'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': u"orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'doc.statedocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.State']", 'null': 'True', 'blank': 'True'}),
'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"})
},
u'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'})
},
u'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
u'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': u"orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
u'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'}),
u'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': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']
symmetrical = True

View file

@ -0,0 +1,375 @@
# -*- coding: utf-8 -*-
from south.db import db
from south.v2 import SchemaMigration
class Migration(SchemaMigration):
def forwards(self, orm):
db.delete_column(u'doc_document', 'shepherd_id')
db.rename_column(u'doc_document', 'shepherd_email_id', 'shepherd_id')
db.delete_column(u'doc_dochistory', 'shepherd_id')
db.rename_column(u'doc_dochistory', 'shepherd_email_id', 'shepherd_id')
def backwards(self, orm):
db.rename_column(u'doc_document', 'shepherd_id', 'shepherd_email_id')
db.add_column(u'doc_document', 'shepherd',
self.gf('django.db.models.fields.related.ForeignKey')(related_name='shepherd_document_set', null=True, to=orm['person.Person'], blank=True),
keep_default=False)
db.rename_column(u'doc_dochistory', 'shepherd_id', 'shepherd_email_id')
db.add_column(u'doc_dochistory', 'shepherd',
self.gf('django.db.models.fields.related.ForeignKey')(related_name='shepherd_document_set', null=True, to=orm['person.Person'], blank=True),
keep_default=False)
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
u'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'}),
u'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'})
},
u'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': [u'doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.BallotType']"}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
u'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': [u'doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': u"orm['name.BallotPositionName']"})
},
u'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'doc.consensusdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': [u'doc.DocEvent']},
'consensus': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
u'doc.deletedevent': {
'Meta': {'object_name': 'DeletedEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'json': ('django.db.models.fields.TextField', [], {}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
u'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'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'})
},
u'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': u"orm['doc.Document']"}),
'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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'}),
'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', [], {'to': u"orm['doc.DocAlias']", 'symmetrical': 'False', 'through': u"orm['doc.RelatedDocHistory']", '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_dochistory_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
u'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
u'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocEvent']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocReminderTypeName']"})
},
u'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': u"orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['person.Email']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
u'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
u'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
u'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': u"orm['doc.DocAlias']"})
},
u'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocAlias']"})
},
u'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'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': u"orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'doc.statedocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.State']", 'null': 'True', 'blank': 'True'}),
'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"})
},
u'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'})
},
u'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
u'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': [u'doc.DocEvent']},
u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
u'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': '32', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'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': u"orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
u'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'}),
u'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': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']

View file

@ -64,7 +64,7 @@ class DocumentInfo(models.Model):
intended_std_level = models.ForeignKey(IntendedStdLevelName, verbose_name="Intended standardization level", blank=True, null=True)
std_level = models.ForeignKey(StdLevelName, verbose_name="Standardization level", blank=True, null=True)
ad = models.ForeignKey(Person, verbose_name="area director", related_name='ad_%(class)s_set', blank=True, null=True)
shepherd = models.ForeignKey(Person, related_name='shepherd_%(class)s_set', blank=True, null=True)
shepherd = models.ForeignKey(Email, related_name='shepherd_%(class)s_set', blank=True, null=True)
expires = models.DateTimeField(blank=True, null=True)
notify = models.CharField(max_length=255, blank=True)
external_url = models.URLField(blank=True) # Should be set for documents with type 'External'.

View file

@ -819,6 +819,9 @@ class IndividualInfoFormsTests(TestCase):
self.assertTrue(self.doc.latest_event(DocEvent,type="added_comment").desc.startswith('Shepherding AD changed'))
def test_doc_change_shepherd(self):
self.doc.shepherd = None
self.doc.save()
url = urlreverse('doc_edit_shepherd',kwargs=dict(name=self.docname))
login_testing_unauthorized(self, "plain", url)
@ -835,23 +838,51 @@ class IndividualInfoFormsTests(TestCase):
self.assertEqual(len(q('form input[id=id_shepherd]')),1)
# change the shepherd
plain = Person.objects.get(name='Plain Man')
plain_email = plain.email_set.all()[0]
r = self.client.post(url,dict(shepherd=plain_email))
plain_email = Email.objects.get(person__name="Plain Man")
r = self.client.post(url, dict(shepherd=plain_email.pk))
self.assertEqual(r.status_code,302)
self.doc = Document.objects.get(name=self.docname)
self.assertEqual(self.doc.shepherd,plain)
self.assertEqual(self.doc.shepherd, plain_email)
comments = '::'.join([x.desc for x in self.doc.docevent_set.filter(time=self.doc.time,type="added_comment")])
self.assertTrue('Document shepherd changed to Plain Man' in comments)
self.assertTrue('Notification list changed' in comments)
# test buggy change
ad = Person.objects.get(name='Aread Irector')
two_answers = "%s,%s" % (plain_email, ad.email_set.all()[0])
r = self.client.post(url,(dict(shepherd=two_answers)))
self.assertEqual(r.status_code,200)
r = self.client.post(url, dict(shepherd=two_answers))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(len(q('form ul.errorlist')) > 0)
def test_doc_change_shepherd_email(self):
self.doc.shepherd = None
self.doc.save()
url = urlreverse('doc_change_shepherd_email',kwargs=dict(name=self.docname))
r = self.client.get(url)
self.assertEqual(r.status_code, 404)
self.doc.shepherd = Email.objects.get(person__user__username="ad1")
self.doc.save()
login_testing_unauthorized(self, "plain", url)
self.doc.shepherd = Email.objects.get(person__user__username="plain")
self.doc.save()
new_email = Email.objects.create(address="anotheremail@example.com", person=self.doc.shepherd.person)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
# change the shepherd email
r = self.client.post(url, dict(shepherd=new_email))
self.assertEqual(r.status_code, 302)
self.doc = Document.objects.get(name=self.docname)
self.assertEqual(self.doc.shepherd, new_email)
self.assertTrue(self.doc.latest_event(DocEvent, type="added_comment").desc.startswith('Document shepherd email changed'))
def test_doc_view_shepherd_writeup(self):
url = urlreverse('doc_shepherd_writeup',kwargs=dict(name=self.docname))

View file

@ -81,6 +81,7 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ad/$', views_draft.edit_ad, name='doc_change_ad'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/consensus/$', views_draft.edit_consensus, name='doc_edit_consensus'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/shepherd/$', views_draft.edit_shepherd, name='doc_edit_shepherd'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/shepherdemail/$', views_draft.change_shepherd_email, name='doc_change_shepherd_email'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/shepherdwriteup/$', views_draft.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_draft.request_publication, name='doc_request_publication'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/adopt/$', views_draft.adopt_draft, name='doc_adopt_draft'),

View file

@ -366,7 +366,7 @@ def start_review_sanity_check(request, name):
def build_notify_addresses(doc_to_review):
# Take care to do the right thing during ietf chair and stream owner transitions
notify_addresses = []
notify_addresses.extend([x.person.formatted_email() for x in Role.objects.filter(group__acronym=doc_to_review.stream.slug,name='chair')])
notify_addresses.extend([r.formatted_email() for r in Role.objects.filter(group__acronym=doc_to_review.stream.slug, name='chair')])
notify_addresses.append("%s@%s" % (doc_to_review.name, settings.TOOLS_SERVER))
return notify_addresses

View file

@ -41,6 +41,8 @@ from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings
from django import forms
import debug # pyflakes:ignore
from ietf.doc.models import ( Document, DocAlias, DocHistory, DocEvent, BallotDocEvent,
ConsensusDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent,
IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS )
@ -263,7 +265,7 @@ def document_main(request, name, rev=None):
shepherd_writeup = doc.latest_event(WriteupDocEvent, type="changed_protocol_writeup")
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd) or has_role(request.user, ["Area Director"])
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd and doc.shepherd.person) or has_role(request.user, ["Area Director"])
can_edit_consensus = False
consensus = None
@ -351,6 +353,7 @@ def document_main(request, name, rev=None):
can_edit=can_edit,
can_change_stream=can_change_stream,
can_edit_stream_info=can_edit_stream_info,
is_shepherd = user_is_person(request.user, doc.shepherd and doc.shepherd.person),
can_edit_shepherd_writeup=can_edit_shepherd_writeup,
can_edit_iana_state=can_edit_iana_state,
can_edit_consensus=can_edit_consensus,
@ -659,7 +662,7 @@ def document_shepherd_writeup(request, name):
writeup_text = "(There is no shepherd's writeup available for this document)"
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd) or has_role(request.user, ["Area Director"])
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd and doc.shepherd.person) or has_role(request.user, ["Area Director"])
return render_to_response("doc/shepherd_writeup.html",
dict(doc=doc,

View file

@ -4,7 +4,7 @@ import datetime, json
from django import forms
from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.shortcuts import render_to_response, get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.template import RequestContext
from django.conf import settings
@ -29,8 +29,8 @@ from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_p
from ietf.ietfauth.utils import role_required
from ietf.message.models import Message
from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName
from ietf.person.forms import EmailsField
from ietf.person.models import Person
from ietf.person.fields import AutocompletedEmailField
from ietf.person.models import Person, Email
from ietf.secr.lib.template import jsonapi
from ietf.utils.mail import send_mail, send_mail_message
from ietf.utils.textupload import get_cleaned_text_file_content
@ -301,10 +301,8 @@ def collect_email_addresses(emails, doc):
for role in doc.group.parent.role_set.filter(name='chair'):
if role.email.address not in emails:
emails[role.email.address] = '"%s"' % (role.person.name)
if doc.shepherd:
address = doc.shepherd.email_address();
if address not in emails:
emails[address] = '"%s"' % (doc.shepherd.name)
if doc.shepherd and doc.shepherd.address not in emails:
emails[doc.shepherd.address] = u'"%s"' % (doc.shepherd.person.name or "")
return emails
class ReplacesForm(forms.Form):
@ -941,13 +939,7 @@ def edit_shepherd_writeup(request, name):
context_instance=RequestContext(request))
class ShepherdForm(forms.Form):
shepherd = EmailsField(label="Shepherd", required=False)
def clean_shepherd(self):
data = self.cleaned_data['shepherd']
if len(data)>1:
raise forms.ValidationError("Please choose at most one shepherd.")
return data
shepherd = AutocompletedEmailField(required=False, only_users=True)
def edit_shepherd(request, name):
"""Change the shepherd for a Document"""
@ -955,30 +947,29 @@ def edit_shepherd(request, name):
doc = get_object_or_404(Document, type="draft", name=name)
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
if not can_edit_stream_info:
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
if request.method == 'POST':
form = ShepherdForm(request.POST)
if form.is_valid():
save_document_in_history(doc)
if form.cleaned_data['shepherd']:
doc.shepherd = form.cleaned_data['shepherd'][0].person
else:
doc.shepherd = None
if form.cleaned_data['shepherd'] != doc.shepherd:
doc.shepherd = form.cleaned_data['shepherd']
doc.save()
login = request.user.person
c = DocEvent(type="added_comment", doc=doc, by=login)
c.desc = "Document shepherd changed to "+ (doc.shepherd.name if doc.shepherd else "(None)")
c.save()
c = DocEvent(type="added_comment", doc=doc, by=request.user.person)
c.desc = "Document shepherd changed to "+ (doc.shepherd.person.name if doc.shepherd else "(None)")
c.save()
if doc.shepherd.formatted_email() not in doc.notify:
login = request.user.person
addrs = doc.notify
if addrs:
addrs += ', '
addrs += doc.shepherd.formatted_email()
e = make_notify_changed_event(request, doc, login, addrs, c.time)
make_notify_changed_event(request, doc, login, addrs, c.time)
doc.notify = addrs
doc.time = c.time
@ -987,19 +978,55 @@ def edit_shepherd(request, name):
return redirect('doc_view', name=doc.name)
else:
current_shepherd = None
if doc.shepherd:
e = doc.shepherd.email_set.order_by("-active", "-time")
if e:
current_shepherd=e[0].pk
init = { "shepherd": current_shepherd}
form = ShepherdForm(initial=init)
form = ShepherdForm(initial={ "shepherd": doc.shepherd_id })
return render_to_response('doc/change_shepherd.html',
{'form': form,
'doc': doc,
},
context_instance = RequestContext(request))
return render(request, 'doc/change_shepherd.html', {
'form': form,
'doc': doc,
})
class ChangeShepherdEmailForm(forms.Form):
shepherd = forms.ModelChoiceField(queryset=Email.objects.all(), label="Shepherd email", empty_label=None)
def __init__(self, *args, **kwargs):
super(ChangeShepherdEmailForm, self).__init__(*args, **kwargs)
self.fields["shepherd"].queryset = self.fields["shepherd"].queryset.filter(person__email=self.initial["shepherd"]).distinct()
def change_shepherd_email(request, name):
"""Change the shepherd email address for a Document"""
doc = get_object_or_404(Document, name=name)
if not doc.shepherd:
raise Http404
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
is_shepherd = user_is_person(request.user, doc.shepherd and doc.shepherd.person)
if not can_edit_stream_info and not is_shepherd:
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
initial = { "shepherd": doc.shepherd_id }
if request.method == 'POST':
form = ChangeShepherdEmailForm(request.POST, initial=initial)
if form.is_valid():
if form.cleaned_data['shepherd'] != doc.shepherd:
save_document_in_history(doc)
doc.shepherd = form.cleaned_data['shepherd']
doc.save()
c = DocEvent(type="added_comment", doc=doc, by=request.user.person)
c.desc = "Document shepherd email changed"
c.save()
return redirect('doc_view', name=doc.name)
else:
form = ChangeShepherdEmailForm(initial=initial)
return render(request, 'doc/change_shepherd_email.html', {
'form': form,
'doc': doc,
})
class AdForm(forms.Form):
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'),

View file

@ -19,7 +19,7 @@ from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStat
from ietf.group.utils import save_group_in_history, can_manage_group_type
from ietf.group.utils import get_group_or_404
from ietf.ietfauth.utils import has_role
from ietf.person.forms import EmailsField
from ietf.person.fields import AutocompletedEmailsField
from ietf.person.models import Person, Email
from ietf.group.mails import email_iesg_secretary_re_charter
@ -29,10 +29,11 @@ class GroupForm(forms.Form):
name = forms.CharField(max_length=255, label="Name", required=True)
acronym = forms.CharField(max_length=10, label="Acronym", required=True)
state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True)
chairs = EmailsField(label="Chairs", required=False)
secretaries = EmailsField(label="Secretaries", required=False)
techadv = EmailsField(label="Technical Advisors", required=False)
delegates = EmailsField(label="Delegates", required=False, help_text=mark_safe("Type in name to search for person<br>Chairs can delegate the authority to update the state of group documents - max %s persons at a given time" % MAX_GROUP_DELEGATES))
chairs = AutocompletedEmailsField(required=False, only_users=True)
secretaries = AutocompletedEmailsField(required=False, only_users=True)
techadv = AutocompletedEmailsField(label="Technical Advisors", required=False, only_users=True)
delegates = AutocompletedEmailsField(required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES,
help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - max %s persons at a given time" % MAX_GROUP_DELEGATES))
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Shepherding AD", empty_label="(None)", required=False)
parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", required=False)
list_email = forms.CharField(max_length=64, required=False)

View file

@ -10,7 +10,7 @@ from ietf.group.models import Group, GroupEvent, Role
from ietf.group.utils import save_group_in_history
from ietf.ietfauth.utils import has_role
from ietf.name.models import StreamName
from ietf.person.forms import EmailsField
from ietf.person.fields import AutocompletedEmailsField
from ietf.person.models import Email
import debug # pyflakes:ignore
@ -31,7 +31,7 @@ def stream_documents(request, acronym):
return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta }, context_instance=RequestContext(request))
class StreamEditForm(forms.Form):
delegates = EmailsField(label="Delegates", required=False, help_text=u"Type in name to search for person")
delegates = AutocompletedEmailsField(required=False, only_users=True)
def stream_edit(request, acronym):
group = get_object_or_404(Group, acronym=acronym)

View file

@ -13,7 +13,7 @@ from ietf.doc.models import Document, DocEvent, DocumentAuthor, RelatedDocument,
from ietf.doc.models import LastCallDocEvent, NewRevisionDocEvent
from ietf.doc.models import IESG_SUBSTATE_TAGS
from ietf.group.models import Group
from ietf.person.models import Person
from ietf.person.models import Person, Email
def all_id_txt():
# this returns a lot of data so try to be efficient
@ -125,8 +125,8 @@ def all_id2_txt():
else:
l.append(a.author.person.plain_name())
shepherds = dict((p.pk, p.formatted_email().replace('"', ''))
for p in Person.objects.filter(shepherd_document_set__type="draft").distinct())
shepherds = dict((e.pk, e.formatted_email().replace('"', ''))
for e in Email.objects.filter(shepherd_document_set__type="draft").select_related("person").distinct())
ads = dict((p.pk, p.formatted_email().replace('"', ''))
for p in Person.objects.filter(ad_document_set__type="draft").distinct())

View file

@ -97,7 +97,7 @@ class IndexTests(TestCase):
self.assertEqual(t[13], draft.title)
author = draft.documentauthor_set.order_by("order").get()
self.assertEqual(t[14], "%s <%s>" % (author.author.person.name, author.author.address))
self.assertEqual(t[15], "%s <%s>" % (draft.shepherd, draft.shepherd.email_address()))
self.assertEqual(t[15], "%s <%s>" % (draft.shepherd.person.name, draft.shepherd.address))
self.assertEqual(t[16], "%s <%s>" % (draft.ad, draft.ad.email_address()))

View file

@ -17,7 +17,8 @@ from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEM
get_user_email, validate_private_key, validate_public_key,
get_or_create_nominee, create_feedback_email)
from ietf.person.models import Email
from ietf.utils import fields as custom_fields
from ietf.person.fields import AutocompletedEmailField
from ietf.utils.fields import MultiEmailField
from ietf.utils.mail import send_mail
@ -31,10 +32,6 @@ def get_nomcom_group_or_404(year):
nomcom__isnull=False)
def get_list(string):
return map(unicode.strip, string.replace('\r\n', '').split(','))
class PositionNomineeField(forms.ChoiceField):
def __init__(self, *args, **kwargs):
@ -109,7 +106,7 @@ class BaseNomcomForm(object):
class EditMembersForm(BaseNomcomForm, forms.Form):
members = custom_fields.MultiEmailField(label="Members email", required=False)
members = MultiEmailField(label="Members email", required=False, widget=forms.Textarea)
fieldsets = [('Members', ('members',))]
@ -133,7 +130,7 @@ class EditMembersFormPreview(FormPreview):
def preview_get(self, request):
"Displays the form"
f = self.form(auto_id=AUTO_ID)
f = self.form(auto_id=self.get_auto_id(), initial=self.get_initial(request))
return render_to_response(self.form_template,
{'form': f,
'stage_field': self.unused_name('stage'),
@ -143,14 +140,14 @@ class EditMembersFormPreview(FormPreview):
'selected': 'edit_members'},
context_instance=RequestContext(request))
def parse_params(self, *args, **kwargs):
def get_initial(self, request):
members = self.group.role_set.filter(name__slug='member')
if members:
self.form.base_fields['members'].initial = ',\r\n'.join([role.email.address for role in members])
return { "members": ",\r\n".join(role.email.address for role in members) }
return {}
def process_preview(self, request, form, context):
members_email = get_list(form.cleaned_data['members'])
members_email = form.cleaned_data['members']
members_info = []
emails_not_found = []
@ -233,10 +230,11 @@ class EditChairFormPreview(FormPreview):
return super(EditChairFormPreview, self).__call__(request, *args, **kwargs)
def parse_params(self, *args, **kwargs):
def get_initial(self, request):
chair = self.group.get_chair()
if chair:
self.form.base_fields['chair'].initial = chair.email.address
return { "chair": chair.email.address }
return {}
def process_preview(self, request, form, context):
chair_email = form.cleaned_data['chair']
@ -297,8 +295,8 @@ class EditNomcomForm(BaseNomcomForm, forms.ModelForm):
class MergeForm(BaseNomcomForm, forms.Form):
secondary_emails = custom_fields.MultiEmailField(label="Secondary email addresses",
help_text="Provide a comma separated list of email addresses. Nominations already received with any of these email address will be moved to show under the primary address")
secondary_emails = MultiEmailField(label="Secondary email addresses",
help_text="Provide a comma separated list of email addresses. Nominations already received with any of these email address will be moved to show under the primary address", widget=forms.Textarea)
primary_email = forms.EmailField(label="Primary email address",
widget=forms.TextInput(attrs={'size': '40'}))
@ -312,22 +310,21 @@ class MergeForm(BaseNomcomForm, forms.Form):
email = self.cleaned_data['primary_email']
nominees = Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(email__address=email)
if not nominees:
msg = "Does not exist a nomiee with this email"
msg = "No nominee with this email exists"
self._errors["primary_email"] = self.error_class([msg])
return email
def clean_secondary_emails(self):
data = self.cleaned_data['secondary_emails']
emails = get_list(data)
emails = self.cleaned_data['secondary_emails']
for email in emails:
nominees = Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(email__address=email)
if not nominees:
msg = "Does not exist a nomiee with email %s" % email
msg = "No nominee with email %s exists" % email
self._errors["primary_email"] = self.error_class([msg])
break
return data
return emails
def clean(self):
primary_email = self.cleaned_data.get("primary_email")
@ -340,7 +337,7 @@ class MergeForm(BaseNomcomForm, forms.Form):
def save(self):
primary_email = self.cleaned_data.get("primary_email")
secondary_emails = get_list(self.cleaned_data.get("secondary_emails"))
secondary_emails = self.cleaned_data.get("secondary_emails")
primary_nominee = Nominee.objects.get_by_nomcom(self.nomcom).get(email__address=primary_email)
while primary_nominee.duplicated:
@ -659,6 +656,8 @@ class PositionForm(BaseNomcomForm, forms.ModelForm):
fieldsets = [('Position', ('name', 'description',
'is_open', 'incumbent'))]
incumbent = AutocompletedEmailField(required=False)
class Meta:
model = Position
fields = ('name', 'description', 'is_open', 'incumbent')

123
ietf/person/fields.py Normal file
View file

@ -0,0 +1,123 @@
import json
from django.utils.html import escape
from django import forms
from django.core.urlresolvers import reverse as urlreverse
import debug # pyflakes:ignore
from ietf.person.models import Email, Person
def tokeninput_id_name_json(objs):
def format_email(e):
return escape(u"%s <%s>" % (e.person.name, e.address))
def format_person(p):
return escape(p.name)
formatter = format_email if objs and isinstance(objs[0], Email) else format_person
return json.dumps([{ "id": o.pk, "name": formatter(o) } for o in objs])
class AutocompletedPersonsField(forms.CharField):
"""Tokenizing autocompleted multi-select field for choosing
persons/emails or just persons using jquery.tokeninput.js.
The field operates on either Email or Person models. In the case
of Email models, the person name is shown next to the email address.
The field uses a comma-separated list of primary keys in a
CharField element as its API, the tokeninput Javascript adds some
selection magic on top of this so we have to pass it a JSON
representation of ids and user-understandable labels."""
def __init__(self,
max_entries=None, # max number of selected objs
only_users=False, # only select persons who also have a user
model=Person, # or Email
hint_text="Type in name to search for person",
*args, **kwargs):
kwargs["max_length"] = 1000
self.max_entries = max_entries
self.only_users = only_users
self.model = model
super(AutocompletedPersonsField, self).__init__(*args, **kwargs)
self.widget.attrs["class"] = "tokenized-field"
self.widget.attrs["data-hint-text"] = hint_text
if self.max_entries != None:
self.widget.attrs["data-max-entries"] = self.max_entries
def parse_tokenized_value(self, value):
return [x.strip() for x in value.split(",") if x.strip()]
def prepare_value(self, value):
if not value:
value = ""
if isinstance(value, basestring):
pks = self.parse_tokenized_value(value)
value = self.model.objects.filter(pk__in=pks).select_related("person")
if isinstance(value, self.model):
value = [value]
self.widget.attrs["data-pre"] = tokeninput_id_name_json(value)
# doing this in the constructor is difficult because the URL
# patterns may not have been fully constructed there yet
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_tokeninput_search", kwargs={ "model_name": self.model.__name__.lower() })
if self.only_users:
self.widget.attrs["data-ajax-url"] += "?user=1" # require a Datatracker account
return ",".join(e.address for e in value)
def clean(self, value):
value = super(AutocompletedPersonsField, self).clean(value)
pks = self.parse_tokenized_value(value)
objs = self.model.objects.filter(pk__in=pks)
if self.model == Email:
objs = objs.exclude(person=None).select_related("person")
# there are still a couple of active roles without accounts so don't disallow those yet
#if self.only_users:
# objs = objs.exclude(person__user=None)
found_pks = [str(o.pk) for o in objs]
failed_pks = [x for x in pks if x not in found_pks]
if failed_pks:
raise forms.ValidationError(u"Could not recognize the following {model_name}s: {pks}. You can only input {model_name}s already registered in the Datatracker.".format(pks=", ".join(failed_pks), model_name=self.model.__name__.lower()))
if self.max_entries != None and len(objs) > self.max_entries:
raise forms.ValidationError(u"You can select at most %s entries only." % self.max_entries)
return objs
class AutocompletedPersonField(AutocompletedPersonsField):
"""Version of AutocompletedPersonsField specialized to a single object."""
def __init__(self, *args, **kwargs):
kwargs["max_entries"] = 1
super(AutocompletedPersonField, self).__init__(*args, **kwargs)
def clean(self, value):
return super(AutocompletedPersonField, self).clean(value).first()
class AutocompletedEmailsField(AutocompletedPersonsField):
"""Version of AutocompletedPersonsField with the defaults right for Emails."""
def __init__(self, model=Email, hint_text="Type in name or email to search for person and email address",
*args, **kwargs):
super(AutocompletedEmailsField, self).__init__(model=model, hint_text=hint_text, *args, **kwargs)
class AutocompletedEmailField(AutocompletedEmailsField):
"""Version of AutocompletedEmailsField specialized to a single object."""
def __init__(self, *args, **kwargs):
kwargs["max_entries"] = 1
super(AutocompletedEmailField, self).__init__(*args, **kwargs)
def clean(self, value):
return super(AutocompletedEmailField, self).clean(value).first()

View file

@ -1,44 +0,0 @@
import json
from django.utils.html import escape
from django.utils.functional import lazy
from django import forms
from django.core.urlresolvers import reverse as urlreverse
import debug # pyflakes:ignore
from ietf.person.models import Email
def json_emails(emails):
if isinstance(emails, basestring):
emails = Email.objects.filter(address__in=[x.strip() for x in emails.split(",") if x.strip()]).select_related("person")
return json.dumps([{"id": e.address + "", "name": escape(u"%s <%s>" % (e.person.name, e.address))} for e in emails])
class EmailsField(forms.CharField):
"""Multi-select field using jquery.tokeninput.js. Since the API of
tokeninput" is asymmetric, we have to pass it a JSON
representation on the way out and parse the ids coming back as a
comma-separated list on the way in."""
def __init__(self, *args, **kwargs):
kwargs["max_length"] = 1000
if not "help_text" in kwargs:
kwargs["help_text"] = "Type in name to search for person"
super(EmailsField, self).__init__(*args, **kwargs)
self.widget.attrs["class"] = "tokenized-field"
self.widget.attrs["data-ajax-url"] = lazy(urlreverse, str)("ajax_search_emails") # make this lazy to prevent initialization problem
def parse_tokenized_value(self, value):
return Email.objects.filter(address__in=[x.strip() for x in value.split(",") if x.strip()]).select_related("person")
def prepare_value(self, value):
if not value:
return ""
if isinstance(value, str) or isinstance(value, unicode):
value = self.parse_tokenized_value(value)
return json_emails(value)
def clean(self, value):
value = super(EmailsField, self).clean(value)
return self.parse_tokenized_value(value)

View file

@ -56,15 +56,15 @@ class PersonInfo(models.Model):
return e[0]
return None
def email_address(self):
e = self.email_set.filter(active=True).order_by("-time")
e = self.email_set.filter(active=True).order_by("-time").first()
if e:
return e[0].address
return e.address
else:
return ""
def formatted_email(self):
e = self.email_set.order_by("-active", "-time")
e = self.email_set.order_by("-active", "-time").first()
if e:
return e[0].formatted_email()
return e.formatted_email()
else:
return ""
def full_name_as_key(self):

View file

@ -11,7 +11,7 @@ class PersonTests(TestCase):
draft = make_test_data()
person = draft.ad
r = self.client.get(urlreverse("ietf.person.views.ajax_search_emails"), dict(q=person.name))
r = self.client.get(urlreverse("ietf.person.views.ajax_tokeninput_search", kwargs={ "model_name": "email"}), dict(q=person.name))
self.assertEqual(r.status_code, 200)
data = json.loads(r.content)
self.assertEqual(data[0]["id"], person.email_address())

View file

@ -2,6 +2,6 @@ from django.conf.urls import patterns
from ietf.person import ajax
urlpatterns = patterns('',
(r'^search/$', "ietf.person.views.ajax_search_emails", None, 'ajax_search_emails'),
(r'^search/(?P<model_name>(person|email))/$', "ietf.person.views.ajax_tokeninput_search", None, 'ajax_tokeninput_search'),
(r'^(?P<personid>[a-z0-9]+).json$', ajax.person_json),
)

View file

@ -1,8 +1,44 @@
from django.http import HttpResponse
from django.db.models import Q
from ietf.person.models import Email
from ietf.person.forms import json_emails
from ietf.person.models import Email, Person
from ietf.person.fields import tokeninput_id_name_json
def ajax_search_emails(request):
emails = Email.objects.filter(person__alias__name__icontains=request.GET.get('q','')).filter(active='true').order_by('person__name').distinct()[:10]
return HttpResponse(json_emails(emails), content_type='application/json')
def ajax_tokeninput_search(request, model_name):
if model_name == "email":
model = Email
else:
model = Person
q = [w.strip() for w in request.GET.get('q', '').split() if w.strip()]
if not q:
objs = model.objects.none()
else:
query = Q()
for t in q:
if model == Email:
query &= Q(person__alias__name__icontains=t) | Q(address__icontains=t)
elif model == Person:
if "@" in t: # allow searching email address if there's a @ in the search term
query &= Q(alias__name__icontains=t) | Q(email__address__icontains=t)
else:
query &= Q(alias__name__icontains=t)
objs = model.objects.filter(query)
# require an account at the Datatracker
only_users = request.GET.get("user") == "1"
if model == Email:
objs = objs.filter(active=True).order_by('person__name').exclude(person=None)
if only_users:
objs = objs.exclude(person__user=None)
elif model == Person:
objs = objs.order_by("name")
if only_users:
objs = objs.exclude(user=None)
objs = objs.distinct()[:10]
return HttpResponse(tokeninput_id_name_json(objs), content_type='application/json')

View file

@ -1,10 +1,10 @@
from django import forms
from django.core.validators import validate_email
from ietf.group.models import Group, Role
from ietf.ietfauth.utils import has_role
from ietf.message.models import Message
from ietf.secr.utils.group import current_nomcom
from ietf.utils.fields import MultiEmailField
# ---------------------------------------------
# Globals
@ -42,33 +42,6 @@ TO_LIST = ('IETF Announcement List <ietf-announce@ietf.org>',
'Working Group Chairs <wgchairs@ietf.org>',
'BoF Chairs <bofchairs@ietf.org>',
'Other...')
# ---------------------------------------------
# Custom Fields
# ---------------------------------------------
class MultiEmailField(forms.Field):
def to_python(self, value):
"Normalize data to a list of strings."
# Return an empty list if no input was given.
if not value:
return []
import types
if isinstance(value, types.StringTypes):
values = value.split(',')
return [ x.strip() for x in values ]
else:
return value
def validate(self, value):
"Check if value consists only of valid emails."
# Use the parent's handling of required fields, etc.
super(MultiEmailField, self).validate(value)
for email in value:
validate_email(email)
# ---------------------------------------------
# Helper Functions

View file

@ -154,7 +154,7 @@ def get_fullcc_list(draft):
# add sheperd
if draft.shepherd:
emails[draft.shepherd.email_address()] = '"%s"' % (draft.shepherd.name)
emails[draft.shepherd.address] = '"%s"' % (draft.shepherd.person.name)
# use sort so we get consistently ordered lists
result_list = []

View file

@ -8,6 +8,7 @@ from ietf.doc.models import Document, DocAlias, State
from ietf.name.models import IntendedStdLevelName, DocRelationshipName
from ietf.group.models import Group
from ietf.person.models import Person, Email
from ietf.person.fields import AutocompletedEmailField
from ietf.secr.groups.forms import get_person
@ -131,7 +132,7 @@ class EditModelForm(forms.ModelForm):
iesg_state = forms.ModelChoiceField(queryset=State.objects.filter(type='draft-iesg'),required=False)
group = GroupModelChoiceField(required=True)
review_by_rfc_editor = forms.BooleanField(required=False)
shepherd = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'class':'name-autocomplete'}),help_text="To see a list of people type the first name, or last name, or both.",required=False)
shepherd = AutocompletedEmailField(required=False, only_users=True)
class Meta:
model = Document
@ -148,8 +149,6 @@ class EditModelForm(forms.ModelForm):
self.initial['state'] = self.instance.get_state().pk
if self.instance.get_state('draft-iesg'):
self.initial['iesg_state'] = self.instance.get_state('draft-iesg').pk
if self.instance.shepherd:
self.initial['shepherd'] = "%s - (%s)" % (self.instance.shepherd.name, self.instance.shepherd.id)
# setup special fields
if self.instance:
@ -191,16 +190,6 @@ class EditModelForm(forms.ModelForm):
raise forms.ValidationError("ERROR: Draft does not exist")
return name
# check for id within parenthesis to ensure name was selected from the list
def clean_shepherd(self):
person = self.cleaned_data.get('shepherd', '')
m = re.search(r'(\d+)', person)
if person and not m:
raise forms.ValidationError("You must select an entry from the list!")
# return person object
return get_person(person)
def clean(self):
super(EditModelForm, self).clean()
cleaned_data = self.cleaned_data

View file

@ -7,7 +7,7 @@ from django.conf import settings
from django.contrib import messages
from django.db.models import Max
from django.forms.formsets import formset_factory
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.shortcuts import render_to_response, get_object_or_404, redirect, render
from django.template import RequestContext
from django.template.loader import render_to_string
@ -799,10 +799,9 @@ def edit(request, id):
else:
form = EditModelForm(instance=draft)
return render_to_response('drafts/edit.html', {
return render(request, 'drafts/edit.html', {
'form': form,
'draft': draft},
RequestContext(request, {}),
)
def email(request, id):

View file

@ -2,7 +2,7 @@ from django import forms
from ietf.group.models import Group
from ietf.meeting.models import ResourceAssociation
from ietf.person.forms import EmailsField
from ietf.person.fields import AutocompletedPersonsField
# -------------------------------------------------
@ -67,7 +67,7 @@ class SessionForm(forms.Form):
wg_selector3 = forms.ChoiceField(choices=WG_CHOICES,required=False)
third_session = forms.BooleanField(required=False)
resources = forms.MultipleChoiceField(choices=[(x.pk,x.desc) for x in ResourceAssociation.objects.all()], widget=forms.CheckboxSelectMultiple,required=False)
bethere = EmailsField(label="Must be present", required=False)
bethere = AutocompletedPersonsField(label="Must be present", required=False)
def __init__(self, *args, **kwargs):
super(SessionForm, self).__init__(*args, **kwargs)

View file

@ -16,7 +16,7 @@ from ietf.secr.utils.decorators import check_permissions, sec_only
from ietf.secr.utils.group import groups_by_session
from ietf.secr.utils.mail import get_ad_email_list, get_chair_email_list, get_cc_list
from ietf.utils.mail import send_mail
from ietf.person.models import Email
from ietf.person.models import Person
# -------------------------------------------------
# Globals
@ -48,15 +48,8 @@ def get_initial_session(sessions):
group = sessions[0].group
conflicts = group.constraint_source_set.filter(meeting=meeting)
bethere_people = [x.person for x in sessions[0].constraints().filter(name='bethere')]
bethere_email = []
for person in bethere_people:
e = person.email_set.order_by("-active","-time").first()
if e:
bethere_email.append(e)
# even if there are three sessions requested, the old form has 2 in this field
initial['num_session'] = sessions.count() if sessions.count() <= 2 else 2
initial['num_session'] = min(sessions.count(), 2)
# accessing these foreign key fields throw errors if they are unset so we
# need to catch these
@ -72,7 +65,7 @@ def get_initial_session(sessions):
initial['conflict3'] = ' '.join([ c.target.acronym for c in conflicts.filter(name__slug='conflic3') ])
initial['comments'] = sessions[0].comments
initial['resources'] = sessions[0].resources.all()
initial['bethere'] = bethere_email
initial['bethere'] = [x.person for x in sessions[0].constraints().filter(name='bethere').select_related("person")]
return initial
def get_lock_message(meeting=None):
@ -234,6 +227,7 @@ def confirm(request, acronym):
This view displays details of the new session that has been requested for the user
to confirm for submission.
'''
# FIXME: this should be using form.is_valid/form.cleaned_data - invalid input will make it crash
querydict = request.session.get('session_form',None)
if not querydict:
raise Http404
@ -241,7 +235,7 @@ def confirm(request, acronym):
if 'resources' in form:
form['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in form['resources'].split(',')]
if 'bethere' in form:
form['bethere'] = [Email.objects.get(address=addr) for addr in form['bethere'].split(',')]
form['bethere'] = Person.objects.filter(pk__in=form['bethere'].split(','))
meeting = get_meeting()
group = get_object_or_404(Group,acronym=acronym)
login = request.user.person
@ -285,8 +279,8 @@ def confirm(request, acronym):
if 'bethere' in form:
bethere_cn = ConstraintName.objects.get(slug='bethere')
for email in form['bethere']:
Constraint.objects.create(name=bethere_cn,source=group,person=email.person,meeting=new_session.meeting)
for p in form['bethere']:
Constraint.objects.create(name=bethere_cn, source=group, person=p, meeting=new_session.meeting)
# deprecated in new schema
# log activity
@ -318,14 +312,8 @@ def add_essential_people(group,initial):
people = set()
if 'bethere' in initial:
people.update(initial['bethere'])
for role in group.role_set.filter(name='chair'):
e = role.person.email_set.order_by("-active","-time").first()
if e:
people.add(e)
if group.ad:
e = group.ad.email_set.order_by("-active","-time").first()
if e:
people.add(e)
people.update(Person.objects.filter(role__group=group, role__name='chair'))
people.add(group.ad)
initial['bethere'] = list(people)
@ -447,8 +435,8 @@ def edit_mtg(request, num, acronym):
if 'bethere' in form.changed_data and set(form.cleaned_data['bethere'])!=set(initial['bethere']):
session.constraints().filter(name='bethere').delete()
bethere_cn = ConstraintName.objects.get(slug='bethere')
for email in form.cleaned_data['bethere']:
Constraint.objects.create(name=bethere_cn,source=group,person=email.person,meeting=session.meeting)
for p in form.cleaned_data['bethere']:
Constraint.objects.create(name=bethere_cn, source=group, person=p, meeting=session.meeting)
# deprecated
# log activity

View file

@ -21,7 +21,7 @@
To: {{ to }}
From: {{ message.frm }}
Cc: {{ message.cc }}
Bcc: {{ message.bccc }}
Bcc: {{ message.bcc }}
Reply To: {{ message.reply_to }}
Subject: {{ message.subject }}

View file

@ -7,6 +7,9 @@
<link rel="stylesheet" type="text/css" href="{{ SECR_STATIC_URL }}css/jquery.ui.autocomplete.css" />
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery-ui-1.8.1.custom.min.js"></script>
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
<script type="text/javascript" src="/js/tokenized-field.js"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}

View file

@ -23,7 +23,7 @@
<tr><td>Area:</td><td>{% if draft.group.parent %}<a href="{% url "areas_view" name=draft.group.parent.acronym %}">{{ draft.group.parent }}{% endif %}</td></tr>
<tr><td>Group:</td><td>{% if draft.group %}<a href="{% url "groups_view" acronym=draft.group.acronym %}">{{ draft.group.acronym }}{% endif %}</td></tr>
<tr><td>Area Director:</td><td>{{ draft.ad }}</td></tr>
<tr><td>Shepherd:</td><td>{{ draft.shepherd }}</td></tr>
<tr><td>Shepherd:</td><td>{% if draft.shepherd %}{{ draft.shepherd.person }} &lt;{{ draft.shepherd.address }}&gt;{% else %}None{% endif %}</td></tr>
<tr><td>Notify:</td><td>{{ draft.notify }}</td></tr>
<tr><td>Document State:</td><td>{{ draft.get_state }}</td></tr>
<tr><td>IESG State:</td><td>{{ draft.iesg_state }}</td></tr>

View file

@ -32,7 +32,7 @@
</tr>
<tr class="row1">
<td>People who must be present:</td>
<td>{% if session.bethere %}<ul>{% for email in session.bethere %}<li>{{ email.person }}</li>{% endfor %}</ul>{% else %}<i>None</i>{% endif %}</td>
<td>{% if session.bethere %}<ul>{% for person in session.bethere %}<li>{{ person }}</li>{% endfor %}</ul>{% else %}<i>None</i>{% endif %}</td>
{% autoescape off %}
<tr class="row2"><td>Special Requests:</td><td>{{ session.comments }}</td></tr>
{% endautoescape %}

View file

@ -33,8 +33,8 @@
<type>{{ entry.type }}</type>
{% if entry.doc.stream.slug %}<stream>{{ entry.doc.stream.slug }}</stream>{% endif %}
<group>{{ entry.doc.group.acronym }}</group>
{% if entry.doc.shepherd.name %}<shepherd>{{entry.doc.shepherd.name }}</shepherd>{% endif %}
{% if entry.doc.ad.name %}<ad>{{entry.doc.ad.name}}</ad>{% endif %}
{% if entry.doc.shepherd %}<shepherd>{{ entry.doc.shepherd.person.name }}</shepherd>{% endif %}
{% if entry.doc.ad %}<ad>{{entry.doc.ad.name}}</ad>{% endif %}
{% for state in entry.doc.states.all %}
<state type="{{ state.type }}">{{ state.slug }}</state>
{% endfor %}

View file

@ -18,7 +18,10 @@ Change the document shepherd for {{ doc.name }}-{{ doc.rev }}
{% block content %}
<h1>Change the document shepherd for {{ doc.name }}-{{ doc.rev }}</h1>
<form class="edit-info" action="" enctype="multipart/form-data" method="post">{% csrf_token %}
<p>The shepherd needs to have a Datatracker account. A new account can be
<a href="{% url "create_account" %}">created here</a>.</p>
<form class="edit-info" action="" method="post">{% csrf_token %}
<table>
{% for field in form.visible_fields %}
<tr>
@ -33,8 +36,8 @@ Change the document shepherd for {{ doc.name }}-{{ doc.rev }}
<tr>
<td></td>
<td class="actions">
<a href="{% url "doc_view" name=doc.name %}">Back</a>
<input type="submit" value="Submit"/>
<a class="button" href="{% url "doc_view" name=doc.name %}">Cancel</a>
<input class="button" type="submit" value="Save"/>
</td>
</tr>
</table>

View file

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block title %}
Change the document shepherd email for {{ doc.name }}-{{ doc.rev }}
{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
{% endblock %}
{% block content %}
<h1>Change the document shepherd email for {{ doc.name }}-{{ doc.rev }}</h1>
<form class="edit-info" method="post">{% csrf_token %}
<table>
{{ form.as_table }}
<tr>
<td></td>
<td class="actions">
<a class="button" href="{% url "doc_view" name=doc.name %}">Cancel</a>
<input class="button" type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -131,8 +131,8 @@
<tr>
<td>Document shepherd:</td>
<td>
<a {% if can_edit_stream_info %}class="editlink" href="{% url "doc_edit_shepherd" name=doc.name %}"{% endif %}>
{{ doc.shepherd|default:"No shepherd assigned" }}
<a {% if can_edit_stream_info or is_shepherd %}class="editlink" href="{% if can_edit_stream_info %}{% url "doc_edit_shepherd" name=doc.name %}{% elif is_shepherd %}{% url "doc_change_shepherd_email" name=doc.name %}{% endif %}"{% endif %}>
{% if doc.shepherd %}{{ doc.shepherd.person }}{% else %}No shepherd assigned{% endif %}
</a>
</td>
</tr>

View file

@ -78,7 +78,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</td>
{% if ad_name == None or ad_name != doc.ad.plain_name %}
<td class="ad">{{ doc.ad|default:"" }}<div class="search-text-shepherd">{{doc.shepherd|default:""}}</div></td>
<td class="ad">{{ doc.ad|default:"" }}<div class="search-text-shepherd">{% if doc.shepherd %}{{ doc.shepherd.person }}{% endif %}</div></td>
{% endif %}
</tr>

View file

@ -29,7 +29,7 @@ Please verify the following information:
<tr>
<td>Document Shepherd:</td>
<td>{% if warn.shepherd %}<img src="/images/warning.png"/>{% endif %}</td>
<td>{{doc.shepherd}}</td>
<td>{% if doc.shepherd %}{{ doc.shepherd.person }}{% endif %}</td>
</tr>
<tr>

View file

@ -1,5 +1,14 @@
{% extends "nomcom/nomcom_private_base.html" %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
{% endblock %}
{% block content_end %}
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
<script type="text/javascript" src="/js/tokenized-field.js"></script>
{% endblock %}
{% block nomcom_content %}
<h3>{% if position %}Edit{% else %}Add{% endif %} position</h3>

View file

@ -11,7 +11,7 @@
<dt>Description:</dt>
<dd>{{ position.description }}</dd>
<dt>Incumbent:</dt>
<dd>{{ position.incumbent }}</dd>
<dd>{% if position.incumbent %}{{ position.incumbent.person }} &lt;{{ position.incumbent.address }}&gt;{% else %}None{% endif %}</dd>
<dt>Is open:</dt>
<dd>{{ position.is_open }}</dd>
<dt>Templates:</dt>

View file

@ -1,23 +1,25 @@
from django import forms
from django.core.validators import validate_email, ValidationError
from django.core.validators import validate_email
class MultiEmailField(forms.Field):
def to_python(self, value):
"Normalize data to a list of strings."
class MultiEmailField(forms.CharField):
widget = forms.widgets.Textarea
def clean(self, value):
super(MultiEmailField, self).clean(value)
# Return an empty list if no input was given.
if not value:
return []
if isinstance(value, basestring):
values = value.split(',')
return [ x.strip() for x in values if x.strip() ]
else:
return value
if value.endswith(','):
value = value[:-1]
emails = [v.strip() for v in value.split(',') if v.strip()]
def validate(self, value):
"Check if value consists only of valid emails."
for email in emails:
try:
validate_email(email)
except ValidationError:
raise ValidationError("This is not a valid comma separated email list.")
# Use the parent's handling of required fields, etc.
super(MultiEmailField, self).validate(value)
return value
for email in value:
validate_email(email)

View file

@ -172,7 +172,7 @@ def make_test_data():
u.set_password("plain+password")
u.save()
plainman = Person.objects.create(name="Plain Man", ascii="Plain Man", user=u)
email = Email.objects.create(address="plain@example.com", person=plainman) # pyflakes:ignore
email = Email.objects.create(address="plain@example.com", person=plainman)
# group personnel
create_person(mars_wg, "chair", name="WG Chair Man", username="marschairman")
@ -194,7 +194,7 @@ def make_test_data():
rev="01",
pages=2,
intended_std_level_id="ps",
shepherd=plainman,
shepherd=email,
ad=ad,
expires=datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
notify="aliens@example.mars",

View file

@ -310,6 +310,10 @@ div.prompt { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em
margin-left: 150px;
}
.baseform .fieldWidget ul.token-input-list {
clear: none;
}
#feedbackformset .fieldWidget {
margin-left: 0px;
}

View file

@ -1,6 +1,6 @@
/*
* jQuery Plugin: Tokenizing Autocomplete Text Entry
* Version 1.5.0
* Version 1.6.0
*
* Copyright (c) 2009 James Smith (http://loopj.com)
* Licensed jointly under the GPL and MIT licenses,
@ -11,26 +11,46 @@
(function ($) {
// Default settings
var DEFAULT_SETTINGS = {
// Search settings
method: "GET",
contentType: "json",
queryParam: "q",
searchDelay: 300,
minChars: 1,
propertyToSearch: "name",
jsonContainer: null,
// Display settings
hintText: "Type in a search term",
noResultsText: "No results",
searchingText: "Searching...",
deleteText: "&times;",
searchDelay: 300,
minChars: 1,
animateDropdown: true,
// Tokenization settings
tokenLimit: null,
jsonContainer: null,
method: "GET",
contentType: "json",
queryParam: "q",
tokenDelimiter: ",",
preventDuplicates: false,
// Output settings
tokenValue: "id",
// Prepopulation settings
prePopulate: null,
processPrePopulate: false,
animateDropdown: true,
// Manipulation settings
idPrefix: "token-input-",
// Formatters
resultsFormatter: function(item){ return "<li>" + item[this.propertyToSearch]+ "</li>" },
tokenFormatter: function(item) { return "<li><p>" + item[this.propertyToSearch] + "</p></li>" },
// Callbacks
onResult: null,
onAdd: null,
onDelete: null,
idPrefix: "token-input-"
onReady: null
};
// Default classes to use when theming
@ -93,7 +113,10 @@ var methods = {
remove: function(item) {
this.data("tokenInputObject").remove(item);
return this;
}
},
get: function() {
return this.data("tokenInputObject").getTokens();
}
}
// Expose the .tokenInput function to jQuery as a plugin
@ -113,16 +136,19 @@ $.TokenList = function (input, url_or_data, settings) {
//
// Configure the data source
if(typeof(url_or_data) === "string") {
if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
// Set the url to query against
settings.url = url_or_data;
// If the URL is a function, evaluate it here to do our initalization work
var url = computeURL();
// Make a smart guess about cross-domain if it wasn't explicitly specified
if(settings.crossDomain === undefined) {
if(settings.url.indexOf("://") === -1) {
if(url.indexOf("://") === -1) {
settings.crossDomain = false;
} else {
settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
}
}
} else if(typeof(url_or_data) === "object") {
@ -223,6 +249,7 @@ $.TokenList = function (input, url_or_data, settings) {
if(!$(this).val().length) {
if(selected_token) {
delete_token($(selected_token));
hidden_input.change();
} else if(previous_token.length) {
select_token($(previous_token.get(0)));
}
@ -242,6 +269,7 @@ $.TokenList = function (input, url_or_data, settings) {
case KEY.COMMA:
if(selected_dropdown_item) {
add_token($(selected_dropdown_item).data("tokeninput"));
hidden_input.change();
return false;
}
break;
@ -346,6 +374,10 @@ $.TokenList = function (input, url_or_data, settings) {
});
}
// Initialization is done
if($.isFunction(settings.onReady)) {
settings.onReady.call();
}
//
// Public functions
@ -380,6 +412,10 @@ $.TokenList = function (input, url_or_data, settings) {
}
});
}
this.getTokens = function() {
return saved_tokens;
}
//
// Private functions
@ -390,8 +426,6 @@ $.TokenList = function (input, url_or_data, settings) {
input_box.hide();
hide_dropdown();
return;
} else {
input_box.focus();
}
}
@ -413,7 +447,8 @@ $.TokenList = function (input, url_or_data, settings) {
// Inner function to a token to the list
function insert_token(item) {
var this_token = $("<li><p>"+ item.name +"</p></li>")
var this_token = settings.tokenFormatter(item);
this_token = $(this_token)
.addClass(settings.classes.token)
.insertBefore(input_token);
@ -423,11 +458,13 @@ $.TokenList = function (input, url_or_data, settings) {
.appendTo(this_token)
.click(function () {
delete_token($(this).parent());
hidden_input.change();
return false;
});
// Store data on the token
var token_data = {"id": item.id, "name": item.name};
var token_data = {"id": item.id};
token_data[settings.propertyToSearch] = item[settings.propertyToSearch];
$.data(this_token.get(0), "tokeninput", item);
// Save this token for duplicate checking
@ -435,13 +472,16 @@ $.TokenList = function (input, url_or_data, settings) {
selected_token_index++;
// Update the hidden input
var token_ids = $.map(saved_tokens, function (el) {
return el.id;
});
hidden_input.val(token_ids.join(settings.tokenDelimiter));
update_hidden_input(saved_tokens, hidden_input);
token_count += 1;
// Check the token limit
if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
input_box.hide();
hide_dropdown();
}
return this_token;
}
@ -470,8 +510,10 @@ $.TokenList = function (input, url_or_data, settings) {
}
// Insert the new tokens
insert_token(item);
checkTokenLimit();
if(settings.tokenLimit == null || token_count < settings.tokenLimit) {
insert_token(item);
checkTokenLimit();
}
// Clear input box
input_box.val("");
@ -553,10 +595,7 @@ $.TokenList = function (input, url_or_data, settings) {
if(index < selected_token_index) selected_token_index--;
// Update the hidden input
var token_ids = $.map(saved_tokens, function (el) {
return el.id;
});
hidden_input.val(token_ids.join(settings.tokenDelimiter));
update_hidden_input(saved_tokens, hidden_input);
token_count -= 1;
@ -573,6 +612,15 @@ $.TokenList = function (input, url_or_data, settings) {
}
}
// Update the hidden input box value
function update_hidden_input(saved_tokens, hidden_input) {
var token_values = $.map(saved_tokens, function (el) {
return el[settings.tokenValue];
});
hidden_input.val(token_values.join(settings.tokenDelimiter));
}
// Hide and clear the results dropdown
function hide_dropdown () {
dropdown.hide().empty();
@ -608,6 +656,10 @@ $.TokenList = function (input, url_or_data, settings) {
function highlight_term(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
}
function find_value_and_highlight_term(template, value, term) {
return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
}
// Populate the results dropdown with some results
function populate_dropdown (query, results) {
@ -620,14 +672,18 @@ $.TokenList = function (input, url_or_data, settings) {
})
.mousedown(function (event) {
add_token($(event.target).closest("li").data("tokeninput"));
hidden_input.change();
return false;
})
.hide();
$.each(results, function(index, value) {
var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
.appendTo(dropdown_ul);
var this_li = settings.resultsFormatter(value);
this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query);
this_li = $(this_li).appendTo(dropdown_ul);
if(index % 2) {
this_li.addClass(settings.classes.dropdownItem);
} else {
@ -699,17 +755,19 @@ $.TokenList = function (input, url_or_data, settings) {
// Do the actual search
function run_search(query) {
var cached_results = cache.get(query);
var cache_key = query + computeURL();
var cached_results = cache.get(cache_key);
if(cached_results) {
populate_dropdown(query, cached_results);
} else {
// Are we doing an ajax search or local data search?
if(settings.url) {
var url = computeURL();
// Extract exisiting get params
var ajax_params = {};
ajax_params.data = {};
if(settings.url.indexOf("?") > -1) {
var parts = settings.url.split("?");
if(url.indexOf("?") > -1) {
var parts = url.split("?");
ajax_params.url = parts[0];
var param_array = parts[1].split("&");
@ -718,7 +776,7 @@ $.TokenList = function (input, url_or_data, settings) {
ajax_params.data[kv[0]] = kv[1];
});
} else {
ajax_params.url = settings.url;
ajax_params.url = url;
}
// Prepare the request
@ -734,7 +792,7 @@ $.TokenList = function (input, url_or_data, settings) {
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, results);
}
cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results);
// only populate the dropdown if the results are associated with the active search query
if(input_box.val().toLowerCase() === query) {
@ -747,17 +805,26 @@ $.TokenList = function (input, url_or_data, settings) {
} else if(settings.local_data) {
// Do the search through local data
var results = $.grep(settings.local_data, function (row) {
return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
});
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, results);
}
cache.add(query, results);
cache.add(cache_key, results);
populate_dropdown(query, results);
}
}
}
// compute the dynamic URL
function computeURL() {
var url = settings.url;
if(typeof settings.url == 'function') {
url = settings.url.call();
}
return url;
}
};
// Really basic cache for the results

View file

@ -3,15 +3,15 @@ function setupTokenizedField(field) {
return; // don't tokenize hidden template snippets
var pre = [];
if (field.val())
pre = JSON.parse((field.val() || "").replace(/&quot;/g, '"'));
else if (field.data("pre"))
if (field.attr("data-pre"))
pre = JSON.parse((field.attr("data-pre") || "").replace(/&quot;/g, '"'));
field.tokenInput(field.attr("data-ajax-url"), {
hintText: "",
preventDuplicates: true,
prePopulate: pre
prePopulate: pre,
tokenLimit: field.attr("data-max-entries"),
noResultsText: "No results - cannot use this entry",
hintText: field.attr("data-hint-text")
});
}

View file

@ -211,8 +211,8 @@ $(document).ready(function() {
minLength: 3,
select: function(event, ui) {
//match number inside paren and then strip paren
id = ui.item.label.match(/\(\d+\)/);
val = id[0].replace(/[\(\)]/g, "");
var id = ui.item.label.match(/\(\d+\)/);
var val = id[0].replace(/[\(\)]/g, "");
//alert(id,val);
//alert(id.match(/\d+/));
$.getJSON('/secr/areas/getemails/',{"id":val},function(data) {