addition to the existing 'pyang' checker. Added modal overlay displays showing the yang check results every place the yin/yang symbol is shown (red or green) to indicate the presencee and result of yang checks. Added a Yang Validation: line in the document meta-information section on the document's page in the datatracker. Added the result of the xym extaction to the yang check results, to make extration failures visible. Added the version of the used xym, pyang, and yanglint commands to the check results. Added an action to move successfully extracted and validated modules to the module library directories immediately on submission. Added the xym and pyang repositories as svn:external components, rather than listing them in requirements.txt, as there has been delays of many months between essential features in the repositories, and an actual release. We may get occasional buildbot failures if broken code is pulled in from the repository, but better that than the functionality failure of severely outdated componets. Added a new management command to re-run yang validation for active drafts for which yang modules were found at submission time, in order to pick up imported models which may have arrived in the model libraries after the draft's submission. Run daily from bin/daily. Added a table to hold version information for external commands. The yang checker output should include the version information of the used checkers, but seems unnecessary to run each command with its --version switch every time we check a module... Added a new management command to collect version information for external commands on demand. To be run daily from bin/daily. Added tests to verify that xym, pyang and yanglint information is available on the submission confirmation page, and updated the yang module contained in the test document to validate under both pyang and yanglint. Updated admin.py and resource.py files as needed. - Legacy-Id: 13630
865 lines
39 KiB
Python
865 lines
39 KiB
Python
# Copyright The IETF Trust 2007, All Rights Reserved
|
|
import base64
|
|
import datetime
|
|
import os
|
|
import xml2rfc
|
|
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.urls import reverse as urlreverse
|
|
from django.core.validators import validate_email, ValidationError
|
|
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden, HttpResponse
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.utils.module_loading import import_string
|
|
|
|
import debug # pyflakes:ignore
|
|
|
|
from ietf.doc.models import Document, DocAlias, AddedMessageEvent
|
|
from ietf.doc.utils import prettify_std_name
|
|
from ietf.group.models import Group
|
|
from ietf.ietfauth.utils import has_role, role_required
|
|
from ietf.mailtrigger.utils import gather_address_lists
|
|
from ietf.message.models import Message, MessageAttachment
|
|
from ietf.name.models import FormalLanguageName
|
|
from ietf.submit.forms import ( SubmissionUploadForm, AuthorForm, SubmitterForm, EditSubmissionForm,
|
|
PreapprovalForm, ReplacesForm, SubmissionEmailForm, MessageModelForm )
|
|
from ietf.submit.mail import ( send_full_url, send_approval_request_to_group,
|
|
send_submission_confirmation, send_manual_post_request, add_submission_email, get_reply_to )
|
|
from ietf.submit.models import (Submission, SubmissionCheck, Preapproval,
|
|
DraftSubmissionStateName, SubmissionEmailEvent )
|
|
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,
|
|
get_person_from_name_email )
|
|
from ietf.stats.utils import clean_country_name
|
|
from ietf.utils.accesstoken import generate_random_key, generate_access_token
|
|
from ietf.utils.draft import Draft
|
|
from ietf.utils.log import log
|
|
from ietf.utils.mail import send_mail_message
|
|
|
|
|
|
def upload_submission(request):
|
|
if request.method == 'POST':
|
|
try:
|
|
form = SubmissionUploadForm(request, data=request.POST, files=request.FILES)
|
|
if form.is_valid():
|
|
authors = []
|
|
file_name = {}
|
|
abstract = None
|
|
file_size = None
|
|
for ext in form.fields.keys():
|
|
f = form.cleaned_data[ext]
|
|
if not f:
|
|
continue
|
|
|
|
name = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.%s' % (form.filename, form.revision, ext))
|
|
file_name[ext] = name
|
|
with open(name, 'wb+') as destination:
|
|
for chunk in f.chunks():
|
|
destination.write(chunk)
|
|
|
|
if form.cleaned_data['xml']:
|
|
if not form.cleaned_data['txt']:
|
|
file_name['txt'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (form.filename, form.revision))
|
|
try:
|
|
pagedwriter = xml2rfc.PaginatedTextRfcWriter(form.xmltree, quiet=True)
|
|
pagedwriter.write(file_name['txt'])
|
|
except Exception as e:
|
|
raise ValidationError("Error from xml2rfc: %s" % e)
|
|
file_size = os.stat(file_name['txt']).st_size
|
|
# Some meta-information, such as the page-count, can only
|
|
# be retrieved from the generated text file. Provide a
|
|
# parsed draft object to get at that kind of information.
|
|
with open(file_name['txt']) as txt_file:
|
|
form.parsed_draft = Draft(txt_file.read(), txt_file.name)
|
|
|
|
else:
|
|
file_size = form.cleaned_data['txt'].size
|
|
|
|
if form.authors:
|
|
authors = form.authors
|
|
else:
|
|
# If we don't have an xml file, try to extract the
|
|
# relevant information from the text file
|
|
for author in form.parsed_draft.get_author_list():
|
|
full_name, first_name, middle_initial, last_name, name_suffix, email, country, company = author
|
|
|
|
name = full_name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
|
|
|
|
if email:
|
|
try:
|
|
validate_email(email)
|
|
except ValidationError:
|
|
email = ""
|
|
|
|
def turn_into_unicode(s):
|
|
if s is None:
|
|
return u""
|
|
|
|
if isinstance(s, unicode):
|
|
return s
|
|
else:
|
|
try:
|
|
return s.decode("utf-8")
|
|
except UnicodeDecodeError:
|
|
try:
|
|
return s.decode("latin-1")
|
|
except UnicodeDecodeError:
|
|
return ""
|
|
|
|
name = turn_into_unicode(name)
|
|
email = turn_into_unicode(email)
|
|
company = turn_into_unicode(company)
|
|
|
|
authors.append({
|
|
"name": name,
|
|
"email": email,
|
|
"affiliation": company,
|
|
"country": country
|
|
})
|
|
|
|
if form.abstract:
|
|
abstract = form.abstract
|
|
else:
|
|
abstract = form.parsed_draft.get_abstract()
|
|
|
|
# See if there is a Submission in state waiting-for-draft
|
|
# for this revision.
|
|
# If so - we're going to update it otherwise we create a new object
|
|
|
|
submissions = Submission.objects.filter(name=form.filename,
|
|
rev=form.revision,
|
|
state_id = "waiting-for-draft").distinct()
|
|
|
|
if not submissions:
|
|
submission = Submission(name=form.filename, rev=form.revision, group=form.group)
|
|
elif len(submissions) == 1:
|
|
submission = submissions[0]
|
|
else:
|
|
raise Exception("Multiple submissions found waiting for upload")
|
|
|
|
try:
|
|
submission.state = DraftSubmissionStateName.objects.get(slug="uploaded")
|
|
submission.remote_ip = form.remote_ip
|
|
submission.title = form.title
|
|
submission.abstract = abstract
|
|
submission.pages = form.parsed_draft.get_pagecount()
|
|
submission.words = form.parsed_draft.get_wordcount()
|
|
submission.authors = authors
|
|
submission.first_two_pages = ''.join(form.parsed_draft.pages[:2])
|
|
submission.file_size = file_size
|
|
submission.file_types = ','.join(form.file_types)
|
|
submission.submission_date = datetime.date.today()
|
|
submission.document_date = form.parsed_draft.get_creation_date()
|
|
submission.replaces = ""
|
|
|
|
submission.save()
|
|
|
|
submission.formal_languages = FormalLanguageName.objects.filter(slug__in=form.parsed_draft.get_formal_languages())
|
|
|
|
except Exception as e:
|
|
log("Exception: %s\n" % e)
|
|
raise
|
|
|
|
# run submission checkers
|
|
def apply_check(submission, checker, method, fn):
|
|
func = getattr(checker, method)
|
|
passed, message, errors, warnings, items = func(fn)
|
|
check = SubmissionCheck(submission=submission, checker=checker.name, passed=passed,
|
|
message=message, errors=errors, warnings=warnings, items=items,
|
|
symbol=checker.symbol)
|
|
check.save()
|
|
|
|
for checker_path in settings.IDSUBMIT_CHECKER_CLASSES:
|
|
checker_class = import_string(checker_path)
|
|
checker = checker_class()
|
|
# ordered list of methods to try
|
|
for method in ("check_fragment_xml", "check_file_xml", "check_fragment_txt", "check_file_txt", ):
|
|
ext = method[-3:]
|
|
if hasattr(checker, method) and ext in file_name:
|
|
apply_check(submission, checker, method, file_name[ext])
|
|
break
|
|
|
|
create_submission_event(request, submission, desc="Uploaded submission")
|
|
# Don't add an "Uploaded new revision doevent yet, in case of cancellation
|
|
|
|
return redirect("ietf.submit.views.submission_status", submission_id=submission.pk, access_token=submission.access_token())
|
|
except IOError as e:
|
|
if "read error" in str(e): # The server got an IOError when trying to read POST data
|
|
form = SubmissionUploadForm(request=request)
|
|
form._errors = {}
|
|
form._errors["__all__"] = form.error_class(["There was a failure receiving the complete form data -- please try again."])
|
|
else:
|
|
raise
|
|
except ValidationError as e:
|
|
form = SubmissionUploadForm(request=request)
|
|
form._errors = {}
|
|
form._errors["__all__"] = form.error_class(["There was a failure converting the xml file to text -- please verify that your xml file is valid. (%s)" % e.message])
|
|
else:
|
|
form = SubmissionUploadForm(request=request)
|
|
|
|
return render(request, 'submit/upload_submission.html',
|
|
{'selected': 'index',
|
|
'form': form})
|
|
|
|
def note_well(request):
|
|
return render(request, 'submit/note_well.html', {'selected': 'notewell'})
|
|
|
|
def tool_instructions(request):
|
|
return render(request, 'submit/tool_instructions.html', {'selected': 'instructions'})
|
|
|
|
def search_submission(request):
|
|
error = None
|
|
name = None
|
|
if request.method == 'POST':
|
|
name = request.POST.get('name', '')
|
|
submission = Submission.objects.filter(name=name).order_by('-pk').first()
|
|
if submission:
|
|
return redirect(submission_status, submission_id=submission.pk)
|
|
error = 'No valid submission found for %s' % name
|
|
return render(request, 'submit/search_submission.html',
|
|
{'selected': 'status',
|
|
'error': error,
|
|
'name': name})
|
|
|
|
def can_edit_submission(user, submission, access_token):
|
|
key_matched = access_token and submission.access_token() == access_token
|
|
if not key_matched: key_matched = submission.access_key == access_token # backwards-compat
|
|
return key_matched or has_role(user, "Secretariat")
|
|
|
|
def submission_status(request, submission_id, access_token=None):
|
|
submission = get_object_or_404(Submission, pk=submission_id)
|
|
|
|
key_matched = access_token and submission.access_token() == access_token
|
|
if not key_matched: key_matched = submission.access_key == access_token # backwards-compat
|
|
if access_token and not key_matched:
|
|
raise Http404
|
|
|
|
errors = validate_submission(submission)
|
|
passes_checks = all([ c.passed!=False for c in submission.checks.all() ])
|
|
|
|
is_secretariat = has_role(request.user, "Secretariat")
|
|
is_chair = submission.group and submission.group.has_role(request.user, "chair")
|
|
|
|
can_edit = can_edit_submission(request.user, submission, access_token) and submission.state_id == "uploaded"
|
|
can_cancel = (key_matched or is_secretariat) and submission.state.next_states.filter(slug="cancel")
|
|
can_group_approve = (is_secretariat or is_chair) and submission.state_id == "grp-appr"
|
|
can_force_post = (
|
|
is_secretariat
|
|
and submission.state.next_states.filter(slug="posted").exists()
|
|
and submission.state_id != "waiting-for-draft")
|
|
show_send_full_url = (
|
|
not key_matched
|
|
and not is_secretariat
|
|
and not submission.state_id in ("cancel", "posted") )
|
|
addrs = gather_address_lists('sub_confirmation_requested',submission=submission)
|
|
confirmation_list = addrs.to
|
|
confirmation_list.extend(addrs.cc)
|
|
|
|
requires_group_approval = (submission.rev == '00' and submission.group and submission.group.type_id in ("wg", "rg", "ietf", "irtf", "iab", "iana", "rfcedtyp") and not Preapproval.objects.filter(name=submission.name).exists())
|
|
|
|
requires_prev_authors_approval = Document.objects.filter(name=submission.name)
|
|
|
|
group_authors_changed = False
|
|
doc = submission.existing_document()
|
|
if doc and doc.group:
|
|
old_authors = [ author.person for author in doc.documentauthor_set.all() ]
|
|
new_authors = [ get_person_from_name_email(author["name"], author.get("email")) for author in submission.authors ]
|
|
group_authors_changed = set(old_authors)!=set(new_authors)
|
|
|
|
message = None
|
|
|
|
if submission.state_id == "cancel":
|
|
message = ('error', 'This submission has been cancelled, modification is no longer possible.')
|
|
elif submission.state_id == "auth":
|
|
message = ('success', u'The submission is pending email authentication. An email has been sent to: %s' % ", ".join(confirmation_list))
|
|
elif submission.state_id == "grp-appr":
|
|
message = ('success', 'The submission is pending approval by the group chairs.')
|
|
elif submission.state_id == "aut-appr":
|
|
message = ('success', 'The submission is pending approval by the authors of the previous version. An email has been sent to: %s' % ", ".join(confirmation_list))
|
|
|
|
|
|
submitter_form = SubmitterForm(initial=submission.submitter_parsed(), prefix="submitter")
|
|
replaces_form = ReplacesForm(name=submission.name,initial=DocAlias.objects.filter(name__in=submission.replaces.split(",")))
|
|
|
|
if request.method == 'POST':
|
|
action = request.POST.get('action')
|
|
if action == "autopost" and submission.state_id == "uploaded":
|
|
if not can_edit:
|
|
return HttpResponseForbidden("You do not have permission to perform this action")
|
|
|
|
submitter_form = SubmitterForm(request.POST, prefix="submitter")
|
|
replaces_form = ReplacesForm(request.POST, name=submission.name)
|
|
validations = [submitter_form.is_valid(), replaces_form.is_valid()]
|
|
if all(validations):
|
|
submission.submitter = submitter_form.cleaned_line()
|
|
replaces = replaces_form.cleaned_data.get("replaces", [])
|
|
submission.replaces = ",".join(o.name for o in replaces)
|
|
|
|
approvals_received = submitter_form.cleaned_data['approvals_received']
|
|
|
|
if approvals_received:
|
|
if not is_secretariat:
|
|
return HttpResponseForbidden('You do not have permission to perform this action')
|
|
|
|
# go directly to posting submission
|
|
docevent_from_submission(request, submission, desc="Uploaded new revision")
|
|
|
|
desc = u"Secretariat manually posting. Approvals already received"
|
|
post_submission(request, submission, desc)
|
|
create_submission_event(request, submission, desc)
|
|
else:
|
|
docevent_from_submission(request, submission, desc="Uploaded new revision")
|
|
|
|
if requires_group_approval:
|
|
submission.state = DraftSubmissionStateName.objects.get(slug="grp-appr")
|
|
submission.save()
|
|
|
|
sent_to = send_approval_request_to_group(request, submission)
|
|
|
|
desc = "sent approval email to group chairs: %s" % u", ".join(sent_to)
|
|
docDesc = u"Request for posting approval emailed to group chairs: %s" % u", ".join(sent_to)
|
|
|
|
else:
|
|
submission.auth_key = generate_random_key()
|
|
if requires_prev_authors_approval:
|
|
submission.state = DraftSubmissionStateName.objects.get(slug="aut-appr")
|
|
else:
|
|
submission.state = DraftSubmissionStateName.objects.get(slug="auth")
|
|
submission.save()
|
|
|
|
sent_to = send_submission_confirmation(request, submission, chair_notice=group_authors_changed)
|
|
|
|
if submission.state_id == "aut-appr":
|
|
desc = u"sent confirmation email to previous authors: %s" % u", ".join(sent_to)
|
|
docDesc = "Request for posting confirmation emailed to previous authors: %s" % u", ".join(sent_to)
|
|
else:
|
|
desc = u"sent confirmation email to submitter and authors: %s" % u", ".join(sent_to)
|
|
docDesc = "Request for posting confirmation emailed to submitter and authors: %s" % u", ".join(sent_to)
|
|
|
|
msg = u"Set submitter to \"%s\", replaces to %s and %s" % (
|
|
submission.submitter,
|
|
", ".join(prettify_std_name(r.name) for r in replaces) if replaces else "(none)",
|
|
desc)
|
|
create_submission_event(request, submission, msg)
|
|
docevent_from_submission(request, submission, docDesc, who="(System)")
|
|
|
|
if access_token:
|
|
return redirect("ietf.submit.views.submission_status", submission_id=submission.pk, access_token=access_token)
|
|
else:
|
|
return redirect("ietf.submit.views.submission_status", submission_id=submission.pk)
|
|
|
|
elif action == "edit" and submission.state_id == "uploaded":
|
|
if access_token:
|
|
return redirect("ietf.submit.views.edit_submission", submission_id=submission.pk, access_token=access_token)
|
|
else:
|
|
return redirect("ietf.submit.views.edit_submission", submission_id=submission.pk)
|
|
|
|
elif action == "sendfullurl" and submission.state_id not in ("cancel", "posted"):
|
|
sent_to = send_full_url(request, submission)
|
|
|
|
message = ('success', u'An email has been sent with the full access URL to: %s' % u",".join(confirmation_list))
|
|
|
|
create_submission_event(request, submission, u"Sent full access URL to: %s" % u", ".join(sent_to))
|
|
|
|
elif action == "cancel" and submission.state.next_states.filter(slug="cancel"):
|
|
if not can_cancel:
|
|
return HttpResponseForbidden('You do not have permission to perform this action')
|
|
|
|
cancel_submission(submission)
|
|
|
|
create_submission_event(request, submission, "Cancelled submission")
|
|
|
|
return redirect("ietf.submit.views.submission_status", submission_id=submission_id)
|
|
|
|
|
|
elif action == "approve" and submission.state_id == "grp-appr":
|
|
if not can_group_approve:
|
|
return HttpResponseForbidden('You do not have permission to perform this action')
|
|
|
|
post_submission(request, submission, "WG -00 approved")
|
|
|
|
create_submission_event(request, submission, "Approved and posted submission")
|
|
|
|
return redirect("ietf.doc.views_doc.document_main", name=submission.name)
|
|
|
|
|
|
elif action == "forcepost" and submission.state.next_states.filter(slug="posted"):
|
|
if not can_force_post:
|
|
return HttpResponseForbidden('You do not have permission to perform this action')
|
|
|
|
if submission.state_id == "manual":
|
|
desc = "Posted submission manually"
|
|
else:
|
|
desc = "Forced post of submission"
|
|
|
|
post_submission(request, submission, desc)
|
|
|
|
create_submission_event(request, submission, desc)
|
|
|
|
return redirect("ietf.doc.views_doc.document_main", name=submission.name)
|
|
|
|
|
|
else:
|
|
# something went wrong, turn this into a GET and let the user deal with it
|
|
return HttpResponseRedirect("")
|
|
|
|
for author in submission.authors:
|
|
author["cleaned_country"] = clean_country_name(author.get("country"))
|
|
|
|
return render(request, 'submit/submission_status.html', {
|
|
'selected': 'status',
|
|
'submission': submission,
|
|
'errors': errors,
|
|
'passes_checks': passes_checks,
|
|
'submitter_form': submitter_form,
|
|
'replaces_form': replaces_form,
|
|
'message': message,
|
|
'can_edit': can_edit,
|
|
'can_force_post': can_force_post,
|
|
'can_group_approve': can_group_approve,
|
|
'can_cancel': can_cancel,
|
|
'show_send_full_url': show_send_full_url,
|
|
'requires_group_approval': requires_group_approval,
|
|
'requires_prev_authors_approval': requires_prev_authors_approval,
|
|
'confirmation_list': confirmation_list,
|
|
})
|
|
|
|
|
|
def edit_submission(request, submission_id, access_token=None):
|
|
submission = get_object_or_404(Submission, pk=submission_id, state="uploaded")
|
|
|
|
if not can_edit_submission(request.user, submission, access_token):
|
|
return HttpResponseForbidden('You do not have permission to access this page')
|
|
|
|
errors = validate_submission(submission)
|
|
form_errors = False
|
|
|
|
# we split the form handling into multiple forms, one for the
|
|
# submission itself, one for the submitter, and a list of forms
|
|
# for the authors
|
|
|
|
empty_author_form = AuthorForm()
|
|
|
|
if request.method == 'POST':
|
|
# get a backup submission now, the model form may change some
|
|
# fields during validation
|
|
prev_submission = Submission.objects.get(pk=submission.pk)
|
|
|
|
edit_form = EditSubmissionForm(request.POST, instance=submission, prefix="edit")
|
|
submitter_form = SubmitterForm(request.POST, prefix="submitter")
|
|
replaces_form = ReplacesForm(request.POST,name=submission.name)
|
|
author_forms = [ AuthorForm(request.POST, prefix=prefix)
|
|
for prefix in request.POST.getlist("authors-prefix")
|
|
if prefix != "authors-" ]
|
|
|
|
# trigger validation of all forms
|
|
validations = [edit_form.is_valid(), submitter_form.is_valid(), replaces_form.is_valid()] + [ f.is_valid() for f in author_forms ]
|
|
if all(validations):
|
|
changed_fields = []
|
|
|
|
submission.submitter = submitter_form.cleaned_line()
|
|
replaces = replaces_form.cleaned_data.get("replaces", [])
|
|
submission.replaces = ",".join(o.name for o in replaces)
|
|
submission.authors = [ { attr: f.cleaned_data.get(attr) or ""
|
|
for attr in ["name", "email", "affiliation", "country"] }
|
|
for f in author_forms ]
|
|
edit_form.save(commit=False) # transfer changes
|
|
|
|
if submission.rev != prev_submission.rev:
|
|
rename_submission_files(submission, prev_submission.rev, submission.rev)
|
|
|
|
submission.state = DraftSubmissionStateName.objects.get(slug="manual")
|
|
submission.save()
|
|
|
|
formal_languages_changed = False
|
|
if set(submission.formal_languages.all()) != set(edit_form.cleaned_data["formal_languages"]):
|
|
submission.formal_languages = edit_form.cleaned_data["formal_languages"]
|
|
formal_languages_changed = True
|
|
|
|
send_manual_post_request(request, submission, errors)
|
|
|
|
changed_fields += [
|
|
submission._meta.get_field(f).verbose_name
|
|
for f in list(edit_form.fields.keys()) + ["submitter", "authors"]
|
|
if (f == "formal_languages" and formal_languages_changed)
|
|
or getattr(submission, f) != getattr(prev_submission, f)
|
|
]
|
|
|
|
if changed_fields:
|
|
desc = u"Edited %s and sent request for manual post" % u", ".join(changed_fields)
|
|
else:
|
|
desc = "Sent request for manual post"
|
|
|
|
create_submission_event(request, submission, desc)
|
|
|
|
return redirect("ietf.submit.views.submission_status", submission_id=submission.pk)
|
|
else:
|
|
form_errors = True
|
|
else:
|
|
edit_form = EditSubmissionForm(instance=submission, prefix="edit")
|
|
submitter_form = SubmitterForm(initial=submission.submitter_parsed(), prefix="submitter")
|
|
replaces_form = ReplacesForm(name=submission.name,initial=DocAlias.objects.filter(name__in=submission.replaces.split(",")))
|
|
author_forms = [ AuthorForm(initial=author, prefix="authors-%s" % i)
|
|
for i, author in enumerate(submission.authors) ]
|
|
|
|
return render(request, 'submit/edit_submission.html',
|
|
{'selected': 'status',
|
|
'submission': submission,
|
|
'edit_form': edit_form,
|
|
'submitter_form': submitter_form,
|
|
'replaces_form': replaces_form,
|
|
'author_forms': author_forms,
|
|
'empty_author_form': empty_author_form,
|
|
'errors': errors,
|
|
'form_errors': form_errors,
|
|
})
|
|
|
|
|
|
def confirm_submission(request, submission_id, auth_token):
|
|
submission = get_object_or_404(Submission, pk=submission_id)
|
|
|
|
key_matched = submission.auth_key and auth_token == generate_access_token(submission.auth_key)
|
|
if not key_matched: key_matched = auth_token == submission.auth_key # backwards-compat
|
|
|
|
if request.method == 'POST' and submission.state_id in ("auth", "aut-appr") and key_matched:
|
|
submitter_parsed = submission.submitter_parsed()
|
|
if submitter_parsed["name"] and submitter_parsed["email"]:
|
|
# We know who approved it
|
|
desc = "New version approved"
|
|
elif submission.state_id == "auth":
|
|
desc = "New version approved by author"
|
|
else:
|
|
desc = "New version approved by previous author"
|
|
|
|
post_submission(request, submission, desc)
|
|
|
|
create_submission_event(request, submission, "Confirmed and posted submission")
|
|
|
|
return redirect("ietf.doc.views_doc.document_main", name=submission.name)
|
|
|
|
return render(request, 'submit/confirm_submission.html', {
|
|
'submission': submission,
|
|
'key_matched': key_matched,
|
|
})
|
|
|
|
|
|
def approvals(request):
|
|
approvals = approvable_submissions_for_user(request.user)
|
|
preapprovals = preapprovals_for_user(request.user)
|
|
|
|
days = 30
|
|
recently_approved = recently_approved_by_user(request.user, datetime.date.today() - datetime.timedelta(days=days))
|
|
|
|
return render(request, 'submit/approvals.html',
|
|
{'selected': 'approvals',
|
|
'approvals': approvals,
|
|
'preapprovals': preapprovals,
|
|
'recently_approved': recently_approved,
|
|
'days': days })
|
|
|
|
|
|
@role_required("Secretariat", "Area Director", "WG Chair", "RG Chair")
|
|
def add_preapproval(request):
|
|
groups = Group.objects.filter(type__in=("wg", "rg")).exclude(state__in=["conclude","bof-conc"]).order_by("acronym").distinct()
|
|
|
|
if not has_role(request.user, "Secretariat"):
|
|
groups = groups.filter(role__person__user=request.user,role__name__in=['ad','chair','delegate','secr'])
|
|
|
|
if request.method == "POST":
|
|
form = PreapprovalForm(request.POST)
|
|
form.groups = groups
|
|
if form.is_valid():
|
|
p = Preapproval()
|
|
p.name = form.cleaned_data["name"]
|
|
p.by = request.user.person
|
|
p.save()
|
|
|
|
return HttpResponseRedirect(urlreverse("ietf.submit.views.approvals") + "#preapprovals")
|
|
else:
|
|
form = PreapprovalForm()
|
|
|
|
return render(request, 'submit/add_preapproval.html',
|
|
{'selected': 'approvals',
|
|
'groups': groups,
|
|
'form': form })
|
|
|
|
@role_required("Secretariat", "WG Chair", "RG Chair")
|
|
def cancel_preapproval(request, preapproval_id):
|
|
preapproval = get_object_or_404(Preapproval, pk=preapproval_id)
|
|
|
|
if preapproval not in preapprovals_for_user(request.user):
|
|
raise HttpResponseForbidden("You do not have permission to cancel this preapproval.")
|
|
|
|
if request.method == "POST" and request.POST.get("action", "") == "cancel":
|
|
preapproval.delete()
|
|
|
|
return HttpResponseRedirect(urlreverse("ietf.submit.views.approvals") + "#preapprovals")
|
|
|
|
return render(request, 'submit/cancel_preapproval.html',
|
|
{'selected': 'approvals',
|
|
'preapproval': preapproval })
|
|
|
|
|
|
def manualpost(request):
|
|
'''
|
|
Main view for manual post requests
|
|
'''
|
|
|
|
manual = Submission.objects.filter(state_id = "manual").distinct()
|
|
|
|
for s in manual:
|
|
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:
|
|
return HttpResponseForbidden('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(request,
|
|
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=request.META.get('REMOTE_ADDR', None),
|
|
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,
|
|
}
|
|
|
|
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):
|
|
submission = get_object_or_404(Submission, pk=submission_id)
|
|
|
|
key_matched = access_token and submission.access_token() == access_token
|
|
if not key_matched: key_matched = submission.access_key == access_token # backwards-compat
|
|
if access_token and not key_matched:
|
|
raise Http404
|
|
|
|
return submission
|