diff --git a/ietf/mailtrigger/utils.py b/ietf/mailtrigger/utils.py index d8b23ff056..9915eae3fd 100644 --- a/ietf/mailtrigger/utils.py +++ b/ietf/mailtrigger/utils.py @@ -149,9 +149,5 @@ def starts_with(prefix): return sorted(rule_list) -def get_base_submission_message_address(): - return Recipient.objects.get(slug="submission_manualpost_handling").gather()[0] - - def get_base_ipr_request_address(): return Recipient.objects.get(slug="ipr_requests").gather()[0] diff --git a/ietf/submit/mail.py b/ietf/submit/mail.py index 1953ad81c9..d7da7e21e0 100644 --- a/ietf/submit/mail.py +++ b/ietf/submit/mail.py @@ -1,33 +1,22 @@ # Copyright The IETF Trust 2013-2020, All Rights Reserved # -*- coding: utf-8 -*- - -import re -import email -import base64 import os -import pyzmail from django.conf import settings from django.urls import reverse as urlreverse -from django.core.exceptions import ValidationError from django.contrib.sites.models import Site from django.template.loader import render_to_string -from django.utils.encoding import force_str import debug # pyflakes:ignore -from ietf.utils.log import log from ietf.utils.mail import send_mail, send_mail_message from ietf.doc.models import Document -from ietf.ipr.mail import utc_from_string from ietf.person.models import Person -from ietf.message.models import Message, MessageAttachment +from ietf.message.models import Message from ietf.utils.accesstoken import generate_access_token -from ietf.mailtrigger.utils import gather_address_lists, get_base_submission_message_address -from ietf.submit.models import SubmissionEmailEvent, Submission +from ietf.mailtrigger.utils import gather_address_lists from ietf.submit.checkers import DraftIdnitsChecker -from ietf.utils.timezone import date_today def send_submission_confirmation(request, submission, chair_notice=False): @@ -196,181 +185,3 @@ def announce_to_authors(request, submission): 'group': group}, cc=cc) - -def get_reply_to(): - """Returns a new reply-to address for use with an outgoing message. This is an - address with "plus addressing" using a random string. Guaranteed to be unique""" - local,domain = get_base_submission_message_address().split('@') - while True: - rand = force_str(base64.urlsafe_b64encode(os.urandom(12))) - address = "{}+{}@{}".format(local,rand,domain) - q = Message.objects.filter(reply_to=address) - if not q: - return address - - -def process_response_email(msg): - """Saves an incoming message. msg=string. Message "To" field is expected to - be in the format ietf-submit+[identifier]@ietf.org. Expect to find a message with - a matching value in the reply_to field, associated to a submission. - Create a Message object for the incoming message and associate it to - the original message via new SubmissionEvent""" - message = email.message_from_string(force_str(msg)) - to = message.get('To') - - # exit if this isn't a response we're interested in (with plus addressing) - local,domain = get_base_submission_message_address().split('@') - if not re.match(r'^{}\+[a-zA-Z0-9_\-]{}@{}'.format(local,'{16}',domain),to): - return None - - try: - to_message = Message.objects.get(reply_to=to) - except Message.DoesNotExist: - log('Error finding matching message ({})'.format(to)) - return None - - try: - submission = to_message.manualevents.first().submission - except: - log('Error processing message ({})'.format(to)) - return None - - if not submission: - log('Error processing message - no submission ({})'.format(to)) - return None - - parts = pyzmail.parse.get_mail_parts(message) - body='' - for part in parts: - if part.is_body == 'text/plain' and part.disposition == None: - payload, used_charset = pyzmail.decode_text(part.get_payload(), part.charset, None) - body = body + payload + '\n' - - by = Person.objects.get(name="(System)") - msg = submit_message_from_message(message, body, by) - - desc = "Email: received message - manual post - {}-{}".format( - submission.name, - submission.rev) - - submission_email_event = SubmissionEmailEvent.objects.create( - submission = submission, - desc = desc, - msgtype = 'msgin', - by = by, - message = msg, - in_reply_to = to_message - ) - - save_submission_email_attachments(submission_email_event, parts) - - log("Received submission email from %s" % msg.frm) - return msg - - -def add_submission_email(request, remote_ip, name, rev, submission_pk, message, by, msgtype): - """Add email to submission history""" - - #in_reply_to = form.cleaned_data['in_reply_to'] - # create Message - parts = pyzmail.parse.get_mail_parts(message) - body='' - for part in parts: - if part.is_body == 'text/plain' and part.disposition == None: - payload, used_charset = pyzmail.decode_text(part.get_payload(), part.charset, None) - body = body + payload + '\n' - - msg = submit_message_from_message(message, body, by) - - if (submission_pk != None): - # Must exist - we're adding a message to an existing submission - submission = Submission.objects.get(pk=submission_pk) - else: - # Must not exist - submissions = Submission.objects.filter(name=name,rev=rev).exclude(state_id='cancel') - if submissions.count() > 0: - raise ValidationError("Submission {} already exists".format(name)) - - # create Submission using the name - try: - submission = Submission.objects.create( - state_id="waiting-for-draft", - remote_ip=remote_ip, - name=name, - rev=rev, - title=name, - note="", - submission_date=date_today(), - replaces="", - ) - from ietf.submit.utils import create_submission_event, docevent_from_submission - desc = "Submission created for rev {} in response to email".format(rev) - create_submission_event(request, - submission, - desc) - docevent_from_submission(submission, desc) - except Exception as e: - log("Exception: %s\n" % e) - raise - - if msgtype == 'msgin': - rs = "Received" - else: - rs = "Sent" - - desc = "{} message - manual post - {}-{}".format(rs, name, rev) - submission_email_event = SubmissionEmailEvent.objects.create( - desc = desc, - submission = submission, - msgtype = msgtype, - by = by, - message = msg) - #in_reply_to = in_reply_to - - save_submission_email_attachments(submission_email_event, parts) - return submission, submission_email_event - - -def submit_message_from_message(message,body,by=None): - """Returns a ietf.message.models.Message. msg=email.Message - A copy of mail.message_from_message with different body handling - """ - if not by: - by = Person.objects.get(name="(System)") - msg = Message.objects.create( - by = by, - subject = message.get('subject',''), - frm = message.get('from',''), - to = message.get('to',''), - cc = message.get('cc',''), - bcc = message.get('bcc',''), - reply_to = message.get('reply_to',''), - body = body, - time = utc_from_string(message.get('date', '')), - content_type = message.get('content_type', 'text/plain'), - ) - return msg - -def save_submission_email_attachments(submission_email_event, parts): - for part in parts: - if part.disposition != 'attachment': - continue - - if part.type == 'text/plain': - payload, used_charset = pyzmail.decode_text(part.get_payload(), - part.charset, - None) - encoding = "" - else: - # Need a better approach - for the moment we'll just handle these - # and encode as base64 - payload = base64.b64encode(part.get_payload()) - encoding = "base64" - - #name = submission_email_event.submission.name - - MessageAttachment.objects.create(message = submission_email_event.message, - content_type = part.type, - encoding = encoding, - filename=part.filename, - body=payload) diff --git a/ietf/submit/management/commands/manualpost_email.py b/ietf/submit/management/commands/manualpost_email.py deleted file mode 100644 index aaabfb39b7..0000000000 --- a/ietf/submit/management/commands/manualpost_email.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright The IETF Trust 2016-2020, All Rights Reserved -# -*- coding: utf-8 -*- - - -import io -import sys - -from django.core.management.base import BaseCommand, CommandError - -from ietf.submit.mail import process_response_email - -import debug # pyflakes:ignore - -class Command(BaseCommand): - help = ("Process incoming manual post email responses") - - def add_arguments(self, parser): - parser.add_argument('--email-file', dest='email', help='File containing email (default: stdin)') - - def handle(self, *args, **options): - email = options.get('email', None) - msg = None - - if not email: - msg = sys.stdin.read() - else: - msg = io.open(email, "r").read() - - try: - process_response_email(msg) - except ValueError as e: - raise CommandError(e) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 1e432cb8ef..6a4051f829 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -42,17 +42,15 @@ from ietf.group.utils import setup_default_community_list_for_group from ietf.meeting.models import Meeting from ietf.meeting.factories import MeetingFactory -from ietf.message.models import Message from ietf.name.models import FormalLanguageName from ietf.person.models import Person from ietf.person.factories import UserFactory, PersonFactory, EmailFactory from ietf.submit.factories import SubmissionFactory, SubmissionExtResourceFactory from ietf.submit.forms import SubmissionBaseUploadForm, SubmissionAutoUploadForm from ietf.submit.models import Submission, Preapproval, SubmissionExtResource -from ietf.submit.mail import add_submission_email, process_response_email from ietf.submit.tasks import cancel_stale_submissions, process_and_accept_uploaded_submission_task from ietf.utils.accesstoken import generate_access_token -from ietf.utils.mail import outbox, empty_outbox, get_payload_text +from ietf.utils.mail import outbox, get_payload_text from ietf.utils.models import VersionInfo from ietf.utils.test_utils import login_testing_unauthorized, TestCase from ietf.utils.timezone import date_today @@ -198,6 +196,28 @@ def create_draft_submission_with_rev_mismatch(rev='01'): return draft, sub +class ManualSubmissionTests(TestCase): + def test_manualpost_view(self): + submission = SubmissionFactory(state_id="manual") + url = urlreverse("ietf.submit.views.manualpost") + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertIn( + urlreverse( + "ietf.submit.views.submission_status", + kwargs=dict(submission_id=submission.pk) + ), + q("#manual.submissions td a").attr("href") + ) + self.assertIn( + submission.name, + q("#manual.submissions td a").text() + ) + + def test_manualpost_cancel(self): + pass + class SubmitTests(BaseSubmitTestCase): def setUp(self): super().setUp() @@ -2293,451 +2313,6 @@ def test_cancel_preapproval(self): self.assertEqual(len(Preapproval.objects.filter(name=preapproval.name)), 0) -class ManualPostsTestCase(BaseSubmitTestCase): - def test_manual_posts(self): - GroupFactory(acronym='mars') - - url = urlreverse('ietf.submit.views.manualpost') - # Secretariat has access - self.client.login(username="secretary", password="secretary+password") - - Submission.objects.create(name="draft-ietf-mars-foo", - group=Group.objects.get(acronym="mars"), - submission_date=date_today(), - state_id="manual") - Submission.objects.create(name="draft-ietf-mars-bar", - group=Group.objects.get(acronym="mars"), - submission_date=date_today(), - rev="00", - state_id="grp-appr") - - # get - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - q = PyQuery(r.content) - - self.assertEqual(len(q('.submissions a:contains("draft-ietf-mars-foo")')), 1) - self.assertEqual(len(q('.submissions a:contains("draft-ietf-mars-bar")')), 0) - - def test_waiting_for_draft(self): - message_string = """To: somebody@ietf.org -From: joe@test.com -Date: {} -Subject: test submission via email - -Please submit my draft at http://test.com/mydraft.txt - -Thank you -""".format(timezone.now().ctime()) - message = email.message_from_string(force_str(message_string)) - submission, submission_email_event = ( - add_submission_email(request=None, - remote_ip ="192.168.0.1", - name = "draft-my-new-draft", - rev='00', - submission_pk=None, - message = message, - by = Person.objects.get(name="(System)"), - msgtype = "msgin") ) - - url = urlreverse('ietf.submit.views.manualpost') - # Secretariat has access - self.client.login(username="secretary", password="secretary+password") - - # get - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - q = PyQuery(r.content) - - self.assertEqual(len(q('.waiting-for-draft a:contains("draft-my-new-draft")')), 1) - - # Same name should raise an error - with self.assertRaises(Exception): - add_submission_email(request=None, - remote_ip ="192.168.0.1", - name = "draft-my-new-draft", - rev='00', - submission_pk=None, - message = message, - by = Person.objects.get(name="(System)"), - msgtype = "msgin") - - # Cancel this one - r = self.client.post(urlreverse("ietf.submit.views.cancel_waiting_for_draft"), { - "submission_id": submission.pk, - "access_token": submission.access_token(), - }) - self.assertEqual(r.status_code, 302) - url = r["Location"] - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - q = PyQuery(r.content) - self.assertEqual(len(q('.waiting-for-draft a:contains("draft-my-new-draft")')), 0) - - # Should now be able to add it again - submission, submission_email_event = ( - add_submission_email(request=None, - remote_ip ="192.168.0.1", - name = "draft-my-new-draft", - rev='00', - submission_pk=None, - message = message, - by = Person.objects.get(name="(System)"), - msgtype = "msgin") ) - - - def test_waiting_for_draft_with_attachment(self): - frm = "joe@test.com" - - message_string = """To: somebody@ietf.org -From: {} -Date: {} -Subject: A very important message with a small attachment -Content-Type: multipart/mixed; boundary="------------090908050800030909090207" - -This is a multi-part message in MIME format. ---------------090908050800030909090207 -Content-Type: text/plain; charset=utf-8; format=flowed -Content-Transfer-Encoding: 7bit - -The message body will probably say something about the attached document - ---------------090908050800030909090207 -Content-Type: text/plain; charset=UTF-8; name="attach.txt" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="attach.txt" - -QW4gZXhhbXBsZSBhdHRhY2htZW50IHd0aG91dCB2ZXJ5IG11Y2ggaW4gaXQuCgpBIGNvdXBs -ZSBvZiBsaW5lcyAtIGJ1dCBpdCBjb3VsZCBiZSBhIGRyYWZ0Cg== ---------------090908050800030909090207-- -""".format(frm, timezone.now().ctime()) - - message = email.message_from_string(force_str(message_string)) - submission, submission_email_event = ( - add_submission_email(request=None, - remote_ip ="192.168.0.1", - name = "draft-my-new-draft", - rev='00', - submission_pk=None, - message = message, - by = Person.objects.get(name="(System)"), - msgtype = "msgin") ) - - manualpost_page_url = urlreverse('ietf.submit.views.manualpost') - # Secretariat has access - self.client.login(username="secretary", password="secretary+password") - - self.check_manualpost_page(submission=submission, - submission_email_event=submission_email_event, - the_url=manualpost_page_url, - submission_name_fragment='draft-my-new-draft', - frm=frm, - is_secretariat=True) - - # Try the status page with no credentials - self.client.logout() - - self.check_manualpost_page(submission=submission, - submission_email_event=submission_email_event, - the_url=manualpost_page_url, - submission_name_fragment='draft-my-new-draft', - frm=frm, - is_secretariat=False) - - # Post another message to this submission using the link - message_string = """To: somebody@ietf.org -From: joe@test.com -Date: {} -Subject: A new submission message with a small attachment -Content-Type: multipart/mixed; boundary="------------090908050800030909090207" - -This is a multi-part message in MIME format. ---------------090908050800030909090207 -Content-Type: text/plain; charset=utf-8; format=flowed -Content-Transfer-Encoding: 7bit - -The message body will probably say something more about the attached document - ---------------090908050800030909090207 -Content-Type: text/plain; charset=UTF-8; name="attach.txt" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="attachment.txt" - -QW4gZXhhbXBsZSBhdHRhY2htZW50IHd0aG91dCB2ZXJ5IG11Y2ggaW4gaXQuCgpBIGNvdXBs -ZSBvZiBsaW5lcyAtIGJ1dCBpdCBjb3VsZCBiZSBhIGRyYWZ0Cg== ---------------090908050800030909090207-- -""".format(timezone.now().ctime()) - - # Back to secretariat - self.client.login(username="secretary", password="secretary+password") - - r, q = self.request_and_parse(manualpost_page_url) - - url = self.get_href(q, "a#new-submission-email:contains('New submission from email')") - - # Get the form - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - q = PyQuery(r.content) - #self.assertEqual(len(q('input[name=edit-title]')), 1) - - # Post the new message - r = self.client.post(url, { - "name": "draft-my-next-new-draft-00", - "direction": "incoming", - "message": message_string, - }) - - if r.status_code != 302: - q = PyQuery(r.content) - print(q) - - self.assertEqual(r.status_code, 302) - - - #self.check_manualpost_page(submission, submission_email_event, - # url, 'draft-my-next-new-draft' - # 'Another very important message', - # true) - - def check_manualpost_page(self, submission, submission_email_event, - the_url, submission_name_fragment, - frm, - is_secretariat): - # get the page listing manual posts - r, q = self.request_and_parse(the_url) - selector = "#waiting-for-draft a#add-submission-email%s:contains('Add email')" % submission.pk - - if is_secretariat: - # Can add an email to the submission - add_email_url = self.get_href(q, selector) - else: - # No add email button button - self.assertEqual(len(q(selector)), 0) - - # Find the link for our submission in those awaiting drafts - submission_url = self.get_href(q, "#waiting-for-draft a#aw{}:contains('{}')". - format(submission.pk, submission_name_fragment)) - - # Follow the link to the status page for this submission - r, q = self.request_and_parse(submission_url) - - selector = "#history a#reply%s:contains('Reply')" % submission.pk - - if is_secretariat: - # check that reply button is visible and get the form - reply_url = self.get_href(q, selector) - - # Get the form - r = self.client.get(reply_url) - self.assertEqual(r.status_code, 200) - reply_q = PyQuery(r.content) - self.assertEqual(len(reply_q('input[name=to]')), 1) - else: - # No reply button - self.assertEqual(len(q(selector)), 0) - - if is_secretariat: - # Now try to send an email using the send email link - - selector = "a#send%s:contains('Send Email')" % submission.pk - send_url = self.get_href(q, selector) - - self.do_submission_email(the_url = send_url, - to = frm, - body = "A new message") - - # print q - # print submission.pk - # print submission_email_event.pk - - # Find the link for our message in the list - url = self.get_href(q, "#aw{}-{}:contains('{}')".format(submission.pk, - submission_email_event.message.pk, - "Received message - manual post")) - - # Page displaying message details - r, q = self.request_and_parse(url) - - if is_secretariat: - # check that reply button is visible - - reply_href = self.get_href(q, "a#reply%s:contains('Reply')" % submission.pk) - - else: - # No reply button - self.assertEqual(len(q(selector)), 0) - reply_href = None - - # check that attachment link is visible - - url = self.get_href(q, "#email-details a#attach{}:contains('attach.txt')".format(submission.pk)) - - # Fetch the attachment - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - - # Attempt a reply if we can - if reply_href == None: - return - - self.do_submission_email(the_url = reply_href, - to = frm, - body = "A reply to the message") - - # try adding an email to the submission - # Use the add email link from the manual post listing page - - if is_secretariat: - # Can add an email to the submission - # add_email_url set previously - r = self.client.get(add_email_url) - self.assertEqual(r.status_code, 200) - add_email_q = PyQuery(r.content) - self.assertEqual(len(add_email_q('input[name=submission_pk]')), 1) - - # Add a simple email - new_message_string = """To: somebody@ietf.org -From: joe@test.com -Date: {} -Subject: Another message - -About my submission - -Thank you -""".format(timezone.now().ctime()) - - r = self.client.post(add_email_url, { - "name": "{}-{}".format(submission.name, submission.rev), - "direction": "incoming", - "submission_pk": submission.pk, - "message": new_message_string, - }) - - if r.status_code != 302: - q = PyQuery(r.content) - print(q) - - self.assertEqual(r.status_code, 302) - - def request_and_parse(self, url): - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - return r, PyQuery(r.content) - - - def get_href(self, q, query): - link = q(query) - self.assertEqual(len(link), 1) - - return PyQuery(link[0]).attr('href') - - - def do_submission_email(self, the_url, to, body): - # check the page - r = self.client.get(the_url) - q = PyQuery(r.content) - post_button = q('[type=submit]:contains("Send email")') - self.assertEqual(len(post_button), 1) - subject = post_button.parents("form").find('input[name="subject"]').val() - frm = post_button.parents("form").find('input[name="frm"]').val() - cc = post_button.parents("form").find('input[name="cc"]').val() - reply_to = post_button.parents("form").find('input[name="reply_to"]').val() - - empty_outbox() - - # post submitter info - r = self.client.post(the_url, { - "subject": subject, - "frm": frm, - "to": to, - "cc": cc, - "reply_to": reply_to, - "body": body, - }) - - self.assertEqual(r.status_code, 302) - - self.assertEqual(len(outbox), 1) - - outmsg = outbox[0] - self.assertTrue(to in outmsg['To']) - - reply_to = outmsg['Reply-To'] - self.assertIsNotNone(reply_to, "Expected Reply-To") - - # Build a reply - - message_string = """To: {} -From: {} -Date: {} -Subject: test -""".format(reply_to, to, timezone.now().ctime()) - - result = process_response_email(message_string) - self.assertIsInstance(result, Message) - - return r - - def do_submission(self, name, rev, group=None, formats=["txt",]): - # We're not testing the submission process - just the submission status - - # get - url = urlreverse('ietf.submit.views.upload_submission') - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - q = PyQuery(r.content) - self.assertEqual(len(q('input[type=file][name=txt]')), 1) - self.assertEqual(len(q('input[type=file][name=xml]')), 1) - - # submit - files = {} - for format in formats: - files[format], author = submission_file(f'{name}-{rev}', f'{name}-{rev}.{format}', group, "test_submission.%s" % format) - - r = self.post_to_upload_submission(url, files) - if r.status_code != 302: - q = PyQuery(r.content) - print(q('div.invalid-feedback span.form-text div').text()) - - self.assertEqual(r.status_code, 302) - - status_url = r["Location"] - for format in formats: - self.assertTrue(os.path.exists(os.path.join(self.staging_dir, "%s-%s.%s" % (name, rev, format)))) - self.assertEqual(Submission.objects.filter(name=name).count(), 1) - submission = Submission.objects.get(name=name) - self.assertTrue(all([ c.passed!=False for c in submission.checks.all() ])) - self.assertEqual(len(submission.authors), 1) - author = submission.authors[0] - self.assertEqual(author["name"], "Author Name") - self.assertEqual(author["email"], "author@example.com") - - return status_url - - - def supply_extra_metadata(self, name, status_url, submitter_name, submitter_email): - # check the page - r = self.client.get(status_url) - q = PyQuery(r.content) - post_button = q('[type=submit]:contains("Post")') - self.assertEqual(len(post_button), 1) - action = post_button.parents("form").find('input[type=hidden][name="action"]').val() - - # post submitter info - r = self.client.post(status_url, { - "action": action, - "submitter-name": submitter_name, - "submitter-email": submitter_email, - "approvals_received": True, - }) - - if r.status_code == 302: - submission = Submission.objects.get(name=name) - self.assertEqual(submission.submitter, email.utils.formataddr((submitter_name, submitter_email))) - - return r - # Transaction.on_commit() requires use of TransactionTestCase, but that has a performance penalty. Replace it # with a no-op for testing purposes. diff --git a/ietf/submit/urls.py b/ietf/submit/urls.py index 2309ec55cd..4f53103c2c 100644 --- a/ietf/submit/urls.py +++ b/ietf/submit/urls.py @@ -17,15 +17,6 @@ url(r'^approvals/cancelpreapproval/(?P[a-f\d]+)/$', views.cancel_preapproval), url(r'^manualpost/$', views.manualpost), - url(r'^manualpost/addemail$', views.add_manualpost_email), - url(r'^manualpost/addemail/(?P\d+)/(?P[a-f\d]*)/$', views.add_manualpost_email), - url(r'^manualpost/attachment/(?P\d+)/(?P\d+)/(?P.*)$', views.show_submission_email_attachment), - url(r'^manualpost/cancel$', views.cancel_waiting_for_draft), - url(r'^manualpost/email/(?P\d+)/(?P\d+)/$', views.show_submission_email_message), - url(r'^manualpost/email/(?P\d+)/(?P\d+)/(?P[a-f\d]*)/$', views.show_submission_email_message), - url(r'^manualpost/replyemail/(?P\d+)/(?P\d+)/$', views.send_submission_email), - url(r'^manualpost/sendemail/(?P\d+)/$', views.send_submission_email), - # proof-of-concept for celery async tasks url(r'^async-poke/?$', views.async_poke_test), -] \ No newline at end of file +] diff --git a/ietf/submit/views.py b/ietf/submit/views.py index b583a53fc4..6f23ba49d4 100644 --- a/ietf/submit/views.py +++ b/ietf/submit/views.py @@ -3,7 +3,6 @@ import re -import base64 import datetime from typing import Optional, cast # pyflakes:ignore @@ -22,31 +21,30 @@ import debug # pyflakes:ignore -from ietf.doc.models import Document, AddedMessageEvent +from ietf.doc.models import Document from ietf.doc.forms import ExtResourceForm from ietf.group.models import Group from ietf.group.utils import group_features_group_filter from ietf.ietfauth.utils import has_role, role_required from ietf.mailtrigger.utils import gather_address_lists -from ietf.message.models import Message, MessageAttachment from ietf.person.models import Email from ietf.submit.forms import (SubmissionAutoUploadForm, AuthorForm, SubmitterForm, EditSubmissionForm, - PreapprovalForm, ReplacesForm, SubmissionEmailForm, MessageModelForm, + PreapprovalForm, ReplacesForm, DeprecatedSubmissionAutoUploadForm, SubmissionManualUploadForm) -from ietf.submit.mail import send_full_url, send_manual_post_request, add_submission_email, get_reply_to +from ietf.submit.mail import send_full_url, send_manual_post_request from ietf.submit.models import (Submission, Preapproval, SubmissionExtResource, - DraftSubmissionStateName, SubmissionEmailEvent ) + DraftSubmissionStateName ) from ietf.submit.tasks import process_uploaded_submission_task, process_and_accept_uploaded_submission_task, poke from ietf.submit.utils import ( approvable_submissions_for_user, preapprovals_for_user, recently_approved_by_user, validate_submission, create_submission_event, docevent_from_submission, post_submission, cancel_submission, rename_submission_files, remove_submission_files, get_draft_meta, get_submission, fill_in_submission, apply_checkers, save_files, clear_existing_files, check_submission_revision_consistency, accept_submission, accept_submission_requires_group_approval, - accept_submission_requires_prev_auth_approval, update_submission_external_resources, remote_ip ) + accept_submission_requires_prev_auth_approval, update_submission_external_resources) from ietf.stats.utils import clean_country_name from ietf.utils.accesstoken import generate_access_token from ietf.utils.log import log -from ietf.utils.mail import parseaddr, send_mail_message +from ietf.utils.mail import parseaddr from ietf.utils.response import permission_denied from ietf.utils.timezone import date_today @@ -780,243 +778,14 @@ def manualpost(request): s.passes_checks = all([ c.passed!=False for c in s.checks.all() ]) s.errors = validate_submission(s) - waiting_for_draft = Submission.objects.filter(state_id = "waiting-for-draft").distinct() - - return render(request, 'submit/manual_post.html', - {'manual': manual, - 'selected': 'manual_posts', - 'waiting_for_draft': waiting_for_draft}) - - -def cancel_waiting_for_draft(request): - if request.method == 'POST': - can_cancel = has_role(request.user, "Secretariat") - - if not can_cancel: - permission_denied(request, 'You do not have permission to perform this action.') - - submission_id = request.POST.get('submission_id', '') - access_token = request.POST.get('access_token', '') - - submission = get_submission_or_404(submission_id, access_token = access_token) - cancel_submission(submission) - - create_submission_event(request, submission, "Cancelled submission") - if (submission.rev != "00"): - # Add a doc event - docevent_from_submission(submission, "Cancelled submission for rev {}".format(submission.rev)) - - return redirect("ietf.submit.views.manualpost") - - -@role_required('Secretariat',) -def add_manualpost_email(request, submission_id=None, access_token=None): - """Add email to submission history""" - - if request.method == 'POST': - try: - button_text = request.POST.get('submit', '') - if button_text == 'Cancel': - return redirect("submit/manual_post.html") - - form = SubmissionEmailForm(request.POST) - if form.is_valid(): - submission_pk = form.cleaned_data['submission_pk'] - message = form.cleaned_data['message'] - #in_reply_to = form.cleaned_data['in_reply_to'] - # create Message - - if form.cleaned_data['direction'] == 'incoming': - msgtype = 'msgin' - else: - msgtype = 'msgout' - - submission, submission_email_event = ( - add_submission_email(request=request, - remote_ip=remote_ip(request), - name = form.draft_name, - rev=form.revision, - submission_pk = submission_pk, - message = message, - by = request.user.person, - msgtype = msgtype) ) - - messages.success(request, 'Email added.') - - try: - draft = Document.objects.get(name=submission.name) - except Document.DoesNotExist: - # Assume this is revision 00 - we'll do this later - draft = None - - if (draft != None): - e = AddedMessageEvent(type="added_message", doc=draft) - e.message = submission_email_event.submissionemailevent.message - e.msgtype = submission_email_event.submissionemailevent.msgtype - e.in_reply_to = submission_email_event.submissionemailevent.in_reply_to - e.by = request.user.person - e.desc = submission_email_event.desc - e.time = submission_email_event.time - e.save() - - return redirect("ietf.submit.views.manualpost") - except ValidationError as e: - form = SubmissionEmailForm(request.POST) - form._errors = {} - form._errors["__all__"] = form.error_class(["There was a failure uploading your message. (%s)" % e.message]) - else: - initial = { - } - - if (submission_id != None): - submission = get_submission_or_404(submission_id, access_token) - initial['name'] = "{}-{}".format(submission.name, submission.rev) - initial['direction'] = 'incoming' - initial['submission_pk'] = submission.pk - else: - initial['direction'] = 'incoming' - - form = SubmissionEmailForm(initial=initial) - - return render(request, 'submit/add_submit_email.html',dict(form=form)) - - -@role_required('Secretariat',) -def send_submission_email(request, submission_id, message_id=None): - """Send an email related to a submission""" - submission = get_submission_or_404(submission_id, access_token = None) - - if request.method == 'POST': - button_text = request.POST.get('submit', '') - if button_text == 'Cancel': - return redirect('ietf.submit.views.submission_status', - submission_id=submission.id, - access_token=submission.access_token()) - - form = MessageModelForm(request.POST) - if form.is_valid(): - # create Message - msg = Message.objects.create( - by = request.user.person, - subject = form.cleaned_data['subject'], - frm = form.cleaned_data['frm'], - to = form.cleaned_data['to'], - cc = form.cleaned_data['cc'], - bcc = form.cleaned_data['bcc'], - reply_to = form.cleaned_data['reply_to'], - body = form.cleaned_data['body'] - ) - - in_reply_to_id = form.cleaned_data['in_reply_to_id'] - in_reply_to = None - rp = "" - - if in_reply_to_id: - rp = " reply" - try: - in_reply_to = Message.objects.get(id=in_reply_to_id) - except Message.DoesNotExist: - log("Unable to retrieve in_reply_to message: %s" % in_reply_to_id) - - desc = "Sent message {} - manual post - {}-{}".format(rp, - submission.name, - submission.rev) - SubmissionEmailEvent.objects.create( - submission = submission, - desc = desc, - msgtype = 'msgout', - by = request.user.person, - message = msg, - in_reply_to = in_reply_to) - - # send email - send_mail_message(None,msg) - - messages.success(request, 'Email sent.') - return redirect('ietf.submit.views.submission_status', - submission_id=submission.id, - access_token=submission.access_token()) - - else: - reply_to = get_reply_to() - msg = None - - if not message_id: - addrs = gather_address_lists('sub_confirmation_requested',submission=submission).as_strings(compact=False) - to_email = addrs.to - cc = addrs.cc - subject = 'Regarding {}'.format(submission.name) - else: - try: - submitEmail = SubmissionEmailEvent.objects.get(id=message_id) - msg = submitEmail.message - - if msg: - to_email = msg.frm - cc = msg.cc - subject = 'Re:{}'.format(msg.subject) - else: - to_email = None - cc = None - subject = 'Regarding {}'.format(submission.name) - except Message.DoesNotExist: - to_email = None - cc = None - subject = 'Regarding {}'.format(submission.name) - - initial = { - 'to': to_email, - 'cc': cc, - 'frm': settings.IDSUBMIT_FROM_EMAIL, - 'subject': subject, - 'reply_to': reply_to, + return render( + request, + 'submit/manual_post.html', + { + 'manual': manual, + 'selected': 'manual_posts' } - - if msg: - initial['in_reply_to_id'] = msg.id - - form = MessageModelForm(initial=initial) - - return render(request, "submit/email.html", { - 'submission': submission, - 'access_token': submission.access_token(), - 'form':form}) - - -def show_submission_email_message(request, submission_id, message_id, access_token=None): - submission = get_submission_or_404(submission_id, access_token) - - submitEmail = get_object_or_404(SubmissionEmailEvent, pk=message_id) - attachments = submitEmail.message.messageattachment_set.all() - - return render(request, 'submit/submission_email.html', - {'submission': submission, - 'message': submitEmail, - 'attachments': attachments}) - -def show_submission_email_attachment(request, submission_id, message_id, filename, access_token=None): - get_submission_or_404(submission_id, access_token) - - message = get_object_or_404(SubmissionEmailEvent, pk=message_id) - - attach = get_object_or_404(MessageAttachment, - message=message.message, - filename=filename) - - if attach.encoding == "base64": - body = base64.b64decode(attach.body) - else: - body = attach.body.encode('utf-8') - - if attach.content_type is None: - content_type='text/plain' - else: - content_type=attach.content_type - - response = HttpResponse(body, content_type=content_type) - response['Content-Disposition'] = 'attachment; filename=%s' % attach.filename - response['Content-Length'] = len(body) - return response + ) def get_submission_or_404(submission_id, access_token=None): diff --git a/ietf/templates/submit/add_submit_email.html b/ietf/templates/submit/add_submit_email.html deleted file mode 100644 index 9ca07ec7fe..0000000000 --- a/ietf/templates/submit/add_submit_email.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2015, All Rights Reserved #} -{% load origin %} -{% load django_bootstrap5 %} -{% block title %} - {% if submission == None %} - Add new submission request email - {% else %} - Add submission request email to {{ submission.name }} - {% endif %} -{% endblock %} -{% block content %} - {% origin %} -

Add email

-

- {% if submission == None %} - A new submission request will be created for the given name and revision. The - name must take the form draft-xxx-nn where xxx is lowercase letters, digits or dashes - and nn is the revision number, 00 for the initial revision. For example, - draft-my-spec-00. - {% else %} - The email will be added to the submission history for {{ submission.name }}. - {% endif %} -

-
- {% csrf_token %} - {% bootstrap_form form %} - - Back -
-{% endblock %} \ No newline at end of file diff --git a/ietf/templates/submit/email.html b/ietf/templates/submit/email.html deleted file mode 100644 index dde4fa50c1..0000000000 --- a/ietf/templates/submit/email.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2015, All Rights Reserved #} -{% load origin %} -{% load static %} -{% load django_bootstrap5 %} -{% block title %}Email related to{{ submission.name }}{% endblock %} -{% block pagehead %} - -{% endblock %} -{% block content %} - {% origin %} -

- Email related to -
- {{ submission.name }} -

-
- {% csrf_token %} - {% bootstrap_form form %} - -
-{% endblock %} -{% block js %} - -{% endblock %} \ No newline at end of file diff --git a/ietf/templates/submit/manual_post.html b/ietf/templates/submit/manual_post.html index 1f74af4a61..6e4a2ba42a 100644 --- a/ietf/templates/submit/manual_post.html +++ b/ietf/templates/submit/manual_post.html @@ -58,76 +58,6 @@

Submissions needing manual posting

{% endif %} -

Submissions awaiting Internet-Draft upload

- {% if not waiting_for_draft %} -

- There are no submissions awaiting Internet-Draft upload. -

- {% else %} - - - - - - - - - - - {% for s in waiting_for_draft %} - - {% if user.is_authenticated %} - - {% else %} - - {% endif %} - - - - - {% endfor %} - -
NameRevSubmitted
- - {{ s.name }} - - - {{ s.name }} - {{ s.rev }}{{ s.submission_date }} - {% if user|has_role:"Secretariat" %} -
- {% csrf_token %} - - - -
- {% endif %} - {% if user|has_role:"Secretariat" %} - - Add email - - {% endif %} -
- {% endif %} - {% if user|has_role:"Secretariat" %} - - New submission from email - - {% endif %} {% endblock %} {% block js %} diff --git a/ietf/templates/submit/submission_email.html b/ietf/templates/submit/submission_email.html deleted file mode 100644 index 801ace3b9b..0000000000 --- a/ietf/templates/submit/submission_email.html +++ /dev/null @@ -1,61 +0,0 @@ -{% extends "submit/submit_base.html" %} -{# Copyright The IETF Trust 2015, All Rights Reserved #} -{% load origin static textfilters ietf_filters %} -{% block title %}Submission email{% endblock %} -{% load ietf_filters %} -{% block submit_content %} - {% origin %} -

Email for {{ submission.name }}

-
-
- Uploaded -
-
- {{ message.time }} -
-
- Date -
-
- {{ message.message.time }} -
-
- From -
-
- {{ message.message.frm|linkify }} -
-
- Subject -
-
- {{ message.message.subject }} -
-
- Message -
-
-
{{ message.message.body|urlize_ietf_docs|linkify|linebreaksbr }}
-
-
- Attachment -
-
- {% for a in attachments %} - - {{ a.filename }} - -
- {% endfor %} -
-
- {% if user|has_role:"Secretariat" %} - - Reply - - {% endif %} -{% endblock %} \ No newline at end of file diff --git a/ietf/templates/submit/submission_status.html b/ietf/templates/submit/submission_status.html index 6f18d70946..733b4217a0 100644 --- a/ietf/templates/submit/submission_status.html +++ b/ietf/templates/submit/submission_status.html @@ -520,14 +520,6 @@

{% endif %} - {% if user|has_role:"Secretariat" %} - - Send Email - - {% endif %} {% if show_send_full_url %}

@@ -584,39 +576,10 @@

{% endif %} - {% if e.desc|startswith:"Received message" or e.desc|startswith:"Sent message" %} - {% with m=e.submissionemailevent.message %} - {% if user.is_authenticated %} - - {% if e.desc|startswith:"Received message" and user|has_role:"Secretariat" %} - - Reply - - {% endif %} - Email: - - {{ e.desc }} - - - {% else %} - - Email: - - {{ e.desc }} - - - {% endif %} - {% endwith %} - {% else %} - - {{ e.desc|urlize_ietf_docs|linkify }} - - {% endif %} + + + {{ e.desc|urlize_ietf_docs|linkify }} + {% endfor %}