From 2070bbfed6a45c508d4fd13d51c612f082b462f2 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 28 Jun 2017 14:55:10 +0000 Subject: [PATCH] Merged in [13722] and [13712] from rcross@amsl.com: Remove all use of request.session from secretariat apps. Add tests for affected views. Fixes #1455. - Legacy-Id: 13724 Note: SVN reference [13712] has been migrated to Git commit 915b445bcffcd4687fb1677230b98282d22dd9a0 Note: SVN reference [13722] has been migrated to Git commit 3a2cb9d3d846168f75d9796b4e5383bdf10b1f60 --- ietf/secr/announcement/forms.py | 8 + ietf/secr/announcement/tests.py | 31 +- ietf/secr/announcement/views.py | 64 ++-- ietf/secr/drafts/email.py | 36 +- ietf/secr/drafts/forms.py | 20 +- ietf/secr/drafts/tests_views.py | 203 ++++++++++- ietf/secr/drafts/urls.py | 2 +- ietf/secr/drafts/views.py | 321 +++++++++--------- ietf/secr/rolodex/forms.py | 13 - ietf/secr/rolodex/tests.py | 24 +- ietf/secr/rolodex/views.py | 24 +- ietf/secr/sreq/forms.py | 9 + ietf/secr/sreq/tests.py | 25 +- ietf/secr/sreq/views.py | 67 ++-- ietf/secr/templates/announcement/confirm.html | 5 +- ietf/secr/templates/announcement/main.html | 4 +- ietf/secr/templates/drafts/confirm.html | 4 +- ietf/secr/templates/drafts/email.html | 2 +- ietf/secr/templates/drafts/view.html | 2 +- .../includes/sessions_request_form.html | 2 +- ietf/secr/templates/rolodex/add.html | 17 +- ietf/secr/templates/sreq/confirm.html | 5 +- ietf/secr/utils/decorators.py | 8 - ietf/utils/log.py | 2 + 24 files changed, 571 insertions(+), 327 deletions(-) diff --git a/ietf/secr/announcement/forms.py b/ietf/secr/announcement/forms.py index 38abf889b..575c535f5 100644 --- a/ietf/secr/announcement/forms.py +++ b/ietf/secr/announcement/forms.py @@ -138,6 +138,10 @@ class AnnounceForm(forms.ModelForm): fields = ('nomcom', 'to','to_custom','frm','cc','bcc','reply_to','subject','body') def __init__(self, *args, **kwargs): + if 'hidden' in kwargs: + self.hidden = kwargs.pop('hidden') + else: + self.hidden = False user = kwargs.pop('user') person = user.person super(AnnounceForm, self).__init__(*args, **kwargs) @@ -154,6 +158,10 @@ class AnnounceForm(forms.ModelForm): if not nomcom_roles and not secr_roles: self.fields['nomcom'].widget = forms.HiddenInput() self.initial['reply_to'] = 'ietf@ietf.org' + + if self.hidden: + for key in self.fields.keys(): + self.fields[key].widget = forms.HiddenInput() def clean(self): super(AnnounceForm, self).clean() diff --git a/ietf/secr/announcement/tests.py b/ietf/secr/announcement/tests.py index d9a5948f2..ac7ea0f3e 100644 --- a/ietf/secr/announcement/tests.py +++ b/ietf/secr/announcement/tests.py @@ -4,9 +4,12 @@ from django.urls import reverse from pyquery import PyQuery from ietf.utils.test_utils import TestCase +from ietf.group.models import Group +from ietf.message.models import Message +from ietf.nomcom.test_data import nomcom_test_data from ietf.person.models import Person from ietf.utils.test_data import make_test_data - +from ietf.utils.mail import outbox, empty_outbox SECR_USER='secretary' WG_USER='' @@ -41,7 +44,6 @@ class SubmitAnnouncementCase(TestCase): make_test_data() url = reverse('ietf.secr.announcement.views.main') post_data = {'id_subject':''} - #self.client.login(username='rcross', password='rcross+password") self.client.login(username="secretary", password="secretary+password") r = self.client.post(url,post_data) self.assertEqual(r.status_code, 200) @@ -51,17 +53,26 @@ class SubmitAnnouncementCase(TestCase): def test_valid_submit(self): "Valid Submit" make_test_data() - #ietf.utils.mail.test_mode = True + nomcom_test_data() + empty_outbox() url = reverse('ietf.secr.announcement.views.main') - redirect = reverse('ietf.secr.announcement.views.confirm') - post_data = {'to':'Other...', + confirm_url = reverse('ietf.secr.announcement.views.confirm') + nomcom = Group.objects.get(type='nomcom') + post_data = {'nomcom': nomcom.pk, + 'to':'Other...', 'to_custom':'rcross@amsl.com', 'frm':'IETF Secretariat <ietf-secretariat@ietf.org>', 'subject':'Test Subject', 'body':'This is a test.'} self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url,post_data,follow=True) - self.assertRedirects(r, redirect) - # good enough if we get to confirm page - #self.assertEqual(len(outbox), 1) - #self.assertTrue(len(outbox) > mailbox_before) + response = self.client.post(url,post_data) + self.assertEqual(response.status_code, 200) + self.assertTrue('Confirm Announcement' in response.content) + response = self.client.post(confirm_url,post_data,follow=True) + self.assertRedirects(response, url) + self.assertEqual(len(outbox),1) + self.assertEqual(outbox[0]['subject'],'Test Subject') + self.assertEqual(outbox[0]['to'],'') + message = Message.objects.last() + self.assertEqual(message.subject,'Test Subject') + self.assertTrue(nomcom in message.related_groups.all()) diff --git a/ietf/secr/announcement/views.py b/ietf/secr/announcement/views.py index c65f82e4a..3d0c4464e 100644 --- a/ietf/secr/announcement/views.py +++ b/ietf/secr/announcement/views.py @@ -6,7 +6,7 @@ from django.shortcuts import render, redirect from ietf.group.models import Role from ietf.ietfauth.utils import has_role from ietf.secr.announcement.forms import AnnounceForm -from ietf.secr.utils.decorators import check_for_cancel, clear_non_auth +from ietf.secr.utils.decorators import check_for_cancel from ietf.utils.mail import send_mail_text # ------------------------------------------------- @@ -59,13 +59,18 @@ def main(request): form = AnnounceForm(request.POST or None,user=request.user) if form.is_valid(): - # nomcom is a ModelChoice, store pk, not Group object - data = form.cleaned_data - if data['nomcom']: - data['nomcom'] = data['nomcom'].pk - request.session['data'] = data + # recast as hidden form for next page of process + form = AnnounceForm(request.POST, user=request.user, hidden=True) + if form.data['to'] == 'Other...': + to = form.data['to_custom'] + else: + to = form.data['to'] - return redirect('ietf.secr.announcement.views.confirm') + return render(request, 'announcement/confirm.html', { + 'message': form.data, + 'to': to, + 'form': form}, + ) return render(request, 'announcement/main.html', { 'form': form} ) @@ -73,37 +78,26 @@ def main(request): @check_for_cancel('../') def confirm(request): - if request.session.get('data',None): - data = request.session['data'] - else: - messages.error(request, 'No session data. Your session may have expired or cookies are disallowed.') - return redirect('ietf.secr.announcement.views.main') + if not check_access(request.user): + return HttpResponseForbidden('Restricted to: Secretariat, IAD, or chair of IETF, IAB, RSOC, RSE, IAOC, ISOC, NomCom.') if request.method == 'POST': - form = AnnounceForm(data, user=request.user) - message = form.save(user=request.user,commit=True) - extra = {'Reply-To':message.reply_to} - send_mail_text(None, - message.to, - message.frm, - message.subject, - message.body, - cc=message.cc, - bcc=message.bcc, - extra=extra) + form = AnnounceForm(request.POST, user=request.user) + if request.method == 'POST': + message = form.save(user=request.user,commit=True) + extra = {'Reply-To':message.reply_to} + send_mail_text(None, + message.to, + message.frm, + message.subject, + message.body, + cc=message.cc, + bcc=message.bcc, + extra=extra) + + messages.success(request, 'The announcement was sent.') + return redirect('ietf.secr.announcement.views.main') - # clear session - clear_non_auth(request.session) - messages.success(request, 'The announcement was sent.') - return redirect('ietf.secr.announcement.views.main') - if data['to'] == 'Other...': - to = ','.join(data['to_custom']) - else: - to = data['to'] - return render(request, 'announcement/confirm.html', { - 'message': data, - 'to': to}, - ) diff --git a/ietf/secr/drafts/email.py b/ietf/secr/drafts/email.py index 49583d990..723c743ca 100644 --- a/ietf/secr/drafts/email.py +++ b/ietf/secr/drafts/email.py @@ -170,7 +170,7 @@ def get_fullcc_list(draft): return ','.join(result_list) -def get_email_initial(draft, type=None, input=None): +def get_email_initial(draft, action=None, input=None): """ Takes a draft object, a string representing the email type: (extend,new,replace,resurrect,revision,update,withdraw) and @@ -182,7 +182,6 @@ def get_email_initial(draft, type=None, input=None): It appears datatracker abbreviates the list with "et al". Datatracker scheduled_announcement entries have "Action" in subject whereas this app uses "ACTION" """ - # assert False, (draft, type, input) expiration_date = (datetime.date.today() + datetime.timedelta(185)).strftime('%B %d, %Y') new_revision = str(int(draft.rev)+1).zfill(2) new_filename = draft.name + '-' + new_revision + '.txt' @@ -190,12 +189,15 @@ def get_email_initial(draft, type=None, input=None): data = {} data['cc'] = get_fullcc_list(draft) data['to'] = '' - if type == 'extend': + data['action'] = action + + if action == 'extend': context = {'doc':curr_filename,'expire_date':input['expiration_date']} data['subject'] = 'Extension of Expiration Date for %s' % (curr_filename) data['body'] = render_to_string('drafts/message_extend.txt', context) + data['expiration_date'] = input['expiration_date'] - elif type == 'new': + elif action == 'new': # if the ID belongs to a group other than "none" add line to message body if draft.group.type.slug == 'wg': wg_message = 'This draft is a work item of the %s Working Group of the IETF.' % draft.group.name @@ -211,37 +213,43 @@ def get_email_initial(draft, type=None, input=None): data['subject'] = 'I-D ACTION:%s' % (curr_filename) data['body'] = render_to_string('drafts/message_new.txt', context) - elif type == 'replace': + elif action == 'replace': ''' input['replaced'] is a DocAlias input['replaced_by'] is a Document ''' - context = {'doc':input['replaced'].name,'replaced_by':input['replaced_by'].name} - data['subject'] = 'Replacement of %s with %s' % (input['replaced'].name,input['replaced_by'].name) + context = {'doc':input['replaced'],'replaced_by':input['replaced_by']} + data['subject'] = 'Replacement of %s with %s' % (input['replaced'],input['replaced_by']) data['body'] = render_to_string('drafts/message_replace.txt', context) - - elif type == 'resurrect': + data['replaced'] = input['replaced'] + data['replaced_by'] = input['replaced_by'] + + elif action == 'resurrect': last_revision = get_last_revision(draft.name) last_filename = draft.name + '-' + last_revision + '.txt' context = {'doc':last_filename,'expire_date':expiration_date} data['subject'] = 'Resurrection of %s' % (last_filename) data['body'] = render_to_string('drafts/message_resurrect.txt', context) + data['action'] = action - elif type == 'revision': + elif action == 'revision': context = {'rev':new_revision,'doc':new_filename,'doc_base':new_filename[:-4]} data['to'] = get_revision_emails(draft) data['cc'] = '' data['subject'] = 'New Version Notification - %s' % (new_filename) data['body'] = render_to_string('drafts/message_revision.txt', context) - elif type == 'update': + elif action == 'update': context = {'doc':input['filename'],'expire_date':expiration_date} data['subject'] = 'Posting of %s' % (input['filename']) data['body'] = render_to_string('drafts/message_update.txt', context) - - elif type == 'withdraw': - context = {'doc':curr_filename,'by':input['type']} + data['action'] = action + + elif action == 'withdraw': + context = {'doc':curr_filename,'by':input['withdraw_type']} data['subject'] = 'Withdrawl of %s' % (curr_filename) data['body'] = render_to_string('drafts/message_withdraw.txt', context) + data['action'] = action + data['withdraw_type'] = input['withdraw_type'] return data diff --git a/ietf/secr/drafts/forms.py b/ietf/secr/drafts/forms.py index f05cebb6a..846cb1f0a 100644 --- a/ietf/secr/drafts/forms.py +++ b/ietf/secr/drafts/forms.py @@ -213,12 +213,30 @@ class EditModelForm(forms.ModelForm): class EmailForm(forms.Form): # max_lengths come from db limits, cc is not limited + action = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) + expiration_date = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) + withdraw_type = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) + replaced = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) + replaced_by = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) + filename = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) to = forms.CharField(max_length=255) cc = forms.CharField(required=False) subject = forms.CharField(max_length=255) body = forms.CharField(widget=forms.Textarea(), strip=False) + def __init__(self, *args, **kwargs): + if 'hidden' in kwargs: + self.hidden = kwargs.pop('hidden') + else: + self.hidden = False + super(EmailForm, self).__init__(*args, **kwargs) + + if self.hidden: + for key in self.fields.keys(): + self.fields[key].widget = forms.HiddenInput() + class ExtendForm(forms.Form): + action = forms.CharField(max_length=255, widget=forms.HiddenInput(),initial='extend') expiration_date = forms.DateField() class ReplaceForm(forms.Form): @@ -369,5 +387,5 @@ class UploadForm(forms.Form): return self.cleaned_data class WithdrawForm(forms.Form): - type = forms.CharField(widget=forms.Select(choices=WITHDRAW_CHOICES),help_text='Select which type of withdraw to perform.') + withdraw_type = forms.CharField(widget=forms.Select(choices=WITHDRAW_CHOICES),help_text='Select which type of withdraw to perform.') diff --git a/ietf/secr/drafts/tests_views.py b/ietf/secr/drafts/tests_views.py index 417890b0d..5061a7712 100644 --- a/ietf/secr/drafts/tests_views.py +++ b/ietf/secr/drafts/tests_views.py @@ -1,19 +1,24 @@ +import datetime +import os import shutil from StringIO import StringIO from django.conf import settings from django.urls import reverse as urlreverse +from django.utils.http import urlencode import debug # pyflakes:ignore -from ietf.doc.models import State +from ietf.doc.expire import expire_draft +from ietf.doc.models import State, Document from ietf.person.models import Person from ietf.submit.models import Preapproval +from ietf.submit.tests import submission_file from ietf.utils.test_utils import TestCase from ietf.utils.test_data import make_test_data +from ietf.utils.mail import empty_outbox from ietf.secr.drafts.email import get_email_initial -from pyquery import PyQuery SECR_USER='secretary' @@ -100,13 +105,34 @@ class SecrDraftsTestCase(TestCase): draft = make_test_data() state = State.objects.get(type='draft-iesg',slug='rfcqueue') draft.set_state(state) - data = get_email_initial(draft,type='revision') + data = get_email_initial(draft,action='revision') self.assertTrue('rfc-editor@rfc-editor.org' in data['to']) + def test_revision(self): + draft = make_test_data() + url = urlreverse('ietf.secr.drafts.views.revision', kwargs={'id':draft.name}) + view_url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + post_data = { + 'title': draft.title, + 'pages': str(draft.pages), + 'abstract': draft.abstract, + } + files = {} + files['txt'] = submission_file(draft.name, '02', draft.group, 'txt', "test_submission.txt") + post_data.update(files) + response = self.client.post(url, post_data) + self.assertRedirects(response, view_url) + draft = Document.objects.get(name=draft.name) + self.assertEqual(draft.rev, '02') + def test_revision_rfcqueue(self): # Makes sure that a manual posting by the Secretariat of an I-D that is # in the RFC Editor Queue will result in notification of the RFC Editor draft = make_test_data() + empty_outbox() state = State.objects.get(type='draft-iesg',slug='rfcqueue') draft.set_state(state) url = urlreverse('ietf.secr.drafts.views.revision', kwargs={'id':draft.name}) @@ -117,9 +143,9 @@ class SecrDraftsTestCase(TestCase): post = {'title':'The Title','pages':'10','txt':file} response = self.client.post(url,post,follow=True) self.assertEqual(response.status_code, 200) - q = PyQuery(response.content) - self.assertTrue('rfc-editor@rfc-editor.org' in q("#draft-confirm-email tr:first-child td").html()) - + # addresses = ','.join([ m['To'] for m in outbox ]) + # self.assertTrue('rfc-editor@rfc-editor.org' in addresses) + def test_makerfc(self): draft = make_test_data() url = urlreverse('ietf.secr.drafts.views.edit', kwargs={'id':draft.name}) @@ -142,11 +168,48 @@ class SecrDraftsTestCase(TestCase): def test_update(self): draft = make_test_data() + path = os.path.join(self.repository_dir, draft.filename_with_rev()) + with open(path, 'w') as file: + file.write('test') + expire_draft(draft) url = urlreverse('ietf.secr.drafts.views.update', kwargs={'id':draft.name}) + email_url = urlreverse('ietf.secr.drafts.views.email', kwargs={'id':draft.name}) + confirm_url = urlreverse('ietf.secr.drafts.views.confirm', kwargs={'id':draft.name}) + do_action_url = urlreverse('ietf.secr.drafts.views.do_action', kwargs={'id':draft.name}) + view_url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) self.client.login(username="secretary", password="secretary+password") response = self.client.get(url) self.assertEqual(response.status_code, 200) - + post_data = { + 'title': draft.title, + 'pages': str(draft.pages), + 'abstract': draft.abstract, + } + formats = ['txt',] + files = {} + for format in formats: + files[format] = submission_file(draft.name, '02', draft.group, format, "test_submission.%s" % format) + post_data.update(files) + response = self.client.post(url, post_data) + self.assertRedirects(response, email_url + '?action=update&filename=%s-02' % (draft.name)) + post_data = { + 'action': 'update', + 'to': 'john@example.com', + 'cc': 'joe@example.com', + 'subject': 'test', + 'body': 'text', + 'submit': 'Save' + } + response = self.client.post(email_url + '?action=update&filename=%s-02' % (draft.name), post_data) + response = self.client.post(confirm_url, post_data) + response = self.client.post(do_action_url, post_data) + self.assertRedirects(response, view_url) + draft = Document.objects.get(name=draft.name) + expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE) + self.assertTrue(draft.get_state_slug('draft') == 'active') + self.assertEqual(draft.rev, '02') + self.assertEqual(draft.expires.replace(second=0,microsecond=0), expires.replace(second=0,microsecond=0)) + def test_view(self): draft = make_test_data() url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) @@ -167,3 +230,129 @@ class SecrDraftsTestCase(TestCase): self.assertRedirects(response, redirect_url) self.assertFalse(draft.documentauthor_set.filter(id=id)) + def test_resurrect(self): + draft = make_test_data() + path = os.path.join(self.repository_dir, draft.filename_with_rev()) + with open(path, 'w') as file: + file.write('test') + expire_draft(draft) + email_url = urlreverse('ietf.secr.drafts.views.email', kwargs={'id':draft.name}) + "?action=resurrect" + confirm_url = urlreverse('ietf.secr.drafts.views.confirm', kwargs={'id':draft.name}) + do_action_url = urlreverse('ietf.secr.drafts.views.do_action', kwargs={'id':draft.name}) + view_url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(email_url) + self.assertEqual(response.status_code, 200) + post_data = { + 'action': 'resurrect', + 'to': 'john@example.com', + 'cc': 'joe@example.com', + 'subject': 'test', + 'body': 'draft resurrected', + 'submit': 'Save' + } + response = self.client.post(email_url, post_data) + response = self.client.post(confirm_url, post_data) + response = self.client.post(do_action_url, post_data) + self.assertRedirects(response, view_url) + draft = Document.objects.get(name=draft.name) + self.assertTrue(draft.get_state_slug('draft') == 'active') + + def test_extend(self): + draft = make_test_data() + url = urlreverse('ietf.secr.drafts.views.extend', kwargs={'id':draft.name}) + email_url = urlreverse('ietf.secr.drafts.views.email', kwargs={'id':draft.name}) + confirm_url = urlreverse('ietf.secr.drafts.views.confirm', kwargs={'id':draft.name}) + do_action_url = urlreverse('ietf.secr.drafts.views.do_action', kwargs={'id':draft.name}) + view_url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) + expiration = datetime.datetime.today() + datetime.timedelta(days=180) + expiration = expiration.replace(hour=0,minute=0,second=0,microsecond=0) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + get_data = { + 'action': 'extend', + 'expiration_date': expiration.strftime('%Y-%m-%d'), + } + post_data = { + 'action': 'extend', + 'expiration_date': expiration.strftime('%Y-%m-%d'), + 'to': 'john@example.com', + 'cc': 'joe@example.com', + 'subject': 'test', + 'body': 'draft resurrected', + 'submit': 'Save' + } + response = self.client.get(email_url + '?' + urlencode(get_data)) + self.assertEqual(response.status_code, 200) + response = self.client.post(confirm_url, post_data) + response = self.client.post(do_action_url, post_data) + self.assertRedirects(response, view_url) + draft = Document.objects.get(name=draft.name) + self.assertTrue(draft.expires == expiration) + + def test_withdraw(self): + draft = make_test_data() + url = urlreverse('ietf.secr.drafts.views.withdraw', kwargs={'id':draft.name}) + email_url = urlreverse('ietf.secr.drafts.views.email', kwargs={'id':draft.name}) + confirm_url = urlreverse('ietf.secr.drafts.views.confirm', kwargs={'id':draft.name}) + do_action_url = urlreverse('ietf.secr.drafts.views.do_action', kwargs={'id':draft.name}) + view_url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + get_data = { + 'action': 'withdraw', + 'withdraw_type': 'ietf', + } + post_data = { + 'action': 'withdraw', + 'withdraw_type': 'ietf', + 'to': 'john@example.com', + 'cc': 'joe@example.com', + 'subject': 'test', + 'body': 'draft resurrected', + 'submit': 'Save' + } + response = self.client.get(email_url + '?' + urlencode(get_data)) + self.assertEqual(response.status_code, 200) + response = self.client.post(confirm_url, post_data) + response = self.client.post(do_action_url, post_data) + self.assertRedirects(response, view_url) + draft = Document.objects.get(name=draft.name) + self.assertTrue(draft.get_state_slug('draft') == 'ietf-rm') + + def test_replace(self): + draft = make_test_data() + other_draft = Document.objects.filter(type='draft').exclude(name=draft.name).first() + url = urlreverse('ietf.secr.drafts.views.replace', kwargs={'id':draft.name}) + email_url = urlreverse('ietf.secr.drafts.views.email', kwargs={'id':draft.name}) + confirm_url = urlreverse('ietf.secr.drafts.views.confirm', kwargs={'id':draft.name}) + do_action_url = urlreverse('ietf.secr.drafts.views.do_action', kwargs={'id':draft.name}) + view_url = urlreverse('ietf.secr.drafts.views.view', kwargs={'id':draft.name}) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + get_data = { + 'action': 'replace', + 'replaced': draft.name, + 'replaced_by': other_draft.name, + } + post_data = { + 'action': 'replace', + 'replaced': draft.name, + 'replaced_by': other_draft.name, + 'to': 'john@example.com', + 'cc': 'joe@example.com', + 'subject': 'test', + 'body': 'draft resurrected', + 'submit': 'Save' + } + response = self.client.get(email_url + '?' + urlencode(get_data)) + self.assertEqual(response.status_code, 200) + response = self.client.post(confirm_url, post_data) + response = self.client.post(do_action_url, post_data) + self.assertRedirects(response, view_url) + draft = Document.objects.get(name=draft.name) + self.assertTrue(draft.get_state_slug('draft') == 'repl') + diff --git a/ietf/secr/drafts/urls.py b/ietf/secr/drafts/urls.py index 6e0f5a8be..c76a863ab 100644 --- a/ietf/secr/drafts/urls.py +++ b/ietf/secr/drafts/urls.py @@ -14,12 +14,12 @@ urlpatterns = [ url(r'^(?P[A-Za-z0-9._\-\+]+)/authors/$', views.authors), url(r'^(?P[A-Za-z0-9._\-\+]+)/author_delete/(?P\d{1,6})$', views.author_delete), url(r'^(?P[A-Za-z0-9._\-\+]+)/confirm/$', views.confirm), + url(r'^(?P[A-Za-z0-9._\-\+]+)/do_action/$', views.do_action), url(r'^(?P[A-Za-z0-9._\-\+]+)/edit/$', views.edit), url(r'^(?P[A-Za-z0-9._\-\+]+)/extend/$', views.extend), url(r'^(?P[A-Za-z0-9._\-\+]+)/email/$', views.email), url(r'^(?P[A-Za-z0-9._\-\+]+)/makerfc/$', views.makerfc), url(r'^(?P[A-Za-z0-9._\-\+]+)/replace/$', views.replace), - url(r'^(?P[A-Za-z0-9._\-\+]+)/resurrect/$', views.resurrect), url(r'^(?P[A-Za-z0-9._\-\+]+)/revision/$', views.revision), url(r'^(?P[A-Za-z0-9._\-\+]+)/update/$', views.update), url(r'^(?P[A-Za-z0-9._\-\+]+)/withdraw/$', views.withdraw), diff --git a/ietf/secr/drafts/views.py b/ietf/secr/drafts/views.py index 3f0cce315..fabb17c0a 100644 --- a/ietf/secr/drafts/views.py +++ b/ietf/secr/drafts/views.py @@ -2,12 +2,15 @@ import datetime import glob import os import shutil +from dateutil.parser import parse from django.conf import settings from django.contrib import messages from django.db.models import Max from django.forms.formsets import formset_factory from django.shortcuts import render, get_object_or_404, redirect +from django.urls import reverse +from django.utils.http import urlencode from ietf.doc.models import Document, DocumentAuthor, DocAlias, DocRelationshipName, RelatedDocument, State from ietf.doc.models import DocEvent, NewRevisionDocEvent @@ -21,9 +24,9 @@ from ietf.secr.drafts.forms import ( AddModelForm, AuthorForm, BaseRevisionModel EmailForm, ExtendForm, ReplaceForm, RevisionModelForm, RfcModelForm, RfcObsoletesForm, SearchForm, UploadForm, WithdrawForm ) from ietf.secr.utils.ams_utils import get_base -from ietf.secr.utils.decorators import clear_non_auth from ietf.secr.utils.document import get_rfc_num, get_start_date from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName, SubmissionEvent +from ietf.submit.mail import announce_new_version, announce_to_lists, announce_to_authors from ietf.utils.draft import Draft @@ -41,19 +44,21 @@ def archive_draft_files(filename): shutil.move(file,settings.INTERNET_DRAFT_ARCHIVE_DIR) return -def get_action_details(draft, session): +def get_action_details(draft, request): ''' - This function takes a draft object and session object and returns a list of dictionaries + This function takes a draft object and request object and returns a list of dictionaries with keys: label, value to be used in displaying information on the confirmation page. ''' result = [] - if session['action'] == 'revision': - m = {'label':'New Revision','value':session['revision']} + data = request.POST + + if data['action'] == 'revision': + m = {'label':'New Revision','value':data['revision']} result.append(m) - if session['action'] == 'replace': - m = {'label':'Replaced By:','value':session['data']['replaced_by']} + if data['action'] == 'replace': + m = {'label':'Replaced By:','value':data['replaced_by']} result.append(m) return result @@ -93,31 +98,28 @@ def process_files(request,draft): the files by calling handle_file_upload() and returns the basename, revision number and a list of file types. Basename and revision are assumed to be the same for all because this is part of the validation process. - - It also creates the Submission record WITHOUT saving, and places it in the - session for saving in the final action step. ''' files = request.FILES file = files[files.keys()[0]] filename = os.path.splitext(file.name)[0] revision = os.path.splitext(file.name)[0][-2:] - basename = get_base(filename) file_type_list = [] for file in files.values(): extension = os.path.splitext(file.name)[1] file_type_list.append(extension) - if extension == '.txt': - txt_size = file.size - wrapper = Draft(file.read().decode('utf8'),file.name) handle_uploaded_file(file) - # create Submission record, leaved unsaved - idsub = Submission( - name=basename, + return (filename,revision,file_type_list) + +def post_submission(request, draft): + with open(draft.get_file_name()) as file: + wrapper = Draft(file.read().decode('utf8'), file.name) + submission = Submission( + name=draft.name, title=draft.title, - rev=revision, + rev=draft.rev, pages=draft.pages, - file_size=txt_size, + file_size=os.path.getsize(draft.get_file_name()), document_date=wrapper.get_creation_date(), submission_date=datetime.date.today(), group_id=draft.group.id, @@ -125,14 +127,8 @@ def process_files(request,draft): first_two_pages=''.join(wrapper.pages[:2]), state=DraftSubmissionStateName.objects.get(slug="posted"), abstract=draft.abstract, - file_types=','.join(file_type_list), + file_types=','.join(file_types_for_draft(draft)), ) - request.session['idsub'] = idsub - - return (filename,revision,file_type_list) - -def post_submission(request): - submission = request.session['idsub'] submission.save() SubmissionEvent.objects.create( @@ -140,6 +136,18 @@ def post_submission(request): by=request.user.person, desc="Submitted and posted manually") + return submission + +def file_types_for_draft(draft): + '''Returns list of file extensions that exist for this draft''' + basename, ext = os.path.splitext(draft.get_file_name()) + files = glob.glob(basename + '.*') + file_types = [] + for filename in files: + base, ext = os.path.splitext(filename) + if ext: + file_types.append(ext) + return file_types def promote_files(draft, types): ''' @@ -160,7 +168,6 @@ moving files, etc. Generally speaking the action buttons trigger a multi-page sequence where information may be gathered using a custom form, an email may be produced and presented to the user to edit, and only then when confirmation is given will the action work take place. That's when these functions are called. -The details of the action are stored in request.session. ''' def do_extend(draft, request): @@ -178,19 +185,20 @@ def do_extend(draft, request): time=draft.time, desc='Extended expiry', ) - draft.expires = request.session['data']['expiration_date'] + draft.expires = parse(request.POST.get('expiration_date')) draft.save_with_history([e]) # save scheduled announcement - announcement_from_form(request.session['email'],by=request.user.person) + form = EmailForm(request.POST) + announcement_from_form(form.data,by=request.user.person) return def do_replace(draft, request): 'Perform document replace' - - replaced = request.session['data']['replaced'] # a DocAlias - replaced_by = request.session['data']['replaced_by'] # a Document + + replaced = DocAlias.objects.get(name=request.POST.get('replaced')) # a DocAlias + replaced_by = Document.objects.get(name=request.POST.get('replaced_by')) # a Document # create relationship RelatedDocument.objects.create(source=replaced_by, @@ -207,7 +215,7 @@ def do_replace(draft, request): doc=replaced_by, rev=replaced_by.rev, time=draft.time, - desc='This document now replaces %s' % request.session['data']['replaced'], + desc='This document now replaces %s' % replaced, ) draft.save_with_history([e]) @@ -216,7 +224,8 @@ def do_replace(draft, request): archive_draft_files(replaced.document.name + '-' + replaced.document.rev) # send announcement - announcement_from_form(request.session['email'],by=request.user.person) + form = EmailForm(request.POST) + announcement_from_form(form.data,by=request.user.person) return @@ -252,11 +261,12 @@ def do_resurrect(draft, request): draft.save_with_history([e]) # send announcement - announcement_from_form(request.session['email'],by=request.user.person) + form = EmailForm(request.POST) + announcement_from_form(form.data,by=request.user.person) return -def do_revision(draft, request): +def do_revision(draft, request, filename, file_type_list): ''' This function handles adding a new revision of an existing Internet-Draft. Prerequisites: draft must be active @@ -277,7 +287,7 @@ def do_revision(draft, request): archive_draft_files(draft.name + '-' + draft.rev) # save form data - form = BaseRevisionModelForm(request.session['data'],instance=draft) + form = BaseRevisionModelForm(request.POST,instance=draft) if form.is_valid(): new_draft = form.save(commit=False) else: @@ -285,7 +295,7 @@ def do_revision(draft, request): raise Exception('Problem with input data %s' % form.data) # set revision and expires - new_draft.rev = request.session['filename'][-2:] + new_draft.rev = filename[-2:] new_draft.expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE) # create DocEvent @@ -301,18 +311,18 @@ def do_revision(draft, request): handle_substate(new_draft) # move uploaded files to production directory - promote_files(new_draft, request.session['file_type']) + promote_files(new_draft, file_type_list) # save the submission record - post_submission(request) - - # send announcement if we are in IESG process - if new_draft.get_state('draft-iesg'): - announcement_from_form(request.session['email'],by=request.user.person) + submission = post_submission(request, new_draft) + announce_to_lists(request, submission) + announce_new_version(request, submission, draft, '') + announce_to_authors(request, submission) + return -def do_update(draft,request): +def do_update(draft,request,filename,file_type_list): ''' Actions - increment revision # @@ -322,7 +332,7 @@ def do_update(draft,request): - change state to Active ''' # save form data - form = BaseRevisionModelForm(request.session['data'],instance=draft) + form = BaseRevisionModelForm(request.POST,instance=draft) if form.is_valid(): new_draft = form.save(commit=False) else: @@ -331,7 +341,7 @@ def do_update(draft,request): handle_substate(new_draft) # update draft record - new_draft.rev = os.path.splitext(request.session['data']['filename'])[0][-2:] + new_draft.rev = os.path.splitext(filename)[0][-2:] new_draft.expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE) new_draft.set_state(State.objects.get(type="draft", slug="active")) @@ -347,13 +357,15 @@ def do_update(draft,request): new_draft.save_with_history([e]) # move uploaded files to production directory - promote_files(new_draft, request.session['file_type']) + promote_files(new_draft, file_type_list) # save the submission record - post_submission(request) + post_submission(request, new_draft) +def do_update_announce(draft, request): # send announcement - announcement_from_form(request.session['email'],by=request.user.person) + form = EmailForm(request.POST) + announcement_from_form(form.data,by=request.user.person) return @@ -363,7 +375,7 @@ def do_withdraw(draft,request): - change state to withdrawn - TODO move file to archive ''' - withdraw_type = request.session['data']['type'] + withdraw_type = request.POST.get('withdraw_type') prev_state = draft.get_state("draft") new_state = None @@ -382,7 +394,8 @@ def do_withdraw(draft,request): draft.save_with_history([e]) # send announcement - announcement_from_form(request.session['email'],by=request.user.person) + form = EmailForm(request.POST) + announcement_from_form(form.data,by=request.user.person) return @@ -421,8 +434,7 @@ def add(request): * form ''' - clear_non_auth(request.session) - + if request.method == 'POST': button_text = request.POST.get('submit', '') if button_text == 'Cancel': @@ -482,12 +494,13 @@ def add(request): promote_files(draft, file_type_list) # save the submission record - post_submission(request) - - request.session['action'] = 'add' + post_submission(request, draft) messages.success(request, 'New draft added successfully!') - return redirect('ietf.secr.drafts.views.authors', id=draft.name) + params = dict(action='add') + url = reverse('ietf.secr.drafts.views.authors', kwargs={'id':draft.pk}) + url = url + '?' + urlencode(params) + return redirect(url) else: form = AddModelForm() @@ -513,7 +526,7 @@ def announce(request, id): ''' draft = get_object_or_404(Document, name=id) - email_form = EmailForm(get_email_initial(draft,type='new')) + email_form = EmailForm(get_email_initial(draft,action='new')) announcement_from_form(email_form.data, by=request.user.person, @@ -566,16 +579,14 @@ def authors(request, id): ''' draft = get_object_or_404(Document, name=id) - + action = request.GET.get('action') + if request.method == 'POST': form = AuthorForm(request.POST) button_text = request.POST.get('submit', '') if button_text == 'Done': - action = request.session.get('action','') if action == 'add': - del request.session['action'] return redirect('ietf.secr.drafts.views.announce', id=id) - return redirect('ietf.secr.drafts.views.view', id=id) if form.is_valid(): @@ -604,22 +615,50 @@ def authors(request, id): @role_required('Secretariat') def confirm(request, id): - ''' - This view displays changes that will be made and calls appropriate - function if the user elects to proceed. If the user cancels then - the session data is cleared and view page is returned. - ''' draft = get_object_or_404(Document, name=id) - + if request.method == 'POST': button_text = request.POST.get('submit', '') if button_text == 'Cancel': - # TODO do cancel functions from session (ie remove uploaded files?) - # clear session data - clear_non_auth(request.session) return redirect('ietf.secr.drafts.views.view', id=id) - action = request.session['action'] + action = request.POST.get('action','') + form = EmailForm(request.POST) + if form.is_valid(): + email = form.data + details = get_action_details(draft, request) + hidden_form = EmailForm(request.POST, hidden=True) + + return render(request, 'drafts/confirm.html', { + 'details': details, + 'email': email, + 'action': action, + 'draft': draft, + 'form': hidden_form}, + ) + else: + return render(request, 'drafts/email.html', { + 'form': form, + 'draft': draft, + 'action': action}, + ) + +@role_required('Secretariat') +def do_action(request, id): + ''' + This view displays changes that will be made and calls appropriate + function if the user elects to proceed. If the user cancels then + the view page is returned. + ''' + draft = get_object_or_404(Document, name=id) + + if request.method == 'POST': + button_text = request.POST.get('submit', '') + if button_text == 'Cancel': + return redirect('ietf.secr.drafts.views.view', id=id) + + action = request.POST.get('action') + if action == 'revision': func = do_revision elif action == 'resurrect': @@ -627,31 +666,17 @@ def confirm(request, id): elif action == 'replace': func = do_replace elif action == 'update': - func = do_update + func = do_update_announce elif action == 'extend': func = do_extend elif action == 'withdraw': func = do_withdraw func(draft,request) - - # clear session data - clear_non_auth(request.session) messages.success(request, '%s action performed successfully!' % action) return redirect('ietf.secr.drafts.views.view', id=id) - details = get_action_details(draft, request.session) - email = request.session.get('email','') - action = request.session.get('action','') - - return render(request, 'drafts/confirm.html', { - 'details': details, - 'email': email, - 'action': action, - 'draft': draft}, - ) - @role_required('Secretariat') def dates(request): ''' @@ -726,45 +751,25 @@ def email(request, id): ''' This function displays the notification message and allows the user to make changes before continuing to confirmation page. - One exception is the "revision" action, save email data and go - directly to confirm page. ''' - draft = get_object_or_404(Document, name=id) - - if request.method == 'POST': - button_text = request.POST.get('submit', '') - if button_text == 'Cancel': - # clear session data - clear_non_auth(request.session) - return redirect('ietf.secr.drafts.views.view', id=id) + action = request.GET.get('action') + data = request.GET - form = EmailForm(request.POST) - if form.is_valid(): - request.session['email'] = form.data - return redirect('ietf.secr.drafts.views.confirm', id=id) - else: - # the resurrect email body references the last revision number, handle - # exception if no last revision found - # if this exception was handled closer to the source it would be easier to debug - # other problems with get_email_initial - try: - form = EmailForm(initial=get_email_initial( - draft, - type=request.session['action'], - input=request.session.get('data', None))) - except Exception, e: - return render(request, 'drafts/error.html', { 'error': e},) - - # for "revision" action skip email page and go directly to confirm - if request.session['action'] == 'revision': - request.session['email'] = form.initial - return redirect('ietf.secr.drafts.views.confirm', id=id) + # the resurrect email body references the last revision number, handle + # exception if no last revision found + # if this exception was handled closer to the source it would be easier to debug + # other problems with get_email_initial + try: + form = EmailForm(initial=get_email_initial(draft,action=action,input=data)) + except Exception, e: + return render(request, 'drafts/error.html', { 'error': e},) return render(request, 'drafts/email.html', { 'form': form, - 'draft': draft}, - ) + 'draft': draft, + 'action': action, + }) @role_required('Secretariat') def extend(request, id): @@ -785,9 +790,11 @@ def extend(request, id): form = ExtendForm(request.POST) if form.is_valid(): - request.session['data'] = form.cleaned_data - request.session['action'] = 'extend' - return redirect('ietf.secr.drafts.views.email', id=id) + params = form.cleaned_data + params['action'] = 'extend' + url = reverse('ietf.secr.drafts.views.email', kwargs={'id':id}) + url = url + '?' + urlencode(params) + return redirect(url) else: form = ExtendForm(initial={'revision_date':datetime.date.today().isoformat()}) @@ -906,9 +913,14 @@ def replace(request, id): form = ReplaceForm(request.POST, draft=draft) if form.is_valid(): - request.session['data'] = form.cleaned_data - request.session['action'] = 'replace' - return redirect('ietf.secr.drafts.views.email', id=id) + #params = form.cleaned_data + params = {} + params['replaced'] = form.data['replaced'] + params['replaced_by'] = form.data['replaced_by'] + params['action'] = 'replace' + url = reverse('ietf.secr.drafts.views.email', kwargs={'id':id}) + url = url + '?' + urlencode(params) + return redirect(url) else: form = ReplaceForm(draft=draft) @@ -918,22 +930,11 @@ def replace(request, id): 'draft': draft}, ) -@role_required('Secretariat') -def resurrect(request, id): - ''' - This view handles resurrection of an Internet-Draft - Prerequisites: draft must be expired - Input: none - ''' - - request.session['action'] = 'resurrect' - return redirect('ietf.secr.drafts.views.email', id=id) - @role_required('Secretariat') def revision(request, id): ''' - This function presents the input form for the New Revision action. If submitted - form is valid state is saved in the session and the email page is returned. + This function presents the input form for the New Revision action. + on POST, updates draft to new revision and sends notification. ''' draft = get_object_or_404(Document, name=id) @@ -949,14 +950,10 @@ def revision(request, id): # process files filename,revision,file_type_list = process_files(request,draft) - # save info in session and proceed to email page - request.session['data'] = form.cleaned_data - request.session['action'] = 'revision' - request.session['filename'] = filename - request.session['revision'] = revision - request.session['file_type'] = file_type_list - - return redirect('ietf.secr.drafts.views.email', id=id) + do_revision(draft, request, filename, file_type_list) + + messages.success(request, 'New Revision successful!') + return redirect('ietf.secr.drafts.views.view', id=id) else: form = RevisionModelForm(instance=draft,initial={'revision_date':datetime.date.today().isoformat()}) @@ -983,7 +980,6 @@ def search(request): ''' results = [] - clear_non_auth(request.session) if request.method == 'POST': form = SearchForm(request.POST) @@ -1019,7 +1015,6 @@ def search(request): kwargs['docevent__time__lte'] = revision_date_end # perform query - #assert False, kwargs if kwargs: qs = Document.objects.filter(**kwargs) else: @@ -1061,14 +1056,13 @@ def update(request, id): # process files filename,revision,file_type_list = process_files(request,draft) - # save state in session and proceed to email page - request.session['data'] = form.data - request.session['action'] = 'update' - request.session['revision'] = revision - request.session['data']['filename'] = filename - request.session['file_type'] = file_type_list - - return redirect('ietf.secr.drafts.views.email', id=id) + do_update(draft, request, filename, file_type_list) + + params = dict(action='update') + params['filename'] = filename + url = reverse('ietf.secr.drafts.views.email', kwargs={'id':id}) + url = url + '?' + urlencode(params) + return redirect(url) else: form = RevisionModelForm(instance=draft,initial={'revision_date':datetime.date.today().isoformat()}) @@ -1094,7 +1088,6 @@ def view(request, id): * draft, area, id_tracker_state ''' draft = get_object_or_404(Document, name=id) - #clear_non_auth(request.session) # TODO fix in Django 1.2 # some boolean state variables for use in the view.html template to manage display @@ -1157,11 +1150,11 @@ def withdraw(request, id): form = WithdrawForm(request.POST) if form.is_valid(): - # save state in session and proceed to email page - request.session['data'] = form.data - request.session['action'] = 'withdraw' - - return redirect('ietf.secr.drafts.views.email', id=id) + params = form.cleaned_data + params['action'] = 'withdraw' + url = reverse('ietf.secr.drafts.views.email', kwargs={'id':id}) + url = url + '?' + urlencode(params) + return redirect(url) else: form = WithdrawForm() diff --git a/ietf/secr/rolodex/forms.py b/ietf/secr/rolodex/forms.py index 25cd9ae79..a900f85c6 100644 --- a/ietf/secr/rolodex/forms.py +++ b/ietf/secr/rolodex/forms.py @@ -52,16 +52,6 @@ class EditPersonForm(forms.ModelForm): else: return None - """ - def save(self, force_insert=False, force_update=False, commit=True): - obj = super(EditPersonForm, self).save(commit=False) - user = self.cleaned_data['user'] - self.user = User.objects.get(username=user) - - if commit: - obj.save() - return obj - """ # ------------------------------------------------------ # Forms for addition of new contacts # These sublcass the regular forms, with additional @@ -100,9 +90,6 @@ class NewPersonForm(forms.ModelForm): class Meta: model = Person exclude = ('time','user') - - #def __init__(self, *args, **kwargs): - # super(NewPersonForm, self).__init__(*args, **kwargs) def clean_email(self): email = self.cleaned_data['email'] diff --git a/ietf/secr/rolodex/tests.py b/ietf/secr/rolodex/tests.py index fbcb3a3f9..b40c01b3b 100644 --- a/ietf/secr/rolodex/tests.py +++ b/ietf/secr/rolodex/tests.py @@ -24,4 +24,26 @@ class RolodexTestCase(TestCase): response = self.client.get(url) self.assertEqual(response.status_code, 200) - + def test_add(self): + make_test_data() + url = reverse('ietf.secr.rolodex.views.add') + add_proceed_url = reverse('ietf.secr.rolodex.views.add_proceed') + '?name=Joe+Smith' + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + response = self.client.post(url, {'name':'Joe Smith'}) + self.assertRedirects(response, add_proceed_url) + post_data = { + 'name': 'Joe Smith', + 'ascii': 'Joe Smith', + 'ascii_short': 'Joe S', + 'affiliation': 'IETF', + 'address': '100 First Ave', + 'email': 'joes@exanple.com', + 'submit': 'Submit', + } + response = self.client.post(add_proceed_url, post_data) + person = Person.objects.get(name='Joe Smith') + view_url = reverse('ietf.secr.rolodex.views.view', kwargs={'id':person.pk}) + self.assertRedirects(response, view_url) + diff --git a/ietf/secr/rolodex/views.py b/ietf/secr/rolodex/views.py index aa291a4a9..9e4a5ab56 100644 --- a/ietf/secr/rolodex/views.py +++ b/ietf/secr/rolodex/views.py @@ -2,8 +2,9 @@ from django.contrib import messages from django.contrib.auth.models import User from django.db import IntegrityError from django.forms.models import inlineformset_factory -from django.http import HttpResponseRedirect from django.shortcuts import render, get_object_or_404, redirect +from django.utils.http import urlencode +from django.urls import reverse from ietf.ietfauth.utils import role_required from ietf.person.models import Person, Email, Alias @@ -35,13 +36,14 @@ def add(request): if request.method == 'POST': form = NameForm(request.POST) if form.is_valid(): - request.session['post_data'] = request.POST - # search to see if contact already exists name = form.cleaned_data['name'] results = Alias.objects.filter(name=name) if not results: - return HttpResponseRedirect('../add-proceed/') + params = dict(name=name) + url = reverse('ietf.secr.rolodex.views.add_proceed') + url = url + '?' + urlencode(params) + return redirect(url) else: form = NameForm() @@ -67,16 +69,14 @@ def add_proceed(request): * form """ - # if we get to this page from the add page, as expected, the session will have post_data. - if request.session['post_data']: - post_data = request.session['post_data'] + if 'name' in request.GET: + name = request.GET.get('name') + elif 'name' in request.POST: + name = request.POST.get('name') else: - messages.error('ERROR: unable to save session data (enable cookies)') # pylint: disable=no-value-for-parameter - return redirect('ietf.secr.rolodex.views.add') + name = '' - name = post_data['name'] - - if request.method == 'POST': + if request.method == 'POST' and request.POST.get('submit') == 'Submit': form = NewPersonForm(request.POST) if form.is_valid(): email = form.cleaned_data['email'] diff --git a/ietf/secr/sreq/forms.py b/ietf/secr/sreq/forms.py index 143ff0891..6a5b07f5b 100644 --- a/ietf/secr/sreq/forms.py +++ b/ietf/secr/sreq/forms.py @@ -72,6 +72,10 @@ class SessionForm(forms.Form): bethere = SearchablePersonsField(label="Must be present", required=False) def __init__(self, *args, **kwargs): + if 'hidden' in kwargs: + self.hidden = kwargs.pop('hidden') + else: + self.hidden = False super(SessionForm, self).__init__(*args, **kwargs) self.fields['num_session'].widget.attrs['onChange'] = "stat_ls(this.selectedIndex);" self.fields['length_session1'].widget.attrs['onClick'] = "if (check_num_session(1)) this.disabled=true;" @@ -92,6 +96,11 @@ class SessionForm(forms.Form): if self.initial['length_session3'] != '0' and self.initial['length_session3'] != None: self.fields['third_session'].initial = True + if self.hidden: + for key in self.fields.keys(): + self.fields[key].widget = forms.HiddenInput() + self.fields['resources'].widget = forms.MultipleHiddenInput() + def clean_conflict1(self): conflict = self.cleaned_data['conflict1'] check_conflict(conflict) diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py index fcb56126f..c9a0ac663 100644 --- a/ietf/secr/sreq/tests.py +++ b/ietf/secr/sreq/tests.py @@ -4,6 +4,7 @@ import debug # pyflakes:ignore from ietf.utils.test_utils import TestCase, unicontent from ietf.group.models import Group +from ietf.meeting.helpers import get_meeting from ietf.meeting.models import Meeting, Session, ResourceAssociation from ietf.meeting.test_data import make_meeting_test_data from ietf.utils.mail import outbox, empty_outbox @@ -44,16 +45,26 @@ class SessionRequestTestCase(TestCase): class SubmitRequestCase(TestCase): def test_submit_request(self): make_test_data() + meeting = get_meeting() group = Group.objects.get(acronym='mars') + session_count_before = Session.objects.filter(meeting=meeting, group=group).count() url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym}) + confirm_url = reverse('ietf.secr.sreq.views.confirm',kwargs={'acronym':group.acronym}) + main_url = reverse('ietf.secr.sreq.views.main') post_data = {'num_session':'1', 'length_session1':'3600', 'attendees':'10', 'conflict1':'', - 'comments':'need projector'} + 'comments':'need projector', + 'submit': 'Continue'} self.client.login(username="secretary", password="secretary+password") r = self.client.post(url,post_data) - self.assertRedirects(r, reverse('ietf.secr.sreq.views.confirm', kwargs={'acronym':group.acronym})) + self.assertEqual(r.status_code, 200) + post_data['submit'] = 'Submit' + r = self.client.post(confirm_url,post_data) + self.assertRedirects(r, main_url) + session_count_after = Session.objects.filter(meeting=meeting, group=group).count() + self.assertTrue(session_count_after == session_count_before + 1) def test_submit_request_invalid(self): make_test_data() @@ -89,13 +100,17 @@ class SubmitRequestCase(TestCase): 'bethere':str(ad.pk), 'conflict1':'', 'comments':'', - 'resources': resource.pk} + 'resources': resource.pk, + 'submit': 'Continue'} self.client.login(username="ameschairman", password="ameschairman+password") # submit r = self.client.post(url,post_data) - self.assertRedirects(r, confirm_url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue('Confirm' in unicode(q("title"))) # confirm - r = self.client.post(confirm_url,{'submit':'Submit'}) + post_data['submit'] = 'Submit' + r = self.client.post(confirm_url,post_data) self.assertRedirects(r, reverse('ietf.secr.sreq.views.main')) self.assertEqual(len(outbox),len_before+1) notification = outbox[-1] diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index 92af5eae3..65bfff285 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -3,7 +3,6 @@ import datetime from django.conf import settings from django.contrib import messages from django.db.models import Q -from django.http import Http404 from django.shortcuts import render, get_object_or_404, redirect import debug # pyflakes:ignore @@ -224,28 +223,24 @@ def confirm(request, acronym): to confirm for submission. ''' # FIXME: this should be using form.is_valid/form.cleaned_data - invalid input will make it crash - querydict = request.session.get('session_form',None) - if not querydict: - raise Http404 - form = querydict.copy() - if 'resources' in form: - form['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in form['resources'].split(',')] - if 'bethere' in form: - person_id_list = [ id for id in form['bethere'].split(',') if id ] - form['bethere'] = Person.objects.filter(pk__in=person_id_list) + form = SessionForm(request.POST, hidden=True) + form.is_valid() meeting = get_meeting() group = get_object_or_404(Group,acronym=acronym) login = request.user.person - if request.method == 'POST': - # clear http session data - del request.session['session_form'] - - button_text = request.POST.get('submit', '') - if button_text == 'Cancel': - messages.success(request, 'Session Request has been canceled') - return redirect('ietf.secr.sreq.views.main') + session_data = form.data.copy() + if 'bethere' in session_data: + person_id_list = [ id for id in form.data['bethere'].split(',') if id ] + session_data['bethere'] = Person.objects.filter(pk__in=person_id_list) + session_data['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in request.POST.getlist('resources') ] + + button_text = request.POST.get('submit', '') + if button_text == 'Cancel': + messages.success(request, 'Session Request has been canceled') + return redirect('ietf.secr.sreq.views.main') + if request.method == 'POST' and button_text == 'Submit': # delete any existing session records with status = canceled or notmeet Session.objects.filter(group=group,meeting=meeting,status__in=('canceled','notmeet')).delete() @@ -253,53 +248,50 @@ def confirm(request, acronym): count = 0 # lenth_session2 and length_session3 fields might be disabled by javascript and so # wouldn't appear in form data - for duration in (form.get('length_session1',None),form.get('length_session2',None),form.get('length_session3',None)): + for duration in (form.data.get('length_session1',None),form.data.get('length_session2',None),form.data.get('length_session3',None)): count += 1 if duration: slug = 'apprw' if count == 3 else 'schedw' new_session = Session(meeting=meeting, group=group, - attendees=form['attendees'], + attendees=form.data['attendees'], requested=datetime.datetime.now(), requested_by=login, requested_duration=datetime.timedelta(0,int(duration)), - comments=form['comments'], + comments=form.data['comments'], status=SessionStatusName.objects.get(slug=slug), type_id='session', ) session_save(new_session) - if 'resources' in form: - new_session.resources = form['resources'] + if 'resources' in form.data: + new_session.resources = session_data['resources'] # write constraint records - save_conflicts(group,meeting,form.get('conflict1',''),'conflict') - save_conflicts(group,meeting,form.get('conflict2',''),'conflic2') - save_conflicts(group,meeting,form.get('conflict3',''),'conflic3') + save_conflicts(group,meeting,form.data.get('conflict1',''),'conflict') + save_conflicts(group,meeting,form.data.get('conflict2',''),'conflic2') + save_conflicts(group,meeting,form.data.get('conflict3',''),'conflic3') - if 'bethere' in form: + if 'bethere' in form.data: bethere_cn = ConstraintName.objects.get(slug='bethere') - for p in form.get('bethere', []): + for p in session_data['bethere']: Constraint.objects.create(name=bethere_cn, source=group, person=p, meeting=new_session.meeting) - # deprecated in new schema - # log activity - #add_session_activity(group,'New session was requested',meeting,user) - # clear not meeting Session.objects.filter(group=group,meeting=meeting,status='notmeet').delete() # send notification - send_notification(group,meeting,login,form,'new') + send_notification(group,meeting,login,session_data,'new') status_text = 'IETF Agenda to be scheduled' messages.success(request, 'Your request has been sent to %s' % status_text) return redirect('ietf.secr.sreq.views.main') - # GET logic + # POST from request submission session_conflicts = session_conflicts_as_string(group, meeting) return render(request, 'sreq/confirm.html', { - 'session': form, + 'form': form, + 'session': session_data, 'group': group, 'session_conflicts': session_conflicts}, ) @@ -539,10 +531,7 @@ def new(request, acronym): if Session.objects.filter(group=group,meeting=meeting).exclude(status__in=('deleted','notmeet')): messages.warning(request, 'Sessions for working group %s have already been requested once.' % group.acronym) return redirect('ietf.secr.sreq.views.main') - - # save in user session - request.session['session_form'] = form.data - return redirect('ietf.secr.sreq.views.confirm',acronym=acronym) + return confirm(request, acronym) # the "previous" querystring causes the form to be returned # pre-populated with data from last meeeting's session request diff --git a/ietf/secr/templates/announcement/confirm.html b/ietf/secr/templates/announcement/confirm.html index f86c2a33b..53a0c93c5 100644 --- a/ietf/secr/templates/announcement/confirm.html +++ b/ietf/secr/templates/announcement/confirm.html @@ -14,9 +14,9 @@ {% block content %}
-

Announcement

+

Confirm Announcement

-
{% csrf_token %} + {% csrf_token %}
 To: {{ to }}
@@ -29,6 +29,7 @@ Subject: {{ message.subject }}
 {{ message.body }}
 
+ {{ form }}
  • diff --git a/ietf/secr/templates/announcement/main.html b/ietf/secr/templates/announcement/main.html index be14b1c32..13f6ffacd 100644 --- a/ietf/secr/templates/announcement/main.html +++ b/ietf/secr/templates/announcement/main.html @@ -23,8 +23,8 @@
      -
    • -
    • +
    • +
    diff --git a/ietf/secr/templates/drafts/confirm.html b/ietf/secr/templates/drafts/confirm.html index f3be09232..1e81555b5 100644 --- a/ietf/secr/templates/drafts/confirm.html +++ b/ietf/secr/templates/drafts/confirm.html @@ -17,7 +17,7 @@

    Draft - Confirm

    - {% csrf_token %} + {% csrf_token %} @@ -39,6 +39,8 @@ {% endif %} + {{ form }} + {% include "includes/buttons_save_cancel.html" %} diff --git a/ietf/secr/templates/drafts/email.html b/ietf/secr/templates/drafts/email.html index e07354daa..ea0a73f0d 100644 --- a/ietf/secr/templates/drafts/email.html +++ b/ietf/secr/templates/drafts/email.html @@ -17,7 +17,7 @@

    Draft - Email

    -
    {% csrf_token %} + {% csrf_token %}
    Action Selected:{{ action }}
    {{ form.as_table }}
    diff --git a/ietf/secr/templates/drafts/view.html b/ietf/secr/templates/drafts/view.html index 2f90838eb..66f366e97 100644 --- a/ietf/secr/templates/drafts/view.html +++ b/ietf/secr/templates/drafts/view.html @@ -82,7 +82,7 @@
    • -
    • +
    • diff --git a/ietf/secr/templates/includes/sessions_request_form.html b/ietf/secr/templates/includes/sessions_request_form.html index ed3edb2eb..6b7583299 100755 --- a/ietf/secr/templates/includes/sessions_request_form.html +++ b/ietf/secr/templates/includes/sessions_request_form.html @@ -64,7 +64,7 @@
        -
      • +
      diff --git a/ietf/secr/templates/rolodex/add.html b/ietf/secr/templates/rolodex/add.html index 13007f815..282738906 100644 --- a/ietf/secr/templates/rolodex/add.html +++ b/ietf/secr/templates/rolodex/add.html @@ -21,14 +21,17 @@

      Adding {{ name }}

      - {% include "includes/search_results_table.html" %} + {% csrf_token %} +
      {{ form.as_p }}
      + {% include "includes/search_results_table.html" %} -
      -
        -
      • -
      • -
      -
      +
      +
        +
      • +
      • +
      +
      +
      {% else %} diff --git a/ietf/secr/templates/sreq/confirm.html b/ietf/secr/templates/sreq/confirm.html index 50b238454..2bf472d31 100755 --- a/ietf/secr/templates/sreq/confirm.html +++ b/ietf/secr/templates/sreq/confirm.html @@ -1,7 +1,7 @@ {% extends "base_site.html" %} {% load staticfiles %} -{% block title %}Sessions - View{% endblock %} +{% block title %}Sessions - Confirm{% endblock %} {% block extrahead %}{{ block.super }} @@ -28,7 +28,8 @@
      {% endif %} -
      {% csrf_token %} + {% csrf_token %} + {{ form }} {% include "includes/buttons_submit_cancel.html" %}
      diff --git a/ietf/secr/utils/decorators.py b/ietf/secr/utils/decorators.py index 4833630d2..450a0cb56 100644 --- a/ietf/secr/utils/decorators.py +++ b/ietf/secr/utils/decorators.py @@ -13,13 +13,6 @@ from ietf.group.models import Group, Role from ietf.meeting.models import Session from ietf.secr.utils.meeting import get_timeslot -def clear_non_auth(session): - """ - Clears non authentication related keys from the session object - """ - for key in session.keys(): - if not key.startswith('_auth'): - del session[key] def check_for_cancel(redirect_url): """ @@ -30,7 +23,6 @@ def check_for_cancel(redirect_url): @wraps(func) def inner(request, *args, **kwargs): if request.method == 'POST' and request.POST.get('submit',None) == 'Cancel': - clear_non_auth(request.session) return HttpResponseRedirect(redirect_url) return func(request, *args, **kwargs) return inner diff --git a/ietf/utils/log.py b/ietf/utils/log.py index 6d35a2832..ce9e9e72f 100644 --- a/ietf/utils/log.py +++ b/ietf/utils/log.py @@ -37,6 +37,8 @@ def log(msg): "Uses syslog by preference. Logs the given calling point and message." if settings.SERVER_MODE == 'test': return + elif settings.DEBUG == True: + logfunc = debug.say if isinstance(msg, unicode): msg = msg.encode('unicode_escape') try: