Manually moved the concepts forward. Reworked some presentation. Working enough to bring testers in at Sprint93
- Legacy-Id: 9717
This commit is contained in:
parent
61474a4988
commit
02a9da52a1
|
@ -4,6 +4,8 @@ from django.utils.html import escape
|
|||
from django import forms
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.doc.models import Document, DocAlias
|
||||
from ietf.doc.utils import uppercase_std_abbreviated_name
|
||||
|
||||
|
|
|
@ -488,3 +488,26 @@ def email_stream_tags_changed(request, doc, added_tags, removed_tags, by, commen
|
|||
by=by,
|
||||
comment=comment))
|
||||
|
||||
def send_review_possibly_replaces_request(request, doc):
|
||||
to_email = []
|
||||
|
||||
if doc.stream_id == "ietf":
|
||||
to_email.extend(r.formatted_email() for r in Role.objects.filter(group=doc.group, name="chair").select_related("email", "person"))
|
||||
elif doc.stream_id == "iab":
|
||||
to_email.append("IAB Stream <iab-stream@iab.org>")
|
||||
elif doc.stream_id == "ise":
|
||||
to_email.append("Independent Submission Editor <rfc-ise@rfc-editor.org>")
|
||||
elif doc.stream_id == "irtf":
|
||||
to_email.append("IRSG <irsg@irtf.org>")
|
||||
|
||||
if not to_email:
|
||||
to_email.append("internet-drafts@ietf.org")
|
||||
|
||||
if to_email:
|
||||
send_mail(request, to_email, settings.DEFAULT_FROM_EMAIL,
|
||||
'Review of suggested possible replacements for %s-%s needed' % (doc.name, doc.rev),
|
||||
'doc/mail/review_possibly_replaces_request.txt', {
|
||||
'doc': doc,
|
||||
'possibly_replaces': doc.related_that_doc("possibly-replaces"),
|
||||
'review_url': settings.IDTRACKER_BASE_URL + urlreverse("doc_review_possibly_replaces", kwargs={ "name": doc.name }),
|
||||
})
|
||||
|
|
|
@ -654,6 +654,8 @@ EVENT_TYPES = [
|
|||
("completed_resurrect", "Completed resurrect"),
|
||||
("changed_consensus", "Changed consensus"),
|
||||
("published_rfc", "Published RFC"),
|
||||
("added_suggested_replaces", "Added suggested replacement relationships"),
|
||||
("reviewed_suggested_replaces", "Reviewed suggested replacement relationships"),
|
||||
|
||||
# WG events
|
||||
("changed_group", "Changed group"),
|
||||
|
|
|
@ -11,7 +11,7 @@ import debug # pyflakes:ignore
|
|||
|
||||
from ietf.doc.models import ( Document, DocAlias, DocReminder, DocumentAuthor, DocEvent,
|
||||
ConsensusDocEvent, LastCallDocEvent, RelatedDocument, State, TelechatDocEvent,
|
||||
WriteupDocEvent, BallotDocEvent)
|
||||
WriteupDocEvent, BallotDocEvent, DocRelationshipName)
|
||||
from ietf.doc.utils import get_tags_for_stream_id
|
||||
from ietf.name.models import StreamName, IntendedStdLevelName, DocTagName
|
||||
from ietf.group.models import Group
|
||||
|
@ -1255,11 +1255,14 @@ class ChangeReplacesTests(TestCase):
|
|||
self.assertEqual(len(q('[type=submit]:contains("Save")')), 1)
|
||||
|
||||
# Post that says replacea replaces base a
|
||||
RelatedDocument.objects.create(source=self.replacea, target=self.basea.docalias_set.first(),
|
||||
relationship=DocRelationshipName.objects.get(slug="possibly-replaces"))
|
||||
self.assertEqual(self.basea.get_state().slug,'active')
|
||||
r = self.client.post(url, dict(replaces=str(DocAlias.objects.get(name=self.basea.name).id)))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(RelatedDocument.objects.filter(relationship__slug='replaces',source=self.replacea).count(),1)
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||
self.assertTrue(not RelatedDocument.objects.filter(relationship='possibly-replaces', source=self.replacea))
|
||||
|
||||
# Post that says replaceboth replaces both base a and base b
|
||||
url = urlreverse('doc_change_replaces', kwargs=dict(name=self.replaceboth.name))
|
||||
|
@ -1281,3 +1284,21 @@ class ChangeReplacesTests(TestCase):
|
|||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'active')
|
||||
|
||||
def test_review_possibly_replaces(self):
|
||||
replaced = self.basea.docalias_set.first()
|
||||
RelatedDocument.objects.create(source=self.replacea, target=replaced,
|
||||
relationship=DocRelationshipName.objects.get(slug="possibly-replaces"))
|
||||
|
||||
url = urlreverse('doc_review_possibly_replaces', kwargs=dict(name=self.replacea.name))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form[name=review-suggested-replaces]')), 1)
|
||||
|
||||
r = self.client.post(url, dict(replaces=[replaced.pk]))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertTrue(not self.replacea.related_that_doc("possibly-replaces"))
|
||||
self.assertEqual(len(self.replacea.related_that_doc("replaces")), 1)
|
||||
self.assertEquals(Document.objects.get(pk=self.basea.pk).get_state().slug, 'repl')
|
||||
|
|
|
@ -79,6 +79,7 @@ urlpatterns = patterns('',
|
|||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/stream/$', views_draft.change_stream, name='doc_change_stream'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/replaces/$', views_draft.replaces, name='doc_change_replaces'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/notify/$', views_doc.edit_notify, name='doc_change_notify'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/suggested-replaces/$', views_draft.review_possibly_replaces, name='doc_review_possibly_replaces'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/status/$', views_draft.change_intention, name='doc_change_intended_status'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/telechat/$', views_doc.telechat_date, name='doc_change_telechat_date'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/iesgnote/$', views_draft.edit_iesg_note, name='doc_change_iesg_note'),
|
||||
|
|
|
@ -10,8 +10,7 @@ from django.db.models.query import EmptyQuerySet
|
|||
from django.forms import ValidationError
|
||||
from django.utils.html import strip_tags, escape
|
||||
|
||||
from ietf.utils import markup_txt
|
||||
from ietf.doc.models import Document, DocHistory
|
||||
from ietf.doc.models import Document, DocHistory, State
|
||||
from ietf.doc.models import DocAlias, RelatedDocument, BallotType, DocReminder
|
||||
from ietf.doc.models import DocEvent, BallotDocEvent, NewRevisionDocEvent, StateDocEvent
|
||||
from ietf.doc.models import save_document_in_history, STATUSCHANGE_RELATIONS
|
||||
|
@ -19,7 +18,7 @@ from ietf.name.models import DocReminderTypeName, DocRelationshipName
|
|||
from ietf.group.models import Role
|
||||
from ietf.person.models import Email
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.utils import draft
|
||||
from ietf.utils import draft, markup_txt
|
||||
from ietf.utils.mail import send_mail
|
||||
|
||||
#FIXME - it would be better if this lived in ietf/doc/mails.py, but there's
|
||||
|
@ -314,9 +313,9 @@ def update_reminder(doc, reminder_type_slug, event, due_date):
|
|||
reminder.active = False
|
||||
reminder.save()
|
||||
|
||||
def prettify_std_name(n):
|
||||
def prettify_std_name(n, spacing=" "):
|
||||
if re.match(r"(rfc|bcp|fyi|std)[0-9]+", n):
|
||||
return n[:3].upper() + " " + n[3:]
|
||||
return n[:3].upper() + spacing + n[3:]
|
||||
else:
|
||||
return n
|
||||
|
||||
|
@ -459,6 +458,75 @@ def rebuild_reference_relations(doc,filename=None):
|
|||
|
||||
return ret
|
||||
|
||||
def collect_email_addresses(emails, doc):
|
||||
for author in doc.authors.all():
|
||||
if author.address not in emails:
|
||||
emails[author.address] = '"%s"' % (author.person.name)
|
||||
if doc.group and doc.group.acronym != 'none':
|
||||
for role in doc.group.role_set.filter(name='chair'):
|
||||
if role.email.address not in emails:
|
||||
emails[role.email.address] = '"%s"' % (role.person.name)
|
||||
if doc.group.type.slug == 'wg':
|
||||
address = '%s-ads@tools.ietf.org' % doc.group.acronym
|
||||
if address not in emails:
|
||||
emails[address] = '"%s-ads"' % (doc.group.acronym)
|
||||
elif doc.group.type.slug == 'rg':
|
||||
for role in doc.group.parent.role_set.filter(name='chair'):
|
||||
if role.email.address not in emails:
|
||||
emails[role.email.address] = '"%s"' % (role.person.name)
|
||||
if doc.shepherd and doc.shepherd.address not in emails:
|
||||
emails[doc.shepherd.address] = u'"%s"' % (doc.shepherd.person.name or "")
|
||||
|
||||
def set_replaces_for_document(request, doc, new_replaces, by, email_subject, email_comment=""):
|
||||
emails = {}
|
||||
collect_email_addresses(emails, doc)
|
||||
|
||||
relationship = DocRelationshipName.objects.get(slug='replaces')
|
||||
old_replaces = doc.related_that_doc("replaces")
|
||||
|
||||
for d in old_replaces:
|
||||
if d not in new_replaces:
|
||||
collect_email_addresses(emails, d.document)
|
||||
RelatedDocument.objects.filter(source=doc, target=d, relationship=relationship).delete()
|
||||
if not RelatedDocument.objects.filter(target=d, relationship=relationship):
|
||||
s = 'active' if d.document.expires > datetime.datetime.now() else 'expired'
|
||||
d.document.set_state(State.objects.get(type='draft', slug=s))
|
||||
|
||||
for d in new_replaces:
|
||||
if d not in old_replaces:
|
||||
collect_email_addresses(emails, d.document)
|
||||
RelatedDocument.objects.create(source=doc, target=d, relationship=relationship)
|
||||
d.document.set_state(State.objects.get(type='draft', slug='repl'))
|
||||
|
||||
e = DocEvent(doc=doc, by=by, type='changed_document')
|
||||
new_replaces_names = u", ".join(d.name for d in new_replaces) or u"None"
|
||||
old_replaces_names = u", ".join(d.name for d in old_replaces) or u"None"
|
||||
e.desc = u"This document now replaces <b>%s</b> instead of %s" % (new_replaces_names, old_replaces_names)
|
||||
e.save()
|
||||
|
||||
# make sure there are no lingering suggestions duplicating new replacements
|
||||
RelatedDocument.objects.filter(source=doc, target__in=new_replaces, relationship="possibly-replaces").delete()
|
||||
|
||||
email_desc = e.desc.replace(", ", "\n ")
|
||||
|
||||
if email_comment:
|
||||
email_desc += "\n" + email_comment
|
||||
|
||||
to = [
|
||||
u'%s <%s>' % (emails[email], email) if emails[email] else u'<%s>' % email
|
||||
for email in sorted(emails)
|
||||
]
|
||||
|
||||
from ietf.doc.mails import html_to_text
|
||||
|
||||
send_mail(request, to,
|
||||
"DraftTracker Mail System <iesg-secretary@ietf.org>",
|
||||
email_subject,
|
||||
"doc/mail/change_notice.txt",
|
||||
dict(text=html_to_text(email_desc),
|
||||
doc=doc,
|
||||
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
|
||||
|
||||
def check_common_doc_name_rules(name):
|
||||
"""Check common rules for document names for use in forms, throws
|
||||
ValidationError in case there's a problem."""
|
||||
|
|
|
@ -345,6 +345,8 @@ def document_main(request, name, rev=None):
|
|||
|
||||
replaces = [d.name for d in doc.related_that_doc("replaces")]
|
||||
replaced_by = [d.name for d in doc.related_that("replaces")]
|
||||
possibly_replaces = [d.name for d in doc.related_that_doc("possibly-replaces")]
|
||||
possibly_replaced_by = [d.name for d in doc.related_that("possibly-replaces")]
|
||||
published = doc.latest_event(type="published_rfc")
|
||||
started_iesg_process = doc.latest_event(type="started_iesg_process")
|
||||
|
||||
|
@ -355,6 +357,8 @@ def document_main(request, name, rev=None):
|
|||
table_rows = dict(doc=4, stream=2, iesg=4, iana=2, rfced=1)
|
||||
table_rows['doc'] += 1 if replaces or can_edit_stream_info else 0
|
||||
table_rows['doc'] += 1 if replaced_by else 0
|
||||
table_rows['doc'] += 1 if possibly_replaces else 0
|
||||
table_rows['doc'] += 1 if possibly_replaced_by else 0
|
||||
table_rows['doc'] += 1 if doc.get_state_slug() != "rfc" else 0
|
||||
table_rows['doc'] += 1 if conflict_reviews else 0
|
||||
|
||||
|
@ -396,6 +400,8 @@ def document_main(request, name, rev=None):
|
|||
|
||||
replaces=replaces,
|
||||
replaced_by=replaced_by,
|
||||
possibly_replaces=possibly_replaces,
|
||||
possibly_replaced_by=possibly_replaced_by,
|
||||
updates=[prettify_std_name(d.name) for d in doc.related_that_doc("updates")],
|
||||
updated_by=[prettify_std_name(d.document.canonical_name()) for d in doc.related_that("updates")],
|
||||
obsoletes=[prettify_std_name(d.name) for d in doc.related_that_doc("obs")],
|
||||
|
|
|
@ -13,16 +13,17 @@ from django.contrib.auth.decorators import login_required
|
|||
from django.template.defaultfilters import pluralize
|
||||
from django.contrib import messages
|
||||
|
||||
from ietf.doc.models import ( Document, DocAlias, DocRelationshipName, RelatedDocument, State,
|
||||
from ietf.doc.models import ( Document, DocAlias, RelatedDocument, State,
|
||||
StateType, DocEvent, ConsensusDocEvent, TelechatDocEvent, WriteupDocEvent, IESG_SUBSTATE_TAGS,
|
||||
save_document_in_history )
|
||||
from ietf.doc.mails import ( email_ad, email_pulled_from_rfc_queue, email_resurrect_requested,
|
||||
email_resurrection_completed, email_state_changed, email_stream_changed,
|
||||
email_stream_state_changed, email_stream_tags_changed, extra_automation_headers,
|
||||
generate_publication_request, html_to_text )
|
||||
generate_publication_request )
|
||||
from ietf.doc.utils import ( add_state_change_event, can_adopt_draft,
|
||||
get_tags_for_stream_id, nice_consensus,
|
||||
update_reminder, update_telechat, make_notify_changed_event, get_initial_notify )
|
||||
update_reminder, update_telechat, make_notify_changed_event, get_initial_notify,
|
||||
set_replaces_for_document )
|
||||
from ietf.doc.lastcall import request_last_call
|
||||
from ietf.doc.fields import SearchableDocAliasesField
|
||||
from ietf.group.models import Group, Role
|
||||
|
@ -287,26 +288,6 @@ def doc_ajax_internet_draft(request):
|
|||
response = [dict(id=r.id, label=r.name) for r in results]
|
||||
return response
|
||||
|
||||
def collect_email_addresses(emails, doc):
|
||||
for author in doc.authors.all():
|
||||
if author.address not in emails:
|
||||
emails[author.address] = '"%s"' % (author.person.name)
|
||||
if doc.group.acronym != 'none':
|
||||
for role in doc.group.role_set.filter(name='chair'):
|
||||
if role.email.address not in emails:
|
||||
emails[role.email.address] = '"%s"' % (role.person.name)
|
||||
if doc.group.type.slug == 'wg':
|
||||
address = '%s-ads@tools.ietf.org' % doc.group.acronym
|
||||
if address not in emails:
|
||||
emails[address] = '"%s-ads"' % (doc.group.acronym)
|
||||
elif doc.group.type.slug == 'rg':
|
||||
for role in doc.group.parent.role_set.filter(name='chair'):
|
||||
if role.email.address not in emails:
|
||||
emails[role.email.address] = '"%s"' % (role.person.name)
|
||||
if doc.shepherd and doc.shepherd.address not in emails:
|
||||
emails[doc.shepherd.address] = u'"%s"' % (doc.shepherd.person.name or "")
|
||||
return emails
|
||||
|
||||
class ReplacesForm(forms.Form):
|
||||
replaces = SearchableDocAliasesField(required=False)
|
||||
comment = forms.CharField(widget=forms.Textarea, required=False)
|
||||
|
@ -333,68 +314,96 @@ def replaces(request, name):
|
|||
if not (has_role(request.user, ("Secretariat", "Area Director"))
|
||||
or is_authorized_in_doc_stream(request.user, doc)):
|
||||
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
|
||||
login = request.user.person
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ReplacesForm(request.POST, doc=doc)
|
||||
if form.is_valid():
|
||||
new_replaces = set(form.cleaned_data['replaces'])
|
||||
comment = form.cleaned_data['comment'].strip()
|
||||
old_replaces = set(doc.related_that_doc("replaces"))
|
||||
by = request.user.person
|
||||
|
||||
if new_replaces != old_replaces:
|
||||
save_document_in_history(doc)
|
||||
emails = {}
|
||||
emails = collect_email_addresses(emails, doc)
|
||||
relationship = DocRelationshipName.objects.get(slug='replaces')
|
||||
for d in old_replaces:
|
||||
if d not in new_replaces:
|
||||
emails = collect_email_addresses(emails, d.document)
|
||||
RelatedDocument.objects.filter(source=doc, target=d, relationship=relationship).delete()
|
||||
if not RelatedDocument.objects.filter(target=d, relationship=relationship):
|
||||
d.document.set_state(State.objects.get(type='draft',slug='active' if d.document.expires>datetime.datetime.now() else 'expired'))
|
||||
for d in new_replaces:
|
||||
if d not in old_replaces:
|
||||
emails = collect_email_addresses(emails, d.document)
|
||||
RelatedDocument.objects.create(source=doc, target=d, relationship=relationship)
|
||||
d.document.set_state(State.objects.get(type='draft',slug='repl'))
|
||||
e = DocEvent(doc=doc,by=login,type='changed_document')
|
||||
new_replaces_names = ", ".join([d.name for d in new_replaces])
|
||||
if not new_replaces_names:
|
||||
new_replaces_names = "None"
|
||||
old_replaces_names = ", ".join([d.name for d in old_replaces])
|
||||
if not old_replaces_names:
|
||||
old_replaces_names = "None"
|
||||
e.desc = u"This document now replaces <b>%s</b> instead of %s"% (new_replaces_names, old_replaces_names)
|
||||
e.save()
|
||||
email_desc = e.desc.replace(", ", "\n ")
|
||||
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.time = datetime.datetime.now()
|
||||
doc.save()
|
||||
email_list = []
|
||||
for key in sorted(emails):
|
||||
if emails[key]:
|
||||
email_list.append('%s <%s>' % (emails[key], key))
|
||||
else:
|
||||
email_list.append('<%s>' % key)
|
||||
email_string = ", ".join(email_list)
|
||||
send_mail(request, email_string,
|
||||
"DraftTracker Mail System <iesg-secretary@ietf.org>",
|
||||
"%s updated by %s" % (doc.name, login),
|
||||
"doc/mail/change_notice.txt",
|
||||
dict(text=html_to_text(email_desc),
|
||||
doc=doc,
|
||||
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
|
||||
|
||||
set_replaces_for_document(request, doc, new_replaces, by=by,
|
||||
email_subject="%s replacement status updated by %s" % (doc.name, by),
|
||||
email_comment=comment)
|
||||
|
||||
if comment:
|
||||
DocEvent.objects.create(doc=doc, by=by, type="added_comment", desc=comment)
|
||||
|
||||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
else:
|
||||
form = ReplacesForm(doc=doc)
|
||||
return render_to_response('doc/draft/change_replaces.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'doc/draft/change_replaces.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
))
|
||||
|
||||
class SuggestedReplacesForm(forms.Form):
|
||||
replaces = forms.ModelMultipleChoiceField(queryset=DocAlias.objects.all(),
|
||||
label="Suggestions", required=False, widget=forms.CheckboxSelectMultiple,
|
||||
help_text="Select only the documents that are replaced by this document")
|
||||
comment = forms.CharField(label="Optional comment", widget=forms.Textarea, required=False)
|
||||
|
||||
def __init__(self, suggested, *args, **kwargs):
|
||||
super(SuggestedReplacesForm, self).__init__(*args, **kwargs)
|
||||
pks = [d.pk for d in suggested]
|
||||
self.fields["replaces"].initial = pks
|
||||
self.fields["replaces"].queryset = self.fields["replaces"].queryset.filter(pk__in=pks)
|
||||
self.fields["replaces"].choices = [(d.pk, d.name) for d in suggested]
|
||||
|
||||
def review_possibly_replaces(request, name):
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.type_id != 'draft':
|
||||
raise Http404
|
||||
if not (has_role(request.user, ("Secretariat", "Area Director"))
|
||||
or is_authorized_in_doc_stream(request.user, doc)):
|
||||
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
|
||||
|
||||
suggested = list(doc.related_that_doc("possibly-replaces"))
|
||||
if not suggested:
|
||||
raise Http404
|
||||
|
||||
if request.method == 'POST':
|
||||
form = SuggestedReplacesForm(suggested, request.POST)
|
||||
if form.is_valid():
|
||||
replaces = set(form.cleaned_data['replaces'])
|
||||
old_replaces = set(doc.related_that_doc("replaces"))
|
||||
new_replaces = old_replaces.union(replaces)
|
||||
|
||||
comment = form.cleaned_data['comment'].strip()
|
||||
by = request.user.person
|
||||
|
||||
save_document_in_history(doc)
|
||||
doc.time = datetime.datetime.now()
|
||||
doc.save()
|
||||
|
||||
# all suggestions reviewed, so get rid of them
|
||||
DocEvent.objects.create(doc=doc, by=by, type="reviewed_suggested_replaces",
|
||||
desc="Reviewed suggested replacement relationships: %s" % ", ".join(d.name for d in suggested))
|
||||
RelatedDocument.objects.filter(source=doc, target__in=suggested,relationship__slug='possibly-replaces').delete()
|
||||
|
||||
if new_replaces != old_replaces:
|
||||
set_replaces_for_document(request, doc, new_replaces, by=by,
|
||||
email_subject="%s replacement status updated by %s" % (doc.name, by),
|
||||
email_comment=comment)
|
||||
|
||||
if comment:
|
||||
DocEvent.objects.create(doc=doc, by=by, type="added_comment", desc=comment)
|
||||
|
||||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
else:
|
||||
form = SuggestedReplacesForm(suggested)
|
||||
|
||||
return render(request, 'doc/draft/review_possibly_replaces.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
))
|
||||
|
||||
|
||||
class ChangeIntentionForm(forms.Form):
|
||||
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.filter(used=True), empty_label="(None)", required=True, label="Intended RFC status")
|
||||
|
|
|
@ -293,6 +293,17 @@
|
|||
"model": "name.docrelationshipname",
|
||||
"pk": "toexp"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"revname": "Possibly Replaced By",
|
||||
"used": true,
|
||||
"name": "Possibly Replaces",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.docrelationshipname",
|
||||
"pk": "possibly-replaces"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 3,
|
||||
|
|
19
ietf/name/migrations/0005_add_sug_replaces.py
Normal file
19
ietf/name/migrations/0005_add_sug_replaces.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def add_possibly_replaces(apps, schema_editor):
|
||||
|
||||
DocRelationshipName = apps.get_model("name","DocRelationshipName")
|
||||
DocRelationshipName.objects.create(slug='possibly-replaces',name='Possibly Replaces',revname='Possibly Replaced By')
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('name', '0004_auto_20150318_1140'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_possibly_replaces)
|
||||
]
|
|
@ -11,6 +11,7 @@ import debug # pyflakes:ignore
|
|||
|
||||
from ietf.group.models import Group
|
||||
from ietf.doc.models import Document
|
||||
from ietf.doc.fields import SearchableDocAliasesField
|
||||
from ietf.meeting.models import Meeting
|
||||
from ietf.submit.models import Submission, Preapproval
|
||||
from ietf.submit.utils import validate_submission_rev, validate_submission_document_date
|
||||
|
@ -248,6 +249,9 @@ class NameEmailForm(forms.Form):
|
|||
line += u" <%s>" % email
|
||||
return line
|
||||
|
||||
class ReplacesForm(forms.Form):
|
||||
replaces = SearchableDocAliasesField(required=False, help_text="Any drafts that this document replaces (approval required for replacing a draft you are not the author of)")
|
||||
|
||||
class EditSubmissionForm(forms.ModelForm):
|
||||
title = forms.CharField(required=True, max_length=255)
|
||||
rev = forms.CharField(label=u'Revision', max_length=2, required=True)
|
||||
|
|
20
ietf/submit/migrations/0003_auto_20150713_1104.py
Normal file
20
ietf/submit/migrations/0003_auto_20150713_1104.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('submit', '0002_auto_20150430_0847'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='submission',
|
||||
name='replaces',
|
||||
field=models.CharField(max_length=1000, blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
|
@ -33,7 +33,7 @@ class Submission(models.Model):
|
|||
pages = models.IntegerField(null=True, blank=True)
|
||||
authors = models.TextField(blank=True, help_text="List of author names and emails, one author per line, e.g. \"John Doe <john@example.org>\".")
|
||||
note = models.TextField(blank=True)
|
||||
replaces = models.CharField(max_length=255, blank=True)
|
||||
replaces = models.CharField(max_length=1000, blank=True)
|
||||
|
||||
first_two_pages = models.TextField(blank=True)
|
||||
file_types = models.CharField(max_length=50, blank=True)
|
||||
|
|
|
@ -16,7 +16,7 @@ from ietf.utils.test_utils import TestCase
|
|||
from ietf.submit.utils import expirable_submissions, expire_submission, ensure_person_email_info_exists
|
||||
from ietf.person.models import Person
|
||||
from ietf.group.models import Group
|
||||
from ietf.doc.models import Document, DocEvent, State, BallotDocEvent, BallotPositionDocEvent, DocumentAuthor
|
||||
from ietf.doc.models import Document, DocAlias, DocEvent, State, BallotDocEvent, BallotPositionDocEvent, DocumentAuthor
|
||||
from ietf.submit.models import Submission, Preapproval
|
||||
|
||||
class SubmitTests(TestCase):
|
||||
|
@ -85,7 +85,7 @@ class SubmitTests(TestCase):
|
|||
|
||||
return status_url
|
||||
|
||||
def supply_submitter(self, name, status_url, submitter_name, submitter_email):
|
||||
def supply_extra_metadata(self, name, status_url, submitter_name, submitter_email, replaces):
|
||||
# check the page
|
||||
r = self.client.get(status_url)
|
||||
q = PyQuery(r.content)
|
||||
|
@ -98,10 +98,12 @@ class SubmitTests(TestCase):
|
|||
"action": action,
|
||||
"submitter-name": submitter_name,
|
||||
"submitter-email": submitter_email,
|
||||
"replaces": replaces,
|
||||
})
|
||||
|
||||
submission = Submission.objects.get(name=name)
|
||||
self.assertEqual(submission.submitter, u"%s <%s>" % (submitter_name, submitter_email))
|
||||
self.assertEqual(submission.replaces, ",".join(d.name for d in DocAlias.objects.filter(pk__in=replaces.split(",") if replaces else [])))
|
||||
|
||||
return r
|
||||
|
||||
|
@ -121,6 +123,27 @@ class SubmitTests(TestCase):
|
|||
# submit new -> supply submitter info -> approve
|
||||
draft = make_test_data()
|
||||
|
||||
# prepare draft to suggest replace
|
||||
sug_replaced_draft = Document.objects.create(
|
||||
name="draft-ietf-ames-sug-replaced",
|
||||
time=datetime.datetime.now(),
|
||||
type_id="draft",
|
||||
title="Draft to be suggested to be replaced",
|
||||
stream_id="ietf",
|
||||
group=Group.objects.get(acronym="ames"),
|
||||
abstract="Blahblahblah.",
|
||||
rev="01",
|
||||
pages=2,
|
||||
intended_std_level_id="ps",
|
||||
ad=draft.ad,
|
||||
expires=datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
|
||||
notify="aliens@example.mars",
|
||||
note="",
|
||||
)
|
||||
sug_replaced_draft.set_state(State.objects.get(used=True, type="draft", slug="active"))
|
||||
sug_replaced_alias = DocAlias.objects.create(document=sug_replaced_draft, name=sug_replaced_draft.name)
|
||||
|
||||
|
||||
name = "draft-ietf-mars-testing-tests"
|
||||
rev = "00"
|
||||
|
||||
|
@ -128,7 +151,9 @@ class SubmitTests(TestCase):
|
|||
|
||||
# supply submitter info, then draft should be in and ready for approval
|
||||
mailbox_before = len(outbox)
|
||||
r = self.supply_submitter(name, status_url, "Author Name", "author@example.com")
|
||||
replaced_alias = draft.docalias_set.first()
|
||||
r = self.supply_extra_metadata(name, status_url, "Author Name", "author@example.com",
|
||||
replaces=str(replaced_alias.pk) + "," + str(sug_replaced_alias.pk))
|
||||
|
||||
self.assertEqual(r.status_code, 302)
|
||||
status_url = r["Location"]
|
||||
|
@ -154,10 +179,11 @@ class SubmitTests(TestCase):
|
|||
|
||||
draft = Document.objects.get(docalias__name=name)
|
||||
self.assertEqual(draft.rev, rev)
|
||||
new_revision = draft.latest_event()
|
||||
new_revision = draft.latest_event(type="new_revision")
|
||||
self.assertEqual(draft.group.acronym, "mars")
|
||||
self.assertEqual(new_revision.type, "new_revision")
|
||||
self.assertEqual(new_revision.by.name, "Author Name")
|
||||
self.assertTrue(draft.latest_event(type="added_suggested_replaces"))
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.repository_dir, u"%s-%s.txt" % (name, rev))))
|
||||
self.assertEqual(draft.type_id, "draft")
|
||||
|
@ -167,12 +193,19 @@ class SubmitTests(TestCase):
|
|||
self.assertEqual(draft.authors.count(), 1)
|
||||
self.assertEqual(draft.authors.all()[0].get_name(), "Author Name")
|
||||
self.assertEqual(draft.authors.all()[0].address, "author@example.com")
|
||||
self.assertEqual(len(outbox), mailbox_before + 2)
|
||||
self.assertTrue((u"I-D Action: %s" % name) in outbox[-2]["Subject"])
|
||||
self.assertTrue("Author Name" in unicode(outbox[-2]))
|
||||
self.assertTrue("New Version Notification" in outbox[-1]["Subject"])
|
||||
self.assertEqual(draft.relations_that_doc("replaces").count(), 1)
|
||||
self.assertTrue(draft.relations_that_doc("replaces").first().target, replaced_alias)
|
||||
self.assertEqual(draft.relations_that_doc("possibly-replaces").count(), 1)
|
||||
self.assertTrue(draft.relations_that_doc("possibly-replaces").first().target, sug_replaced_alias)
|
||||
self.assertEqual(len(outbox), mailbox_before + 4)
|
||||
self.assertTrue((u"I-D Action: %s" % name) in outbox[-3]["Subject"])
|
||||
self.assertTrue("Author Name" in unicode(outbox[-3]))
|
||||
self.assertTrue("New Version Notification" in outbox[-2]["Subject"])
|
||||
self.assertTrue(name in unicode(outbox[-2]))
|
||||
self.assertTrue("mars" in unicode(outbox[-2]))
|
||||
self.assertTrue("review" in outbox[-1]["Subject"].lower())
|
||||
self.assertTrue(name in unicode(outbox[-1]))
|
||||
self.assertTrue("mars" in unicode(outbox[-1]))
|
||||
self.assertTrue(sug_replaced_alias.name in unicode(outbox[-1]))
|
||||
|
||||
def test_submit_existing(self):
|
||||
# submit new revision of existing -> supply submitter info -> prev authors confirm
|
||||
|
@ -214,7 +247,7 @@ class SubmitTests(TestCase):
|
|||
|
||||
# supply submitter info, then previous authors get a confirmation email
|
||||
mailbox_before = len(outbox)
|
||||
r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com")
|
||||
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "submitter@example.com", replaces="")
|
||||
self.assertEqual(r.status_code, 302)
|
||||
status_url = r["Location"]
|
||||
r = self.client.get(status_url)
|
||||
|
@ -284,7 +317,7 @@ class SubmitTests(TestCase):
|
|||
|
||||
# supply submitter info, then draft should be be ready for email auth
|
||||
mailbox_before = len(outbox)
|
||||
r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com")
|
||||
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "submitter@example.com", replaces="")
|
||||
|
||||
self.assertEqual(r.status_code, 302)
|
||||
status_url = r["Location"]
|
||||
|
@ -374,7 +407,7 @@ class SubmitTests(TestCase):
|
|||
|
||||
def test_edit_submission_and_force_post(self):
|
||||
# submit -> edit
|
||||
make_test_data()
|
||||
draft = make_test_data()
|
||||
|
||||
name = "draft-ietf-mars-testing-tests"
|
||||
rev = "00"
|
||||
|
@ -412,6 +445,7 @@ class SubmitTests(TestCase):
|
|||
"edit-pages": "123",
|
||||
"submitter-name": "Some Random Test Person",
|
||||
"submitter-email": "random@example.com",
|
||||
"replaces": str(draft.docalias_set.all().first().pk),
|
||||
"edit-note": "no comments",
|
||||
"authors-0-name": "Person 1",
|
||||
"authors-0-email": "person1@example.com",
|
||||
|
@ -428,6 +462,7 @@ class SubmitTests(TestCase):
|
|||
self.assertEqual(submission.pages, 123)
|
||||
self.assertEqual(submission.note, "no comments")
|
||||
self.assertEqual(submission.submitter, "Some Random Test Person <random@example.com>")
|
||||
self.assertEqual(submission.replaces, draft.docalias_set.all().first().name)
|
||||
self.assertEqual(submission.state_id, "manual")
|
||||
|
||||
authors = submission.authors_parsed()
|
||||
|
|
|
@ -4,8 +4,12 @@ import datetime
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.doc.models import Document, State, DocAlias, DocEvent, DocumentAuthor, NewRevisionDocEvent, save_document_in_history
|
||||
from ietf.doc.models import Document, State, DocAlias, DocEvent, DocumentAuthor
|
||||
from ietf.doc.models import NewRevisionDocEvent, save_document_in_history
|
||||
from ietf.doc.models import RelatedDocument, DocRelationshipName
|
||||
from ietf.doc.utils import add_state_change_event, rebuild_reference_relations
|
||||
from ietf.doc.utils import set_replaces_for_document
|
||||
from ietf.doc.mails import send_review_possibly_replaces_request
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.name.models import StreamName
|
||||
|
@ -202,12 +206,62 @@ def post_submission(request, submission):
|
|||
move_files_to_repository(submission)
|
||||
submission.state = DraftSubmissionStateName.objects.get(slug="posted")
|
||||
|
||||
new_replaces, new_possibly_replaces = update_replaces_from_submission(request, submission, draft)
|
||||
|
||||
announce_to_lists(request, submission)
|
||||
announce_new_version(request, submission, draft, state_change_msg)
|
||||
announce_to_authors(request, submission)
|
||||
|
||||
if new_possibly_replaces:
|
||||
send_review_possibly_replaces_request(request, draft)
|
||||
|
||||
submission.save()
|
||||
|
||||
def update_replaces_from_submission(request, submission, draft):
|
||||
if not submission.replaces:
|
||||
return [], []
|
||||
|
||||
is_secretariat = has_role(request.user, "Secretariat")
|
||||
is_chair_of = []
|
||||
if request.user.is_authenticated():
|
||||
is_chair_of = list(Group.objects.filter(role__person__user=request.user, role__name="chair"))
|
||||
|
||||
replaces = DocAlias.objects.filter(name__in=submission.replaces.split(",")).select_related("document", "document__group")
|
||||
existing_replaces = list(draft.related_that_doc("replaces"))
|
||||
existing_suggested = set(draft.related_that_doc("possibly-replaces"))
|
||||
|
||||
submitter_email = submission.submitter_parsed()["email"]
|
||||
|
||||
approved = []
|
||||
suggested = []
|
||||
for r in replaces:
|
||||
if r in existing_replaces:
|
||||
continue
|
||||
|
||||
rdoc = r.document
|
||||
|
||||
if (is_secretariat
|
||||
or (draft.group in is_chair_of and (rdoc.group.type_id == "individ" or rdoc.group in is_chair_of))
|
||||
or (submitter_email and rdoc.authors.filter(address__iexact=submitter_email)).exists()):
|
||||
approved.append(r)
|
||||
else:
|
||||
if r not in existing_suggested:
|
||||
suggested.append(r)
|
||||
|
||||
by = request.user.person if request.user.is_authenticated() else Person.objects.get(name="(System)")
|
||||
set_replaces_for_document(request, draft, existing_replaces + approved, by,
|
||||
email_subject="%s replacement status set during submit by %s" % (draft.name, submission.submitter_parsed()["name"]))
|
||||
|
||||
|
||||
if suggested:
|
||||
possibly_replaces = DocRelationshipName.objects.get(slug="possibly-replaces")
|
||||
for r in suggested:
|
||||
RelatedDocument.objects.create(source=draft, target=r, relationship=possibly_replaces)
|
||||
|
||||
DocEvent.objects.create(doc=draft, by=by, type="added_suggested_replaces",
|
||||
desc="Added suggested replacement relationships: %s" % ", ".join(d.name for d in suggested))
|
||||
|
||||
return approved, suggested
|
||||
|
||||
def get_person_from_name_email(name, email):
|
||||
# try email
|
||||
|
|
|
@ -6,14 +6,13 @@ from django.conf import settings
|
|||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.core.validators import validate_email, ValidationError
|
||||
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
|
||||
from ietf.doc.models import Document
|
||||
from ietf.doc.models import Document, DocAlias
|
||||
from ietf.doc.utils import prettify_std_name
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role, role_required
|
||||
from ietf.submit.forms import UploadForm, NameEmailForm, EditSubmissionForm, PreapprovalForm
|
||||
from ietf.submit.forms import UploadForm, NameEmailForm, EditSubmissionForm, PreapprovalForm, ReplacesForm
|
||||
from ietf.submit.mail import send_full_url, send_approval_request_to_group, send_submission_confirmation, submission_confirmation_email_list, send_manual_post_request
|
||||
from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName
|
||||
from ietf.submit.utils import approvable_submissions_for_user, preapprovals_for_user, recently_approved_by_user
|
||||
|
@ -98,18 +97,15 @@ def upload_submission(request):
|
|||
else:
|
||||
form = UploadForm(request=request)
|
||||
|
||||
return render_to_response('submit/upload_submission.html',
|
||||
return render(request, 'submit/upload_submission.html',
|
||||
{'selected': 'index',
|
||||
'form': form},
|
||||
context_instance=RequestContext(request))
|
||||
'form': form})
|
||||
|
||||
def note_well(request):
|
||||
return render_to_response('submit/note_well.html', {'selected': 'notewell'},
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'submit/note_well.html', {'selected': 'notewell'})
|
||||
|
||||
def tool_instructions(request):
|
||||
return render_to_response('submit/tool_instructions.html', {'selected': 'instructions'},
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'submit/tool_instructions.html', {'selected': 'instructions'})
|
||||
|
||||
def search_submission(request):
|
||||
error = None
|
||||
|
@ -120,11 +116,10 @@ def search_submission(request):
|
|||
if submission:
|
||||
return redirect(submission_status, submission_id=submission.pk)
|
||||
error = 'No valid submission found for %s' % name
|
||||
return render_to_response('submit/search_submission.html',
|
||||
return render(request, 'submit/search_submission.html',
|
||||
{'selected': 'status',
|
||||
'error': error,
|
||||
'name': name},
|
||||
context_instance=RequestContext(request))
|
||||
'name': name})
|
||||
|
||||
def can_edit_submission(user, submission, access_token):
|
||||
key_matched = access_token and submission.access_token() == access_token
|
||||
|
@ -153,12 +148,7 @@ def submission_status(request, submission_id, access_token=None):
|
|||
|
||||
confirmation_list = submission_confirmation_email_list(submission)
|
||||
|
||||
try:
|
||||
preapproval = Preapproval.objects.get(name=submission.name)
|
||||
except Preapproval.DoesNotExist:
|
||||
preapproval = None
|
||||
|
||||
requires_group_approval = submission.rev == '00' and submission.group and submission.group.type_id in ("wg", "rg", "ietf", "irtf", "iab", "iana", "rfcedtyp") and not preapproval
|
||||
requires_group_approval = (submission.rev == '00' and submission.group and submission.group.type_id in ("wg", "rg", "ietf", "irtf", "iab", "iana", "rfcedtyp") and not Preapproval.objects.filter(name=submission.name).exists())
|
||||
|
||||
requires_prev_authors_approval = Document.objects.filter(name=submission.name)
|
||||
|
||||
|
@ -175,6 +165,7 @@ def submission_status(request, submission_id, access_token=None):
|
|||
|
||||
|
||||
submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
|
||||
replaces_form = ReplacesForm(initial=DocAlias.objects.filter(name__in=submission.replaces.split(",")))
|
||||
|
||||
if request.method == 'POST':
|
||||
action = request.POST.get('action')
|
||||
|
@ -183,8 +174,12 @@ def submission_status(request, submission_id, access_token=None):
|
|||
return HttpResponseForbidden("You do not have permission to perfom this action")
|
||||
|
||||
submitter_form = NameEmailForm(request.POST, prefix="submitter")
|
||||
if submitter_form.is_valid():
|
||||
replaces_form = ReplacesForm(request.POST)
|
||||
validations = [submitter_form.is_valid(), replaces_form.is_valid()]
|
||||
if all(validations):
|
||||
submission.submitter = submitter_form.cleaned_line()
|
||||
replaces = replaces_form.cleaned_data.get("replaces", [])
|
||||
submission.replaces = ",".join(o.name for o in replaces)
|
||||
|
||||
if requires_group_approval:
|
||||
submission.state = DraftSubmissionStateName.objects.get(slug="grp-appr")
|
||||
|
@ -209,7 +204,11 @@ def submission_status(request, submission_id, access_token=None):
|
|||
else:
|
||||
desc = u"sent confirmation email to submitter and authors: %s" % u", ".join(sent_to)
|
||||
|
||||
create_submission_event(request, submission, u"Set submitter to \"%s\" and %s" % (submission.submitter, desc))
|
||||
msg = u"Set submitter to \"%s\", replaces to %s and %s" % (
|
||||
submission.submitter,
|
||||
", ".join(prettify_std_name(r.name) for r in replaces) if replaces else "(none)",
|
||||
desc)
|
||||
create_submission_event(request, submission, msg)
|
||||
|
||||
if access_token:
|
||||
return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_token=access_token)
|
||||
|
@ -271,23 +270,23 @@ def submission_status(request, submission_id, access_token=None):
|
|||
# something went wrong, turn this into a GET and let the user deal with it
|
||||
return HttpResponseRedirect("")
|
||||
|
||||
return render_to_response('submit/submission_status.html',
|
||||
{'selected': 'status',
|
||||
'submission': submission,
|
||||
'errors': errors,
|
||||
'passes_idnits': passes_idnits,
|
||||
'submitter_form': submitter_form,
|
||||
'message': message,
|
||||
'can_edit': can_edit,
|
||||
'can_force_post': can_force_post,
|
||||
'can_group_approve': can_group_approve,
|
||||
'can_cancel': can_cancel,
|
||||
'show_send_full_url': show_send_full_url,
|
||||
'requires_group_approval': requires_group_approval,
|
||||
'requires_prev_authors_approval': requires_prev_authors_approval,
|
||||
'confirmation_list': confirmation_list,
|
||||
},
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'submit/submission_status.html', {
|
||||
'selected': 'status',
|
||||
'submission': submission,
|
||||
'errors': errors,
|
||||
'passes_idnits': passes_idnits,
|
||||
'submitter_form': submitter_form,
|
||||
'replaces_form': replaces_form,
|
||||
'message': message,
|
||||
'can_edit': can_edit,
|
||||
'can_force_post': can_force_post,
|
||||
'can_group_approve': can_group_approve,
|
||||
'can_cancel': can_cancel,
|
||||
'show_send_full_url': show_send_full_url,
|
||||
'requires_group_approval': requires_group_approval,
|
||||
'requires_prev_authors_approval': requires_prev_authors_approval,
|
||||
'confirmation_list': confirmation_list,
|
||||
})
|
||||
|
||||
|
||||
def edit_submission(request, submission_id, access_token=None):
|
||||
|
@ -312,14 +311,17 @@ def edit_submission(request, submission_id, access_token=None):
|
|||
|
||||
edit_form = EditSubmissionForm(request.POST, instance=submission, prefix="edit")
|
||||
submitter_form = NameEmailForm(request.POST, prefix="submitter")
|
||||
replaces_form = ReplacesForm(request.POST)
|
||||
author_forms = [ NameEmailForm(request.POST, email_required=False, prefix=prefix)
|
||||
for prefix in request.POST.getlist("authors-prefix")
|
||||
if prefix != "authors-" ]
|
||||
|
||||
# trigger validation of all forms
|
||||
validations = [edit_form.is_valid(), submitter_form.is_valid()] + [ f.is_valid() for f in author_forms ]
|
||||
validations = [edit_form.is_valid(), submitter_form.is_valid(), replaces_form.is_valid()] + [ f.is_valid() for f in author_forms ]
|
||||
if all(validations):
|
||||
submission.submitter = submitter_form.cleaned_line()
|
||||
replaces = replaces_form.cleaned_data.get("replaces", [])
|
||||
submission.replaces = ",".join(o.name for o in replaces)
|
||||
submission.authors = "\n".join(f.cleaned_line() for f in author_forms)
|
||||
edit_form.save(commit=False) # transfer changes
|
||||
|
||||
|
@ -350,20 +352,21 @@ def edit_submission(request, submission_id, access_token=None):
|
|||
else:
|
||||
edit_form = EditSubmissionForm(instance=submission, prefix="edit")
|
||||
submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
|
||||
replaces_form = ReplacesForm(initial=DocAlias.objects.filter(name__in=submission.replaces.split(",")))
|
||||
author_forms = [ NameEmailForm(initial=author, email_required=False, prefix="authors-%s" % i)
|
||||
for i, author in enumerate(submission.authors_parsed()) ]
|
||||
|
||||
return render_to_response('submit/edit_submission.html',
|
||||
return render(request, 'submit/edit_submission.html',
|
||||
{'selected': 'status',
|
||||
'submission': submission,
|
||||
'edit_form': edit_form,
|
||||
'submitter_form': submitter_form,
|
||||
'replaces_form': replaces_form,
|
||||
'author_forms': author_forms,
|
||||
'empty_author_form': empty_author_form,
|
||||
'errors': errors,
|
||||
'form_errors': form_errors,
|
||||
},
|
||||
context_instance=RequestContext(request))
|
||||
})
|
||||
|
||||
|
||||
def confirm_submission(request, submission_id, auth_token):
|
||||
|
@ -379,10 +382,10 @@ def confirm_submission(request, submission_id, auth_token):
|
|||
|
||||
return redirect("doc_view", name=submission.name)
|
||||
|
||||
return render_to_response('submit/confirm_submission.html', {
|
||||
return render(request, 'submit/confirm_submission.html', {
|
||||
'submission': submission,
|
||||
'key_matched': key_matched,
|
||||
}, context_instance=RequestContext(request))
|
||||
})
|
||||
|
||||
|
||||
def approvals(request):
|
||||
|
@ -392,13 +395,12 @@ def approvals(request):
|
|||
days = 30
|
||||
recently_approved = recently_approved_by_user(request.user, datetime.date.today() - datetime.timedelta(days=days))
|
||||
|
||||
return render_to_response('submit/approvals.html',
|
||||
return render(request, 'submit/approvals.html',
|
||||
{'selected': 'approvals',
|
||||
'approvals': approvals,
|
||||
'preapprovals': preapprovals,
|
||||
'recently_approved': recently_approved,
|
||||
'days': days },
|
||||
context_instance=RequestContext(request))
|
||||
'days': days })
|
||||
|
||||
|
||||
@role_required("Secretariat", "WG Chair", "RG Chair")
|
||||
|
@ -421,11 +423,10 @@ def add_preapproval(request):
|
|||
else:
|
||||
form = PreapprovalForm()
|
||||
|
||||
return render_to_response('submit/add_preapproval.html',
|
||||
return render(request, 'submit/add_preapproval.html',
|
||||
{'selected': 'approvals',
|
||||
'groups': groups,
|
||||
'form': form },
|
||||
context_instance=RequestContext(request))
|
||||
'form': form })
|
||||
|
||||
@role_required("Secretariat", "WG Chair", "RG Chair")
|
||||
def cancel_preapproval(request, preapproval_id):
|
||||
|
@ -439,7 +440,6 @@ def cancel_preapproval(request, preapproval_id):
|
|||
|
||||
return HttpResponseRedirect(urlreverse("submit_approvals") + "#preapprovals")
|
||||
|
||||
return render_to_response('submit/cancel_preapproval.html',
|
||||
return render(request, 'submit/cancel_preapproval.html',
|
||||
{'selected': 'approvals',
|
||||
'preapproval': preapproval },
|
||||
context_instance=RequestContext(request))
|
||||
'preapproval': preapproval })
|
||||
|
|
|
@ -93,7 +93,35 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if possibly_replaces %}
|
||||
<tr>
|
||||
<th>Possibly Replaces</th>
|
||||
<td class="edit">
|
||||
{% if can_edit_stream_info %}
|
||||
<a class="btn btn-default btn-xs" href="{% url "doc_review_possibly_replaces" name=doc.name %}">Edit</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ possibly_replaces|join:", "|urlize_ietf_docs }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if possibly_replaced_by %}
|
||||
<tr>
|
||||
<th>Possibly Replaced By</th>
|
||||
<td class="edit">
|
||||
{% if can_edit_stream_info %}
|
||||
{% comment %}<a class="btn btn-default btn-xs" href="{% url "doc_review_possibly_replaces" name=doc.name %}">Edit</a>{% endcomment %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ possibly_replaced_by|join:", "|urlize_ietf_docs }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<th>Stream</th>
|
||||
<td class="edit">
|
||||
|
|
23
ietf/templates/doc/draft/review_possibly_replaces.html
Normal file
23
ietf/templates/doc/draft/review_possibly_replaces.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Review suggestions for documents that {{ doc }} replaces{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>Review suggestions for documents that {{ doc }} replaces</h1>
|
||||
|
||||
<form name="review-suggested-replaces" role="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<a class="btn btn-default pull-right" href="{{ doc.get_absolute_url }}">Cancel</a>
|
||||
<button type="submit" value="Save" class="btn btn-primary">Save</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
12
ietf/templates/doc/mail/review_possibly_replaces_request.txt
Normal file
12
ietf/templates/doc/mail/review_possibly_replaces_request.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% autoescape off %}
|
||||
{{ doc }} is suggested to replace:
|
||||
|
||||
{% for d in possibly_replaces %} {{ d.name }}
|
||||
{% endfor %}
|
||||
|
||||
Please visit
|
||||
|
||||
{{ review_url }}
|
||||
|
||||
and either accept or decline the suggestion{{ suggested_replaces|pluralize:"s" }}.
|
||||
{% endautoescape %}
|
|
@ -69,6 +69,7 @@
|
|||
|
||||
<h3>Submitter</h3>
|
||||
{% include "submit/submitter_form.html" %}
|
||||
{% include "submit/replaces_form.html" %}
|
||||
|
||||
{% for form in author_forms %}
|
||||
<div {% if forloop.last %}id="cloner"{% endif %}>
|
||||
|
|
12
ietf/templates/submit/replaces_form.html
Normal file
12
ietf/templates/submit/replaces_form.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% for field in replaces_form %}
|
||||
<tr{% if field.errors %} class="error"{% endif %}>
|
||||
<th>{{ field.label_tag }}</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<div class="helptext">{{ field.help_text }}</div>
|
||||
{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
|
@ -6,6 +6,12 @@
|
|||
|
||||
{% block title %}Submission status of {{ submission.name }}-{{ submission.rev }}{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" href="/css/lib/select2.css">
|
||||
<link rel="stylesheet" href="/css/lib/select2-bootstrap.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block submit_content %}
|
||||
{% origin %}
|
||||
{% if submission.state_id != "uploaded" %}
|
||||
|
@ -26,7 +32,7 @@
|
|||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if submitter_form.errors %}
|
||||
{% if submitter_form.errors or replaces_form.errors %}
|
||||
<p class="alert alert-danger">Please fix errors in the form below.</p>
|
||||
{% endif %}
|
||||
|
||||
|
@ -218,6 +224,7 @@
|
|||
<form class="idsubmit" method="post">
|
||||
{% csrf_token %}
|
||||
{% include "submit/submitter_form.html" %}
|
||||
{% include "submit/replaces_form.html" %}
|
||||
<input type="hidden" name="action" value="autopost">
|
||||
<button class="btn btn-primary" type="submit">Post submission</button>
|
||||
</form>
|
||||
|
@ -243,6 +250,12 @@
|
|||
<tr><th>Email address</th><td>{{ submission.submitter_parsed.email }}</td></tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if submission.replaces %}
|
||||
<h3>Replaced documents</h3>
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr><th>Replaces</th><td>{{ submission.replaces|split:","|join:", "|urlize_ietf_docs }}</td></tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if can_cancel %}
|
||||
|
@ -321,3 +334,8 @@
|
|||
{% include "submit/problem-reports-footer.html" %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="/js/lib/select2-3.5.2.min.js"></script>
|
||||
<script src="/js/select2-field.js"></script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue