|
22 | 22 | from ietf.idrfc.utils import * |
23 | 23 | from ietf.idrfc.lastcall import request_last_call |
24 | 24 |
|
25 | | -from doc.models import Document, Event, save_document_in_history, DocHistory |
26 | | -from name.models import IesgDocStateName, get_next_iesg_states |
| 25 | +from doc.models import Document, Event, Status, Telechat, save_document_in_history, DocHistory |
| 26 | +from name.models import IesgDocStateName, IntendedStdLevelName, DocInfoTagName, get_next_iesg_states |
27 | 27 |
|
28 | 28 | class ChangeStateForm(forms.Form): |
29 | 29 | state = forms.ModelChoiceField(IDState.objects.all(), empty_label=None, required=True) |
@@ -90,7 +90,7 @@ def change_stateREDESIGN(request, name): |
90 | 90 | """Change state of Internet Draft, notifying parties as necessary |
91 | 91 | and logging the change as a comment.""" |
92 | 92 | doc = get_object_or_404(Document, docalias__name=name) |
93 | | - if not doc.latest_event(type="started_iesg_process") or doc.state_id == "expired": |
| 93 | + if (not doc.latest_event(type="started_iesg_process")) or doc.state_id == "expired": |
94 | 94 | raise Http404() |
95 | 95 |
|
96 | 96 | login = request.user.get_profile().email() |
@@ -379,6 +379,234 @@ def diff(obj, attr, name): |
379 | 379 | login=login), |
380 | 380 | context_instance=RequestContext(request)) |
381 | 381 |
|
| 382 | +class NameFromEmailModelChoiceField(forms.ModelChoiceField): |
| 383 | + def label_from_instance(self, obj): |
| 384 | + return obj.get_name() |
| 385 | + |
| 386 | +class EditInfoFormREDESIGN(forms.Form): |
| 387 | + intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.all(), empty_label=None, required=True) |
| 388 | + status_date = forms.DateField(required=False, help_text="Format is YYYY-MM-DD") |
| 389 | + via_rfc_editor = forms.BooleanField(required=False, label="Via IRTF or RFC Editor") |
| 390 | + ad = NameFromEmailModelChoiceField(Email.objects.filter(role__name__in=("ad", "ex-ad")).order_by('role__name', 'person__name'), label="Responsible AD", empty_label=None, required=True) |
| 391 | + notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False) |
| 392 | + note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False) |
| 393 | + telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False) |
| 394 | + returning_item = forms.BooleanField(required=False) |
| 395 | + |
| 396 | + def __init__(self, *args, **kwargs): |
| 397 | + old_ads = kwargs.pop('old_ads') |
| 398 | + |
| 399 | + super(self.__class__, self).__init__(*args, **kwargs) |
| 400 | + |
| 401 | + # fix up ad field |
| 402 | + choices = self.fields['ad'].choices |
| 403 | + ex_ads = dict((e.pk, e) for e in Email.objects.filter(role__name="ex-ad")) |
| 404 | + if old_ads: |
| 405 | + # separate active ADs from inactive |
| 406 | + for i, t in enumerate(choices): |
| 407 | + if t[0] in ex_ads: |
| 408 | + choices.insert(i, ("", "----------------")) |
| 409 | + break |
| 410 | + else: |
| 411 | + # remove old ones |
| 412 | + self.fields['ad'].choices = [t for t in choices if t[0] not in ex_ads] |
| 413 | + |
| 414 | + # telechat choices |
| 415 | + dates = TelechatDates.objects.all()[0].dates() |
| 416 | + init = kwargs['initial']['telechat_date'] |
| 417 | + if init and init not in dates: |
| 418 | + dates.insert(0, init) |
| 419 | + |
| 420 | + choices = [("", "(not on agenda)")] |
| 421 | + for d in dates: |
| 422 | + choices.append((d, d.strftime("%Y-%m-%d"))) |
| 423 | + |
| 424 | + self.fields['telechat_date'].choices = choices |
| 425 | + |
| 426 | + # returning item is rendered non-standard |
| 427 | + self.standard_fields = [x for x in self.visible_fields() if x.name not in ('returning_item',)] |
| 428 | + |
| 429 | + def clean_status_date(self): |
| 430 | + d = self.cleaned_data['status_date'] |
| 431 | + if d: |
| 432 | + if d < date.today(): |
| 433 | + raise forms.ValidationError("Date must not be in the past.") |
| 434 | + if d >= date.today() + timedelta(days=365 * 2): |
| 435 | + raise forms.ValidationError("Date must be within two years.") |
| 436 | + |
| 437 | + return d |
| 438 | + |
| 439 | + def clean_note(self): |
| 440 | + # note is stored munged in the database |
| 441 | + return self.cleaned_data['note'].replace('\n', '<br>').replace('\r', '').replace(' ', ' ') |
| 442 | + |
| 443 | + |
| 444 | +def get_initial_notify(doc): |
| 445 | + # set change state notice to something sensible |
| 446 | + receivers = [] |
| 447 | + if doc.group.type_id == "individ": |
| 448 | + for a in doc.authors.all(): |
| 449 | + receivers.append(e.address) |
| 450 | + else: |
| 451 | + receivers.append("%s-chairs@%s" % (doc.group.acronym, settings.TOOLS_SERVER)) |
| 452 | + for editor in Email.objects.filter(role__name="wgeditor", role__group=doc.group): |
| 453 | + receivers.append(e.address) |
| 454 | + |
| 455 | + receivers.append("%s@%s" % (doc.name, settings.TOOLS_SERVER)) |
| 456 | + return ", ".join(receivers) |
| 457 | + |
| 458 | +@group_required('Area_Director','Secretariat') |
| 459 | +def edit_infoREDESIGN(request, name): |
| 460 | + """Edit various Internet Draft attributes, notifying parties as |
| 461 | + necessary and logging changes as document events.""" |
| 462 | + doc = get_object_or_404(Document, name=name) |
| 463 | + if doc.state_id == "expired": |
| 464 | + raise Http404() |
| 465 | + |
| 466 | + login = request.user.get_profile().email() |
| 467 | + |
| 468 | + new_document = False |
| 469 | + if not doc.iesg_state: # FIXME: should probably get this as argument to view |
| 470 | + new_document = True |
| 471 | + doc.iesg_state = IesgDocStateName.objects.get(slug="pub-req") |
| 472 | + doc.notify = get_initial_notify(doc) |
| 473 | + |
| 474 | + e = doc.latest_event(Telechat, type="scheduled_for_telechat") |
| 475 | + initial_telechat_date = e.telechat_date if e else None |
| 476 | + initial_returning_item = bool(e and e.returning_item) |
| 477 | + |
| 478 | + if request.method == 'POST': |
| 479 | + form = EditInfoForm(request.POST, |
| 480 | + old_ads=False, |
| 481 | + initial=dict(telechat_date=initial_telechat_date)) |
| 482 | + if form.is_valid(): |
| 483 | + save_document_in_history(doc) |
| 484 | + |
| 485 | + r = form.cleaned_data |
| 486 | + if new_document: |
| 487 | + # fix so Django doesn't barf in the diff below because these |
| 488 | + # fields can't be NULL |
| 489 | + doc.ad = r['ad'] |
| 490 | + |
| 491 | + replaces = Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces") |
| 492 | + if replaces: |
| 493 | + # this should perhaps be somewhere else, e.g. the |
| 494 | + # place where the replace relationship is established |
| 495 | + e = Event() |
| 496 | + e.type = "added_comment" |
| 497 | + e.by = Email.objects.get(address="(System)") |
| 498 | + e.doc = doc |
| 499 | + e.desc = "Earlier history may be found in the Comment Log for <a href=\"%s\">%s</a>" % (replaces[0], replaces[0].get_absolute_url()) |
| 500 | + e.save() |
| 501 | + |
| 502 | + e = Event() |
| 503 | + e.type = "started_iesg_process" |
| 504 | + e.by = login |
| 505 | + e.doc = doc |
| 506 | + e.desc = "IESG process started in state <b>%s</b>" % doc.iesg_state.name |
| 507 | + e.save() |
| 508 | + |
| 509 | + orig_ad = doc.ad |
| 510 | + |
| 511 | + changes = [] |
| 512 | + |
| 513 | + def desc(attr, new, old): |
| 514 | + entry = "%(attr)s has been changed to <b>%(new)s</b> from <b>%(old)s</b>" |
| 515 | + if new_document: |
| 516 | + entry = "%(attr)s has been changed to <b>%(new)s</b>" |
| 517 | + |
| 518 | + return entry % dict(attr=attr, new=new, old=old) |
| 519 | + |
| 520 | + def diff(attr, name): |
| 521 | + v = getattr(doc, attr) |
| 522 | + if r[attr] != v: |
| 523 | + changes.append(desc(name, r[attr], v)) |
| 524 | + setattr(doc, attr, r[attr]) |
| 525 | + |
| 526 | + # update the attributes, keeping track of what we're doing |
| 527 | + diff('intended_std_level', "Intended Status") |
| 528 | + diff('ad', "Responsible AD") |
| 529 | + diff('notify', "State Change Notice email list") |
| 530 | + |
| 531 | + if r['note'] != doc.note: |
| 532 | + if not r['note']: |
| 533 | + if doc.note: |
| 534 | + changes.append("Note field has been cleared") |
| 535 | + else: |
| 536 | + if doc.note: |
| 537 | + changes.append("Note changed to '%s'" % r['note']) |
| 538 | + else: |
| 539 | + changes.append("Note added '%s'" % r['note']) |
| 540 | + |
| 541 | + doc.note = r['note'] |
| 542 | + |
| 543 | + for c in changes: |
| 544 | + e = Event() |
| 545 | + e.type = "changed_document" |
| 546 | + e.by = login |
| 547 | + e.doc = doc |
| 548 | + e.desc = c + " by %s" % login.get_name() |
| 549 | + e.save() |
| 550 | + |
| 551 | + update_telechat(request, doc, login, |
| 552 | + r['telechat_date'], r['returning_item']) |
| 553 | + |
| 554 | + e = doc.latest_event(Status, type="changed_status_date") |
| 555 | + status_date = e.date if e else None |
| 556 | + if r["status_date"] != status_date: |
| 557 | + e = Status() |
| 558 | + e.type ="changed_status_date" |
| 559 | + e.by = login |
| 560 | + e.doc = doc |
| 561 | + d = desc("Status date", r["status_date"], status_date) |
| 562 | + changes.append(d) |
| 563 | + e.desc = d + " by %s" % login.get_name() |
| 564 | + e.date = r["status_date"] |
| 565 | + e.save() |
| 566 | + |
| 567 | + if in_group(request.user, 'Secretariat'): |
| 568 | + via_rfc = DocInfoTagName.objects.get(slug="via-rfc") |
| 569 | + if r['via_rfc_editor']: |
| 570 | + doc.tags.add(via_rfc) |
| 571 | + else: |
| 572 | + doc.tags.remove(via_rfc) |
| 573 | + |
| 574 | + doc.time = datetime.datetime.now() |
| 575 | + |
| 576 | + if changes and not new_document: |
| 577 | + email_owner(request, doc, orig_ad, login, "\n".join(changes)) |
| 578 | + |
| 579 | + doc.save() |
| 580 | + return HttpResponseRedirect(doc.get_absolute_url()) |
| 581 | + else: |
| 582 | + e = doc.latest_event(Status) |
| 583 | + status = e.date if e else None |
| 584 | + init = dict(intended_std_level=doc.intended_std_level, |
| 585 | + status_date=status, |
| 586 | + ad=doc.ad, |
| 587 | + notify=doc.notify, |
| 588 | + note=dehtmlify_textarea_text(doc.note), |
| 589 | + telechat_date=initial_telechat_date, |
| 590 | + returning_item=initial_returning_item, |
| 591 | + ) |
| 592 | + |
| 593 | + form = EditInfoForm(old_ads=False, initial=init) |
| 594 | + |
| 595 | + if not in_group(request.user, 'Secretariat'): |
| 596 | + # filter out Via RFC Editor |
| 597 | + form.standard_fields = [x for x in form.standard_fields if x.name != "via_rfc_editor"] |
| 598 | + |
| 599 | + return render_to_response('idrfc/edit_infoREDESIGN.html', |
| 600 | + dict(doc=doc, |
| 601 | + form=form, |
| 602 | + user=request.user, |
| 603 | + login=login), |
| 604 | + context_instance=RequestContext(request)) |
| 605 | + |
| 606 | +if settings.USE_DB_REDESIGN_PROXY_CLASSES: |
| 607 | + EditInfoForm = EditInfoFormREDESIGN |
| 608 | + edit_info = edit_infoREDESIGN |
| 609 | + |
382 | 610 |
|
383 | 611 | @group_required('Area_Director','Secretariat') |
384 | 612 | def request_resurrect(request, name): |
|
0 commit comments