Merged [4600] from rjsparks@nostrum.com:
* Adds a new document type for conflict reviews, with a ballot for the IESG 5742 response to a review request
* Integrated the new document type into the iESG agenda views (including RSS feeds)* Removed the Edit and Add buttons from the document main view.
* Replaced Add with actions appropriate for the document type, such as "Begin IESG Processing" or "Begin IETF Conflict Review", and made most data directly editable on the document's main page, depending on access permissions.
* Removed a manual editing step that the secretariat had to perform when sending conflict review messages. The view now composes the message correctly given the stream.
* Added a pencil icon motif to differentiate fields that are editable.
* Generalized several views and helper functions to use Document instead of (e.g.) IdWrapper
* Generalized reading documents from the repository
* Added a way to get from IdWrapper to the underlying Document to facilitate migrating way from the Wrapper classes* Added many helpers to Document to assist with migrating off IdWrapper
* Minor fixes and other changes
* Fixes to document main view to avoid (silent) template failures.
* Began removing some of the code that is no longer reachable post-migration * Corrected the behavior of the undefer code and added test cases for it
* Improved initial population of notification lists and added the ability to regenerate the initial list
* Made the test code that scans for template coverage more robust
* Deployment notes:
* new setting: CONFLICT_REVIEW_PATH. The associated directory will need to be created
This branch fixes bugs #805, #744 and #812
- Legacy-Id: 4652
Note: SVN reference [4600] has been migrated to Git commit 798e7a50e7
This commit is contained in:
commit
798f7697af
|
@ -26,6 +26,11 @@ class DocAuthorInline(admin.TabularInline):
|
|||
raw_id_fields = ['author', ]
|
||||
extra = 1
|
||||
|
||||
class RelatedDocumentInline(admin.TabularInline):
|
||||
model = RelatedDocument
|
||||
raw_id_fields = ['target']
|
||||
extra = 1
|
||||
|
||||
# document form for managing states in a less confusing way
|
||||
|
||||
class StatesWidget(forms.SelectMultiple):
|
||||
|
@ -89,7 +94,7 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||
search_fields = ['name']
|
||||
list_filter = ['type']
|
||||
raw_id_fields = ['authors', 'related', 'group', 'shepherd', 'ad']
|
||||
inlines = [DocAliasInline, DocAuthorInline, ]
|
||||
inlines = [DocAliasInline, DocAuthorInline, RelatedDocumentInline, ]
|
||||
form = DocumentForm
|
||||
|
||||
def state(self, instance):
|
||||
|
@ -114,6 +119,13 @@ class DocAliasAdmin(admin.ModelAdmin):
|
|||
raw_id_fields = ['document']
|
||||
admin.site.register(DocAlias, DocAliasAdmin)
|
||||
|
||||
class RelatedDocumentAdmin(admin.ModelAdmin):
|
||||
list_display = ['source', 'target', 'relationship', ]
|
||||
list_filter = ['relationship', ]
|
||||
search_fields = ['source__name', 'target__name', 'target__document__name', ]
|
||||
raw_id_fields = ['source', 'target', ]
|
||||
admin.site.register(RelatedDocument, RelatedDocumentAdmin)
|
||||
|
||||
class BallotTypeAdmin(admin.ModelAdmin):
|
||||
list_display = ["slug", "doc_type", "name", "question"]
|
||||
admin.site.register(BallotType, BallotTypeAdmin)
|
||||
|
@ -140,9 +152,3 @@ class BallotPositionDocEventAdmin(DocEventAdmin):
|
|||
|
||||
admin.site.register(BallotPositionDocEvent, BallotPositionDocEventAdmin)
|
||||
|
||||
class RelatedDocumentAdmin(admin.ModelAdmin):
|
||||
list_display = ['source', 'target', 'relationship', ]
|
||||
list_filter = ['relationship', ]
|
||||
search_fields = ['source__name', 'target__name', 'target__document__name', ]
|
||||
raw_id_fields = ['source', 'target', ]
|
||||
admin.site.register(RelatedDocument, RelatedDocumentAdmin)
|
||||
|
|
436
ietf/doc/migrations/0003_add_conflict.py
Normal file
436
ietf/doc/migrations/0003_add_conflict.py
Normal file
|
@ -0,0 +1,436 @@
|
|||
# encoding: utf-8
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
|
||||
from doc.models import StateType, State, BallotType, DocTypeName
|
||||
from name.models import BallotPositionName
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
#These slugs need to be more than 8 chars
|
||||
def forwards(self, orm):
|
||||
|
||||
conflrev = StateType(slug="conflrev",label="Conflict Review State")
|
||||
conflrev.save()
|
||||
|
||||
needshep = State(
|
||||
type=conflrev,slug="needshep",
|
||||
name="Needs Shepherd",used=True,order=1,
|
||||
desc="A conflict review has been requested, but a shepherding AD has not yet been assigned")
|
||||
needshep.save()
|
||||
|
||||
adrev = State(
|
||||
type=conflrev,slug="adrev",
|
||||
name="AD Review",used=True,order=2,
|
||||
desc="The sponsoring AD is reviewing the document and preparing a proposed response")
|
||||
adrev.save()
|
||||
|
||||
iesgeval = State(
|
||||
type=conflrev,slug="iesgeval",
|
||||
name="IESG Evaluation",used=True,order=3,
|
||||
desc="The IESG is considering the proposed conflict review response")
|
||||
iesgeval.save()
|
||||
|
||||
defer = State(
|
||||
type=conflrev,slug="defer",
|
||||
name="IESG Evaluation - Defer",used=True, order=4,
|
||||
desc="The evaluation of the proposed conflict review response has been deferred to the next telechat")
|
||||
defer.save()
|
||||
|
||||
appr_reqnopub_pend = State(
|
||||
type=conflrev,slug="appr-reqnopub-pend",
|
||||
name="Approved Request to Not Publish - announcement to be sent", used=True,order=5,
|
||||
desc="The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response")
|
||||
appr_reqnopub_pend.save()
|
||||
|
||||
appr_noprob_pend = State(
|
||||
type=conflrev,slug="appr-noprob-pend",
|
||||
name="Approved No Problem - announcement to be sent", used=True,order=6,
|
||||
desc="The IESG has approved the conflict review response, but the secretariat has not yet sent the response")
|
||||
appr_noprob_pend.save()
|
||||
|
||||
appr_reqnopub_sent = State(
|
||||
type=conflrev,slug="appr-reqnopub-sent",
|
||||
name="Approved Request to Not Publish - announcement sent",used=True,order=7,
|
||||
desc="The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester")
|
||||
appr_reqnopub_sent.save()
|
||||
|
||||
appr_noprob_sent = State(
|
||||
type=conflrev,slug="appr-noprob-sent",
|
||||
name="Approved No Problem - announcement sent",used=True,order=8,
|
||||
desc="The secretariat has delivered the IESG's approved conflict review response to the requester")
|
||||
appr_noprob_sent.save()
|
||||
|
||||
withdraw = State(
|
||||
type=conflrev,slug="withdraw",
|
||||
name="Withdrawn",used=True,order=9,
|
||||
desc="The request for conflict review was withdrawn")
|
||||
withdraw.save()
|
||||
|
||||
dead = State(
|
||||
type=conflrev,slug="dead",
|
||||
name="Dead",used=True,order=10,
|
||||
desc="The conflict review has been abandoned")
|
||||
dead.save()
|
||||
|
||||
needshep.next_states.add(adrev,withdraw,dead)
|
||||
needshep.save()
|
||||
adrev.next_states.add(iesgeval,withdraw,dead)
|
||||
adrev.save()
|
||||
iesgeval.next_states.add(appr_reqnopub_pend,appr_noprob_pend,defer,withdraw,dead)
|
||||
iesgeval.save()
|
||||
defer.next_states.add(iesgeval,appr_reqnopub_pend,appr_noprob_pend,withdraw,dead)
|
||||
defer.save()
|
||||
appr_reqnopub_pend.next_states.add(appr_reqnopub_sent,withdraw)
|
||||
appr_reqnopub_pend.save()
|
||||
appr_noprob_pend.next_states.add(appr_noprob_sent,withdraw)
|
||||
appr_noprob_pend.save()
|
||||
withdraw.next_states.add(needshep)
|
||||
withdraw.save()
|
||||
dead.next_states.add(needshep)
|
||||
dead.save()
|
||||
|
||||
conflict_ballot = BallotType(doc_type=DocTypeName.objects.get(slug='conflrev'),slug='conflrev',name="Approve",used=True,
|
||||
question="Is this the correct conflict review response?")
|
||||
conflict_ballot.save()
|
||||
conflict_ballot.positions.add('yes','noobj','discuss','abstain','recuse','norecord')
|
||||
conflict_ballot.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
State.objects.filter(type__slug='conflrev').delete()
|
||||
StateType.objects.filter(slug='conflrev').delete()
|
||||
BallotType.objects.filter(slug='conflrev').delete()
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'doc.ballotdocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}),
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'doc.ballotpositiondocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "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'}),
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
|
||||
},
|
||||
'doc.ballottype': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
|
||||
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
|
||||
'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': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'doc.docalias': {
|
||||
'Meta': {'object_name': 'DocAlias'},
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
|
||||
},
|
||||
'doc.docevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
|
||||
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {}),
|
||||
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'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': "orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
|
||||
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "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': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'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': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "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': "orm['person.Person']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.dochistoryauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {})
|
||||
},
|
||||
'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': "orm['doc.DocEvent']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
|
||||
},
|
||||
'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'related': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'reversely_related_document_set'", 'blank': 'True', 'through': "orm['doc.RelatedDocument']", 'to': "orm['doc.DocAlias']"}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
'doc.initialreviewdocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.lastcalldocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.newrevisiondocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
|
||||
},
|
||||
'doc.relateddochistory': {
|
||||
'Meta': {'object_name': 'RelatedDocHistory'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
|
||||
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
|
||||
},
|
||||
'doc.relateddocument': {
|
||||
'Meta': {'object_name': 'RelatedDocument'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
|
||||
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
|
||||
},
|
||||
'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'previous_states'", 'symmetrical': 'False', 'to': "orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
'doc.telechatdocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "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'})
|
||||
},
|
||||
'doc.writeupdocevent': {
|
||||
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
|
||||
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'name.ballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
|
||||
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.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'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docremindertypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['doc']
|
|
@ -205,6 +205,113 @@ class Document(DocumentInfo):
|
|||
name = name.upper()
|
||||
return name
|
||||
|
||||
#TODO can/should this be a function instead of a property? Currently a view uses it as a property
|
||||
@property
|
||||
def telechat_date(self):
|
||||
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
return e.telechat_date if e else None
|
||||
|
||||
def area_acronym(self):
|
||||
g = self.group
|
||||
if g:
|
||||
if g.type_id == "area":
|
||||
return g.acronym
|
||||
elif g.type_id != "individ":
|
||||
return g.parent.acronym
|
||||
else:
|
||||
return None
|
||||
|
||||
def group_acronym(self):
|
||||
g = self.group
|
||||
if g and g.type_id != "area":
|
||||
return g.acronym
|
||||
else:
|
||||
return "none"
|
||||
|
||||
def on_upcoming_agenda(self):
|
||||
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
return bool(e and e.telechat_date and e.telechat_date >= datetime.date.today())
|
||||
|
||||
def returning_item(self):
|
||||
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
return e.returning_item if e else None
|
||||
|
||||
# This is brittle. Resist the temptation to make it more brittle by combining the search against those description
|
||||
# strings to one command. It is coincidence that those states have the same description - one might change.
|
||||
# Also, this needs further review - is it really the case that there would be no other changed_document events
|
||||
# between when the state was changed to defer and when some bit of code wants to know if we are deferred? Why
|
||||
# isn't this just returning whether the state is currently a defer state for that document type?
|
||||
def active_defer_event(self):
|
||||
if self.type_id == "draft" and self.get_state_slug("draft-iesg") == "defer":
|
||||
return self.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>")
|
||||
elif self.type_id == "conflrev" and self.get_state_slug("conflrev") == "defer":
|
||||
return self.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>")
|
||||
return None
|
||||
|
||||
def displayname_with_link(self):
|
||||
return '<a href="%s">%s-%s</a>' % (self.get_absolute_url(), self.name , self.rev)
|
||||
|
||||
def rfc_number(self):
|
||||
qs = self.docalias_set.filter(name__startswith='rfc')
|
||||
return qs[0].name[3:] if qs else None
|
||||
|
||||
def replaced_by(self):
|
||||
return [ rel.source for alias in self.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='replaces') ]
|
||||
|
||||
def friendly_state(self):
|
||||
""" Return a concise text description of the document's current state """
|
||||
if self.type_id=='draft':
|
||||
# started_iesg_process is is how the redesigned database schema (as of May2012) captured what
|
||||
# used to be "has an IDInternal", aka *Wrapper.in_ietf_process()=True
|
||||
in_iesg_process = self.latest_event(type='started_iesg_process')
|
||||
iesg_state_summary=None
|
||||
if in_iesg_process:
|
||||
iesg_state = self.states.get(type='draft-iesg')
|
||||
# This knowledge about which tags are reportable IESG substate tags is duplicated in idrfc
|
||||
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
|
||||
iesg_substate = self.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
|
||||
# There really shouldn't be more than one tag in iesg_substate, but this will do something sort-of-sensible if there is
|
||||
iesg_state_summary = iesg_state.name
|
||||
if iesg_substate:
|
||||
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
|
||||
|
||||
if self.get_state_slug() == "rfc":
|
||||
return "<a href=\"%s\">RFC %d</a>" % (urlreverse('doc_view', args=['rfc%d' % self.rfc_number]), self.rfc_number)
|
||||
elif self.get_state_slug() == "repl":
|
||||
rs = self.replaced_by()
|
||||
if rs:
|
||||
return "Replaced by "+", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', args=[name]),name) for name in rs)
|
||||
else:
|
||||
return "Replaced"
|
||||
elif self.get_state_slug() == "active":
|
||||
if in_iesg_process:
|
||||
if iesg_state.slug == "dead":
|
||||
# Many drafts in the draft-iesg "Dead" state are not dead
|
||||
# in other state machines; they're just not currently under
|
||||
# IESG processing. Show them as "I-D Exists (IESG: Dead)" instead...
|
||||
return "I-D Exists (IESG: "+iesg_state_summary+")"
|
||||
elif iesg_state.slug == "lc":
|
||||
expiration_date = str(self.latest_event(LastCallDocEvent,type="sent_last_call").expires.date())
|
||||
return iesg_state_summary + " (ends "+expiration_date+")"
|
||||
else:
|
||||
return iesg_state_summary
|
||||
else:
|
||||
return "I-D Exists"
|
||||
else:
|
||||
if in_iesg_process and iesg_state.slug == "dead":
|
||||
return self.get_state().name +" (IESG: "+iesg_state_summary+")"
|
||||
# Expired/Withdrawn by Submitter/IETF
|
||||
return self.get_state().name
|
||||
else:
|
||||
return self.get_state().name
|
||||
|
||||
def ipr(self):
|
||||
"""Returns the IPR disclosures against this document (as a queryset over IprDocAlias)."""
|
||||
from ietf.ipr.models import IprDocAlias
|
||||
return IprDocAlias.objects.filter(doc_alias__document=self)
|
||||
|
||||
|
||||
|
||||
class RelatedDocHistory(models.Model):
|
||||
source = models.ForeignKey('DocHistory')
|
||||
target = models.ForeignKey('DocAlias', related_name="reversely_related_document_history_set")
|
||||
|
|
0
ietf/doc/templatetags/__init__.py
Normal file
0
ietf/doc/templatetags/__init__.py
Normal file
8
ietf/doc/templatetags/doc_states.py
Normal file
8
ietf/doc/templatetags/doc_states.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def rfc_editor_state(doc):
|
||||
state = doc.states.filter(type='draft-stream-ise')
|
||||
return state[0] if state else None
|
3
ietf/doc/tests.py
Normal file
3
ietf/doc/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
|
||||
from ietf.doc.tests_conflict_review import *
|
342
ietf/doc/tests_conflict_review.py
Normal file
342
ietf/doc/tests_conflict_review.py
Normal file
|
@ -0,0 +1,342 @@
|
|||
import os
|
||||
import shutil
|
||||
|
||||
from pyquery import PyQuery
|
||||
from StringIO import StringIO
|
||||
from textwrap import wrap
|
||||
|
||||
|
||||
import django.test
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.utils.test_utils import login_testing_unauthorized
|
||||
from ietf.utils.test_data import make_test_data
|
||||
from ietf.utils.mail import outbox
|
||||
from ietf.doc.utils import active_ballot, create_ballot_if_not_open, ballot_open
|
||||
from ietf.doc.views_conflict_review import default_approval_text
|
||||
|
||||
from ietf.doc.models import Document,DocEvent,NewRevisionDocEvent,BallotPositionDocEvent,TelechatDocEvent,DocAlias,State
|
||||
from ietf.name.models import StreamName
|
||||
from ietf.group.models import Person
|
||||
from ietf.iesg.models import TelechatDate
|
||||
|
||||
|
||||
class ConflictReviewTestCase(django.test.TestCase):
|
||||
|
||||
fixtures = ['names']
|
||||
|
||||
def test_start_review(self):
|
||||
|
||||
doc = Document.objects.get(name='draft-imaginary-independent-submission')
|
||||
url = urlreverse('conflict_review_start',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# can't start conflict reviews on documents not in the ise or irtf streams
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 404)
|
||||
|
||||
doc.stream=StreamName.objects.get(slug='ise')
|
||||
doc.save()
|
||||
|
||||
# normal get should succeed and get a reasonable form
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form select[name=create_in_state]')),1)
|
||||
|
||||
# faulty posts
|
||||
r = self.client.post(url,dict(create_in_state=""))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertEquals(Document.objects.filter(name='conflict-review-imaginary-independent-submission').count() , 0)
|
||||
|
||||
r = self.client.post(url,dict(ad=""))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertEquals(Document.objects.filter(name='conflict-review-imaginary-independent-submission').count() , 0)
|
||||
|
||||
# successful review start
|
||||
ad_strpk = str(Person.objects.get(name='Aread Irector').pk)
|
||||
state_strpk = str(State.objects.get(slug='needshep',type__slug='conflrev').pk)
|
||||
r = self.client.post(url,dict(ad=ad_strpk,create_in_state=state_strpk,notify='ipu@ietf.org'))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
review_doc = Document.objects.get(name='conflict-review-imaginary-independent-submission')
|
||||
self.assertEquals(review_doc.get_state('conflrev').slug,'needshep')
|
||||
self.assertEquals(review_doc.rev,u'00')
|
||||
self.assertEquals(review_doc.ad.name,u'Aread Irector')
|
||||
self.assertEquals(review_doc.notify,u'ipu@ietf.org')
|
||||
doc = Document.objects.get(name='draft-imaginary-independent-submission')
|
||||
self.assertTrue(doc in [x.target.document for x in review_doc.relateddocument_set.filter(relationship__slug='conflrev')])
|
||||
|
||||
self.assertTrue(review_doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review requested"))
|
||||
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review initiated"))
|
||||
|
||||
# verify you can't start a review when a review is already in progress
|
||||
r = self.client.post(url,dict(ad="Aread Irector",create_in_state="Needs Shepherd",notify='ipu@ietf.org'))
|
||||
self.assertEquals(r.status_code, 404)
|
||||
|
||||
def test_change_state(self):
|
||||
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_change_state',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form select[name=review_state]')),1)
|
||||
|
||||
# faulty post
|
||||
r = self.client.post(url,dict(review_state=""))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
|
||||
# successful change to AD Review
|
||||
adrev_pk = str(State.objects.get(slug='adrev',type__slug='conflrev').pk)
|
||||
r = self.client.post(url,dict(review_state=adrev_pk,comment='RDNK84ZD'))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
review_doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(review_doc.get_state('conflrev').slug,'adrev')
|
||||
self.assertTrue(review_doc.latest_event(DocEvent,type="added_comment").desc.startswith('RDNK84ZD'))
|
||||
self.assertFalse(active_ballot(review_doc))
|
||||
|
||||
# successful change to IESG Evaluation
|
||||
iesgeval_pk = str(State.objects.get(slug='iesgeval',type__slug='conflrev').pk)
|
||||
r = self.client.post(url,dict(review_state=iesgeval_pk,comment='TGmZtEjt'))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
review_doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(review_doc.get_state('conflrev').slug,'iesgeval')
|
||||
self.assertTrue(review_doc.latest_event(DocEvent,type="added_comment").desc.startswith('TGmZtEjt'))
|
||||
self.assertTrue(active_ballot(review_doc))
|
||||
self.assertEquals(review_doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position").pos_id,'yes')
|
||||
|
||||
|
||||
def test_edit_notices(self):
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_notices',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form input[name=notify]')),1)
|
||||
self.assertEquals(doc.notify,q('form input[name=notify]')[0].value)
|
||||
|
||||
# change notice list
|
||||
newlist = '"Foo Bar" <foo@bar.baz.com>'
|
||||
r = self.client.post(url,dict(notify=newlist))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(doc.notify,newlist)
|
||||
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith('Notification list changed'))
|
||||
|
||||
|
||||
def test_edit_ad(self):
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_ad',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('select[name=ad]')),1)
|
||||
|
||||
# change ads
|
||||
ad2 = Person.objects.get(name='Ad No2')
|
||||
r = self.client.post(url,dict(ad=str(ad2.pk)))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(doc.ad,ad2)
|
||||
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith('Shepherding AD changed'))
|
||||
|
||||
|
||||
def test_edit_telechat_date(self):
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_telechat_date',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('select[name=telechat_date]')),1)
|
||||
|
||||
# set a date
|
||||
self.assertFalse(doc.latest_event(TelechatDocEvent, "scheduled_for_telechat"))
|
||||
telechat_date = TelechatDate.objects.active().order_by('date')[0].date
|
||||
r = self.client.post(url,dict(telechat_date=telechat_date.isoformat()))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(doc.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date,telechat_date)
|
||||
|
||||
# move it forward a telechat (this should set the returning item bit)
|
||||
telechat_date = TelechatDate.objects.active().order_by('date')[1].date
|
||||
r = self.client.post(url,dict(telechat_date=telechat_date.isoformat()))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertTrue(doc.returning_item())
|
||||
|
||||
# clear the returning item bit
|
||||
r = self.client.post(url,dict(telechat_date=telechat_date.isoformat()))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertFalse(doc.returning_item())
|
||||
|
||||
# set the returning item bit without changing the date
|
||||
r = self.client.post(url,dict(telechat_date=telechat_date.isoformat(),returning_item="on"))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertTrue(doc.returning_item())
|
||||
|
||||
# Take the doc back off any telechat
|
||||
r = self.client.post(url,dict(telechat_date=""))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(doc.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date,None)
|
||||
|
||||
def approve_test_helper(self,approve_type):
|
||||
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_approve',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# Some additional setup
|
||||
create_ballot_if_not_open(doc,Person.objects.get(name="Sec Retary"),"conflrev")
|
||||
doc.set_state(State.objects.get(slug=approve_type+'-pend',type='conflrev'))
|
||||
doc.save()
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.approve')),1)
|
||||
if approve_type == 'appr-noprob':
|
||||
self.assertTrue( 'IESG has no problem' in ''.join(wrap(r.content,2**16)))
|
||||
else:
|
||||
self.assertTrue( 'NOT be published' in ''.join(wrap(r.content,2**16)))
|
||||
|
||||
# submit
|
||||
messages_before = len(outbox)
|
||||
r = self.client.post(url,dict(announcement_text=default_approval_text(doc)))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(doc.get_state_slug(),approve_type+'-sent')
|
||||
self.assertFalse(ballot_open(doc, "conflrev"))
|
||||
|
||||
self.assertEquals(len(outbox), messages_before + 1)
|
||||
self.assertTrue('Results of IETF-conflict review' in outbox[-1]['Subject'])
|
||||
if approve_type == 'appr-noprob':
|
||||
self.assertTrue( 'IESG has no problem' in ''.join(wrap(unicode(outbox[-1]),2**16)))
|
||||
else:
|
||||
self.assertTrue( 'NOT be published' in ''.join(wrap(unicode(outbox[-1]),2**16)))
|
||||
|
||||
|
||||
def test_approve_reqnopub(self):
|
||||
self.approve_test_helper('appr-reqnopub')
|
||||
|
||||
def test_approve_noprob(self):
|
||||
self.approve_test_helper('appr-noprob')
|
||||
|
||||
def setUp(self):
|
||||
make_test_data()
|
||||
|
||||
|
||||
class ConflictReviewSubmitTestCase(django.test.TestCase):
|
||||
|
||||
fixtures = ['names']
|
||||
|
||||
def test_initial_submission(self):
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_submit',kwargs=dict(name=doc.name))
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(q('textarea')[0].text.startswith("[Edit this page"))
|
||||
|
||||
# Faulty posts using textbox
|
||||
# Right now, nothing to test - we let people put whatever the web browser will let them put into that textbox
|
||||
|
||||
# sane post using textbox
|
||||
path = os.path.join(settings.CONFLICT_REVIEW_PATH, '%s-%s.txt' % (doc.canonical_name(), doc.rev))
|
||||
self.assertEquals(doc.rev,u'00')
|
||||
self.assertFalse(os.path.exists(path))
|
||||
r = self.client.post(url,dict(content="Some initial review text\n",submit_response="1"))
|
||||
self.assertEquals(r.status_code,302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(doc.rev,u'00')
|
||||
with open(path) as f:
|
||||
self.assertEquals(f.read(),"Some initial review text\n")
|
||||
f.close()
|
||||
self.assertTrue( "submission-00" in doc.latest_event(NewRevisionDocEvent).desc)
|
||||
|
||||
def test_subsequent_submission(self):
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
url = urlreverse('conflict_review_submit',kwargs=dict(name=doc.name))
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# A little additional setup
|
||||
# doc.rev is u'00' per the test setup - double-checking that here - if it fails, the breakage is in setUp
|
||||
self.assertEquals(doc.rev,u'00')
|
||||
path = os.path.join(settings.CONFLICT_REVIEW_PATH, '%s-%s.txt' % (doc.canonical_name(), doc.rev))
|
||||
with open(path,'w') as f:
|
||||
f.write('This is the old proposal.')
|
||||
f.close()
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(q('textarea')[0].text.startswith("This is the old proposal."))
|
||||
|
||||
# faulty posts trying to use file upload
|
||||
# Copied from wgtracker tests - is this really testing the server code, or is it testing
|
||||
# how client.post populates Content-Type?
|
||||
test_file = StringIO("\x10\x11\x12") # post binary file
|
||||
test_file.name = "unnamed"
|
||||
r = self.client.post(url, dict(txt=test_file,submit_response="1"))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertTrue("does not appear to be a text file" in r.content)
|
||||
|
||||
# sane post uploading a file
|
||||
test_file = StringIO("This is a new proposal.")
|
||||
test_file.name = "unnamed"
|
||||
r = self.client.post(url,dict(txt=test_file,submit_response="1"))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
|
||||
self.assertEquals(doc.rev,u'01')
|
||||
path = os.path.join(settings.CONFLICT_REVIEW_PATH, '%s-%s.txt' % (doc.canonical_name(), doc.rev))
|
||||
with open(path) as f:
|
||||
self.assertEquals(f.read(),"This is a new proposal.")
|
||||
f.close()
|
||||
self.assertTrue( "submission-01" in doc.latest_event(NewRevisionDocEvent).desc)
|
||||
|
||||
# verify reset text button works
|
||||
r = self.client.post(url,dict(reset_text="1"))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(q('textarea')[0].text.startswith("[Edit this page"))
|
||||
|
||||
def setUp(self):
|
||||
make_test_data()
|
||||
self.test_dir = os.path.abspath("tmp-conflict-review-testdir")
|
||||
os.mkdir(self.test_dir)
|
||||
settings.CONFLICT_REVIEW_PATH = self.test_dir
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.test_dir)
|
12
ietf/doc/urls_conflict_review.py
Normal file
12
ietf/doc/urls_conflict_review.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('ietf.doc.views_conflict_review',
|
||||
url(r'^state/$', "change_state", name='conflict_review_change_state'),
|
||||
url(r'^submit/$', "submit", name='conflict_review_submit'),
|
||||
url(r'^notices/$', "edit_notices", name='conflict_review_notices'),
|
||||
url(r'^ad/$', "edit_ad", name='conflict_review_ad'),
|
||||
url(r'^approve/$', "approve", name='conflict_review_approve'),
|
||||
url(r'^start_conflict_review/$', "start_review", name='conflict_review_start'),
|
||||
url(r'^telechat/$', "telechat_date", name='conflict_review_telechat_date'),
|
||||
)
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
import os
|
||||
from django.conf import settings
|
||||
|
||||
# Should this move from idrfc to doc?
|
||||
from ietf.idrfc import markup_txt
|
||||
|
||||
from ietf.doc.models import *
|
||||
|
||||
def get_state_types(doc):
|
||||
|
@ -30,6 +36,15 @@ def get_tags_for_stream_id(stream_id):
|
|||
else:
|
||||
return []
|
||||
|
||||
# This, and several other utilities here, assume that there is only one active ballot for a document at any point in time.
|
||||
# If that assumption is violated, they will only expose the most recently created ballot
|
||||
def active_ballot(doc):
|
||||
"""Returns the most recently created ballot if it isn't closed."""
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
open = ballot_open(doc,ballot.ballot_type.slug) if ballot else False
|
||||
return ballot if open else None
|
||||
|
||||
|
||||
def active_ballot_positions(doc, ballot=None):
|
||||
"""Return dict mapping each active AD to a current ballot position (or None if they haven't voted)."""
|
||||
active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active"))
|
||||
|
@ -106,10 +121,6 @@ def close_open_ballots(doc, by):
|
|||
e.desc = 'Closed "%s" ballot' % t.name
|
||||
e.save()
|
||||
|
||||
def get_rfc_number(doc):
|
||||
qs = doc.docalias_set.filter(name__startswith='rfc')
|
||||
return qs[0].name[3:] if qs else None
|
||||
|
||||
def augment_with_start_time(docs):
|
||||
"""Add a started_time attribute to each document with the time of
|
||||
the first revision."""
|
||||
|
@ -165,23 +176,31 @@ def augment_events_with_revision(doc, events):
|
|||
e.rev = cur_rev
|
||||
|
||||
|
||||
def augment_with_telechat_date(docs):
|
||||
"""Add a telechat_date attribute to each document with the
|
||||
scheduled telechat or None if it's not scheduled."""
|
||||
docs = list(docs)
|
||||
def get_document_content(key, filename, split=True, markup=True):
|
||||
f = None
|
||||
try:
|
||||
f = open(filename, 'rb')
|
||||
raw_content = f.read()
|
||||
except IOError:
|
||||
error = "Error; cannot read ("+key+")"
|
||||
if split:
|
||||
return (error, "")
|
||||
else:
|
||||
return error
|
||||
finally:
|
||||
if f:
|
||||
f.close()
|
||||
if markup:
|
||||
return markup_txt.markup(raw_content,split)
|
||||
else:
|
||||
return raw_content
|
||||
|
||||
docs_dict = {}
|
||||
for d in docs:
|
||||
docs_dict[d.pk] = d
|
||||
d.telechat_date = None
|
||||
def log_state_changed(request, doc, by, new_description, old_description):
|
||||
from ietf.doc.models import DocEvent
|
||||
|
||||
seen = set()
|
||||
e = DocEvent(doc=doc, by=by)
|
||||
e.type = "changed_document"
|
||||
e.desc = u"State changed to <b>%s</b> from %s" % (new_description, old_description)
|
||||
e.save()
|
||||
return e
|
||||
|
||||
for e in TelechatDocEvent.objects.filter(type="scheduled_for_telechat", doc__in=docs).order_by('-time'):
|
||||
if e.doc_id in seen:
|
||||
continue
|
||||
|
||||
docs_dict[e.doc_id].telechat_date = e.telechat_date
|
||||
seen.add(e.doc_id)
|
||||
|
||||
return docs
|
||||
|
|
498
ietf/doc/views_conflict_review.py
Normal file
498
ietf/doc/views_conflict_review.py
Normal file
|
@ -0,0 +1,498 @@
|
|||
import datetime, os
|
||||
|
||||
from django import forms
|
||||
from django.shortcuts import render_to_response, get_object_or_404, redirect
|
||||
from django.http import HttpResponseRedirect, Http404
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.idrfc.utils import update_telechat
|
||||
|
||||
from ietf.doc.utils import log_state_changed
|
||||
|
||||
from ietf.doc.models import save_document_in_history
|
||||
from ietf.doc.utils import create_ballot_if_not_open, close_open_ballots, get_document_content
|
||||
from ietf.ietfauth.decorators import has_role, role_required
|
||||
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||
from ietf.utils.mail import send_mail_preformatted
|
||||
|
||||
from ietf.doc.models import State, Document, DocHistory, DocAlias
|
||||
from ietf.doc.models import DocEvent, NewRevisionDocEvent, WriteupDocEvent, TelechatDocEvent, BallotDocEvent, BallotPositionDocEvent
|
||||
from ietf.person.models import Person
|
||||
from ietf.iesg.models import TelechatDate
|
||||
from ietf.group.models import Role, Group
|
||||
|
||||
class ChangeStateForm(forms.Form):
|
||||
review_state = forms.ModelChoiceField(State.objects.filter(type="conflrev", used=True), label="Conflict review state", empty_label=None, required=True)
|
||||
comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the review history", required=False)
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.hide = kwargs.pop('hide', None)
|
||||
super(ChangeStateForm, self).__init__(*args, **kwargs)
|
||||
# hide requested fields
|
||||
if self.hide:
|
||||
for f in self.hide:
|
||||
self.fields[f].widget = forms.HiddenInput
|
||||
|
||||
@role_required("Area Director", "Secretariat")
|
||||
def change_state(request, name, option=None):
|
||||
"""Change state of and IESG review for IETF conflicts in other stream's documents, notifying parties as necessary
|
||||
and logging the change as a comment."""
|
||||
review = get_object_or_404(Document, type="conflrev", name=name)
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ChangeStateForm(request.POST)
|
||||
if form.is_valid():
|
||||
clean = form.cleaned_data
|
||||
review_state = clean['review_state']
|
||||
comment = clean['comment'].rstrip()
|
||||
|
||||
if comment:
|
||||
c = DocEvent(type="added_comment", doc=review, by=login)
|
||||
c.desc = comment
|
||||
c.save()
|
||||
|
||||
if review_state != review.get_state():
|
||||
save_document_in_history(review)
|
||||
|
||||
old_description = review.friendly_state()
|
||||
review.set_state(review_state)
|
||||
new_description = review.friendly_state()
|
||||
|
||||
log_state_changed(request, review, login, new_description, old_description)
|
||||
|
||||
review.time = datetime.datetime.now()
|
||||
review.save()
|
||||
|
||||
if review_state.slug == "iesgeval":
|
||||
create_ballot_if_not_open(review, login, "conflrev")
|
||||
ballot = review.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if has_role(request.user, "Area Director") and not review.latest_event(BallotPositionDocEvent, ad=login, ballot=ballot, type="changed_ballot_position"):
|
||||
|
||||
# The AD putting a conflict review into iesgeval who doesn't already have a position is saying "yes"
|
||||
pos = BallotPositionDocEvent(doc=review, by=login)
|
||||
pos.ballot = ballot
|
||||
pos.type = "changed_ballot_position"
|
||||
pos.ad = login
|
||||
pos.pos_id = "yes"
|
||||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
|
||||
pos.save()
|
||||
|
||||
|
||||
return redirect('doc_view', name=review.name)
|
||||
else:
|
||||
hide = []
|
||||
s = review.get_state()
|
||||
init = dict(review_state=s.pk if s else None)
|
||||
form = ChangeStateForm(hide=hide, initial=init)
|
||||
|
||||
return render_to_response('doc/conflict_review/change_state.html',
|
||||
dict(form=form,
|
||||
doc=review,
|
||||
login=login,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class UploadForm(forms.Form):
|
||||
content = forms.CharField(widget=forms.Textarea, label="Conflict review response", help_text="Edit the conflict review response", required=False)
|
||||
txt = forms.FileField(label=".txt format", help_text="Or upload a .txt file", required=False)
|
||||
|
||||
def clean_content(self):
|
||||
return self.cleaned_data["content"].replace("\r", "")
|
||||
|
||||
def clean_txt(self):
|
||||
return get_cleaned_text_file_content(self.cleaned_data["txt"])
|
||||
|
||||
def save(self, review):
|
||||
filename = os.path.join(settings.CONFLICT_REVIEW_PATH, '%s-%s.txt' % (review.canonical_name(), review.rev))
|
||||
with open(filename, 'wb') as destination:
|
||||
if self.cleaned_data['txt']:
|
||||
destination.write(self.cleaned_data['txt'])
|
||||
else:
|
||||
destination.write(self.cleaned_data['content'])
|
||||
|
||||
#This is very close to submit on charter - can we get better reuse?
|
||||
@role_required('Area Director','Secretariat')
|
||||
def submit(request, name):
|
||||
review = get_object_or_404(Document, type="conflrev", name=name)
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
path = os.path.join(settings.CONFLICT_REVIEW_PATH, '%s-%s.txt' % (review.canonical_name(), review.rev))
|
||||
not_uploaded_yet = review.rev == "00" and not os.path.exists(path)
|
||||
|
||||
if not_uploaded_yet:
|
||||
# this case is special - the conflict review text document doesn't actually exist yet
|
||||
next_rev = review.rev
|
||||
else:
|
||||
next_rev = "%02d" % (int(review.rev)+1)
|
||||
|
||||
if request.method == 'POST':
|
||||
if "submit_response" in request.POST:
|
||||
form = UploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
save_document_in_history(review)
|
||||
|
||||
review.rev = next_rev
|
||||
|
||||
e = NewRevisionDocEvent(doc=review, by=login, type="new_revision")
|
||||
e.desc = "New version available: <b>%s-%s.txt</b>" % (review.canonical_name(), review.rev)
|
||||
e.rev = review.rev
|
||||
e.save()
|
||||
|
||||
# Save file on disk
|
||||
form.save(review)
|
||||
|
||||
review.time = datetime.datetime.now()
|
||||
review.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('doc_view', kwargs={'name': review.name}))
|
||||
|
||||
elif "reset_text" in request.POST:
|
||||
|
||||
init = { "content": render_to_string("doc/conflict_review/review_choices.txt",dict())}
|
||||
form = UploadForm(initial=init)
|
||||
|
||||
# Protect against handcrufted malicious posts
|
||||
else:
|
||||
form = None
|
||||
|
||||
else:
|
||||
form = None
|
||||
|
||||
if not form:
|
||||
init = { "content": ""}
|
||||
|
||||
if not_uploaded_yet:
|
||||
init["content"] = render_to_string("doc/conflict_review/review_choices.txt",
|
||||
dict(),
|
||||
)
|
||||
else:
|
||||
filename = os.path.join(settings.CONFLICT_REVIEW_PATH, '%s-%s.txt' % (review.canonical_name(), review.rev))
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
init["content"] = f.read()
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
form = UploadForm(initial=init)
|
||||
|
||||
return render_to_response('doc/conflict_review/submit.html',
|
||||
{'form': form,
|
||||
'next_rev': next_rev,
|
||||
'review' : review,
|
||||
'conflictdoc' : review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
||||
},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class NotifyForm(forms.Form):
|
||||
notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False)
|
||||
|
||||
|
||||
@role_required("Area Director", "Secretariat")
|
||||
def edit_notices(request, name):
|
||||
"""Change the set of email addresses document change notificaitions go to."""
|
||||
|
||||
review = get_object_or_404(Document, type="conflrev", name=name)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = NotifyForm(request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
review.notify = form.cleaned_data['notify']
|
||||
review.save()
|
||||
|
||||
login = request.user.get_profile()
|
||||
c = DocEvent(type="added_comment", doc=review, by=login)
|
||||
c.desc = "Notification list changed to : "+review.notify
|
||||
c.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('doc_view', kwargs={'name': review.name}))
|
||||
|
||||
else:
|
||||
|
||||
init = { "notify" : review.notify }
|
||||
form = NotifyForm(initial=init)
|
||||
|
||||
return render_to_response('doc/conflict_review/notify.html',
|
||||
{'form': form,
|
||||
'review': review,
|
||||
'conflictdoc' : review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
||||
},
|
||||
context_instance = RequestContext(request))
|
||||
|
||||
class AdForm(forms.Form):
|
||||
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'),
|
||||
label="Shepherding AD", empty_label="(None)", required=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
# if previous AD is now ex-AD, append that person to the list
|
||||
ad_pk = self.initial.get('ad')
|
||||
choices = self.fields['ad'].choices
|
||||
if ad_pk and ad_pk not in [pk for pk, name in choices]:
|
||||
self.fields['ad'].choices = list(choices) + [("", "-------"), (ad_pk, Person.objects.get(pk=ad_pk).plain_name())]
|
||||
|
||||
|
||||
|
||||
@role_required("Area Director", "Secretariat")
|
||||
def edit_ad(request, name):
|
||||
"""Change the shepherding Area Director for this review."""
|
||||
|
||||
review = get_object_or_404(Document, type="conflrev", name=name)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = AdForm(request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
review.ad = form.cleaned_data['ad']
|
||||
review.save()
|
||||
|
||||
login = request.user.get_profile()
|
||||
c = DocEvent(type="added_comment", doc=review, by=login)
|
||||
c.desc = "Shepherding AD changed to "+review.ad.name
|
||||
c.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('doc_view', kwargs={'name': review.name}))
|
||||
|
||||
else:
|
||||
init = { "ad" : review.ad_id }
|
||||
form = AdForm(initial=init)
|
||||
|
||||
return render_to_response('doc/conflict_review/change_ad.html',
|
||||
{'form': form,
|
||||
'review': review,
|
||||
'conflictdoc' : review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
||||
},
|
||||
context_instance = RequestContext(request))
|
||||
|
||||
def default_approval_text(review):
|
||||
|
||||
# Leaving these commented out lines for current_text to make it easy to put the actual review result into the approval email
|
||||
# message should the stream owners decide that is a good thing.
|
||||
#filename = "%s-%s.txt" % (review.canonical_name(), review.rev)
|
||||
#current_text = get_document_content(filename, os.path.join(settings.CONFLICT_REVIEW_PATH, filename), split=False, markup=False)
|
||||
|
||||
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document
|
||||
if conflictdoc.stream_id=='ise':
|
||||
receiver = 'RFC-Editor'
|
||||
elif conflictdoc.stream_id=='irtf':
|
||||
receiver = 'IRTF'
|
||||
else:
|
||||
receiver = 'recipient'
|
||||
text = render_to_string("doc/conflict_review/approval_text.txt",
|
||||
dict(review=review,
|
||||
review_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
||||
conflictdoc = conflictdoc,
|
||||
conflictdoc_url = settings.IDTRACKER_BASE_URL+conflictdoc.get_absolute_url(),
|
||||
receiver=receiver,
|
||||
#approved_review = current_text
|
||||
)
|
||||
)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
class AnnouncementForm(forms.Form):
|
||||
announcement_text = forms.CharField(widget=forms.Textarea, label="IETF Conflict Review Announcement", help_text="Edit the announcement message", required=True)
|
||||
|
||||
@role_required("Secretariat")
|
||||
def approve(request, name):
|
||||
"""Approve this conflict review, setting the appropriate state and send the announcement to the right parties."""
|
||||
review = get_object_or_404(Document, type="conflrev", name=name)
|
||||
|
||||
if review.get_state('conflrev').slug not in ('appr-reqnopub-pend','appr-noprob-pend'):
|
||||
return Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
if request.method == 'POST':
|
||||
|
||||
form = AnnouncementForm(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
|
||||
new_state_slug = 'appr-reqnopub-sent' if review.get_state('conflrev').slug=='appr-reqnopub-pend' else 'appr-noprob-sent'
|
||||
new_review_state = State.objects.get(type="conflrev", slug=new_state_slug)
|
||||
save_document_in_history(review)
|
||||
old_description = review.friendly_state()
|
||||
review.set_state(new_review_state)
|
||||
new_description = review.friendly_state()
|
||||
|
||||
log_state_changed(request, review, login, new_description, old_description)
|
||||
|
||||
close_open_ballots(review, login)
|
||||
|
||||
e = DocEvent(doc=review, by=login)
|
||||
e.type = "iesg_approved"
|
||||
e.desc = "IESG has approved the conflict review response"
|
||||
e.save()
|
||||
|
||||
review.time = e.time
|
||||
review.save()
|
||||
|
||||
# send announcement
|
||||
send_mail_preformatted(request, form.cleaned_data['announcement_text'])
|
||||
|
||||
c = DocEvent(type="added_comment", doc=review, by=login)
|
||||
c.desc = "The following approval message was sent\n"+form.cleaned_data['announcement_text']
|
||||
c.save()
|
||||
|
||||
return HttpResponseRedirect(review.get_absolute_url())
|
||||
|
||||
else:
|
||||
|
||||
init = { "announcement_text" : default_approval_text(review) }
|
||||
form = AnnouncementForm(initial=init)
|
||||
|
||||
return render_to_response('doc/conflict_review/approve.html',
|
||||
dict(
|
||||
review = review,
|
||||
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
||||
form = form,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class StartReviewForm(forms.Form):
|
||||
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'),
|
||||
label="Shepherding AD", empty_label="(None)", required=True)
|
||||
create_in_state = forms.ModelChoiceField(State.objects.filter(type="conflrev", slug__in=("needshep", "adrev")), empty_label=None, required=False)
|
||||
notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False)
|
||||
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False, widget=forms.Select(attrs={'onchange':'make_bold()'}))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
# telechat choices
|
||||
dates = [d.date for d in TelechatDate.objects.active().order_by('date')]
|
||||
#init = kwargs['initial']['telechat_date']
|
||||
#if init and init not in dates:
|
||||
# dates.insert(0, init)
|
||||
|
||||
self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, d.strftime("%Y-%m-%d")) for d in dates]
|
||||
|
||||
@role_required("Secretariat")
|
||||
def start_review(request, name):
|
||||
"""Start the conflict review process, setting the initial shepherding AD, and possibly putting the review on a telechat."""
|
||||
|
||||
doc_to_review = get_object_or_404(Document, type="draft", name=name)
|
||||
|
||||
if not doc_to_review.stream_id in ('ise','irtf'):
|
||||
raise Http404
|
||||
|
||||
# sanity check that there's not already a conflict review document for this document
|
||||
if [ rel.source for alias in doc_to_review.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='conflrev') ]:
|
||||
raise Http404
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
form = StartReviewForm(request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
if doc_to_review.name.startswith('draft-'):
|
||||
review_name = 'conflict-review-'+doc_to_review.name[6:]
|
||||
else:
|
||||
# This is a failsafe - and might be treated better as an error
|
||||
review_name = 'conflict-review-'+doc_to_review.name
|
||||
|
||||
iesg_group = Group.objects.get(acronym='iesg')
|
||||
|
||||
conflict_review=Document( type_id = "conflrev",
|
||||
title = "IETF conflict review for %s" % doc_to_review.name,
|
||||
name = review_name,
|
||||
rev = "00",
|
||||
ad = form.cleaned_data['ad'],
|
||||
notify = form.cleaned_data['notify'],
|
||||
stream_id = 'ietf',
|
||||
group = iesg_group,
|
||||
)
|
||||
conflict_review.set_state(form.cleaned_data['create_in_state'])
|
||||
conflict_review.save()
|
||||
|
||||
DocAlias.objects.create( name=review_name , document=conflict_review )
|
||||
|
||||
conflict_review.relateddocument_set.create(target=DocAlias.objects.get(name=doc_to_review.name),relationship_id='conflrev')
|
||||
|
||||
c = DocEvent(type="added_comment", doc=conflict_review, by=login)
|
||||
c.desc = "IETF conflict review requested"
|
||||
c.save()
|
||||
|
||||
c = DocEvent(type="added_comment", doc=doc_to_review, by=login)
|
||||
# Is it really OK to put html tags into comment text?
|
||||
c.desc = 'IETF conflict review initiated - see <a href="%s">%s</a>' % (reverse('doc_view', kwargs={'name':conflict_review.name}),conflict_review.name)
|
||||
c.save()
|
||||
|
||||
tc_date = form.cleaned_data['telechat_date']
|
||||
if tc_date:
|
||||
update_telechat(request, conflict_review, login, tc_date)
|
||||
|
||||
return HttpResponseRedirect(conflict_review.get_absolute_url())
|
||||
else:
|
||||
# Take care to do the right thing during ietf chair and stream owner transitions
|
||||
ietf_chair_id = Role.objects.filter(group__acronym='ietf',name='chair')[0].person.id
|
||||
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.append("%s@%s" % (name, settings.TOOLS_SERVER))
|
||||
|
||||
init = {
|
||||
"ad" : ietf_chair_id,
|
||||
"notify" : u', '.join(notify_addresses),
|
||||
}
|
||||
form = StartReviewForm(initial=init)
|
||||
|
||||
return render_to_response('doc/conflict_review/start.html',
|
||||
{'form': form,
|
||||
'doc_to_review': doc_to_review,
|
||||
},
|
||||
context_instance = RequestContext(request))
|
||||
|
||||
|
||||
# There should really only be one of these living in Doc instead of it being spread between idrfc,charter, and here
|
||||
class TelechatForm(forms.Form):
|
||||
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False)
|
||||
returning_item = forms.BooleanField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
dates = [d.date for d in TelechatDate.objects.active().order_by('date')]
|
||||
init = kwargs['initial'].get("telechat_date")
|
||||
if init and init not in dates:
|
||||
dates.insert(0, init)
|
||||
|
||||
self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, d.strftime("%Y-%m-%d")) for d in dates]
|
||||
|
||||
|
||||
@role_required("Area Director", "Secretariat")
|
||||
def telechat_date(request, name):
|
||||
doc = get_object_or_404(Document, type="conflrev", name=name)
|
||||
login = request.user.get_profile()
|
||||
|
||||
e = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
initial_returning_item = bool(e and e.returning_item)
|
||||
|
||||
initial = dict(telechat_date=e.telechat_date if e else None,
|
||||
returning_item = initial_returning_item,
|
||||
)
|
||||
if request.method == "POST":
|
||||
form = TelechatForm(request.POST, initial=initial)
|
||||
|
||||
if form.is_valid():
|
||||
update_telechat(request, doc, login, form.cleaned_data['telechat_date'], form.cleaned_data['returning_item'])
|
||||
return redirect("doc_view", name=doc.name)
|
||||
else:
|
||||
form = TelechatForm(initial=initial)
|
||||
|
||||
return render_to_response('doc/conflict_review/edit_telechat_date.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
user=request.user,
|
||||
login=login),
|
||||
context_instance=RequestContext(request))
|
||||
|
|
@ -272,6 +272,11 @@ class IdWrapper:
|
|||
def displayname_with_link(self):
|
||||
return '<a href="%s">%s</a>' % (self.get_absolute_url(), self.draft_name_and_revision())
|
||||
|
||||
def underlying_document(self):
|
||||
""" Expose the Document object underneath the proxy """
|
||||
from ietf.doc.models import Document
|
||||
return Document.objects.get(docalias__name=self.draft_name)
|
||||
|
||||
def to_json(self):
|
||||
result = jsonify_helper(self, ['draft_name', 'draft_status', 'latest_revision', 'rfc_number', 'title', 'tracker_id', 'publication_date','rfc_editor_state', 'replaced_by', 'replaces', 'in_ietf_process', 'file_types', 'group_acronym', 'stream_id','friendly_state', 'abstract', 'ad_name'])
|
||||
if self.in_ietf_process():
|
||||
|
@ -741,7 +746,7 @@ class BallotWrapper:
|
|||
return
|
||||
|
||||
from ietf.person.models import Person
|
||||
from ietf.doc.models import BallotPositionDocEvent, NewRevisionDocEvent
|
||||
from ietf.doc.models import BallotPositionDocEvent, NewRevisionDocEvent, BallotDocEvent
|
||||
|
||||
active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct()
|
||||
|
||||
|
|
|
@ -38,6 +38,31 @@ def email_state_changedREDESIGN(request, doc, text):
|
|||
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
email_state_changed = email_state_changedREDESIGN
|
||||
|
||||
def email_stream_changed(request, doc, old_stream, new_stream, text=""):
|
||||
"""Email the change text to the notify group and to the stream chairs"""
|
||||
to = [x.strip() for x in doc.notify.replace(';', ',').split(',')]
|
||||
from ietf.group.models import Role as RedesignRole
|
||||
|
||||
# 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 RedesignRole.objects.filter(group__acronym=old_stream.slug,name='chair')])
|
||||
if new_stream:
|
||||
to.extend([x.person.formatted_email() for x in RedesignRole.objects.filter(group__acronym=new_stream.slug,name='chair')])
|
||||
|
||||
if not to:
|
||||
return
|
||||
|
||||
if not text:
|
||||
text = u"Stream changed to <b>%s</b> from %s"% (new_stream,old_stream)
|
||||
text = strip_tags(text)
|
||||
|
||||
send_mail(request, to, None,
|
||||
"ID Tracker Stream Change Notice: %s" % doc.file_tag(),
|
||||
"idrfc/stream_changed_email.txt",
|
||||
dict(text=text,
|
||||
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
|
||||
|
||||
|
||||
def html_to_text(html):
|
||||
return strip_tags(html.replace("<", "<").replace(">", ">").replace("&", "&").replace("<br>", "\n"))
|
||||
|
|
219
ietf/idrfc/templatetags/ballot_icon_redesign.py
Normal file
219
ietf/idrfc/templatetags/ballot_icon_redesign.py
Normal file
|
@ -0,0 +1,219 @@
|
|||
# Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# * Neither the name of the Nokia Corporation and/or its
|
||||
# subsidiary(-ies) nor the names of its contributors may be used
|
||||
# to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from django import template
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from ietf.idtracker.models import IDInternal, BallotInfo
|
||||
from ietf.idrfc.idrfc_wrapper import position_to_string, BALLOT_ACTIVE_STATES
|
||||
from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days
|
||||
from ietf.ietfauth.decorators import has_role
|
||||
from ietf.doc.utils import active_ballot_positions, active_ballot
|
||||
from ietf.doc.models import BallotDocEvent, BallotPositionDocEvent
|
||||
|
||||
from datetime import date
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
def get_user_name(context):
|
||||
if 'user' in context and context['user'].is_authenticated():
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
from ietf.person.models import Person
|
||||
try:
|
||||
return context['user'].get_profile().plain_name()
|
||||
except Person.DoesNotExist:
|
||||
return None
|
||||
|
||||
person = context['user'].get_profile().person()
|
||||
if person:
|
||||
return str(person)
|
||||
return None
|
||||
|
||||
def render_ballot_icon(user, doc):
|
||||
if not doc:
|
||||
return ""
|
||||
|
||||
if doc.type_id == "draft":
|
||||
s = doc.get_state("draft-iesg")
|
||||
if s and s.name not in BALLOT_ACTIVE_STATES:
|
||||
return ""
|
||||
elif doc.type_id == "charter":
|
||||
if doc.get_state_slug() not in ("intrev", "iesgrev"):
|
||||
return ""
|
||||
elif doc.type_id == "conflrev":
|
||||
if doc.get_state_slug() not in ("iesgeval","defer"):
|
||||
return ""
|
||||
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if not ballot:
|
||||
return ""
|
||||
|
||||
edit_position_url = urlreverse('ietf.idrfc.views_ballot.edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
|
||||
|
||||
def sort_key(t):
|
||||
_, pos = t
|
||||
if not pos:
|
||||
return (2, 0)
|
||||
elif pos.pos.blocking:
|
||||
return (0, pos.pos.order)
|
||||
else:
|
||||
return (1, pos.pos.order)
|
||||
|
||||
positions = list(active_ballot_positions(doc, ballot).items())
|
||||
positions.sort(key=sort_key)
|
||||
|
||||
cm = ""
|
||||
if has_role(user, "Area Director"):
|
||||
cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
|
||||
|
||||
res = ['<table class="ballot_icon" title="IESG Evaluation Record (click to show more, right-click to edit position)" onclick="showBallot(\'' + doc.name + '\',\'' + str(edit_position_url) + '\')"' + cm + '>']
|
||||
|
||||
res.append("<tr>")
|
||||
|
||||
for i, (ad, pos) in enumerate(positions):
|
||||
if i > 0 and i % 5 == 0:
|
||||
res.append("</tr>")
|
||||
res.append("<tr>")
|
||||
|
||||
c = "position-%s" % (pos.pos.slug if pos else "norecord")
|
||||
|
||||
if hasattr(user, "get_profile") and ad == user.get_profile():
|
||||
c += " my"
|
||||
|
||||
res.append('<td class="%s" />' % c)
|
||||
|
||||
res.append("</tr>")
|
||||
res.append("</table>")
|
||||
|
||||
return "".join(res)
|
||||
|
||||
class BallotIconNode(template.Node):
|
||||
def __init__(self, doc_var):
|
||||
self.doc_var = doc_var
|
||||
def render(self, context):
|
||||
doc = template.resolve_variable(self.doc_var, context)
|
||||
#if hasattr(doc, "_idinternal"):
|
||||
# # hack for old schema
|
||||
# doc = doc._idinternal
|
||||
return render_ballot_icon(context.get("user"), doc)
|
||||
|
||||
def do_ballot_icon(parser, token):
|
||||
try:
|
||||
tagName, docName = token.split_contents()
|
||||
except ValueError:
|
||||
raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0]
|
||||
return BallotIconNode(docName)
|
||||
|
||||
register.tag('ballot_icon', do_ballot_icon)
|
||||
|
||||
@register.filter
|
||||
def my_position(doc, user):
|
||||
user_name = get_user_name({'user':user})
|
||||
if not user_name:
|
||||
return None
|
||||
if not in_group(user, "Area_Director"):
|
||||
return None
|
||||
ballot = active_ballot(doc)
|
||||
pos = "No Record"
|
||||
if ballot:
|
||||
changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__name=user_name, ballot=ballot)
|
||||
if changed_pos:
|
||||
pos = changed_pos.pos.name;
|
||||
return pos
|
||||
|
||||
@register.filter
|
||||
def state_age_colored(doc):
|
||||
if doc.type.slug == 'draft':
|
||||
if not doc.latest_event(type='started_iesg_process'):
|
||||
return ""
|
||||
if not doc.get_state_slug() in ["active", "rfc"]:
|
||||
# Don't show anything for expired/withdrawn/replaced drafts
|
||||
return ""
|
||||
main_state = doc.get_state('draft-iesg')
|
||||
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
|
||||
sub_states = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
|
||||
|
||||
if main_state.slug in ["dead","watching","pub"]:
|
||||
return ""
|
||||
try:
|
||||
state_date = doc.docevent_set.filter(
|
||||
Q(desc__istartswith="Draft Added by ")|
|
||||
Q(desc__istartswith="Draft Added in state ")|
|
||||
Q(desc__istartswith="Draft added in state ")|
|
||||
Q(desc__istartswith="State changed to ")|
|
||||
Q(desc__istartswith="State Changes to ")|
|
||||
Q(desc__istartswith="Sub state has been changed to ")|
|
||||
Q(desc__istartswith="State has been changed to ")|
|
||||
Q(desc__istartswith="IESG has approved and state has been changed to")|
|
||||
Q(desc__istartswith="IESG process started in state")
|
||||
).order_by('-time')[0].time.date()
|
||||
except IndexError:
|
||||
state_date = date(1990,1,1)
|
||||
days = timesince_days(state_date)
|
||||
# loosely based on
|
||||
# http://trac.tools.ietf.org/group/iesg/trac/wiki/PublishPath
|
||||
if main_state.slug == "lc":
|
||||
goal1 = 30
|
||||
goal2 = 30
|
||||
elif main_state.slug == "rfcqueue":
|
||||
goal1 = 60
|
||||
goal2 = 120
|
||||
elif main_state.slug in ["lc-req", "ann"]:
|
||||
goal1 = 4
|
||||
goal2 = 7
|
||||
elif 'need-rev' in [x.slug for x in sub_states]:
|
||||
goal1 = 14
|
||||
goal2 = 28
|
||||
elif main_state.slug == "pub-req":
|
||||
goal1 = 7
|
||||
goal2 = 14
|
||||
elif main_state.slug == "ad-eval":
|
||||
goal1 = 14
|
||||
goal2 = 28
|
||||
else:
|
||||
goal1 = 14
|
||||
goal2 = 28
|
||||
if days > goal2:
|
||||
class_name = "ietf-small ietf-highlight-r"
|
||||
elif days > goal1:
|
||||
class_name = "ietf-small ietf-highlight-y"
|
||||
else:
|
||||
class_name = "ietf-small"
|
||||
if days > goal1:
|
||||
title = ' title="Goal is <%d days"' % (goal1,)
|
||||
else:
|
||||
title = ''
|
||||
return '<span class="%s"%s>(for %d day%s)</span>' % (class_name,title,days,('','s')[days != 1])
|
||||
else:
|
||||
return ""
|
|
@ -293,7 +293,6 @@ class EditInfoTestCase(django.test.TestCase):
|
|||
|
||||
r = self.client.post(url,
|
||||
dict(intended_std_level=str(draft.intended_std_level_id),
|
||||
stream="ietf",
|
||||
ad=ad.pk,
|
||||
create_in_state=State.objects.get(type="draft-iesg", slug="watching").pk,
|
||||
notify="test@example.com",
|
||||
|
@ -307,9 +306,9 @@ class EditInfoTestCase(django.test.TestCase):
|
|||
self.assertEquals(draft.ad, ad)
|
||||
self.assertEquals(draft.note, "This is a note")
|
||||
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))
|
||||
self.assertEquals(draft.docevent_set.count(), events_before + 4)
|
||||
self.assertEquals(draft.docevent_set.count(), events_before + 3)
|
||||
events = list(draft.docevent_set.order_by('time', 'id'))
|
||||
self.assertEquals(events[-4].type, "started_iesg_process")
|
||||
self.assertEquals(events[-3].type, "started_iesg_process")
|
||||
self.assertEquals(len(outbox), mailbox_before)
|
||||
|
||||
|
||||
|
@ -500,7 +499,7 @@ class EditPositionTestCase(django.test.TestCase):
|
|||
def test_cannot_edit_position_as_pre_ad(self):
|
||||
draft = make_test_data()
|
||||
url = urlreverse('ietf.idrfc.views_ballot.edit_position', kwargs=dict(name=draft.name,
|
||||
ballot_id=draft.latest_event(BallotDocEvent, type="created_ballot").pk))
|
||||
ballot_id=draft.latest_event(BallotDocEvent, type="created_ballot").pk))
|
||||
|
||||
# transform to pre-ad
|
||||
ad_role = Role.objects.filter(name="ad")[0]
|
||||
|
@ -1435,3 +1434,152 @@ class MirrorScriptTestCases(unittest.TestCase,RealDatabaseTest):
|
|||
self.assertEquals(len(refs), 3)
|
||||
print "OK"
|
||||
|
||||
|
||||
class IndividualInfoFormsTestCase(django.test.TestCase):
|
||||
|
||||
fixtures = ['names']
|
||||
|
||||
def test_doc_change_stream(self):
|
||||
url = urlreverse('doc_change_stream', kwargs=dict(name=self.docname))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.change-stream')),1)
|
||||
|
||||
# shift to ISE stream
|
||||
messages_before = len(outbox)
|
||||
r = self.client.post(url,dict(stream="ise",comment="7gRMTjBM"))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.stream_id,'ise')
|
||||
self.assertEquals(len(outbox),messages_before+1)
|
||||
self.assertTrue('Stream Change Notice' in outbox[-1]['Subject'])
|
||||
self.assertTrue('7gRMTjBM' in str(outbox[-1]))
|
||||
self.assertTrue('7gRMTjBM' in self.doc.latest_event(DocEvent,type='added_comment').desc)
|
||||
# Would be nice to test that the stream managers were in the To header...
|
||||
|
||||
# shift to an unknown stream (it must be possible to throw a document out of any stream)
|
||||
r = self.client.post(url,dict(stream=""))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.stream,None)
|
||||
|
||||
def test_doc_change_notify(self):
|
||||
url = urlreverse('doc_change_notify', kwargs=dict(name=self.docname))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form input[name=notify]')),1)
|
||||
|
||||
# Provide a list
|
||||
r = self.client.post(url,dict(notify="TJ2APh2P@ietf.org",save_addresses="1"))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.notify,'TJ2APh2P@ietf.org')
|
||||
|
||||
# Ask the form to regenerate the list
|
||||
r = self.client.post(url,dict(regenerate_addresses="1"))
|
||||
self.assertEquals(r.status_code,200)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
# Regenerate does not save!
|
||||
self.assertEquals(self.doc.notify,'TJ2APh2P@ietf.org')
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue('TJ2Aph2P' not in q('form input[name=notify]')[0].value)
|
||||
|
||||
def test_doc_change_intended_status(self):
|
||||
url = urlreverse('doc_change_intended_status', kwargs=dict(name=self.docname))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.change-intended-status')),1)
|
||||
|
||||
# don't allow status level to be cleared
|
||||
r = self.client.post(url,dict(intended_std_level=""))
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
|
||||
# change intended status level
|
||||
messages_before = len(outbox)
|
||||
r = self.client.post(url,dict(intended_std_level="bcp",comment="ZpyQFGmA"))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.intended_std_level_id,'bcp')
|
||||
self.assertEquals(len(outbox),messages_before+1)
|
||||
self.assertTrue('ZpyQFGmA' in str(outbox[-1]))
|
||||
self.assertTrue('ZpyQFGmA' in self.doc.latest_event(DocEvent,type='added_comment').desc)
|
||||
|
||||
def test_doc_change_telechat_date(self):
|
||||
url = urlreverse('doc_change_telechat_date', kwargs=dict(name=self.docname))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.telechat-date')),1)
|
||||
|
||||
# set a date
|
||||
self.assertFalse(self.doc.latest_event(TelechatDocEvent, "scheduled_for_telechat"))
|
||||
telechat_date = TelechatDate.objects.active().order_by('date')[0].date
|
||||
r = self.client.post(url,dict(telechat_date=telechat_date.isoformat()))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date,telechat_date)
|
||||
|
||||
# Take the doc back off any telechat
|
||||
r = self.client.post(url,dict(telechat_date=""))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(self.doc.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date,None)
|
||||
|
||||
def test_doc_change_iesg_note(self):
|
||||
url = urlreverse('doc_change_iesg_note', kwargs=dict(name=self.docname))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.edit-iesg-note')),1)
|
||||
|
||||
# No validation code to test
|
||||
|
||||
# post - testing that the munge code exists in note.clean...
|
||||
r = self.client.post(url,dict(note='ZpyQFGmA\nZpyQFGmA'))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.note,'ZpyQFGmA<br>ZpyQFGmA')
|
||||
self.assertTrue('ZpyQFGmA' in self.doc.latest_event(DocEvent,type='added_comment').desc)
|
||||
|
||||
def test_doc_change_ad(self):
|
||||
url = urlreverse('doc_change_ad', kwargs=dict(name=self.docname))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form select[name=ad]')),1)
|
||||
|
||||
# change ads
|
||||
ad2 = Person.objects.get(name='Ad No2')
|
||||
r = self.client.post(url,dict(ad=str(ad2.pk)))
|
||||
self.assertEquals(r.status_code,302)
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
self.assertEquals(self.doc.ad,ad2)
|
||||
self.assertTrue(self.doc.latest_event(DocEvent,type="added_comment").desc.startswith('Shepherding AD changed'))
|
||||
|
||||
def setUp(self):
|
||||
make_test_data()
|
||||
self.docname='draft-ietf-mars-test'
|
||||
self.doc = Document.objects.get(name=self.docname)
|
||||
|
||||
|
|
|
@ -54,11 +54,19 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot.tsv$', views_doc.ballot_tsv),
|
||||
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot.json$', views_doc.ballot_json),
|
||||
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/$', views_edit.change_state, name='doc_change_state'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/$', views_edit.change_state, name='doc_change_state'), # IESG state
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/info/$', views_edit.edit_info, name='doc_edit_info'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/requestresurrect/$', views_edit.request_resurrect, name='doc_request_resurrect'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/resurrect/$', views_edit.resurrect, name='doc_resurrect'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/addcomment/$', views_edit.add_comment, name='doc_add_comment'),
|
||||
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/stream/$', views_edit.change_stream, name='doc_change_stream'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/notify/$', views_edit.edit_notices, name='doc_change_notify'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/status/$', views_edit.change_intention, name='doc_change_intended_status'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/telechat/$', views_edit.telechat_date, name='doc_change_telechat_date'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/iesgnote/$', views_edit.edit_iesg_note, name='doc_change_iesg_note'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ad/$', views_edit.edit_ad, name='doc_change_ad'),
|
||||
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/deferballot/$', views_ballot.defer_ballot, name='doc_defer_ballot'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/undeferballot/$', views_ballot.undefer_ballot, name='doc_undefer_ballot'),
|
||||
|
@ -68,11 +76,13 @@ urlpatterns = patterns('',
|
|||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),
|
||||
|
||||
(r'^(?P<name>charter-[A-Za-z0-9.-]+)/', include('ietf.wgcharter.urls')),
|
||||
(r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.wgcharter.urls')),
|
||||
(r'^(?P<name>[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django.views.generic.simple',
|
||||
url(r'^help/state/charter/$', 'direct_to_template', { 'template': 'wgcharter/states.html', 'extra_context': { 'states': State.objects.filter(type="charter") } }, name='help_charter_states'),
|
||||
url(r'^help/state/charter/$', 'direct_to_template', { 'template': 'doc/states.html', 'extra_context': { 'states': State.objects.filter(type="charter"),'title':"Charter" } }, name='help_charter_states'),
|
||||
url(r'^help/state/conflict-review/$', 'direct_to_template', { 'template': 'doc/states.html', 'extra_context': { 'states': State.objects.filter(type="conflrev").order_by("order"),'title':"Conflict Review" } }, name='help_conflict_review_states'),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -167,9 +167,9 @@ def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_i
|
|||
# we didn't reschedule but flipped returning item bit - let's
|
||||
# just explain that
|
||||
if returning:
|
||||
e.desc = "Added as returning item on telechat"
|
||||
e.desc = "Set telechat returning item indication"
|
||||
else:
|
||||
e.desc = "Removed as returning item on telechat"
|
||||
e.desc = "Removed telechat returning item indication"
|
||||
|
||||
e.save()
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ from ietf.name.models import BallotPositionName
|
|||
from ietf.message.utils import infer_message
|
||||
from ietf.person.models import Person
|
||||
|
||||
from ietf.doc.utils import log_state_changed
|
||||
|
||||
BALLOT_CHOICES = (("yes", "Yes"),
|
||||
("noobj", "No Objection"),
|
||||
("discuss", "Discuss"),
|
||||
|
@ -65,155 +67,11 @@ def get_ballot_info(ballot, area_director):
|
|||
return (pos, discuss, comment)
|
||||
|
||||
class EditPositionForm(forms.Form):
|
||||
position = forms.ChoiceField(choices=BALLOT_CHOICES, widget=forms.RadioSelect, required=False)
|
||||
discuss_text = forms.CharField(required=False, widget=forms.Textarea)
|
||||
comment_text = forms.CharField(required=False, widget=forms.Textarea)
|
||||
return_to_url = forms.CharField(required=False, widget=forms.HiddenInput)
|
||||
def clean_discuss_text(self):
|
||||
entered_discuss = self.cleaned_data["discuss_text"]
|
||||
entered_pos = self.cleaned_data["position"]
|
||||
if entered_pos == "discuss" and not entered_discuss:
|
||||
print "Raising discuss ",entered_pos," ",entered_discuss
|
||||
raise forms.ValidationError("You must enter a non-empty discuss")
|
||||
return entered_discuss
|
||||
pass
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def edit_position(request, name):
|
||||
"""Vote and edit discuss and comment on Internet Draft as Area Director."""
|
||||
doc = get_object_or_404(InternetDraft, filename=name)
|
||||
if not doc.idinternal:
|
||||
raise Http404()
|
||||
|
||||
ad = login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
|
||||
if 'HTTP_REFERER' in request.META:
|
||||
return_to_url = request.META['HTTP_REFERER']
|
||||
else:
|
||||
return_to_url = doc.idinternal.get_absolute_url()
|
||||
|
||||
|
||||
# if we're in the Secretariat, we can select an AD to act as stand-in for
|
||||
if not in_group(request.user, "Area_Director"):
|
||||
ad_username = request.GET.get('ad')
|
||||
if not ad_username:
|
||||
raise Http404()
|
||||
ad = get_object_or_404(IESGLogin, login_name=ad_username)
|
||||
|
||||
pos, discuss, comment = get_ballot_info(doc.idinternal.ballot, ad)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = EditPositionForm(request.POST)
|
||||
if form.is_valid():
|
||||
# save the vote
|
||||
clean = form.cleaned_data
|
||||
|
||||
if clean['return_to_url']:
|
||||
return_to_url = clean['return_to_url']
|
||||
|
||||
vote = clean['position']
|
||||
if pos:
|
||||
# mark discuss as cleared (quirk from old system)
|
||||
if pos.discuss:
|
||||
pos.discuss = -1
|
||||
else:
|
||||
pos = Position(ballot=doc.idinternal.ballot, ad=ad)
|
||||
pos.discuss = 0
|
||||
|
||||
old_vote = position_to_ballot_choice(pos)
|
||||
|
||||
pos.yes = pos.noobj = pos.abstain = pos.recuse = 0
|
||||
if vote:
|
||||
setattr(pos, vote, 1)
|
||||
|
||||
if pos.id:
|
||||
if vote:
|
||||
pos.save()
|
||||
else:
|
||||
pos.delete()
|
||||
if vote != old_vote:
|
||||
add_document_comment(request, doc, "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos.ad, position_label(vote), position_label(old_vote)))
|
||||
elif vote:
|
||||
pos.save()
|
||||
add_document_comment(request, doc, "[Ballot Position Update] New position, %s, has been recorded" % position_label(vote))
|
||||
|
||||
# save discuss
|
||||
if (discuss and clean['discuss_text'] != discuss.text) or (clean['discuss_text'] and not discuss):
|
||||
if not discuss:
|
||||
discuss = IESGDiscuss(ballot=doc.idinternal.ballot, ad=ad)
|
||||
|
||||
discuss.text = clean['discuss_text']
|
||||
discuss.date = date.today()
|
||||
discuss.revision = doc.revision_display()
|
||||
discuss.active = True
|
||||
discuss.save()
|
||||
|
||||
if discuss.text:
|
||||
add_document_comment(request, doc, discuss.text,
|
||||
ballot=DocumentComment.BALLOT_DISCUSS)
|
||||
|
||||
if pos.discuss < 1:
|
||||
IESGDiscuss.objects.filter(ballot=doc.idinternal.ballot, ad=pos.ad).update(active=False)
|
||||
|
||||
# similar for comment (could share code with discuss, but
|
||||
# it's maybe better to coalesce them in the model instead
|
||||
# than doing a clever hack here)
|
||||
if (comment and clean['comment_text'] != comment.text) or (clean['comment_text'] and not comment):
|
||||
if not comment:
|
||||
comment = IESGComment(ballot=doc.idinternal.ballot, ad=ad)
|
||||
|
||||
comment.text = clean['comment_text']
|
||||
comment.date = date.today()
|
||||
comment.revision = doc.revision_display()
|
||||
comment.active = True
|
||||
comment.save()
|
||||
|
||||
if comment.text:
|
||||
add_document_comment(request, doc, comment.text,
|
||||
ballot=DocumentComment.BALLOT_COMMENT)
|
||||
|
||||
doc.idinternal.event_date = date.today()
|
||||
doc.idinternal.save()
|
||||
|
||||
if request.POST.get("send_mail"):
|
||||
qstr = "?return_to_url=%s" % return_to_url
|
||||
if request.GET.get('ad'):
|
||||
qstr += "&ad=%s" % request.GET.get('ad')
|
||||
return HttpResponseRedirect(urlreverse("doc_send_ballot_comment", kwargs=dict(name=doc.filename)) + qstr)
|
||||
else:
|
||||
if request.POST.get("Defer"):
|
||||
return HttpResponseRedirect(urlreverse("doc_defer_ballot", kwargs=dict(name=doc)))
|
||||
else:
|
||||
if request.POST.get("Undefer"):
|
||||
return HttpResponseRedirect(urlreverse("doc_undefer_ballot", kwargs=dict(name=doc)))
|
||||
else:
|
||||
return HttpResponseRedirect(return_to_url)
|
||||
else:
|
||||
initial = {}
|
||||
if pos:
|
||||
initial['position'] = position_to_ballot_choice(pos)
|
||||
|
||||
if discuss:
|
||||
initial['discuss_text'] = discuss.text
|
||||
|
||||
if comment:
|
||||
initial['comment_text'] = comment.text
|
||||
|
||||
if return_to_url:
|
||||
initial['return_to_url'] = return_to_url
|
||||
|
||||
form = EditPositionForm(initial=initial)
|
||||
|
||||
|
||||
return render_to_response('idrfc/edit_position.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
discuss=discuss,
|
||||
comment=comment,
|
||||
ad=ad,
|
||||
return_to_url=return_to_url,
|
||||
ballot=BallotWrapper(doc.idinternal)
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
pass
|
||||
|
||||
class EditPositionFormREDESIGN(forms.Form):
|
||||
position = forms.ModelChoiceField(queryset=BallotPositionName.objects.all(), widget=forms.RadioSelect, initial="norecord", required=True)
|
||||
|
@ -355,9 +213,7 @@ def edit_positionREDESIGN(request, name, ballot_id):
|
|||
|
||||
blocking_positions = dict((p.pk, p.name) for p in form.fields["position"].queryset.all() if p.blocking)
|
||||
|
||||
ballot_deferred = None
|
||||
if doc.get_state_slug("%s-iesg" % doc.type_id) == "defer":
|
||||
ballot_deferred = doc.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>")
|
||||
ballot_deferred = doc.active_defer_event()
|
||||
|
||||
return render_to_response('idrfc/edit_positionREDESIGN.html',
|
||||
dict(doc=doc,
|
||||
|
@ -366,6 +222,7 @@ def edit_positionREDESIGN(request, name, ballot_id):
|
|||
return_to_url=return_to_url,
|
||||
old_pos=old_pos,
|
||||
ballot_deferred=ballot_deferred,
|
||||
ballot = ballot,
|
||||
show_discuss_text=old_pos and old_pos.pos_id=="discuss",
|
||||
blocking_positions=simplejson.dumps(blocking_positions),
|
||||
),
|
||||
|
@ -547,43 +404,15 @@ def clear_ballot(request, name):
|
|||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def defer_ballot(request, name):
|
||||
"""Signal post-pone of Internet Draft ballot, notifying relevant parties."""
|
||||
doc = get_object_or_404(InternetDraft, filename=name)
|
||||
if not doc.idinternal:
|
||||
raise Http404()
|
||||
|
||||
login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
telechat_date = TelechatDates.objects.all()[0].date2
|
||||
|
||||
if request.method == 'POST':
|
||||
doc.idinternal.ballot.defer = True
|
||||
doc.idinternal.ballot.defer_by = login
|
||||
doc.idinternal.ballot.defer_date = date.today()
|
||||
doc.idinternal.ballot.save()
|
||||
|
||||
doc.idinternal.change_state(IDState.objects.get(document_state_id=IDState.IESG_EVALUATION_DEFER), None)
|
||||
doc.idinternal.agenda = True
|
||||
doc.idinternal.telechat_date = telechat_date
|
||||
doc.idinternal.event_date = date.today()
|
||||
doc.idinternal.save()
|
||||
|
||||
email_ballot_deferred(request, doc, login, telechat_date)
|
||||
|
||||
log_state_changed(request, doc, login)
|
||||
|
||||
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
|
||||
|
||||
return render_to_response('idrfc/defer_ballot.html',
|
||||
dict(doc=doc,
|
||||
telechat_date=telechat_date,
|
||||
back_url=doc.idinternal.get_absolute_url()),
|
||||
context_instance=RequestContext(request))
|
||||
pass
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def defer_ballotREDESIGN(request, name):
|
||||
"""Signal post-pone of Internet Draft ballot, notifying relevant parties."""
|
||||
"""Signal post-pone of ballot, notifying relevant parties."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.get_state("draft-iesg"):
|
||||
if doc.type_id not in ('draft','conflrev'):
|
||||
raise Http404()
|
||||
if doc.type_id == 'draft' and not doc.get_state("draft-iesg"):
|
||||
raise Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
@ -592,15 +421,17 @@ def defer_ballotREDESIGN(request, name):
|
|||
if request.method == 'POST':
|
||||
save_document_in_history(doc)
|
||||
|
||||
prev = doc.get_state("draft-iesg")
|
||||
doc.set_state(State.objects.get(type="draft-iesg", slug='defer'))
|
||||
prev_state = doc.friendly_state()
|
||||
if doc.type_id == 'draft':
|
||||
doc.set_state(State.objects.get(type="draft-iesg", slug='defer'))
|
||||
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
|
||||
prev_tag = prev_tag[0] if prev_tag else None
|
||||
if prev_tag:
|
||||
doc.tags.remove(prev_tag)
|
||||
elif doc.type_id == 'conflrev':
|
||||
doc.set_state(State.objects.get(type='conflrev', slug='defer'))
|
||||
|
||||
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
|
||||
prev_tag = prev_tag[0] if prev_tag else None
|
||||
if prev_tag:
|
||||
doc.tags.remove(prev_tag)
|
||||
|
||||
e = log_state_changed(request, doc, login, prev, prev_tag)
|
||||
e = log_state_changed(request, doc, login, doc.friendly_state(), prev_state)
|
||||
|
||||
doc.time = e.time
|
||||
doc.save()
|
||||
|
@ -623,38 +454,15 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
|||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def undefer_ballot(request, name):
|
||||
"""Delete deferral of Internet Draft ballot."""
|
||||
doc = get_object_or_404(InternetDraft, filename=name)
|
||||
if not doc.idinternal:
|
||||
raise Http404()
|
||||
|
||||
login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
telechat_date = TelechatDates.objects.all()[0].date1
|
||||
|
||||
if request.method == 'POST':
|
||||
doc.idinternal.ballot.defer = False
|
||||
doc.idinternal.ballot.save()
|
||||
|
||||
doc.idinternal.change_state(IDState.objects.get(document_state_id=IDState.IESG_EVALUATION), None)
|
||||
doc.idinternal.telechat_date = telechat_date
|
||||
doc.idinternal.event_date = date.today()
|
||||
doc.idinternal.save()
|
||||
|
||||
log_state_changed(request, doc, login)
|
||||
|
||||
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
|
||||
|
||||
return render_to_response('idrfc/undefer_ballot.html',
|
||||
dict(doc=doc,
|
||||
telechat_date=telechat_date,
|
||||
back_url=doc.idinternal.get_absolute_url()),
|
||||
context_instance=RequestContext(request))
|
||||
pass
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def undefer_ballotREDESIGN(request, name):
|
||||
"""Delete deferral of Internet Draft ballot."""
|
||||
"""undo deferral of ballot ballot."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.get_state("draft-iesg"):
|
||||
if doc.type_id not in ('draft','conflrev'):
|
||||
raise Http404()
|
||||
if doc.type_id == 'draft' and not doc.get_state("draft-iesg"):
|
||||
raise Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
@ -663,19 +471,22 @@ def undefer_ballotREDESIGN(request, name):
|
|||
if request.method == 'POST':
|
||||
save_document_in_history(doc)
|
||||
|
||||
prev = doc.get_state("draft-iesg")
|
||||
doc.set_state(State.objects.get(type="draft-iesg", slug='iesg-eva'))
|
||||
prev_state = doc.friendly_state()
|
||||
if doc.type_id == 'draft':
|
||||
doc.set_state(State.objects.get(type="draft-iesg", slug='iesg-eva'))
|
||||
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
|
||||
prev_tag = prev_tag[0] if prev_tag else None
|
||||
if prev_tag:
|
||||
doc.tags.remove(prev_tag)
|
||||
elif doc.type_id == 'conflrev':
|
||||
doc.set_state(State.objects.get(type='conflrev',slug='iesgeval'))
|
||||
|
||||
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
|
||||
prev_tag = prev_tag[0] if prev_tag else None
|
||||
if prev_tag:
|
||||
doc.tags.remove(prev_tag)
|
||||
|
||||
e = log_state_changed(request, doc, login, prev, prev_tag)
|
||||
e = log_state_changed(request, doc, login, doc.friendly_state(), prev_state)
|
||||
|
||||
doc.time = e.time
|
||||
doc.save()
|
||||
|
||||
update_telechat(request, doc, login, telechat_date)
|
||||
email_state_changed(request, doc, e.desc)
|
||||
|
||||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
|
|
|
@ -60,15 +60,17 @@ def render_document_top(request, doc, tab, name):
|
|||
tabs.append(("Document", "document", urlreverse("ietf.idrfc.views_doc.document_main", kwargs=dict(name=name)), True))
|
||||
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if doc.type_id == "draft":
|
||||
if doc.type_id in ("draft","conflrev"):
|
||||
# if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
|
||||
tabs.append(("IESG Evaluation Record", "ballot", urlreverse("ietf.idrfc.views_doc.document_ballot", kwargs=dict(name=name)), ballot))
|
||||
elif doc.type_id == "charter":
|
||||
tabs.append(("IESG Review", "ballot", urlreverse("ietf.idrfc.views_doc.document_ballot", kwargs=dict(name=name)), ballot))
|
||||
|
||||
# FIXME: if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
|
||||
tabs.append(("IESG Writeups", "writeup", urlreverse("ietf.idrfc.views_doc.document_writeup", kwargs=dict(name=name)), True))
|
||||
tabs.append(("History", "history", urlreverse("ietf.idrfc.views_doc.document_history", kwargs=dict(name=name)), True))
|
||||
if doc.type_id != "conflrev":
|
||||
tabs.append(("IESG Writeups", "writeup", urlreverse("ietf.idrfc.views_doc.document_writeup", kwargs=dict(name=doc.name)), True))
|
||||
|
||||
tabs.append(("History", "history", urlreverse("ietf.idrfc.views_doc.document_history", kwargs=dict(name=doc.name)), True))
|
||||
|
||||
name = doc.canonical_name()
|
||||
if name.startswith("rfc"):
|
||||
|
@ -91,6 +93,8 @@ def document_main(request, name, rev=None):
|
|||
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
group = doc.group
|
||||
if doc.type_id == 'conflrev':
|
||||
conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
|
||||
|
||||
revisions = []
|
||||
for h in doc.history_set.order_by("time", "id"):
|
||||
|
@ -115,18 +119,23 @@ def document_main(request, name, rev=None):
|
|||
if not snapshot:
|
||||
return redirect('doc_view', name=name)
|
||||
|
||||
# find old group, too
|
||||
gh = find_history_active_at(doc.group, doc.time)
|
||||
if gh:
|
||||
group = gh
|
||||
if doc.type_id == "charter":
|
||||
# find old group, too
|
||||
gh = find_history_active_at(doc.group, doc.time)
|
||||
if gh:
|
||||
group = gh
|
||||
|
||||
top = render_document_top(request, doc, "document", name)
|
||||
|
||||
|
||||
|
||||
telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
if telechat and not telechat.telechat_date:
|
||||
telechat = None
|
||||
if telechat and telechat.telechat_date < datetime.date.today():
|
||||
telechat = None
|
||||
|
||||
|
||||
if doc.type_id == "charter":
|
||||
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
|
||||
|
||||
|
@ -150,6 +159,33 @@ def document_main(request, name, rev=None):
|
|||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
if doc.type_id == "conflrev":
|
||||
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
|
||||
pathname = os.path.join(settings.CONFLICT_REVIEW_PATH,filename)
|
||||
|
||||
if doc.rev == "00" and not os.path.isfile(pathname):
|
||||
# This could move to a template
|
||||
content = "A conflict review response has not yet been proposed."
|
||||
else:
|
||||
content = _get_html(filename, pathname, split=False)
|
||||
|
||||
ballot_summary = None
|
||||
if doc.get_state_slug() in ("iesgeval"):
|
||||
ballot_summary = needed_ballot_positions(doc, active_ballot_positions(doc).values())
|
||||
|
||||
return render_to_response("idrfc/document_conflict_review.html",
|
||||
dict(doc=doc,
|
||||
top=top,
|
||||
content=content,
|
||||
revisions=revisions,
|
||||
snapshot=snapshot,
|
||||
telechat=telechat,
|
||||
conflictdoc=conflictdoc,
|
||||
ballot_summary=ballot_summary,
|
||||
approved_states=('appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent')
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
raise Http404()
|
||||
|
||||
|
||||
|
@ -254,10 +290,7 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
|
|||
if not ballot:
|
||||
raise Http404
|
||||
|
||||
deferred = None
|
||||
if doc.type_id == "draft" and doc.get_state_slug("draft-iesg") == "defer":
|
||||
# FIXME: fragile
|
||||
deferred = doc.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>")
|
||||
deferred = doc.active_defer_event()
|
||||
|
||||
# collect positions
|
||||
active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active").distinct())
|
||||
|
@ -351,20 +384,7 @@ def document_debug(request, name):
|
|||
return HttpResponse(doc.to_json(), mimetype='text/plain')
|
||||
|
||||
def _get_html(key, filename, split=True):
|
||||
f = None
|
||||
try:
|
||||
f = open(filename, 'rb')
|
||||
raw_content = f.read()
|
||||
except IOError:
|
||||
error = "Error; cannot read '%s'" % key
|
||||
if split:
|
||||
return (error, "")
|
||||
else:
|
||||
return error
|
||||
finally:
|
||||
if f:
|
||||
f.close()
|
||||
return markup_txt.markup(raw_content, split)
|
||||
return get_document_content(key, filename, split=split, markup=True)
|
||||
|
||||
def include_text(request):
|
||||
include_text = request.GET.get( 'include_text' )
|
||||
|
@ -415,6 +435,8 @@ def document_main_idrfc(request, name, tab):
|
|||
info = {}
|
||||
info['has_pdf'] = (".pdf" in doc.file_types())
|
||||
info['is_rfc'] = False
|
||||
|
||||
info['conflict_reviews'] = [ rel.source for alias in id.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='conflrev') ]
|
||||
|
||||
(content1, content2) = _get_html(
|
||||
str(name)+","+str(id.revision)+",html",
|
||||
|
@ -547,37 +569,45 @@ def get_ballot(name):
|
|||
from ietf.doc.models import DocAlias
|
||||
alias = get_object_or_404(DocAlias, name=name)
|
||||
d = alias.document
|
||||
id = get_object_or_404(InternetDraft, name=d.name)
|
||||
try:
|
||||
if not id.ballot.ballot_issued:
|
||||
id = None
|
||||
bw = None
|
||||
dw = None
|
||||
if (d.type_id=='draft'):
|
||||
id = get_object_or_404(InternetDraft, name=d.name)
|
||||
try:
|
||||
if not id.ballot.ballot_issued:
|
||||
raise Http404
|
||||
except BallotInfo.DoesNotExist:
|
||||
raise Http404
|
||||
except BallotInfo.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
bw = BallotWrapper(id) # XXX Fixme: Eliminate this as we go forward
|
||||
# Python caches ~100 regex'es -- explicitly compiling it inside a method
|
||||
# (where you then throw away the compiled version!) doesn't make sense at
|
||||
# all.
|
||||
if re.search("^rfc([1-9][0-9]*)$", name):
|
||||
id.viewing_as_rfc = True
|
||||
dw = RfcWrapper(id)
|
||||
else:
|
||||
dw = IdWrapper(id)
|
||||
# XXX Fixme: Eliminate 'dw' as we go forward
|
||||
|
||||
try:
|
||||
b = d.latest_event(BallotDocEvent, type="created_ballot")
|
||||
except BallotDocEvent.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
bw = BallotWrapper(id) # XXX Fixme: Eliminate this as we go forward
|
||||
|
||||
# Python caches ~100 regex'es -- explicitly compiling it inside a method
|
||||
# (where you then throw away the compiled version!) doesn't make sense at
|
||||
# all.
|
||||
if re.search("^rfc([1-9][0-9]*)$", name):
|
||||
id.viewing_as_rfc = True
|
||||
dw = RfcWrapper(id)
|
||||
else:
|
||||
dw = IdWrapper(id)
|
||||
# XXX Fixme: Eliminate 'dw' as we go forward
|
||||
|
||||
|
||||
return (bw, dw, b, d)
|
||||
|
||||
|
||||
def ballot_for_popup(request, name):
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
return HttpResponse(document_ballot_content(request, doc, ballot_id=None, editable=False))
|
||||
|
||||
def ballot_html(request, name):
|
||||
bw, dw, ballot, doc = get_ballot(name)
|
||||
content = document_ballot_content(request, doc, ballot.pk, editable=True)
|
||||
return HttpResponse(content)
|
||||
|
||||
def ballot_tsv(request, name):
|
||||
ballot, doc, b, d = get_ballot(name)
|
||||
return HttpResponse(render_to_string('idrfc/ballot.tsv', {'ballot':ballot}, RequestContext(request)), content_type="text/plain")
|
||||
|
|
|
@ -32,76 +32,11 @@ from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName
|
|||
from ietf.person.models import Person, Email
|
||||
|
||||
class ChangeStateForm(forms.Form):
|
||||
state = forms.ModelChoiceField(IDState.objects.all(), empty_label=None, required=True)
|
||||
substate = forms.ModelChoiceField(IDSubState.objects.all(), required=False)
|
||||
note = forms.CharField(widget=forms.Textarea, label="Comment", required=False)
|
||||
pass
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def change_state(request, name):
|
||||
"""Change state of Internet Draft, notifying parties as necessary
|
||||
and logging the change as a comment."""
|
||||
doc = get_object_or_404(InternetDraft, filename=name)
|
||||
if not doc.idinternal or doc.status.status == "Expired":
|
||||
raise Http404()
|
||||
|
||||
login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ChangeStateForm(request.POST)
|
||||
if form.is_valid():
|
||||
state = form.cleaned_data['state']
|
||||
sub_state = form.cleaned_data['substate']
|
||||
note = form.cleaned_data['note']
|
||||
internal = doc.idinternal
|
||||
if state != internal.cur_state or sub_state != internal.cur_sub_state:
|
||||
internal.change_state(state, sub_state)
|
||||
internal.event_date = date.today()
|
||||
internal.mark_by = login
|
||||
internal.save()
|
||||
|
||||
change = log_state_changed(request, doc, login, note=note)
|
||||
email_owner(request, doc, internal.job_owner, login, change)
|
||||
|
||||
if internal.cur_state.document_state_id == IDState.LAST_CALL_REQUESTED:
|
||||
request_last_call(request, doc)
|
||||
|
||||
return render_to_response('idrfc/last_call_requested.html',
|
||||
dict(doc=doc,
|
||||
url=doc.idinternal.get_absolute_url()),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
return HttpResponseRedirect(internal.get_absolute_url())
|
||||
|
||||
else:
|
||||
init = dict(state=doc.idinternal.cur_state_id,
|
||||
substate=doc.idinternal.cur_sub_state_id)
|
||||
form = ChangeStateForm(initial=init)
|
||||
|
||||
next_states = IDNextState.objects.filter(cur_state=doc.idinternal.cur_state)
|
||||
prev_state_formatted = format_document_state(doc.idinternal.prev_state,
|
||||
doc.idinternal.prev_sub_state)
|
||||
|
||||
try:
|
||||
ballot_issued = doc.idinternal.ballot.ballot_issued
|
||||
except BallotInfo.DoesNotExist:
|
||||
ballot_issued = False
|
||||
|
||||
to_iesg_eval = None
|
||||
if not ballot_issued:
|
||||
try:
|
||||
to_iesg_eval = next_states.filter(next_state=IDState.IESG_EVALUATION)[0]
|
||||
except IndexError:
|
||||
pass
|
||||
if to_iesg_eval:
|
||||
next_states = next_states.exclude(next_state=IDState.IESG_EVALUATION)
|
||||
|
||||
return render_to_response('idrfc/change_state.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
prev_state_formatted=prev_state_formatted,
|
||||
next_states=next_states,
|
||||
to_iesg_eval=to_iesg_eval),
|
||||
context_instance=RequestContext(request))
|
||||
pass
|
||||
|
||||
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
|
||||
|
||||
|
@ -203,6 +138,115 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
|||
change_state = change_stateREDESIGN
|
||||
ChangeStateForm = ChangeStateFormREDESIGN
|
||||
|
||||
class ChangeStreamForm(forms.Form):
|
||||
stream = forms.ModelChoiceField(StreamName.objects.exclude(slug="legacy"), required=False)
|
||||
comment = forms.CharField(widget=forms.Textarea, required=False)
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def change_stream(request, name):
|
||||
"""Change the stream of a Document of type 'draft' , notifying parties as necessary
|
||||
and logging the change as a comment."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.type_id=='draft':
|
||||
raise Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ChangeStreamForm(request.POST)
|
||||
if form.is_valid():
|
||||
new_stream = form.cleaned_data['stream']
|
||||
comment = form.cleaned_data['comment'].strip()
|
||||
old_stream = doc.stream
|
||||
|
||||
if new_stream != old_stream:
|
||||
save_document_in_history(doc)
|
||||
|
||||
doc.stream = new_stream
|
||||
|
||||
e = DocEvent(doc=doc,by=login,type='changed_document')
|
||||
e.desc = u"Stream changed to <b>%s</b> from %s"% (new_stream,old_stream)
|
||||
e.save()
|
||||
|
||||
email_desc = e.desc
|
||||
|
||||
if comment:
|
||||
c = DocEvent(doc=doc,by=login,type="added_comment")
|
||||
c.desc = comment
|
||||
c.save()
|
||||
email_desc += "\n"+c.desc
|
||||
|
||||
doc.time = e.time
|
||||
doc.save()
|
||||
|
||||
email_stream_changed(request, doc, old_stream, new_stream, email_desc)
|
||||
|
||||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
|
||||
else:
|
||||
stream = doc.stream
|
||||
form = ChangeStreamForm(initial=dict(stream=stream))
|
||||
|
||||
return render_to_response('idrfc/change_stream.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class ChangeIntentionForm(forms.Form):
|
||||
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.filter(used=True), empty_label="(None)", required=True, label="Intended RFC status")
|
||||
comment = forms.CharField(widget=forms.Textarea, required=False)
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def change_intention(request, name):
|
||||
"""Change the intended publication status of a Document of type 'draft' , notifying parties
|
||||
as necessary and logging the change as a comment."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.type_id=='draft':
|
||||
raise Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ChangeIntentionForm(request.POST)
|
||||
if form.is_valid():
|
||||
new_level = form.cleaned_data['intended_std_level']
|
||||
comment = form.cleaned_data['comment'].strip()
|
||||
old_level = doc.intended_std_level
|
||||
|
||||
if new_level != old_level:
|
||||
save_document_in_history(doc)
|
||||
|
||||
doc.intended_std_level = new_level
|
||||
|
||||
e = DocEvent(doc=doc,by=login,type='changed_document')
|
||||
e.desc = u"Intended Status changed to <b>%s</b> from %s"% (new_level,old_level)
|
||||
e.save()
|
||||
|
||||
email_desc = e.desc
|
||||
|
||||
if comment:
|
||||
c = DocEvent(doc=doc,by=login,type="added_comment")
|
||||
c.desc = comment
|
||||
c.save()
|
||||
email_desc += "\n"+c.desc
|
||||
|
||||
doc.time = e.time
|
||||
doc.save()
|
||||
|
||||
email_owner(request, doc, doc.ad, login, email_desc)
|
||||
|
||||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
|
||||
else:
|
||||
intended_std_level = doc.intended_std_level
|
||||
form = ChangeIntentionForm(initial=dict(intended_std_level=intended_std_level))
|
||||
|
||||
return render_to_response('idrfc/change_intended_status.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def dehtmlify_textarea_text(s):
|
||||
return s.replace("<br>", "\n").replace("<b>", "").replace("</b>", "").replace(" ", " ")
|
||||
|
@ -234,157 +278,10 @@ def get_new_ballot_id():
|
|||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def edit_info(request, name):
|
||||
"""Edit various Internet Draft attributes, notifying parties as
|
||||
necessary and logging changes as document comments."""
|
||||
doc = get_object_or_404(InternetDraft, filename=name)
|
||||
if doc.status.status == "Expired":
|
||||
raise Http404()
|
||||
|
||||
login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
|
||||
new_document = False
|
||||
if not doc.idinternal:
|
||||
new_document = True
|
||||
doc.idinternal = IDInternal(draft=doc,
|
||||
rfc_flag=type(doc) == Rfc,
|
||||
cur_state_id=IDState.PUBLICATION_REQUESTED,
|
||||
prev_state_id=IDState.PUBLICATION_REQUESTED,
|
||||
state_change_notice_to=get_initial_state_change_notice(doc),
|
||||
primary_flag=1,
|
||||
area_acronym_id=Acronym.INDIVIDUAL_SUBMITTER,
|
||||
# would be better to use NULL to
|
||||
# signify an empty ballot
|
||||
ballot_id=get_new_ballot_id(),
|
||||
)
|
||||
|
||||
if doc.idinternal.agenda:
|
||||
initial_telechat_date = doc.idinternal.telechat_date
|
||||
else:
|
||||
initial_telechat_date = None
|
||||
|
||||
if request.method == 'POST':
|
||||
form = EditInfoForm(request.POST,
|
||||
#old_ads needs to be True here - sometimes the user needs to touch
|
||||
#the information on an older document and the AD associated with it
|
||||
#should remain the same - if th old ADs aren't offered, the form
|
||||
#won't let the user proceed without doing the wrong thing
|
||||
old_ads=True,
|
||||
initial=dict(telechat_date=initial_telechat_date,
|
||||
area_acronym=doc.idinternal.area_acronym_id))
|
||||
|
||||
if form.is_valid():
|
||||
changes = []
|
||||
r = form.cleaned_data
|
||||
entry = "%s has been changed to <b>%s</b> from <b>%s</b>"
|
||||
if new_document:
|
||||
doc.idinternal.cur_state_id=r['create_in_state'].document_state_id
|
||||
doc.idinternal.prev_state_id=r['create_in_state'].document_state_id
|
||||
# Django barfs in the diff below because these fields
|
||||
# can't be NULL
|
||||
doc.idinternal.job_owner = r['job_owner']
|
||||
if 'area_acronym' in r:
|
||||
doc.idinternal.area_acronym = r['area_acronym']
|
||||
|
||||
replaces = doc.replaces_set.all()
|
||||
if replaces and replaces[0].idinternal:
|
||||
c = "Earlier history may be found in the Comment Log for <a href=\"%s\">%s</a>" % (replaces[0], replaces[0].idinternal.get_absolute_url())
|
||||
add_document_comment(request, doc, c)
|
||||
|
||||
orig_job_owner = doc.idinternal.job_owner
|
||||
|
||||
# update the attributes, keeping track of what we're doing
|
||||
|
||||
# coalesce some of the changes into one comment, mail them below
|
||||
def diff(obj, attr, name):
|
||||
v = getattr(obj, attr)
|
||||
if r[attr] != v:
|
||||
changes.append(entry % (name, r[attr], v))
|
||||
setattr(obj, attr, r[attr])
|
||||
|
||||
diff(doc, 'intended_status', "Intended Status")
|
||||
if 'area_acronym' in r and r['area_acronym']:
|
||||
diff(doc.idinternal, 'area_acronym', 'Area acronym')
|
||||
diff(doc.idinternal, 'job_owner', 'Responsible AD')
|
||||
diff(doc.idinternal, 'state_change_notice_to', "State Change Notice email list")
|
||||
|
||||
if changes and not new_document:
|
||||
add_document_comment(request, doc, "<br>".join(changes))
|
||||
|
||||
# handle note (for some reason the old Perl code didn't
|
||||
# include that in the changes)
|
||||
if r['note'] != doc.idinternal.note:
|
||||
if not r['note']:
|
||||
if doc.idinternal.note:
|
||||
add_document_comment(request, doc, "Note field has been cleared")
|
||||
else:
|
||||
if doc.idinternal.note:
|
||||
add_document_comment(request, doc, "[Note]: changed to '%s'" % r['note'])
|
||||
else:
|
||||
add_document_comment(request, doc, "[Note]: '%s' added" % r['note'])
|
||||
|
||||
doc.idinternal.note = r['note']
|
||||
|
||||
update_telechat(request, doc.idinternal,
|
||||
r['telechat_date'], r['returning_item'])
|
||||
|
||||
|
||||
doc.idinternal.email_display = str(doc.idinternal.job_owner)
|
||||
doc.idinternal.token_name = str(doc.idinternal.job_owner)
|
||||
doc.idinternal.token_email = doc.idinternal.job_owner.person.email()[1]
|
||||
doc.idinternal.mark_by = login
|
||||
doc.idinternal.event_date = date.today()
|
||||
doc.idinternal.status_date = date.today()
|
||||
|
||||
|
||||
update_stream(request, doc,
|
||||
"Setting stream while adding document to the tracker",
|
||||
person=request.user.get_profile().person(),
|
||||
to_stream = r['stream']
|
||||
)
|
||||
|
||||
if changes and not new_document:
|
||||
email_owner(request, doc, orig_job_owner, login, "\n".join(changes))
|
||||
if new_document:
|
||||
add_document_comment(request, doc, "Draft added in state %s" % doc.idinternal.cur_state.state)
|
||||
|
||||
doc.idinternal.save()
|
||||
doc.save()
|
||||
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
|
||||
else:
|
||||
stream=get_stream_from_draft(doc)
|
||||
stream_id = stream.id if stream else None
|
||||
init = dict(intended_status=doc.intended_status_id,
|
||||
area_acronym=doc.idinternal.area_acronym_id,
|
||||
job_owner=doc.idinternal.job_owner_id,
|
||||
state_change_notice_to=doc.idinternal.state_change_notice_to,
|
||||
note=dehtmlify_textarea_text(doc.idinternal.note),
|
||||
telechat_date=initial_telechat_date,
|
||||
returning_item=doc.idinternal.returning_item,
|
||||
stream=stream_id
|
||||
)
|
||||
|
||||
form = EditInfoForm(old_ads=False, initial=init)
|
||||
|
||||
|
||||
if not new_document:
|
||||
form.standard_fields = [x for x in form.standard_fields if x.name != "create_in_state"]
|
||||
|
||||
try:
|
||||
ballot_issued = doc.idinternal.ballot.ballot_issued
|
||||
except BallotInfo.DoesNotExist:
|
||||
ballot_issued = False
|
||||
|
||||
return render_to_response('idrfc/edit_info.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
user=request.user,
|
||||
login=login,
|
||||
ballot_issued=ballot_issued),
|
||||
context_instance=RequestContext(request))
|
||||
pass
|
||||
|
||||
class EditInfoFormREDESIGN(forms.Form):
|
||||
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.filter(used=True), empty_label="(None)", required=True, label="Intended RFC status")
|
||||
stream = forms.ModelChoiceField(StreamName.objects.all(), empty_label="(None)", required=True)
|
||||
area = forms.ModelChoiceField(Group.objects.filter(type="area", state="active"), empty_label="(None - individual submission)", required=False, label="Assigned to area")
|
||||
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Responsible AD", empty_label="(None)", required=True)
|
||||
create_in_state = forms.ModelChoiceField(State.objects.filter(type="draft-iesg", slug__in=("pub-req", "watching")), empty_label=None, required=False)
|
||||
|
@ -504,7 +401,6 @@ def edit_infoREDESIGN(request, name):
|
|||
# update the attributes, keeping track of what we're doing
|
||||
diff('intended_std_level', "Intended Status")
|
||||
diff('ad', "Responsible AD")
|
||||
diff('stream', "Stream")
|
||||
diff('notify', "State Change Notice email list")
|
||||
|
||||
if r['note'] != doc.note:
|
||||
|
@ -550,7 +446,6 @@ def edit_infoREDESIGN(request, name):
|
|||
init = dict(intended_std_level=doc.intended_std_level_id,
|
||||
area=doc.group_id,
|
||||
ad=doc.ad_id,
|
||||
stream=doc.stream_id,
|
||||
notify=doc.notify,
|
||||
note=dehtmlify_textarea_text(doc.note),
|
||||
telechat_date=initial_telechat_date,
|
||||
|
@ -753,3 +648,182 @@ def add_commentREDESIGN(request, name):
|
|||
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
add_comment = add_commentREDESIGN
|
||||
|
||||
class NotifyForm(forms.Form):
|
||||
notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False)
|
||||
|
||||
|
||||
@group_required('Area_Director','Secretariat')
|
||||
def edit_notices(request, name):
|
||||
"""Change the set of email addresses document change notificaitions go to."""
|
||||
|
||||
doc = get_object_or_404(Document, type="draft", name=name)
|
||||
|
||||
if request.method == 'POST':
|
||||
|
||||
if "save_addresses" in request.POST:
|
||||
form = NotifyForm(request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
doc.notify = form.cleaned_data['notify']
|
||||
doc.save()
|
||||
|
||||
login = request.user.get_profile()
|
||||
c = DocEvent(type="added_comment", doc=doc, by=login)
|
||||
c.desc = "Notification list changed to : "+doc.notify
|
||||
c.save()
|
||||
|
||||
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
|
||||
|
||||
elif "regenerate_addresses" in request.POST:
|
||||
init = { "notify" : get_initial_notify(doc) }
|
||||
form = NotifyForm(initial=init)
|
||||
|
||||
# Protect from handcrufted POST
|
||||
else:
|
||||
init = { "notify" : doc.notify }
|
||||
form = NotifyForm(initial=init)
|
||||
|
||||
else:
|
||||
|
||||
init = { "notify" : doc.notify }
|
||||
form = NotifyForm(initial=init)
|
||||
|
||||
return render_to_response('idrfc/change_notify.html',
|
||||
{'form': form,
|
||||
'doc': doc,
|
||||
},
|
||||
context_instance = RequestContext(request))
|
||||
|
||||
class TelechatForm(forms.Form):
|
||||
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False)
|
||||
returning_item = forms.BooleanField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
dates = [d.date for d in TelechatDate.objects.active().order_by('date')]
|
||||
init = kwargs['initial'].get("telechat_date")
|
||||
if init and init not in dates:
|
||||
dates.insert(0, init)
|
||||
|
||||
self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, d.strftime("%Y-%m-%d")) for d in dates]
|
||||
|
||||
|
||||
@group_required("Area Director", "Secretariat")
|
||||
def telechat_date(request, name):
|
||||
doc = get_object_or_404(Document, type="draft", name=name)
|
||||
login = request.user.get_profile()
|
||||
|
||||
e = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
initial_returning_item = bool(e and e.returning_item)
|
||||
|
||||
initial = dict(telechat_date=e.telechat_date if e else None,
|
||||
returning_item = initial_returning_item,
|
||||
)
|
||||
if request.method == "POST":
|
||||
form = TelechatForm(request.POST, initial=initial)
|
||||
|
||||
if form.is_valid():
|
||||
update_telechat(request, doc, login, form.cleaned_data['telechat_date'],form.cleaned_data['returning_item'])
|
||||
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
|
||||
else:
|
||||
form = TelechatForm(initial=initial)
|
||||
|
||||
return render_to_response('idrfc/edit_telechat_date.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
user=request.user,
|
||||
login=login),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
class IESGNoteForm(forms.Form):
|
||||
note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False)
|
||||
|
||||
def clean_note(self):
|
||||
# note is stored munged in the database
|
||||
return self.cleaned_data['note'].replace('\n', '<br>').replace('\r', '').replace(' ', ' ')
|
||||
|
||||
@group_required("Area Director", "Secretariat")
|
||||
def edit_iesg_note(request, name):
|
||||
doc = get_object_or_404(Document, type="draft", name=name)
|
||||
login = request.user.get_profile()
|
||||
|
||||
initial = dict(note=dehtmlify_textarea_text(doc.note))
|
||||
|
||||
if request.method == "POST":
|
||||
form = IESGNoteForm(request.POST, initial=initial)
|
||||
|
||||
if form.is_valid():
|
||||
new_note = form.cleaned_data['note']
|
||||
if new_note != doc.note:
|
||||
if not new_note:
|
||||
if doc.note:
|
||||
log_message = "Note field has been cleared"
|
||||
else:
|
||||
if doc.note:
|
||||
log_message = "Note changed to '%s'" % new_note
|
||||
else:
|
||||
log_message = "Note added '%s'" % new_note
|
||||
|
||||
doc.note = new_note
|
||||
doc.save()
|
||||
|
||||
c = DocEvent(type="added_comment", doc=doc, by=login)
|
||||
c.desc = log_message
|
||||
c.save()
|
||||
|
||||
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
|
||||
else:
|
||||
form = IESGNoteForm(initial=initial)
|
||||
|
||||
return render_to_response('idrfc/edit_iesg_note.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class AdForm(forms.Form):
|
||||
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'),
|
||||
label="Shepherding AD", empty_label="(None)", required=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
# if previous AD is now ex-AD, append that person to the list
|
||||
ad_pk = self.initial.get('ad')
|
||||
choices = self.fields['ad'].choices
|
||||
if ad_pk and ad_pk not in [pk for pk, name in choices]:
|
||||
self.fields['ad'].choices = list(choices) + [("", "-------"), (ad_pk, Person.objects.get(pk=ad_pk).plain_name())]
|
||||
|
||||
@group_required("Area Director", "Secretariat")
|
||||
def edit_ad(request, name):
|
||||
"""Change the shepherding Area Director for this draft."""
|
||||
|
||||
doc = get_object_or_404(Document, type="draft", name=name)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = AdForm(request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
doc.ad = form.cleaned_data['ad']
|
||||
doc.save()
|
||||
|
||||
login = request.user.get_profile()
|
||||
c = DocEvent(type="added_comment", doc=doc, by=login)
|
||||
c.desc = "Shepherding AD changed to "+doc.ad.name
|
||||
c.save()
|
||||
|
||||
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
|
||||
|
||||
else:
|
||||
init = { "ad" : doc.ad_id }
|
||||
form = AdForm(initial=init)
|
||||
|
||||
return render_to_response('idrfc/change_ad.html',
|
||||
{'form': form,
|
||||
'doc': doc,
|
||||
},
|
||||
context_instance = RequestContext(request))
|
||||
|
||||
|
|
|
@ -210,6 +210,7 @@ def urlize_ietf_docs(string, autoescape=None):
|
|||
string = re.sub("(?<!>)(STD ?)0{0,3}(\d+)", "<a href=\"http://tools.ietf.org/html/std\\2/\">\\1\\2</a>", string)
|
||||
string = re.sub("(?<!>)(FYI ?)0{0,3}(\d+)", "<a href=\"http://tools.ietf.org/html/fyi\\2/\">\\1\\2</a>", string)
|
||||
string = re.sub("(?<!>)(draft-[-0-9a-zA-Z._+]+)", "<a href=\"/doc/\\1/\">\\1</a>", string)
|
||||
string = re.sub("(?<!>)(conflict-review-[-0-9a-zA-Z._+]+)", "<a href=\"/doc/\\1/\">\\1</a>", string)
|
||||
return mark_safe(string)
|
||||
urlize_ietf_docs.is_safe = True
|
||||
urlize_ietf_docs.needs_autoescape = True
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.syndication.feeds import Feed
|
||||
from django.utils.feedgenerator import Atom1Feed
|
||||
from ietf.idtracker.models import IDInternal
|
||||
from ietf.doc.models import Document
|
||||
import datetime
|
||||
|
||||
class IESGAgenda(Feed):
|
||||
|
@ -12,36 +12,23 @@ class IESGAgenda(Feed):
|
|||
feed_type = Atom1Feed
|
||||
|
||||
def items(self):
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
drafts = IDInternal.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.min).distinct()
|
||||
for d in drafts:
|
||||
d.latest_telechat_event = d.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
drafts = [d for d in drafts if d.latest_telechat_event.telechat_date]
|
||||
drafts.sort(key=lambda d: d.latest_telechat_event.telechat_date)
|
||||
return drafts
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
drafts = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.min).distinct()
|
||||
for d in drafts:
|
||||
d.latest_telechat_event = d.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
drafts = [d for d in drafts if d.latest_telechat_event.telechat_date]
|
||||
drafts.sort(key=lambda d: d.latest_telechat_event.telechat_date)
|
||||
return drafts
|
||||
|
||||
return IDInternal.objects.filter(agenda=1).order_by('telechat_date')
|
||||
|
||||
def item_categories(self, item):
|
||||
return [ str(item.telechat_date) ]
|
||||
|
||||
def item_pubdate(self, item):
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
return item.latest_telechat_event.time
|
||||
return item.latest_telechat_event.time
|
||||
|
||||
f = item.comments().filter(comment_text__startswith='Placed on agenda for telechat')
|
||||
try:
|
||||
comment = f[0]
|
||||
date = comment.datetime()
|
||||
except IndexError:
|
||||
date = datetime.datetime.now() #XXX
|
||||
return date
|
||||
|
||||
def item_author_name(self, item):
|
||||
return str( item.job_owner )
|
||||
return str( item.ad ) if item.ad else "None"
|
||||
|
||||
def item_author_email(self, item):
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
return item.ad.role_email("ad")
|
||||
|
||||
return item.job_owner.person.email()[1]
|
||||
return str( item.ad.role_email("ad") ) if item.ad else ""
|
||||
|
|
|
@ -470,3 +470,107 @@ class IesgUrlTestCase(SimpleUrlTestCase):
|
|||
else:
|
||||
return content
|
||||
|
||||
#Tests added since database redesign that speak the new clases
|
||||
|
||||
from ietf.doc.models import Document,TelechatDocEvent,State
|
||||
from ietf.group.models import Person
|
||||
class DeferUndeferTestCase(django.test.TestCase):
|
||||
|
||||
fixtures=['names']
|
||||
|
||||
def helper_test_defer(self,name):
|
||||
|
||||
doc = Document.objects.get(name=name)
|
||||
url = urlreverse('doc_defer_ballot',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# some additional setup
|
||||
dates = TelechatDate.objects.active().order_by("date")
|
||||
first_date = dates[0].date
|
||||
second_date = dates[1].date
|
||||
|
||||
e = TelechatDocEvent(type="scheduled_for_telechat",
|
||||
doc = doc,
|
||||
by = Person.objects.get(name="Aread Irector"),
|
||||
telechat_date = first_date,
|
||||
returning_item = False,
|
||||
)
|
||||
e.save()
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.defer')),1)
|
||||
|
||||
# defer
|
||||
self.assertEquals(doc.telechat_date,first_date)
|
||||
r = self.client.post(url,dict())
|
||||
self.assertEquals(r.status_code, 302)
|
||||
doc = Document.objects.get(name=name)
|
||||
self.assertEquals(doc.telechat_date,second_date)
|
||||
self.assertTrue(doc.returning_item())
|
||||
defer_states = dict(draft=['draft-iesg','defer'],conflrev=['conflrev','defer'])
|
||||
if doc.type_id in defer_states:
|
||||
self.assertEquals(doc.get_state(defer_states[doc.type_id][0]).slug,defer_states[doc.type_id][1])
|
||||
|
||||
|
||||
def helper_test_undefer(self,name):
|
||||
|
||||
doc = Document.objects.get(name=name)
|
||||
url = urlreverse('doc_undefer_ballot',kwargs=dict(name=doc.name))
|
||||
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
# some additional setup
|
||||
dates = TelechatDate.objects.active().order_by("date")
|
||||
first_date = dates[0].date
|
||||
second_date = dates[1].date
|
||||
|
||||
e = TelechatDocEvent(type="scheduled_for_telechat",
|
||||
doc = doc,
|
||||
by = Person.objects.get(name="Aread Irector"),
|
||||
telechat_date = second_date,
|
||||
returning_item = True,
|
||||
)
|
||||
e.save()
|
||||
defer_states = dict(draft=['draft-iesg','defer'],conflrev=['conflrev','defer'])
|
||||
if doc.type_id in defer_states:
|
||||
doc.set_state(State.objects.get(type=defer_states[doc.type_id][0],slug=defer_states[doc.type_id][1]))
|
||||
doc.save()
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form.undefer')),1)
|
||||
|
||||
# undefer
|
||||
self.assertEquals(doc.telechat_date,second_date)
|
||||
r = self.client.post(url,dict())
|
||||
self.assertEquals(r.status_code, 302)
|
||||
doc = Document.objects.get(name=name)
|
||||
self.assertEquals(doc.telechat_date,first_date)
|
||||
self.assertTrue(doc.returning_item())
|
||||
undefer_states = dict(draft=['draft-iesg','iesg-eva'],conflrev=['conflrev','iesgeval'])
|
||||
if doc.type_id in undefer_states:
|
||||
self.assertEquals(doc.get_state(undefer_states[doc.type_id][0]).slug,undefer_states[doc.type_id][1])
|
||||
|
||||
def test_defer_draft(self):
|
||||
self.helper_test_defer('draft-ietf-mars-test')
|
||||
|
||||
def test_defer_conflict_review(self):
|
||||
self.helper_test_defer('conflict-review-imaginary-irtf-submission')
|
||||
|
||||
def test_undefer_draft(self):
|
||||
self.helper_test_undefer('draft-ietf-mars-test')
|
||||
|
||||
def test_undefer_conflict_review(self):
|
||||
self.helper_test_undefer('conflict-review-imaginary-irtf-submission')
|
||||
|
||||
# when charters support being deferred, be sure to test them here
|
||||
|
||||
def setUp(self):
|
||||
from ietf.utils.test_data import make_test_data
|
||||
make_test_data()
|
||||
|
|
|
@ -52,7 +52,7 @@ from ietf.idrfc.models import RfcIndex
|
|||
from ietf.idrfc.utils import update_telechat
|
||||
from ietf.ietfauth.decorators import group_required
|
||||
from ietf.idtracker.templatetags.ietf_filters import in_group
|
||||
from ietf.ipr.models import IprRfc, IprDraft, IprDetail
|
||||
from ietf.ipr.models import IprDocAlias
|
||||
from ietf.doc.models import Document, TelechatDocEvent
|
||||
from ietf.group.models import Group
|
||||
|
||||
|
@ -136,26 +136,37 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
|||
def get_doc_section(id):
|
||||
pass
|
||||
|
||||
def get_doc_sectionREDESIGN(id):
|
||||
states = [16,17,18,19,20,21]
|
||||
if id.intended_std_level_id in ["bcp", "ds", "ps", "std"]:
|
||||
s = "2"
|
||||
else:
|
||||
s = "3"
|
||||
def get_doc_sectionREDESIGN(doc):
|
||||
if doc.type_id == 'draft':
|
||||
states = [16,17,18,19,20,21]
|
||||
if doc.intended_std_level_id in ["bcp", "ds", "ps", "std"]:
|
||||
s = "2"
|
||||
else:
|
||||
s = "3"
|
||||
|
||||
g = doc.group_acronym()
|
||||
if g and str(g) != 'none':
|
||||
s = s + "1"
|
||||
elif (s == "3") and doc.stream_id in ("ise","irtf"):
|
||||
s = s + "3"
|
||||
else:
|
||||
s = s + "2"
|
||||
if not doc.get_state_slug=="rfc" and doc.get_state('draft-iesg').order not in states:
|
||||
s = s + "3"
|
||||
elif doc.returning_item():
|
||||
s = s + "2"
|
||||
else:
|
||||
s = s + "1"
|
||||
elif doc.type_id == 'charter':
|
||||
s = get_wg_section(doc.group)
|
||||
elif doc.type_id == 'conflrev':
|
||||
if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'):
|
||||
s = "333"
|
||||
elif doc.returning_item():
|
||||
s = "332"
|
||||
else:
|
||||
s = "331"
|
||||
|
||||
g = id.document().group_acronym()
|
||||
if g and str(g) != 'none':
|
||||
s = s + "1"
|
||||
elif (s == "3") and id.stream in ("ISE","IRTF"):
|
||||
s = s + "3"
|
||||
else:
|
||||
s = s + "2"
|
||||
if not id.rfc_flag and id.cur_state.document_state_id not in states:
|
||||
s = s + "3"
|
||||
elif id.returning_item:
|
||||
s = s + "2"
|
||||
else:
|
||||
s = s + "1"
|
||||
return s
|
||||
|
||||
def get_wg_section(wg):
|
||||
|
@ -179,42 +190,28 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
|||
get_doc_section = get_doc_sectionREDESIGN
|
||||
|
||||
def agenda_docs(date, next_agenda):
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
|
||||
matches = IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct()
|
||||
matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct()
|
||||
|
||||
idmatches = []
|
||||
rfcmatches = []
|
||||
docmatches = []
|
||||
|
||||
for m in matches:
|
||||
if m.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
|
||||
continue
|
||||
for m in matches:
|
||||
if m.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
|
||||
continue
|
||||
|
||||
e = m.latest_event(type="started_iesg_process")
|
||||
m.balloting_started = e.time if e else datetime.datetime.min
|
||||
e = m.latest_event(type="started_iesg_process")
|
||||
m.balloting_started = e.time if e else datetime.datetime.min
|
||||
|
||||
if m.docalias_set.filter(name__startswith="rfc"):
|
||||
rfcmatches.append(m)
|
||||
else:
|
||||
idmatches.append(m)
|
||||
|
||||
idmatches.sort(key=lambda d: d.balloting_started)
|
||||
rfcmatches.sort(key=lambda d: d.balloting_started)
|
||||
else:
|
||||
if next_agenda:
|
||||
matches = IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1)
|
||||
else:
|
||||
matches = IDInternal.objects.filter(telechat_date=date, primary_flag=1)
|
||||
idmatches = matches.filter(rfc_flag=0).order_by('ballot')
|
||||
rfcmatches = matches.filter(rfc_flag=1).order_by('ballot')
|
||||
docmatches.append(m)
|
||||
|
||||
res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4))
|
||||
for id in list(idmatches)+list(rfcmatches):
|
||||
for id in docmatches:
|
||||
section_key = "s"+get_doc_section(id)
|
||||
if section_key not in res:
|
||||
res[section_key] = []
|
||||
if id.note:
|
||||
# TODO: Find out why this is _here_
|
||||
id.note = id.note.replace(u"\240",u" ")
|
||||
res[section_key].append({'obj':id})
|
||||
return res
|
||||
|
@ -311,15 +308,12 @@ def agenda_documents_txt(request):
|
|||
dates = TelechatDates.objects.all()[0].dates()
|
||||
docs = []
|
||||
for date in dates:
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct():
|
||||
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date:
|
||||
docs.append(d)
|
||||
else:
|
||||
docs.extend(IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1))
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
for d in Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct():
|
||||
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date:
|
||||
docs.append(d)
|
||||
t = loader.get_template('iesg/agenda_documents.txt')
|
||||
c = Context({'docs':docs,'special_stream_list':['ISE','IRTF']})
|
||||
c = Context({'docs':docs,'special_stream_list':['ise','irtf']})
|
||||
return HttpResponse(t.render(c), mimetype='text/plain')
|
||||
|
||||
class RescheduleForm(forms.Form):
|
||||
|
@ -342,83 +336,62 @@ class RescheduleForm(forms.Form):
|
|||
|
||||
self.fields['telechat_date'].choices = choices
|
||||
|
||||
def handle_reschedule_form(request, idinternal, dates):
|
||||
def handle_reschedule_form(request, doc, dates):
|
||||
initial = dict(
|
||||
telechat_date=idinternal.telechat_date if idinternal.agenda else None)
|
||||
telechat_date=doc.telechat_date if doc.on_upcoming_agenda() else None)
|
||||
|
||||
formargs = dict(telechat_dates=dates,
|
||||
prefix="%s" % idinternal.draft_id,
|
||||
prefix="%s" % doc.name,
|
||||
initial=initial)
|
||||
if request.method == 'POST':
|
||||
form = RescheduleForm(request.POST, **formargs)
|
||||
if form.is_valid():
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
login = request.user.get_profile()
|
||||
update_telechat(request, idinternal, login,
|
||||
form.cleaned_data['telechat_date'],
|
||||
False if form.cleaned_data['clear_returning_item'] else None)
|
||||
idinternal.time = datetime.datetime.now()
|
||||
idinternal.save()
|
||||
else:
|
||||
update_telechat(request, idinternal,
|
||||
form.cleaned_data['telechat_date'])
|
||||
if form.cleaned_data['clear_returning_item']:
|
||||
idinternal.returning_item = False
|
||||
idinternal.event_date = datetime.date.today()
|
||||
idinternal.save()
|
||||
login = request.user.get_profile()
|
||||
update_telechat(request, doc, login,
|
||||
form.cleaned_data['telechat_date'],
|
||||
False if form.cleaned_data['clear_returning_item'] else None)
|
||||
doc.time = datetime.datetime.now()
|
||||
doc.save()
|
||||
else:
|
||||
form = RescheduleForm(**formargs)
|
||||
|
||||
form.show_clear = idinternal.returning_item
|
||||
form.show_clear = doc.returning_item()
|
||||
return form
|
||||
|
||||
def agenda_documents(request):
|
||||
dates = TelechatDates.objects.all()[0].dates()
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
idinternals = []
|
||||
for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).distinct():
|
||||
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date in dates:
|
||||
idinternals.append(d)
|
||||
from ietf.doc.models import TelechatDocEvent
|
||||
docs = []
|
||||
for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).distinct():
|
||||
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date in dates:
|
||||
docs.append(d)
|
||||
|
||||
e = d.latest_event(type="started_iesg_process")
|
||||
d.balloting_started = e.time if e else datetime.datetime.min
|
||||
idinternals.sort(key=lambda d: d.balloting_started)
|
||||
else:
|
||||
idinternals = list(IDInternal.objects.filter(telechat_date__in=dates,primary_flag=1,agenda=1).order_by('rfc_flag', 'ballot'))
|
||||
for i in idinternals:
|
||||
e = d.latest_event(type="started_iesg_process")
|
||||
d.balloting_started = e.time if e else datetime.datetime.min
|
||||
docs.sort(key=lambda d: d.balloting_started)
|
||||
for i in docs:
|
||||
i.reschedule_form = handle_reschedule_form(request, i, dates)
|
||||
|
||||
# some may have been taken off the schedule by the reschedule form
|
||||
idinternals = filter(lambda x: x.agenda, idinternals)
|
||||
docs = filter(lambda x: x.on_upcoming_agenda(), docs)
|
||||
|
||||
telechats = []
|
||||
for date in dates:
|
||||
matches = filter(lambda x: x.telechat_date == date, idinternals)
|
||||
matches = filter(lambda x: x.telechat_date == date, docs)
|
||||
res = {}
|
||||
for i in matches:
|
||||
section_key = "s" + get_doc_section(i)
|
||||
if section_key not in res:
|
||||
res[section_key] = []
|
||||
# PM - add code to fill in IPR details. (Would be better to use IdRfc_Wrapper - but this breaks other code
|
||||
if not i.rfc_flag:
|
||||
w = IdWrapper(draft=i)
|
||||
w.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(w.id.tracker_id)
|
||||
iprs = IprDraft.objects.filter(document=w.id.tracker_id)
|
||||
else:
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
ri = i
|
||||
if i.type_id=='draft':
|
||||
if i.get_state_slug()!="rfc":
|
||||
i.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(i.name)
|
||||
else:
|
||||
ri = RfcIndex.objects.get(rfc_number=i.draft_id)
|
||||
w = RfcWrapper(ri)
|
||||
w.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(w.rfc.rfc_number)
|
||||
iprs = IprRfc.objects.filter(document=w.rfc.rfc_number)
|
||||
w.iprCount = len(iprs)
|
||||
w.reschedule_form = i.reschedule_form
|
||||
w.pages = i.pages
|
||||
res[section_key].append(w)
|
||||
i.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(i.rfc_number())
|
||||
i.iprCount = len(i.ipr())
|
||||
res[section_key].append(i)
|
||||
telechats.append({'date':date, 'docs':res})
|
||||
return direct_to_template(request, 'iesg/agenda_documents.html', {'telechats':telechats, 'hide_telechat_date':True})
|
||||
return direct_to_template(request, 'iesg/agenda_documents_redesign.html', {'telechats':telechats, 'hide_telechat_date':True})
|
||||
|
||||
def telechat_docs_tarfile(request,year,month,day):
|
||||
from tempfile import mkstemp
|
||||
|
|
112
ietf/ietfworkflows/templatetags/ietf_streams_redesign.py
Normal file
112
ietf/ietfworkflows/templatetags/ietf_streams_redesign.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
from django import template
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper
|
||||
from ietf.ietfworkflows.utils import (get_workflow_for_draft,
|
||||
get_state_for_draft)
|
||||
from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document,
|
||||
can_manage_writeup_of_a_document)
|
||||
from ietf.ietfworkflows.streams import get_stream_from_wrapper
|
||||
from ietf.ietfworkflows.models import Stream
|
||||
from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream,
|
||||
is_chair_of_stream, can_adopt)
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag('ietfworkflows/stream_state_redesign.html', takes_context=True)
|
||||
def stream_state(context, doc):
|
||||
data = {}
|
||||
stream = doc.stream
|
||||
data.update({'stream': stream})
|
||||
if not stream:
|
||||
return data
|
||||
|
||||
if doc.type.slug != 'draft':
|
||||
return data
|
||||
|
||||
state = get_state_for_draft(doc)
|
||||
|
||||
data.update({
|
||||
'state': state,
|
||||
'doc': doc,
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True)
|
||||
def workflow_history_entry(context, entry):
|
||||
real_entry = entry.get_real_instance()
|
||||
return {'entry': real_entry,
|
||||
'entry_class': real_entry.__class__.__name__.lower()}
|
||||
|
||||
|
||||
@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True)
|
||||
def edit_actions(context, wrapper):
|
||||
request = context.get('request', None)
|
||||
user = request and request.user
|
||||
if not user:
|
||||
return {}
|
||||
idwrapper = None
|
||||
if isinstance(wrapper, IdRfcWrapper):
|
||||
idwrapper = wrapper.id
|
||||
elif isinstance(wrapper, IdWrapper):
|
||||
idwrapper = wrapper
|
||||
if not idwrapper:
|
||||
return None
|
||||
doc = wrapper
|
||||
draft = wrapper._draft
|
||||
|
||||
actions = []
|
||||
if can_adopt(user, draft):
|
||||
actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name))))
|
||||
|
||||
if can_edit_state(user, draft):
|
||||
actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name))))
|
||||
|
||||
if can_edit_stream(user, draft):
|
||||
actions.append(("Change stream", urlreverse('edit_stream', kwargs=dict(name=doc.draft_name))))
|
||||
|
||||
if can_manage_shepherd_of_a_document(user, draft):
|
||||
actions.append(("Change shepherd", urlreverse('doc_managing_shepherd', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
|
||||
|
||||
if can_manage_writeup_of_a_document(user, draft):
|
||||
actions.append(("Change stream writeup", urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
|
||||
else:
|
||||
actions.append(("View writeup", urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
|
||||
|
||||
return dict(actions=actions)
|
||||
|
||||
|
||||
class StreamListNode(template.Node):
|
||||
|
||||
def __init__(self, user, var_name):
|
||||
self.user = user
|
||||
self.var_name = var_name
|
||||
|
||||
def render(self, context):
|
||||
user = self.user.resolve(context)
|
||||
streams = []
|
||||
for i in Stream.objects.all():
|
||||
if "Legacy" in i.name:
|
||||
continue
|
||||
if is_chair_of_stream(user, i):
|
||||
streams.append(i)
|
||||
context.update({self.var_name: streams})
|
||||
return ''
|
||||
|
||||
|
||||
@register.tag
|
||||
def get_user_managed_streams(parser, token):
|
||||
firstbits = token.contents.split(None, 2)
|
||||
if len(firstbits) != 3:
|
||||
raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments")
|
||||
user = parser.compile_filter(firstbits[1])
|
||||
lastbits_reversed = firstbits[2][::-1].split(None, 2)
|
||||
if lastbits_reversed[1][::-1] != 'as':
|
||||
raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must"
|
||||
" be 'as'")
|
||||
var_name = lastbits_reversed[0][::-1]
|
||||
return StreamListNode(user, var_name)
|
|
@ -19,7 +19,7 @@
|
|||
<field type="TextField" name="desc"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">3</field>
|
||||
<field type="BooleanField" name="blocking">False</field>
|
||||
<field type="BooleanField" name="blocking">True</field>
|
||||
</object>
|
||||
<object pk="block" model="name.ballotpositionname">
|
||||
<field type="CharField" name="name">Block</field>
|
||||
|
@ -85,6 +85,12 @@
|
|||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="conflrev" model="name.docrelationshipname">
|
||||
<field type="CharField" name="name">conflict reviews</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="stream-s" model="name.docremindertypename">
|
||||
<field type="CharField" name="name">Stream state should change</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
|
@ -307,6 +313,18 @@
|
|||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="maturity" model="name.doctypename">
|
||||
<field type="CharField" name="name">Maturity Change</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="conflrev" model="name.doctypename">
|
||||
<field type="CharField" name="name">Conflict Review</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="no" model="name.groupballotpositionname">
|
||||
<field type="CharField" name="name">No</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
|
@ -628,7 +646,7 @@
|
|||
<object pk="ds" model="name.stdlevelname">
|
||||
<field type="CharField" name="name">Draft Standard</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="BooleanField" name="used">False</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="ps" model="name.stdlevelname">
|
||||
|
@ -766,6 +784,9 @@
|
|||
<object pk="charter" model="doc.statetype">
|
||||
<field type="CharField" name="label">State</field>
|
||||
</object>
|
||||
<object pk="conflrev" model="doc.statetype">
|
||||
<field type="CharField" name="label">Conflict Review State</field>
|
||||
</object>
|
||||
<object pk="81" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">agenda</field>
|
||||
<field type="SlugField" name="slug">active</field>
|
||||
|
@ -838,6 +859,96 @@
|
|||
<field type="IntegerField" name="order">0</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
<object pk="89" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">needshep</field>
|
||||
<field type="CharField" name="name">Needs Shepherd</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">A conflict review has been requested, but a shepherding AD has not yet been assigned</field>
|
||||
<field type="IntegerField" name="order">1</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="90"></object><object pk="97"></object><object pk="98"></object></field>
|
||||
</object>
|
||||
<object pk="90" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">adrev</field>
|
||||
<field type="CharField" name="name">AD Review</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The sponsoring AD is reviewing the document and preparing a proposed response</field>
|
||||
<field type="IntegerField" name="order">2</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="91"></object><object pk="97"></object><object pk="98"></object></field>
|
||||
</object>
|
||||
<object pk="91" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">iesgeval</field>
|
||||
<field type="CharField" name="name">IESG Evaluation</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The IESG is considering the proposed conflict review response</field>
|
||||
<field type="IntegerField" name="order">3</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="92"></object><object pk="93"></object><object pk="94"></object><object pk="97"></object><object pk="98"></object></field>
|
||||
</object>
|
||||
<object pk="92" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">defer</field>
|
||||
<field type="CharField" name="name">IESG Evaluation - Defer</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The evaluation of the proposed conflict review response has been deferred to the next telechat</field>
|
||||
<field type="IntegerField" name="order">4</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="91"></object><object pk="93"></object><object pk="94"></object><object pk="97"></object><object pk="98"></object></field>
|
||||
</object>
|
||||
<object pk="93" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">appr-reqnopub-pend</field>
|
||||
<field type="CharField" name="name">Approved Request to Not Publish - announcement to be sent</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response</field>
|
||||
<field type="IntegerField" name="order">5</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="95"></object><object pk="97"></object></field>
|
||||
</object>
|
||||
<object pk="94" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">appr-noprob-pend</field>
|
||||
<field type="CharField" name="name">Approved No Problem - announcement to be sent</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The IESG has approved the conflict review response, but the secretariat has not yet sent the response</field>
|
||||
<field type="IntegerField" name="order">6</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="96"></object><object pk="97"></object></field>
|
||||
</object>
|
||||
<object pk="95" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">appr-reqnopub-sent</field>
|
||||
<field type="CharField" name="name">Approved Request to Not Publish - announcement sent</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester</field>
|
||||
<field type="IntegerField" name="order">7</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
<object pk="96" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">appr-noprob-sent</field>
|
||||
<field type="CharField" name="name">Approved No Problem - announcement sent</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The secretariat has delivered the IESG's approved conflict review response to the requester</field>
|
||||
<field type="IntegerField" name="order">8</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
<object pk="97" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">withdraw</field>
|
||||
<field type="CharField" name="name">Withdrawn</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The request for conflict review was withdrawn</field>
|
||||
<field type="IntegerField" name="order">9</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="89"></object></field>
|
||||
</object>
|
||||
<object pk="98" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">dead</field>
|
||||
<field type="CharField" name="name">Dead</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="TextField" name="desc">The conflict review has been abandoned</field>
|
||||
<field type="IntegerField" name="order">10</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"><object pk="89"></object></field>
|
||||
</object>
|
||||
<object pk="1" model="doc.state">
|
||||
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft</field>
|
||||
<field type="SlugField" name="slug">active</field>
|
||||
|
@ -1624,31 +1735,49 @@
|
|||
<field type="IntegerField" name="order">2</field>
|
||||
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
<object pk="5" model="doc.ballottype">
|
||||
<field to="name.doctypename" name="doc_type" rel="ManyToOneRel">conflrev</field>
|
||||
<field type="SlugField" name="slug">conflrev</field>
|
||||
<field type="CharField" name="name">Approve</field>
|
||||
<field type="TextField" name="question">Is this the correct conflict review response?</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="discuss"></object><object pk="abstain"></object><object pk="recuse"></object><object pk="norecord"></object></field>
|
||||
</object>
|
||||
<object pk="1" model="doc.ballottype">
|
||||
<field to="name.doctypename" name="doc_type" rel="ManyToOneRel">charter</field>
|
||||
<field type="SlugField" name="slug">r-extrev</field>
|
||||
<field type="CharField" name="name">Ready for external review</field>
|
||||
<field type="TextField" name="question">Is this charter ready for external review?</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
<field type="IntegerField" name="order">1</field>
|
||||
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="block"></object><object pk="abstain"></object><object pk="norecord"></object></field>
|
||||
</object>
|
||||
<object pk="2" model="doc.ballottype">
|
||||
<field to="name.doctypename" name="doc_type" rel="ManyToOneRel">charter</field>
|
||||
<field type="SlugField" name="slug">approve</field>
|
||||
<field type="CharField" name="name">Approve</field>
|
||||
<field type="TextField" name="question">Do we approve of this charter?</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="block"></object><object pk="abstain"></object><object pk="norecord"></object></field>
|
||||
</object>
|
||||
<object pk="3" model="doc.ballottype">
|
||||
<object pk="4" model="doc.ballottype">
|
||||
<field to="name.doctypename" name="doc_type" rel="ManyToOneRel">draft</field>
|
||||
<field type="SlugField" name="slug">approve</field>
|
||||
<field type="CharField" name="name">Approve</field>
|
||||
<field type="TextField" name="question"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
<field type="IntegerField" name="order">1</field>
|
||||
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="discuss"></object><object pk="abstain"></object><object pk="recuse"></object><object pk="norecord"></object></field>
|
||||
</object>
|
||||
<object pk="2" model="doc.ballottype">
|
||||
<field to="name.doctypename" name="doc_type" rel="ManyToOneRel">charter</field>
|
||||
<field type="SlugField" name="slug">r-wo-ext</field>
|
||||
<field type="CharField" name="name">Ready w/o external review</field>
|
||||
<field type="TextField" name="question">Is this charter ready for external review? Is this charter ready for approval without external review?</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">2</field>
|
||||
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="block"></object><object pk="abstain"></object><object pk="norecord"></object></field>
|
||||
</object>
|
||||
<object pk="3" model="doc.ballottype">
|
||||
<field to="name.doctypename" name="doc_type" rel="ManyToOneRel">charter</field>
|
||||
<field type="SlugField" name="slug">approve</field>
|
||||
<field type="CharField" name="name">Approve</field>
|
||||
<field type="TextField" name="question">Do we approve of this charter?</field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">3</field>
|
||||
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="block"></object><object pk="abstain"></object><object pk="norecord"></object></field>
|
||||
</object>
|
||||
</django-objects>
|
161
ietf/name/migrations/0003_add_conflict.py
Normal file
161
ietf/name/migrations/0003_add_conflict.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
# encoding: utf-8
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
|
||||
from name.models import DocTypeName
|
||||
from name.models import DocRelationshipName
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
DocTypeName(slug='conflrev',name='Conflict Review',used=True).save()
|
||||
DocRelationshipName(slug='conflrev',name='conflict reviews',used=True).save()
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
pass
|
||||
|
||||
|
||||
models = {
|
||||
'name.ballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
|
||||
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docrelationshipname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docremindertypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupBallotPositionName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.rolename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['name']
|
|
@ -161,17 +161,19 @@ class MainUrlTestCase(SimpleUrlTestCase):
|
|||
|
||||
def get_templates():
|
||||
templates = set()
|
||||
for root, dirs, files in os.walk(os.path.join(settings.BASE_DIR,"templates")):
|
||||
# Shoud we teach this to use TEMPLATE_DIRS?
|
||||
templatepath = os.path.join(settings.BASE_DIR,"templates")
|
||||
for root, dirs, files in os.walk(templatepath):
|
||||
if ".svn" in dirs:
|
||||
dirs.remove(".svn")
|
||||
last_dir = os.path.split(root)[1]
|
||||
relative_path = root[len(templatepath)+1:]
|
||||
for file in files:
|
||||
if file.endswith("~") or file.startswith("#"):
|
||||
continue
|
||||
if last_dir == "templates":
|
||||
if relative_path == "":
|
||||
templates.add(file)
|
||||
else:
|
||||
templates.add(os.path.join(last_dir, file))
|
||||
templates.add(os.path.join(relative_path, file))
|
||||
return templates
|
||||
|
||||
class TemplateCoverageTestCase(unittest.TestCase):
|
||||
|
|
|
@ -195,6 +195,7 @@ INTERNET_DRAFT_PDF_PATH = '/a/www/ietf-datatracker/pdf/'
|
|||
RFC_PATH = '/a/www/ietf-ftp/rfc/'
|
||||
CHARTER_PATH = '/a/www/ietf-ftp/charters/'
|
||||
CHARTER_TXT_URL = 'http://www.ietf.org/charter/'
|
||||
CONFLICT_REVIEW_PATH = '/a/www/ietf-ftp/conflict_reviews'
|
||||
AGENDA_PATH = '/a/www/www6s/proceedings/'
|
||||
AGENDA_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/agenda/%(wg)s.%(ext)s'
|
||||
MINUTES_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/minutes/%(wg)s.%(ext)s'
|
||||
|
|
|
@ -57,16 +57,16 @@ class UploadForm(forms.Form):
|
|||
|
||||
if now.date() >= first_cut_off and now.date() < second_cut_off: # We are in the first_cut_off
|
||||
if now.date() == first_cut_off and now.hour < settings.CUTOFF_HOUR:
|
||||
self.cutoff_warning = 'The pre-meeting cutoff date for new documents (i.e., version -00 Internet-Drafts) is %s, at %02sh UTC. After that, you will not be able to submit a new document until %s, at %sh UTC' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, )
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) is %s, at %02sh UTC. After that, you will not be able to submit a new document until %s, at %sh UTC' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, )
|
||||
else: # No 00 version allowed
|
||||
self.cutoff_warning = 'The pre-meeting cutoff date for new documents (i.e., version -00 Internet-Drafts) was %s at %sh UTC. You will not be able to submit a new document until %s, at %sh UTC.<br>You can still submit a version -01 or higher Internet-Draft until %sh UTC, %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, second_cut_off, )
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %sh UTC. You will not be able to submit a new document until %s, at %sh UTC.<br>You can still submit a version -01 or higher Internet-Draft until %sh UTC, %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, second_cut_off, )
|
||||
self.in_first_cut_off = True
|
||||
elif now.date() >= second_cut_off and now.date() < ietf_monday:
|
||||
if now.date() == second_cut_off and now.hour < settings.CUTOFF_HOUR: # We are in the first_cut_off yet
|
||||
self.cutoff_warning = 'The pre-meeting cutoff date for new documents (i.e., version -00 Internet-Drafts) was %s at %02sh UTC. You will not be able to submit a new document until %s, at %02sh UTC.<br>The I-D submission tool will be shut down at %02sh UTC today, and reopened at %02sh UTC on %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %02sh UTC. You will not be able to submit a new document until %s, at %02sh UTC.<br>The I-D submission tool will be shut down at %02sh UTC today, and reopened at %02sh UTC on %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.in_first_cut_off = True
|
||||
else: # Completely shut down of the tool
|
||||
self.cutoff_warning = 'The cut off time for the I-D submission was %02dh UTC, %s.<br>The I-D submission tool will be reopened at %02dh UTC, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s.<br>The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.shutdown = True
|
||||
|
||||
def __unicode__(self):
|
||||
|
|
30
ietf/templates/doc/conflict_review/approval_text.txt
Normal file
30
ietf/templates/doc/conflict_review/approval_text.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% load mail_filters %}{% autoescape off %}From: The IESG <iesg-secretary@ietf.org>
|
||||
To: {{ review.notify }}
|
||||
Cc: The IESG <iesg@ietf.org>, <iana@iana.org>, <ietf-announce@ietf.org>
|
||||
Subject: Results of IETF-conflict review for {{conflictdoc.canonical_name}}-{{conflictdoc.rev}}
|
||||
|
||||
{% filter wordwrap:73 %}The IESG has completed a review of {{conflictdoc.canonical_name}}-{{conflictdoc.rev}} consistent with RFC5742.
|
||||
|
||||
{% if review.get_state_slug == 'appr-reqnopub-pend' %}
|
||||
The IESG recommends that '{{ conflictdoc.title }}' {{ conflictdoc.file_tag|safe }} NOT be published as {{ conflictdoc|std_level_prompt_with_article }}.
|
||||
{% else %}
|
||||
The IESG has no problem with the publication of '{{ conflictdoc.title }}' {{ conflictdoc.file_tag|safe }} as {{ conflictdoc|std_level_prompt_with_article }}.
|
||||
{% endif %}
|
||||
|
||||
The IESG would also like the {{receiver}} to review the comments in the datatracker related to this document and determine whether or not they merit incorporation into the document. Comments may exist in both the ballot and the history log.
|
||||
|
||||
The IESG review is documented at:
|
||||
{{review_url}}
|
||||
|
||||
A URL of the reviewed Internet Draft is:
|
||||
{{conflictdoc_url}}
|
||||
|
||||
The process for such documents is described at
|
||||
http://www.rfc-editor.org/indsubs.html
|
||||
|
||||
Thank you,
|
||||
|
||||
The IESG Secretary
|
||||
|
||||
{% endfilter %}
|
||||
{% endautoescape %}
|
39
ietf/templates/doc/conflict_review/approve.html
Normal file
39
ietf/templates/doc/conflict_review/approve.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Approve {{ review.canonical_name }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form #id_announcement_text {
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
width: 800px;
|
||||
height: 400px;
|
||||
border: 1px solid #bbb;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Approve {{ review.canonical_name }}</h1>
|
||||
|
||||
<form class="approve" action="" method="POST">
|
||||
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<td>
|
||||
<div>{{ field.label_tag }}:</div>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=review.name %}">Back</a>
|
||||
<input type="submit" value="Send out the announcement and close the ballot"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
39
ietf/templates/doc/conflict_review/change_ad.html
Normal file
39
ietf/templates/doc/conflict_review/change_ad.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
Change the shepherding AD for the conflict review of {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change the shepherding AD for the conflict review of {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}</h1>
|
||||
|
||||
<form class="edit-info" action="" enctype="multipart/form-data" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=review.canonical_name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
46
ietf/templates/doc/conflict_review/change_state.html
Normal file
46
ietf/templates/doc/conflict_review/change_state.html
Normal file
|
@ -0,0 +1,46 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Change State: {{doc.title}}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.change-state select {
|
||||
width: 22em;
|
||||
}
|
||||
|
||||
#id_message, #id_comment {
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
form.change-state .actions {
|
||||
text-align: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change State: {{doc.title}}</h1>
|
||||
|
||||
<p class="helptext">For help on the states, see the <a href="{% url help_conflict_review_states %}">state table</a>.</p>
|
||||
|
||||
<form class="change-state" action="" method="post">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
<a href="{% url doc_view name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
29
ietf/templates/doc/conflict_review/edit_telechat_date.html
Normal file
29
ietf/templates/doc/conflict_review/edit_telechat_date.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Set Telechat Date for {{ doc.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.telechat-date td.actions {
|
||||
padding-top: 1em;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load ietf_filters %}
|
||||
<h1>Set Telechat Date for {{ doc.name }}</h1>
|
||||
|
||||
<form class="telechat-date" action="" method="POST">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
42
ietf/templates/doc/conflict_review/notify.html
Normal file
42
ietf/templates/doc/conflict_review/notify.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
form.edit-info #id_notify {
|
||||
width: 600px;
|
||||
}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
Edit notification addresses for the conflict review of {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Edit notification addresses for the conflict review of {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}</h1>
|
||||
|
||||
<form class="edit-info" action="" enctype="multipart/form-data" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=review.canonical_name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
17
ietf/templates/doc/conflict_review/review_choices.txt
Normal file
17
ietf/templates/doc/conflict_review/review_choices.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
[Edit this page to chose one of the following five responses, adding the information requested in <> as necessary. Delete these instructions.]
|
||||
|
||||
The IESG has concluded that there is no conflict between this document and IETF work.
|
||||
|
||||
The IESG has concluded that this work is related to IETF work done in WG <X>, but this relationship does not prevent publishing.
|
||||
|
||||
The IESG has concluded that publication could potentially disrupt the IETF work done in WG <X> and recommends not publishing the document at this time.
|
||||
|
||||
The IESG has concluded that this document violates IETF procedures for <Y> and should therefore not be published without IETF review and IESG approval.
|
||||
|
||||
The IESG has concluded that this document extends an IETF protocol in a way that requires IETF review and should therefore not be published without IETF review and IESG approval.
|
||||
|
||||
[In the exceptional case that an IESG note is needed, add the following section (otherwise delete it): Delete these instructions.]
|
||||
|
||||
Additionally, the IESG requests the following note be added to the document if it is published:
|
||||
|
||||
<IESG NOTE GOES HERE>
|
44
ietf/templates/doc/conflict_review/start.html
Normal file
44
ietf/templates/doc/conflict_review/start.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Begin IETF conflict review : {{doc_to_review.canonical_name}}-{{doc_to_review.rev}}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.start-conflict-review #id_notify {
|
||||
width: 600px;
|
||||
}
|
||||
form.start-conflict-review .actions {
|
||||
padding-top: 20px;
|
||||
}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Begin IETF conflict review for {{doc_to_review.canonical_name}}-{{doc_to_review.rev}}</h1>
|
||||
|
||||
<p class="helptext">For help on the initial state choice, see the <a href="{% url help_conflict_review_states %}">state table</a>.</p>
|
||||
|
||||
<form class="start-conflict-review" action="" method="post">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
<a href="{% url doc_view name=doc_to_review.name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
41
ietf/templates/doc/conflict_review/submit.html
Normal file
41
ietf/templates/doc/conflict_review/submit.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
form #id_content {
|
||||
width: 40em;
|
||||
height: 450px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
Edit conflict review for {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Edit conflict review for {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}</h1>
|
||||
|
||||
<p>The text will be submitted as <strong>{{ review.canonical_name }}-{{ next_rev }}</strong></p>
|
||||
<form class="edit-info" action="" enctype="multipart/form-data" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=review.canonical_name %}">Back</a>
|
||||
<input type="submit" name="reset_text" value="Reset to Template Text"/>
|
||||
<input type="submit" name="submit_response" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -1,7 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2011, All Rights Reserved #}
|
||||
|
||||
{% block title %}Charter States{% endblock %}
|
||||
{% block title %}{{ title }} States{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
.state_column {
|
||||
|
@ -11,7 +10,7 @@
|
|||
|
||||
{% block content %}
|
||||
|
||||
<h1>Charter States</h1>
|
||||
<h1>{{ title }} States</h1>
|
||||
|
||||
<table class="ietf-table">
|
||||
|
54
ietf/templates/doc/status_columns.html
Normal file
54
ietf/templates/doc/status_columns.html
Normal file
|
@ -0,0 +1,54 @@
|
|||
{% comment %}
|
||||
Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of the Nokia Corporation and/or its
|
||||
subsidiary(-ies) nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{% endcomment %}
|
||||
{% load ietf_filters ietf_streams_redesign %}{% load ballot_icon_redesign %}{% load doc_states %}
|
||||
<td class="status">
|
||||
{{ doc.friendly_state|safe }} {% if not doc.rfc %}{{ doc|state_age_colored|safe }}{% endif %}
|
||||
{% if not hide_telechat_date %}{% if doc.telechat_date %}<br/>IESG Telechat: {{ doc.telechat_date }}{% endif %}{% endif %}
|
||||
|
||||
{% block extra_status %}{% endblock %}
|
||||
{% if doc.rfc %}
|
||||
{%comment%}
|
||||
TODO: Port this block to Document when something that shows RFCs uses this template
|
||||
{% if doc.rfc.obsoleted_by %}<br />Obsoleted by {{ doc.rfc.obsoleted_by|urlize_ietf_docs }}{%endif %}
|
||||
{% if doc.rfc.updated_by %}<br />Updated by {{ doc.rfc.updated_by|urlize_ietf_docs }}{%endif %}
|
||||
{% if doc.rfc.has_errata %}<br /><a href="http://www.rfc-editor.org/errata_search.php?rfc={{doc.rfc.rfc_number}}" rel="nofollow">Errata</a>{% endif %}
|
||||
{%endcomment%}
|
||||
{% else %}{# not rfc #}
|
||||
{% if doc|rfc_editor_state %}<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{doc.name}}">{{ doc|rfc_editor_state|escape }}</a>{% endif %}
|
||||
{% stream_state doc %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="ballot">
|
||||
{% ballot_icon doc %}
|
||||
</td>
|
|
@ -1,3 +1,3 @@
|
|||
{# Copyright The IETF Trust 2008, All Rights Reserved #}
|
||||
{{ obj.document.title|escape }}<br>
|
||||
{{ obj.document.abstract|escape|truncatewords:50 }}
|
||||
{{ obj.title|escape }}<br>
|
||||
{{ obj.abstract|escape|truncatewords:50 }}
|
||||
|
|
39
ietf/templates/idrfc/change_ad.html
Normal file
39
ietf/templates/idrfc/change_ad.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
Change the shepherding AD for {{ doc.name }}-{{ doc.rev }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change the shepherding AD for {{ doc.name }}-{{ doc.rev }}</h1>
|
||||
|
||||
<form class="edit-info" action="" enctype="multipart/form-data" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
29
ietf/templates/idrfc/change_intended_status.html
Normal file
29
ietf/templates/idrfc/change_intended_status.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Change intended status for {{ doc }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.change-intended-status select {
|
||||
width: 22em;
|
||||
}
|
||||
form.change-intended-status .actions {
|
||||
text-align: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change intended status for {{ doc }}</h1>
|
||||
|
||||
<form class="change-intended-status" action="" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
<a href="{{ doc.get_absolute_url }}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
43
ietf/templates/idrfc/change_notify.html
Normal file
43
ietf/templates/idrfc/change_notify.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
form.edit-info #id_notify {
|
||||
width: 600px;
|
||||
}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
Edit notification addresses for {{ doc.canonical_name }}-{{ doc.rev }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Edit notification addresses for {{ doc.canonical_name }}-{{ doc.rev }}</h1>
|
||||
|
||||
<form class="edit-info" action="" enctype="multipart/form-data" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=doc.canonical_name %}">Back</a>
|
||||
<input type="submit" name="save_addresses" value="Save" />
|
||||
<input type="submit" name="regenerate_addresses" value="Regenerate Address List" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
29
ietf/templates/idrfc/change_stream.html
Normal file
29
ietf/templates/idrfc/change_stream.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Change stream for {{ doc }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.change-stream select {
|
||||
width: 22em;
|
||||
}
|
||||
form.change-stream .actions {
|
||||
text-align: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change stream for {{ doc }}</h1>
|
||||
|
||||
<form class="change-stream" action="" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
<a href="{{ doc.get_absolute_url }}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -5,7 +5,7 @@
|
|||
{% block content %}
|
||||
<h1>Defer ballot for {{ doc }}</h1>
|
||||
|
||||
<form action="" method="POST">
|
||||
<form class="defer" action="" method="POST">
|
||||
<p>Defer the ballot for {{ doc.file_tag }}?</p>
|
||||
|
||||
<p>The ballot will then be on the IESG agenda of {{ telechat_date }}.</p>
|
||||
|
|
|
@ -33,15 +33,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
-->{% endcomment %}
|
||||
{% load ietf_filters %}
|
||||
<table class="ietf-ballot"><tr valign="top"><td class="left">
|
||||
|
||||
{% if doc_ballot_edit_button and user|in_group:"Area_Director,Secretariat" %}
|
||||
{% if user|in_group:"Area_Director" %}
|
||||
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url idrfc.views_ballot.edit_position name=doc.name,ballot_id=ballot.pk %}">Edit position</a></span></span></div>
|
||||
{% endif %}
|
||||
|
||||
{% if bw.was_deferred %}
|
||||
{% if doc.active_defer_event %}
|
||||
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_undefer_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url doc_undefer_ballot name=doc.name,ballot_id=ballot.pk %}">Undefer ballot</a></span></span></div>
|
||||
<div style="margin-top:8px; margin-bottom:8px;">Ballot deferred by {{ bw.deferred_by }} on {{ bw.deferred_date }}.</div>
|
||||
<div style="margin-top:8px; margin-bottom:8px;">Ballot deferred by {{ doc.active_defer_event.by }} on {{ doc.active_defer_event.time|date:"Y-m-d" }}.</div>
|
||||
{% else %}
|
||||
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_defer_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url doc_defer_ballot name=doc.name,ballot_id=ballot.pk %}">Defer ballot</a></span></span></div>
|
||||
{% endif %}
|
||||
|
|
|
@ -49,6 +49,21 @@ div.diffTool { padding: 8px 4px; margin: 8px 0;}
|
|||
.m_hdr, .m_ftr { color: #808080; }
|
||||
.m_ftr { border-bottom: 1px solid #a0a0a0; }
|
||||
.m_h { font-family: arial; font-weight:bold;}
|
||||
|
||||
a.editlink {
|
||||
background-image: url("/images/pencil.png");
|
||||
background-size:10px;
|
||||
background-position: right top;
|
||||
background-attachment: scroll;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
a.editlink:link {text-decoration:none; color:inherit;}
|
||||
a.editlink:visited {text-decoration:none; color:inherit;}
|
||||
a.editlink:hover {text-decoration:underline;}
|
||||
a.editlink:active {text-decoration:underline;}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
{% extends "idrfc/doc_main.html" %}
|
||||
{% comment %}
|
||||
Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of the Nokia Corporation and/or its
|
||||
subsidiary(-ies) nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{% endcomment %}
|
||||
{% load ietf_filters ietf_streams %}
|
||||
|
||||
{% block title %}{{ doc.draft_name_and_revision }}{% endblock %}
|
||||
{% block doc_meta_description %}{{ doc.title }} ({{info.type}}; {{doc.publication_date|date:"Y"}}){% endblock %}
|
||||
{% block doc_h1 %}{{ doc.title|escape }}<br/>{{ doc.draft_name_and_revision }}{% endblock %}
|
||||
|
||||
{% block doc_metatable %}
|
||||
<tr><td>Document Stream:</td><td> {{ stream_info.stream.name|default:"No stream defined" }}</td></tr>
|
||||
<tr><td>I-D availability status:</td><td> {{ doc.draft_status }}
|
||||
{% ifequal doc.draft_status "Expired" %}
|
||||
{% if doc.resurrect_requested_by %}(resurrect requested by {{ doc.resurrect_requested_by }}){% endif %}
|
||||
{% endifequal %}
|
||||
{% with doc.replaces as r %}{% if r %}<br />Replaces {% filter urlize_ietf_docs %}{{ r|join:", "}}{% endfilter %}{% endif %}{% endwith %}
|
||||
</td></tr>
|
||||
<tr><td>Last updated:</td><td> {{ doc.publication_date|default:"(data missing)" }}</td></tr>
|
||||
<tr><td>IETF WG state:</td><td>{{ stream_info.state.name }} ({{ stream_info.streamed.get_group }})
|
||||
{% if stream_info.tags %}<br /><i>{% for tag in stream_info.tags %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}
|
||||
</td></tr>
|
||||
<tr><td>Intended RFC status:</td><td>{% if doc.in_ietf_process %}{{ doc.ietf_process.intended_maturity_level|default:"-" }}{% else %}-{%endif%}</td></tr>
|
||||
<tr><td>Document shepherd:</td><td>{{ stream_info.shepherd }}</td></tr>
|
||||
<tr><td><a href="/idtracker/help/state/">IESG State</>:</td><td> {{ doc.friendly_state|safe }}
|
||||
{% if doc.rfc_editor_state %}<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{doc.draft_name}}">{{ doc.rfc_editor_state|escape }}</a>{% endif %}
|
||||
{% if doc.in_ietf_process %}{% if doc.ietf_process.telechat_date %}<br/>On agenda of {{ doc.ietf_process.telechat_date }} IESG telechat {% if doc.ietf_process.telechat_returning_item %} (returning item){%endif%}{%endif%}{% if doc.ietf_process.has_active_iesg_ballot %}<br/><i>({{ doc.ietf_process.iesg_ballot_needed }})</i>{%endif%}{%endif%}
|
||||
</td></tr>
|
||||
<tr><td>Submission:</td><td>{{ doc.submission }}</td></tr>
|
||||
<tr><td>Responsible AD:</td><td>{% if doc.in_ietf_process %}{{ doc.ietf_process.ad_name|default:"-"|escape }}{%else%}-{%endif%}</td></tr>
|
||||
{% if doc.in_ietf_process and doc.ietf_process.iesg_note %}<tr><td>IESG Note:</td><td>{{ doc.ietf_process.iesg_note|format_textarea|safe }}</td></tr>{% endif %}
|
||||
{% if user|in_group:"Area_Director,Secretariat" %}
|
||||
{% if doc.in_ietf_process %}<tr><td>Send notices to:</td><td>{{ doc.ietf_process.state_change_notice_to}}</td></tr>{% endif %}
|
||||
{% endif %}{# if user|in_group:... #}
|
||||
|
||||
{% ifequal doc.draft_status "Active" %}
|
||||
<tr><td>Other versions:</td><td>
|
||||
<a href="http://www.ietf.org/id/{{doc.draft_name_and_revision}}.txt">plain text</a>,
|
||||
{% for ext in doc.file_types %}
|
||||
{% ifnotequal ext ".txt" %}
|
||||
<a href="http://www.ietf.org/id/{{doc.draft_name_and_revision}}{{ext}}">{{ext|cut:"."}}</a>,
|
||||
{% endifnotequal %}
|
||||
{% endfor %}
|
||||
{% if not info.has_pdf %}
|
||||
<a href="http://tools.ietf.org/pdf/{{doc.draft_name_and_revision}}.pdf">pdf</a>,
|
||||
{% endif %}
|
||||
<a href="http://tools.ietf.org/html/{{doc.draft_name_and_revision}}">html</a>
|
||||
</td></tr>
|
||||
{% endifequal %}
|
||||
{% endblock doc_metatable %}
|
||||
|
||||
{% block doc_metalinks %}
|
||||
{% edit_actions doc %}
|
||||
<div>
|
||||
<a href="mailto:{{doc.draft_name}}@tools.ietf.org?subject=Mail%20regarding%20{{doc.draft_name}}" rel="nofollow">Email Authors</a>
|
||||
| <a href="/ipr/search/?option=document_search&id_document_tag={{doc.tracker_id}}" rel="nofollow">IPR Disclosures</a>
|
||||
| <a href="http://www.fenron.net/~fenner/ietf/deps/index.cgi?dep={{doc.draft_name}}" rel="nofollow">Dependencies to this draft</a>
|
||||
| <a href="http://tools.ietf.org/idnits?url=http://tools.ietf.org/id/{{doc.draft_name_and_revision}}.txt" rel="nofollow" target="_blank">Check nits</a>
|
||||
{% if doc.in_ietf_process %}| <a href="/feed/comments/{% if info.is_rfc %}rfc{{doc.rfc_number}}{% else %}{{doc.draft_name}}{% endif %}/">Comments feed</a>{% endif %}
|
||||
| <a href="http://www.google.com/search?as_q={{doc.draft_name}}&as_sitesearch={{ doc.search_archive }}" rel="nofollow" target="_blank">Search Mailing Lists</a>
|
||||
{% if user|in_group:"Area_Director" %}
|
||||
| <a href="https://www.iesg.org/bin/c5i?mid=6&rid=77&target={{doc.draft_name}}" rel="nofollow" target="_blank">Search Mailing Lists (ARO)</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user|in_group:"Area_Director,Secretariat" %}
|
||||
<div>
|
||||
{% if doc.in_ietf_process %}
|
||||
<a href="{% url doc_ballot_lastcall name=doc.draft_name %}">Last Call Text</a>
|
||||
| <a href="{% url doc_ballot_writeupnotes name=doc.draft_name %}">Ballot Text</a>
|
||||
| <a href="{% url doc_ballot_approvaltext name=doc.draft_name %}">Announcement Text</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block doc_text1 %}
|
||||
{% ifequal doc.draft_status "Active" %}
|
||||
<div class="markup_draft">
|
||||
{{ content1|safe }}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>This Internet-Draft is no longer active. Unofficial copies of old Internet-Drafts can be found here:<br/>
|
||||
<a href="http://tools.ietf.org/id/{{doc.draft_name}}">http://tools.ietf.org/id/{{doc.draft_name}}</a>.</p>
|
||||
|
||||
<p style="max-width: 400px;"><b>Abstract:</b><br/> {{ doc.abstract|escape }}</p>
|
||||
|
||||
<p><b>Authors:</b><br/>
|
||||
{% for author in doc.authors.all %}
|
||||
|
||||
{% if author.email %}
|
||||
<a href="mailto:{{ author.email }}">{{ author.person }} <{{author.email}}></a>
|
||||
{% else %}
|
||||
{% if author.person %}
|
||||
{{ author.person }}
|
||||
{% else %}
|
||||
Missing author info #{{ author.person_id }}
|
||||
{% endif %}
|
||||
{% endif %}<br />
|
||||
|
||||
{% endfor %}</p>
|
||||
|
||||
<p>(Note: The e-mail addresses provided for the authors of this Internet-Draft may no longer be valid)</p>
|
||||
|
||||
{% endifequal %}
|
||||
{% endblock %}{# doc_text1 #}
|
||||
|
||||
{% block doc_text2 %}
|
||||
{% ifequal doc.draft_status "Active" %}
|
||||
<div class="markup_draft">
|
||||
{{ content2|safe }}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% endblock %} {# doc_text2 #}
|
|
@ -56,10 +56,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
<span id="doc_resurrect_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url doc_resurrect name=doc.draft_name %}">Resurrect</a></span></span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if doc.in_ietf_process %}
|
||||
<span id="doc_edit_info_button" class="yui-button yui-link-button" style="margin-left:2px;">{% url doc_edit_info name=doc.draft_name as doc_edit_url %}{% if doc_edit_url %}<span class="first-child"><a href="{{doc_edit_url}}">Edit</a></span>{% endif %}</span>
|
||||
{% if stream_info.stream.name == 'ISE' or stream_info.stream.name == 'IRTF' %}
|
||||
{% if user|in_group:"Secretariat" and not info.conflict_reviews %}
|
||||
<span id="doc_conflict_review_button" class="yui-button yui-link-button" style="margin-left:2px;">{% url conflict_review_start name=doc.draft_name as start_review_url %}{% if start_review_url %}<span class="first-child"><a href="{{start_review_url}}">Begin IETF Conflict Review</a></span>{% endif %}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span id="doc_add_button" class="yui-button yui-link-button" style="margin-left:2px;">{% url doc_edit_info name=doc.draft_name as doc_edit_url %}{% if doc_edit_url %}<span class="first-child"><a href="{{doc_edit_url}}">Add</a></span>{% endif %}</span>
|
||||
{% if stream_info.stream.name == 'IETF'%}{%if not doc.in_ietf_process %}
|
||||
<span id="doc_add_button" class="yui-button yui-link-button" style="margin-left:2px;">{% url doc_edit_info name=doc.draft_name as doc_edit_url %}{% if doc_edit_url %}<span class="first-child"><a href="{{doc_edit_url}}">Begin IESG Processing</a></span>{% endif %}</span>
|
||||
{% endif %}{% endif %}
|
||||
{% endif %}
|
||||
{% endifequal %}
|
||||
|
||||
|
|
|
@ -41,13 +41,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{% if doc.resurrect_requested_by %}(resurrect requested by {{ doc.resurrect_requested_by }}){% endif %}
|
||||
{% endifequal %} </td>
|
||||
|
||||
<tr><td>Document Stream:</td><td> {{ stream_info.stream.name|default:"No stream defined" }}</td></tr>
|
||||
<tr><td>Document Stream:</td><td>
|
||||
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
|
||||
{% if add_link %}<a class="editlink" href="{% url doc_change_stream name=doc.draft_name %}">{% endif %}
|
||||
{% if stream_info.stream %}{{ stream_info.stream.name|default:"No stream defined" }}{% else %}No stream defined{% endif %}
|
||||
{% if add_link %}</a>{% endif %}
|
||||
{% endwith %}
|
||||
</td></tr>
|
||||
|
||||
<tr><td>Last updated:</td><td> {{ doc.publication_date|default:"(data missing)" }}</td></tr>
|
||||
|
||||
{% with doc.replaces as r %}{% if r %}<tr><td>Replaces:</td><td> {% filter urlize_ietf_docs %}{{ r|join:", "}}{% endfilter %}</td></tr>{% endif %}{% endwith %}
|
||||
|
||||
<tr><td>Intended RFC status:</td><td>{% if doc.in_ietf_process %}{{ doc.ietf_process.intended_maturity_level|default:"-" }}{% else %}-{%endif%}</td></tr>
|
||||
{% with info.conflict_reviews as r %}{% if r %}<tr><td>IETF Conflict Review:</td><td> {% filter urlize_ietf_docs %}{{ r|join:","}}{% endfilter %}</td></tr>{% endif %} {% endwith %}
|
||||
|
||||
<tr><td>Intended RFC status:</td><td>
|
||||
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
|
||||
{% if add_link %}<a class="editlink" href="{% url doc_change_intended_status name=doc.draft_name %}">{% endif %}
|
||||
{{ doc.underlying_document.intended_std_level|default:"-" }}
|
||||
{% if add_link %}</a>{% endif %}
|
||||
{% endwith %}
|
||||
</td></tr>
|
||||
|
||||
<tr><td>Other versions:</td>
|
||||
{% ifequal doc.draft_status "Active" %}
|
||||
|
@ -109,7 +123,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
<tr><td><a href="/idtracker/help/state/">IESG State</>:</td><td>
|
||||
{% if doc.in_ietf_process and user|in_group:"Area_Director,Secretariat" %}
|
||||
<a href="{% url doc_change_state name=doc.draft_name %}"> {{ doc.friendly_state|safe }} </a>
|
||||
<a class="editlink" href="{% url doc_change_state name=doc.draft_name %}"> {{ doc.friendly_state|safe }} </a>
|
||||
{% else %}
|
||||
{{ doc.friendly_state|safe }}
|
||||
{% endif %}
|
||||
|
@ -117,14 +131,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{% ifequal doc.draft_status "Expired" %}
|
||||
{% if doc.resurrect_requested_by %}(resurrect requested by {{ doc.resurrect_requested_by }}){% endif %}
|
||||
{% endifequal %}
|
||||
{% if doc.in_ietf_process %}{% if doc.ietf_process.telechat_date %}<br/>On agenda of {{ doc.ietf_process.telechat_date }} IESG telechat {% if doc.ietf_process.telechat_returning_item %} (returning item){%endif%}{%endif%}{% if doc.ietf_process.has_active_iesg_ballot %}<br/><i>({{ doc.ietf_process.iesg_ballot_needed }})</i>{%endif%}{%endif%}
|
||||
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
|
||||
{% if add_link %}<a class="editlink" href="{% url doc_change_telechat_date name=doc.draft_name %}">{% endif %}
|
||||
{% if doc.underlying_document.telechat_date %}<br/>On agenda of {{ doc.underlying_document.telechat_date }} IESG telechat {% if doc.underlying_document.returning_item %} (returning item){% endif %}{% else %}{%if add_link %}<br/>Not on an upcoming telechat agenda{% endif %}{% endif %}
|
||||
{% if add_link %}</a>{% endif %}
|
||||
{% if doc.ietf_process.has_active_iesg_ballot %}<br/><i>({{ doc.ietf_process.iesg_ballot_needed }})</i>{%endif%}
|
||||
{% endwith %}
|
||||
</td></tr>
|
||||
{# <tr><td>Submission:</td><td>{{ doc.submission|safe }}</td></tr> #}
|
||||
<tr><td>Responsible AD:</td><td>{% if doc.in_ietf_process %}{{ doc.ietf_process.ad_name|default:"-"|escape }}{%else%}-{%endif%}</td></tr>
|
||||
{% if doc.in_ietf_process and doc.ietf_process.iesg_note %}<tr><td>IESG Note:</td><td>{{ doc.ietf_process.iesg_note|format_textarea|safe }}</td></tr>{% endif %}
|
||||
{% if user|in_group:"Area_Director,Secretariat" %}
|
||||
{% if doc.in_ietf_process %}<tr><td>Send notices to:</td><td>{{ doc.ietf_process.state_change_notice_to}}</td></tr>{% endif %}
|
||||
{% endif %}{# if user|in_group:... #}
|
||||
<tr><td>Responsible AD:</td><td>
|
||||
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
|
||||
{% if add_link %}<a class="editlink" href="{% url doc_change_ad name=doc.draft_name %}">{% endif %}
|
||||
{% if doc.in_ietf_process %}{{ doc.ietf_process.ad_name|default:"-"|escape }}{%else%}-{%endif%}
|
||||
{% if add_link %}</a>{% endif %}
|
||||
{% endwith %}
|
||||
</td></tr>
|
||||
{% if doc.in_ietf_process and doc.ietf_process.iesg_note %}<tr><td>
|
||||
IESG Note:</td><td>
|
||||
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
|
||||
{% if add_link %}<a class="editlink" href="{% url doc_change_iesg_note name=doc.draft_name %}">{% endif %}
|
||||
{{ doc.ietf_process.iesg_note|format_textarea|safe }}
|
||||
{% if add_link %}</a>{% endif %}
|
||||
{% endwith %}
|
||||
</td></tr>{% endif %}
|
||||
<tr><td>Send notices to:</td><td>
|
||||
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
|
||||
{% if add_link %}<a class="editlink" href="{% url doc_change_notify name=doc.draft_name %}">{% endif %}
|
||||
{{ doc.underlying_document.notify|default:"No addresses provided"}}
|
||||
{% if add_link %}</a>{% endif %}
|
||||
{% endwith %}
|
||||
</td></tr>
|
||||
|
||||
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<div class="action"><a href="{% url idrfc.views_ballot.edit_position name=doc.name,ballot_id=ballot.pk %}">Edit position</a></div>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.type_id == "draft" %}
|
||||
{% if doc.type_id == "draft" or doc.type_id == "conflrev" %}
|
||||
<div class="action">
|
||||
{% if deferred %}
|
||||
<a href="{% url doc_undefer_ballot name=doc.name %}">Undefer ballot</a>
|
||||
|
|
116
ietf/templates/idrfc/document_conflict_review.html
Normal file
116
ietf/templates/idrfc/document_conflict_review.html
Normal file
|
@ -0,0 +1,116 @@
|
|||
{% extends "idrfc/doc_main.html" %}
|
||||
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block title %}{{ doc.canonical_name }}-{{ doc.rev }}{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" type="text/css" href="/css/doc.css"></link>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ top|safe }}
|
||||
|
||||
<div class="snapshots">
|
||||
Versions:
|
||||
<span class="revisions">
|
||||
{% for rev in revisions %}
|
||||
<a {% if rev != doc.rev %}href="{% url doc_view name=doc.name %}{% if not forloop.last %}{{ rev }}/{% endif %}"{% endif %}>{{ rev }}</a>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="ietf-box metabox">
|
||||
<div>
|
||||
{% if snapshot %}Snapshot of{% endif %}
|
||||
{% if doc.get_state_slug not in approved_states %}Proposed{% endif %}
|
||||
IESG Conflict Review for the {{conflictdoc.stream}} document: <a href="{% url doc_view name=conflictdoc.canonical_name %}">{{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}</a>
|
||||
</div>
|
||||
|
||||
<table id="metatable" width="100%">
|
||||
<tr>
|
||||
<td><a href="/doc/help/state/conflict-review/">Conflict Review State</a>:</td>
|
||||
<td>
|
||||
<div>
|
||||
<a title="{{ doc.get_state.desc }}"{% if not snapshot and user|has_role:"Area Director,Secretariat" %} class="editlink" href="{% url conflict_review_change_state name=doc.name %}"{% endif %}>{{ doc.get_state.name }}</a>
|
||||
|
||||
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
|
||||
|
||||
{% if request.user|has_role:"Secretariat" %}{% if doc.get_state_slug = 'appr-reqnopub-pend' or doc.get_state_slug = 'appr-noprob-pend' %}
|
||||
- <a href="{% url conflict_review_approve name=doc.name %}">Approve conflict review</a>
|
||||
{% endif %}{% endif %}
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if not snapshot %}
|
||||
<div class="telechat">
|
||||
<a {% if not snapshot and user|has_role:"Area Director,Secretariat" and doc.get_state_slug not in approved_states %}
|
||||
class="editlink" href="{% url conflict_review_telechat_date name=doc.name %}"
|
||||
{%endif%} >
|
||||
{% if not telechat %}Not on agenda of an IESG telechat{% else %}On agenda of {{ telechat.telechat_date|date:"Y-m-d" }} IESG telechat{% if doc.returning_item %} (returning item){% endif %}{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if ballot_summary %}
|
||||
<div class="ballot-summary">
|
||||
({{ ballot_summary }})
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Shepherding AD:</td>
|
||||
<td>
|
||||
<a {% if not snapshot and user|has_role:"Area Director,Secretariat" and doc.get_state_slug not in approved_states %}
|
||||
class="editlink" href="{% url conflict_review_ad name=doc.name %}"
|
||||
{% endif %}
|
||||
>
|
||||
{{doc.ad}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Send notices to:</td>
|
||||
<td>
|
||||
<a {% if not snapshot and user|has_role:"Area Director,Secretariat" and doc.get_state_slug not in approved_states %}
|
||||
class="editlink" href="{% url conflict_review_notices name=doc.name %}"
|
||||
{% endif %}
|
||||
>
|
||||
{{doc.notify}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
|
||||
|
||||
<tr><td>Last updated:</td><td> {{ doc.time|date:"Y-m-d" }}</td></tr>
|
||||
|
||||
{% comment %}
|
||||
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
|
||||
{% endcomment %}
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<h3>Conflict Review for {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
|
||||
|
||||
{% if not snapshot and user|has_role:"Area Director,Secretariat" and doc.get_state_slug != 'apprsent' %}
|
||||
<a class="edit" href="{% url conflict_review_submit name=doc.name %}">Change conflict review text</a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
{% if doc.rev %}
|
||||
<div class="markup_draft">
|
||||
{{ content|fill:"80"|safe|linebreaksbr|keep_spacing|sanitize_html|safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
34
ietf/templates/idrfc/edit_iesg_note.html
Normal file
34
ietf/templates/idrfc/edit_iesg_note.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Edit IESG note for {{ doc.name }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.edit-iesg-note #id_note {
|
||||
width: 600px;
|
||||
height: 150px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Edit IESG note for {{ doc.name }}</h1>
|
||||
|
||||
<form class="edit-iesg-note" action="" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}:</th>
|
||||
<td>{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{{ doc.get_absolute_url }}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -30,10 +30,17 @@ form.position-form #id_comment {
|
|||
form.position-form .comment-text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
div.question {
|
||||
font-size: 173%;
|
||||
padding-left: 5px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change position for {{ ad.plain_name }} {{ doc }}</h1>
|
||||
<h1>Change position for {{ ad.plain_name }} on {{ doc }}</h1>
|
||||
|
||||
<div class="question">{{ ballot.ballot_type.question }}</div>
|
||||
|
||||
{% if ballot_deferred %}
|
||||
<div class="ballot-deferred">Ballot deferred by {{ ballot_deferred.by }} on {{ ballot_deferred.time|date:"Y-m-d" }}.</div>
|
||||
|
@ -45,7 +52,7 @@ form.position-form .comment-text {
|
|||
<span class="actions">
|
||||
<input type="submit" name="send_mail" value="Save and send email"/>
|
||||
<input type="submit" value="Save"/>
|
||||
{% if doc.type_id == "draft" %}
|
||||
{% if doc.type_id == "draft" or doc.type_id == "conflrev" %}
|
||||
{% if ballot_deferred %}<input type="submit" name="Undefer" value="Undefer"/>{% else %}<input type="submit" name="Defer" value="Defer"/>{% endif %}
|
||||
{% endif %}
|
||||
</span>
|
||||
|
|
29
ietf/templates/idrfc/edit_telechat_date.html
Normal file
29
ietf/templates/idrfc/edit_telechat_date.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Set Telechat Date for {{ doc.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.telechat-date td.actions {
|
||||
padding-top: 1em;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load ietf_filters %}
|
||||
<h1>Set Telechat Date for {{ doc.name }}</h1>
|
||||
|
||||
<form class="telechat-date" action="" method="POST">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
3
ietf/templates/idrfc/stream_changed_email.txt
Normal file
3
ietf/templates/idrfc/stream_changed_email.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% autoescape off %}{{ text }}
|
||||
ID Tracker URL: {{ url }}
|
||||
{% endautoescape %}
|
|
@ -5,7 +5,7 @@
|
|||
{% block content %}
|
||||
<h1>Undefer ballot for {{ doc }}</h1>
|
||||
|
||||
<form action="" method="POST">
|
||||
<form class="undefer" action="" method="POST">
|
||||
<p>Undefer the ballot for {{ doc.file_tag }}?</p>
|
||||
|
||||
<p>The ballot will then be on the IESG agenda of {{ telechat_date }}.</p>
|
||||
|
|
|
@ -34,7 +34,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{% comment %}
|
||||
Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
||||
{% endcomment %}
|
||||
{% load ietf_filters %}{% load ballot_icon %}
|
||||
{% load ietf_filters %}{% load ballot_icon_redesign %}
|
||||
|
||||
{% if title2_first %}{% if title1_first %}<h2>{{ title1 }}</h2>
|
||||
{% if title1|startswith:"2." %}
|
||||
|
@ -69,6 +69,7 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
|||
review and should therefore not be published without IETF review
|
||||
and IESG approval.<br/>
|
||||
<br />
|
||||
<b>(Old instructions)</b>
|
||||
The document shepherd must propose one of these responses in the
|
||||
document write-up in the Data Tracker, and the document shepherd
|
||||
may supply text for an IESG Note in the write-up. The Area
|
||||
|
@ -76,6 +77,13 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
|||
proposed by the document shepherd and agreement that the IESG
|
||||
should request inclusion of the IESG Note.<br/>
|
||||
<br />
|
||||
<b>(New instructions)</b>
|
||||
The document shepherd must propose one of these responses in the
|
||||
conflict-review document, and the document shepherd may supply text
|
||||
for an IESG Note in that document. The Area Director ballot positions
|
||||
indicate consensus with the response proposed by the document shepherd
|
||||
and agreement that the IESG should request inclusion of the IESG Note.<br/>
|
||||
<br />
|
||||
Other matters may be recorded in comments, and the comments will
|
||||
be passed on to the RFC Editor as community review of the document.
|
||||
</blockquote>
|
||||
|
@ -89,26 +97,27 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
|||
<table class="agenda-doc">
|
||||
<tbody>
|
||||
<tr><td>
|
||||
{% if doc.obj.rfc_flag %}
|
||||
<a href="/doc/rfc{{doc.obj.document.rfc_number}}">{{doc.obj.document.filename}}</a>
|
||||
<a href="http://www.rfc-editor.org/rfc/rfc{{doc.obj.document.rfc_number}}/">[txt]</a>
|
||||
<a href="{% url doc_view name=doc.obj.canonical_name %}">{{doc.obj.canonical_name}}</a>
|
||||
{% with doc.obj.rfc_number as rfc_number %}
|
||||
{% if rfc_number %}
|
||||
<a href="http://www.rfc-editor.org/rfc/rfc{{rfc_number}}/">[txt]</a>
|
||||
{% else %}
|
||||
<a href="/doc/{{doc.obj.document.filename}}/">{{doc.obj.document.displayname|safe}}</a>
|
||||
<a href="http://www.ietf.org/id/{{doc.obj.document.filename}}-{{doc.obj.document.revision}}.txt">[txt]</a>
|
||||
<a href="http://www.ietf.org/id/{{doc.obj.name}}-{{doc.obj.rev}}.txt">[txt]</a>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<br/>{{ doc.obj.document.title|escape }} ({{ doc.obj.document.intended_status }})
|
||||
<br/>{{ doc.obj.title|escape }} ({{ doc.obj.intended_std_level }})
|
||||
|
||||
|
||||
{% if doc.obj.note %}
|
||||
<br/>Note: {{ doc.obj.note|unescape }}
|
||||
{% endif %}
|
||||
|
||||
{% if doc.obj.draft.ipr.all %}
|
||||
{% if doc.obj.ipr %}
|
||||
<br />
|
||||
<h5>IPR:</h5>
|
||||
<ul>
|
||||
{% for ipr in doc.obj.draft.ipr.all %}
|
||||
{% for ipr in doc.obj.ipr %}
|
||||
{% ifequal ipr.ipr.status 1 %}
|
||||
<li><a href="/ipr/{{ ipr.ipr.ipr_id }}/">{{ ipr.ipr.title|escape }}</a></li>
|
||||
{% endifequal %}
|
||||
|
@ -117,10 +126,12 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
|||
|
||||
{% endif %}
|
||||
|
||||
<br/>Token: {{ doc.obj.token_name|escape }} ({{doc.obj.area_acronym}} area)
|
||||
{% if doc.obj.ballot.defer %}
|
||||
<br/>Was deferred by {{doc.obj.ballot.defer_by}} on {{doc.obj.ballot.defer_date}}
|
||||
<br/>Token: {{ doc.obj.ad }} ({{doc.obj.area_acronym}} area)
|
||||
{% with doc.obj.active_defer_event as defer %}
|
||||
{% if defer %}
|
||||
<br/>Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</td><td style="padding-left:20px; width: 50px;">
|
||||
{% ballot_icon doc.obj %}
|
||||
</td></tr></tbody></table>
|
||||
|
|
|
@ -38,12 +38,12 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
|||
{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %}
|
||||
{{ title2 }}
|
||||
{% endif %}{{ title3 }}
|
||||
{% for doc in section_docs %}
|
||||
o {{doc.obj.document.filename}}{% if not doc.obj.rfc_flag %}-{{doc.obj.document.revision}}{% endif %}
|
||||
{% filter wordwrap:"68"|indent|indent %}{{ doc.obj.document.title }} ({{ doc.obj.document.intended_status }}){% endfilter %}
|
||||
{% for doc in section_docs %}{% with doc.obj.rfc_number as rfc_number %}
|
||||
o {{doc.obj.canonical_name}}{% if not rfc_number %}-{{doc.obj.rev}}{% endif %}{% endwith %}
|
||||
{% filter wordwrap:"68"|indent|indent %}{{ doc.obj.title }} ({{ doc.obj.intended_std_level }}){% endfilter %}
|
||||
{% if doc.obj.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.obj.note|striptags }}{% endfilter %}
|
||||
{% endif %} Token: {{ doc.obj.token_name }}
|
||||
{% if doc.obj.ballot.defer %} Was deferred by {{doc.obj.ballot.defer_by}} on {{doc.obj.ballot.defer_date}}{% endif %}
|
||||
{% endif %} Token: {{ doc.obj.ad }}
|
||||
{% with doc.obj.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}
|
||||
{% empty %}
|
||||
NONE
|
||||
{% endfor %}
|
||||
|
|
|
@ -31,4 +31,4 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{% endcomment %}# Fields: telechat date, filename (draft-foo-bar or rfc1234), intended status, rfc editor submission flag (0=no, 1=yes), area acronym, AD name, version{% for doc in docs %}
|
||||
{{ doc.telechat_date }} {{ doc.document.filename }} {{ doc.document.intended_status }} {% if doc.stream in special_stream_list %}1{% else %}0{% endif %} {{doc.document.revision}}{% endfor %}
|
||||
{{ doc.telechat_date }} {{ doc.name }} {{ doc.intended_std_level }} {% if doc.stream.slug in special_stream_list %}1{% else %}0{% endif %} {{doc.area_acronym|lower}} {% with doc.ad as ad %}{% if ad %}{{ ad.plain_name }}{% else %}None Assigned{% endif %}{% endwith %} {{doc.rev}}{% endfor %}
|
||||
|
|
143
ietf/templates/iesg/agenda_documents_redesign.html
Normal file
143
ietf/templates/iesg/agenda_documents_redesign.html
Normal file
|
@ -0,0 +1,143 @@
|
|||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of the Nokia Corporation and/or its
|
||||
subsidiary(-ies) nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{% endcomment %}
|
||||
{% load ballot_icon_redesign %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block title %}Documents on Future IESG Telechat Agendas{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
.agenda_docs tr.oddrow {background-color: #EDF5FF; }
|
||||
.agenda_docs tr.header.telechat_date { margin-top:10px; background:#2647A0; color: white;}
|
||||
.agenda_docs tr.header.telechat_date td { font-size: 125%; }
|
||||
.agenda_docs tr.header + tr.header { border-top: 2px solid white;}
|
||||
.agenda_docs tr td {
|
||||
vertical-align: top;
|
||||
}
|
||||
.agenda_docs tr .reschedule,
|
||||
.agenda_docs tr .clear-returning-item {
|
||||
font-size: 11px;
|
||||
}
|
||||
.agenda_docs tr .doc_pages {
|
||||
font-size:80%; font-style:italic;
|
||||
}
|
||||
.secretariat-actions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="alternate" type="application/atom+xml" href="/feed/iesg-agenda/" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Documents on Future IESG Telechat Agendas</h1>
|
||||
|
||||
<form action="" method="POST">
|
||||
{% if user|in_group:"Secretariat" %}
|
||||
<div class="secretariat-actions">
|
||||
<button id="clear-all-on-schedule">Set all to not on agenda</button>
|
||||
<input type="submit" value="Save"/>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class="ietf-table ietf-doctable agenda_docs">
|
||||
{% for t in telechats %}
|
||||
|
||||
{% if not forloop.first %}
|
||||
<tr class="header"><td colspan="6"> </td></tr>
|
||||
{% endif %}
|
||||
|
||||
<tr class="header telechat_date"><td colspan="6">IESG telechat {{t.date}}</td></tr>
|
||||
|
||||
{% if forloop.first %}
|
||||
<tr class="header"><td colspan="6"><a href="/iesg/agenda/">Full IESG Agenda</a></td></tr>
|
||||
{% endif %}
|
||||
|
||||
<tr class="header"><td colspan="6"><a href="/iesg/agenda/telechat-{{t.date|date:"Y"}}-{{t.date|date:"m"}}-{{t.date|date:"d"}}-docs.tgz">Download Documents</a></td></tr>
|
||||
|
||||
<tr class="header"><td colspan="6">2. Protocol Actions</td></tr>
|
||||
|
||||
<tr class="header"><td colspan="6">2.1 WG Submissions</td></tr>
|
||||
|
||||
{% for doc in t.docs.s211 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s212 %}<tr class="header"><td colspan="6">2.1.2 Returning Item</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s212 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s213 %}<tr class="header"><td colspan="6">2.1.3 For Action</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s213 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
|
||||
<tr class="header"><td colspan="6">2.2 Individual Submissions</td></tr>
|
||||
|
||||
{% for doc in t.docs.s221 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s222 %}<tr class="header"><td colspan="6">2.2.2 Returning Item</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s222 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s223 %}<tr class="header"><td colspan="6">2.2.3 For Action</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s223 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
|
||||
<tr class="header"><td colspan="6">3. Document Actions</td></tr>
|
||||
|
||||
<tr class="header"><td colspan="6">3.1 WG Submissions</td></tr>
|
||||
|
||||
{% for doc in t.docs.s311 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s312 %}<tr class="header"><td colspan="6">3.1.2 Returning Item</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s312 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s313 %}<tr class="header"><td colspan="6">3.1.3 For Action</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s313 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
|
||||
<tr class="header"><td colspan="6">3.2 Individual Submissions Via AD</td></tr>
|
||||
|
||||
{% for doc in t.docs.s321 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s322 %}<tr class="header"><td colspan="6">3.2.2 Returning Item</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s322 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s323 %}<tr class="header"><td colspan="6">3.2.3 For Action</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s323 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
|
||||
<tr class="header"><td colspan="6">3.3 IRTF and Independent Submission Stream Documents</td></tr>
|
||||
|
||||
{% for doc in t.docs.s331 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s332 %}<tr class="header"><td colspan="6">3.3.2 Returning Item</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s332 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
{% if t.docs.s333 %}<tr class="header"><td colspan="6">3.3.3 For Action</td></tr>{% endif %}
|
||||
{% for doc in t.docs.s333 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
</form>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block content_end %}
|
||||
<script type="text/javascript" src="/js/agenda-documents.js"></script>
|
||||
{% endblock %}
|
|
@ -54,7 +54,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="title">{{ doc.title }}<span class="doc_pages"> ({{doc.pages}} pp)</span></td>
|
||||
<td class="title">{{ doc.title }}
|
||||
{% with doc.pages as pagecount %}{% if pagecount %}<span class="doc_pages">({{doc.pages}} pp)</span>{% endif %}{% endwith %}
|
||||
</td>
|
||||
{% include "iesg/agenda_documents_row_status.html" %}
|
||||
{% include "idrfc/ipr_column_with_label.html" %}
|
||||
<td class="ad">{{ doc.ad_name|default:"" }}</td>
|
||||
|
|
63
ietf/templates/iesg/agenda_documents_row_redesign.html
Normal file
63
ietf/templates/iesg/agenda_documents_row_redesign.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
{% comment %}
|
||||
Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of the Nokia Corporation and/or its
|
||||
subsidiary(-ies) nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{% endcomment %}
|
||||
|
||||
{% load ballot_icon_redesign %}
|
||||
{% load ietf_filters %}
|
||||
<tr
|
||||
{% if user|in_group:"Area_Director" %}
|
||||
{% if doc|my_position:user|equal:"Discuss" %}style="background:#ffa0a0;"{% endif %}
|
||||
{% if doc|my_position:user|equal:"Abstain" %}style="background:#ffff00;"{% endif %}
|
||||
{% if doc|my_position:user|equal:"Yes" or doc|my_position:user|slugify|equal:"no-objection" %}style="background:#a0ffa0;"{% endif %}
|
||||
{% if doc|my_position:user|equal:"Recuse" %}style="background:#c0c0c0;"{% endif %}
|
||||
{% endif %}
|
||||
>
|
||||
<td class="doc">
|
||||
<div>{{ doc.displayname_with_link|safe }}</div>
|
||||
{% with doc.active_defer_event as defer %}{% if defer %}
|
||||
<div><b title="deferred by {{ defer.by }}">(deferred on {{ defer.time|date:"Y-m-d" }})</b></div>
|
||||
{% endif %}{% endwith %}
|
||||
{% if user|in_group:"Secretariat" %}
|
||||
<div class="reschedule"><label>Reschedule: {{ doc.reschedule_form.telechat_date }}</label></div>
|
||||
{% if doc.reschedule_form.show_clear %}
|
||||
<div class="clear-returning-item"><label>{{ doc.reschedule_form.clear_returning_item }} Clear returning item</label></div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="title">{{ doc.title }}
|
||||
{% with doc.pages as pagecount %}{% if pagecount %}<span class="doc_pages">({{doc.pages}} pp)</span>{% endif %}{% endwith %}
|
||||
</td>
|
||||
{% include "iesg/agenda_documents_row_status_redesign.html" %}
|
||||
{% include "idrfc/ipr_column_with_label.html" %}
|
||||
<td class="ad">{{ doc.ad.name|default:"" }}</td>
|
||||
</tr>
|
|
@ -0,0 +1,6 @@
|
|||
{% extends "doc/status_columns.html" %}
|
||||
{% block extra_status %}
|
||||
{% if doc.type.slug == 'draft' %}
|
||||
<br/>Intended status: {{doc.intended_std_level}}
|
||||
{% endif %}
|
||||
{% endblock %}
|
10
ietf/templates/ietfworkflows/stream_state_redesign.html
Normal file
10
ietf/templates/ietfworkflows/stream_state_redesign.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
{% if doc %}
|
||||
<div class="stream_state">
|
||||
<div class="stream_state_more" style="float: left; margin: 1px 0.5em 0 0;"><a href="{% url stream_history doc.name %}" class="show_stream_info" title="Stream information for {{ doc.name }}" style="text-decoration: none; color:transparent; margin: 0; padding: 0; border: 0;"><img src="/images/plus.png" style="margin: 0; padding: 0; border:0;"><img></a></div>
|
||||
{% if stream %}
|
||||
{% if state %}{{ state.name }}{% else %}{{ stream }}{% endif %}
|
||||
{% else %}
|
||||
No stream assigned
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
|
@ -32,7 +32,7 @@ def make_test_data():
|
|||
state_id="active",
|
||||
type_id="ietf",
|
||||
parent=None)
|
||||
for x in ["irtf", "iab", "ise"]:
|
||||
for x in ["irtf", "iab", "ise", "iesg"]:
|
||||
Group.objects.create(
|
||||
name=x.upper(),
|
||||
acronym=x,
|
||||
|
@ -178,6 +178,25 @@ def make_test_data():
|
|||
group=areahist,
|
||||
person=p,
|
||||
email=email)
|
||||
|
||||
# stream chairs
|
||||
for stream in ['ietf','irtf','iab','iesg']:
|
||||
u = User.objects.create( username= ("%schair"%stream) )
|
||||
p = Person.objects.create(
|
||||
name="%s chair"%stream,
|
||||
ascii="%s chair"%stream,
|
||||
user = u,
|
||||
)
|
||||
chairmail = Email.objects.create(
|
||||
address="%schair@ietf.org"%stream,
|
||||
person = p,
|
||||
)
|
||||
Role.objects.create(
|
||||
name_id = "chair",
|
||||
group = Group.objects.get(acronym=stream),
|
||||
person = p,
|
||||
email = chairmail,
|
||||
)
|
||||
|
||||
# group chair
|
||||
u = User.objects.create(username="marschairman")
|
||||
|
@ -313,5 +332,20 @@ def make_test_data():
|
|||
break_area="Lounge",
|
||||
reg_area="Lobby",
|
||||
)
|
||||
|
||||
# an independent submission before review
|
||||
doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft')
|
||||
DocAlias.objects.create( name='draft-imaginary-independent-submission',document=doc)
|
||||
|
||||
# an irtf submission mid review
|
||||
doc = Document.objects.create( name='draft-imaginary-irtf-submission',type_id='draft')
|
||||
docalias = DocAlias.objects.create(name='draft-imaginary-irtf-submission',document=doc)
|
||||
doc.stream = StreamName.objects.get(slug='irtf')
|
||||
doc.save()
|
||||
crdoc = Document.objects.create(name='conflict-review-imaginary-irtf-submission',type_id='conflrev',rev='00',notify="fsm@ietf.org")
|
||||
DocAlias.objects.create( name='conflict-review-imaginary-irtf-submission',document=crdoc)
|
||||
crdoc.set_state(State.objects.get(name='Needs Shepherd',type__slug='conflrev'))
|
||||
crdoc.save()
|
||||
crdoc.relateddocument_set.create(target=docalias,relationship_id='conflrev')
|
||||
|
||||
return draft
|
||||
|
|
|
@ -43,7 +43,7 @@ from ietf.idrfc.idrfc_wrapper import IdRfcWrapper
|
|||
from ietf.ipr.models import IprDetail
|
||||
from ietf.group.models import Group
|
||||
from ietf.doc.models import State
|
||||
from ietf.doc.utils import get_chartering_type, augment_with_telechat_date
|
||||
from ietf.doc.utils import get_chartering_type
|
||||
|
||||
|
||||
def fill_in_charter_info(wg, include_drafts=False):
|
||||
|
@ -126,7 +126,6 @@ def chartering_wgs(request):
|
|||
charter_states = State.objects.filter(type="charter").exclude(slug__in=("approved", "notrev"))
|
||||
groups = Group.objects.filter(type="wg", charter__states__in=charter_states).select_related("state", "charter")
|
||||
|
||||
augment_with_telechat_date([g.charter for g in groups])
|
||||
|
||||
for g in groups:
|
||||
g.chartering_type = get_chartering_type(g.charter)
|
||||
|
|
BIN
static/images/pencil.png
Normal file
BIN
static/images/pencil.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
|
@ -46,8 +46,9 @@ function showBallot(draftName, editPositionUrl) {
|
|||
document.getElementById("ietf-extras").appendChild(el);
|
||||
|
||||
var buttons = [{text:"Close", handler:handleClose, isDefault:true}];
|
||||
if (("Area_Director" in IETF.user_groups) ||
|
||||
("Secretariat" in IETF.user_groups)) {
|
||||
// if (("Area_Director" in IETF.user_groups) ||
|
||||
// ("Secretariat" in IETF.user_groups)) {
|
||||
if ("Area_Director" in IETF.user_groups) {
|
||||
buttons.unshift({text:"Edit Position", handler:handleEditPosition});
|
||||
}
|
||||
var kl = [new YAHOO.util.KeyListener(document, {keys:27}, handleClose)]
|
||||
|
|
|
@ -166,7 +166,7 @@ changes=$( sed -n "/^ietfdb ($VER.*)/,/^ -- /p" changelog | awk '{ if (line) pri
|
|||
[ "$changes" ] || die "No changelog information for $VER found"
|
||||
note "$changes"
|
||||
|
||||
contributors=$(echo "$changes" | sed 's/\.[ \t\n]/ /'| tr -c "a-z0-9.@-" "\n" | sort | uniq | grep '@' | sed 's/^/-c /' || true)
|
||||
contributors=$(echo "$changes" | sed 's/\.[ \t\n]/ /'| tr -c "a-z0-9.@-" "\n" | sort | uniq | grep '@' | sed -r -e 's/^\.+//' -e 's/\.+$//' -e 's/^/-c /' || true)
|
||||
|
||||
note "Set the current time on the release notes in the changelog file"
|
||||
sed -r -i -e "1,/^ -- /s/([A-Za-z-]+ <[a-z0-9.-]+@[a-z0-9.-]+> ).*$/\1$(date +'%d %b %Y %H:%M:%S %z')/" changelog
|
||||
|
@ -230,7 +230,8 @@ Regards,
|
|||
|
||||
Henrik
|
||||
(via the mkrelease script)
|
||||
" | tee /tmp/release.mail | $do mail -s "New datatracker release: v$VER" -c henrik@levkowetz.com -c glen@amsl.com -c fenner@fenron.net -c rjs@nostrum.com -c housley@vigilsec.com -c cmorgan@amsl.com -c avezza@amsl.com -c amorris@amsl.com -c smccammon@amsl.com -c kmoreland@amsl.com -c stevey@amsl.com -c olau@iola.dk $contributors codesprints@ietf.org
|
||||
" > ~/src/db/release-mail-v$VER.txt
|
||||
cat ~/src/db/release-mail-v$VER.txt | $do mail -s "New datatracker release: v$VER" -c henrik@levkowetz.com -c glen@amsl.com -c fenner@fenron.net -c rjs@nostrum.com -c housley@vigilsec.com -c cmorgan@amsl.com -c avezza@amsl.com -c amorris@amsl.com -c smccammon@amsl.com -c kmoreland@amsl.com -c stevey@amsl.com -c olau@iola.dk $contributors codesprints@ietf.org
|
||||
|
||||
$do toolsfeed control changelog /www/tools.ietf.org/tools/atomfeed.xml
|
||||
$do toolpush /www/tools.ietf.org/tools/atomfeed.xml
|
||||
|
|
Loading…
Reference in a new issue