diff --git a/bin/test-crawl b/bin/test-crawl index 2c4fce706..204df4744 100755 --- a/bin/test-crawl +++ b/bin/test-crawl @@ -1,6 +1,6 @@ #!/usr/bin/env python -import os, sys, re, datetime, argparse, traceback, tempfile, json, subprocess +import os, sys, re, datetime, argparse, traceback, json, subprocess import html5lib import random diff --git a/ietf/bin/rfc-editor-index-updates b/ietf/bin/rfc-editor-index-updates index 430f1fe1a..4387805f0 100755 --- a/ietf/bin/rfc-editor-index-updates +++ b/ietf/bin/rfc-editor-index-updates @@ -1,6 +1,6 @@ #!/usr/bin/env python -import os, sys, re, json, datetime +import os, sys, datetime import syslog import traceback @@ -29,22 +29,28 @@ if options.skip_date: skip_date = datetime.datetime.strptime(options.skip_date, "%Y-%m-%d").date() from ietf.utils.pipe import pipe -from ietf.sync.rfceditor import * from ietf.doc.utils import rebuild_reference_relations +import ietf.sync.rfceditor syslog.syslog("Updating document metadata from RFC index from %s" % settings.RFC_EDITOR_QUEUE_URL) -response = fetch_index_xml(settings.RFC_EDITOR_INDEX_URL) -data = parse_index(response) +response = ietf.sync.rfceditor.fetch_index_xml(settings.RFC_EDITOR_INDEX_URL) +data = ietf.sync.rfceditor.parse_index(response) -if len(data) < MIN_INDEX_RESULTS: +if len(data) < ietf.sync.rfceditor.MIN_INDEX_RESULTS: syslog.syslog("Not enough results, only %s" % len(data)) sys.exit(1) -changed, new_rfcs = update_docs_from_rfc_index(data, skip_older_than_date=skip_date) +new_rfcs = [] +for changes, doc, rfc_published in ietf.sync.rfceditor.update_docs_from_rfc_index(data, skip_older_than_date=skip_date): + if rfc_published: + new_rfcs.append(doc) -for c in changed: - syslog.syslog(c) + for c in changes: + syslog.syslog("%s: %s" % (doc.name, c)) + print "%s: %s" % (doc.name, c) + +sys.exit(0) # This can be called while processing a notifying POST from the RFC Editor # Spawn a child to sync the rfcs and calculate new reference relationships diff --git a/ietf/doc/expire.py b/ietf/doc/expire.py index 4e215abc6..8a833ea53 100644 --- a/ietf/doc/expire.py +++ b/ietf/doc/expire.py @@ -6,7 +6,7 @@ import datetime, os, shutil, glob, re from pathlib import Path from ietf.utils.mail import send_mail -from ietf.doc.models import Document, DocEvent, State, save_document_in_history, IESG_SUBSTATE_TAGS +from ietf.doc.models import Document, DocEvent, State, IESG_SUBSTATE_TAGS from ietf.person.models import Person from ietf.meeting.models import Meeting from ietf.doc.utils import add_state_change_event @@ -129,8 +129,9 @@ def expire_draft(doc): system = Person.objects.get(name="(System)") + events = [] + # change the state - save_document_in_history(doc) if doc.latest_event(type='started_iesg_process'): new_state = State.objects.get(used=True, type="draft-iesg", slug="dead") prev_state = doc.get_state(new_state.type_id) @@ -139,15 +140,13 @@ def expire_draft(doc): doc.set_state(new_state) doc.tags.remove(*prev_tags) e = add_state_change_event(doc, system, prev_state, new_state, prev_tags=prev_tags, new_tags=[]) + if e: + events.append(e) - e = DocEvent(doc=doc, by=system) - e.type = "expired_document" - e.desc = "Document has expired" - e.save() + events.append(DocEvent.objects.create(doc=doc, by=system, type="expired_document", desc="Document has expired")) doc.set_state(State.objects.get(used=True, type="draft", slug="expired")) - doc.time = datetime.datetime.now() - doc.save() + doc.save_with_history(events) def clean_up_draft_files(): """Move unidentified and old files out of the Internet Draft directory.""" diff --git a/ietf/doc/lastcall.py b/ietf/doc/lastcall.py index 6310de4a4..86cae3832 100644 --- a/ietf/doc/lastcall.py +++ b/ietf/doc/lastcall.py @@ -5,7 +5,6 @@ import datetime from django.db.models import Q from ietf.doc.models import Document, State, DocEvent, LastCallDocEvent, WriteupDocEvent -from ietf.doc.models import save_document_in_history from ietf.doc.models import IESG_SUBSTATE_TAGS from ietf.person.models import Person from ietf.doc.utils import add_state_change_event @@ -14,12 +13,15 @@ from ietf.doc.mails import send_last_call_request, email_last_call_expired def request_last_call(request, doc): if not doc.latest_event(type="changed_ballot_writeup_text"): - generate_ballot_writeup(request, doc) + e = generate_ballot_writeup(request, doc) + e.save() if not doc.latest_event(type="changed_ballot_approval_text"): - generate_approval_mail(request, doc) + e = generate_approval_mail(request, doc) + e.save() if not doc.latest_event(type="changed_last_call_text"): - generate_last_call_announcement(request, doc) - + e = generate_last_call_announcement(request, doc) + e.save() + send_last_call_request(request, doc) e = DocEvent() @@ -50,8 +52,6 @@ def expire_last_call(doc): else: raise ValueError("Unexpected document type to expire_last_call(): %s" % doc.type) - save_document_in_history(doc) - prev_state = doc.get_state(new_state.type_id) doc.set_state(new_state) @@ -60,8 +60,7 @@ def expire_last_call(doc): system = Person.objects.get(name="(System)") e = add_state_change_event(doc, system, prev_state, new_state, prev_tags=prev_tags, new_tags=[]) - - doc.time = (e and e.time) or datetime.datetime.now() - doc.save() + if e: + doc.save_with_history([e]) email_last_call_expired(doc) diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py index ab7d336c6..2b07cfedc 100644 --- a/ietf/doc/mails.py +++ b/ietf/doc/mails.py @@ -78,6 +78,21 @@ def email_iesg_processing_document(request, doc, changes): def html_to_text(html): return strip_tags(html.replace("<", "<").replace(">", ">").replace("&", "&").replace("
", "\n")) +def email_update_telechat(request, doc, text): + (to, cc) = gather_address_lists('doc_telechat_details_changed',doc=doc) + + if not to: + return + + text = strip_tags(text) + send_mail(request, to, None, + "Telechat update notice: %s" % doc.file_tag(), + "doc/mail/update_telechat.txt", + dict(text=text, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()), + cc=cc) + + def generate_ballot_writeup(request, doc): e = doc.latest_event(type="iana_review") iana = e.desc if e else "" @@ -88,8 +103,8 @@ def generate_ballot_writeup(request, doc): e.doc = doc e.desc = u"Ballot writeup was generated" e.text = unicode(render_to_string("doc/mail/ballot_writeup.txt", {'iana': iana})) - e.save() - + + # caller is responsible for saving, if necessary return e def generate_last_call_announcement(request, doc): @@ -130,8 +145,8 @@ def generate_last_call_announcement(request, doc): e.doc = doc e.desc = u"Last call announcement was generated" e.text = unicode(mail) - e.save() + # caller is responsible for saving, if necessary return e @@ -149,8 +164,8 @@ def generate_approval_mail(request, doc): e.doc = doc e.desc = u"Ballot approval text was generated" e.text = unicode(mail) - e.save() + # caller is responsible for saving, if necessary return e def generate_approval_mail_approved(request, doc): diff --git a/ietf/doc/migrations/0011_auto_20151027_1127.py b/ietf/doc/migrations/0011_auto_20151027_1127.py new file mode 100644 index 000000000..1039d0678 --- /dev/null +++ b/ietf/doc/migrations/0011_auto_20151027_1127.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + +def fix_buggy_author_foreignkey(apps, schema_editor): + DocumentAuthor = apps.get_model("doc", "DocumentAuthor") + # apparently, we have a buggy key in the DB, fix it + DocumentAuthor.objects.filter(author="[]").update(author="d3e3e3@gmail.com") + +def save_all_documents_in_history(apps, schema_editor): + State = apps.get_model("doc", "State") + Document = apps.get_model("doc", "Document") + DocHistory = apps.get_model("doc", "DocHistory") + RelatedDocument = apps.get_model("doc", "RelatedDocument") + RelatedDocHistory = apps.get_model("doc", "RelatedDocHistory") + DocumentAuthor = apps.get_model("doc", "DocumentAuthor") + DocHistoryAuthor = apps.get_model("doc", "DocHistoryAuthor") + + def canonical_name(self): + name = self.name + state = State.objects.filter(document=self, type_id=self.type_id).first() + if self.type_id == "draft" and state.slug == "rfc": + a = self.docalias_set.filter(name__startswith="rfc") + if a: + name = a[0].name + elif self.type_id == "charter": + return charter_name_for_group(self.chartered_group) + return name + + def charter_name_for_group(group): + if group.type_id == "rg": + top_org = "irtf" + else: + top_org = "ietf" + + return "charter-%s-%s" % (top_org, group.acronym) + + def save_document_in_history(doc): + """Save a snapshot of document and related objects in the database.""" + def get_model_fields_as_dict(obj): + return dict((field.name, getattr(obj, field.name)) + for field in obj._meta.fields + if field is not obj._meta.pk) + + # copy fields + fields = get_model_fields_as_dict(doc) + fields["doc"] = doc + fields["name"] = canonical_name(doc) + + dochist = DocHistory(**fields) + dochist.save() + + # copy many to many + for field in doc._meta.many_to_many: + if field.rel.through and field.rel.through._meta.auto_created: + setattr(dochist, field.name, getattr(doc, field.name).all()) + + # copy remaining tricky many to many + def transfer_fields(obj, HistModel): + mfields = get_model_fields_as_dict(item) + # map doc -> dochist + for k, v in mfields.iteritems(): + if v == doc: + mfields[k] = dochist + HistModel.objects.create(**mfields) + + for item in RelatedDocument.objects.filter(source=doc): + transfer_fields(item, RelatedDocHistory) + + for item in DocumentAuthor.objects.filter(document=doc): + transfer_fields(item, DocHistoryAuthor) + + return dochist + + from django.conf import settings + settings.DEBUG = False # prevent out-of-memory problems + + for d in Document.objects.iterator(): + save_document_in_history(d) + +class Migration(migrations.Migration): + + dependencies = [ + ('doc', '0010_auto_20150930_0251'), + ] + + operations = [ + migrations.RunPython(fix_buggy_author_foreignkey), + migrations.RunPython(save_all_documents_in_history) + ] diff --git a/ietf/doc/models.py b/ietf/doc/models.py index ddc20b297..d6133fc50 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -18,7 +18,6 @@ from ietf.name.models import ( DocTypeName, DocTagName, StreamName, IntendedStdL from ietf.person.models import Email, Person from ietf.utils.admin import admin_link - class StateType(models.Model): slug = models.CharField(primary_key=True, max_length=30) # draft, draft-iesg, charter, ... label = models.CharField(max_length=255, help_text="Label that should be used (e.g. in admin) for state drop-down for this type of state") # State, IESG state, WG state, ... @@ -432,6 +431,27 @@ class Document(DocumentInfo): name = name.upper() return name + def save_with_history(self, events): + """Save document and put a snapshot in the history models where they + can be retrieved later. You must pass in at least one event + with a description of what happened.""" + + assert events, "You must always add at least one event to describe the changes in the history log" + self.time = max(self.time, events[0].time) + + self._has_an_event_so_saving_is_allowed = True + self.save() + del self._has_an_event_so_saving_is_allowed + + from ietf.doc.utils import save_document_in_history + save_document_in_history(self) + + def save(self, *args, **kwargs): + # if there's no primary key yet, we can allow the save to go + # through to break the cycle between the document and any + # events + assert kwargs.get("force_insert", False) or getattr(self, "_has_an_event_so_saving_is_allowed", None), "Use .save_with_history to save documents" + super(Document, self).save(*args, **kwargs) def telechat_date(self, e=None): if not e: @@ -568,50 +588,6 @@ class DocHistory(DocumentInfo): verbose_name = "document history" verbose_name_plural = "document histories" -def save_document_in_history(doc): - """This should be called before saving changes to a Document instance, - so that the DocHistory entries contain all previous states, while - the Group entry contain the current state. XXX TODO: Call this - directly from Document.save(), and add event listeners for save() - on related objects so we can save as needed when they change, too. - """ - def get_model_fields_as_dict(obj): - return dict((field.name, getattr(obj, field.name)) - for field in obj._meta.fields - if field is not obj._meta.pk) - - # copy fields - fields = get_model_fields_as_dict(doc) - fields["doc"] = doc - fields["name"] = doc.canonical_name() - - dochist = DocHistory(**fields) - dochist.save() - - # copy many to many - for field in doc._meta.many_to_many: - if field.rel.through and field.rel.through._meta.auto_created: - setattr(dochist, field.name, getattr(doc, field.name).all()) - - # copy remaining tricky many to many - def transfer_fields(obj, HistModel): - mfields = get_model_fields_as_dict(item) - # map doc -> dochist - for k, v in mfields.iteritems(): - if v == doc: - mfields[k] = dochist - HistModel.objects.create(**mfields) - - for item in RelatedDocument.objects.filter(source=doc): - transfer_fields(item, RelatedDocHistory) - - for item in DocumentAuthor.objects.filter(document=doc): - transfer_fields(item, DocHistoryAuthor) - - return dochist - - - class DocAlias(models.Model): """This is used for documents that may appear under multiple names, and in particular for RFCs, which for continuity still keep the @@ -693,7 +669,8 @@ EVENT_TYPES = [ # RFC Editor ("rfc_editor_received_announcement", "Announcement was received by RFC Editor"), - ("requested_publication", "Publication at RFC Editor requested") + ("requested_publication", "Publication at RFC Editor requested"), + ("sync_from_rfc_editor", "Received updated information from RFC Editor"), ] class DocEvent(models.Model): @@ -705,10 +682,11 @@ class DocEvent(models.Model): desc = models.TextField() def for_current_revision(self): - return self.time >= self.doc.latest_event(NewRevisionDocEvent,type='new_revision').time + e = self.doc.latest_event(NewRevisionDocEvent,type='new_revision') + return not e or (self.time, self.pk) >= (e.time, e.pk) def get_dochistory(self): - return DocHistory.objects.filter(time__lte=self.time,doc__name=self.doc.name).order_by('-time').first() + return DocHistory.objects.filter(time__lte=self.time,doc__name=self.doc.name).order_by('-time', '-pk').first() def __unicode__(self): return u"%s %s by %s at %s" % (self.doc.name, self.get_type_display().lower(), self.by.plain_name(), self.time) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 382f95447..5017bbf53 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -18,8 +18,7 @@ from django.conf import settings import debug # pyflakes:ignore from ietf.doc.models import ( Document, DocAlias, DocRelationshipName, RelatedDocument, State, - DocEvent, BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent, NewRevisionDocEvent, - save_document_in_history ) + DocEvent, BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent, NewRevisionDocEvent ) from ietf.group.models import Group from ietf.meeting.models import Meeting, Session, SessionPresentation from ietf.name.models import SessionStatusName @@ -111,10 +110,11 @@ class SearchTests(TestCase): def test_search_for_name(self): draft = make_test_data() - save_document_in_history(draft) + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) + prev_rev = draft.rev draft.rev = "%02d" % (int(prev_rev) + 1) - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) # exact match r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name=draft.name))) @@ -513,9 +513,8 @@ Man Expires September 22, 2015 [Page 3] # draft published as RFC draft.set_state(State.objects.get(type="draft", slug="rfc")) draft.std_level_id = "bcp" - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="published_rfc", by=Person.objects.get(name="(System)"))]) - DocEvent.objects.create(doc=draft, type="published_rfc", by=Person.objects.get(name="(System)")) rfc_alias = DocAlias.objects.create(name="rfc123456", document=draft) bcp_alias = DocAlias.objects.create(name="bcp123456", document=draft) @@ -561,9 +560,10 @@ Man Expires September 22, 2015 [Page 3] ]: doc = Document.objects.get(name=docname) # give it some history - save_document_in_history(doc) - doc.rev="01" - doc.save() + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) + + doc.rev = "01" + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name))) self.assertEqual(r.status_code, 200) @@ -620,7 +620,8 @@ class DocTestCase(TestCase): doc = make_test_data() ballot = doc.active_ballot() - save_document_in_history(doc) + # make sure we have some history + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) pos = BallotPositionDocEvent.objects.create( doc=doc, @@ -648,9 +649,8 @@ class DocTestCase(TestCase): # Now simulate a new revision and make sure positions on older revisions are marked as such oldrev = doc.rev e = NewRevisionDocEvent.objects.create(doc=doc,rev='%02d'%(int(doc.rev)+1),type='new_revision',by=Person.objects.get(name="(System)")) - save_document_in_history(doc) doc.rev = e.rev - doc.save() + doc.save_with_history([e]) r = self.client.get(urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name))) self.assertEqual(r.status_code, 200) self.assertTrue( '(%s for -%s)' % (pos.comment_time.strftime('%Y-%m-%d'), oldrev) in r.content) diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py index 1589df22c..cb14c3c2e 100644 --- a/ietf/doc/tests_ballot.py +++ b/ietf/doc/tests_ballot.py @@ -125,7 +125,7 @@ class EditPositionTests(TestCase): def test_send_ballot_comment(self): draft = make_test_data() draft.notify = "somebody@example.com" - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) ad = Person.objects.get(name="Aread Irector") @@ -217,8 +217,8 @@ class BallotWriteupsTests(TestCase): regenerate_last_call_text="1")) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - draft = Document.objects.get(name=draft.name) - self.assertTrue("Subject: Last Call" in draft.latest_event(WriteupDocEvent, type="changed_last_call_text").text) + text = q("[name=last_call_text]").text() + self.assertTrue("Subject: Last Call" in text) def test_request_last_call(self): @@ -229,12 +229,14 @@ class BallotWriteupsTests(TestCase): # give us an announcement to send r = self.client.post(url, dict(regenerate_last_call_text="1")) self.assertEqual(r.status_code, 200) - + q = PyQuery(r.content) + text = q("[name=last_call_text]").text() + mailbox_before = len(outbox) # send r = self.client.post(url, dict( - last_call_text=draft.latest_event(WriteupDocEvent, type="changed_last_call_text").text, + last_call_text=text, send_last_call_request="1")) draft = Document.objects.get(name=draft.name) self.assertEqual(draft.get_state_slug("draft-iesg"), "lc-req") @@ -318,7 +320,6 @@ class BallotWriteupsTests(TestCase): # test regenerate r = self.client.post(url, dict(regenerate_approval_text="1")) self.assertEqual(r.status_code, 200) - draft = Document.objects.get(name=draft.name) self.assertTrue("Subject: Protocol Action" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text) # test regenerate when it's a disapprove @@ -326,18 +327,16 @@ class BallotWriteupsTests(TestCase): r = self.client.post(url, dict(regenerate_approval_text="1")) self.assertEqual(r.status_code, 200) - draft = Document.objects.get(name=draft.name) self.assertTrue("NOT be published" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text) # test regenerate when it's a conflict review draft.group = Group.objects.get(type="individ") draft.stream_id = "irtf" - draft.save() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) r = self.client.post(url, dict(regenerate_approval_text="1")) self.assertEqual(r.status_code, 200) - draft = Document.objects.get(name=draft.name) self.assertTrue("Subject: Results of IETF-conflict review" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text) @@ -519,7 +518,6 @@ class DeferUndeferTestCase(TestCase): defer_states = dict(draft=['draft-iesg','defer'],conflrev=['conflrev','defer'],statchg=['statchg','defer']) if doc.type_id in defer_states: doc.set_state(State.objects.get(used=True, type=defer_states[doc.type_id][0],slug=defer_states[doc.type_id][1])) - doc.save() # get r = self.client.get(url) diff --git a/ietf/doc/tests_charter.py b/ietf/doc/tests_charter.py index 2ea147f36..e429c6d8a 100644 --- a/ietf/doc/tests_charter.py +++ b/ietf/doc/tests_charter.py @@ -9,7 +9,8 @@ from django.core.urlresolvers import reverse as urlreverse from ietf.doc.models import ( Document, State, BallotDocEvent, BallotType, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent ) -from ietf.doc.utils_charter import next_revision, default_review_text, default_action_text +from ietf.doc.utils_charter import ( next_revision, default_review_text, default_action_text, + charter_name_for_group ) from ietf.group.models import Group, GroupMilestone from ietf.iesg.models import TelechatDate from ietf.person.models import Person @@ -27,6 +28,10 @@ class EditCharterTests(TestCase): def tearDown(self): shutil.rmtree(self.charter_dir) + def write_charter_file(self, charter): + with open(os.path.join(self.charter_dir, "%s-%s.txt" % (charter.canonical_name(), charter.rev)), "w") as f: + f.write("This is a charter.") + def test_startstop_process(self): make_test_data() @@ -43,6 +48,8 @@ class EditCharterTests(TestCase): self.assertEqual(r.status_code, 200) # post + self.write_charter_file(charter) + r = self.client.post(url, dict(message="test message")) self.assertEqual(r.status_code, 302) if option == "abandon": @@ -273,6 +280,40 @@ class EditCharterTests(TestCase): self.assertEqual(f.read(), "Windows line\nMac line\nUnix line\n" + utf_8_snippet) + def test_submit_initial_charter(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + # get rid of existing charter + charter = group.charter + group.charter = None + group.save() + charter.delete() + charter = None + + url = urlreverse('charter_submit', kwargs=dict(name=charter_name_for_group(group))) + login_testing_unauthorized(self, "secretary", url) + + # normal get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('form input[name=txt]')), 1) + + # create charter + test_file = StringIO("Simple test") + test_file.name = "unnamed" + + r = self.client.post(url, dict(txt=test_file)) + self.assertEqual(r.status_code, 302) + + charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) + self.assertEqual(charter.rev, "00-00") + self.assertTrue("new_revision" in charter.latest_event().type) + + group = Group.objects.get(pk=group.pk) + self.assertEqual(group.charter, charter) + def test_edit_review_announcement_text(self): draft = make_test_data() charter = draft.group.charter @@ -385,7 +426,8 @@ class EditCharterTests(TestCase): url = urlreverse('ietf.doc.views_charter.ballot_writeupnotes', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) - default_action_text(draft.group, charter, by) + e = default_action_text(draft.group, charter, by) + e.save() # normal get r = self.client.get(url) @@ -417,8 +459,7 @@ class EditCharterTests(TestCase): url = urlreverse('charter_approve', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) - with open(os.path.join(self.charter_dir, "%s-%s.txt" % (charter.canonical_name(), charter.rev)), "w") as f: - f.write("This is a charter.") + self.write_charter_file(charter) p = Person.objects.get(name="Aread Irector") diff --git a/ietf/doc/tests_conflict_review.py b/ietf/doc/tests_conflict_review.py index 34edd19d8..f3e2cd0cf 100644 --- a/ietf/doc/tests_conflict_review.py +++ b/ietf/doc/tests_conflict_review.py @@ -32,8 +32,8 @@ class ConflictReviewTests(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 404) - doc.stream=StreamName.objects.get(slug='ise') - doc.save() + doc.stream = StreamName.objects.get(slug='ise') + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")]) # normal get should succeed and get a reasonable form r = self.client.get(url) @@ -89,14 +89,14 @@ class ConflictReviewTests(TestCase): # can't start conflict reviews on documents in some other stream - doc.stream=StreamName.objects.get(slug='irtf') - doc.save() + doc.stream = StreamName.objects.get(slug='irtf') + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")]) r = self.client.get(url) self.assertEquals(r.status_code, 404) # successful get - doc.stream=StreamName.objects.get(slug='ise') - doc.save() + doc.stream = StreamName.objects.get(slug='ise') + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")]) r = self.client.get(url) self.assertEquals(r.status_code, 200) q = PyQuery(r.content) @@ -269,7 +269,6 @@ class ConflictReviewTests(TestCase): # Some additional setup create_ballot_if_not_open(doc,Person.objects.get(name="Sec Retary"),"conflrev") doc.set_state(State.objects.get(used=True, slug=approve_type+'-pend',type='conflrev')) - doc.save() # get r = self.client.get(url) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 12094d341..f4f248123 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -370,9 +370,9 @@ class EditInfoTests(TestCase): self.assertEqual(draft.ad, ad) self.assertEqual(draft.note, "This is a note") self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat")) - self.assertEqual(draft.docevent_set.count(), events_before + 3) + self.assertEqual(draft.docevent_set.count(), events_before + 4) events = list(draft.docevent_set.order_by('time', 'id')) - self.assertEqual(events[-3].type, "started_iesg_process") + self.assertEqual(events[-4].type, "started_iesg_process") self.assertEqual(len(outbox), mailbox_before+1) self.assertTrue('IESG processing' in outbox[-1]['Subject']) self.assertTrue('draft-ietf-mars-test2@' in outbox[-1]['To']) @@ -381,7 +381,7 @@ class EditInfoTests(TestCase): draft.unset_state('draft-iesg') draft.set_state(State.objects.get(type='draft-stream-ietf',slug='writeupw')) draft.stream = StreamName.objects.get(slug='ietf') - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")]) r = self.client.post(url, dict(intended_std_level=str(draft.intended_std_level_id), ad=ad.pk, @@ -537,7 +537,7 @@ class ExpireIDsTests(TestCase): # hack into expirable state draft.unset_state("draft-iesg") draft.expires = datetime.datetime.now() + datetime.timedelta(days=10) - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) self.assertEqual(len(list(get_soon_to_expire_drafts(14))), 1) @@ -561,7 +561,7 @@ class ExpireIDsTests(TestCase): # hack into expirable state draft.unset_state("draft-iesg") draft.expires = datetime.datetime.now() - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) self.assertEqual(len(list(get_expired_drafts())), 1) @@ -624,7 +624,6 @@ class ExpireIDsTests(TestCase): # RFC draft draft.set_state(State.objects.get(used=True, type="draft", slug="rfc")) - draft.save() txt = "%s-%s.txt" % (draft.name, draft.rev) self.write_draft_file(txt, 5000) @@ -642,7 +641,7 @@ class ExpireIDsTests(TestCase): # expire draft draft.set_state(State.objects.get(used=True, type="draft", slug="expired")) draft.expires = datetime.datetime.now() - datetime.timedelta(days=1) - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) e = DocEvent() e.doc = draft @@ -862,7 +861,7 @@ class IndividualInfoFormsTests(TestCase): def test_doc_change_shepherd(self): self.doc.shepherd = None - self.doc.save() + self.doc.save_with_history([DocEvent.objects.create(doc=self.doc, type="changed_shepherd", by=Person.objects.get(user__username="secretary"), desc="Test")]) url = urlreverse('doc_edit_shepherd',kwargs=dict(name=self.docname)) @@ -914,19 +913,19 @@ class IndividualInfoFormsTests(TestCase): def test_doc_change_shepherd_email(self): self.doc.shepherd = None - self.doc.save() + self.doc.save_with_history([DocEvent.objects.create(doc=self.doc, type="changed_shepherd", by=Person.objects.get(user__username="secretary"), desc="Test")]) url = urlreverse('doc_change_shepherd_email',kwargs=dict(name=self.docname)) r = self.client.get(url) self.assertEqual(r.status_code, 404) self.doc.shepherd = Email.objects.get(person__user__username="ad1") - self.doc.save() + self.doc.save_with_history([DocEvent.objects.create(doc=self.doc, type="changed_shepherd", by=Person.objects.get(user__username="secretary"), desc="Test")]) login_testing_unauthorized(self, "plain", url) self.doc.shepherd = Email.objects.get(person__user__username="plain") - self.doc.save() + self.doc.save_with_history([DocEvent.objects.create(doc=self.doc, type="changed_shepherd", by=Person.objects.get(user__username="secretary"), desc="Test")]) new_email = Email.objects.create(address="anotheremail@example.com", person=self.doc.shepherd.person) @@ -961,7 +960,7 @@ class IndividualInfoFormsTests(TestCase): # Try again when no longer a shepherd. self.doc.shepherd = None - self.doc.save() + self.doc.save_with_history([DocEvent.objects.create(doc=self.doc, type="changed_shepherd", by=Person.objects.get(user__username="secretary"), desc="Test")]) r = self.client.get(url) self.assertEqual(r.status_code,200) q = PyQuery(r.content) @@ -1070,7 +1069,7 @@ class RequestPublicationTests(TestCase): draft.stream = StreamName.objects.get(slug="iab") draft.group = Group.objects.get(acronym="iab") draft.intended_std_level = IntendedStdLevelName.objects.get(slug="inf") - draft.save() + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) draft.set_state(State.objects.get(used=True, type="draft-stream-iab", slug="approved")) url = urlreverse('doc_request_publication', kwargs=dict(name=draft.name)) @@ -1110,8 +1109,8 @@ class AdoptDraftTests(TestCase): draft = make_test_data() draft.stream = None draft.group = Group.objects.get(type="individ") - draft.save() draft.unset_state("draft-stream-ietf") + draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) url = urlreverse('doc_adopt_draft', kwargs=dict(name=draft.name)) login_testing_unauthorized(self, "marschairman", url) diff --git a/ietf/doc/tests_status_change.py b/ietf/doc/tests_status_change.py index f9a564549..491779e34 100644 --- a/ietf/doc/tests_status_change.py +++ b/ietf/doc/tests_status_change.py @@ -23,7 +23,7 @@ from ietf.utils.test_utils import login_testing_unauthorized class StatusChangeTests(TestCase): def test_start_review(self): - url = urlreverse('start_rfc_status_change',kwargs=dict(name="")) + url = urlreverse('start_rfc_status_change') login_testing_unauthorized(self, "secretary", url) # normal get should succeed and get a reasonable form @@ -104,7 +104,7 @@ class StatusChangeTests(TestCase): # successful change to Last Call Requested messages_before = len(outbox) doc.ad = Person.objects.get(user__username='ad') - doc.save() + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) lc_req_pk = str(State.objects.get(slug='lc-req',type__slug='statchg').pk) r = self.client.post(url,dict(new_state=lc_req_pk)) self.assertEquals(r.status_code, 200) @@ -251,8 +251,8 @@ class StatusChangeTests(TestCase): # additional setup doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois') doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist') - doc.ad = Person.objects.get(name='Ad No2') - doc.save() + doc.ad = Person.objects.get(name='Ad No2') + doc.save_with_history([DocEvent.objects.create(doc=doc, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")]) # get r = self.client.get(url) @@ -295,9 +295,8 @@ class StatusChangeTests(TestCase): # Some additional setup doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois') doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist') - create_ballot_if_not_open(doc,Person.objects.get(name="Sec Retary"),"statchg") + create_ballot_if_not_open(doc,Person.objects.get(user__username="secretary"),"statchg") doc.set_state(State.objects.get(slug='appr-pend',type='statchg')) - doc.save() # get r = self.client.get(url) diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 49cbc3d38..aab6d594d 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -32,6 +32,7 @@ from django.conf.urls import patterns, url, include from django.views.generic import RedirectView +from django.conf import settings from ietf.doc import views_search, views_draft, views_ballot from ietf.doc import views_status_change @@ -45,7 +46,7 @@ urlpatterns = patterns('', url(r'^ad/(?P[A-Za-z0-9.-]+)/$', views_search.docs_for_ad, name="docs_for_ad"), (r'^ad2/(?P[A-Za-z0-9.-]+)/$', RedirectView.as_view(url='/doc/ad/%(name)s/', permanent=True)), url(r'^rfc-status-changes/$', views_status_change.rfc_status_changes, name='rfc_status_changes'), - url(r'^start-rfc-status-change/(?P[A-Za-z0-9._+-]*)$', views_status_change.start_rfc_status_change, name='start_rfc_status_change'), + url(r'^start-rfc-status-change/(?:%(name)s/)?$' % settings.URL_REGEXPS, views_status_change.start_rfc_status_change, name='start_rfc_status_change'), url(r'^iesg/(?P[A-Za-z0-9.-]+/)?$', views_search.drafts_in_iesg_process, name="drafts_in_iesg_process"), url(r'^email-aliases/$', views_doc.email_aliases), @@ -53,62 +54,62 @@ urlpatterns = patterns('', url(r'^active/$', views_search.index_active_drafts, name="index_active_drafts"), url(r'^select2search/(?P(document|docalias))/(?Pdraft)/$', views_search.ajax_select2_search_docs, name="ajax_select2_search_docs"), - url(r'^(?P[A-Za-z0-9._+-]+)/(?:(?P[0-9-]+)/)?$', views_doc.document_main, name="doc_view"), - url(r'^(?P[A-Za-z0-9._+-]+)/history/$', views_doc.document_history, name="doc_history"), - url(r'^(?P[A-Za-z0-9._+-]+)/writeup/$', views_doc.document_writeup, name="doc_writeup"), - url(r'^(?P[A-Za-z0-9._+-]+)/email/$', views_doc.document_email, name="doc_email"), - url(r'^(?P[A-Za-z0-9._+-]+)/shepherdwriteup/$', views_doc.document_shepherd_writeup, name="doc_shepherd_writeup"), - url(r'^(?P[A-Za-z0-9._+-]+)/references/$', views_doc.document_references, name="doc_references"), - url(r'^(?P[A-Za-z0-9._+-]+)/referencedby/$', views_doc.document_referenced_by, name="doc_referenced_by"), - url(r'^(?P[A-Za-z0-9._+-]+)/ballot/(?P[0-9]+)/position/$', views_ballot.edit_position), - url(r'^(?P[A-Za-z0-9._+-]+)/ballot/(?P[0-9]+)/emailposition/$', views_ballot.send_ballot_comment, name='doc_send_ballot_comment'), - url(r'^(?P[A-Za-z0-9._+-]+)/ballot/(?P[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"), - url(r'^(?P[A-Za-z0-9._+-]+)/ballot/$', views_doc.document_ballot, name="doc_ballot"), - (r'^(?P[A-Za-z0-9._+-]+)/doc.json$', views_doc.document_json), - (r'^(?P[A-Za-z0-9._+-]+)/ballotpopup/(?P[0-9]+)/$', views_doc.ballot_popup), + url(r'^%(name)s/(?:%(rev)s/)?$' % settings.URL_REGEXPS, views_doc.document_main, name="doc_view"), + url(r'^%(name)s/history/$' % settings.URL_REGEXPS, views_doc.document_history, name="doc_history"), + url(r'^%(name)s/writeup/$' % settings.URL_REGEXPS, views_doc.document_writeup, name="doc_writeup"), + url(r'^%(name)s/email/$' % settings.URL_REGEXPS, views_doc.document_email, name="doc_email"), + url(r'^%(name)s/shepherdwriteup/$' % settings.URL_REGEXPS, views_doc.document_shepherd_writeup, name="doc_shepherd_writeup"), + url(r'^%(name)s/references/$' % settings.URL_REGEXPS, views_doc.document_references, name="doc_references"), + url(r'^%(name)s/referencedby/$' % settings.URL_REGEXPS, views_doc.document_referenced_by, name="doc_referenced_by"), + url(r'^%(name)s/ballot/(?P[0-9]+)/position/$' % settings.URL_REGEXPS, views_ballot.edit_position), + url(r'^%(name)s/ballot/(?P[0-9]+)/emailposition/$' % settings.URL_REGEXPS, views_ballot.send_ballot_comment, name='doc_send_ballot_comment'), + url(r'^%(name)s/ballot/(?P[0-9]+)/$' % settings.URL_REGEXPS, views_doc.document_ballot, name="doc_ballot"), + url(r'^%(name)s/ballot/$' % settings.URL_REGEXPS, views_doc.document_ballot, name="doc_ballot"), + (r'^%(name)s/doc.json$' % settings.URL_REGEXPS, views_doc.document_json), + (r'^%(name)s/ballotpopup/(?P[0-9]+)/$' % settings.URL_REGEXPS, views_doc.ballot_popup), - url(r'^(?P[A-Za-z0-9._+-]+)/email-aliases/$', RedirectView.as_view(pattern_name='doc_email', permanent=False),name='doc_specific_email_aliases'), + url(r'^%(name)s/email-aliases/$' % settings.URL_REGEXPS, RedirectView.as_view(pattern_name='doc_email', permanent=False),name='doc_specific_email_aliases'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/$', views_draft.change_state, name='doc_change_state'), # IESG state - url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/(?Piana-action|iana-review)/$', views_draft.change_iana_state, name='doc_change_iana_state'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/info/$', views_draft.edit_info, name='doc_edit_info'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/requestresurrect/$', views_draft.request_resurrect, name='doc_request_resurrect'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/submit-to-iesg/$', views_draft.to_iesg, name='doc_to_iesg'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/resurrect/$', views_draft.resurrect, name='doc_resurrect'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/addcomment/$', views_doc.add_comment, name='doc_add_comment'), + url(r'^%(name)s/edit/state/$' % settings.URL_REGEXPS, views_draft.change_state, name='doc_change_state'), # IESG state + url(r'^%(name)s/edit/state/(?Piana-action|iana-review)/$' % settings.URL_REGEXPS, views_draft.change_iana_state, name='doc_change_iana_state'), + url(r'^%(name)s/edit/info/$' % settings.URL_REGEXPS, views_draft.edit_info, name='doc_edit_info'), + url(r'^%(name)s/edit/requestresurrect/$' % settings.URL_REGEXPS, views_draft.request_resurrect, name='doc_request_resurrect'), + url(r'^%(name)s/edit/submit-to-iesg/$' % settings.URL_REGEXPS, views_draft.to_iesg, name='doc_to_iesg'), + url(r'^%(name)s/edit/resurrect/$' % settings.URL_REGEXPS, views_draft.resurrect, name='doc_resurrect'), + url(r'^%(name)s/edit/addcomment/$' % settings.URL_REGEXPS, views_doc.add_comment, name='doc_add_comment'), url(r'^ajax/internet_draft/?$', views_draft.doc_ajax_internet_draft, name="doc_ajax_internet_draft"), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/stream/$', views_draft.change_stream, name='doc_change_stream'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/replaces/$', views_draft.replaces, name='doc_change_replaces'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/notify/$', views_doc.edit_notify, name='doc_change_notify'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/suggested-replaces/$', views_draft.review_possibly_replaces, name='doc_review_possibly_replaces'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/status/$', views_draft.change_intention, name='doc_change_intended_status'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/telechat/$', views_doc.telechat_date, name='doc_change_telechat_date'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/iesgnote/$', views_draft.edit_iesg_note, name='doc_change_iesg_note'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/ad/$', views_draft.edit_ad, name='doc_change_ad'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/consensus/$', views_draft.edit_consensus, name='doc_edit_consensus'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherd/$', views_draft.edit_shepherd, name='doc_edit_shepherd'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherdemail/$', views_draft.change_shepherd_email, name='doc_change_shepherd_email'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherdwriteup/$', views_draft.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_draft.request_publication, name='doc_request_publication'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/adopt/$', views_draft.adopt_draft, name='doc_adopt_draft'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/(?Pdraft-stream-[a-z]+)/$', views_draft.change_stream_state, name='doc_change_stream_state'), + url(r'^%(name)s/edit/stream/$' % settings.URL_REGEXPS, views_draft.change_stream, name='doc_change_stream'), + url(r'^%(name)s/edit/replaces/$' % settings.URL_REGEXPS, views_draft.replaces, name='doc_change_replaces'), + url(r'^%(name)s/edit/notify/$' % settings.URL_REGEXPS, views_doc.edit_notify, name='doc_change_notify'), + url(r'^%(name)s/edit/suggested-replaces/$' % settings.URL_REGEXPS, views_draft.review_possibly_replaces, name='doc_review_possibly_replaces'), + url(r'^%(name)s/edit/status/$' % settings.URL_REGEXPS, views_draft.change_intention, name='doc_change_intended_status'), + url(r'^%(name)s/edit/telechat/$' % settings.URL_REGEXPS, views_doc.telechat_date, name='doc_change_telechat_date'), + url(r'^%(name)s/edit/iesgnote/$' % settings.URL_REGEXPS, views_draft.edit_iesg_note, name='doc_change_iesg_note'), + url(r'^%(name)s/edit/ad/$' % settings.URL_REGEXPS, views_draft.edit_ad, name='doc_change_ad'), + url(r'^%(name)s/edit/consensus/$' % settings.URL_REGEXPS, views_draft.edit_consensus, name='doc_edit_consensus'), + url(r'^%(name)s/edit/shepherd/$' % settings.URL_REGEXPS, views_draft.edit_shepherd, name='doc_edit_shepherd'), + url(r'^%(name)s/edit/shepherdemail/$' % settings.URL_REGEXPS, views_draft.change_shepherd_email, name='doc_change_shepherd_email'), + url(r'^%(name)s/edit/shepherdwriteup/$' % settings.URL_REGEXPS, views_draft.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'), + url(r'^%(name)s/edit/requestpublication/$' % settings.URL_REGEXPS, views_draft.request_publication, name='doc_request_publication'), + url(r'^%(name)s/edit/adopt/$' % settings.URL_REGEXPS, views_draft.adopt_draft, name='doc_adopt_draft'), + url(r'^%(name)s/edit/state/(?Pdraft-stream-[a-z]+)/$' % settings.URL_REGEXPS, views_draft.change_stream_state, name='doc_change_stream_state'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/deferballot/$', views_ballot.defer_ballot, name='doc_defer_ballot'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/undeferballot/$', views_ballot.undefer_ballot, name='doc_undefer_ballot'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/lastcalltext/$', views_ballot.lastcalltext, name='doc_ballot_lastcall'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/ballotwriteupnotes/$', views_ballot.ballot_writeupnotes, name='doc_ballot_writeupnotes'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/approvaltext/$', views_ballot.ballot_approvaltext, name='doc_ballot_approvaltext'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'), + url(r'^%(name)s/edit/clearballot/$' % settings.URL_REGEXPS, views_ballot.clear_ballot, name='doc_clear_ballot'), + url(r'^%(name)s/edit/deferballot/$' % settings.URL_REGEXPS, views_ballot.defer_ballot, name='doc_defer_ballot'), + url(r'^%(name)s/edit/undeferballot/$' % settings.URL_REGEXPS, views_ballot.undefer_ballot, name='doc_undefer_ballot'), + url(r'^%(name)s/edit/lastcalltext/$' % settings.URL_REGEXPS, views_ballot.lastcalltext, name='doc_ballot_lastcall'), + url(r'^%(name)s/edit/ballotwriteupnotes/$' % settings.URL_REGEXPS, views_ballot.ballot_writeupnotes, name='doc_ballot_writeupnotes'), + url(r'^%(name)s/edit/approvaltext/$' % settings.URL_REGEXPS, views_ballot.ballot_approvaltext, name='doc_ballot_approvaltext'), + url(r'^%(name)s/edit/approveballot/$' % settings.URL_REGEXPS, views_ballot.approve_ballot, name='doc_approve_ballot'), + url(r'^%(name)s/edit/makelastcall/$' % settings.URL_REGEXPS, views_ballot.make_last_call, name='doc_make_last_call'), url(r'^help/state/(?P[\w-]+)/$', 'ietf.doc.views_help.state_help', name="state_help"), url(r'^help/relationships/$', 'ietf.doc.views_help.relationship_help', name="relationship_help"), url(r'^help/relationships/(?P\w+)/$', 'ietf.doc.views_help.relationship_help', name="relationship_subset_help"), (r'^(?Pcharter-[A-Za-z0-9._+-]+)/', include('ietf.doc.urls_charter')), - (r'^(?P[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')), - (r'^(?P[A-Za-z0-9._+-]+)/status-change/', include('ietf.doc.urls_status_change')), - (r'^(?P[A-Za-z0-9._+-]+)/material/', include('ietf.doc.urls_material')), + (r'^%(name)s/conflict-review/' % settings.URL_REGEXPS, include('ietf.doc.urls_conflict_review')), + (r'^%(name)s/status-change/' % settings.URL_REGEXPS, include('ietf.doc.urls_status_change')), + (r'^%(name)s/material/' % settings.URL_REGEXPS, include('ietf.doc.urls_material')), ) diff --git a/ietf/doc/urls_charter.py b/ietf/doc/urls_charter.py index 5bc52770f..4b708c52b 100644 --- a/ietf/doc/urls_charter.py +++ b/ietf/doc/urls_charter.py @@ -1,6 +1,7 @@ # Copyright The IETF Trust 2011, All Rights Reserved from django.conf.urls import patterns, url +from django.conf import settings urlpatterns = patterns('', url(r'^state/$', "ietf.doc.views_charter.change_state", name='charter_change_state'), @@ -14,5 +15,5 @@ urlpatterns = patterns('', url(r'^ballotwriteupnotes/$', "ietf.doc.views_charter.ballot_writeupnotes"), url(r'^approve/$', "ietf.doc.views_charter.approve", name='charter_approve'), url(r'^submit/(?:(?P