diff --git a/ietf/api/urls.py b/ietf/api/urls.py index 396b3813d..48525dfda 100644 --- a/ietf/api/urls.py +++ b/ietf/api/urls.py @@ -59,7 +59,7 @@ urlpatterns = [ # Email alias listing url(r'^person/email/$', api_views.active_email_list), # Draft submission API - url(r'^submit/?$', submit_views.api_submit), + url(r'^submit/?$', submit_views.api_submit_tombstone), # Draft upload API url(r'^submission/?$', submit_views.api_submission), # Draft submission state API diff --git a/ietf/submit/forms.py b/ietf/submit/forms.py index 4e5644b36..2781d3365 100644 --- a/ietf/submit/forms.py +++ b/ietf/submit/forms.py @@ -2,19 +2,16 @@ # -*- coding: utf-8 -*- -import io import os import re import datetime import email import sys import tempfile -import xml2rfc from contextlib import ExitStack from email.utils import formataddr from typing import Tuple -from unidecode import unidecode from django import forms from django.conf import settings @@ -37,10 +34,8 @@ from ietf.submit.models import Submission, Preapproval from ietf.submit.utils import validate_submission_name, validate_submission_rev, validate_submission_document_date, remote_ip from ietf.submit.parsers.plain_parser import PlainParser from ietf.submit.parsers.xml_parser import XMLParser -from ietf.utils import log from ietf.utils.draft import PlaintextDraft from ietf.utils.fields import ModelMultipleChoiceField -from ietf.utils.text import normalize_text from ietf.utils.timezone import date_today from ietf.utils.xmldraft import InvalidXMLError, XMLDraft, XMLParseError @@ -371,273 +366,6 @@ class SubmissionBaseUploadForm(forms.Form): return None -class DeprecatedSubmissionBaseUploadForm(SubmissionBaseUploadForm): - def clean(self): - def format_messages(where, e, log): - out = log.write_out.getvalue().splitlines() - err = log.write_err.getvalue().splitlines() - m = str(e) - if m: - m = [ m ] - else: - import traceback - typ, val, tb = sys.exc_info() - m = traceback.format_exception(typ, val, tb) - m = [ l.replace('\n ', ':\n ') for l in m ] - msgs = [s for s in (["Error from xml2rfc (%s):" % (where,)] + m + out + err) if s] - return msgs - - if self.shutdown and not has_role(self.request.user, "Secretariat"): - raise forms.ValidationError(self.cutoff_warning) - - for ext in self.formats: - f = self.cleaned_data.get(ext, None) - if not f: - continue - self.file_types.append('.%s' % ext) - if not ('.txt' in self.file_types or '.xml' in self.file_types): - if not self.errors: - raise forms.ValidationError('Unexpected submission file types; found %s, but %s is required' % (', '.join(self.file_types), ' or '.join(self.base_formats))) - - #debug.show('self.cleaned_data["xml"]') - if self.cleaned_data.get('xml'): - #if not self.cleaned_data.get('txt'): - xml_file = self.cleaned_data.get('xml') - file_name = {} - xml2rfc.log.write_out = io.StringIO() # open(os.devnull, "w") - xml2rfc.log.write_err = io.StringIO() # open(os.devnull, "w") - tfn = None - with ExitStack() as stack: - @stack.callback - def cleanup(): # called when context exited, even in case of exception - if tfn is not None: - os.unlink(tfn) - - # We need to write the xml file to disk in order to hand it - # over to the xml parser. XXX FIXME: investigate updating - # xml2rfc to be able to work with file handles to in-memory - # files. - name, ext = os.path.splitext(os.path.basename(xml_file.name)) - with tempfile.NamedTemporaryFile(prefix=name+'-', - suffix='.xml', - mode='wb+', - delete=False) as tf: - tfn = tf.name - for chunk in xml_file.chunks(): - tf.write(chunk) - - parser = xml2rfc.XmlRfcParser(str(tfn), quiet=True) - # --- Parse the xml --- - try: - self.xmltree = parser.parse(remove_comments=False) - # If we have v2, run it through v2v3. Keep track of the submitted version, though. - self.xmlroot = self.xmltree.getroot() - self.xml_version = self.xmlroot.get('version', '2') - if self.xml_version == '2': - v2v3 = xml2rfc.V2v3XmlWriter(self.xmltree) - self.xmltree.tree = v2v3.convert2to3() - self.xmlroot = self.xmltree.getroot() # update to the new root - - draftname = self.xmlroot.attrib.get('docName') - if draftname is None: - self.add_error('xml', "No docName attribute found in the xml root element") - name_error = validate_submission_name(draftname) - if name_error: - self.add_error('xml', name_error) # This is a critical and immediate failure - do not proceed with other validation. - else: - revmatch = re.search("-[0-9][0-9]$", draftname) - if revmatch: - self.revision = draftname[-2:] - self.filename = draftname[:-3] - else: - self.revision = None - self.filename = draftname - self.title = self.xmlroot.findtext('front/title').strip() - if type(self.title) is str: - self.title = unidecode(self.title) - self.title = normalize_text(self.title) - self.abstract = (self.xmlroot.findtext('front/abstract') or '').strip() - if type(self.abstract) is str: - self.abstract = unidecode(self.abstract) - author_info = self.xmlroot.findall('front/author') - for author in author_info: - info = { - "name": author.attrib.get('fullname'), - "email": author.findtext('address/email'), - "affiliation": author.findtext('organization'), - } - elem = author.find('address/postal/country') - if elem != None: - ascii_country = elem.get('ascii', None) - info['country'] = ascii_country if ascii_country else elem.text - - for item in info: - if info[item]: - info[item] = info[item].strip() - self.authors.append(info) - - # --- Prep the xml --- - file_name['xml'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (self.filename, self.revision, ext)) - try: - prep = xml2rfc.PrepToolWriter(self.xmltree, quiet=True, liberal=True, keep_pis=[xml2rfc.V3_PI_TARGET]) - prep.options.accept_prepped = True - self.xmltree.tree = prep.prep() - if self.xmltree.tree == None: - self.add_error('xml', "Error from xml2rfc (prep): %s" % prep.errors) - except Exception as e: - msgs = format_messages('prep', e, xml2rfc.log) - self.add_error('xml', msgs) - - # --- Convert to txt --- - if not ('txt' in self.cleaned_data and self.cleaned_data['txt']): - file_name['txt'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (self.filename, self.revision)) - try: - writer = xml2rfc.TextWriter(self.xmltree, quiet=True) - writer.options.accept_prepped = True - writer.write(file_name['txt']) - log.log("In %s: xml2rfc %s generated %s from %s (version %s)" % - ( os.path.dirname(file_name['xml']), - xml2rfc.__version__, - os.path.basename(file_name['txt']), - os.path.basename(file_name['xml']), - self.xml_version)) - except Exception as e: - msgs = format_messages('txt', e, xml2rfc.log) - log.log('\n'.join(msgs)) - self.add_error('xml', msgs) - - # --- Convert to html --- - try: - file_name['html'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.html' % (self.filename, self.revision)) - writer = xml2rfc.HtmlWriter(self.xmltree, quiet=True) - writer.write(file_name['html']) - self.file_types.append('.html') - log.log("In %s: xml2rfc %s generated %s from %s (version %s)" % - ( os.path.dirname(file_name['xml']), - xml2rfc.__version__, - os.path.basename(file_name['html']), - os.path.basename(file_name['xml']), - self.xml_version)) - except Exception as e: - msgs = format_messages('html', e, xml2rfc.log) - self.add_error('xml', msgs) - - except Exception as e: - try: - msgs = format_messages('txt', e, xml2rfc.log) - log.log('\n'.join(msgs)) - self.add_error('xml', msgs) - except Exception: - self.add_error('xml', "An exception occurred when trying to process the XML file: %s" % e) - - # The following errors are likely noise if we have previous field - # errors: - if self.errors: - raise forms.ValidationError('') - - if self.cleaned_data.get('txt'): - # try to parse it - txt_file = self.cleaned_data['txt'] - txt_file.seek(0) - bytes = txt_file.read() - txt_file.seek(0) - try: - text = bytes.decode(PlainParser.encoding) - self.parsed_draft = PlaintextDraft(text, txt_file.name) - if self.filename == None: - self.filename = self.parsed_draft.filename - elif self.filename != self.parsed_draft.filename: - self.add_error('txt', "Inconsistent name information: xml:%s, txt:%s" % (self.filename, self.parsed_draft.filename)) - if self.revision == None: - self.revision = self.parsed_draft.revision - elif self.revision != self.parsed_draft.revision: - self.add_error('txt', "Inconsistent revision information: xml:%s, txt:%s" % (self.revision, self.parsed_draft.revision)) - if self.title == None: - self.title = self.parsed_draft.get_title() - elif self.title != self.parsed_draft.get_title(): - self.add_error('txt', "Inconsistent title information: xml:%s, txt:%s" % (self.title, self.parsed_draft.get_title())) - except (UnicodeDecodeError, LookupError) as e: - self.add_error('txt', 'Failed decoding the uploaded file: "%s"' % str(e)) - - rev_error = validate_submission_rev(self.filename, self.revision) - if rev_error: - raise forms.ValidationError(rev_error) - - # The following errors are likely noise if we have previous field - # errors: - if self.errors: - raise forms.ValidationError('') - - if not self.filename: - raise forms.ValidationError("Could not extract a valid Internet-Draft name from the upload. " - "To fix this in a text upload, please make sure that the full Internet-Draft name including " - "revision number appears centered on its own line below the document title on the " - "first page. In an xml upload, please make sure that the top-level " - "element has a docName attribute which provides the full Internet-Draft name including " - "revision number.") - - if not self.revision: - raise forms.ValidationError("Could not extract a valid Internet-Draft revision from the upload. " - "To fix this in a text upload, please make sure that the full Internet-Draft name including " - "revision number appears centered on its own line below the document title on the " - "first page. In an xml upload, please make sure that the top-level " - "element has a docName attribute which provides the full Internet-Draft name including " - "revision number.") - - if not self.title: - raise forms.ValidationError("Could not extract a valid title from the upload") - - if self.cleaned_data.get('txt') or self.cleaned_data.get('xml'): - # check group - self.group = self.deduce_group(self.filename) - - # check existing - existing = Submission.objects.filter(name=self.filename, rev=self.revision).exclude(state__in=("posted", "cancel", "waiting-for-draft")) - if existing: - raise forms.ValidationError(mark_safe('A submission with same name and revision is currently being processed. Check the status here.' % urlreverse("ietf.submit.views.submission_status", kwargs={ 'submission_id': existing[0].pk }))) - - # cut-off - if self.revision == '00' and self.in_first_cut_off: - raise forms.ValidationError(mark_safe(self.cutoff_warning)) - - # check thresholds - today = date_today() - - self.check_submissions_thresholds( - "for the Internet-Draft %s" % self.filename, - dict(name=self.filename, rev=self.revision, submission_date=today), - settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME, settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME_SIZE, - ) - self.check_submissions_thresholds( - "for the same submitter", - dict(remote_ip=self.remote_ip, submission_date=today), - settings.IDSUBMIT_MAX_DAILY_SAME_SUBMITTER, settings.IDSUBMIT_MAX_DAILY_SAME_SUBMITTER_SIZE, - ) - if self.group: - self.check_submissions_thresholds( - "for the group \"%s\"" % (self.group.acronym), - dict(group=self.group, submission_date=today), - settings.IDSUBMIT_MAX_DAILY_SAME_GROUP, settings.IDSUBMIT_MAX_DAILY_SAME_GROUP_SIZE, - ) - self.check_submissions_thresholds( - "across all submitters", - dict(submission_date=today), - settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS, settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS_SIZE, - ) - - return super().clean() - - -class DeprecatedSubmissionAutoUploadForm(DeprecatedSubmissionBaseUploadForm): - """Full-service upload form, replaced by the asynchronous version""" - user = forms.EmailField(required=True) - - def __init__(self, request, *args, **kwargs): - super(DeprecatedSubmissionAutoUploadForm, self).__init__(request, *args, **kwargs) - self.formats = ['xml', ] - self.base_formats = ['xml', ] - - class SubmissionManualUploadForm(SubmissionBaseUploadForm): txt = forms.FileField(label='.txt format', required=False) formats = SubmissionBaseUploadForm.formats + ('txt',) @@ -676,6 +404,7 @@ class SubmissionManualUploadForm(SubmissionBaseUploadForm): ) return txt_file + class SubmissionAutoUploadForm(SubmissionBaseUploadForm): user = forms.EmailField(required=True) replaces = forms.CharField(required=False, max_length=1000, strip=True) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index b48168f8a..ed28c7ef0 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -44,7 +44,7 @@ from ietf.meeting.models import Meeting from ietf.meeting.factories import MeetingFactory from ietf.name.models import DraftSubmissionStateName, FormalLanguageName from ietf.person.models import Person -from ietf.person.factories import UserFactory, PersonFactory, EmailFactory +from ietf.person.factories import UserFactory, PersonFactory from ietf.submit.factories import SubmissionFactory, SubmissionExtResourceFactory from ietf.submit.forms import SubmissionBaseUploadForm, SubmissionAutoUploadForm from ietf.submit.models import Submission, Preapproval, SubmissionExtResource @@ -2345,6 +2345,12 @@ class ApiSubmissionTests(BaseSubmitTestCase): super().setUp() MeetingFactory(type_id='ietf', date=date_today()+datetime.timedelta(days=60)) + def test_api_submit_tombstone(self): + """Tombstone for obsolete API endpoint should return 410 Gone""" + url = urlreverse("ietf.submit.views.api_submit_tombstone") + self.assertEqual(self.client.get(url).status_code, 410) + self.assertEqual(self.client.post(url).status_code, 410) + def test_upload_draft(self): """api_submission accepts a submission and queues it for processing""" url = urlreverse('ietf.submit.views.api_submission') @@ -3191,141 +3197,6 @@ class AsyncSubmissionTests(BaseSubmitTestCase): self.assertEqual(subm.state_id, "cancel") self.assertEqual(subm.submissionevent_set.count(), 2) - -class ApiSubmitTests(BaseSubmitTestCase): - def setUp(self): - super().setUp() - # break early in case of missing configuration - self.assertTrue(os.path.exists(settings.IDSUBMIT_IDNITS_BINARY)) - MeetingFactory(type_id='ietf', date=date_today()+datetime.timedelta(days=60)) - - def do_post_submission(self, rev, author=None, name=None, group=None, email=None, title=None, year=None): - url = urlreverse('ietf.submit.views.api_submit') - if author is None: - author = PersonFactory() - if name is None: - slug = re.sub('[^a-z0-9-]+', '', author.ascii_parts()[3].lower()) - name = 'draft-%s-foo' % slug - if email is None: - email = author.user.username - # submit - data = {} - data['xml'], author = submission_file(f'{name}-{rev}', f'{name}-{rev}.xml', group, "test_submission.xml", author=author, email=email, title=title, year=year) - data['user'] = email - r = self.client.post(url, data) - return r, author, name - - def test_api_submit_info(self): - url = urlreverse('ietf.submit.views.api_submit') - r = self.client.get(url) - expected = "A simplified Internet-Draft submission interface, intended for automation" - self.assertContains(r, expected, status_code=200) - - def test_api_submit_bad_method(self): - url = urlreverse('ietf.submit.views.api_submit') - r = self.client.put(url) - self.assertEqual(r.status_code, 405) - - def test_api_submit_ok(self): - r, author, name = self.do_post_submission('00') - expected = "Upload of %s OK, confirmation requests sent to:\n %s" % (name, author.formatted_email().replace('\n','')) - self.assertContains(r, expected, status_code=200) - - def test_api_submit_secondary_email_active(self): - person = PersonFactory() - email = EmailFactory(person=person) - r, author, name = self.do_post_submission('00', author=person, email=email.address) - for expected in [ - "Upload of %s OK, confirmation requests sent to:" % (name, ), - author.formatted_email().replace('\n',''), - ]: - self.assertContains(r, expected, status_code=200) - - def test_api_submit_secondary_email_inactive(self): - person = PersonFactory() - prim = person.email() - prim.primary = True - prim.save() - email = EmailFactory(person=person, active=False) - r, author, name = self.do_post_submission('00', author=person, email=email.address) - expected = "No such user: %s" % email.address - self.assertContains(r, expected, status_code=400) - - def test_api_submit_no_user(self): - email='nonexistant.user@example.org' - r, author, name = self.do_post_submission('00', email=email) - expected = "No such user: %s" % email - self.assertContains(r, expected, status_code=400) - - def test_api_submit_no_person(self): - user = UserFactory() - email = user.username - r, author, name = self.do_post_submission('00', email=email) - expected = "No person with username %s" % email - self.assertContains(r, expected, status_code=400) - - def test_api_submit_wrong_revision(self): - r, author, name = self.do_post_submission('01') - expected = "Invalid revision (revision 00 is expected)" - self.assertContains(r, expected, status_code=400) - - def test_api_submit_update_existing_submissiondocevent_rev(self): - draft, _ = create_draft_submission_with_rev_mismatch(rev='01') - r, _, __ = self.do_post_submission(rev='01', name=draft.name) - expected = "Submission failed" - self.assertContains(r, expected, status_code=409) - - def test_api_submit_update_later_submissiondocevent_rev(self): - draft, _ = create_draft_submission_with_rev_mismatch(rev='02') - r, _, __ = self.do_post_submission(rev='01', name=draft.name) - expected = "Submission failed" - self.assertContains(r, expected, status_code=409) - - def test_api_submit_pending_submission(self): - r, author, name = self.do_post_submission('00') - expected = "Upload of" - self.assertContains(r, expected, status_code=200) - r, author, name = self.do_post_submission('00', author=author, name=name) - expected = "A submission with same name and revision is currently being processed" - self.assertContains(r, expected, status_code=400) - - def test_api_submit_no_title(self): - r, author, name = self.do_post_submission('00', title=" ") - expected = "Could not extract a valid title from the upload" - self.assertContains(r, expected, status_code=400) - - def test_api_submit_failed_idnits(self): - # `year` on the next line must be leap year or this test will fail every Feb 29 - r, author, name = self.do_post_submission('00', year="2012") - expected = "Document date must be within 3 days of submission date" - self.assertContains(r, expected, status_code=400) - - def test_api_submit_keeps_extresources(self): - """API submit should not disturb doc external resources - - Tests that the submission inherits the existing doc's docextresource_set. - Relies on separate testing that Submission external_resources will be - handled appropriately. - """ - draft = WgDraftFactory() - - # add an external resource - self.assertEqual(draft.docextresource_set.count(), 0) - extres = draft.docextresource_set.create( - name_id='faq', - display_name='this is a display name', - value='https://example.com/faq-for-test.html', - ) - - r, _, __ = self.do_post_submission('01', name=draft.name) - self.assertEqual(r.status_code, 200) - # draft = Document.objects.get(pk=draft.pk) # update the draft - sub = Submission.objects.get(name=draft.name) - self.assertEqual( - [str(r) for r in sub.external_resources.all()], - [str(extres)], - ) - class RefsTests(BaseSubmitTestCase): diff --git a/ietf/submit/views.py b/ietf/submit/views.py index 6f23ba49d..3f745741e 100644 --- a/ietf/submit/views.py +++ b/ietf/submit/views.py @@ -1,7 +1,5 @@ # Copyright The IETF Trust 2011-2020, All Rights Reserved # -*- coding: utf-8 -*- - - import re import datetime @@ -29,17 +27,15 @@ from ietf.ietfauth.utils import has_role, role_required from ietf.mailtrigger.utils import gather_address_lists from ietf.person.models import Email from ietf.submit.forms import (SubmissionAutoUploadForm, AuthorForm, SubmitterForm, EditSubmissionForm, - PreapprovalForm, ReplacesForm, - DeprecatedSubmissionAutoUploadForm, SubmissionManualUploadForm) + PreapprovalForm, ReplacesForm, SubmissionManualUploadForm) from ietf.submit.mail import send_full_url, send_manual_post_request from ietf.submit.models import (Submission, Preapproval, SubmissionExtResource, 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, + post_submission, cancel_submission, rename_submission_files, remove_submission_files, + get_submission, save_files, clear_existing_files, accept_submission, accept_submission_requires_group_approval, 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 @@ -187,97 +183,14 @@ def api_submission_status(request, submission_id): @csrf_exempt -def api_submit(request): - "Automated submission entrypoint" - submission = None - def err(code, text): - return HttpResponse(text, status=code, content_type='text/plain') +def api_submit_tombstone(request): + """Tombstone for removed automated submission entrypoint""" + return render( + request, + 'submit/api_submit_info.html', + status=410, # Gone + ) - if request.method == 'GET': - return render(request, 'submit/api_submit_info.html') - elif request.method == 'POST': - exception = None - try: - form = DeprecatedSubmissionAutoUploadForm(request, data=request.POST, files=request.FILES) - if form.is_valid(): - log('got valid submission form for %s' % form.filename) - username = form.cleaned_data['user'] - user = User.objects.filter(username__iexact=username) - if user.count() == 0: - # See if a secondary login was being used - email = Email.objects.filter(address=username, active=True) - # The error messages don't talk about 'email', as the field we're - # looking at is still the 'username' field. - if email.count() == 0: - return err(400, "No such user: %s" % username) - elif email.count() > 1: - return err(500, "Multiple matching accounts for %s" % username) - email = email.first() - if not hasattr(email, 'person'): - return err(400, "No person matches %s" % username) - person = email.person - if not hasattr(person, 'user'): - return err(400, "No user matches: %s" % username) - user = person.user - elif user.count() > 1: - return err(500, "Multiple matching accounts for %s" % username) - else: - user = user.first() - if not hasattr(user, 'person'): - return err(400, "No person with username %s" % username) - - saved_files = save_files(form) - authors, abstract, file_name, file_size = get_draft_meta(form, saved_files) - for a in authors: - if not a['email']: - raise ValidationError("Missing email address for author %s" % a) - - submission = get_submission(form) - fill_in_submission(form, submission, authors, abstract, file_size) - apply_checkers(submission, file_name) - - create_submission_event(request, submission, desc="Uploaded submission via api_submit") - - errors = validate_submission(submission) - if errors: - raise ValidationError(errors) - - # must do this after validate_submission() or data needed for check may be invalid - if check_submission_revision_consistency(submission): - return err( 409, "Submission failed due to a document revision inconsistency error " - "in the database. Please contact the secretariat for assistance.") - - errors = [ c.message for c in submission.checks.all() if c.passed==False ] - if errors: - raise ValidationError(errors) - - if not username.lower() in [ a['email'].lower() for a in authors ]: - raise ValidationError('Submitter %s is not one of the document authors' % user.username) - - submission.submitter = user.person.formatted_email() - sent_to = accept_submission(submission, request) - - return HttpResponse( - "Upload of %s OK, confirmation requests sent to:\n %s" % (submission.name, ',\n '.join(sent_to)), - content_type="text/plain") - else: - raise ValidationError(form.errors) - except IOError as e: - exception = e - return err(500, "IO Error: %s" % str(e)) - except ValidationError as e: - exception = e - return err(400, "Validation Error: %s" % str(e)) - except Exception as e: - exception = e - raise - return err(500, "Exception: %s" % str(e)) - finally: - if exception and submission: - remove_submission_files(submission) - submission.delete() - else: - return err(405, "Method not allowed") def tool_instructions(request): return render(request, 'submit/tool_instructions.html', {'selected': 'instructions'}) diff --git a/ietf/templates/api/index.html b/ietf/templates/api/index.html index 8373a387f..e21a50101 100644 --- a/ietf/templates/api/index.html +++ b/ietf/templates/api/index.html @@ -9,7 +9,7 @@

This section describes the autogenerated read-only API towards the database tables. See also the - Internet-Draft submission API description + Internet-Draft submission API description and the IESG ballot position API description

diff --git a/ietf/templates/submit/api_submit_info.html b/ietf/templates/submit/api_submit_info.html index cd0d52410..75fc1abfc 100644 --- a/ietf/templates/submit/api_submit_info.html +++ b/ietf/templates/submit/api_submit_info.html @@ -1,56 +1,13 @@ {% extends "base.html" %} -{# Copyright The IETF Trust 2015-2022, All Rights Reserved #} +{# Copyright The IETF Trust 2015-2024, All Rights Reserved #} {% load origin ietf_filters %} -{% block title %}I-D submission API instructions{% endblock %} +{% block title %}Obsolete I-D submission API notice{% endblock %} {% block content %} {% origin %} -

Internet-Draft submission API instructions

+

Obsolete Internet-Draft submission API notice

- Note: API endpoint described here is known to have a slow response time or to fail - due to timeout for some Internet-Draft submissions, particularly those with large file sizes. - It is recommended to use the new API endpoint - instead for increased reliability. + The API endpoint previously available here is obsolete and is no longer supported. + Please use the new API endpoint + instead.

-

- A simplified Internet-Draft submission interface, intended for automation, - is available at {% absurl 'ietf.submit.views.api_submit' %}. -

-

- The interface accepts only XML uploads that can be processed on the server, and - requires the user to have a datatracker account. A successful submit still requires - the same email confirmation round-trip as submissions done through the regular - submission tool. -

-

- This interface does not provide all the options which the regular submission tool does. - Some limitations: -

- -

- It takes two parameters: -

- -

- It returns an appropriate http result code, and a brief explanatory text message. -

-

- Here is an example: -

-
-$ curl -S -F "user=user.name@example.com" -F "xml=@~/draft-user-example.xml" {% absurl 'ietf.submit.views.api_submit' %}
-Upload of draft-user-example OK, confirmation requests sent to:
-User Name <user.name@example.com>
-{% endblock %} \ No newline at end of file +{% endblock %}