diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index a6ef4fe00..2b7a66a0e 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -1,5 +1,5 @@ import datetime - +import debug #pyflakes:ignore from django import forms from ietf.iesg.models import TelechatDate @@ -45,3 +45,73 @@ class NotifyForm(forms.Form): def clean_notify(self): addrspecs = [x.strip() for x in self.cleaned_data["notify"].split(',')] return ', '.join(addrspecs) + +from ietf.doc.models import Document, RelatedDocument, DocAlias, State +from ietf.doc.fields import SearchableDocAliasesField, SearchableDocAliasField + +IESG_APPROVED_STATE_LIST = ("ann", "rfcqueue", "pub") + +class AddDownrefForm(forms.Form): + rfc = SearchableDocAliasField( + label="Referenced RFC", + help_text="The RFC that is approved for downref", + required=True) + drafts = SearchableDocAliasesField( + label="Internet-Drafts that makes the reference", + help_text="The drafts that approve the downref in thier Last Call", + required=True) + + def clean_rfc(self): + if 'rfc' not in self.cleaned_data: + raise forms.ValidationError("Must provide a referenced RFC and a referencing Internet-Draft") + + rfc = self.cleaned_data['rfc'] + if not rfc.document.is_rfc(): + raise forms.ValidationError("Cannot find the RFC: " + rfc.name) + return rfc + + def clean_drafts(self): + if 'drafts' not in self.cleaned_data: + raise forms.ValidationError("Must provide a referenced RFC and a referencing Internet-Draft") + + v_err_names = "" + drafts = self.cleaned_data['drafts'] + for da in drafts: + state = da.document.get_state("draft-iesg") + if not state or state.slug not in IESG_APPROVED_STATE_LIST: + if v_err_names: + v_err_names = v_err_names + ", " + da.name + else: + v_err_names = da.name + if v_err_names: + raise forms.ValidationError("Draft is not yet approved: " + v_err_names) + return drafts + + def clean(self): + if 'rfc' not in self.cleaned_data or 'drafts' not in self.cleaned_data: + raise forms.ValidationError("Must provide a referenced RFC and a referencing Internet-Draft") + + v_err_pairs = "" + rfc = self.cleaned_data['rfc'] + drafts = self.cleaned_data['drafts'] + for da in drafts: + if RelatedDocument.objects.filter(source=da.document, target=rfc, relationship_id='downrefappr'): + if v_err_pairs: + v_err_pairs = v_err_pairs + ", " + da.name + " --> RFC " + rfc.document.rfc_number() + else: + v_err_pairs = da.name + " --> RFC " + rfc.document.rfc_number() + if v_err_pairs: + raise forms.ValidationError("Downref is already in the registry: " + v_err_pairs) + + if 'save_downref_anyway' not in self.data: + # this check is skipped if the save_downref_anyway button is used + v_err_refnorm = "" + for da in drafts: + if not RelatedDocument.objects.filter(source=da.document, target=rfc, relationship_id='refnorm'): + if v_err_refnorm: + v_err_refnorm = v_err_refnorm + " or " + da.name + else: + v_err_refnorm = da.name + if v_err_refnorm: + v_err_refnorm_prefix = "There does not seem to be a normative reference to RFC " + rfc.document.rfc_number() + " by " + raise forms.ValidationError(v_err_refnorm_prefix + v_err_refnorm) diff --git a/ietf/doc/migrations/0026_add_downrefappr_from_wiki.py b/ietf/doc/migrations/0026_add_downrefappr_from_wiki.py new file mode 100644 index 000000000..d073c8ed8 --- /dev/null +++ b/ietf/doc/migrations/0026_add_downrefappr_from_wiki.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +from django.db import models, migrations + +downref_registry_from_wiki = [ + ['rfc952', 'draft-hollenbeck-rfc4931bis'], + ['rfc952', 'draft-hollenbeck-rfc4932bis'], + ['rfc1094','draft-ietf-nfsv4-nfsdirect'], + ['rfc1321','rfc3967'], + ['rfc1813','draft-ietf-nfsv4-nfsdirect'], + ['rfc1951','draft-ietf-lemonade-compress'], + ['rfc1952','draft-sweet-rfc2911bis'], + ['rfc1977','draft-sweet-rfc2911bis'], + ['rfc2104','rfc3967'], + ['rfc2144','draft-ietf-secsh-newmodes'], + ['rfc2315','draft-eastlake-additional-xmlsec-uris'], + ['rfc2330','draft-ietf-ippm-metrictest'], + ['rfc2412','draft-ietf-cat-kerberos-pk-init'], + ['rfc2648','draft-ietf-simple-xcap-diff'], + ['rfc2683','draft-ietf-qresync-rfc5162bis'], + ['rfc2702','draft-ietf-isis-admin-tags'], + ['rfc2781','draft-ietf-appsawg-xml-mediatypes'], + ['rfc2818','draft-dusseault-caldav'], + ['rfc2898','draft-turner-asymmetrickeyformat-algs'], + ['rfc2966','draft-ietf-isis-admin-tags'], + ['rfc2985','rfc5750'], + ['rfc2986','rfc6487'], + ['rfc3032','draft-ietf-pals-rfc4447bis'], + ['rfc3174','draft-harris-ssh-rsa-kex'], + ['rfc3196','draft-sweet-rfc2911bis'], + ['rfc3217','draft-ietf-smime-cms-rsa-kem'], + ['rfc3272','draft-ietf-mpls-cosfield-def'], + ['rfc3280','rfc3852'], + ['rfc3281','rfc3852'], + ['rfc3394','draft-ietf-smime-cms-rsa-kem'], + ['rfc3447','draft-ietf-cat-kerberos-pk-init'], + ['rfc3469','draft-ietf-mpls-cosfield-def'], + ['rfc3548','draft-ietf-dnsext-dnssec-records'], + ['rfc3564','draft-ietf-mpls-cosfield-def'], + ['rfc3567','draft-ietf-pce-disco-proto-isis'], + ['rfc3610','rfc4309'], + ['rfc3843','rfc5953'], + ['rfc3579','draft-ietf-radext-rfc4590bis'], + ['rfc3618','draft-ietf-mboned-msdp-deploy'], + ['rfc3713','draft-kato-ipsec-ciph-camellia'], + ['rfc3784','draft-ietf-isis-admin-tags'], + ['rfc3985','draft-ietf-mpls-cosfield-def'], + ['rfc4050','draft-eastlake-additional-xmlsec-uris'], + ['rfc4082','draft-ietf-msec-srtp-tesla'], + ['rfc4226','draft-ietf-keyprov-pskc'], + ['rfc4269','draft-eastlake-additional-xmlsec-uris'], + ['rfc4291','draft-hollenbeck-rfc4932bis'], + ['rfc4347','rfc5953'], + ['rfc4357','draft-ietf-pkix-gost-cppk'], + ['rfc4366','rfc5953'], + ['rfc4492','draft-ietf-tls-chacha20-poly1305'], + ['rfc4493','draft-songlee-aes-cmac-96'], + ['rfc4627','draft-ietf-mediactrl-ivr-control-package'], + ['rfc4753','draft-ietf-ipsec-ike-auth-ecdsa'], + ['rfc4949','draft-ietf-oauth-v2'], + ['rfc5036','draft-ietf-pals-rfc4447bis'], + ['rfc5246','rfc5953'], + ['rfc5280','rfc5953'], + ['rfc5322','draft-hollenbeck-rfc4933bis'], + ['rfc5410','draft-arkko-mikey-iana'], + ['rfc5489','draft-ietf-tls-chacha20-poly1305'], + ['rfc5598','draft-ietf-dkim-mailinglists'], + ['rfc5649','draft-turner-asymmetrickeyformat-algs'], + ['rfc5753','draft-turner-cms-symmetrickeypackage-algs'], + ['rfc5781','draft-ietf-sidr-res-certs'], + ['rfc5869','draft-ietf-trill-channel-tunnel'], + ['rfc5890','draft-ietf-dkim-rfc4871bis'], + ['rfc5911','draft-turner-asymmetrickeyformat'], + ['rfc5912','draft-ietf-pkix-authorityclearanceconstraints'], + ['rfc5952','rfc5953'], + ['rfc6043','draft-arkko-mikey-iana'], + ['rfc6090','draft-turner-akf-algs-update'], + ['rfc6151','draft-ietf-netmod-system-mgmt'], + ['rfc6234','draft-schaad-pkix-rfc2875-bis'], + ['rfc6386','draft-ietf-rtcweb-video'], + ['rfc6480','rfc6485'], + ['rfc6480','rfc6489'], + ['rfc6480','rfc6491'], + ['rfc6480','rfc7935'], + ['rfc6707','draft-ietf-cdni-metadata'], + ['rfc6839','draft-ietf-appsawg-xml-mediatypes'], + ['rfc7251','rfc7252'], + ['rfc7358','draft-ietf-pals-rfc4447bis'], + ['rfc7539','draft-ietf-tls-chacha20-poly1305'], + ['rfc7612','draft-sweet-rfc2911bis'], + ['rfc7748','draft-ietf-jose-cfrg-curves'], + ['rfc8032','draft-ietf-jose-cfrg-curves'] ] + + +def addDownrefRelationships(apps,schema_editor): + Document = apps.get_model('doc','Document') + DocAlias = apps.get_model('doc','DocAlias') + RelatedDocument = apps.get_model('doc','RelatedDocument') + + for [fn2, fn1] in downref_registry_from_wiki: + da1 = DocAlias.objects.get(name=fn1) + da2 = DocAlias.objects.get(name=fn2) + RelatedDocument.objects.create(source=da1.document, + target=da2, relationship_id='downrefappr') + + +def removeDownrefRelationships(apps,schema_editor): + Document = apps.get_model('doc','Document') + DocAlias = apps.get_model('doc','DocAlias') + RelatedDocument = apps.get_model('doc','RelatedDocument') + + for [fn2, fn1] in downref_registry_from_wiki: + da1 = DocAlias.objects.get(name=fn1) + da2 = DocAlias.objects.get(name=fn2) + RelatedDocument.objects.filter(source=da1.document, + target=da2, relationship_id='downrefappr').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('name', '0019_add_docrelationshoname_downrefappr'), + ('doc', '0025_auto_20170307_0146'), + ] + + operations = [ + migrations.RunPython(addDownrefRelationships,removeDownrefRelationships) + ] diff --git a/ietf/doc/models.py b/ietf/doc/models.py index f3b65091a..ca5388f13 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -885,6 +885,9 @@ EVENT_TYPES = [ ("requested_review", "Requested review"), ("assigned_review_request", "Assigned review request"), ("closed_review_request", "Closed review request"), + + # downref + ("downref_approved", "Downref approved"), ] class DocEvent(models.Model): diff --git a/ietf/doc/tests_downref.py b/ietf/doc/tests_downref.py new file mode 100644 index 000000000..5417a9962 --- /dev/null +++ b/ietf/doc/tests_downref.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +import debug # pyflakes:ignore + +from pyquery import PyQuery + +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse + +from ietf.doc.models import Document, DocAlias, RelatedDocument, State +from ietf.utils.test_utils import TestCase +from ietf.utils.test_data import make_test_data, make_downref_test_data +from ietf.utils.test_utils import login_testing_unauthorized, unicontent + +class Downref(TestCase): + def test_downref_registry(self): + url = urlreverse('ietf.doc.views_downref.downref_registry') + + # normal - get the table without the "Add downref" button + self.client.login(username="plain", password="plain+password") + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + content = unicontent(r) + self.assertTrue('
+ Add downref +
+ {% endbuttons %} + {% endif %} + +Referenced RFC | +Internet-Draft making the reference | +
---|---|
+ RFC {{ target_doc.rfc_number }}
+ {{ target_doc.title }} + |
+
+ {{ source_doc.name }}
+ {{ source_doc.title }} + |
+